feat: prefix and anti multi-check
This commit is contained in:
@@ -1,14 +1,17 @@
|
|||||||
|
const API_PREFIX = "/duty";
|
||||||
|
|
||||||
export const get = async (url: string) => {
|
export const get = async (url: string) => {
|
||||||
const resp = await fetch(url);
|
const resp = await fetch(`${API_PREFIX}${url}`);
|
||||||
const json = await resp.json();
|
const json = await resp.json();
|
||||||
return json;
|
return json;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const post = async (
|
export const post = async (
|
||||||
url: string,
|
url: string,
|
||||||
json: any,
|
json: any,
|
||||||
headers: Record<string, string> = {}
|
headers: Record<string, string> = {}
|
||||||
) => {
|
) => {
|
||||||
const resp = await fetch(url, {
|
const resp = await fetch(`${API_PREFIX}${url}`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: { "Content-Type": "application/json", ...headers },
|
headers: { "Content-Type": "application/json", ...headers },
|
||||||
body: JSON.stringify(json),
|
body: JSON.stringify(json),
|
||||||
|
|||||||
@@ -79,10 +79,13 @@ const Timetable = ({
|
|||||||
|
|
||||||
if (disableNetwork) return;
|
if (disableNetwork) return;
|
||||||
|
|
||||||
|
// Immediately revert the checkbox state
|
||||||
|
target.checked = !target.checked;
|
||||||
|
|
||||||
// post request
|
// post request
|
||||||
const json = await post("/api/record", {
|
const json = await post("/api/record", {
|
||||||
name: target.name,
|
name: target.name,
|
||||||
checked: target.checked,
|
checked: !target.checked,
|
||||||
user,
|
user,
|
||||||
});
|
});
|
||||||
if (json.error !== undefined) {
|
if (json.error !== undefined) {
|
||||||
@@ -100,6 +103,7 @@ const Timetable = ({
|
|||||||
|
|
||||||
const handleInput = (event: React.ChangeEvent<HTMLInputElement>): boolean => {
|
const handleInput = (event: React.ChangeEvent<HTMLInputElement>): boolean => {
|
||||||
const { target } = event;
|
const { target } = event;
|
||||||
|
|
||||||
// validate
|
// validate
|
||||||
if (target?.children[0]?.tagName !== "TABLE") {
|
if (target?.children[0]?.tagName !== "TABLE") {
|
||||||
console.log("not a table");
|
console.log("not a table");
|
||||||
@@ -202,7 +206,7 @@ const Timetable = ({
|
|||||||
}
|
}
|
||||||
const occupied: string[] = json.occupied;
|
const occupied: string[] = json.occupied;
|
||||||
const myselect: string[] = json.myselect;
|
const myselect: string[] = json.myselect;
|
||||||
console.log(json);
|
// console.log(json);
|
||||||
for (const index in indexToElement) {
|
for (const index in indexToElement) {
|
||||||
if (occupied.includes(index)) {
|
if (occupied.includes(index)) {
|
||||||
indexToElement[index].style.display = "none";
|
indexToElement[index].style.display = "none";
|
||||||
@@ -226,7 +230,7 @@ const Timetable = ({
|
|||||||
|
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
refresh();
|
refresh();
|
||||||
}, 1000);
|
}, 1500);
|
||||||
return () => {
|
return () => {
|
||||||
clearInterval(interval);
|
clearInterval(interval);
|
||||||
};
|
};
|
||||||
@@ -264,7 +268,9 @@ const Timetable = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<h2 style={{ textAlign: "center" }}>Login as {user}</h2>
|
<h2 style={{ textAlign: "center" }}>ITSC 学 生 助 理 抢 班 系 统</h2>
|
||||||
|
<h3 style={{ textAlign: "center" }}>Login as {user}</h3>
|
||||||
|
<h3 style={{ textAlign: "center" }}>请勿在短时间内多次操作同一时段的选择,选择后请耐心等待状态更新以确认是否选择成功</h3>
|
||||||
<div
|
<div
|
||||||
align="center"
|
align="center"
|
||||||
ref={ref}
|
ref={ref}
|
||||||
@@ -273,7 +279,7 @@ const Timetable = ({
|
|||||||
overflow: "scroll",
|
overflow: "scroll",
|
||||||
}}
|
}}
|
||||||
onInput={handleInput}
|
onInput={handleInput}
|
||||||
></div>{" "}
|
></div>
|
||||||
{!hideDownloadButton && (
|
{!hideDownloadButton && (
|
||||||
<p style={{ display: "flex", justifyContent: "center" }}>
|
<p style={{ display: "flex", justifyContent: "center" }}>
|
||||||
<button onClick={DownloadMarks}>Download Selection</button>
|
<button onClick={DownloadMarks}>Download Selection</button>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const UserInputWrap = ({ children, setUser }) => {
|
|||||||
const [begin, setBegin] = React.useState(false);
|
const [begin, setBegin] = React.useState(false);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setInputUser(localStorage.getItem("user") || "");
|
setInputUser(localStorage.getItem("username") || "");
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -17,9 +17,10 @@ const UserInputWrap = ({ children, setUser }) => {
|
|||||||
display: "grid",
|
display: "grid",
|
||||||
placeItems: "center",
|
placeItems: "center",
|
||||||
alignItems: "center",
|
alignItems: "center",
|
||||||
minHeight: "100vh",
|
// minHeight: "100vh",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
<h2 style={{ textAlign: "center" }}>ITSC 学 生 助 理 抢 班 系 统</h2>
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input
|
||||||
style={{
|
style={{
|
||||||
@@ -45,7 +46,7 @@ const UserInputWrap = ({ children, setUser }) => {
|
|||||||
}
|
}
|
||||||
setUser(inputUser.trim());
|
setUser(inputUser.trim());
|
||||||
setBegin(true);
|
setBegin(true);
|
||||||
localStorage.setItem("user", inputUser.trim());
|
localStorage.setItem("username", inputUser.trim());
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Login
|
Login
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"esbuild": "./node_modules/.bin/esbuild --bundle --tsconfig=entrypoints/tsconfig.json --alias:'@'='./' --outdir=entrypoints --splitting --format=esm entrypoints/index.tsx entrypoints/edit/index.tsx entrypoints/report/index.tsx entrypoints/control/index.tsx --minify",
|
"esbuild": "./node_modules/.bin/esbuild --bundle --tsconfig=entrypoints/tsconfig.json --alias:'@'='./' --outdir=entrypoints --splitting --format=esm entrypoints/index.tsx entrypoints/edit/index.tsx entrypoints/report/index.tsx entrypoints/control/index.tsx --minify",
|
||||||
"dev": "next dev",
|
"dev": "next dev -p 4000",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start -p 4000",
|
"start": "next start -p 4000",
|
||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
|
|||||||
@@ -22,25 +22,35 @@ export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
|||||||
count += 1;
|
count += 1;
|
||||||
if (count >= config.limit) {
|
if (count >= config.limit) {
|
||||||
res.status(403).json({
|
res.status(403).json({
|
||||||
error: `超过选择数量限制,您至多选 ${config.limit} 个班.`,
|
error: `超过选择数量限制,您至多选 ${config.limit} 个班次`,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// check whether it is already occupied
|
// check whether the user repeatly select
|
||||||
if (store[json.name] !== undefined) {
|
if (store[json.name] === json.user) {
|
||||||
|
console.log("api::request: repeat select", json);
|
||||||
res.status(403).json({
|
res.status(403).json({
|
||||||
error: `当前位置已被他人占用`,
|
error: `您已经选择了这个班次,请勿重复选择`,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// check whether it is already occupied
|
||||||
|
else if (store[json.name] !== undefined) {
|
||||||
|
console.log("api::request: occupied", json);
|
||||||
|
res.status(403).json({
|
||||||
|
error: `当前位置已被他人占用,请选择其他班次`,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
store[json.name] = json.user;
|
store[json.name] = json.user;
|
||||||
} else {
|
} else {
|
||||||
|
// console.log(store, json);
|
||||||
// check whether the request name match the taken name
|
// check whether the request name match the taken name
|
||||||
if (store[json.name] !== json.user) {
|
if (store[json.name] !== json.user) {
|
||||||
res.status(403).json({
|
res.status(403).json({
|
||||||
error: `失败:您未选择到当前位置`,
|
error: `您已经取消了这个班次,请勿重复点击复选框`,
|
||||||
})
|
})
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ export default function Home() {
|
|||||||
<>
|
<>
|
||||||
<Head>
|
<Head>
|
||||||
<title>抢 班</title>
|
<title>抢 班</title>
|
||||||
<meta name="description" content="ITSC抢班系统" />
|
<meta name="description" content="ITSC 学 生 助 理 抢 班 系 统" />
|
||||||
{/* <meta name="viewport" content="width=device-width, initial-scale=1" /> */}
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
|
||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
</Head>
|
</Head>
|
||||||
<main>
|
<main>
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
|
import { get, post } from "@/common";
|
||||||
|
|
||||||
const ReportPage = () => {
|
const ReportPage = () => {
|
||||||
const ref = React.useRef();
|
const ref = React.useRef();
|
||||||
const getReport = async () => {
|
const getReport = async () => {
|
||||||
const resp = await fetch("/api/html").then((resp) => resp.json());
|
const resp = await get("/api/html");
|
||||||
ref.current.innerHTML = resp.html;
|
ref.current.innerHTML = resp.html;
|
||||||
const json: Record<string, string> = await fetch("/api/tool").then((resp) =>
|
const json: Record<string, string> = await get("/api/tool");
|
||||||
resp.json()
|
|
||||||
);
|
|
||||||
const table = ref.current.children[0];
|
const table = ref.current.children[0];
|
||||||
const tbody = table.children[table.children.length - 1];
|
const tbody = table.children[table.children.length - 1];
|
||||||
for (const tr_index in tbody.children) {
|
for (const tr_index in tbody.children) {
|
||||||
|
|||||||
@@ -1,14 +1,13 @@
|
|||||||
import React from "react";
|
import React from "react";
|
||||||
import Head from "next/head";
|
import Head from "next/head";
|
||||||
|
import { get, post } from "@/common";
|
||||||
|
|
||||||
const ReportPage = () => {
|
const ReportPage = () => {
|
||||||
const ref = React.useRef();
|
const ref = React.useRef();
|
||||||
const getReport = async () => {
|
const getReport = async () => {
|
||||||
const resp = await fetch("/api/html").then((resp) => resp.json());
|
const resp = await get("/api/html");
|
||||||
ref.current.innerHTML = resp.html;
|
ref.current.innerHTML = resp.html;
|
||||||
const json: Record<string, string> = await fetch("/api/admin").then(
|
const json: Record<string, string> = await get("/api/admin");
|
||||||
(resp) => resp.json()
|
|
||||||
);
|
|
||||||
const table = ref.current.children[0];
|
const table = ref.current.children[0];
|
||||||
const tbody = table.children[table.children.length - 1];
|
const tbody = table.children[table.children.length - 1];
|
||||||
for (const tr_index in tbody.children) {
|
for (const tr_index in tbody.children) {
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export default function Home() {
|
|||||||
const [begin, setBegin] = React.useState(false);
|
const [begin, setBegin] = React.useState(false);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
setUser(localStorage.getItem("user") || "");
|
setUser(localStorage.getItem("username") || "");
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -34,7 +34,7 @@ export default function Home() {
|
|||||||
}
|
}
|
||||||
setUser(user.trim());
|
setUser(user.trim());
|
||||||
setBegin(true);
|
setBegin(true);
|
||||||
localStorage.setItem("user", user);
|
localStorage.setItem("username", user);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
Login
|
Login
|
||||||
|
|||||||
Reference in New Issue
Block a user