Skip to content

Commit 21e51b2

Browse files
authored
Merge pull request #46 from codegasms/problem
Manage add problem state locally, and other misc fixes and works
2 parents 8963b62 + 2e011b8 commit 21e51b2

File tree

4 files changed

+182
-36
lines changed

4 files changed

+182
-36
lines changed

app/[orgId]/problems/new/page.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { AddProblem } from "@/components/add-problem";
1+
import { ProblemEditor } from "@/components/problem-editor";
22

3-
export default function AddProblemPage() {
4-
return <AddProblem />;
3+
export default function NewProblemPage() {
4+
return <ProblemEditor />;
55
}

app/[orgId]/problems/page.tsx

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { mockProblems, Problem } from "./mockProblems";
33
import { useToast } from "@/hooks/use-toast";
44
import { GenericListing, ColumnDef } from "@/mint/generic-listing";
55
import { useEffect, useState } from "react";
6+
import { ProblemEditor } from "@/components/problem-editor";
7+
import { Dialog, DialogContent } from "@/components/ui/dialog";
68

79
const columns: ColumnDef<Problem>[] = [
810
{ header: "Problem Code", accessorKey: "nameId", sortable: true },
@@ -18,7 +20,7 @@ export default function ProblemsPage({
1820
}) {
1921
const [problems, setProblems] = useState<Problem[]>([]);
2022
const [selectedProblem, setSelectedProblem] = useState<Problem | null>(null);
21-
// const [isEditorOpen, setIsEditorOpen] = useState(false);
23+
const [isEditorOpen, setIsEditorOpen] = useState(false);
2224

2325
const { toast } = useToast();
2426

@@ -31,11 +33,6 @@ export default function ProblemsPage({
3133
setProblems(data);
3234
} catch (error) {
3335
console.error("Error fetching problems:", error);
34-
// toast({
35-
// title: "Error fetching problems",
36-
// description: "Using mock data as fallback",
37-
// variant: "destructive",
38-
// });
3936
setProblems(mockProblems);
4037
}
4138
};
@@ -64,7 +61,6 @@ export default function ProblemsPage({
6461
setProblems((prev) => prev.filter((p) => p.id !== problem.id));
6562
} catch (error) {
6663
console.error("Error deleting problem:", error);
67-
// TODO: Add proper error handling
6864
}
6965
};
7066

@@ -91,17 +87,24 @@ export default function ProblemsPage({
9187
}
9288
return [...prev, savedProblem];
9389
});
94-
95-
setIsEditorOpen(false);
96-
setSelectedProblem(null);
9790
} catch (error) {
9891
console.error("Error saving problem:", error);
99-
// TODO: Add proper error handling
10092
}
10193
};
10294

95+
const handleSavelocal = (updatedProblem: Problem) => {
96+
setProblems(
97+
problems.map((p) => (p.id === updatedProblem.id ? updatedProblem : p)),
98+
);
99+
toast({
100+
title: "Success!",
101+
description: "Problem updated successfully",
102+
});
103+
setIsEditorOpen(false);
104+
};
105+
103106
return (
104-
<>
107+
<div className="container mx-auto py-10">
105108
<GenericListing
106109
data={problems}
107110
columns={columns}
@@ -113,6 +116,16 @@ export default function ProblemsPage({
113116
allowDownload={true}
114117
addPage="new"
115118
/>
116-
</>
119+
120+
<Dialog open={isEditorOpen} onOpenChange={setIsEditorOpen}>
121+
<DialogContent className="max-w-4xl">
122+
<ProblemEditor
123+
problem={selectedProblem}
124+
onClose={() => setIsEditorOpen(false)}
125+
onSave={handleSavelocal}
126+
/>
127+
</DialogContent>
128+
</Dialog>
129+
</div>
117130
);
118131
}

components/code-editor.tsx

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -309,9 +309,6 @@ export function CodeEditor() {
309309
</div>
310310
<div className="bg-muted rounded p-2">
311311
<div className="text-sm font-mono">
312-
<span className="text-muted-foreground">
313-
x ={" "}
314-
</span>
315312
<span className="text-foreground">121</span>
316313
</div>
317314
</div>
@@ -325,9 +322,6 @@ export function CodeEditor() {
325322
</div>
326323
<div className="bg-muted rounded p-2">
327324
<div className="text-sm font-mono">
328-
<span className="text-muted-foreground">
329-
x ={" "}
330-
</span>
331325
<span className="text-foreground">-121</span>
332326
</div>
333327
</div>
Lines changed: 153 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,28 +7,53 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
77
import { Plus, Trash2 } from "lucide-react";
88
import { useRouter } from "next/navigation";
99
import { useToast } from "@/components/ui/use-toast";
10+
import {
11+
mockProblems,
12+
Problem as ListProblem,
13+
} from "@/app/[orgId]/problems/mockProblems";
1014

1115
interface TestCase {
1216
input: string;
13-
expectedOutput: string;
17+
output: string;
18+
kind: "example" | "test";
1419
}
1520

1621
interface Problem {
17-
id: string;
22+
id?: string;
23+
code: string;
1824
title: string;
1925
description: string;
26+
allowedLanguages: string[];
2027
testCases: TestCase[];
2128
}
2229

23-
export function AddProblem() {
30+
interface ProblemEditorProps {
31+
problem?: ListProblem | null;
32+
onClose?: () => void;
33+
onSave?: (problem: ListProblem) => void;
34+
}
35+
36+
export function ProblemEditor({
37+
problem,
38+
onClose,
39+
onSave,
40+
}: ProblemEditorProps) {
2441
const router = useRouter();
2542
const { toast } = useToast();
2643
const [problems, setProblems] = useState<Problem[]>([
2744
{
28-
id: "1",
29-
title: "",
30-
description: "",
31-
testCases: [{ input: "", expectedOutput: "" }],
45+
id: problem?.id?.toString() || "1",
46+
code: problem?.nameId || "",
47+
title: problem?.title || "",
48+
description: problem?.description || "",
49+
allowedLanguages: problem?.allowedLanguages || [
50+
"python",
51+
"javascript",
52+
"typescript",
53+
],
54+
testCases: problem?.testCases || [
55+
{ input: "", output: "", kind: "example" },
56+
],
3257
},
3358
]);
3459
const [currentProblemIndex, setCurrentProblemIndex] = useState(0);
@@ -41,7 +66,7 @@ export function AddProblem() {
4166
...currentProblem,
4267
testCases: [
4368
...currentProblem.testCases,
44-
{ input: "", expectedOutput: "" },
69+
{ input: "", output: "", kind: "example" },
4570
],
4671
};
4772
setProblems(updatedProblems);
@@ -58,7 +83,7 @@ export function AddProblem() {
5883

5984
const updateTestCase = (
6085
index: number,
61-
field: "input" | "expectedOutput",
86+
field: "input" | "output" | "kind",
6287
value: string,
6388
) => {
6489
const updatedProblems = [...problems];
@@ -73,8 +98,8 @@ export function AddProblem() {
7398
};
7499

75100
const updateProblemField = (
76-
field: "title" | "description",
77-
value: string,
101+
field: "code" | "title" | "description" | "allowedLanguages",
102+
value: string | string[],
78103
) => {
79104
const updatedProblems = [...problems];
80105
updatedProblems[currentProblemIndex] = {
@@ -87,9 +112,11 @@ export function AddProblem() {
87112
const addNewProblem = () => {
88113
const newProblem: Problem = {
89114
id: (problems.length + 1).toString(),
115+
code: "",
90116
title: "",
91117
description: "",
92-
testCases: [{ input: "", expectedOutput: "" }],
118+
allowedLanguages: ["python", "javascript", "typescript"],
119+
testCases: [{ input: "", output: "", kind: "example" }],
93120
};
94121
setProblems([...problems, newProblem]);
95122
setCurrentProblemIndex(problems.length);
@@ -115,9 +142,84 @@ export function AddProblem() {
115142
router.push("/contests");
116143
};
117144

145+
const handleSaveProblem = async () => {
146+
try {
147+
// Validate required fields
148+
if (!currentProblem.title || !currentProblem.description) {
149+
toast({
150+
title: "Error",
151+
description: "Please fill in all required fields",
152+
variant: "destructive",
153+
});
154+
return;
155+
}
156+
157+
// Validate test cases
158+
if (currentProblem.testCases.some((tc) => !tc.input || !tc.output)) {
159+
toast({
160+
title: "Error",
161+
description: "Please fill in all test cases",
162+
variant: "destructive",
163+
});
164+
return;
165+
}
166+
167+
// If editing an existing problem
168+
if (problem) {
169+
const updatedProblem: ListProblem = {
170+
...problem,
171+
title: currentProblem.title,
172+
allowedLanguages: currentProblem.allowedLanguages,
173+
};
174+
onSave?.(updatedProblem);
175+
onClose?.();
176+
return;
177+
}
178+
179+
// Creating a new problem
180+
const nameId = Math.random().toString(36).substring(2, 7).toUpperCase();
181+
const newProblem: ListProblem = {
182+
id: mockProblems.length + 1,
183+
nameId,
184+
title: currentProblem.title,
185+
allowedLanguages: currentProblem.allowedLanguages,
186+
createdAt: new Date().toISOString().split("T")[0],
187+
orgId: 1,
188+
};
189+
190+
mockProblems.push(newProblem);
191+
192+
toast({
193+
title: "Success!",
194+
description: "Problem saved successfully",
195+
});
196+
197+
// Get orgId from URL
198+
const pathSegments = window.location.pathname.split("/");
199+
const orgId = pathSegments[1];
200+
201+
// Redirect to problems page
202+
router.push(`/${orgId}/problems`);
203+
} catch (error) {
204+
toast({
205+
title: "Error",
206+
description: "Failed to save problem",
207+
variant: "destructive",
208+
});
209+
}
210+
};
211+
118212
return (
119213
<div className="min-h-screen bg-background text-foreground flex flex-col">
120214
<div className="flex-1 p-6">
215+
<div className="flex justify-end mb-4">
216+
<Button
217+
onClick={handleSaveProblem}
218+
className="bg-primary hover:bg-primary/90 text-primary-foreground"
219+
>
220+
Save Problem
221+
</Button>
222+
</div>
121223
{/* <div className="flex justify-between items-center mb-6">
122224
<div className="flex gap-2">
123225
{problems.map((problem, index) => (
@@ -172,6 +274,22 @@ export function AddProblem() {
172274

173275
<TabsContent value="problem">
174276
<div className="space-y-4">
277+
<div>
278+
<label
279+
htmlFor="code"
280+
className="block text-sm font-medium mb-1"
281+
>
282+
Problem Code
283+
</label>
284+
<Input
285+
id="code"
286+
value={currentProblem.code}
287+
onChange={(e) => updateProblemField("code", e.target.value)}
288+
placeholder="Enter problem code"
289+
className="bg-muted border-border"
290+
/>
291+
</div>
292+
175293
<div>
176294
<label
177295
htmlFor="title"
@@ -205,6 +323,27 @@ export function AddProblem() {
205323
className="bg-muted border-border min-h-[200px]"
206324
/>
207325
</div>
326+
327+
<div>
328+
<label
329+
htmlFor="allowedLanguages"
330+
className="block text-sm font-medium mb-1"
331+
>
332+
Allowed Languages
333+
</label>
334+
<Input
335+
id="allowedLanguages"
336+
value={currentProblem.allowedLanguages.join(", ")}
337+
onChange={(e) =>
338+
updateProblemField(
339+
"allowedLanguages",
340+
e.target.value.split(", "),
341+
)
342+
}
343+
placeholder="Enter allowed languages"
344+
className="bg-muted border-border"
345+
/>
346+
</div>
208347
</div>
209348
</TabsContent>
210349

@@ -250,9 +389,9 @@ export function AddProblem() {
250389
</label>
251390
<Textarea
252391
id={`output-${index}`}
253-
value={testCase.expectedOutput}
392+
value={testCase.output}
254393
onChange={(e) =>
255-
updateTestCase(index, "expectedOutput", e.target.value)
394+
updateTestCase(index, "output", e.target.value)
256395
}
257396
placeholder="Enter expected output"
258397
className="bg-muted-foreground/20 border-border"

0 commit comments

Comments
 (0)