@tscircuit/fake-snippets 0.0.36 → 0.0.38

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.
Files changed (31) hide show
  1. package/bun-tests/fake-snippets-api/routes/package_files/create_or_update.test.ts +575 -0
  2. package/bun-tests/fake-snippets-api/routes/package_files/delete.test.ts +233 -0
  3. package/bun-tests/fake-snippets-api/routes/snippets/list_newest.test.ts +2 -2
  4. package/dist/bundle.js +499 -249
  5. package/dist/index.d.ts +23 -4
  6. package/dist/index.js +30 -2
  7. package/fake-snippets-api/lib/db/db-client.ts +29 -1
  8. package/fake-snippets-api/lib/db/schema.ts +3 -0
  9. package/fake-snippets-api/routes/api/package_files/create_or_update.ts +179 -0
  10. package/fake-snippets-api/routes/api/package_files/delete.ts +106 -0
  11. package/fake-snippets-api/routes/api/snippets/{list_newest.ts → list_latest.ts} +2 -2
  12. package/package.json +1 -1
  13. package/scripts/generate-sitemap.ts +1 -1
  14. package/src/App.tsx +2 -2
  15. package/src/components/EditorNav.tsx +4 -4
  16. package/src/components/Footer.tsx +9 -3
  17. package/src/components/HiddenFilesDropdown.tsx +44 -0
  18. package/src/components/LatestSnippets.tsx +1 -1
  19. package/src/components/ViewPackagePage/components/package-header.tsx +0 -4
  20. package/src/components/ViewPackagePage/components/tab-views/3d-view.tsx +1 -1
  21. package/src/components/ViewPackagePage/components/tab-views/bom-view.tsx +1 -1
  22. package/src/components/ViewPackagePage/components/tab-views/files-view.tsx +23 -2
  23. package/src/components/ViewPackagePage/components/tab-views/schematic-view.tsx +1 -0
  24. package/src/components/dialogs/confirm-delete-package-dialog.tsx +48 -0
  25. package/src/hooks/use-delete-package.ts +40 -0
  26. package/src/hooks/use-fork-package-mutation.ts +14 -61
  27. package/src/pages/dashboard.tsx +8 -8
  28. package/src/pages/latest.tsx +212 -0
  29. package/src/pages/user-profile.tsx +15 -14
  30. package/src/components/dialogs/confirm-delete-snippet-dialog.tsx +0 -80
  31. package/src/pages/newest.tsx +0 -16
@@ -11,7 +11,7 @@ export const LatestSnippets: React.FC = () => {
11
11
  isLoading,
12
12
  error,
13
13
  } = useQuery<Snippet[]>("latestSnippets", async () => {
14
- const response = await axios.get("/snippets/list_newest")
14
+ const response = await axios.get("/snippets/list_latest")
15
15
  return response.data.snippets
16
16
  })
17
17
 
@@ -61,10 +61,6 @@ export default function PackageHeader({
61
61
  const handleForkClick = async () => {
62
62
  if (!packageInfo?.package_id) return
63
63
  await forkPackage(packageInfo.package_id)
64
- toast({
65
- title: "Forked package",
66
- description: "Package forked successfully",
67
- })
68
64
  }
69
65
 
70
66
  const isStarLoading =
@@ -24,7 +24,7 @@ export default function ThreeDView() {
24
24
 
25
25
  return (
26
26
  <div className="h-[620px]">
27
- <CadViewer circuitJson={circuitJson} />
27
+ <CadViewer clickToInteractEnabled circuitJson={circuitJson} />
28
28
  </div>
29
29
  )
30
30
  }
@@ -23,7 +23,7 @@ export default function BOMView() {
23
23
  }
24
24
 
25
25
  return (
26
- <div className="border border-gray-200 dark:border-[#30363d] rounded-md p-4 mb-4 bg-white dark:bg-[#0d1117] w-full h-[620px]">
26
+ <div className="border border-gray-200 dark:border-[#30363d] rounded-md p-4 mb-4 bg-white dark:bg-[#0d1117] w-full h-[620px] overflow-y-scroll">
27
27
  <BomTable circuitJson={circuitJson} />
28
28
  </div>
29
29
  )
@@ -5,6 +5,14 @@ import { FileText, Folder } from "lucide-react"
5
5
  import { useMemo, useState } from "react"
6
6
  import { isHiddenFile } from "../../utils/is-hidden-file"
7
7
  import { isWithinDirectory } from "../../utils/is-within-directory"
8
+ import {
9
+ DropdownMenu,
10
+ DropdownMenuContent,
11
+ DropdownMenuItem,
12
+ DropdownMenuTrigger,
13
+ } from "@/components/ui/dropdown-menu"
14
+ import { Settings } from "lucide-react"
15
+ import HiddenFilesDropdown from "@/components/HiddenFilesDropdown"
8
16
 
9
17
  interface Directory {
10
18
  type: "directory"
@@ -41,6 +49,7 @@ export default function FilesView({
41
49
  onFileClicked,
42
50
  }: FilesViewProps) {
43
51
  const [activeDir, setActiveDir] = useState("")
52
+ const [showHiddenFiles, setShowHiddenFiles] = useState(false)
44
53
 
45
54
  // Parse package files to determine directories and files structure
46
55
  const { directories, files } = useMemo(() => {
@@ -52,7 +61,7 @@ export default function FilesView({
52
61
  const filesList: File[] = []
53
62
 
54
63
  packageFiles
55
- .filter((file) => !isHiddenFile(file.file_path))
64
+ .filter((file) => showHiddenFiles || !isHiddenFile(file.file_path))
56
65
  .forEach((file) => {
57
66
  // Extract directory path
58
67
  const pathParts = file.file_path.split("/")
@@ -64,6 +73,7 @@ export default function FilesView({
64
73
  currentPath += (currentPath ? "/" : "") + part
65
74
  // Only add directory if it contains visible files
66
75
  if (
76
+ showHiddenFiles ||
67
77
  packageFiles.some(
68
78
  (f) =>
69
79
  f.file_path.startsWith(currentPath + "/") &&
@@ -100,7 +110,7 @@ export default function FilesView({
100
110
  directories: dirsList,
101
111
  files: filesList,
102
112
  }
103
- }, [packageFiles])
113
+ }, [packageFiles, showHiddenFiles])
104
114
  // Format date for display
105
115
  const formatDate = (dateString: string) => {
106
116
  const date = new Date(dateString)
@@ -155,6 +165,8 @@ export default function FilesView({
155
165
  setActiveDir(parentDir)
156
166
  }
157
167
 
168
+ const toggleHiddenFiles = () => setShowHiddenFiles((prev) => !prev)
169
+
158
170
  if (isLoading) {
159
171
  return (
160
172
  <div className="mb-4 border border-gray-200 dark:border-[#30363d] rounded-md overflow-hidden">
@@ -197,6 +209,11 @@ export default function FilesView({
197
209
  <span>
198
210
  {files.length} files, {directories.length} directories
199
211
  </span>
212
+
213
+ <HiddenFilesDropdown
214
+ showHiddenFiles={showHiddenFiles}
215
+ onToggleHiddenFiles={toggleHiddenFiles}
216
+ />
200
217
  </div>
201
218
 
202
219
  {/* Mobile view */}
@@ -208,6 +225,10 @@ export default function FilesView({
208
225
  </div>
209
226
  <div className="flex items-center text-xs text-gray-500 dark:text-[#8b949e]">
210
227
  <span>{files.length + directories.length} items</span>
228
+ <HiddenFilesDropdown
229
+ showHiddenFiles={showHiddenFiles}
230
+ onToggleHiddenFiles={toggleHiddenFiles}
231
+ />
211
232
  </div>
212
233
  </div>
213
234
  </div>
@@ -26,6 +26,7 @@ export default function SchematicView() {
26
26
  return (
27
27
  <div className="h-[620px]">
28
28
  <SchematicViewer
29
+ clickToInteractEnabled
29
30
  circuitJson={circuitJson}
30
31
  containerStyle={{
31
32
  height: 620,
@@ -0,0 +1,48 @@
1
+ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog"
2
+ import { Button } from "../ui/button"
3
+ import { createUseDialog } from "./create-use-dialog"
4
+ import { useDeletePackage } from "@/hooks/use-delete-package"
5
+
6
+ export const ConfirmDeletePackageDialog = ({
7
+ open,
8
+ onOpenChange,
9
+ packageId,
10
+ packageName,
11
+ }: {
12
+ open: boolean
13
+ onOpenChange: (open: boolean) => void
14
+ packageId: string
15
+ packageName: string
16
+ }) => {
17
+ const { mutate: deletePackage, isLoading } = useDeletePackage({
18
+ onSuccess: () => onOpenChange(false),
19
+ })
20
+
21
+ return (
22
+ <Dialog open={open} onOpenChange={onOpenChange}>
23
+ <DialogContent>
24
+ <DialogHeader>
25
+ <DialogTitle>Confirm Delete Package</DialogTitle>
26
+ </DialogHeader>
27
+ <p>Are you sure you want to delete the package "{packageName}"?</p>
28
+ <p>This action cannot be undone.</p>
29
+ <div className="flex justify-end space-x-2 mt-4">
30
+ <Button variant="outline" onClick={() => onOpenChange(false)}>
31
+ Cancel
32
+ </Button>
33
+ <Button
34
+ variant="destructive"
35
+ onClick={() => deletePackage({ package_id: packageId })}
36
+ disabled={isLoading}
37
+ >
38
+ {isLoading ? "Deleting..." : "Delete"}
39
+ </Button>
40
+ </div>
41
+ </DialogContent>
42
+ </Dialog>
43
+ )
44
+ }
45
+
46
+ export const useConfirmDeletePackageDialog = createUseDialog(
47
+ ConfirmDeletePackageDialog,
48
+ )
@@ -0,0 +1,40 @@
1
+ import { useMutation } from "react-query"
2
+ import { useAxios } from "./use-axios"
3
+ import { useToast } from "./use-toast"
4
+
5
+ export const useDeletePackage = ({
6
+ onSuccess,
7
+ }: { onSuccess?: () => void } = {}) => {
8
+ const axios = useAxios()
9
+ const { toast } = useToast()
10
+
11
+ return useMutation(
12
+ ["deletePackage"],
13
+ async ({ package_id }: { package_id: string }) => {
14
+ const response = await axios.post("/packages/delete", {
15
+ package_id,
16
+ })
17
+
18
+ if (!response.data.ok) {
19
+ throw new Error("Failed to delete package")
20
+ }
21
+
22
+ return response.data
23
+ },
24
+ {
25
+ onSuccess: () => {
26
+ toast({
27
+ title: "Package deleted",
28
+ description: "Package deleted successfully",
29
+ })
30
+ onSuccess?.()
31
+ },
32
+ onError: (error: any) => {
33
+ toast({
34
+ title: "Error deleting package",
35
+ description: "Failed to delete package",
36
+ })
37
+ },
38
+ },
39
+ )
40
+ }
@@ -1,15 +1,8 @@
1
- import {
2
- Package,
3
- PackageFile,
4
- PackageRelease,
5
- } from "fake-snippets-api/lib/db/schema"
1
+ import { Package } from "fake-snippets-api/lib/db/schema"
6
2
  import { useMutation } from "react-query"
7
3
  import { useAxios } from "./use-axios"
8
4
  import { useGlobalStore } from "./use-global-store"
9
5
  import { useToast } from "./use-toast"
10
- import { useCreatePackageMutation } from "./use-create-package-mutation"
11
- import { useCreatePackageReleaseMutation } from "./use-create-package-release-mutation"
12
- import { useCreatePackageFilesMutation } from "./use-create-package-files-mutation"
13
6
 
14
7
  export const useForkPackageMutation = ({
15
8
  onSuccess,
@@ -20,73 +13,33 @@ export const useForkPackageMutation = ({
20
13
  const session = useGlobalStore((s) => s.session)
21
14
  const { toast } = useToast()
22
15
 
23
- const { mutateAsync: createPackage } = useCreatePackageMutation()
24
- const { mutateAsync: createRelease } = useCreatePackageReleaseMutation()
25
- const { mutateAsync: createFile } = useCreatePackageFilesMutation()
26
-
27
16
  return useMutation(
28
17
  ["forkPackage"],
29
18
  async (packageId: string) => {
30
19
  if (!session) throw new Error("No session")
31
20
 
32
- // Step 1: Fetch source package data
33
- const { data: packageData } = await axios.get("/packages/get", {
34
- params: { package_id: packageId },
35
- })
36
- const sourcePackage: Package = packageData.package
37
- if (!sourcePackage) throw new Error("Source package not found")
38
-
39
- // Step 2: Fetch latest release
40
- const { data: releaseData } = await axios.post("/package_releases/get", {
41
- package_release_id: sourcePackage.latest_package_release_id,
21
+ const { data } = await axios.post("/packages/fork", {
22
+ package_id: packageId,
42
23
  })
43
- const sourceRelease: PackageRelease = releaseData.package_release
44
- if (!sourceRelease) throw new Error("Source release not found")
45
24
 
46
- // Step 3: Fetch all files for the release
47
- const { data: filesData } = await axios.post("/package_files/list", {
48
- package_release_id: sourceRelease.package_release_id,
49
- })
50
- const sourceFiles: PackageFile[] = filesData.package_files
51
- if (!sourceFiles?.length) throw new Error("No source files found")
52
-
53
- // Step 4: Create new package
54
- const newPackage = await createPackage({
55
- name: `${session.github_username}/${sourcePackage.unscoped_name}`,
56
- description: `Fork of ${sourcePackage.name}`,
57
- })
25
+ const forkedPackage: Package = data.package
26
+ if (!forkedPackage) throw new Error("Failed to fork package")
58
27
 
59
- // Step 5: Create new release
60
- const newRelease = await createRelease({
61
- package_id: newPackage.package_id,
62
- version: sourceRelease.version ?? undefined,
63
- is_latest: true,
64
- })
65
-
66
- // Step 6: Create all files
67
- const newFiles = await Promise.all(
68
- sourceFiles.map((file: PackageFile) =>
69
- createFile({
70
- package_release_id: newRelease.package_release_id,
71
- file_path: file.file_path,
72
- content_text: file.content_text ?? undefined,
73
- }),
74
- ),
75
- )
76
-
77
- return {
78
- package: newPackage,
79
- release: newRelease,
80
- files: newFiles,
81
- }
28
+ return forkedPackage
82
29
  },
83
30
  {
84
31
  onSuccess: (result) => {
85
32
  toast({
86
33
  title: "Package Forked",
87
- description: `Successfully forked package to @${session?.github_username}/${result.package.unscoped_name}`,
34
+ description: `Successfully forked package to @${session?.github_username}/${result.unscoped_name}`,
88
35
  })
89
- onSuccess?.(result.package)
36
+
37
+ const url = new URL(window.location.href)
38
+ url.pathname = `/${session?.github_username}/${result.unscoped_name}`
39
+ url.search = ""
40
+ window.location.href = url.toString()
41
+
42
+ onSuccess?.(result)
90
43
  },
91
44
  onError: (error: any) => {
92
45
  toast({
@@ -18,7 +18,7 @@ export const DashboardPage = () => {
18
18
 
19
19
  const currentUser = useGlobalStore((s) => s.session?.github_username)
20
20
  const [showAllTrending, setShowAllTrending] = useState(false)
21
- const [showAllNewest, setShowAllNewest] = useState(false)
21
+ const [showAllLatest, setShowAllLatest] = useState(false)
22
22
 
23
23
  const {
24
24
  data: mySnippets,
@@ -41,10 +41,10 @@ export const DashboardPage = () => {
41
41
  },
42
42
  )
43
43
 
44
- const { data: newestSnippets } = useQuery<Snippet[]>(
45
- "newestSnippets",
44
+ const { data: latestSnippets } = useQuery<Snippet[]>(
45
+ "latestSnippets",
46
46
  async () => {
47
- const response = await axios.get("/snippets/list_newest")
47
+ const response = await axios.get("/snippets/list_latest")
48
48
  return response.data.snippets
49
49
  },
50
50
  )
@@ -129,10 +129,10 @@ export const DashboardPage = () => {
129
129
  />
130
130
  <div className="mt-8">
131
131
  <SnippetList
132
- title="Newest Snippets"
133
- snippets={newestSnippets}
134
- showAll={showAllNewest}
135
- onToggleShowAll={() => setShowAllNewest(!showAllNewest)}
132
+ title="Latest Snippets"
133
+ snippets={latestSnippets}
134
+ showAll={showAllLatest}
135
+ onToggleShowAll={() => setShowAllLatest(!showAllLatest)}
136
136
  />
137
137
  </div>
138
138
  </div>
@@ -0,0 +1,212 @@
1
+ import React, { useState } from "react"
2
+ import { useQuery } from "react-query"
3
+ import { useAxios } from "@/hooks/use-axios"
4
+ import { Snippet } from "fake-snippets-api/lib/db/schema"
5
+ import Header from "@/components/Header"
6
+ import Footer from "@/components/Footer"
7
+ import {
8
+ Search,
9
+ Tag,
10
+ Calendar,
11
+ Keyboard,
12
+ Cpu,
13
+ Layers,
14
+ LucideBellElectric,
15
+ } from "lucide-react"
16
+ import { Input } from "@/components/ui/input"
17
+ import { Badge } from "@/components/ui/badge"
18
+ import { useSnippetsBaseApiUrl } from "@/hooks/use-snippets-base-api-url"
19
+ import {
20
+ Select,
21
+ SelectContent,
22
+ SelectItem,
23
+ SelectTrigger,
24
+ SelectValue,
25
+ } from "@/components/ui/select"
26
+ import { SnippetCard } from "@/components/SnippetCard"
27
+
28
+ const LatestPage: React.FC = () => {
29
+ const axios = useAxios()
30
+ const apiBaseUrl = useSnippetsBaseApiUrl()
31
+ const [searchQuery, setSearchQuery] = useState("")
32
+ const [category, setCategory] = useState("all")
33
+
34
+ const {
35
+ data: snippets,
36
+ isLoading,
37
+ error,
38
+ } = useQuery<Snippet[]>(
39
+ ["latestSnippets", category],
40
+ async () => {
41
+ const params = category !== "all" ? { tag: category } : {}
42
+ const response = await axios.get("/snippets/list_latest", { params })
43
+ return response.data.snippets
44
+ },
45
+ {
46
+ keepPreviousData: true,
47
+ },
48
+ )
49
+
50
+ const filteredSnippets = snippets?.filter((snippet) => {
51
+ if (!searchQuery) return true
52
+
53
+ const query = searchQuery.toLowerCase().trim()
54
+
55
+ const searchableFields = [
56
+ snippet.unscoped_name.toLowerCase(),
57
+ snippet.owner_name.toLowerCase(),
58
+ (snippet.description || "").toLowerCase(),
59
+ ]
60
+
61
+ return searchableFields.some((field) => {
62
+ const queryWords = query.split(/\s+/).filter((word) => word.length > 0)
63
+ return queryWords.every((word) => field.includes(word))
64
+ })
65
+ })
66
+
67
+ return (
68
+ <div className="min-h-screen flex flex-col bg-gray-50">
69
+ <Header />
70
+ <main className="flex-grow container mx-auto px-4 py-8">
71
+ <div className="mb-8 max-w-3xl">
72
+ <div className="flex items-center gap-2 mb-3">
73
+ <Calendar className="w-6 h-6 text-blue-500" />
74
+ <h1 className="text-4xl font-bold text-gray-900">
75
+ Latest Snippets
76
+ </h1>
77
+ </div>
78
+ <p className="text-lg text-gray-600 mb-4">
79
+ Explore the latest circuit designs from our community. These fresh
80
+ additions showcase new ideas and innovative approaches to circuit
81
+ design.
82
+ </p>
83
+ <div className="flex flex-wrap gap-3">
84
+ <Badge variant="secondary" className="px-3 py-1">
85
+ <Tag className="w-3.5 h-3.5 mr-1" />
86
+ <span>Latest Uploads</span>
87
+ </Badge>
88
+ <Badge variant="secondary" className="px-3 py-1">
89
+ <Calendar className="w-3.5 h-3.5 mr-1" />
90
+ <span>Most Recent First</span>
91
+ </Badge>
92
+ </div>
93
+ </div>
94
+
95
+ <div className="mb-6">
96
+ <div className="flex flex-col sm:flex-row gap-4 mb-4">
97
+ <div className="relative flex-grow">
98
+ <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-4 w-4" />
99
+ <Input
100
+ type="text"
101
+ placeholder="Search latest snippets..."
102
+ value={searchQuery}
103
+ onChange={(e) => setSearchQuery(e.target.value)}
104
+ className="pl-10"
105
+ />
106
+ </div>
107
+ <Select value={category} onValueChange={setCategory}>
108
+ <SelectTrigger className="w-full sm:w-[180px]">
109
+ <SelectValue placeholder="Category" />
110
+ </SelectTrigger>
111
+ <SelectContent>
112
+ <SelectItem value="all">All Categories</SelectItem>
113
+ <SelectItem value="keyboard">
114
+ <div className="flex items-center">
115
+ <Keyboard className="mr-2 h-4 w-4" />
116
+ <span>Keyboards</span>
117
+ </div>
118
+ </SelectItem>
119
+ <SelectItem value="microcontroller">
120
+ <div className="flex items-center">
121
+ <Cpu className="mr-2 h-4 w-4" />
122
+ <span>Microcontrollers</span>
123
+ </div>
124
+ </SelectItem>
125
+ <SelectItem value="connector">
126
+ <div className="flex items-center">
127
+ <Layers className="mr-2 h-4 w-4" />
128
+ <span>Connectors</span>
129
+ </div>
130
+ </SelectItem>
131
+ <SelectItem value="sensor">
132
+ <div className="flex items-center">
133
+ <LucideBellElectric className="mr-2 h-4 w-4" />
134
+ <span>Sensors</span>
135
+ </div>
136
+ </SelectItem>
137
+ </SelectContent>
138
+ </Select>
139
+ </div>
140
+ </div>
141
+
142
+ {isLoading ? (
143
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
144
+ {[...Array(6)].map((_, i) => (
145
+ <div key={i} className="border p-4 rounded-md animate-pulse">
146
+ <div className="flex items-start gap-4">
147
+ <div className="h-16 w-16 flex-shrink-0 rounded-md bg-slate-200"></div>
148
+ <div className="flex-1">
149
+ <div className="h-5 bg-slate-200 rounded w-3/4 mb-2"></div>
150
+ <div className="h-4 bg-slate-200 rounded w-1/2 mb-2"></div>
151
+ <div className="flex gap-2">
152
+ <div className="h-3 bg-slate-200 rounded w-16"></div>
153
+ <div className="h-3 bg-slate-200 rounded w-16"></div>
154
+ </div>
155
+ </div>
156
+ </div>
157
+ </div>
158
+ ))}
159
+ </div>
160
+ ) : error ? (
161
+ <div className="bg-red-50 border border-red-200 text-red-700 p-6 rounded-xl shadow-sm max-w-2xl mx-auto">
162
+ <div className="flex items-start">
163
+ <div className="mr-4 bg-red-100 p-2 rounded-full">
164
+ <Search className="w-6 h-6 text-red-600" />
165
+ </div>
166
+ <div>
167
+ <h3 className="text-lg font-semibold mb-2">
168
+ Error Loading Snippets
169
+ </h3>
170
+ <p className="text-red-600">
171
+ We couldn't load the latest snippets. Please try again later.
172
+ </p>
173
+ </div>
174
+ </div>
175
+ </div>
176
+ ) : filteredSnippets?.length === 0 ? (
177
+ <div className="text-center py-12 px-4">
178
+ <div className="bg-slate-50 inline-flex rounded-full p-4 mb-4">
179
+ <Search className="w-8 h-8 text-slate-400" />
180
+ </div>
181
+ <h3 className="text-xl font-medium text-slate-900 mb-2">
182
+ No Matching Snippets
183
+ </h3>
184
+ <p className="text-slate-500 max-w-md mx-auto mb-6">
185
+ {searchQuery
186
+ ? `No snippets match your search for "${searchQuery}".`
187
+ : category !== "all"
188
+ ? `No ${category} snippets found in the latest list.`
189
+ : "There are no new snippets at the moment."}
190
+ </p>
191
+ </div>
192
+ ) : (
193
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
194
+ {filteredSnippets
195
+ ?.sort((a, b) => b.created_at.localeCompare(a.created_at))
196
+ ?.map((snippet) => (
197
+ <SnippetCard
198
+ key={snippet.snippet_id}
199
+ snippet={snippet}
200
+ baseUrl={apiBaseUrl}
201
+ showOwner={true}
202
+ />
203
+ ))}
204
+ </div>
205
+ )}
206
+ </main>
207
+ <Footer />
208
+ </div>
209
+ )
210
+ }
211
+
212
+ export default LatestPage
@@ -1,19 +1,20 @@
1
- import React, { useState } from "react"
2
- import { useParams } from "wouter"
3
- import { useQuery } from "react-query"
4
- import { useAxios } from "@/hooks/use-axios"
5
- import Header from "@/components/Header"
6
1
  import Footer from "@/components/Footer"
7
- import { Snippet } from "fake-snippets-api/lib/db/schema"
2
+ import Header from "@/components/Header"
3
+ import { SnippetCard } from "@/components/SnippetCard"
4
+ import { useConfirmDeletePackageDialog } from "@/components/dialogs/confirm-delete-package-dialog"
5
+ import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
8
6
  import { Button } from "@/components/ui/button"
9
- import { GitHubLogoIcon } from "@radix-ui/react-icons"
10
7
  import { Input } from "@/components/ui/input"
11
- import { useGlobalStore } from "@/hooks/use-global-store"
12
- import { useConfirmDeleteSnippetDialog } from "@/components/dialogs/confirm-delete-snippet-dialog"
13
- import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"
14
8
  import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"
9
+ import { useAxios } from "@/hooks/use-axios"
10
+ import { useGlobalStore } from "@/hooks/use-global-store"
15
11
  import { useSnippetsBaseApiUrl } from "@/hooks/use-snippets-base-api-url"
16
- import { SnippetCard } from "@/components/SnippetCard"
12
+ import { GitHubLogoIcon } from "@radix-ui/react-icons"
13
+ import type { Snippet } from "fake-snippets-api/lib/db/schema"
14
+ import type React from "react"
15
+ import { useState } from "react"
16
+ import { useQuery } from "react-query"
17
+ import { useParams } from "wouter"
17
18
 
18
19
  export const UserProfilePage = () => {
19
20
  const { username } = useParams()
@@ -23,7 +24,7 @@ export const UserProfilePage = () => {
23
24
  const session = useGlobalStore((s) => s.session)
24
25
  const isCurrentUserProfile = username === session?.github_username
25
26
  const { Dialog: DeleteDialog, openDialog: openDeleteDialog } =
26
- useConfirmDeleteSnippetDialog()
27
+ useConfirmDeletePackageDialog()
27
28
  const [snippetToDelete, setSnippetToDelete] = useState<Snippet | null>(null)
28
29
 
29
30
  const { data: userSnippets, isLoading: isLoadingUserSnippets } = useQuery<
@@ -138,8 +139,8 @@ export const UserProfilePage = () => {
138
139
  </div>
139
140
  {snippetToDelete && (
140
141
  <DeleteDialog
141
- snippetId={snippetToDelete.snippet_id}
142
- snippetName={snippetToDelete.unscoped_name}
142
+ packageId={snippetToDelete.snippet_id}
143
+ packageName={snippetToDelete.unscoped_name}
143
144
  />
144
145
  )}
145
146
  <Footer />