[FEATURE] Initial Packaging For Resume Display (#7)
Co-authored-by: dunemask <dunemask@gmail.com> Co-authored-by: Dunemask <dunemask@gmail.com> Reviewed-on: https://gitea.dunemask.net/elysium/nile/pulls/7
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
dist/
|
dist/
|
||||||
node_modules/
|
node_modules/
|
||||||
|
*.swp
|
||||||
|
|
38
package-lock.json
generated
|
@ -8,6 +8,9 @@
|
||||||
"name": "nile",
|
"name": "nile",
|
||||||
"version": "0.0.1",
|
"version": "0.0.1",
|
||||||
"license": "LGPL-2.1-only",
|
"license": "LGPL-2.1-only",
|
||||||
|
"dependencies": {
|
||||||
|
"react-responsive-carousel": "^3.2.23"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@emotion/react": "^11.11.1",
|
"@emotion/react": "^11.11.1",
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
|
@ -1366,6 +1369,11 @@
|
||||||
"node": ">=0.8.0"
|
"node": ">=0.8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/classnames": {
|
||||||
|
"version": "2.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
|
||||||
|
"integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
|
||||||
|
},
|
||||||
"node_modules/clsx": {
|
"node_modules/clsx": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz",
|
||||||
|
@ -1687,8 +1695,7 @@
|
||||||
"node_modules/js-tokens": {
|
"node_modules/js-tokens": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
|
||||||
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
|
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/jsesc": {
|
"node_modules/jsesc": {
|
||||||
"version": "2.5.2",
|
"version": "2.5.2",
|
||||||
|
@ -1730,7 +1737,6 @@
|
||||||
"version": "1.4.0",
|
"version": "1.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
|
||||||
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"js-tokens": "^3.0.0 || ^4.0.0"
|
"js-tokens": "^3.0.0 || ^4.0.0"
|
||||||
},
|
},
|
||||||
|
@ -1781,7 +1787,6 @@
|
||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
|
@ -1896,7 +1901,6 @@
|
||||||
"version": "15.8.1",
|
"version": "15.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||||
"dev": true,
|
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"loose-envify": "^1.4.0",
|
"loose-envify": "^1.4.0",
|
||||||
"object-assign": "^4.1.1",
|
"object-assign": "^4.1.1",
|
||||||
|
@ -1906,8 +1910,7 @@
|
||||||
"node_modules/prop-types/node_modules/react-is": {
|
"node_modules/prop-types/node_modules/react-is": {
|
||||||
"version": "16.13.1",
|
"version": "16.13.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"node_modules/react": {
|
"node_modules/react": {
|
||||||
"version": "18.2.0",
|
"version": "18.2.0",
|
||||||
|
@ -1934,6 +1937,17 @@
|
||||||
"react": "^18.2.0"
|
"react": "^18.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-easy-swipe": {
|
||||||
|
"version": "0.0.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-easy-swipe/-/react-easy-swipe-0.0.21.tgz",
|
||||||
|
"integrity": "sha512-OeR2jAxdoqUMHIn/nS9fgreI5hSpgGoL5ezdal4+oO7YSSgJR8ga+PkYGJrSrJ9MKlPcQjMQXnketrD7WNmNsg==",
|
||||||
|
"dependencies": {
|
||||||
|
"prop-types": "^15.5.8"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-icons": {
|
"node_modules/react-icons": {
|
||||||
"version": "4.10.1",
|
"version": "4.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.10.1.tgz",
|
||||||
|
@ -1981,6 +1995,16 @@
|
||||||
"node": ">=0.10.0"
|
"node": ">=0.10.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/react-responsive-carousel": {
|
||||||
|
"version": "3.2.23",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-responsive-carousel/-/react-responsive-carousel-3.2.23.tgz",
|
||||||
|
"integrity": "sha512-pqJLsBaKHWJhw/ItODgbVoziR2z4lpcJg+YwmRlSk4rKH32VE633mAtZZ9kDXjy4wFO+pgUZmDKPsPe1fPmHCg==",
|
||||||
|
"dependencies": {
|
||||||
|
"classnames": "^2.2.5",
|
||||||
|
"prop-types": "^15.5.8",
|
||||||
|
"react-easy-swipe": "^0.0.21"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/react-router": {
|
"node_modules/react-router": {
|
||||||
"version": "6.15.0",
|
"version": "6.15.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.15.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.15.0.tgz",
|
||||||
|
|
|
@ -35,5 +35,8 @@
|
||||||
"react-material-ui-carousel": "^3.4.2",
|
"react-material-ui-carousel": "^3.4.2",
|
||||||
"react-router-dom": "^6.15.0",
|
"react-router-dom": "^6.15.0",
|
||||||
"vite": "4.4.9"
|
"vite": "4.4.9"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react-responsive-carousel": "^3.2.23"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
BIN
public/portfolio/bottega-cert-full.png
Normal file
After Width: | Height: | Size: 696 KiB |
Before Width: | Height: | Size: 696 KiB After Width: | Height: | Size: 400 KiB |
BIN
public/portfolio/linux-cert-full.png
Normal file
After Width: | Height: | Size: 642 KiB |
Before Width: | Height: | Size: 642 KiB After Width: | Height: | Size: 336 KiB |
Before Width: | Height: | Size: 370 KiB After Width: | Height: | Size: 106 KiB |
Before Width: | Height: | Size: 84 KiB After Width: | Height: | Size: 27 KiB |
BIN
public/portfolio/projects/minecluster.png
Normal file
After Width: | Height: | Size: 92 KiB |
Before Width: | Height: | Size: 504 KiB After Width: | Height: | Size: 216 KiB |
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 33 KiB |
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 51 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 45 KiB After Width: | Height: | Size: 16 KiB |
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 17 KiB |
|
@ -1,8 +1,5 @@
|
||||||
import { createRoot } from "react-dom/client";
|
import { createRoot } from "react-dom/client";
|
||||||
import Nile from "./Nile.jsx";
|
import Nile from "./Nile.jsx";
|
||||||
import Footer from "./pages/delta/Footer.jsx";
|
|
||||||
|
|
||||||
const nileRoot = createRoot(document.getElementById("root"));
|
const nileRoot = createRoot(document.getElementById("root"));
|
||||||
nileRoot.render(<Nile />);
|
nileRoot.render(<Nile />);
|
||||||
/*const footerRoot = createRoot(document.getElementById("footer"))
|
|
||||||
footerRoot.render(<Footer />);*/
|
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
import React from "react";
|
|
||||||
export default function Loading() {
|
|
||||||
return <h1>They see me loading, they hating...</h1>;
|
|
||||||
}
|
|
|
@ -1,19 +1,60 @@
|
||||||
// React
|
// React
|
||||||
|
import { useState } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import AppBar from "@mui/material/AppBar";
|
import AppBar from "@mui/material/AppBar";
|
||||||
|
import Drawer from "@mui/material/Drawer";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
import Toolbar from "@mui/material/Toolbar";
|
import Toolbar from "@mui/material/Toolbar";
|
||||||
import IconButton from "@mui/material/IconButton";
|
import IconButton from "@mui/material/IconButton";
|
||||||
import { Button } from "@mui/material";
|
import Button from "@mui/material/Button";
|
||||||
|
import ListItemText from "@mui/material/ListItemText";
|
||||||
|
import List from "@mui/material/List";
|
||||||
|
import ListItemButton from "@mui/material/ListItemButton";
|
||||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||||
import { useTheme } from "@mui/material/styles";
|
import { useTheme } from "@mui/material/styles";
|
||||||
|
|
||||||
|
import MenuIcon from "@mui/icons-material/Menu";
|
||||||
|
|
||||||
|
const links = [
|
||||||
|
{ url: "/#portfolio", title: "Portfolio" },
|
||||||
|
{ url: "/#achievements", title: "Achivements" },
|
||||||
|
{ url: "/#contact", title: "Contact" },
|
||||||
|
//{ url: "/references", title: "References" },
|
||||||
|
];
|
||||||
|
|
||||||
export default function Navbar() {
|
export default function Navbar() {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const hideNavLinks = useMediaQuery(theme.breakpoints.down("md"));
|
const [miniNavOpen, setMiniNavOpen] = useState();
|
||||||
|
const miniNav = useMediaQuery(theme.breakpoints.down("md"));
|
||||||
|
const toggleMiniNavOpen = () => setMiniNavOpen(!miniNavOpen);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<AppBar position="fixed" sx={{ bgcolor: "black" }}>
|
<AppBar position="fixed" sx={{ bgcolor: "black" }}>
|
||||||
|
<Drawer open={miniNavOpen} anchor="top" onClose={toggleMiniNavOpen}>
|
||||||
|
<AppBar position="fixed" sx={{ bgcolor: "black" }} />
|
||||||
|
<Box sx={{ margin: "0 auto", width: "100%" }}>
|
||||||
|
<List>
|
||||||
|
<ListItemButton onClick={toggleMiniNavOpen}>
|
||||||
|
{" "}
|
||||||
|
<ListItemText sx={{ textAlign: "center" }}>Back</ListItemText>
|
||||||
|
</ListItemButton>
|
||||||
|
{links.map((l, i) => (
|
||||||
|
<ListItemButton
|
||||||
|
key={i}
|
||||||
|
component={Link}
|
||||||
|
to={l.url}
|
||||||
|
onClick={toggleMiniNavOpen}
|
||||||
|
>
|
||||||
|
<ListItemText
|
||||||
|
sx={{ textAlign: "center" }}
|
||||||
|
primary={l.title.charAt(0).toUpperCase() + l.title.slice(1)}
|
||||||
|
/>
|
||||||
|
</ListItemButton>
|
||||||
|
))}
|
||||||
|
</List>
|
||||||
|
</Box>
|
||||||
|
</Drawer>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
flexGrow: 1,
|
flexGrow: 1,
|
||||||
|
@ -38,17 +79,26 @@ export default function Navbar() {
|
||||||
<Typography variant="h6" noWrap component="div">
|
<Typography variant="h6" noWrap component="div">
|
||||||
Dunemask
|
Dunemask
|
||||||
</Typography>
|
</Typography>
|
||||||
{!hideNavLinks && (
|
{miniNav && (
|
||||||
<Box sx={{ marginLeft: "auto" }}>
|
<Box sx={{ marginLeft: "auto" }}>
|
||||||
<Link to="#portfolio" style={{ textDecoration: "none" }}>
|
<IconButton
|
||||||
<Button sx={{ color: "white" }}>Portfolio</Button>
|
size="large"
|
||||||
</Link>
|
edge="start"
|
||||||
<Link to="#achievements" style={{ textDecoration: "none" }}>
|
color="inherit"
|
||||||
<Button sx={{ color: "white" }}>Achievements</Button>
|
aria-label="menu"
|
||||||
</Link>
|
onClick={toggleMiniNavOpen}
|
||||||
<Link to="#contact" style={{ textDecoration: "none" }}>
|
>
|
||||||
<Button sx={{ color: "white" }}>Contact</Button>
|
<MenuIcon />
|
||||||
</Link>
|
</IconButton>
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
{!miniNav && (
|
||||||
|
<Box sx={{ marginLeft: "auto" }}>
|
||||||
|
{links.map((l, i) => (
|
||||||
|
<Link key={i} to={l.url} style={{ textDecoration: "none" }}>
|
||||||
|
<Button sx={{ color: "white" }}>{l.title}</Button>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
</Toolbar>
|
</Toolbar>
|
||||||
|
|
14
src/Nile.jsx
|
@ -1,7 +1,10 @@
|
||||||
import { MemoryRouter } from "react-router-dom";
|
import { useEffect } from "react";
|
||||||
|
import { BrowserRouter /*MemoryRouter*/ } from "react-router-dom";
|
||||||
import Routing from "./Routing.jsx";
|
import Routing from "./Routing.jsx";
|
||||||
import Navbar from "./Navbar.jsx";
|
import Navbar from "./Navbar.jsx";
|
||||||
import { useEffect } from "react";
|
import Footer from "@components/Footer.jsx";
|
||||||
|
import "./css/common.css";
|
||||||
|
|
||||||
export default function Nile() {
|
export default function Nile() {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const loadingElements = document.getElementById("loading");
|
const loadingElements = document.getElementById("loading");
|
||||||
|
@ -9,9 +12,12 @@ export default function Nile() {
|
||||||
loadingElements.remove();
|
loadingElements.remove();
|
||||||
});
|
});
|
||||||
return (
|
return (
|
||||||
<MemoryRouter>
|
<BrowserRouter>
|
||||||
|
{/*<MemoryRouter>*/}
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<Routing />
|
<Routing />
|
||||||
</MemoryRouter>
|
<Footer />
|
||||||
|
{/*</MemoryRouter>*/}
|
||||||
|
</BrowserRouter>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Routes, Route, Navigate } from "react-router-dom";
|
import { Routes, Route, Navigate } from "react-router-dom";
|
||||||
import Toolbar from "@mui/material/Toolbar";
|
import Toolbar from "@mui/material/Toolbar";
|
||||||
import Delta from "./pages/delta/Delta.jsx";
|
import Delta from "./pages/delta/Delta.jsx";
|
||||||
// import NotFound from "./pages/handlers/NotFound.jsx";
|
import References from "./pages/references/References.jsx";
|
||||||
import { useScrollToLocation } from "./hooks.jsx";
|
import { useScrollToLocation } from "./hooks.jsx";
|
||||||
export default function Routing() {
|
export default function Routing() {
|
||||||
useScrollToLocation();
|
useScrollToLocation();
|
||||||
|
@ -10,8 +10,9 @@ export default function Routing() {
|
||||||
<Toolbar disableGutters />
|
<Toolbar disableGutters />
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Delta />} />
|
<Route path="/" element={<Delta />} />
|
||||||
{/*<Route path="/404" element={<NotFound />} /> */}
|
{/*<Route path="/references" element={<References />} />*/}
|
||||||
<Route path="*" element={<Navigate to="/" replace />} />
|
<Route path="*" element={<Navigate to="/" replace />} />
|
||||||
|
<Route path="/*" element={<Navigate to="/" replace />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
20
src/components/ContentWrapper.jsx
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import LogoBackground from "@components/LogoBackground.jsx";
|
||||||
|
export default function ContentWrapper(props) {
|
||||||
|
const { id, children, noWrapper } = props;
|
||||||
|
return (
|
||||||
|
<Box id={id}>
|
||||||
|
{!noWrapper && <LogoBackground />}
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
margin: "0 auto",
|
||||||
|
textAlign: "center",
|
||||||
|
maxWidth: 1200,
|
||||||
|
minHeight: "90vh",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
import GitHubIcon from "@mui/icons-material/GitHub";
|
import GitHubIcon from "@mui/icons-material/GitHub";
|
||||||
import LinkedInIcon from "@mui/icons-material/LinkedIn";
|
import LinkedInIcon from "@mui/icons-material/LinkedIn";
|
||||||
import "../../css/footer.css";
|
import "../css/footer.css";
|
||||||
|
|
||||||
export default function Footer() {
|
export default function Footer() {
|
||||||
return (
|
return (
|
25
src/components/LogoBackground.jsx
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
export default function LogoBackground() {
|
||||||
|
return (
|
||||||
|
<div style={{ position: "relative", display: "block" }}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
display: "flex",
|
||||||
|
zIndex: -1,
|
||||||
|
opacity: 0.2,
|
||||||
|
marginTop: -40,
|
||||||
|
marginLeft: -20,
|
||||||
|
overflow: "hidden",
|
||||||
|
justifyContent: "center",
|
||||||
|
margin: "auto",
|
||||||
|
width: "100%",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
src="/images/phx-mini.png"
|
||||||
|
style={{ minWidth: "260px", width: "50%", maxWidth: 512 }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
3
src/css/common.css
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
a:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
|
@ -20,16 +20,12 @@ footer {
|
||||||
.connections a {
|
.connections a {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 0px 4px;
|
padding: 0px 5px;
|
||||||
width: 40;
|
width: 40;
|
||||||
height: 40;
|
height: 40;
|
||||||
margin: auto;
|
margin: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.connections a:hover {
|
|
||||||
opacity: 0.7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.copyright {
|
.copyright {
|
||||||
display: flex;
|
display: flex;
|
||||||
margin: auto 0;
|
margin: auto 0;
|
||||||
|
|
|
@ -7,12 +7,14 @@ export function useScrollToLocation() {
|
||||||
const hashRef = React.useRef(hash);
|
const hashRef = React.useRef(hash);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (!hash) return;
|
if (!hash) {
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (hashRef.current !== hash) {
|
if (hashRef.current !== hash) {
|
||||||
hashRef.current = hash;
|
hashRef.current = hash;
|
||||||
scrolledRef.current = false;
|
scrolledRef.current = false;
|
||||||
}
|
}
|
||||||
navigate();
|
|
||||||
if (scrolledRef.current) return;
|
if (scrolledRef.current) return;
|
||||||
const id = hash.replace("#", "");
|
const id = hash.replace("#", "");
|
||||||
const element = document.getElementById(id);
|
const element = document.getElementById(id);
|
||||||
|
|
|
@ -1,45 +1,21 @@
|
||||||
import React from "react";
|
import React, { Suspense } from "react";
|
||||||
import Box from "@mui/material/Box";
|
|
||||||
import About from "./About.jsx";
|
import About from "./About.jsx";
|
||||||
import Projects from "./Projects.jsx";
|
import Projects from "./Projects.jsx";
|
||||||
//import Environments from "./Environments.jsx";
|
//import Environments from "./Environments.jsx";
|
||||||
import Footer from "./Footer.jsx";
|
import ContentWrapper from "@components/ContentWrapper.jsx";
|
||||||
|
|
||||||
const Skills = React.lazy(() => import("./Skills.jsx"));
|
const Skills = React.lazy(() => import("./Skills.jsx"));
|
||||||
const Social = React.lazy(() => import("./Social.jsx"));
|
const Social = React.lazy(() => import("./Social.jsx"));
|
||||||
|
|
||||||
export default function Delta() {
|
export default function Delta() {
|
||||||
return (
|
return (
|
||||||
<Box id="delta">
|
<ContentWrapper id="delta">
|
||||||
<div style={{ position: "relative", display: "block" }}>
|
<About />
|
||||||
<div
|
<Projects />
|
||||||
style={{
|
<Suspense>
|
||||||
position: "absolute",
|
|
||||||
display: "flex",
|
|
||||||
zIndex: -1,
|
|
||||||
opacity: 0.2,
|
|
||||||
marginTop: -40,
|
|
||||||
marginLeft: -20,
|
|
||||||
overflow: "hidden",
|
|
||||||
justifyContent: "center",
|
|
||||||
margin: "auto",
|
|
||||||
width: "100%",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="/images/phx-mini.png"
|
|
||||||
style={{ minWidth: "260px", width: "50%", maxWidth: 512 }}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div style={{ margin: "0 auto", textAlign: "center", maxWidth: 1200 }}>
|
|
||||||
<About />
|
|
||||||
<Projects />
|
|
||||||
<Skills />
|
<Skills />
|
||||||
{/* <Environments /> */}
|
|
||||||
<Social />
|
<Social />
|
||||||
</div>
|
</Suspense>
|
||||||
<Footer />
|
</ContentWrapper>
|
||||||
</Box>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import AccordionSummary from "@mui/material/AccordionSummary";
|
||||||
import AccordionDetails from "@mui/material/AccordionDetails";
|
import AccordionDetails from "@mui/material/AccordionDetails";
|
||||||
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
|
||||||
export default function Environments() {
|
export default function Environments() {
|
||||||
return (
|
return (
|
||||||
|
@ -19,7 +20,7 @@ export default function Environments() {
|
||||||
expandIcon={<ExpandMoreIcon />}
|
expandIcon={<ExpandMoreIcon />}
|
||||||
style={{ margin: 0 }}
|
style={{ margin: 0 }}
|
||||||
>
|
>
|
||||||
<h3>Environments</h3>
|
<Typography variant="h3">Environment</Typography>
|
||||||
</AccordionSummary>
|
</AccordionSummary>
|
||||||
<AccordionDetails>
|
<AccordionDetails>
|
||||||
<Box
|
<Box
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
import { useState } from "react";
|
||||||
import Box from "@mui/material/Box";
|
import Box from "@mui/material/Box";
|
||||||
import useMediaQuery from "@mui/material/useMediaQuery";
|
import useMediaQuery from "@mui/material/useMediaQuery";
|
||||||
import { useTheme } from "@mui/material/styles";
|
import { useTheme } from "@mui/material/styles";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
|
import { SiGitea } from "react-icons/si";
|
||||||
|
|
||||||
export default function ProjectTile({
|
export default function ProjectTile({
|
||||||
image,
|
image,
|
||||||
|
@ -10,9 +12,13 @@ export default function ProjectTile({
|
||||||
children: description,
|
children: description,
|
||||||
disableGutter,
|
disableGutter,
|
||||||
openPhoto,
|
openPhoto,
|
||||||
|
link,
|
||||||
}) {
|
}) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const smallMode = useMediaQuery(theme.breakpoints.down("md"));
|
const smallMode = useMediaQuery(theme.breakpoints.down("md"));
|
||||||
|
const [gitHov, setGitHov] = useState(false);
|
||||||
|
|
||||||
|
const toggleHover = () => setGitHov(!gitHov);
|
||||||
|
|
||||||
const imageClick = () => openPhoto(image);
|
const imageClick = () => openPhoto(image);
|
||||||
return (
|
return (
|
||||||
|
@ -48,6 +54,7 @@ export default function ProjectTile({
|
||||||
display: "inline-flex",
|
display: "inline-flex",
|
||||||
width: "100%",
|
width: "100%",
|
||||||
justifyContent: smallMode ? "center" : "",
|
justifyContent: smallMode ? "center" : "",
|
||||||
|
flexWrap: "wrap",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Typography variant="h5" component="div" style={{ fontWeight: 800 }}>
|
<Typography variant="h5" component="div" style={{ fontWeight: 800 }}>
|
||||||
|
@ -65,6 +72,25 @@ export default function ProjectTile({
|
||||||
>
|
>
|
||||||
{year}
|
{year}
|
||||||
</span>
|
</span>
|
||||||
|
<a
|
||||||
|
href={link ?? "/"}
|
||||||
|
style={{
|
||||||
|
color: "#609926",
|
||||||
|
opacity: gitHov ? ".8" : "1",
|
||||||
|
display: "flex",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SiGitea
|
||||||
|
size="1.5em"
|
||||||
|
style={{
|
||||||
|
padding: 5,
|
||||||
|
margin: "auto 0 auto 10px",
|
||||||
|
cursor: "pointer",
|
||||||
|
}}
|
||||||
|
onMouseEnter={toggleHover}
|
||||||
|
onMouseLeave={toggleHover}
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|
|
@ -24,10 +24,22 @@ export default function Projects() {
|
||||||
<div style={{ display: "flex" }}>
|
<div style={{ display: "flex" }}>
|
||||||
<h3 style={{ fontSize: "1.75rem", margin: "2rem auto" }}>Portfolio</h3>
|
<h3 style={{ fontSize: "1.75rem", margin: "2rem auto" }}>Portfolio</h3>
|
||||||
</div>
|
</div>
|
||||||
|
<ProjectTile
|
||||||
|
image="/portfolio/projects/minecluster.png"
|
||||||
|
title="Minecluster"
|
||||||
|
year="2023"
|
||||||
|
link="https://gitea.dunemask.net/elysium/minecluster"
|
||||||
|
openPhoto={openPhoto}
|
||||||
|
>
|
||||||
|
Deploy and monitor MInecraft servers in your kubernetes cluster. Easily
|
||||||
|
scales and provides a stateful experience to make managing multiple
|
||||||
|
servers simple.
|
||||||
|
</ProjectTile>
|
||||||
<ProjectTile
|
<ProjectTile
|
||||||
image="/portfolio/projects/qualiteer.png"
|
image="/portfolio/projects/qualiteer.png"
|
||||||
title="Qualiteer"
|
title="Qualiteer"
|
||||||
year="2022"
|
year="2022"
|
||||||
|
link="https://gitea.dunemask.net/elysium/qualiteer"
|
||||||
openPhoto={openPhoto}
|
openPhoto={openPhoto}
|
||||||
>
|
>
|
||||||
Manage failing tests and silence unecessary alerts. Check the state of
|
Manage failing tests and silence unecessary alerts. Check the state of
|
||||||
|
@ -38,6 +50,7 @@ export default function Projects() {
|
||||||
image="/portfolio/projects/khufu.png"
|
image="/portfolio/projects/khufu.png"
|
||||||
title="Khufu"
|
title="Khufu"
|
||||||
year="2021"
|
year="2021"
|
||||||
|
link="https://gitea.dunemask.net/elysium/khufu"
|
||||||
openPhoto={openPhoto}
|
openPhoto={openPhoto}
|
||||||
>
|
>
|
||||||
Basic cloud file management built on React class components. Simple
|
Basic cloud file management built on React class components. Simple
|
||||||
|
@ -47,12 +60,13 @@ export default function Projects() {
|
||||||
image="/portfolio/projects/codepen.png"
|
image="/portfolio/projects/codepen.png"
|
||||||
title="Codepen"
|
title="Codepen"
|
||||||
year="2020"
|
year="2020"
|
||||||
|
link="https://gitea.dunemask.net/dunemask/codepen"
|
||||||
openPhoto={openPhoto}
|
openPhoto={openPhoto}
|
||||||
>
|
>
|
||||||
Visual replication of the website{" "}
|
Visual replication of the website{" "}
|
||||||
<a
|
<a
|
||||||
href="https://codepen.io"
|
href="https://codepen.io"
|
||||||
style={{ color: "black", fontFamily: "inherit" }}
|
style={{ color: "black", fontFamily: "inherit", fontSize: "inherit" }}
|
||||||
>
|
>
|
||||||
codepen.io
|
codepen.io
|
||||||
</a>{" "}
|
</a>{" "}
|
||||||
|
@ -62,6 +76,7 @@ export default function Projects() {
|
||||||
image="/portfolio/projects/movieplayer.png"
|
image="/portfolio/projects/movieplayer.png"
|
||||||
title="Media Player"
|
title="Media Player"
|
||||||
year="2018"
|
year="2018"
|
||||||
|
link="https://legacy-21.dunemask.net/files/java/MoviePlayer.jar"
|
||||||
openPhoto={openPhoto}
|
openPhoto={openPhoto}
|
||||||
>
|
>
|
||||||
Simple media player built on javafx. Player supports media seeking,
|
Simple media player built on javafx. Player supports media seeking,
|
||||||
|
@ -71,6 +86,7 @@ export default function Projects() {
|
||||||
image="/portfolio/projects/voxelcraft.png"
|
image="/portfolio/projects/voxelcraft.png"
|
||||||
title="Voxelcraft"
|
title="Voxelcraft"
|
||||||
year="2018"
|
year="2018"
|
||||||
|
link="https://gitea.dunemask.net/dunemask/voxelcraft"
|
||||||
openPhoto={openPhoto}
|
openPhoto={openPhoto}
|
||||||
>
|
>
|
||||||
Voxel game built on a simple rendering engine written with JavaFX.
|
Voxel game built on a simple rendering engine written with JavaFX.
|
||||||
|
|
|
@ -23,7 +23,6 @@ export default function SkillPaper({
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src={src}
|
src={src}
|
||||||
style={{ width: "100%" }}
|
|
||||||
title={`${heading} Pluralsight Score`}
|
title={`${heading} Pluralsight Score`}
|
||||||
onClick={mediaClick}
|
onClick={mediaClick}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { Suspense, useState, useEffect } from "react";
|
import React, { Suspense, useState } from "react";
|
||||||
import { FaReact, FaPython, FaJava, FaHtml5 } from "react-icons/fa";
|
import { FaReact, FaPython, FaJava, FaHtml5 } from "react-icons/fa";
|
||||||
import { SiJavascript } from "react-icons/si";
|
import { SiJavascript } from "react-icons/si";
|
||||||
import Typography from "@mui/material/Typography";
|
import Typography from "@mui/material/Typography";
|
||||||
|
@ -9,14 +9,14 @@ import SkillPaper from "./SkillPaper.jsx";
|
||||||
import PhotoHover from "./PhotoHover.jsx";
|
import PhotoHover from "./PhotoHover.jsx";
|
||||||
import Skeleton from "@mui/material/Skeleton";
|
import Skeleton from "@mui/material/Skeleton";
|
||||||
|
|
||||||
const Carousel = React.lazy(() => import("react-material-ui-carousel"));
|
import "react-responsive-carousel/lib/styles/carousel.min.css";
|
||||||
|
import { Carousel } from "react-responsive-carousel";
|
||||||
|
|
||||||
export default function Skills() {
|
export default function Skills() {
|
||||||
|
const carouselInterval = 7000;
|
||||||
const [image, setImage] = useState();
|
const [image, setImage] = useState();
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [carouselInterval, setCarouselInterval] = useState(200);
|
|
||||||
|
|
||||||
useEffect(()=>{setTimeout(()=>setCarouselInterval(7000),500)},[]); // Mitigates bug where height doesn't load properly
|
|
||||||
|
|
||||||
function openPhoto(image) {
|
function openPhoto(image) {
|
||||||
setImage(image);
|
setImage(image);
|
||||||
setOpen(true);
|
setOpen(true);
|
||||||
|
@ -27,7 +27,6 @@ export default function Skills() {
|
||||||
|
|
||||||
const skills = [
|
const skills = [
|
||||||
<SkillPaper
|
<SkillPaper
|
||||||
openPhoto={openPhoto}
|
|
||||||
src="/portfolio/iq/python.png"
|
src="/portfolio/iq/python.png"
|
||||||
fgColor="white"
|
fgColor="white"
|
||||||
bgColor="#3f7ed4"
|
bgColor="#3f7ed4"
|
||||||
|
@ -35,7 +34,6 @@ export default function Skills() {
|
||||||
Icon={FaPython}
|
Icon={FaPython}
|
||||||
/>,
|
/>,
|
||||||
<SkillPaper
|
<SkillPaper
|
||||||
openPhoto={openPhoto}
|
|
||||||
src="/portfolio/iq/react.png"
|
src="/portfolio/iq/react.png"
|
||||||
fgColor="black"
|
fgColor="black"
|
||||||
bgColor="#61dafb"
|
bgColor="#61dafb"
|
||||||
|
@ -43,7 +41,6 @@ export default function Skills() {
|
||||||
Icon={FaReact}
|
Icon={FaReact}
|
||||||
/>,
|
/>,
|
||||||
<SkillPaper
|
<SkillPaper
|
||||||
openPhoto={openPhoto}
|
|
||||||
src="/portfolio/iq/java.png"
|
src="/portfolio/iq/java.png"
|
||||||
fgColor="white"
|
fgColor="white"
|
||||||
bgColor="#ea8c10 "
|
bgColor="#ea8c10 "
|
||||||
|
@ -51,7 +48,6 @@ export default function Skills() {
|
||||||
Icon={FaJava}
|
Icon={FaJava}
|
||||||
/>,
|
/>,
|
||||||
<SkillPaper
|
<SkillPaper
|
||||||
openPhoto={openPhoto}
|
|
||||||
src="/portfolio/iq/javascript.png"
|
src="/portfolio/iq/javascript.png"
|
||||||
fgColor="black"
|
fgColor="black"
|
||||||
bgColor="#edd43a"
|
bgColor="#edd43a"
|
||||||
|
@ -59,7 +55,6 @@ export default function Skills() {
|
||||||
Icon={SiJavascript}
|
Icon={SiJavascript}
|
||||||
/>,
|
/>,
|
||||||
<SkillPaper
|
<SkillPaper
|
||||||
openPhoto={openPhoto}
|
|
||||||
src="/portfolio/iq/html.png"
|
src="/portfolio/iq/html.png"
|
||||||
fgColor="white"
|
fgColor="white"
|
||||||
bgColor="#e9562f"
|
bgColor="#e9562f"
|
||||||
|
@ -68,6 +63,8 @@ export default function Skills() {
|
||||||
/>,
|
/>,
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const openSkill = (skillIndex) => openPhoto(skills[skillIndex].props.src);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box style={{ padding: 10, scrollMarginTop: "4rem" }} id="achievements">
|
<Box style={{ padding: 10, scrollMarginTop: "4rem" }} id="achievements">
|
||||||
<PhotoHover
|
<PhotoHover
|
||||||
|
@ -86,9 +83,9 @@ export default function Skills() {
|
||||||
>
|
>
|
||||||
<Box style={{ flex: 1 }}>
|
<Box style={{ flex: 1 }}>
|
||||||
<div style={{ display: "flex" }}>
|
<div style={{ display: "flex" }}>
|
||||||
<h3 style={{ fontSize: 20, margin: "2rem auto" }}>
|
<Typography variant="h2" sx={{ margin: "2rem auto", fontSize: 30 }}>
|
||||||
Certifications
|
Certifications
|
||||||
</h3>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
<Box
|
<Box
|
||||||
style={{
|
style={{
|
||||||
|
@ -163,14 +160,15 @@ export default function Skills() {
|
||||||
flexWrap: "wrap",
|
flexWrap: "wrap",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Box style={{ flex: 1, textAlign: "left" }}>
|
<Box style={{ flex: 1, textAlign: "left", width: "100%" }}>
|
||||||
<div style={{ display: "flex", paddingTop: 30 }}>
|
<div style={{ display: "flex", paddingTop: 30 }}>
|
||||||
<h3 style={{ fontSize: 20, margin: "2rem auto" }}>
|
<Typography variant="h2" sx={{ margin: "2rem auto", fontSize: 30 }}>
|
||||||
Pluralsight Scores
|
Pluralsight IQ
|
||||||
</h3>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
|
width: "100%",
|
||||||
maxWidth: 374,
|
maxWidth: 374,
|
||||||
display: "block",
|
display: "block",
|
||||||
margin: "auto",
|
margin: "auto",
|
||||||
|
@ -184,12 +182,15 @@ export default function Skills() {
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Carousel
|
<Carousel
|
||||||
|
showArrows={true}
|
||||||
|
showStatus={false}
|
||||||
|
showIndicators={false}
|
||||||
|
useKeyboardArrows={false}
|
||||||
|
showThumbs={false}
|
||||||
|
onClickItem={openSkill}
|
||||||
|
infiniteLoop={true}
|
||||||
interval={carouselInterval}
|
interval={carouselInterval}
|
||||||
navButtonsAlwaysVisible
|
autoPlay={true}
|
||||||
indicatorContainerProps={{ style: { display: "none" } }}
|
|
||||||
stopAutoPlayOnHover={true}
|
|
||||||
sx={{ height: "100%"}}
|
|
||||||
animation="slide"
|
|
||||||
>
|
>
|
||||||
{skills.map((skill, i) => (
|
{skills.map((skill, i) => (
|
||||||
<React.Fragment key={skill}>{skill}</React.Fragment>
|
<React.Fragment key={skill}>{skill}</React.Fragment>
|
||||||
|
|
|
@ -3,6 +3,7 @@ import MailIcon from "@mui/icons-material/Mail";
|
||||||
import GitHubIcon from "@mui/icons-material/GitHub";
|
import GitHubIcon from "@mui/icons-material/GitHub";
|
||||||
import LinkedInIcon from "@mui/icons-material/LinkedIn";
|
import LinkedInIcon from "@mui/icons-material/LinkedIn";
|
||||||
import { FaGitlab } from "react-icons/fa";
|
import { FaGitlab } from "react-icons/fa";
|
||||||
|
import { SiGitea } from "react-icons/si";
|
||||||
|
|
||||||
const socialLinks = [
|
const socialLinks = [
|
||||||
{
|
{
|
||||||
|
@ -13,9 +14,13 @@ const socialLinks = [
|
||||||
url: "mailto: elijahglennparker@outlook.com",
|
url: "mailto: elijahglennparker@outlook.com",
|
||||||
icon: <MailIcon />,
|
icon: <MailIcon />,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
url: "https://gitea.dunemask.net/dunemask",
|
||||||
|
icon: <SiGitea size="1.5rem" />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
url: "https://gitlab.com/dunemask",
|
url: "https://gitlab.com/dunemask",
|
||||||
icon: <FaGitlab />,
|
icon: <FaGitlab size="1.5rem" />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
url: "https://github.com/dunemask",
|
url: "https://github.com/dunemask",
|
||||||
|
@ -33,7 +38,7 @@ export default function Social() {
|
||||||
<a
|
<a
|
||||||
key={i}
|
key={i}
|
||||||
href={v.url}
|
href={v.url}
|
||||||
style={{ margin: "auto 0", color: "black", padding: "10px" }}
|
style={{ margin: "auto 0", color: "black", padding: "0px 5px" }}
|
||||||
>
|
>
|
||||||
{v.icon}
|
{v.icon}
|
||||||
</a>
|
</a>
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
export default function NotFound() {
|
|
||||||
return <h1>Page not found!</h1>;
|
|
||||||
}
|
|
13
src/pages/references/References.jsx
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
import React from "react";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import ContentWrapper from "../../components/ContentWrapper";
|
||||||
|
|
||||||
|
export default function References() {
|
||||||
|
return (
|
||||||
|
<Box id="delta">
|
||||||
|
<ContentWrapper>
|
||||||
|
<h1>References</h1>
|
||||||
|
</ContentWrapper>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
}
|
|
@ -17,6 +17,8 @@ export default () => {
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
"@": path.resolve("./src"),
|
"@": path.resolve("./src"),
|
||||||
|
"@components": path.resolve("./src/components"),
|
||||||
|
"@images": path.resolve("./src/images")
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|