diff --git a/bun-tests/fake-snippets-api/routes/snippets/update.test.ts b/bun-tests/fake-snippets-api/routes/snippets/update.test.ts index e24f659c..deaaf098 100644 --- a/bun-tests/fake-snippets-api/routes/snippets/update.test.ts +++ b/bun-tests/fake-snippets-api/routes/snippets/update.test.ts @@ -114,3 +114,45 @@ test("update snippet with null compiled_js", async () => { const updatedSnippet = db.snippets[0] expect(updatedSnippet.compiled_js).toBeNull() }) + +test("update snippet datasheet URL", async () => { + const { axios, db } = await getTestServer() + + // Add a test snippet + const snippet = { + unscoped_name: "TestSnippet", + owner_name: "testuser", + code: "Original Content", + created_at: "2023-01-01T00:00:00Z", + updated_at: "2023-01-01T00:00:00Z", + name: "testuser/TestSnippet", + snippet_type: "package", + description: "Original Description", + datasheet_url: null + } + db.addSnippet(snippet as any) + + const addedSnippet = db.snippets[0] + + // Update the datasheet URL + const newUrl = "https://example.com/datasheet.pdf" + const response = await axios.post( + "/api/snippets/update", + { + snippet_id: addedSnippet.snippet_id, + datasheet_url: newUrl, + }, + { + headers: { + Authorization: "Bearer 1234", + }, + }, + ) + + expect(response.status).toBe(200) + expect(response.data.snippet.datasheet_url).toBe(newUrl) + + // Verify the snippet was updated in the database + const updatedSnippet = db.snippets[0] + expect(updatedSnippet.datasheet_url).toBe(newUrl) +}) diff --git a/fake-snippets-api/lib/db/schema.ts b/fake-snippets-api/lib/db/schema.ts index 6fd36fb7..80de1a38 100644 --- a/fake-snippets-api/lib/db/schema.ts +++ b/fake-snippets-api/lib/db/schema.ts @@ -17,6 +17,7 @@ export const snippetSchema = z.object({ description: z.string().optional(), version: z.string().default("0.0.1"), star_count: z.number().default(0), + datasheet_url: z.string().url().optional().nullable(), }) export type Snippet = z.infer diff --git a/fake-snippets-api/routes/api/snippets/update.ts b/fake-snippets-api/routes/api/snippets/update.ts index ec5cbaff..a7af6004 100644 --- a/fake-snippets-api/routes/api/snippets/update.ts +++ b/fake-snippets-api/routes/api/snippets/update.ts @@ -15,6 +15,7 @@ export default withRouteSpec({ circuit_json: z.array(z.record(z.any())).optional().nullable(), manual_edits_json_content: z.string().optional().nullable(), snippet_type: z.enum(["board", "package", "model", "footprint"]).optional(), + datasheet_url: z.string().url().optional().nullable(), }), jsonResponse: z.object({ ok: z.boolean(), @@ -31,6 +32,7 @@ export default withRouteSpec({ circuit_json, snippet_type, manual_edits_json_content, + datasheet_url, } = req.jsonBody const snippetIndex = ctx.db.snippets.findIndex( @@ -69,6 +71,7 @@ export default withRouteSpec({ circuit_json: circuit_json !== undefined ? circuit_json : snippet.circuit_json, snippet_type: snippet_type ?? snippet.snippet_type, + datasheet_url: datasheet_url !== undefined ? datasheet_url : snippet.datasheet_url, updated_at: new Date().toISOString(), }) diff --git a/src/components/ViewSnippetSidebar.tsx b/src/components/ViewSnippetSidebar.tsx index 74e18fe0..ecf8d380 100644 --- a/src/components/ViewSnippetSidebar.tsx +++ b/src/components/ViewSnippetSidebar.tsx @@ -2,9 +2,12 @@ import { useCopyToClipboard } from "@/hooks/use-copy-to-clipboard" import { useCurrentSnippet } from "@/hooks/use-current-snippet" import { useToast } from "@/hooks/use-toast" import { cn } from "@/lib/utils" -import { AtSign, Bot, Clock, Code, File, GitFork, Package } from "lucide-react" +import { AtSign, Bot, Clock, Code, File, GitFork, Link2, Package } from "lucide-react" import { Link } from "wouter" import { useFilesDialog } from "./dialogs/files-dialog" +import { useEditDatasheetUrlDialog } from "./dialogs/edit-datasheet-url-dialog" +import { Button } from "./ui/button" +import { useState } from "react" export default function ViewSnippetSidebar({ className, @@ -14,7 +17,9 @@ export default function ViewSnippetSidebar({ const { snippet } = useCurrentSnippet() const { toast } = useToast() const { Dialog: FilesDialog, openDialog: openFilesDialog } = useFilesDialog() + const { Dialog: DatasheetUrlDialog, openDialog: openDatasheetUrlDialog } = useEditDatasheetUrlDialog() const { copyToClipboard } = useCopyToClipboard() + const [datasheetUrlDialogProps, setDatasheetUrlDialogProps] = useState<{ snippetId: string; currentUrl: string | null } | null>(null) return (
, - // label: "Github", - // }, + { + icon: , + label: "Datasheet", + content: snippet?.datasheet_url ? ( +
+ + View Datasheet + + +
+ ) : ( + + ), + badge: snippet?.datasheet_url ? "URL" : undefined, + }, { icon: , label: "Forks", @@ -71,7 +114,6 @@ export default function ViewSnippetSidebar({ } }, }, - // { icon: , label: "Settings" }, ].map((item, index) => (
  • {snippet && } + {snippet && datasheetUrlDialogProps && } ) } diff --git a/src/components/dialogs/edit-datasheet-url-dialog.tsx b/src/components/dialogs/edit-datasheet-url-dialog.tsx new file mode 100644 index 00000000..6ddd2e3a --- /dev/null +++ b/src/components/dialogs/edit-datasheet-url-dialog.tsx @@ -0,0 +1,82 @@ +import React from "react" +import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog" +import { Input } from "../ui/input" +import { Button } from "../ui/button" +import { useState } from "react" +import { useMutation, useQueryClient } from "react-query" +import { createUseDialog } from "./create-use-dialog" +import { useAxios } from "@/hooks/use-axios" +import { useToast } from "@/hooks/use-toast" + +type EditDatasheetUrlDialogProps = { + open: boolean + onOpenChange: (open: boolean) => void + snippetId: string + currentUrl: string | null +} + +export const EditDatasheetUrlDialog = ({ + open, + onOpenChange, + snippetId, + currentUrl, +}: EditDatasheetUrlDialogProps) => { + const [url, setUrl] = useState(currentUrl ?? "") + const axios = useAxios() + const { toast } = useToast() + const qc = useQueryClient() + + const updateDatasheetUrlMutation = useMutation({ + mutationFn: async () => { + const response = await axios.post("/snippets/update", { + snippet_id: snippetId, + datasheet_url: url || null, + }) + if (response.status !== 200) { + throw new Error("Failed to update datasheet URL") + } + return response.data + }, + onSuccess: () => { + onOpenChange(false) + toast({ + title: "Datasheet URL updated", + description: url ? "Successfully updated datasheet URL" : "Removed datasheet URL", + }) + qc.invalidateQueries({ queryKey: ["snippets", snippetId] }) + }, + onError: (error) => { + console.error("Error updating datasheet URL:", error) + toast({ + title: "Error", + description: "Failed to update the datasheet URL. Please try again.", + variant: "destructive", + }) + }, + }) + + return ( + + + + Edit Datasheet URL + + setUrl(e.target.value)} + placeholder="Enter datasheet URL" + type="url" + disabled={updateDatasheetUrlMutation.isLoading} + /> + + + + ) +} + +export const useEditDatasheetUrlDialog = createUseDialog(EditDatasheetUrlDialog)