feat: add layout

This commit is contained in:
Paul Pan 2024-02-21 22:11:28 +08:00
parent 08621df30b
commit e5bb8bea6e
14 changed files with 1807 additions and 135 deletions

View File

@ -15,10 +15,17 @@
"prepare": "husky" "prepare": "husky"
}, },
"dependencies": { "dependencies": {
"@chakra-ui/icons": "^2.1.1",
"@chakra-ui/react": "^2.8.2",
"@emotion/react": "^11.11.3",
"@emotion/styled": "^11.11.0",
"@reduxjs/toolkit": "^2.2.1", "@reduxjs/toolkit": "^2.2.1",
"framer-motion": "^11.0.5",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-redux": "^9.1.0" "react-icons": "^5.0.1",
"react-redux": "^9.1.0",
"react-router-dom": "^6.22.1"
}, },
"devDependencies": { "devDependencies": {
"@types/react": "^18.2.57", "@types/react": "^18.2.57",

File diff suppressed because it is too large Load Diff

1
public/vite.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

@ -1,39 +0,0 @@
.App {
text-align: center;
}
.App-logo {
height: 40vmin;
pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
.App-logo {
animation: App-logo-float infinite 3s ease-in-out;
}
}
.App-header {
min-height: 100vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
}
.App-link {
color: rgb(112, 76, 182);
}
@keyframes App-logo-float {
0% {
transform: translateY(0);
}
50% {
transform: translateY(10px);
}
100% {
transform: translateY(0px);
}
}

View File

@ -1,48 +0,0 @@
import "./App.css";
import { Counter } from "./features/counter/Counter";
import { Quotes } from "./features/quotes/Quotes";
import logo from "./logo.svg";
const App = () => {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<Counter />
<p>
Edit <code>src/App.tsx</code> and save to reload.
</p>
<Quotes />
<span>
<span>Learn </span>
<a className="App-link" href="https://reactjs.org" target="_blank" rel="noopener noreferrer">
React
</a>
<span>, </span>
<a className="App-link" href="https://redux.js.org" target="_blank" rel="noopener noreferrer">
Redux
</a>
<span>, </span>
<a
className="App-link"
href="https://redux-toolkit.js.org"
target="_blank"
rel="noopener noreferrer"
>
Redux Toolkit
</a>
<span>, </span>
<a className="App-link" href="https://react-redux.js.org" target="_blank" rel="noopener noreferrer">
React Redux
</a>
,<span> and </span>
<a className="App-link" href="https://reselect.js.org" target="_blank" rel="noopener noreferrer">
Reselect
</a>
</span>
</header>
</div>
);
};
export default App;

30
src/components/Footer.tsx Normal file
View File

@ -0,0 +1,30 @@
import { Box, Flex, Text, useColorModeValue } from "@chakra-ui/react";
export default function Footer() {
return (
<Box w="100%" bg={useColorModeValue("gray.50", "gray.900")} color={useColorModeValue("gray.700", "gray.200")}>
<Flex
align="center"
pt="8"
_before={{
content: '""',
borderBottom: "1px solid",
borderColor: useColorModeValue("gray.200", "gray.700"),
flexGrow: 1,
mr: 8,
}}
_after={{
content: '""',
borderBottom: "1px solid",
borderColor: useColorModeValue("gray.200", "gray.700"),
flexGrow: 1,
ml: 8,
}}
>
<Text fontSize="sm" textAlign="center">
&copy; {new Date().getFullYear()} WOJ Created by WHUPRJ.
</Text>
</Flex>
</Box>
);
}

58
src/components/Header.tsx Normal file
View File

@ -0,0 +1,58 @@
import {
Avatar,
Box,
Button,
Center,
Flex,
Link as ChakraLink,
Menu,
MenuButton,
MenuDivider,
MenuItem,
MenuList,
Stack,
Text,
useColorMode,
useColorModeValue,
} from "@chakra-ui/react";
import { Link as ReactRouterLink } from "react-router-dom";
import { MoonIcon, SunIcon } from "@chakra-ui/icons";
export const Header = () => {
const { colorMode, toggleColorMode } = useColorMode();
return (
<Box width="100%" bg={useColorModeValue("gray.100", "gray.900")} px={4}>
<Flex h={16} alignItems="center" justifyContent="space-between">
<Box>
<ChakraLink as={ReactRouterLink} to="/home">
<Text as="b" fontSize="lg">
Woo Online Judge
</Text>
</ChakraLink>
</Box>
<Flex alignItems="center">
<Stack direction="row" spacing={7}>
<Button onClick={toggleColorMode}>{colorMode === "light" ? <MoonIcon /> : <SunIcon />}</Button>
<Menu>
<MenuButton as={Button} rounded="full" variant="link" cursor="pointer" minW={0}>
<Avatar size="sm" src={"https://api.dicebear.com/7.x/shapes/svg"} />
</MenuButton>
<MenuList alignItems="center">
<Center p={2}>
<Avatar size="2xl" src={"https://api.dicebear.com/7.x/shapes/svg"} />
</Center>
<Center>
<p>Username</p>
</Center>
<MenuDivider />
<MenuItem>Account Settings</MenuItem>
<MenuItem>Logout</MenuItem>
</MenuList>
</Menu>
</Stack>
</Flex>
</Flex>
</Box>
);
};

View File

@ -1,11 +0,0 @@
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans",
"Droid Sans", "Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
}

View File

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><g fill="#764ABC"><path d="M65.6 65.4c2.9-.3 5.1-2.8 5-5.8-.1-3-2.6-5.4-5.6-5.4h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 1.5.7 2.8 1.6 3.7-3.4 6.7-8.6 11.6-16.4 15.7-5.3 2.8-10.8 3.8-16.3 3.1-4.5-.6-8-2.6-10.2-5.9-3.2-4.9-3.5-10.2-.8-15.5 1.9-3.8 4.9-6.6 6.8-8-.4-1.3-1-3.5-1.3-5.1-14.5 10.5-13 24.7-8.6 31.4 3.3 5 10 8.1 17.4 8.1 2 0 4-.2 6-.7 12.8-2.5 22.5-10.1 28-21.4z"/><path d="M83.2 53c-7.6-8.9-18.8-13.8-31.6-13.8H50c-.9-1.8-2.8-3-4.9-3h-.2c-3.1.1-5.5 2.7-5.4 5.8.1 3 2.6 5.4 5.6 5.4h.2c2.2-.1 4.1-1.5 4.9-3.4H52c7.6 0 14.8 2.2 21.3 6.5 5 3.3 8.6 7.6 10.6 12.8 1.7 4.2 1.6 8.3-.2 11.8-2.8 5.3-7.5 8.2-13.7 8.2-4 0-7.8-1.2-9.8-2.1-1.1 1-3.1 2.6-4.5 3.6 4.3 2 8.7 3.1 12.9 3.1 9.6 0 16.7-5.3 19.4-10.6 2.9-5.8 2.7-15.8-4.8-24.3z"/><path d="M32.4 67.1c.1 3 2.6 5.4 5.6 5.4h.2c3.1-.1 5.5-2.7 5.4-5.8-.1-3-2.6-5.4-5.6-5.4h-.2c-.2 0-.5 0-.7.1-4.1-6.8-5.8-14.2-5.2-22.2.4-6 2.4-11.2 5.9-15.5 2.9-3.7 8.5-5.5 12.3-5.6 10.6-.2 15.1 13 15.4 18.3 1.3.3 3.5 1 5 1.5-1.2-16.2-11.2-24.6-20.8-24.6-9 0-17.3 6.5-20.6 16.1-4.6 12.8-1.6 25.1 4 34.8-.5.7-.8 1.8-.7 2.9z"/></g></svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -1,10 +1,11 @@
import React from "react"; import React from "react";
import { createRoot } from "react-dom/client"; import { createRoot } from "react-dom/client";
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import { Provider } from "react-redux"; import { Provider } from "react-redux";
import { ChakraProvider } from "@chakra-ui/react";
import App from "./App";
import { store } from "./app/store"; import { store } from "./app/store";
import "./index.css"; import { router } from "./routes";
const container = document.getElementById("root"); const container = document.getElementById("root");
@ -14,7 +15,9 @@ if (container) {
root.render( root.render(
<React.StrictMode> <React.StrictMode>
<Provider store={store}> <Provider store={store}>
<App /> <ChakraProvider>
<RouterProvider router={createBrowserRouter(router)} />
</ChakraProvider>
</Provider> </Provider>
</React.StrictMode>, </React.StrictMode>,
); );

47
src/pages/ErrorPage.tsx Normal file
View File

@ -0,0 +1,47 @@
import { isRouteErrorResponse, useNavigate, useRouteError } from "react-router-dom";
import { Alert, AlertDescription, AlertIcon, AlertTitle, Button, ButtonGroup } from "@chakra-ui/react";
export const ErrorPage = () => {
const navigate = useNavigate();
const error = useRouteError();
const convertError = (error: unknown): string => {
if (isRouteErrorResponse(error)) {
return `${error.status} ${error.statusText}`;
} else if (error instanceof Error) {
return error.message;
} else if (typeof error === "string") {
return error;
} else {
console.error(error);
return "Unknown error";
}
};
return (
<Alert
status="error"
variant="subtle"
flexDirection="column"
alignItems="center"
justifyContent="center"
textAlign="center"
height="200px"
>
<AlertIcon boxSize="40px" mr={0} />
<AlertTitle mt={4} mb={1} fontSize="lg">
{" "}
Sorry, an unexpected error has occurred.{" "}
</AlertTitle>
<AlertDescription maxWidth="sm">{convertError(error)}</AlertDescription>
<ButtonGroup>
<Button variant="solid" colorScheme="orange" onClick={() => navigate(-1)}>
Previous Page
</Button>
<Button variant="solid" colorScheme="orange" onClick={() => navigate("/")}>
Home
</Button>
</ButtonGroup>
</Alert>
);
};

9
src/pages/HomePage.tsx Normal file
View File

@ -0,0 +1,9 @@
import { Container, Heading } from "@chakra-ui/react";
export const HomePage = () => {
return (
<Container maxW="container.lg">
<Heading>Hello World!</Heading>
</Container>
);
};

27
src/pages/Root.tsx Normal file
View File

@ -0,0 +1,27 @@
import React from "react";
import { Box, Flex, SkeletonCircle, SkeletonText } from "@chakra-ui/react";
import { Outlet } from "react-router-dom";
import Footer from "../components/Footer";
import { Header } from "../components/Header";
const SkeletonPage = () => (
<Box padding="6" boxShadow="lg" bg="white">
<SkeletonCircle size="10" />
<SkeletonText mt="4" noOfLines={6} spacing="4" skeletonHeight="2" />
</Box>
);
export const Root = () => (
<Flex direction="column" align="center" maxW={{ xl: "1200px" }} m="0 auto">
<Header />
<Box width="100%" flex="1">
<React.Suspense fallback={<SkeletonPage />}>
<Outlet />
</React.Suspense>
</Box>
<Footer />
</Flex>
);

24
src/routes.tsx Normal file
View File

@ -0,0 +1,24 @@
import type { RouteObject } from "react-router-dom";
import { Root } from "./pages/Root";
import { ErrorPage } from "./pages/ErrorPage";
import { HomePage } from "./pages/HomePage";
export const router: RouteObject[] = [
{
path: "/",
element: <Root />,
errorElement: <ErrorPage />,
children: [
{
errorElement: <ErrorPage />,
children: [
{
index: true,
element: <HomePage />,
},
],
},
],
},
];