@tscircuit/fake-snippets 0.0.92 → 0.0.94

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1069,7 +1069,7 @@ declare const createDatabase: ({ seed }?: {
1069
1069
  }[] | null;
1070
1070
  datasheet_pdf_urls: string[] | null;
1071
1071
  }[];
1072
- }, "addOrder" | "getOrderById" | "getOrderFilesByOrderId" | "addOrderQuote" | "getOrderQuoteById" | "getJlcpcbOrderStatesByOrderId" | "getJlcpcbOrderStepRunsByJlcpcbOrderStateId" | "updateOrder" | "addJlcpcbOrderState" | "updateJlcpcbOrderState" | "addOrderFile" | "getOrderFileById" | "addAccount" | "addAccountPackage" | "getAccountPackageById" | "updateAccountPackage" | "deleteAccountPackage" | "addSnippet" | "getLatestSnippets" | "getTrendingSnippets" | "getPackagesByAuthor" | "getSnippetByAuthorAndName" | "updateSnippet" | "getSnippetById" | "searchSnippets" | "searchPackages" | "deleteSnippet" | "addSession" | "getSessions" | "createLoginPage" | "getLoginPage" | "updateLoginPage" | "getAccount" | "updateAccount" | "createSession" | "addStar" | "removeStar" | "hasStarred" | "addPackage" | "updatePackage" | "getPackageById" | "getPackageReleaseById" | "addPackageRelease" | "updatePackageRelease" | "deletePackageFile" | "addPackageFile" | "updatePackageFile" | "getStarCount" | "getPackageFilesByReleaseId" | "updatePackageReleaseFsSha" | "addAiReview" | "updateAiReview" | "getAiReviewById" | "listAiReviews" | "addDatasheet" | "getDatasheetById" | "updateDatasheet"> & {
1072
+ }, "addOrder" | "getOrderById" | "getOrderFilesByOrderId" | "addOrderQuote" | "getOrderQuoteById" | "getJlcpcbOrderStatesByOrderId" | "getJlcpcbOrderStepRunsByJlcpcbOrderStateId" | "updateOrder" | "addJlcpcbOrderState" | "updateJlcpcbOrderState" | "addOrderFile" | "getOrderFileById" | "addAccount" | "addAccountPackage" | "getAccountPackageById" | "updateAccountPackage" | "deleteAccountPackage" | "addSnippet" | "getLatestSnippets" | "getTrendingSnippets" | "getPackagesByAuthor" | "getSnippetByAuthorAndName" | "updateSnippet" | "getSnippetById" | "searchSnippets" | "searchPackages" | "deleteSnippet" | "addSession" | "getSessions" | "createLoginPage" | "getLoginPage" | "updateLoginPage" | "getAccount" | "updateAccount" | "createSession" | "addStar" | "removeStar" | "hasStarred" | "addPackage" | "updatePackage" | "getPackageById" | "getPackageReleaseById" | "addPackageRelease" | "updatePackageRelease" | "deletePackageFile" | "addPackageFile" | "updatePackageFile" | "getStarCount" | "getPackageFilesByReleaseId" | "updatePackageReleaseFsSha" | "addAiReview" | "updateAiReview" | "getAiReviewById" | "listAiReviews" | "addDatasheet" | "getDatasheetById" | "getDatasheetByChipName" | "listDatasheets" | "updateDatasheet"> & {
1073
1073
  addOrder: (order: Omit<Order, "order_id">) => Order;
1074
1074
  getOrderById: (orderId: string) => Order | undefined;
1075
1075
  getOrderFilesByOrderId: (orderId: string) => OrderFile[];
@@ -1155,6 +1155,11 @@ declare const createDatabase: ({ seed }?: {
1155
1155
  chip_name: string;
1156
1156
  }) => Datasheet;
1157
1157
  getDatasheetById: (datasheetId: string) => Datasheet | undefined;
1158
+ getDatasheetByChipName: (chipName: string) => Datasheet | undefined;
1159
+ listDatasheets: ({ chip_name, is_popular, }?: {
1160
+ chip_name?: string;
1161
+ is_popular?: boolean;
1162
+ }) => Datasheet[];
1158
1163
  updateDatasheet: (datasheetId: string, updates: Partial<Datasheet>) => Datasheet | undefined;
1159
1164
  }> & Omit<{
1160
1165
  idCounter: number;
@@ -1416,7 +1421,7 @@ declare const createDatabase: ({ seed }?: {
1416
1421
  }[] | null;
1417
1422
  datasheet_pdf_urls: string[] | null;
1418
1423
  }[];
1419
- }, "addOrder" | "getOrderById" | "getOrderFilesByOrderId" | "addOrderQuote" | "getOrderQuoteById" | "getJlcpcbOrderStatesByOrderId" | "getJlcpcbOrderStepRunsByJlcpcbOrderStateId" | "updateOrder" | "addJlcpcbOrderState" | "updateJlcpcbOrderState" | "addOrderFile" | "getOrderFileById" | "addAccount" | "addAccountPackage" | "getAccountPackageById" | "updateAccountPackage" | "deleteAccountPackage" | "addSnippet" | "getLatestSnippets" | "getTrendingSnippets" | "getPackagesByAuthor" | "getSnippetByAuthorAndName" | "updateSnippet" | "getSnippetById" | "searchSnippets" | "searchPackages" | "deleteSnippet" | "addSession" | "getSessions" | "createLoginPage" | "getLoginPage" | "updateLoginPage" | "getAccount" | "updateAccount" | "createSession" | "addStar" | "removeStar" | "hasStarred" | "addPackage" | "updatePackage" | "getPackageById" | "getPackageReleaseById" | "addPackageRelease" | "updatePackageRelease" | "deletePackageFile" | "addPackageFile" | "updatePackageFile" | "getStarCount" | "getPackageFilesByReleaseId" | "updatePackageReleaseFsSha" | "addAiReview" | "updateAiReview" | "getAiReviewById" | "listAiReviews" | "addDatasheet" | "getDatasheetById" | "updateDatasheet"> & {
1424
+ }, "addOrder" | "getOrderById" | "getOrderFilesByOrderId" | "addOrderQuote" | "getOrderQuoteById" | "getJlcpcbOrderStatesByOrderId" | "getJlcpcbOrderStepRunsByJlcpcbOrderStateId" | "updateOrder" | "addJlcpcbOrderState" | "updateJlcpcbOrderState" | "addOrderFile" | "getOrderFileById" | "addAccount" | "addAccountPackage" | "getAccountPackageById" | "updateAccountPackage" | "deleteAccountPackage" | "addSnippet" | "getLatestSnippets" | "getTrendingSnippets" | "getPackagesByAuthor" | "getSnippetByAuthorAndName" | "updateSnippet" | "getSnippetById" | "searchSnippets" | "searchPackages" | "deleteSnippet" | "addSession" | "getSessions" | "createLoginPage" | "getLoginPage" | "updateLoginPage" | "getAccount" | "updateAccount" | "createSession" | "addStar" | "removeStar" | "hasStarred" | "addPackage" | "updatePackage" | "getPackageById" | "getPackageReleaseById" | "addPackageRelease" | "updatePackageRelease" | "deletePackageFile" | "addPackageFile" | "updatePackageFile" | "getStarCount" | "getPackageFilesByReleaseId" | "updatePackageReleaseFsSha" | "addAiReview" | "updateAiReview" | "getAiReviewById" | "listAiReviews" | "addDatasheet" | "getDatasheetById" | "getDatasheetByChipName" | "listDatasheets" | "updateDatasheet"> & {
1420
1425
  addOrder: (order: Omit<Order, "order_id">) => Order;
1421
1426
  getOrderById: (orderId: string) => Order | undefined;
1422
1427
  getOrderFilesByOrderId: (orderId: string) => OrderFile[];
@@ -1502,6 +1507,11 @@ declare const createDatabase: ({ seed }?: {
1502
1507
  chip_name: string;
1503
1508
  }) => Datasheet;
1504
1509
  getDatasheetById: (datasheetId: string) => Datasheet | undefined;
1510
+ getDatasheetByChipName: (chipName: string) => Datasheet | undefined;
1511
+ listDatasheets: ({ chip_name, is_popular, }?: {
1512
+ chip_name?: string;
1513
+ is_popular?: boolean;
1514
+ }) => Datasheet[];
1505
1515
  updateDatasheet: (datasheetId: string, updates: Partial<Datasheet>) => Datasheet | undefined;
1506
1516
  };
1507
1517
  type DbClient = ReturnType<typeof createDatabase>;
package/dist/index.js CHANGED
@@ -3112,7 +3112,7 @@ var initializer = combine(databaseSchema.parse({}), (set, get) => ({
3112
3112
  },
3113
3113
  addDatasheet: ({ chip_name }) => {
3114
3114
  const newDatasheet = datasheetSchema.parse({
3115
- datasheet_id: `datasheet_${Date.now()}`,
3115
+ datasheet_id: crypto.randomUUID(),
3116
3116
  chip_name,
3117
3117
  created_at: (/* @__PURE__ */ new Date()).toISOString(),
3118
3118
  pin_information: null,
@@ -3127,6 +3127,25 @@ var initializer = combine(databaseSchema.parse({}), (set, get) => ({
3127
3127
  const state = get();
3128
3128
  return state.datasheets.find((d) => d.datasheet_id === datasheetId);
3129
3129
  },
3130
+ getDatasheetByChipName: (chipName) => {
3131
+ const state = get();
3132
+ return state.datasheets.find((d) => d.chip_name === chipName);
3133
+ },
3134
+ listDatasheets: ({
3135
+ chip_name,
3136
+ is_popular
3137
+ } = {}) => {
3138
+ const state = get();
3139
+ if (is_popular) {
3140
+ return state.datasheets;
3141
+ }
3142
+ if (chip_name) {
3143
+ return state.datasheets.filter(
3144
+ (d) => d.chip_name.toLowerCase() === chip_name.toLowerCase()
3145
+ );
3146
+ }
3147
+ return state.datasheets;
3148
+ },
3130
3149
  updateDatasheet: (datasheetId, updates) => {
3131
3150
  let updated;
3132
3151
  set((state) => {
@@ -1382,7 +1382,7 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
1382
1382
  },
1383
1383
  addDatasheet: ({ chip_name }: { chip_name: string }): Datasheet => {
1384
1384
  const newDatasheet = datasheetSchema.parse({
1385
- datasheet_id: `datasheet_${Date.now()}`,
1385
+ datasheet_id: crypto.randomUUID(),
1386
1386
  chip_name,
1387
1387
  created_at: new Date().toISOString(),
1388
1388
  pin_information: null,
@@ -1397,6 +1397,27 @@ const initializer = combine(databaseSchema.parse({}), (set, get) => ({
1397
1397
  const state = get()
1398
1398
  return state.datasheets.find((d) => d.datasheet_id === datasheetId)
1399
1399
  },
1400
+ getDatasheetByChipName: (chipName: string): Datasheet | undefined => {
1401
+ const state = get()
1402
+ return state.datasheets.find((d) => d.chip_name === chipName)
1403
+ },
1404
+ listDatasheets: ({
1405
+ chip_name,
1406
+ is_popular,
1407
+ }: { chip_name?: string; is_popular?: boolean } = {}): Datasheet[] => {
1408
+ const state = get()
1409
+ if (is_popular) {
1410
+ return state.datasheets
1411
+ }
1412
+
1413
+ if (chip_name) {
1414
+ return state.datasheets.filter(
1415
+ (d) => d.chip_name.toLowerCase() === chip_name.toLowerCase(),
1416
+ )
1417
+ }
1418
+
1419
+ return state.datasheets
1420
+ },
1400
1421
  updateDatasheet: (
1401
1422
  datasheetId: string,
1402
1423
  updates: Partial<Datasheet>,
@@ -5,16 +5,26 @@ import { z } from "zod"
5
5
  export default withRouteSpec({
6
6
  methods: ["GET", "POST"],
7
7
  auth: "session",
8
- queryParams: z.object({
9
- datasheet_id: z.string(),
10
- }),
8
+ queryParams: z
9
+ .object({
10
+ datasheet_id: z.string().optional(),
11
+ chip_name: z.string().optional(),
12
+ })
13
+ .refine((val) => val.datasheet_id || val.chip_name, {
14
+ message: "datasheet_id or chip_name required",
15
+ }),
11
16
  jsonBody: z.any().optional(),
12
17
  jsonResponse: z.object({
13
18
  datasheet: datasheetSchema,
14
19
  }),
15
20
  })(async (req, ctx) => {
16
- const { datasheet_id } = req.query
17
- const datasheet = ctx.db.getDatasheetById(datasheet_id)
21
+ const { datasheet_id, chip_name } = req.query
22
+ let datasheet
23
+ if (datasheet_id) {
24
+ datasheet = ctx.db.getDatasheetById(datasheet_id)
25
+ } else if (chip_name) {
26
+ datasheet = ctx.db.getDatasheetByChipName(chip_name)
27
+ }
18
28
  if (!datasheet) {
19
29
  return ctx.error(404, {
20
30
  error_code: "datasheet_not_found",
@@ -0,0 +1,29 @@
1
+ import { withRouteSpec } from "fake-snippets-api/lib/middleware/with-winter-spec"
2
+ import { z } from "zod"
3
+
4
+ export default withRouteSpec({
5
+ methods: ["GET", "POST"],
6
+ auth: "none",
7
+ commonParams: z.object({
8
+ chip_name: z.string().optional(),
9
+ is_popular: z.boolean().optional(),
10
+ }),
11
+ jsonResponse: z.object({
12
+ datasheets: z.array(
13
+ z.object({
14
+ datasheet_id: z.string().uuid(),
15
+ chip_name: z.string(),
16
+ }),
17
+ ),
18
+ }),
19
+ })(async (req, ctx) => {
20
+ const { chip_name, is_popular } = req.commonParams
21
+ const datasheets = ctx.db
22
+ .listDatasheets({ chip_name, is_popular })
23
+ .map((ds) => ({
24
+ datasheet_id: ds.datasheet_id,
25
+ chip_name: ds.chip_name,
26
+ }))
27
+
28
+ return ctx.json({ datasheets })
29
+ })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tscircuit/fake-snippets",
3
- "version": "0.0.92",
3
+ "version": "0.0.94",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
package/src/App.tsx CHANGED
@@ -68,6 +68,7 @@ const DevLoginPage = lazyImport(() => import("@/pages/dev-login"))
68
68
  const ViewPackagePage = lazyImport(() => import("@/pages/view-package"))
69
69
  const PackageBuildsPage = lazyImport(() => import("@/pages/package-builds"))
70
70
  const TrendingPage = lazyImport(() => import("@/pages/trending"))
71
+ const DatasheetPage = lazyImport(() => import("@/pages/datasheet"))
71
72
  const PackageEditorPage = lazyImport(async () => {
72
73
  const [editorModule] = await Promise.all([
73
74
  import("@/pages/package-editor"),
@@ -180,6 +181,7 @@ function App() {
180
181
  <Route path="/settings" component={SettingsPage} />
181
182
  <Route path="/search" component={SearchPage} />
182
183
  <Route path="/trending" component={TrendingPage} />
184
+ <Route path="/datasheets/:chipName" component={DatasheetPage} />
183
185
  <Route path="/authorize" component={AuthenticatePage} />
184
186
  <Route path="/my-orders" component={MyOrdersPage} />
185
187
  <Route path="/dev-login" component={DevLoginPage} />
@@ -282,7 +282,7 @@ const CmdKMenu = () => {
282
282
  switch (type) {
283
283
  case "package":
284
284
  case "recent":
285
- window.location.href = `/editor?package_id=${item.package_id}`
285
+ window.location.href = `/${item.owner_github_username}/${item.unscoped_name}`
286
286
  setOpen(false)
287
287
  break
288
288
  case "blank":
@@ -18,6 +18,7 @@ export const LogContent = ({
18
18
  logs: Array<{
19
19
  type?: "info" | "success" | "error"
20
20
  msg?: string
21
+ message?: string
21
22
  timestamp?: string
22
23
  }>
23
24
  error?: ErrorObject | string | null
@@ -25,7 +26,8 @@ export const LogContent = ({
25
26
  return (
26
27
  <div className="font-mono text-xs space-y-1 min-w-0">
27
28
  {logs.map((log, i) => {
28
- if (!log.msg) return null
29
+ const text = log.msg ?? log.message
30
+ if (!text) return null
29
31
 
30
32
  return (
31
33
  <div
@@ -44,7 +46,7 @@ export const LogContent = ({
44
46
  </span>
45
47
  )}
46
48
  {log.timestamp && " "}
47
- <span className="break-all">{log.msg}</span>
49
+ <span className="break-all">{text}</span>
48
50
  </div>
49
51
  )
50
52
  })}
@@ -99,6 +99,8 @@ export const PackageCard: React.FC<PackageCardProps> = ({
99
99
  })
100
100
  }
101
101
 
102
+ const availableImages = ["pcb", "schematic", "assembly", "3d"]
103
+
102
104
  const cardContent = (
103
105
  <div
104
106
  className={`border p-4 rounded-md hover:shadow-md transition-shadow flex flex-col gap-4 ${className}`}
@@ -108,8 +110,8 @@ export const PackageCard: React.FC<PackageCardProps> = ({
108
110
  className={`${imageSize} flex-shrink-0 rounded-md overflow-hidden`}
109
111
  >
110
112
  <ImageWithFallback
111
- src={`${baseUrl}/packages/images/${pkg.owner_github_username}/${pkg.unscoped_name}/pcb.svg?fs_sha=${pkg.latest_package_release_fs_sha}`}
112
- alt={`${pkg.unscoped_name} PCB image`}
113
+ src={`${baseUrl}/packages/images/${pkg.owner_github_username}/${pkg.unscoped_name}/${availableImages.includes(pkg.default_view || "") ? pkg.default_view : "3d"}.png?fs_sha=${pkg.latest_package_release_fs_sha}`}
114
+ alt={`${pkg.unscoped_name} ${availableImages.includes(pkg.default_view || "") ? pkg.default_view : "3D"} view`}
113
115
  className={`object-cover h-full w-full ${imageTransform}`}
114
116
  />
115
117
  </div>
@@ -224,34 +224,33 @@ export function CodeAndPreview({ pkg, projectUrl }: Props) {
224
224
  pkgFilesLoaded={!isLoading}
225
225
  />
226
226
  </div>
227
- {state.showPreview && (
228
- <div
229
- className={cn(
230
- "flex p-0 flex-col min-h-[640px]",
231
- state.fullScreen
232
- ? "fixed inset-0 z-50 bg-white p-4 overflow-hidden"
233
- : "w-full md:w-1/2",
234
- )}
235
- >
236
- <SuspenseRunFrame
237
- showRunButton
238
- forceLatestEvalVersion
239
- onRenderStarted={() =>
240
- setState((prev) => ({ ...prev, lastRunCode: currentFileCode }))
241
- }
242
- onRenderFinished={({ circuitJson }) => {
243
- setState((prev) => ({ ...prev, circuitJson }))
244
- toastManualEditConflicts(circuitJson, toast)
245
- }}
246
- mainComponentPath={mainComponentPath}
247
- onEditEvent={(event) => {
248
- handleEditEvent(event)
249
- }}
250
- fsMap={fsMap ?? {}}
251
- projectUrl={projectUrl}
252
- />
253
- </div>
254
- )}
227
+ <div
228
+ className={cn(
229
+ "flex p-0 flex-col min-h-[640px]",
230
+ state.fullScreen
231
+ ? "fixed inset-0 z-50 bg-white p-4 overflow-hidden"
232
+ : "w-full md:w-1/2",
233
+ !state.showPreview && "hidden",
234
+ )}
235
+ >
236
+ <SuspenseRunFrame
237
+ showRunButton
238
+ forceLatestEvalVersion
239
+ onRenderStarted={() =>
240
+ setState((prev) => ({ ...prev, lastRunCode: currentFileCode }))
241
+ }
242
+ onRenderFinished={({ circuitJson }) => {
243
+ setState((prev) => ({ ...prev, circuitJson }))
244
+ toastManualEditConflicts(circuitJson, toast)
245
+ }}
246
+ mainComponentPath={mainComponentPath}
247
+ onEditEvent={(event) => {
248
+ handleEditEvent(event)
249
+ }}
250
+ fsMap={fsMap ?? {}}
251
+ projectUrl={projectUrl}
252
+ />
253
+ </div>
255
254
  </div>
256
255
  <NewPackageSaveDialog initialIsPrivate={false} onSave={savePackage} />
257
256
  <DiscardChangesDialog onConfirm={handleDiscardChanges} />
@@ -0,0 +1,30 @@
1
+ import { useMutation, useQueryClient } from "react-query"
2
+ import { useAxios } from "@/hooks/use-axios"
3
+ import type { Datasheet } from "fake-snippets-api/lib/db/schema"
4
+
5
+ export const useCreateDatasheet = ({
6
+ onSuccess,
7
+ }: { onSuccess?: (datasheet: Datasheet) => void } = {}) => {
8
+ const axios = useAxios()
9
+ const queryClient = useQueryClient()
10
+
11
+ return useMutation(
12
+ ["createDatasheet"],
13
+ async ({ chip_name }: { chip_name: string }) => {
14
+ const { data } = await axios.post("/datasheets/create", { chip_name })
15
+ await axios.get("/_fake/run_async_tasks")
16
+ return data.datasheet as Datasheet
17
+ },
18
+ {
19
+ onSuccess: (datasheet, variables) => {
20
+ if (variables?.chip_name) {
21
+ queryClient.invalidateQueries(["datasheet", variables.chip_name])
22
+ }
23
+ onSuccess?.(datasheet)
24
+ },
25
+ onError: (error: any) => {
26
+ console.error("Error creating datasheet:", error)
27
+ },
28
+ },
29
+ )
30
+ }
@@ -0,0 +1,18 @@
1
+ import { useQuery } from "react-query"
2
+ import { useAxios } from "@/hooks/use-axios"
3
+ import type { Datasheet } from "fake-snippets-api/lib/db/schema"
4
+
5
+ export const useDatasheet = (chipName: string | null) => {
6
+ const axios = useAxios()
7
+ return useQuery<Datasheet, Error & { status: number }>(
8
+ ["datasheet", chipName],
9
+ async () => {
10
+ if (!chipName) throw new Error("chip name required")
11
+ const { data } = await axios.get("/datasheets/get", {
12
+ params: { chip_name: chipName },
13
+ })
14
+ return data.datasheet as Datasheet
15
+ },
16
+ { enabled: Boolean(chipName), retry: false, refetchOnWindowFocus: false },
17
+ )
18
+ }
@@ -71,7 +71,11 @@ export const DashboardPage = () => {
71
71
  const { data: latestPackages } = useQuery<Package[]>(
72
72
  "latestPackages",
73
73
  async () => {
74
- const response = await axios.get("/packages/list_latest")
74
+ const response = await axios.get("/packages/list_latest", {
75
+ params: {
76
+ limit: 10,
77
+ },
78
+ })
75
79
  return response.data.packages
76
80
  },
77
81
  {
@@ -0,0 +1,87 @@
1
+ import { useParams } from "wouter"
2
+ import { useDatasheet } from "@/hooks/use-datasheet"
3
+ import { useCreateDatasheet } from "@/hooks/use-create-datasheet"
4
+ import Header from "@/components/Header"
5
+ import Footer from "@/components/Footer"
6
+ import type { Datasheet } from "fake-snippets-api/lib/db/schema"
7
+
8
+ export const DatasheetPage = () => {
9
+ const { chipName } = useParams<{ chipName: string }>()
10
+ const datasheetQuery = useDatasheet(chipName)
11
+ const createDatasheet = useCreateDatasheet()
12
+
13
+ const handleCreate = () => {
14
+ if (!chipName) return
15
+ createDatasheet.mutate({ chip_name: chipName })
16
+ }
17
+
18
+ return (
19
+ <div className="min-h-screen flex flex-col">
20
+ <Header />
21
+ <main className="container mx-auto flex-1 px-4 py-8">
22
+ <h1 className="text-3xl font-bold mb-6">{chipName} Datasheet</h1>
23
+ {datasheetQuery.isLoading ? (
24
+ <p>Loading...</p>
25
+ ) : datasheetQuery.data ? (
26
+ <div>
27
+ <h2 className="text-xl font-semibold mb-2">Pin Information</h2>
28
+ {datasheetQuery.data.pin_information ? (
29
+ <table className="table-auto border-collapse mb-6">
30
+ <thead>
31
+ <tr>
32
+ <th className="border px-2 py-1">Pin</th>
33
+ <th className="border px-2 py-1">Name</th>
34
+ <th className="border px-2 py-1">Description</th>
35
+ </tr>
36
+ </thead>
37
+ <tbody>
38
+ {datasheetQuery.data.pin_information.map((pin) => (
39
+ <tr key={pin.pin_number}>
40
+ <td className="border px-2 py-1">{pin.pin_number}</td>
41
+ <td className="border px-2 py-1">{pin.name}</td>
42
+ <td className="border px-2 py-1">{pin.description}</td>
43
+ </tr>
44
+ ))}
45
+ </tbody>
46
+ </table>
47
+ ) : (
48
+ <p>No pin information available.</p>
49
+ )}
50
+
51
+ <h2 className="text-xl font-semibold mb-2">PDFs</h2>
52
+ {datasheetQuery.data.datasheet_pdf_urls ? (
53
+ <ul className="list-disc pl-5">
54
+ {datasheetQuery.data.datasheet_pdf_urls.map((url) => (
55
+ <li key={url}>
56
+ <a href={url} className="text-blue-600 underline">
57
+ {url}
58
+ </a>
59
+ </li>
60
+ ))}
61
+ </ul>
62
+ ) : (
63
+ <p>No datasheet PDFs available.</p>
64
+ )}
65
+ </div>
66
+ ) : datasheetQuery.error &&
67
+ (datasheetQuery.error as any).status === 404 ? (
68
+ <div>
69
+ <p>No datasheet found.</p>
70
+ <button
71
+ className="mt-2 px-4 py-2 bg-blue-500 text-white rounded"
72
+ onClick={handleCreate}
73
+ disabled={createDatasheet.isLoading}
74
+ >
75
+ {createDatasheet.isLoading ? "Creating..." : "Create Datasheet"}
76
+ </button>
77
+ </div>
78
+ ) : (
79
+ <p>Error loading datasheet.</p>
80
+ )}
81
+ </main>
82
+ <Footer />
83
+ </div>
84
+ )
85
+ }
86
+
87
+ export default DatasheetPage