Skip to content

Commit e845740

Browse files
committed
feat: add snippet creation with validation
Implemented Zod schema for snippet form validation and tags transformation Refactored handleCreateSnippet to use schema instead of manual validation Updated EditorPage and SnippetsPage to integrate form and validation Added src/schemas/ folder to store Zod schemas
1 parent a61dd71 commit e845740

File tree

4 files changed

+44
-16
lines changed

4 files changed

+44
-16
lines changed

src/pages/editor/EditorPage.tsx

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ import {
4848
fetchProjectById,
4949
updateProject,
5050
} from "@/store/slices/projectSlice";
51-
import type { EditorSettings, TemplateFile } from "@/types";
51+
import type { EditorSettings, SnippetFormState, TemplateFile } from "@/types";
5252
import Editor from "@monaco-editor/react";
5353
import { PanelBottomIcon as LuPanelBottom } from "lucide-react";
5454
import { useEffect, useState } from "react";
@@ -67,6 +67,7 @@ import { useNavigate, useSearchParams } from "react-router-dom";
6767
import OutputConsole from "../../components/editor/OutputConsole";
6868
import { createSnippet } from "@/store/slices/snippetSlice";
6969
import LoadingSnipper from "@/components/LoadingSnipper";
70+
import { snippetFormSchema } from "@/schemas";
7071

7172
const EditorPage = () => {
7273
const navigate = useNavigate();
@@ -84,7 +85,7 @@ const EditorPage = () => {
8485
error: projectError,
8586
} = useSelector((state: RootState) => state.project);
8687
const [isSnippetModalOpen, setIsSnippetModalOpen] = useState(false);
87-
const [snippetForm, setSnippetForm] = useState({
88+
const [snippetForm, setSnippetForm] = useState<SnippetFormState>({
8889
title: "",
8990
description: "",
9091
tags: "",
@@ -309,24 +310,18 @@ const EditorPage = () => {
309310
showToast("No active file selected", "error");
310311
}
311312

312-
if (!snippetForm.title.trim()) {
313-
showToast("Please enter a title for the snippet", "error");
314-
return;
315-
}
316-
317313
try {
318-
const tagsArray = snippetForm.tags
319-
.split(",")
320-
.map((tag) => tag.trim().toLowerCase());
314+
const parsedData = snippetFormSchema.parse(snippetForm);
315+
321316
const language =
322-
snippetForm.language || getFileLanguage(activeFile?.name ?? "");
317+
parsedData.language || getFileLanguage(activeFile?.name ?? "");
323318

324319
const result = await dispatch(
325320
createSnippet({
326-
title: snippetForm.title,
327-
description: snippetForm.description,
321+
title: parsedData.title,
322+
description: parsedData.description,
328323
code: activeFile?.content || "",
329-
tags: tagsArray,
324+
tags: parsedData.tags,
330325
language,
331326
author: {
332327
_id: user?._id || "",
@@ -344,11 +339,17 @@ const EditorPage = () => {
344339
language: "",
345340
});
346341
setIsSnippetModalOpen(false);
342+
showToast("Snippet created successfully", "success");
347343
} else {
348344
throw new Error(result.payload as string);
349345
}
350346
} catch (error: any) {
351-
console.error("Failed to create snippet:", error.message || error);
347+
if (error.errors) {
348+
showToast(error.errors[0].message, "error");
349+
} else {
350+
showToast(error.message || "Failed to create snippet", "error");
351+
}
352+
console.error("Failed to create snippet:", error);
352353
}
353354
};
354355

src/pages/snippets/SnippetsPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ const SnippetsPage = () => {
178178
</Badge>
179179
))}
180180
</div>
181-
<pre className="bg-muted p-4 rounded-md overflow-x-auto max-h-40">
181+
<pre className="bg-muted p-4 rounded-md overflow-x-auto min-h-[120px] max-h-40 ">
182182
<code>
183183
{snippet.code.split("\n").slice(0, 5).join("\n")}
184184
{snippet.code.split("\n").length > 5 ? "..." : ""}

src/schemas/index.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { z } from "zod";
2+
3+
export const snippetFormSchema = z.object({
4+
title: z.string().min(5, "Title is required with atleast 5 characters"),
5+
description: z
6+
.string()
7+
.min(10, "Description is required with atleast 10 characters"),
8+
tags: z
9+
.string()
10+
.min(1, "At least one tag is required")
11+
.transform((val) =>
12+
val
13+
.split(",")
14+
.map((tag) => tag.trim().toLowerCase())
15+
.filter(Boolean)
16+
),
17+
language: z.string().optional(),
18+
});
19+
20+
export type SnippetFormInput = z.infer<typeof snippetFormSchema>;

src/types/index.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,3 +181,10 @@ export interface SnippetState {
181181
currentPage: number;
182182
totalCount: number;
183183
}
184+
185+
export interface SnippetFormState {
186+
title: string;
187+
description: string;
188+
tags: string;
189+
language: string;
190+
}

0 commit comments

Comments
 (0)