Skip to content
This repository has been archived by the owner on Sep 20, 2024. It is now read-only.

Commit

Permalink
feat : ProblemAddModal
Browse files Browse the repository at this point in the history
  • Loading branch information
kasterra committed Feb 1, 2024
1 parent 32de4cf commit 193709a
Show file tree
Hide file tree
Showing 8 changed files with 295 additions and 23 deletions.
1 change: 0 additions & 1 deletion app/components/Modal/modal.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
flex-direction: column;
gap: 54px;
position: relative;
width: 460px;
border-radius: 24px;
background: #fff;
box-shadow: 0px 32px 64px 0px rgba(44, 39, 56, 0.04),
Expand Down
13 changes: 13 additions & 0 deletions app/components/common/form.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,16 @@
background-color: #0880ae;
color: #ebf4f8;
}

.secondary-button {
border: none;
cursor: pointer;
display: flex;
width: 100%;
height: 45px;
justify-content: center;
align-items: center;
border-radius: 6px;
background-color: #756f86;
color: #ebf4f8;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import Modal from "~/components/Modal";
import CodeBlank from "~/components/CodeBlank";
import { codeHole, parsedCodeElement } from "~/util/codeHole";
import toast from "react-hot-toast";

interface Props {
isOpen: boolean;
onClose: () => void;
codeString: string;
language: string;
}

const BlankPreviewModal = ({
isOpen,
onClose,
codeString,
language,
}: Props) => {
let parsedCode: parsedCodeElement[][];
try {
parsedCode = codeHole(codeString, language);
} catch (error) {
toast.error(
"올바르게 빈칸을 렌더링 할 수 없습니다. 코드를 다시 확인해주세요"
);
onClose();
return null;
}
return (
<Modal
title="빈칸 미리보기"
subtitle="빈칸이 의도한대로 나오는지 미리보기"
isOpen={isOpen}
onClose={onClose}
>
<CodeBlank parsedCode={parsedCode} language={language} />
</Modal>
);
};

export default BlankPreviewModal;
176 changes: 172 additions & 4 deletions app/routes/_procted+/lectures+/$lectureId+/_layout/ProblemAddModal.tsx
Original file line number Diff line number Diff line change
@@ -1,20 +1,188 @@
import { useState } from "react";
import Modal from "~/components/Modal";
import styles from "./modal.module.css";
import formStyles from "~/components/common/form.module.css";
import RadioGroup from "~/components/Radio/RadioGroup";
import TextInput from "~/components/Input/TextInput";
import SingleFileInput from "~/components/Input/SingleFileInput";
import CodeBlock from "~/components/CodeBlock";
import { codeHole, parsedCodeElement } from "~/util/codeHole";
import toast from "react-hot-toast";
import { postBlankProblem, postSolveProblem } from "~/API/problem";
import { useAuth } from "~/contexts/AuthContext";
import BlankPreviewModal from "./BlankPreviewModal";

interface Props {
lectureName: string;
practiceName: string;
practiceId: number;
isOpen: boolean;
onClose: () => void;
}

const ProblemAddModal = ({ isOpen, onClose }: Props) => {
const ProblemAddModal = ({
lectureName,
practiceName,
practiceId,
isOpen,
onClose,
}: Props) => {
const [problemType, setProblemType] = useState<"blank" | "solving">(
"solving"
);
const [language, setLanguage] = useState<
"c" | "java" | "javascript" | "python" | "plaintext"
>("c");
const [codeString, setCodeString] = useState("");
const [isPreviewModalOpen, setIsPreviewModalOpen] = useState(false);
const auth = useAuth();
return (
<Modal
title="문제 추가 - 문제 해결"
subtitle="기초프로그래밍 실습3에 문제를 추가합니다"
title={`문제 추가 - ${
problemType === "blank" ? "빈칸 채우기" : "문제 해결"
}`}
subtitle={`${lectureName} ${practiceName}에 문제를 추가합니다`}
isOpen={isOpen}
onClose={onClose}
>
<div className={styles["modal-body"]}></div>
<form
onSubmit={async (e) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const name = formData.get("name") as string;
const time = parseInt(formData.get("time") as string, 10);
const memory = parseInt(formData.get("memory") as string, 10);
const file = formData.get("pdf") as File;

switch (problemType) {
case "blank":
let holes: parsedCodeElement[][] = [];
try {
holes = codeHole(codeString, language);
} catch {
toast.error("블록 주석에 오류가 있습니다!");
return;
}
const blankResponse = await postBlankProblem(
file,
memory,
holes,
practiceId,
time,
name,
auth.token
);
if (blankResponse.status === 201) {
toast.success("문제를 성공적으로 추가했습니다!");
onClose();
}
break;
case "solving":
const solvingResponse = await postSolveProblem(
file,
memory,
practiceId,
time,
name,
auth.token
);
if (solvingResponse.status === 201) {
toast.success("문제를 성공적으로 추가했습니다!");
onClose();
}
break;
}
}}
>
<div className={styles["modal-section"]}>
<div className={styles["modal-body"]}>
<TextInput
title="문제 이름"
name="name"
placeholder="문제 이름 입력"
required
/>
<RadioGroup
title="문제 유형"
name="problemType"
valueList={["solving", "blank"]}
textList={["문제 해결", "빈칸 채우기"]}
onChange={setProblemType as (value: string) => void}
/>
<TextInput
title="메모리 제한(MB)"
name="memory"
placeholder="0~4096 사이의 값"
required
/>
<TextInput
title="실행 시간(ms)"
name="time"
placeholder="0~10,000 사이의 값"
required
/>
<SingleFileInput
title="문제 pdf 파일"
name="pdf"
fileValidator={(file) => {
return file.name.endsWith(".pdf");
}}
onFileUpload={() => {}}
accept="application/pdf"
/>
<button type="submit" className={formStyles["primary-button"]}>
문제 저장
</button>
</div>
<div className={styles["modal-body"]}>
{problemType === "blank" ? (
<div className={styles["blank-section"]}>
<label htmlFor="language">빈칸 언어</label>
<select
name="language"
id="language"
onChange={(e) =>
setLanguage(
e.target.value as
| "c"
| "java"
| "javascript"
| "python"
| "plaintext"
)
}
>
<option value="c">C</option>
<option value="java">Java</option>
<option value="javascript">JavaScript</option>
<option value="python">Python</option>
<option value="plaintext">Text</option>
</select>
<CodeBlock
height={500}
language={language}
value={codeString}
onChange={setCodeString}
/>
<div
className={formStyles["secondary-button"]}
onClick={() => setIsPreviewModalOpen(true)}
>
빈칸 미리보기
</div>
{isPreviewModalOpen && (
<BlankPreviewModal
isOpen={isPreviewModalOpen}
onClose={() => setIsPreviewModalOpen(false)}
codeString={codeString}
language={language}
/>
)}
</div>
) : null}
</div>
</div>
</form>
</Modal>
);
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import Modal from "~/components/Modal";
import styles from "./modal.module.css";
import { useState } from "react";

interface Props {
isOpen: boolean;
onClose: () => void;
editingProblemId: number;
}

const ProblemAddModal = ({ isOpen, onClose }: Props) => {
const ProblemEditModal = ({ isOpen, onClose, editingProblemId }: Props) => {
const [loading, setLoading] = useState(true);
return (
<Modal
title="문제 수정 - 문제 해결"
Expand All @@ -19,4 +22,4 @@ const ProblemAddModal = ({ isOpen, onClose }: Props) => {
);
};

export default ProblemAddModal;
export default ProblemEditModal;
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ import styles from "./modal.module.css";
interface Props {
isOpen: boolean;
onClose: () => void;
testCaseId: number;
}

const TestCaseEditModal = ({ isOpen, onClose }: Props) => {
const TestCaseEditModal = ({ isOpen, onClose, testCaseId }: Props) => {
return (
<Modal
title="테스트 케이스 수정"
Expand Down
Loading

0 comments on commit 193709a

Please sign in to comment.