diff --git a/.vscode/settings.json b/.vscode/settings.json
index 9bde8be..574acab 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -2,10 +2,14 @@
"editor.formatOnSave": true,
"markdownlint.config": {
"no-inline-html": {
- "allowed_elements": ["img", "u"]
+ "allowed_elements": [
+ "img",
+ "u",
+ "br"
+ ]
},
"no-empty-links": false,
"no-hard-tabs": false,
"single-h1": false
}
-}
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index bee6686..b79ac55 100644
--- a/README.md
+++ b/README.md
@@ -8,3 +8,9 @@ This website is built using [Docusaurus](https://docusaurus.io/), a modern stati
npx docusaurus write-translations -l zh-CN
cp -rn docs/** i18n/zh-CN/docusaurus-plugin-content-docs/current
```
+
+Start your site on the Chinese locale:
+
+```shell
+npm run start -- --locale zh-CN
+```
diff --git a/docs/development/platform-services/_category_.json b/docs/development/backend/_category_.json
similarity index 67%
rename from docs/development/platform-services/_category_.json
rename to docs/development/backend/_category_.json
index 3c6b591..4f7c726 100644
--- a/docs/development/platform-services/_category_.json
+++ b/docs/development/backend/_category_.json
@@ -1,5 +1,5 @@
{
- "label": "Platform Services",
+ "label": "Backend",
"position": 3,
"link": {
"type": "generated-index"
diff --git a/docs/development/platform-services/form-of-api.md b/docs/development/backend/form-of-api.md
similarity index 100%
rename from docs/development/platform-services/form-of-api.md
rename to docs/development/backend/form-of-api.md
diff --git a/docs/development/platform-services/swag-doc-gen.md b/docs/development/backend/swag-doc-gen.md
similarity index 100%
rename from docs/development/platform-services/swag-doc-gen.md
rename to docs/development/backend/swag-doc-gen.md
diff --git a/docs/development/dev-environment/_category_.json b/docs/development/environment/_category_.json
similarity index 66%
rename from docs/development/dev-environment/_category_.json
rename to docs/development/environment/_category_.json
index f9ea983..b127b58 100644
--- a/docs/development/dev-environment/_category_.json
+++ b/docs/development/environment/_category_.json
@@ -1,5 +1,5 @@
{
- "label": "Develop Environment",
+ "label": "Environment",
"position": 2,
"link": {
"type": "generated-index"
diff --git a/docs/development/dev-environment/k8s-setup-guide.md b/docs/development/environment/k8s-setup-guide.md
similarity index 100%
rename from docs/development/dev-environment/k8s-setup-guide.md
rename to docs/development/environment/k8s-setup-guide.md
diff --git a/docs/development/dev-environment/ubuntu-setup-guide.md b/docs/development/environment/ubuntu-setup-guide.md
similarity index 100%
rename from docs/development/dev-environment/ubuntu-setup-guide.md
rename to docs/development/environment/ubuntu-setup-guide.md
diff --git a/docs/development/frontend/_category_.json b/docs/development/frontend/_category_.json
new file mode 100644
index 0000000..83fc72f
--- /dev/null
+++ b/docs/development/frontend/_category_.json
@@ -0,0 +1,7 @@
+{
+ "label": "Frontend",
+ "position": 3,
+ "link": {
+ "type": "generated-index"
+ }
+}
diff --git a/docs/development/frontend/making-view.md b/docs/development/frontend/making-view.md
new file mode 100644
index 0000000..473c96c
--- /dev/null
+++ b/docs/development/frontend/making-view.md
@@ -0,0 +1,187 @@
+# Making View
+
+This document will guide you through the process of creating a new view in
+[OJ Lab front](https://github.com/oj-lab/oj-lab-front)
+.
+
+You will get to know with three main directories:
+`/layouts`, `/pages` and `/components`.
+So that you can understand how the view is structured and what kind of codes you need to provide in each directory.
+
+:::tip
+
+We will discuss the usage of
+[React Hooks](https://reactjs.org/docs/hooks-intro.html) and state management
+in some parts of the document.
+
+If you are not familiar with React Hooks, you can check the official documentation.
+
+:::
+
+## Main Directories
+
+### Layouts
+
+Layout is a fundamental part of the view,
+it usually provides `header`, `footer`, `sidebar`, `nav` and other common components.
+In another word, layout is something it usually doesn't change between different pages.
+
+The `/layouts` directory contains the layout components.
+Currently, OJ Lab front has only one layout,
+so all the components are placed in the root of the directory
+(If we have more layouts in the future, we will create subdirectories for each layout).
+
+In OJ Lab front's `Router.tsx`,
+the layout component wraps other pages through React Router's [Outlet](https://reactrouter.com/en/main/components/outlet).
+
+```tsx
+// In Layout.tsx
+
+ {!props.children && }
+
+
+// In Router.tsx
+}>
+ } />
+ {/* Other routes */}
+
+```
+
+### Pages
+
+Pages compose the main content of the view by combining different components and hooking in necessary states.
+
+```tsx
+const Judge: React.FC = () => {
+ const uid = useParams().uid as string;
+ const { getJudge } = useJudge(uid);
+
+ const judge = getJudge();
+
+ return (
+ judge && (
+
+ )
+ );
+};
+```
+
+In this example, we get the judge data by using the `useJudge` hook.
+The judge data is got by calling backend's API
+and then passed to the `JudgeTable` and `MarkdownRender` components.
+When the judge data is ready, the page will render the components.
+
+### Components
+
+Components are reusable pieces of code that can be used in different pages.
+
+:::warning
+
+There are some special circumstances where you need to use state in components.
+For example, the monaco editor component needs to change it's theme and size according to window settings.
+
+But usually, components should be stateless.
+
+:::
+
+In OJ Lab front, components are classified into `control`,
+`display`, `input`, `navigation` four categories.
+
+- `control` components are used to control the view, such as buttons, dropdowns, etc.
+- `display` components are used to display data, such as tables, cards, etc.
+- `input` components are used to get user input, such as forms, inputs, etc.
+- `navigation` components are used to navigate between pages, such as breadcrumbs, menus, etc.
+
+```tsx
+export interface UserMenuAction {
+ name: string;
+ href?: string;
+ onClick?: () => void;
+}
+
+export interface UserMenuProps {
+ avatarUrl?: string;
+ actions?: Array;
+}
+
+/**
+ * @param {string} props.avatarUrl
+ * @param {Array<{ name: string, href: string }>} props.navigation The name of navigation will be translated.
+ */
+const UserMenu: React.FC = (props) => {
+```
+
+When working on a new view, you should first check if there are any existing components that can be reused.
+Then, if a new component is needed, you should design the props to pass into the component.
+
+## Tools
+
+### Tailwind CSS
+
+OJ Lab front uses [Tailwind CSS](https://tailwindcss.com/) as the main CSS framework.
+You can control the style of each component in a regular way by simply adding inline Tailwind CSS classes.
+With VSCode's [Tailwind CSS IntelliSense](https://marketplace.visualstudio.com/items?itemName=bradlc.vscode-tailwindcss) extension,
+writing Tailwind CSS classes becomes much easier.
+
+You may also need to use `joinClass` utility function to dynamically add classes to a component.
+
+```tsx
+ {
+ setOpen(!open);
+ }}
+>
+```
+
+:::tip
+
+There are also many other useful utility functions
+which can be used in different parts of the project
+in the `utils` directory.
+
+:::
+
+## Best Practices
+
+You should avoid using too many inline logic in the TSX code.
+Instead, you should extract the logic into hooks or functions.
+
+```tsx
+const ProblemActions: React.FC
= (props) => {
+ const onClickDelete = (e: React.MouseEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ props.onClickDelete();
+ const modal = document.getElementById(
+ "delete_confirm_modal",
+ ) as HTMLDialogElement;
+ modal?.showModal();
+ };
+
+ return (
+
+ );
+};
+```
+
+In this example, the `onClickDelete` function is extracted from the `onClick` event handler.
diff --git a/docs/development/frontend/models-and-apis.md b/docs/development/frontend/models-and-apis.md
new file mode 100644
index 0000000..160a67e
--- /dev/null
+++ b/docs/development/frontend/models-and-apis.md
@@ -0,0 +1,138 @@
+# Models and APIs
+
+In this document, we will introduce how to use APIs
+and how models help us safely constructing OJ Lab front's data transmission by TypeScript.
+
+## Models
+
+In OJ Lab front, models are classified into two types: `ServiceModel` and `ViewModel`.
+
+- `ServiceModel` is expected to be exactly the format of the data returned by the backend APIs
+- `ViewModel` on the other hand, provides a more user-friendly format for the front-end components.
+They are usually transformed from the `ServiceModel` through `/pipes` module.
+
+```ts
+import { formatBytes, formatNanoSeconds } from "@/utils/unit";
+import * as JudgeServiceModel from "@/models/service/judge";
+import * as JudgeViewModel from "@/models/view/judge";
+
+export const judgeVerdictPipe = (
+ judgeVerdict: JudgeServiceModel.JudgeVerdict,
+): JudgeViewModel.JudgeVerdict => {
+ let verdict = judgeVerdict.verdict;
+ let time_usage = formatNanoSeconds(judgeVerdict.time_usage.nanos);
+ let memory_usage = formatBytes(judgeVerdict.memory_usage_bytes);
+
+ return { verdict, time_usage, memory_usage };
+};
+```
+
+In this example, we define a pipe function to transform the `JudgeServiceModel.JudgeVerdict` to `JudgeViewModel.JudgeVerdict` with `formatBytes` and `formatNanoSeconds` utility functions.
+
+## APIs
+
+For RESTful APIs, we use `axios` to send requests and handle responses.
+
+```ts
+export async function getCurrentUser(): Promise {
+ let res = await axiosClient.get(
+ "/api/v1/user/current",
+ );
+ if (res.status !== 200) {
+ throw Error("failed to get current user");
+ }
+ return res.data;
+}
+```
+
+`axiosClient` is a utility function that wraps `axios` with some default configurations.
+
+### Use APIs in Hook
+
+In OJ Lab front, we usually hook detailed APIs usage in customized hooks.
+
+```ts
+export const useJudge = (uid: string) => {
+ const [judge, setJudge] = useState();
+ useEffect(() => {
+ JudgeService.getJudge(uid)
+ .then((res) => {
+ setJudge(res);
+ })
+ .catch((err) => {
+ console.log(err);
+ });
+ }, [uid]);
+
+ function getJudge() {
+ return judge;
+ }
+
+ return { getJudge };
+};
+```
+
+In the page component, we can use the `useJudge` hook to get the judge data and render the components.
+This part of code has been introduced in [Making View](./making-view.md#pages).
+
+### Use APIs in Redux Saga
+
+A more advanced usage is to use APIs in Redux Saga.
+This approach is usually used in global state management,
+for example, getting the current user information when the app is initialized.
+
+```ts
+function* getCurrentUserSaga() {
+ const user: UserServiceModel.UserInfo = yield call(getCurrentUser);
+ yield put(setUserInfo(user));
+}
+
+const GetCurrentUserSagaPattern = "user/getCurrentUser/saga";
+
+export const getCurrentUserAction: Action = {
+ type: GetCurrentUserSagaPattern,
+};
+
+export function* watchGetUserInfo() {
+ yield takeEvery(GetCurrentUserSagaPattern, getCurrentUserSaga);
+}
+```
+
+Then in the layout component, we can dispatch the `getCurrentUserAction` to trigger the saga.
+
+```tsx
+const Layout: React.FC = (props) => {
+ let dispatch = useDispatch();
+
+ useEffect(() => {
+ dispatch(getCurrentUserAction);
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, []);
+
+ // ...
+};
+```
+
+Finally, you can use the `useSelector` hook to get the user information in any component.
+
+```tsx
+const UserMenu: React.FC = (props) => {
+ // ...
+ const userInfo = useSelector(userInfoSelector);
+ const isLogined = userInfo !== undefined;
+
+ // ...
+};
+```
+
+## Best Practices
+
+### Import with Alias
+
+To avoid the confusion and conflicts of the same name modules,
+we should use alias to import the models and APIs.
+
+```ts
+import * as JudgeServiceModel from "@/models/service/judge";
+import * as JudgeService from "@/apis/judge";
+```
diff --git a/docs/development/intro.md b/docs/development/intro.md
index 7045d82..f674476 100644
--- a/docs/development/intro.md
+++ b/docs/development/intro.md
@@ -4,15 +4,10 @@ sidebar_position: 1
# OJ Lab Development Intro
-:::caution
-
-🤖 This series of documents are partly generated by AI, please forgive me for any mistakes.
-
-:::
-
-:::caution
+:::warning
+🤖 This series of documents are partly generated by AI,
+please forgive me for any mistakes.
🚧 This document is still under construction, please wait patiently.
-You can try switching to the Chinese version.
:::
diff --git a/docs/docusaurus/tutorial-extras/translate-your-site.md b/docs/docusaurus/tutorial-extras/translate-your-site.md
index caeaffb..40ec1aa 100644
--- a/docs/docusaurus/tutorial-extras/translate-your-site.md
+++ b/docs/docusaurus/tutorial-extras/translate-your-site.md
@@ -41,7 +41,7 @@ npm run start -- --locale fr
Your localized site is accessible at [http://localhost:3000/fr/](http://localhost:3000/fr/) and the `Getting Started` page is translated.
-:::caution
+:::warning
In development, you can only use one locale at a same time.
diff --git a/i18n/zh-CN/code.json b/i18n/zh-CN/code.json
index 64de7d0..5225a05 100644
--- a/i18n/zh-CN/code.json
+++ b/i18n/zh-CN/code.json
@@ -35,9 +35,9 @@
"message": "信息",
"description": "The default label used for the Info admonition (:::info)"
},
- "theme.admonition.caution": {
+ "theme.admonition.warning": {
"message": "警告",
- "description": "The default label used for the Caution admonition (:::caution)"
+ "description": "The default label used for the warning admonition (:::warning)"
},
"theme.BackToTopButton.buttonAriaLabel": {
"message": "回到顶部",
diff --git a/i18n/zh-CN/docusaurus-plugin-content-blog/2023-10-6-welcome/index.md b/i18n/zh-CN/docusaurus-plugin-content-blog/2023-10-6-welcome/index.md
index fa0e3f0..3015265 100644
--- a/i18n/zh-CN/docusaurus-plugin-content-blog/2023-10-6-welcome/index.md
+++ b/i18n/zh-CN/docusaurus-plugin-content-blog/2023-10-6-welcome/index.md
@@ -37,7 +37,7 @@ OJ Lab一步步地完善,但我还是无法解决一些问题,其中最核
### 参与 OJ Lab 开发的好处
-:::caution
+:::warning
这里的内容是用来吸引开发者的,如果您暂时不打算成为开发者,可以无视这一部分。
diff --git a/i18n/zh-CN/docusaurus-plugin-content-blog/index.md b/i18n/zh-CN/docusaurus-plugin-content-blog/index.md
index fa0e3f0..3015265 100644
--- a/i18n/zh-CN/docusaurus-plugin-content-blog/index.md
+++ b/i18n/zh-CN/docusaurus-plugin-content-blog/index.md
@@ -37,7 +37,7 @@ OJ Lab一步步地完善,但我还是无法解决一些问题,其中最核
### 参与 OJ Lab 开发的好处
-:::caution
+:::warning
这里的内容是用来吸引开发者的,如果您暂时不打算成为开发者,可以无视这一部分。
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/development/intro.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/development/intro.md
index e220f6c..7c784f5 100644
--- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/development/intro.md
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/development/intro.md
@@ -2,16 +2,11 @@
sidebar_position: 1
---
-# OJ Lab 学习 介绍
+# OJ Lab 学习介绍
-:::caution
-
-🤖 此系列文档的部分内容由AI生成,请注意甄别。
-
-:::
-
-:::caution
+:::warning
+🤖 此系列文档的部分内容由AI生成,请注意甄别。
🚧 这篇文档还在施工中,欢迎参与完善。
:::
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/development/platform-services/form-of-api.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/development/platform-services/form-of-api.md
deleted file mode 100644
index 0c4d93f..0000000
--- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/development/platform-services/form-of-api.md
+++ /dev/null
@@ -1,5 +0,0 @@
----
-sidebar_position: 1
----
-
-# API的形式
\ No newline at end of file
diff --git a/i18n/zh-CN/docusaurus-plugin-content-docs/current/docusaurus/tutorial-extras/translate-your-site.md b/i18n/zh-CN/docusaurus-plugin-content-docs/current/docusaurus/tutorial-extras/translate-your-site.md
index caeaffb..40ec1aa 100644
--- a/i18n/zh-CN/docusaurus-plugin-content-docs/current/docusaurus/tutorial-extras/translate-your-site.md
+++ b/i18n/zh-CN/docusaurus-plugin-content-docs/current/docusaurus/tutorial-extras/translate-your-site.md
@@ -41,7 +41,7 @@ npm run start -- --locale fr
Your localized site is accessible at [http://localhost:3000/fr/](http://localhost:3000/fr/) and the `Getting Started` page is translated.
-:::caution
+:::warning
In development, you can only use one locale at a same time.