diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 13d40395d..2a5f40046 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,6 +2,7 @@ "recommendations": [ "esbenp.prettier-vscode", "dbaeumer.vscode-eslint", - "bradlc.vscode-tailwindcss" + "bradlc.vscode-tailwindcss", + "gruntfuggly.todo-tree" ] -} +} \ No newline at end of file diff --git a/index.html b/index.html index 658eecbe9..bc4cd407b 100644 --- a/index.html +++ b/index.html @@ -1,29 +1,28 @@ - - - - - - - - - - - - Online Judge Lab - - - - -
- - - + + + \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 3da4a0cb4..19b07ec9c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,8 +1,8 @@ -import PageRouter from "./routes/Router"; -import "./App.css"; +import Router from "@/Router"; +import "@/App.css"; function App() { - return ; + return ; } export default App; diff --git a/src/index.css b/src/Index.css similarity index 100% rename from src/index.css rename to src/Index.css diff --git a/src/index.tsx b/src/Index.tsx similarity index 51% rename from src/index.tsx rename to src/Index.tsx index cbfedbf69..b0a47fa94 100644 --- a/src/index.tsx +++ b/src/Index.tsx @@ -1,41 +1,40 @@ -import App from "./App"; import React from "react"; import ReactDOM from "react-dom/client"; -import reportWebVitals from "./reportWebVitals"; -import "./i18n/i18n"; -import "./index.css"; -import { getMode, isGhPages, isMock } from "./utils/environment"; +import reportWebVitals from "@/reportWebVitals"; import { Provider } from "react-redux"; -import store from "./store"; +import App from "@/App"; +import "@/i18n/module"; +import "@/Index.css"; +import { getViteMode, isGhPages, isMock } from "@/utils/environment"; +import store from "@/store"; -console.log("Running in:", getMode()); +console.log("Running in:", getViteMode()); -async function enableMocking() { +async function enableMockService() { if (!isMock() && !isGhPages()) { return; } - const { worker } = await import("./mocks/server"); + const { mockServiceWorker } = await import("./mocks/worker"); - if (isMock()) { - return worker.start({ - onUnhandledRequest: "bypass", - }); - } + let workerURL = "/mockServiceWorker.js"; if (isGhPages()) { - return worker.start({ - onUnhandledRequest: "bypass", - serviceWorker: { - url: "/oj-lab-front/mockServiceWorker.js", - }, - }); + workerURL = "/oj-lab-front/mockServiceWorker.js"; } + + return mockServiceWorker.start({ + onUnhandledRequest: "bypass", + serviceWorker: { + url: workerURL, + }, + }); } -enableMocking().then(() => { +enableMockService().then(() => { const root = ReactDOM.createRoot( document.getElementById("root") as HTMLElement, ); + root.render( diff --git a/src/routes/Router.tsx b/src/Router.tsx similarity index 69% rename from src/routes/Router.tsx rename to src/Router.tsx index 912fbde7b..f6a9cd9f9 100644 --- a/src/routes/Router.tsx +++ b/src/Router.tsx @@ -1,20 +1,16 @@ import React, { Suspense, lazy } from "react"; import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"; -import Layout from "../layouts/Layout"; +import Layout from "@/layouts/Layout"; -const Problem = lazy(() => import("../pages/Problem")); -const AdminProblemList = lazy( - () => import("../pages/admin-dashboard/ProblemList"), -); -const AdminCreateProblem = lazy( - () => import("../pages/admin-dashboard/CreateProblem"), -); -const ProblemList = lazy(() => import("../pages/ProblemList")); -const JudgeList = lazy(() => import("../pages/JudgeList")); -const Judge = lazy(() => import("../pages/Judge")); -const Login = lazy(() => import("../pages/Login")); +const Problem = lazy(() => import("@/pages/Problem")); +const AdminProblemList = lazy(() => import("@/pages/admin/ProblemList")); +const AdminCreateProblem = lazy(() => import("@/pages/admin/CreateProblem")); +const ProblemList = lazy(() => import("@/pages/ProblemList")); +const JudgeList = lazy(() => import("@/pages/JudgeList")); +const Judge = lazy(() => import("@/pages/Judge")); +const Login = lazy(() => import("@/pages/Login")); -const PageRouter: React.FC = () => { +const Router: React.FC = () => { return ( {/* TODO: Perf loading view */} @@ -46,4 +42,4 @@ const PageRouter: React.FC = () => { ); }; -export default PageRouter; +export default Router; diff --git a/src/api/auth.ts b/src/apis/auth.ts similarity index 64% rename from src/api/auth.ts rename to src/apis/auth.ts index 84980f7ce..ca32f1a1b 100644 --- a/src/api/auth.ts +++ b/src/apis/auth.ts @@ -1,8 +1,8 @@ -import { UserServiceModel } from "@/typings/user"; -import { client } from "./client"; +import * as UserServiceModel from "@/models/service/user"; +import { axiosClient } from "@/utils/axiosClient"; export async function postLogin(account: string, password: string) { - let res = await client.post("/api/v1/user/login", { + let res = await axiosClient.post("/api/v1/user/login", { account: account, password: password, }); @@ -13,7 +13,7 @@ export async function postLogin(account: string, password: string) { } export async function postSignOut() { - let res = await client.post("/api/v1/user/logout"); + let res = await axiosClient.post("/api/v1/user/logout"); if (res.status !== 200) { throw Error("failed to sign out"); } @@ -21,7 +21,9 @@ export async function postSignOut() { } export async function getCurrentUser(): Promise { - let res = await client.get("/api/v1/user/current"); + let res = await axiosClient.get( + "/api/v1/user/current", + ); if (res.status !== 200) { throw Error("failed to get current user"); } diff --git a/src/api/event.ts b/src/apis/event.ts similarity index 100% rename from src/api/event.ts rename to src/apis/event.ts diff --git a/src/api/judge.ts b/src/apis/judge.ts similarity index 74% rename from src/api/judge.ts rename to src/apis/judge.ts index c69da57db..6eaed7c1c 100644 --- a/src/api/judge.ts +++ b/src/apis/judge.ts @@ -1,5 +1,5 @@ -import { JudgeServiceModel } from "../typings/judge"; -import { client } from "./client"; +import * as JudgeServiceModel from "@/models/service/judge"; +import { axiosClient } from "@/utils/axiosClient"; export namespace JudgeService { export async function postJudge( @@ -13,7 +13,7 @@ export namespace JudgeService { }; let data = JSON.stringify(body); - let res = await client.post( + let res = await axiosClient.post( `/api/v1/problem/${slug}/judge`, data, ); @@ -25,7 +25,7 @@ export namespace JudgeService { } export async function getJudgeList() { - let res = await client.get<{ + let res = await axiosClient.get<{ total: number; list: JudgeServiceModel.JudgeInfo[]; }>(`/api/v1/judge`); @@ -35,7 +35,7 @@ export namespace JudgeService { return res.data; } export async function getJudge(uid: string) { - let res = await client.get( + let res = await axiosClient.get( `/api/v1/judge/${uid}`, ); if (res.status !== 200) { diff --git a/src/api/problem.ts b/src/apis/problem.ts similarity index 78% rename from src/api/problem.ts rename to src/apis/problem.ts index 552a4998b..44863ceac 100644 --- a/src/api/problem.ts +++ b/src/apis/problem.ts @@ -1,11 +1,10 @@ -import { ProblemServiceModel } from "../typings/problem"; -import { client } from "./client"; - +import * as ProblemServiceModel from "@/models/service/problem"; +import { axiosClient } from "@/utils/axiosClient"; export namespace ProblemService { export async function getProblem( slug: string, ): Promise { - let res = await client.get( + let res = await axiosClient.get( `/api/v1/problem/${slug}`, ); if (res.status !== 200) { @@ -17,7 +16,7 @@ export namespace ProblemService { export async function putProblem( problem: ProblemServiceModel.Problem, ): Promise { - let res = await client.put( + let res = await axiosClient.put( `/api/v1/problem`, problem, ); @@ -38,7 +37,7 @@ export namespace ProblemService { limit = limit || 10; offset = offset || 0; - let res = await client.get<{ + let res = await axiosClient.get<{ total: number; list: ProblemServiceModel.ProblemInfo[]; }>(`/api/v1/problem`, { @@ -59,7 +58,7 @@ export namespace ProblemService { ): Promise { let formData = new FormData(); formData.append("file", packageFile); - let res = await client.put( + let res = await axiosClient.put( `/api/v1/problem/${problemSlug}/package`, formData, ); @@ -72,7 +71,7 @@ export namespace ProblemService { export async function checkProblemSlug( slug: string, ): Promise<{ valid: boolean }> { - let res = await client.get<{ valid: boolean }>( + let res = await axiosClient.get<{ valid: boolean }>( `/api/v1/problem/${slug}/check`, ); @@ -80,7 +79,7 @@ export namespace ProblemService { } export async function deleteProblem(slug: string) { - let res = await client.delete(`/api/v1/problem/${slug}`); + let res = await axiosClient.delete(`/api/v1/problem/${slug}`); if (res.status !== 200) { throw Error("failed to delete problem"); } diff --git a/src/components/JudgeVerdictTable.tsx b/src/components/JudgeVerdictTable.tsx index 5526940a0..3a779990d 100644 --- a/src/components/JudgeVerdictTable.tsx +++ b/src/components/JudgeVerdictTable.tsx @@ -1,5 +1,5 @@ import { joinClasses } from "@/utils/common"; -import { JudgeModel } from "../typings/judge"; +import { JudgeModel } from "../models/service/judge"; const columns = [ { name: "Result", uid: "result" }, diff --git a/src/layouts/Breadcrumbs.tsx b/src/components/PageBreadcrumbs.tsx similarity index 92% rename from src/layouts/Breadcrumbs.tsx rename to src/components/PageBreadcrumbs.tsx index 190226c2b..a1f4aef0d 100644 --- a/src/layouts/Breadcrumbs.tsx +++ b/src/components/PageBreadcrumbs.tsx @@ -1,6 +1,6 @@ import { useLocation, useNavigate } from "react-router-dom"; -const Breadcrumbs: React.FC = () => { +const PageBreadcrumbs: React.FC = () => { const location = useLocation(); const navigate = useNavigate(); @@ -33,4 +33,4 @@ const Breadcrumbs: React.FC = () => { ); }; -export default Breadcrumbs; +export default PageBreadcrumbs; diff --git a/src/components/icons/OJLabIcon.tsx b/src/components/icons/OJLabIcon.tsx new file mode 100644 index 000000000..de63318f0 --- /dev/null +++ b/src/components/icons/OJLabIcon.tsx @@ -0,0 +1,11 @@ +import { FC } from "react"; + +const OJLabIconPath = `${import.meta.env.BASE_URL}images/oj-lab-icon.svg`; + +const OJLabIcon: FC = () => { + return ( + OJ Lab + ); +}; + +export default OJLabIcon; diff --git a/src/components/judge/JudgeDetail.tsx b/src/components/judge/JudgeDetail.tsx index 6ebd09a0f..c4d663bd9 100644 --- a/src/components/judge/JudgeDetail.tsx +++ b/src/components/judge/JudgeDetail.tsx @@ -1,6 +1,6 @@ import React, { Suspense, lazy, useState } from "react"; import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism"; -import { JudgeServiceModel } from "../../typings/judge"; +import * as JudgeServiceModel from "../../models/service/judge"; import { CopyToClipboard } from "react-copy-to-clipboard"; import DocumentDuplicateIcon from "@heroicons/react/24/outline/DocumentDuplicateIcon"; import JudgeTable from "./JudgeTable"; diff --git a/src/components/judge/JudgeTable.tsx b/src/components/judge/JudgeTable.tsx index e9cf75016..cfb2007f9 100644 --- a/src/components/judge/JudgeTable.tsx +++ b/src/components/judge/JudgeTable.tsx @@ -1,10 +1,10 @@ import React from "react"; import { useNavigate } from "react-router-dom"; -import { joinClasses } from "../../utils/common"; -import { JudgeServiceModel } from "../../typings/judge"; -import BrandCPPIcon from "../icons/tabler/BrandCPPIcon"; -import BrandPythonIcon from "../icons/tabler/BrandPythonIcon"; -import { getGravatarUrl } from "@/utils/avatar_url"; +import { joinClasses } from "@/utils/common"; +import * as JudgeServiceModel from "@/models/service/judge"; +import BrandCPPIcon from "@/components/icons/tabler/BrandCPPIcon"; +import BrandPythonIcon from "@/components/icons/tabler/BrandPythonIcon"; +import { getGravatarUrl } from "@/utils/avatarURL"; const columns = [ { name: "User", uid: "user" }, diff --git a/src/components/i18n/LanguageMenu.tsx b/src/components/menu/LanguageMenu.tsx similarity index 94% rename from src/components/i18n/LanguageMenu.tsx rename to src/components/menu/LanguageMenu.tsx index b91d3f484..ad6d6751d 100644 --- a/src/components/i18n/LanguageMenu.tsx +++ b/src/components/menu/LanguageMenu.tsx @@ -1,7 +1,7 @@ import { useTranslation } from "react-i18next"; -import { joinClasses } from "../../utils/common"; +import { joinClasses } from "@/utils/common"; import { changeLanguage } from "i18next"; -import { LANGUAGE_SELECTIONS } from "../../i18n/i18n"; +import { LANGUAGE_SELECTIONS } from "@/i18n/module"; import React from "react"; import LanguageIcon from "@/components/icons/tabler/LanguageIcon"; diff --git a/src/components/menu/PageMenu.tsx b/src/components/menu/PageMenu.tsx new file mode 100644 index 000000000..f78a3b45d --- /dev/null +++ b/src/components/menu/PageMenu.tsx @@ -0,0 +1,49 @@ +import { isCurrentPath } from "@/utils/window"; +import { FC } from "react"; +import { useTranslation } from "react-i18next"; +import { useNavigate } from "react-router-dom"; + +export interface PageMenuItem { + name: string; + href: string; + icon: JSX.Element; +} + +export interface PageMenuSection { + title: string; + items: PageMenuItem[]; +} + +export interface PageMenuProps { + sections: PageMenuSection[]; +} + +export const PageMenu: FC = (props) => { + const { t } = useTranslation(); + const navigate = useNavigate(); + + return ( +
    + {props.sections.map((section) => ( + <> +
  • + {t(section.title)} +
  • + {section.items.map((item) => ( +
  • +
    navigate(item.href)} + className={isCurrentPath(item.href) ? "active" : ""} + > + {item.icon} +

    {t(item.name)}

    +
    +
  • + ))} + + ))} +
+ ); +}; + +export default PageMenu; diff --git a/src/layouts/UserMenu.tsx b/src/components/menu/UserMenu.tsx similarity index 98% rename from src/layouts/UserMenu.tsx rename to src/components/menu/UserMenu.tsx index 42b719164..5d5f8c62c 100644 --- a/src/layouts/UserMenu.tsx +++ b/src/components/menu/UserMenu.tsx @@ -1,5 +1,5 @@ import { useNavigate } from "react-router-dom"; -import { joinClasses } from "../utils/common"; +import { joinClasses } from "../../utils/common"; import React from "react"; import { UserState, useUser } from "@/hooks/user"; import { useCookies } from "react-cookie"; diff --git a/src/components/problem/ProblemTable.tsx b/src/components/problem/ProblemTable.tsx index afc3d8333..8e8a08ef8 100644 --- a/src/components/problem/ProblemTable.tsx +++ b/src/components/problem/ProblemTable.tsx @@ -1,7 +1,7 @@ -import { ProblemServiceModel } from "../../typings/problem"; +import * as ProblemServiceModel from "@/models/service/problem"; import React from "react"; import { useNavigate } from "react-router-dom"; -import { ProblemService } from "@/api/problem"; +import { ProblemService } from "@/apis/problem"; import TrashIcon from "../icons/tabler/TrashIcon"; import PencilIcon from "../icons/tabler/PencilIcon"; import { joinClasses } from "@/utils/common"; diff --git a/src/hooks/event.ts b/src/hooks/event.ts index 3a9d3a678..5eec6a52f 100644 --- a/src/hooks/event.ts +++ b/src/hooks/event.ts @@ -1,5 +1,5 @@ import { useLayoutEffect } from "react"; -import { EVENT_URL } from "../api/event"; +import { EVENT_URL } from "../apis/event"; import { isGhPages, isMock } from "../utils/environment"; export const useEvent = () => { diff --git a/src/hooks/judge.ts b/src/hooks/judge.ts index af17a6f0b..330beb0b1 100644 --- a/src/hooks/judge.ts +++ b/src/hooks/judge.ts @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; -import { JudgeServiceModel } from "../typings/judge"; -import { JudgeService } from "../api/judge"; +import * as JudgeServiceModel from "@/models/service/judge"; +import { JudgeService } from "../apis/judge"; export const useJudge = (uid: string) => { const [judge, setJudge] = useState(); diff --git a/src/hooks/problem.ts b/src/hooks/problem.ts index cd4e522b2..8b1cc552d 100644 --- a/src/hooks/problem.ts +++ b/src/hooks/problem.ts @@ -1,6 +1,6 @@ import { useEffect, useRef, useState } from "react"; -import { ProblemServiceModel } from "../typings/problem"; -import { ProblemService } from "../api/problem"; +import * as ProblemServiceModel from "@/models/service/problem"; +import { ProblemService } from "@/apis/problem"; export const useProblem = (slug: string, fallback?: () => void) => { const [problem, setProblem] = useState( diff --git a/src/hooks/user.ts b/src/hooks/user.ts index ffc7b07c2..0cb2b859f 100644 --- a/src/hooks/user.ts +++ b/src/hooks/user.ts @@ -1,5 +1,5 @@ -import { getCurrentUser } from "@/api/auth"; -import { UserServiceModel } from "@/typings/user"; +import { getCurrentUser } from "@/apis/auth"; +import * as UserServiceModel from "@/models/service/user"; import { useEffect, useState } from "react"; export enum UserState { diff --git a/src/i18n/i18n.ts b/src/i18n/module.ts similarity index 90% rename from src/i18n/i18n.ts rename to src/i18n/module.ts index 92d455414..e48b35c51 100644 --- a/src/i18n/i18n.ts +++ b/src/i18n/module.ts @@ -1,7 +1,7 @@ import i18n from "i18next"; import { initReactI18next } from "react-i18next"; -import EN_US_TRANSLATIONS from "./en_US"; -import ZH_CN_TRANSLATIONS from "./zh_CN"; +import EN_US_TRANSLATIONS from "./resources/en_US"; +import ZH_CN_TRANSLATIONS from "./resources/zh_CN"; export const LANGUAGE_SELECTIONS = [ { value: "en_US", label: "English (US)" }, diff --git a/src/i18n/en_US.ts b/src/i18n/resources/en_US.ts similarity index 100% rename from src/i18n/en_US.ts rename to src/i18n/resources/en_US.ts diff --git a/src/i18n/zh_CN.ts b/src/i18n/resources/zh_CN.ts similarity index 100% rename from src/i18n/zh_CN.ts rename to src/i18n/resources/zh_CN.ts diff --git a/src/layouts/Drawer.tsx b/src/layouts/Drawer.tsx index e21ca7b3c..07bf412eb 100644 --- a/src/layouts/Drawer.tsx +++ b/src/layouts/Drawer.tsx @@ -2,9 +2,64 @@ import Menu3Icon from "@/components/icons/tabler/Menu3Icon"; import { joinClasses } from "@/utils/common"; import { LargeWindowWidth } from "@/utils/const"; import React, { useEffect } from "react"; -import Menu from "./Menu"; +import OJLabIcon from "@/components/icons/OJLabIcon"; +import FileTextIcon from "@/components/icons/tabler/FileTextIcon"; +import ActivityIcon from "@/components/icons/tabler/ActivityIcon"; +import AwardIcon from "@/components/icons/tabler/AwardIcon"; +import PackageIcon from "@/components/icons/tabler/PackageIcon"; +import UsersIcon from "@/components/icons/tabler/UsersIcon"; +import { useUser } from "@/hooks/user"; +import PageMenu, { + PageMenuItem, + PageMenuSection, +} from "@/components/menu/PageMenu"; -const OJLabIconPath = `${import.meta.env.BASE_URL}images/oj-lab-icon.svg`; +const APPNavigation: PageMenuItem[] = [ + { + name: "Problems", + href: "/problem", + icon: , + }, + { + name: "Judges", + href: "/judge", + icon: , + }, + { + name: "Rank", + href: "/rank", + icon: , + }, +]; + +const AdminNavigation: PageMenuItem[] = [ + { + name: "Problem Packages", + href: "/admin/problem", + icon: , + }, + { + name: "User Management", + href: "/admin/user", + icon: , + }, +]; + +function getPageMenuSections(isAdmin: boolean): PageMenuSection[] { + let menuSections = [ + { + title: "App", + items: APPNavigation, + }, + ]; + if (isAdmin) { + menuSections.push({ + title: "Admin", + items: AdminNavigation, + }); + } + return menuSections; +} export interface DrawerProps { children?: React.ReactNode; @@ -13,6 +68,8 @@ export interface DrawerProps { export const Drawer: React.FC = (props) => { const [open, setOpen] = React.useState(true); + const { getUser } = useUser(); + const isAdmin = getUser()?.roles?.includes("admin") ?? false; useEffect(() => { localStorage.setItem("isDrawerOpen", open ? "true" : "false"); @@ -54,14 +111,10 @@ export const Drawer: React.FC = (props) => { />
- OJ Lab +

OJ LAB

- +
diff --git a/src/layouts/Layout.tsx b/src/layouts/Layout.tsx index 878a25a3e..efe8e538e 100644 --- a/src/layouts/Layout.tsx +++ b/src/layouts/Layout.tsx @@ -1,7 +1,7 @@ import { Outlet } from "react-router-dom"; import Header from "./Navbar"; import Drawer from "./Drawer"; -import Breadcrumbs from "@/layouts/Breadcrumbs"; +import PageBreadcrumbs from "@/components/PageBreadcrumbs"; export interface LayoutProps { children?: React.ReactNode; @@ -14,7 +14,7 @@ const Layout: React.FC = (props) => {
- + {props.children} {!props.children && }
diff --git a/src/layouts/Menu.tsx b/src/layouts/Menu.tsx deleted file mode 100644 index b695580bf..000000000 --- a/src/layouts/Menu.tsx +++ /dev/null @@ -1,66 +0,0 @@ -import ActivityIcon from "@/components/icons/tabler/ActivityIcon"; -import AwardIcon from "@/components/icons/tabler/AwardIcon"; -import FileTextIcon from "@/components/icons/tabler/FileTextIcon"; -import PackageIcon from "@/components/icons/tabler/PackageIcon"; -import UsersIcon from "@/components/icons/tabler/UsersIcon"; -import { useUser } from "@/hooks/user"; -import { useTranslation } from "react-i18next"; -import { useNavigate } from "react-router-dom"; - -const navigation = [ - { name: "Problems", href: "/problem", icon: FileTextIcon }, - { name: "Judges", href: "/judge", icon: ActivityIcon }, - { name: "Rank", href: "/rank", icon: AwardIcon }, -]; - -const adminNavigation = [ - { name: "Problem Packages", href: "/admin/problem", icon: PackageIcon }, - { name: "User Management", href: "/admin/user", icon: UsersIcon }, -]; - -function isCurrentPath(path: string) { - return window.location.pathname === path; -} - -export const Menu = () => { - const { t } = useTranslation(); - const navigate = useNavigate(); - - const { getUser } = useUser(); - const isAdmin = getUser()?.roles.includes("admin"); - - return ( -
    -
  • {t("Apps")}
  • - {navigation.map((item) => ( -
  • -
    navigate(item.href)} - className={isCurrentPath(item.href) ? "active" : ""} - > - -

    {t(item.name)}

    -
    -
  • - ))} - {isAdmin && ( - <> -
  • {t("Admin")}
  • - {adminNavigation.map((item) => ( -
  • -
    navigate(item.href)} - className={isCurrentPath(item.href) ? "active" : ""} - > - -

    {t(item.name)}

    -
    -
  • - ))} - - )} -
- ); -}; - -export default Menu; diff --git a/src/layouts/Navbar.tsx b/src/layouts/Navbar.tsx index 0a416b71c..6ba971bfa 100644 --- a/src/layouts/Navbar.tsx +++ b/src/layouts/Navbar.tsx @@ -1,5 +1,5 @@ -import UserMenu from "./UserMenu"; -import LanguageMenu from "../components/i18n/LanguageMenu"; +import UserMenu from "../components/menu/UserMenu"; +import LanguageMenu from "../components/menu/LanguageMenu"; import DarkLightToggle from "../components/theme/DarkLightToggle"; const user = { diff --git a/src/mocks/data/problem.ts b/src/mocks/data/problem.ts index 2fe6b037c..9ac9827bb 100644 --- a/src/mocks/data/problem.ts +++ b/src/mocks/data/problem.ts @@ -1,4 +1,4 @@ -import { ProblemServiceModel } from "@/typings/problem"; +import * as ProblemServiceModel from "@/models/service/problem"; export namespace ProblemMockData { export const ProblemInfoList: ProblemServiceModel.ProblemInfo[] = [ diff --git a/src/mocks/rest/judge.ts b/src/mocks/handlers/judge.ts similarity index 97% rename from src/mocks/rest/judge.ts rename to src/mocks/handlers/judge.ts index 4f200bdc6..635de2b2e 100644 --- a/src/mocks/rest/judge.ts +++ b/src/mocks/handlers/judge.ts @@ -1,5 +1,5 @@ import { http } from "msw"; -import { JudgeServiceModel } from "../../typings/judge"; +import * as JudgeServiceModel from "@/models/service/judge"; export const postJudge = http.post("/api/v1/problem/:slug/judge", (info) => { const response: JudgeServiceModel.JudgeVerdict[] = [ diff --git a/src/mocks/rest/problem.ts b/src/mocks/handlers/problem.ts similarity index 97% rename from src/mocks/rest/problem.ts rename to src/mocks/handlers/problem.ts index e66ab6186..604eb85fd 100644 --- a/src/mocks/rest/problem.ts +++ b/src/mocks/handlers/problem.ts @@ -1,5 +1,5 @@ import { HttpResponse, http } from "msw"; -import { ProblemServiceModel } from "../../typings/problem"; +import { ProblemServiceModel } from "../../models/service/problem"; import { ProblemMockData } from "../data/problem"; export const getProblemInfo = http.get( diff --git a/src/mocks/rest/user.ts b/src/mocks/handlers/user.ts similarity index 100% rename from src/mocks/rest/user.ts rename to src/mocks/handlers/user.ts diff --git a/src/mocks/server.ts b/src/mocks/server.ts deleted file mode 100644 index a63864195..000000000 --- a/src/mocks/server.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { setupWorker } from "msw/browser"; -import { restHandlers } from "./handlers"; - -export const worker = setupWorker(...restHandlers); diff --git a/src/mocks/handlers.ts b/src/mocks/worker.ts similarity index 51% rename from src/mocks/handlers.ts rename to src/mocks/worker.ts index 966c7071b..f89f33cf8 100644 --- a/src/mocks/handlers.ts +++ b/src/mocks/worker.ts @@ -1,4 +1,5 @@ -import { postJudge, getJudgeInfoList, getJudgeInfo } from "./rest/judge"; +import { setupWorker } from "msw/browser"; +import { postJudge, getJudgeInfoList, getJudgeInfo } from "./handlers/judge"; import { checkProblemSlug, getProblemInfo, @@ -6,10 +7,10 @@ import { putProblem, deleteProblem, putProblemPackage, -} from "./rest/problem"; -import { getCurrentUser, postLogin, postSignOut } from "./rest/user"; +} from "./handlers/problem"; +import { getCurrentUser, postLogin, postSignOut } from "./handlers/user"; -export const restHandlers = [ +const restHandlers = [ getCurrentUser, postLogin, postSignOut, @@ -23,3 +24,5 @@ export const restHandlers = [ deleteProblem, postJudge, ]; + +export const mockServiceWorker = setupWorker(...restHandlers); diff --git a/src/models/service/judge.ts b/src/models/service/judge.ts new file mode 100644 index 000000000..8dbdb772c --- /dev/null +++ b/src/models/service/judge.ts @@ -0,0 +1,29 @@ +import { ProblemInfo } from "./problem"; +import { UserInfo } from "./user"; + +export interface JudgeInfo { + UID: string; + user: UserInfo; + problem: ProblemInfo; + language: string; + code: string; + status: string; + verdict: string; +} +export interface RunJudgeRequest { + code: string; + language: string; +} + +export interface JudgeTimeUsage { + secs: number; + nanos: number; +} + +export interface JudgeVerdict { + verdict: string; + time_usage: JudgeTimeUsage; + memory_usage_bytes: number; + exit_status: number; + checker_exit_status: number; +} diff --git a/src/models/service/problem.ts b/src/models/service/problem.ts new file mode 100644 index 000000000..120ffeadb --- /dev/null +++ b/src/models/service/problem.ts @@ -0,0 +1,12 @@ +export interface Problem { + slug: string; + title: string; + description: string; + tags: { name: string }[]; +} + +export interface ProblemInfo { + slug: string; + title: string; + tags: { name: string }[]; +} diff --git a/src/models/service/user.ts b/src/models/service/user.ts new file mode 100644 index 000000000..7ce421e43 --- /dev/null +++ b/src/models/service/user.ts @@ -0,0 +1,6 @@ +export interface UserInfo { + name: string; + account: string; + roles?: string[]; + avatarUrl?: string; +} diff --git a/src/models/view/judge.ts b/src/models/view/judge.ts new file mode 100644 index 000000000..e0f11a8a7 --- /dev/null +++ b/src/models/view/judge.ts @@ -0,0 +1,6 @@ +export interface JudgeVerdict { + id: string; + verdict: string; + time_usage: string; + memory_usage: string; +} diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx index c67a81f6b..9c32ddbeb 100644 --- a/src/pages/Login.tsx +++ b/src/pages/Login.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { redirectToOAuthGitHub, postLogin } from "@/api/auth"; +import { redirectToOAuthGitHub, postLogin } from "@/apis/auth"; import GitHubIcon from "@/components/icons/GitHubIcon"; import { EyeIcon, EyeSlashIcon } from "@heroicons/react/24/outline"; diff --git a/src/pages/admin-dashboard/CreateProblem.tsx b/src/pages/admin/CreateProblem.tsx similarity index 99% rename from src/pages/admin-dashboard/CreateProblem.tsx rename to src/pages/admin/CreateProblem.tsx index 4e30bd702..0c937ce45 100644 --- a/src/pages/admin-dashboard/CreateProblem.tsx +++ b/src/pages/admin/CreateProblem.tsx @@ -3,7 +3,7 @@ import { useTranslation } from "react-i18next"; import { PlusIcon } from "@heroicons/react/24/outline"; import { CheckCircleIcon, XCircleIcon } from "@heroicons/react/20/solid"; import MarkdownRender from "@/components/markdown/MarkdownRender"; -import { ProblemService } from "@/api/problem"; +import { ProblemService } from "@/apis/problem"; import { useNavigate } from "react-router-dom"; import React from "react"; diff --git a/src/pages/admin-dashboard/ProblemList.tsx b/src/pages/admin/ProblemList.tsx similarity index 100% rename from src/pages/admin-dashboard/ProblemList.tsx rename to src/pages/admin/ProblemList.tsx diff --git a/src/pipes/judge.ts b/src/pipes/judge.ts index 42d9d63a4..331ad9f5f 100644 --- a/src/pipes/judge.ts +++ b/src/pipes/judge.ts @@ -1,5 +1,6 @@ -import { JudgeModel, JudgeServiceModel } from "../typings/judge"; -import { formatBytes, formatNanoSeconds } from "./common"; +import { formatBytes, formatNanoSeconds } from "@/utils/unit"; +import * as JudgeServiceModel from "@/models/service/judge"; +import * as JudgeViewModel from "@/models/view/judge"; export const judgeVerdictListPipe = ( judgeVerdicts: JudgeServiceModel.JudgeVerdict[], @@ -16,7 +17,7 @@ export const judgeVerdictListPipe = ( export const judgeVerdictPipe = ( judgeVerdict: JudgeServiceModel.JudgeVerdict, -): JudgeModel.JudgeVerdict => { +): JudgeViewModel.JudgeVerdict => { let id = ""; let verdict = judgeVerdict.verdict; let time_usage = formatNanoSeconds(judgeVerdict.time_usage.nanos); diff --git a/src/react-app-env.d.ts b/src/react-app-env.d.ts deleted file mode 100644 index 6431bc5fc..000000000 --- a/src/react-app-env.d.ts +++ /dev/null @@ -1 +0,0 @@ -/// diff --git a/src/typings/judge.ts b/src/typings/judge.ts deleted file mode 100644 index f51bab87a..000000000 --- a/src/typings/judge.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { ProblemServiceModel } from "./problem"; -import { UserServiceModel } from "./user"; - -export namespace JudgeServiceModel { - export interface JudgeInfo { - UID: string; - user: UserServiceModel.UserInfo; - problem: ProblemServiceModel.ProblemInfo; - language: string; - code: string; - status: string; - verdict: string; - } - export interface RunJudgeRequest { - code: string; - language: string; - } - - export interface JudgeTimeUsage { - secs: number; - nanos: number; - } - - export interface JudgeVerdict { - verdict: string; - time_usage: JudgeTimeUsage; - memory_usage_bytes: number; - exit_status: number; - checker_exit_status: number; - } -} - -export namespace JudgeModel { - export interface JudgeVerdict { - id: string; - verdict: string; - time_usage: string; - memory_usage: string; - } -} diff --git a/src/typings/problem.ts b/src/typings/problem.ts deleted file mode 100644 index 02bd9c9ce..000000000 --- a/src/typings/problem.ts +++ /dev/null @@ -1,14 +0,0 @@ -export namespace ProblemServiceModel { - export interface Problem { - slug: string; - title: string; - description: string; - tags: { name: string }[]; - } - - export interface ProblemInfo { - slug: string; - title: string; - tags: { name: string }[]; - } -} diff --git a/src/typings/user.ts b/src/typings/user.ts deleted file mode 100644 index b8a02b262..000000000 --- a/src/typings/user.ts +++ /dev/null @@ -1,8 +0,0 @@ -export namespace UserServiceModel { - export interface UserInfo { - name: string; - account: string; - roles: string[]; - avatarUrl?: string; - } -} diff --git a/src/utils/avatar_url.ts b/src/utils/avatarURL.ts similarity index 100% rename from src/utils/avatar_url.ts rename to src/utils/avatarURL.ts diff --git a/src/api/client.ts b/src/utils/axiosClient.ts similarity index 60% rename from src/api/client.ts rename to src/utils/axiosClient.ts index d78351035..b1782e48f 100644 --- a/src/api/client.ts +++ b/src/utils/axiosClient.ts @@ -1,6 +1,6 @@ import axios from "axios"; -export const client = axios.create({ +export const axiosClient = axios.create({ baseURL: "", timeout: 5000, }); diff --git a/src/utils/environment.ts b/src/utils/environment.ts index 6c5dd26b7..eec7ea115 100644 --- a/src/utils/environment.ts +++ b/src/utils/environment.ts @@ -1,4 +1,4 @@ -export const getMode = () => import.meta.env.MODE; +export const getViteMode = () => import.meta.env.MODE; -export const isMock = () => getMode() === "mock"; -export const isGhPages = () => getMode() === "gh-pages"; +export const isMock = () => getViteMode() === "mock"; +export const isGhPages = () => getViteMode() === "gh-pages"; diff --git a/src/pipes/common.ts b/src/utils/unit.ts similarity index 100% rename from src/pipes/common.ts rename to src/utils/unit.ts diff --git a/src/utils/window.ts b/src/utils/window.ts new file mode 100644 index 000000000..b77338426 --- /dev/null +++ b/src/utils/window.ts @@ -0,0 +1,3 @@ +export function isCurrentPath(path: string) { + return window.location.pathname === path; +} diff --git a/tsconfig.json b/tsconfig.json index bd5167e75..ccaba00db 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "esModuleInterop": true, @@ -17,8 +21,15 @@ "jsx": "react-jsx", "baseUrl": "./", "paths": { - "@/*": ["src/*"] - } + "@/*": [ + "src/*" + ] + }, }, - "include": ["src"] -} + "files": [ + "src/models/service/problem.ts" + ], + "include": [ + "src" + ] +} \ No newline at end of file