chore: configure eslint and prettier
This commit is contained in:
parent
7553091ada
commit
322917982e
@ -1,36 +1,44 @@
|
||||
/* eslint-env node */
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
env: { browser: true, es2020: true },
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true,
|
||||
},
|
||||
extends: [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||
"plugin:react/recommended",
|
||||
"plugin:react-hooks/recommended",
|
||||
"prettier"
|
||||
"eslint-config-prettier",
|
||||
],
|
||||
overrides: [
|
||||
{
|
||||
env: { node: true },
|
||||
files: [".eslintrc.{js,cjs}"],
|
||||
parserOptions: { sourceType: "script" },
|
||||
},
|
||||
],
|
||||
settings: { react: { version: "detect" } },
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "module",
|
||||
project: true,
|
||||
tsconfigRootDir: __dirname
|
||||
tsconfigRootDir: __dirname,
|
||||
},
|
||||
plugins: ["react-refresh"],
|
||||
plugins: ["@typescript-eslint", "react", "react-refresh", "prettier"],
|
||||
rules: {
|
||||
"react-refresh/only-export-components": [
|
||||
"warn",
|
||||
{ allowConstantExport: true }
|
||||
],
|
||||
"@typescript-eslint/no-non-null-assertion": "off",
|
||||
"linebreak-style": ["error", "unix"],
|
||||
"quotes": ["error", "double"],
|
||||
"react/react-in-jsx-scope": ["off"],
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
"argsIgnorePattern": "^_",
|
||||
"varsIgnorePattern": "^_",
|
||||
"caughtErrorsIgnorePattern": "^_"
|
||||
}
|
||||
]
|
||||
}
|
||||
"caughtErrorsIgnorePattern": "^_",
|
||||
},
|
||||
],
|
||||
"prettier/prettier": ["error"],
|
||||
},
|
||||
};
|
||||
|
4
.prettierignore
Normal file
4
.prettierignore
Normal file
@ -0,0 +1,4 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.eslintrc.cjs
|
||||
pnpm-lock.yaml
|
@ -1,3 +1,9 @@
|
||||
{
|
||||
"tabWidth": 4
|
||||
"bracketSpacing": true,
|
||||
"endOfLine": "lf",
|
||||
"printWidth": 120,
|
||||
"semi": true,
|
||||
"singleQuote": false,
|
||||
"tabWidth": 4,
|
||||
"trailingComma": "all"
|
||||
}
|
||||
|
12
package.json
12
package.json
@ -12,10 +12,10 @@
|
||||
"dependencies": {
|
||||
"@ant-design/icons": "^5.2.6",
|
||||
"@ant-design/pro-components": "^2.6.43",
|
||||
"@sentry/react": "^7.90.0",
|
||||
"@sentry/react": "^7.91.0",
|
||||
"@sentry/vite-plugin": "^2.10.2",
|
||||
"ace-builds": "^1.32.2",
|
||||
"antd": "^5.12.4",
|
||||
"antd": "^5.12.5",
|
||||
"axios": "^1.6.2",
|
||||
"dayjs": "^1.11.10",
|
||||
"github-markdown-css": "^5.5.0",
|
||||
@ -23,7 +23,7 @@
|
||||
"react-ace": "^10.1.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-markdown": "^8.0.7",
|
||||
"react-router-dom": "^6.21.0",
|
||||
"react-router-dom": "^6.21.1",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"rehype-mathjax": "^4.0.3",
|
||||
"rehype-raw": "^6.1.1",
|
||||
@ -36,11 +36,13 @@
|
||||
"@types/react": "^18.2.45",
|
||||
"@types/react-dom": "^18.2.18",
|
||||
"@types/react-syntax-highlighter": "^15.5.11",
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
"@typescript-eslint/parser": "^5.62.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.15.0",
|
||||
"@typescript-eslint/parser": "^6.15.0",
|
||||
"@vitejs/plugin-react-swc": "^3.5.0",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-prettier": "^8.10.0",
|
||||
"eslint-plugin-prettier": "^5.1.1",
|
||||
"eslint-plugin-react": "^7.33.2",
|
||||
"eslint-plugin-react-hooks": "^4.6.0",
|
||||
"eslint-plugin-react-refresh": "^0.4.5",
|
||||
"prettier": "3.0.0",
|
||||
|
3106
pnpm-lock.yaml
3106
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
@ -1,9 +1,6 @@
|
||||
import axios from "axios";
|
||||
|
||||
axios.defaults.baseURL =
|
||||
import.meta.env.MODE === "production"
|
||||
? "/api/v1"
|
||||
: "http://127.0.0.1:8000/api/v1";
|
||||
axios.defaults.baseURL = import.meta.env.MODE === "production" ? "/api/v1" : "http://127.0.0.1:8000/api/v1";
|
||||
|
||||
export interface ResponseWrap<T> {
|
||||
code: number;
|
||||
@ -23,11 +20,7 @@ export interface Meta {
|
||||
DeletedAt: Date;
|
||||
}
|
||||
|
||||
export async function send<D, T>(
|
||||
api: string,
|
||||
data?: D,
|
||||
token?: string,
|
||||
): Promise<T> {
|
||||
export async function send<D, T>(api: string, data?: D, token?: string): Promise<T> {
|
||||
try {
|
||||
const resp = await axios.post<ResponseWrap<T>>(api, data, {
|
||||
headers: { Authorization: token },
|
||||
|
@ -50,10 +50,7 @@ export interface UploadResp {
|
||||
}
|
||||
|
||||
export class ProblemApi {
|
||||
static async CreateVersion(
|
||||
data: CreateVersionReq,
|
||||
token: string,
|
||||
): Promise<void> {
|
||||
static async CreateVersion(data: CreateVersionReq, token: string): Promise<void> {
|
||||
return send("/problem/create_version", data, token);
|
||||
}
|
||||
|
||||
|
@ -65,24 +65,15 @@ export interface QueryByVersionResp {
|
||||
}
|
||||
|
||||
export class StatusApi {
|
||||
static async Query(
|
||||
data: QueryReq,
|
||||
token: string,
|
||||
): Promise<WithCount<SubmissionWithPoint>> {
|
||||
static async Query(data: QueryReq, token: string): Promise<WithCount<SubmissionWithPoint>> {
|
||||
return send("/status/query", data, token);
|
||||
}
|
||||
|
||||
static async QueryBySubmission(
|
||||
data: QueryBySubmissionReq,
|
||||
token: string,
|
||||
): Promise<StatusInfo> {
|
||||
static async QueryBySubmission(data: QueryBySubmissionReq, token: string): Promise<StatusInfo> {
|
||||
return send("/status/query/submission", data, token);
|
||||
}
|
||||
|
||||
static async QueryByVersion(
|
||||
data: QueryByVersionReq,
|
||||
token: string,
|
||||
): Promise<WithCount<StatusInfo>> {
|
||||
static async QueryByVersion(data: QueryByVersionReq, token: string): Promise<WithCount<StatusInfo>> {
|
||||
return send("/status/query/version", data, token);
|
||||
}
|
||||
}
|
||||
|
@ -7,27 +7,14 @@ import { useAuth } from "./hook.ts";
|
||||
interface AuthContextType {
|
||||
token: string;
|
||||
isLoggedIn: boolean;
|
||||
login: (
|
||||
username: string,
|
||||
password: string,
|
||||
onSuccess?: VoidFunction,
|
||||
onFailed?: (error: string) => void,
|
||||
) => void;
|
||||
logout: (
|
||||
onSuccess?: VoidFunction,
|
||||
onFailed?: (error: string) => void,
|
||||
) => void;
|
||||
login: (username: string, password: string, onSuccess?: VoidFunction, onFailed?: (error: string) => void) => void;
|
||||
logout: (onSuccess?: VoidFunction, onFailed?: (error: string) => void) => void;
|
||||
}
|
||||
|
||||
const AuthContext = React.createContext<AuthContextType>({
|
||||
token: "",
|
||||
isLoggedIn: false,
|
||||
login: (
|
||||
_username: string,
|
||||
_password: string,
|
||||
_onSuccess?: VoidFunction,
|
||||
onFailed?: (error: string) => void,
|
||||
) => {
|
||||
login: (_username: string, _password: string, _onSuccess?: VoidFunction, onFailed?: (error: string) => void) => {
|
||||
onFailed && onFailed("not implemented");
|
||||
},
|
||||
logout: (_onSuccess?: VoidFunction, onFailed?: (error: string) => void) => {
|
||||
@ -67,10 +54,7 @@ function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
});
|
||||
};
|
||||
|
||||
const logout = (
|
||||
onSuccess?: VoidFunction,
|
||||
onFailed?: (error: string) => void,
|
||||
) => {
|
||||
const logout = (onSuccess?: VoidFunction, onFailed?: (error: string) => void) => {
|
||||
localStorage.removeItem("token");
|
||||
setToken("");
|
||||
setIsLoggedIn(false);
|
||||
@ -92,9 +76,7 @@ function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
|
||||
const value = { token, isLoggedIn, login, logout };
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={value}> {children} </AuthContext.Provider>
|
||||
);
|
||||
return <AuthContext.Provider value={value}> {children} </AuthContext.Provider>;
|
||||
}
|
||||
|
||||
function RequireAuth({ children }: { children: React.ReactNode }) {
|
||||
|
@ -18,9 +18,7 @@ interface LoginFormProps<T> {
|
||||
extra?: React.ReactNode;
|
||||
}
|
||||
|
||||
export default function MyLoginForm<T = LoginFormValues>(
|
||||
props: LoginFormProps<T>,
|
||||
) {
|
||||
export default function MyLoginForm<T = LoginFormValues>(props: LoginFormProps<T>) {
|
||||
const [msg, msgContextHolder] = message.useMessage();
|
||||
const [errMsg, setErrMsg] = useState("");
|
||||
|
||||
|
@ -20,24 +20,20 @@ export default function Markdown(props: MarkdownProps) {
|
||||
const rehypePlugins = [rehypeRaw, rehypeMathJaxSvg];
|
||||
|
||||
const renderers: Components = {
|
||||
code: function makeCodeBlock({
|
||||
inline,
|
||||
className,
|
||||
children,
|
||||
...props
|
||||
}) {
|
||||
code: function makeCodeBlock({ inline, className, children, ...props }) {
|
||||
const match = /language-(\w+)/.exec(className || "");
|
||||
const codeBlock = (
|
||||
<PrismAsync
|
||||
{...props}
|
||||
language={(match || ["", "text"])[1]}
|
||||
PreTag="div"
|
||||
children={String(children).replace(/\n$/, "")}
|
||||
wrapLines={true}
|
||||
showLineNumbers={true}
|
||||
wrapLongLines={true}
|
||||
style={oneLight}
|
||||
/>
|
||||
>
|
||||
{String(children).replace(/\n$/, "")}
|
||||
</PrismAsync>
|
||||
);
|
||||
const codeInline = <code {...props}>{children}</code>;
|
||||
return !inline && match ? codeBlock : codeInline;
|
||||
@ -48,11 +44,12 @@ export default function Markdown(props: MarkdownProps) {
|
||||
<>
|
||||
<ReactMarkdown
|
||||
className={"markdown-body"}
|
||||
children={props.markdown}
|
||||
remarkPlugins={remarkPlugins}
|
||||
rehypePlugins={rehypePlugins}
|
||||
components={renderers}
|
||||
/>
|
||||
>
|
||||
{props.markdown}
|
||||
</ReactMarkdown>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -49,9 +49,7 @@ const columns: ProColumns<SubmissionInfo>[] = [
|
||||
return (
|
||||
<Space>
|
||||
<Button>
|
||||
<Link to={`/problem/${entity.problem_id}`}>
|
||||
查看问题
|
||||
</Link>
|
||||
<Link to={`/problem/${entity.problem_id}`}>查看问题</Link>
|
||||
</Button>
|
||||
|
||||
<Button>
|
||||
|
@ -16,9 +16,7 @@ export default function UserInfo(props: UserInfoProps) {
|
||||
void UserApi.Profile({ uid: props.uid }, props.token).then(setProfile);
|
||||
}, [props.uid, props.token]);
|
||||
|
||||
const roleName = Object.entries(UserRole).filter(
|
||||
([_, v]) => v === profile?.role,
|
||||
);
|
||||
const roleName = Object.entries(UserRole).filter(([, v]) => v === profile?.role);
|
||||
|
||||
const items: CollapseProps["items"] = [
|
||||
{
|
||||
@ -26,14 +24,9 @@ export default function UserInfo(props: UserInfoProps) {
|
||||
label: "User Info",
|
||||
children: (
|
||||
<Descriptions bordered column={1} size="small">
|
||||
<Descriptions.Item label="Nickname">
|
||||
{profile?.nick_name || "Not Found"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="Nickname">{profile?.nick_name || "Not Found"}</Descriptions.Item>
|
||||
<Descriptions.Item label="Joined at">
|
||||
{(profile?.meta.CreatedAt &&
|
||||
dayjs(profile?.meta.CreatedAt).format(
|
||||
"YYYY-MM-DD HH:mm:ss",
|
||||
)) ||
|
||||
{(profile?.meta.CreatedAt && dayjs(profile?.meta.CreatedAt).format("YYYY-MM-DD HH:mm:ss")) ||
|
||||
"N/A"}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="Role">
|
||||
|
@ -1,7 +1,6 @@
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto",
|
||||
"Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans",
|
||||
"Helvetica Neue", sans-serif;
|
||||
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;
|
||||
|
||||
@ -9,6 +8,5 @@ body {
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
|
||||
monospace;
|
||||
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
|
||||
}
|
||||
|
@ -43,8 +43,7 @@ Sentry.init({
|
||||
replaysOnErrorSampleRate: 1.0,
|
||||
});
|
||||
|
||||
const sentryCreateBrowserRouter =
|
||||
Sentry.wrapCreateBrowserRouter(createBrowserRouter);
|
||||
const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouter(createBrowserRouter);
|
||||
|
||||
const router = sentryCreateBrowserRouter([
|
||||
{
|
||||
|
@ -1,8 +1,4 @@
|
||||
import {
|
||||
useRouteError,
|
||||
isRouteErrorResponse,
|
||||
useNavigate,
|
||||
} from "react-router-dom";
|
||||
import { useRouteError, isRouteErrorResponse, useNavigate } from "react-router-dom";
|
||||
import { Button, Result, Space } from "antd";
|
||||
|
||||
const ErrorPage = () => {
|
||||
|
@ -14,18 +14,11 @@ export default function ProblemPage() {
|
||||
<Space wrap>
|
||||
<Button
|
||||
icon={<PlayCircleOutlined />}
|
||||
onClick={() =>
|
||||
navigate(`/problem/${details.problem.meta.ID}/submit`)
|
||||
}
|
||||
onClick={() => navigate(`/problem/${details.problem.meta.ID}/submit`)}
|
||||
>
|
||||
Submit
|
||||
</Button>
|
||||
<Button
|
||||
icon={<SearchOutlined />}
|
||||
onClick={() =>
|
||||
navigate(`/problem/${details.problem.meta.ID}/status`)
|
||||
}
|
||||
>
|
||||
<Button icon={<SearchOutlined />} onClick={() => navigate(`/problem/${details.problem.meta.ID}/status`)}>
|
||||
Status
|
||||
</Button>
|
||||
</Space>
|
||||
|
@ -15,20 +15,14 @@ type RegisterFormValues = {
|
||||
export default function RegisterPage() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const onFinish: CallbackType<RegisterFormValues> = (
|
||||
values,
|
||||
setErrMsg,
|
||||
msg,
|
||||
) => {
|
||||
const onFinish: CallbackType<RegisterFormValues> = (values, setErrMsg, msg) => {
|
||||
UserApi.Create({
|
||||
username: values.username,
|
||||
nickname: values.nickname,
|
||||
password: values.password,
|
||||
})
|
||||
.then(() => {
|
||||
void msg
|
||||
.success("注册成功")
|
||||
.then(() => navigate("/login", { replace: true }));
|
||||
void msg.success("注册成功").then(() => navigate("/login", { replace: true }));
|
||||
})
|
||||
.catch((err: string) => setErrMsg(err));
|
||||
};
|
||||
|
@ -1,19 +1,8 @@
|
||||
import React, { useEffect } from "react";
|
||||
import { Link, Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||
|
||||
import {
|
||||
DefaultFooter,
|
||||
PageContainer,
|
||||
ProLayout,
|
||||
ProLayoutProps,
|
||||
} from "@ant-design/pro-components";
|
||||
import {
|
||||
BlockOutlined,
|
||||
LoginOutlined,
|
||||
LogoutOutlined,
|
||||
PlusCircleOutlined,
|
||||
ProfileOutlined,
|
||||
} from "@ant-design/icons";
|
||||
import { DefaultFooter, PageContainer, ProLayout, ProLayoutProps } from "@ant-design/pro-components";
|
||||
import { BlockOutlined, LoginOutlined, LogoutOutlined, PlusCircleOutlined, ProfileOutlined } from "@ant-design/icons";
|
||||
import { Dropdown, message, Skeleton } from "antd";
|
||||
|
||||
import { NavConfigs } from "../routes.tsx";
|
||||
@ -36,9 +25,7 @@ const LayoutProps: ProLayoutProps = {
|
||||
}),
|
||||
},
|
||||
menuItemRender: (item, dom) => <Link to={item.path || "/"}>{dom}</Link>,
|
||||
footerRender: () => (
|
||||
<DefaultFooter copyright="2023 WOJ Created by WHUPRJ" />
|
||||
),
|
||||
footerRender: () => <DefaultFooter copyright="2023 WOJ Created by WHUPRJ" />,
|
||||
};
|
||||
|
||||
const AvatarUserItems = [
|
||||
@ -115,9 +102,7 @@ export default function Root() {
|
||||
return (
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: auth.isLoggedIn
|
||||
? AvatarUserItems
|
||||
: AvatarGuestItems,
|
||||
items: auth.isLoggedIn ? AvatarUserItems : AvatarGuestItems,
|
||||
onClick: (key) => avatarActions[key.key](),
|
||||
}}
|
||||
>
|
||||
@ -128,11 +113,7 @@ export default function Root() {
|
||||
};
|
||||
|
||||
return (
|
||||
<ProLayout
|
||||
{...LayoutProps}
|
||||
location={{ pathname: curTab }}
|
||||
avatarProps={avatarProps}
|
||||
>
|
||||
<ProLayout {...LayoutProps} location={{ pathname: curTab }} avatarProps={avatarProps}>
|
||||
{msgContextHolder}
|
||||
<PageContainer>
|
||||
<React.Suspense fallback={<Skeleton />}>
|
||||
|
@ -27,9 +27,7 @@ export default function SubmitPage() {
|
||||
const auth = useAuth();
|
||||
const [msg, msgContextHolder] = message.useMessage();
|
||||
|
||||
const langOptions = AvailLang.filter((l) =>
|
||||
details.context.Languages.some((x) => x.Lang === l.value),
|
||||
);
|
||||
const langOptions = AvailLang.filter((l) => details.context.Languages.some((x) => x.Lang === l.value));
|
||||
|
||||
const [lang, setLang] = useState(langOptions[0].value);
|
||||
const [code, setCode] = useState("");
|
||||
|
Reference in New Issue
Block a user