Skip to content

Commit

Permalink
Prepare problem APIs
Browse files Browse the repository at this point in the history
  • Loading branch information
slhmy committed Feb 8, 2024
1 parent 26c5439 commit 3419750
Show file tree
Hide file tree
Showing 5 changed files with 123 additions and 11 deletions.
33 changes: 33 additions & 0 deletions src/api/problem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,37 @@ export namespace ProblemService {
}
return res.data;
}

export async function putProblemPackage(
problemSlug: string,
packageFile: File,
) {
let formData = new FormData();
formData.append("file", packageFile);
let res = await client.put<ProblemServiceModel.Problem>(
`/api/v1/problem/${problemSlug}/package`,
formData,
);
if (res.status !== 200) {
throw Error("failed to put problem package");
}
return res.data;
}

export async function checkProblemSlug(
slug: string,
): Promise<{ valid: boolean }> {
let res = await client.get<{ valid: boolean }>(
`/api/v1/problem/${slug}/check`,
);

return res.data;
}

export async function deleteProblem(slug: string) {
let res = await client.delete(`/api/v1/problem/${slug}`);
if (res.status !== 200) {
throw Error("failed to delete problem");
}
}
}
27 changes: 23 additions & 4 deletions src/components/ProblemTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { TrashIcon, PencilSquareIcon } from "@heroicons/react/24/outline";
import { ProblemServiceModel } from "../typings/problem";
import React from "react";
import { useNavigate } from "react-router-dom";
import { ProblemService } from "@/api/problem";

const columns = [
{ name: "SLUG", uid: "slug" },
Expand All @@ -19,6 +20,8 @@ export interface ProblemTableProps {
}

const ProblemTable: React.FC<ProblemTableProps> = (props) => {
const [deletingSlug, setDeletingSlug] = React.useState<string>("");

const navigate = useNavigate();

return (
Expand Down Expand Up @@ -56,7 +59,9 @@ const ProblemTable: React.FC<ProblemTableProps> = (props) => {
</td>
{props.showActions && (
<td>
<Actions />
<Actions
onClickDelete={() => setDeletingSlug(problemInfo.slug)}
/>
</td>
)}
</tr>
Expand All @@ -75,7 +80,13 @@ const ProblemTable: React.FC<ProblemTableProps> = (props) => {
<button
className="btn btn-error btn-sm"
onClick={() => {
// delete problem
ProblemService.deleteProblem(deletingSlug).then((_) => {
navigate("/admin/problem");
});
const modal = document.getElementById(
"delete_confirm_modal",
) as HTMLDialogElement;
modal?.close();
}}
>
delete
Expand All @@ -90,11 +101,18 @@ const ProblemTable: React.FC<ProblemTableProps> = (props) => {
);
};

const Actions: React.FC = () => {
interface ActionsProps {
onClickDelete: () => void;
}

const Actions: React.FC<ActionsProps> = (props) => {
return (
<>
<div className="flex space-x-2">
<button className="btn btn-square btn-outline btn-primary btn-xs">
<button
className="btn btn-square btn-outline btn-primary btn-xs"
disabled
>
<PencilSquareIcon className="h-4 w-4" />
</button>
<button
Expand All @@ -103,6 +121,7 @@ const Actions: React.FC = () => {
e.preventDefault();
e.stopPropagation();

props.onClickDelete();
const modal = document.getElementById(
"delete_confirm_modal",
) as HTMLDialogElement;
Expand Down
12 changes: 11 additions & 1 deletion src/mocks/handlers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import { postJudge } from "./rest/judge";
import { getProblemInfo, getProblemInfoList, putProblem } from "./rest/problem";
import {
checkProblemSlug,
getProblemInfo,
getProblemInfoList,
putProblem,
deleteProblem,
putProblemPackage,
} from "./rest/problem";
import { getSubmissionInfoList } from "./rest/submission";
import { getCurrentUser, postLogin } from "./rest/user";

Expand All @@ -10,5 +17,8 @@ export const restHandlers = [
getProblemInfo,
getProblemInfoList,
getSubmissionInfoList,
checkProblemSlug,
putProblemPackage,
deleteProblem,
postJudge,
];
26 changes: 24 additions & 2 deletions src/mocks/rest/problem.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,30 @@ export const getProblemInfoList = http.get("/api/v1/problem", (info) => {
});

export const putProblem = http.put("/api/v1/problem", async (info) => {
let requestBody: ProblemServiceModel.Problem =
(await info.request.json()) as ProblemServiceModel.Problem;
let requestBody = (await info.request.json()) as ProblemServiceModel.Problem;

return HttpResponse.json(requestBody);
});

export const putProblemPackage = http.put(
"/api/v1/problem/:slug/package",
async (info) => {
let requestBody = await info.request.formData();
let file = requestBody.get("file") as File;
console.log("file:", file);

return HttpResponse.json({ success: true });
},
);

export const checkProblemSlug = http.get(
"/api/v1/problem/:slug/check",
async ({ params }) => {
const { slug } = params;
return HttpResponse.json({ valid: slug === "hello-world" });
},
);

export const deleteProblem = http.delete("/api/v1/problem/:slug", async (_) => {
return HttpResponse.json({ success: true });
});
36 changes: 32 additions & 4 deletions src/pages/admin-dashboard/CreateProblem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { CheckCircleIcon, XCircleIcon } from "@heroicons/react/20/solid";
import MarkdownRender from "@/components/markdown/MarkdownRender";
import { ProblemService } from "@/api/problem";
import { useNavigate } from "react-router-dom";
import React from "react";

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const descriptionPlaceholder = `Output a string with format: \`Hello! %s\`.
Expand Down Expand Up @@ -41,6 +42,9 @@ const CreateProblem: React.FC = () => {
);
const [tags, setTags] = useState<string[]>(["default"]);
const [addingTag, setAddingTag] = useState<string>("");
const [isValidSlug, setIsValidSlug] = useState<boolean | undefined>(
undefined,
);

const { t } = useTranslation();
const navigate = useNavigate();
Expand Down Expand Up @@ -99,21 +103,30 @@ const CreateProblem: React.FC = () => {
className="input input-bordered w-full max-w-xs"
onChange={(e) => {
setSlug(e.target.value);
if (e.target.value === "") {
setIsValidSlug(undefined);
return;
}
ProblemService.checkProblemSlug(e.target.value).then(
(data) => {
setIsValidSlug(data.valid);
},
);
}}
value={slug}
/>
</label>
<div className="mt-9">
{slug && slug !== "slug" && (
{slug && isValidSlug === undefined && (
<div className="flex items-center gap-2">
<span className="loading loading-spinner loading-md" />
<p className="text-sm ">{t("Checking slug is valid...")}</p>
</div>
)}
{slug === "slug" && (
{isValidSlug && (
<CheckCircleIcon className="h-8 fill-green-500" />
)}
{!slug && (
{isValidSlug === false && (
<div className="flex items-center gap-2">
<XCircleIcon
className="h-8 fill-red-500"
Expand All @@ -128,7 +141,22 @@ const CreateProblem: React.FC = () => {
<div className="label">
<span className="label-text">Upload Package</span>
</div>
<input type="file" className="file-input w-full max-w-xs" />
<input
type="file"
className="file-input w-full max-w-xs"
onChange={(e) => {
if (e.target.files) {
ProblemService.putProblemPackage(
slug,
e.target.files[0],
).then((_) => {
console.log("uploaded");
});
}
}}
disabled={!isValidSlug}
accept=".zip"
/>
</label>
<div className="divider" />
<label className="form-control -mt-3 w-full max-w-xs">
Expand Down

0 comments on commit 3419750

Please sign in to comment.