Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Creates a create testcase page for maintainers #63

Merged
merged 15 commits into from
Nov 13, 2023
Merged
13 changes: 13 additions & 0 deletions src/components/NavBar.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { useSession } from "next-auth/react";
import Link from "next/link";
import { useState } from "react";

Expand All @@ -10,6 +11,8 @@ const NavBar = () => {

const closeDropdown = () => setIsDropdownOpen(false);

const { data: session } = useSession();

return (
<div className="relative bg-slate-800 py-3">
<div className="">
Expand Down Expand Up @@ -71,6 +74,16 @@ const NavBar = () => {
>
SignIn
</Link>
{
session?.user.role === "MAINTAINER" &&
<Link
href="/maintainer/testcases"
onClick={closeDropdown}
className="flex items-center justify-center font-bold text-white no-underline transition hover:bg-white/20 rounded-md whitespace-nowrap bg-white/10 flex-[1_0_0%] px-4"
>
Test cases
</Link>
}
</div>
)}
</div>
Expand Down
12 changes: 9 additions & 3 deletions src/hooks/useQuestions.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useState, useEffect } from "react";
import toast from "react-hot-toast";
import { CodeOutput, Difficulty, Language, Question, TestCase } from "~/types/global";
import { CodeOutput, Difficulty, Environment, Language, Question, TestCase } from "~/types/global";
import { api } from "~/utils/api";

type UseQuestionsReturn = {
Expand Down Expand Up @@ -39,6 +39,7 @@ type UseQuestionsReturn = {
currentLanguage: Language | undefined;
setCurrentLanguage: (lang: Language) => void;
environmentId: string;
environment: Environment | undefined | null;
/**
* The submission status
*/
Expand Down Expand Up @@ -72,7 +73,6 @@ export default function useQuestions(): UseQuestionsReturn {
passed: number,
numOfTests: number
} | undefined>(undefined);

const questions =
api.question.getAllReduced.useQuery(undefined, {
onError: (e) => {
Expand Down Expand Up @@ -166,7 +166,7 @@ export default function useQuestions(): UseQuestionsReturn {
);

const currentTest = testCases.data?.find((val) => {
val.id === testCaseId
return val.id === testCaseId
});

useEffect(() => {
Expand All @@ -180,6 +180,7 @@ export default function useQuestions(): UseQuestionsReturn {
}
}, [submissionStatus])

// update testcase related info
useEffect(() => {
setTestCaseIdList(
testCases?.data?.map(({ id, description }) => ({ id, description })) ??
Expand Down Expand Up @@ -224,5 +225,10 @@ export default function useQuestions(): UseQuestionsReturn {
});
},
submissionStatus: tempSubmissionStatus,
environment: currentEnvironment ? {
...currentEnvironment,
prepend: currentEnvironment?.prepend ?? "",
append: currentEnvironment?.append ?? "",
} : undefined
};
}
10 changes: 9 additions & 1 deletion src/pages/collab/rooms/[id].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,17 @@ const Output = ({
name="output"
id="output"
className="mt-3 h-full min-h-[2rem] w-full p-2 font-mono box-border bg-gray-500 border border-gray-300 text-gray-900 sm:text-sm rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white "
value={output?.status.description ?? ""}
value={output?.status?.description ?? ""}
readOnly
/>
{output?.compile_output && <><em>Compile output</em>
<textarea
name="output"
id="output"
className="mt-3 h-full min-h-[8rem] w-full p-2 font-mono box-border bg-gray-500 border border-gray-300 text-gray-900 sm:text-sm rounded-lg dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white "
value={output?.compile_output ?? ""}
readOnly
/></>}
</label>
</div>
);
Expand Down
258 changes: 258 additions & 0 deletions src/pages/maintainer/environments.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
/**
* Page used to add test cases
*/

import { loadLanguage } from "@uiw/codemirror-extensions-langs";
import ReactCodeMirror from "@uiw/react-codemirror";
import { useSession } from "next-auth/react";
import { useRouter } from "next/router";
import { useEffect, useState } from "react";
import toast from "react-hot-toast";
import QuestionView from "~/components/QuestionView";
import { StyledButton } from "~/components/StyledButton";
import { StyledTextarea } from "~/components/StyledInput";
import QuestionToggleModal from "~/components/code/QuestionToggleModal";
import { WithAuthWrapper } from "~/components/wrapper/AuthWrapper";
import useQuestions from "~/hooks/useQuestions";
import {
Language,
ModifyQuestionProps,
ModifyTestCaseProps,
} from "~/types/global";
import { api } from "~/utils/api";
import { getLanguage } from "~/utils/utils";

// todo: toolbar for options
const Toolbar = ({
judgeLanguages,
currentLanguage,
setCurrentLanguage,
modifyQuestionProps,
onUpdate,
deleteEnv,
canDelete
}: {
judgeLanguages: Language[];
currentLanguage: number;
setCurrentLanguage: (language: Language) => void;
modifyQuestionProps: ModifyQuestionProps;
onUpdate: () => void;
deleteEnv: () => void;
canDelete: boolean;
}) => {
return (
<div className="bg-slate-900 overflow-x-auto overflow-y-clip text-white items-center p-3 grid grid-cols-9 gap-x-5">
<label className="flex flex-row col-span-2">
Language&nbsp;
<select
name="language"
id="language"
value={currentLanguage}
onChange={(e) => {
const lang = judgeLanguages.find((l) => l.id === parseInt(e.target.value ));
lang && setCurrentLanguage(lang);
}}
className="bg-gray-50 border border-gray-300 text-gray-900 sm:text-sm rounded-lg focus:ring-primary-600 focus:border-primary-600 block w-full dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"
required
>
{judgeLanguages.map((language, i) => (
<option key={language.id} value={language.id}>
{language.name}
</option>
))}
</select>
</label>
<label className="flex flex-row col-span-2">
<label className="flex flex-row col-span-2">
<QuestionToggleModal questionTitleList={modifyQuestionProps.questionTitleList} setQuestionId={modifyQuestionProps.setQuestionId} />
</label>
</label>
<div className="flex flex-row col-span-2 gap-2">
<StyledButton onClick={onUpdate}>Upsert env</StyledButton>
<StyledButton onClick={deleteEnv} disabled={!canDelete}>Delete env</StyledButton>
</div>
</div>
);
};

type FormState = {
template: string;
prepend: string;
append: string;
languageId: number;
};

const emptyFormState: FormState = {
template: "",
prepend: "",
append: "",
languageId: 0,
};

// maintainer route to add and delete test cases
const CreateEnvironment = () => {
const useQuestionObject = useQuestions();
const { data: session } = useSession();

const router = useRouter();
const isMaintainer = session?.user.role === "MAINTAINER";
useEffect(() => {
if (session && !isMaintainer) {
void router.push("/");
}
}, [router]);
const [formData, setFormData] = useState<FormState>(emptyFormState);
const languages = api.judge.getLanguages.useQuery();

const setFormDataWrapper = (item: Partial<FormState>) => {
setFormData((prev) => ({
...prev,
...item,
}));
};


const updateEnvMutation = api.environment.upsertEnvironment.useMutation(
{
onError() {
toast.error("Failed to update")
}
}
);
const deleteEnvMutation = api.environment.deleteEnvironment.useMutation(
{
onError() {
toast.error("Failed to delete")
}
}
);

function inEnvironments(languageId: number) {
if (useQuestionObject.languages.length === 0) {
return false;
}
return useQuestionObject.languages.find((lang) => {
return lang.id === languageId
}) !== undefined;
}


function updateEnv() {
if (!useQuestionObject.currentQuestion) {
toast.error("No question selected");
return;
};
updateEnvMutation.mutate({
...formData,
questionId: useQuestionObject.currentQuestion?.id
});
}

function deleteEnv() {

deleteEnvMutation.mutate({
id: useQuestionObject.environmentId
})
}



useEffect(() => {
if (useQuestionObject.environment) {
setFormData({
...useQuestionObject.environment,
});
}
}, [useQuestionObject.environmentId]);

useEffect(() => {
if (useQuestionObject.environment?.languageId === formData.languageId) return;
if (!inEnvironments(formData.languageId)) {
const { languageId, ...empty } = emptyFormState;
setFormDataWrapper(empty);
return;
}
useQuestionObject.setCurrentLanguage(useQuestionObject.languages.find((val) => val.id === formData.languageId)!);

}, [formData.languageId])

return (
<div className="flex flex-col bg-slate-600 h-screen text-white">
<Toolbar
judgeLanguages={languages.data ?? []}
currentLanguage={formData.languageId}
setCurrentLanguage={(lang) => setFormDataWrapper({ languageId: lang.id })}
modifyQuestionProps={{
questionTitleList: useQuestionObject.questionTitleList,
setQuestionId: useQuestionObject.setQuestionId,
currentQuestion: useQuestionObject.currentQuestion,
}}
deleteEnv={deleteEnv}
onUpdate={updateEnv}
canDelete={inEnvironments(formData.languageId)}
/>
<div className="grid grid-cols-2 h-screen overflow-auto">
<QuestionView
question={useQuestionObject.currentQuestion}
template={useQuestionObject.template}
language={
getLanguage(useQuestionObject.currentLanguage?.name ?? "") ?? "c"
}
className="w-full m-3 col-span-1 flex flex-col"
/>
<div className="overflow-auto col-span-1 flex flex-col bg-slate-800 text-white p-8 w-full h-full">
<label>
Prepend
<ReactCodeMirror
theme="dark"
value={formData.prepend}
onChange={(value) => {
setFormDataWrapper({ prepend: value });
}}
extensions={[
loadLanguage(
getLanguage(useQuestionObject.currentLanguage?.name ?? "") ??
"c",
)!,
]}
/>
</label>
<label>
Append
<ReactCodeMirror
theme="dark"
value={formData.append}
onChange={(value) => {
setFormDataWrapper({ append: value });
}}
extensions={[
loadLanguage(
getLanguage(useQuestionObject.currentLanguage?.name ?? "") ??
"c",
)!,
]}
/>
</label>
<label>
Template
<ReactCodeMirror
theme="dark"
value={formData.template}
onChange={(value) => {
setFormDataWrapper({ template: value });
}}
extensions={[
loadLanguage(
getLanguage(useQuestionObject.currentLanguage?.name ?? "") ??
"c",
)!,
]}
/>
</label>
</div>
</div>
</div>
);
};

export default WithAuthWrapper(CreateEnvironment, true);
Loading
Loading