@tscircuit/fake-snippets 0.0.82 → 0.0.84

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 (44) hide show
  1. package/README.md +5 -2
  2. package/bun-tests/fake-snippets-api/routes/ai_reviews/create.test.ts +12 -0
  3. package/bun-tests/fake-snippets-api/routes/ai_reviews/get.test.ts +16 -0
  4. package/bun-tests/fake-snippets-api/routes/ai_reviews/list.test.ts +14 -0
  5. package/bun-tests/fake-snippets-api/routes/ai_reviews/process_review.test.ts +16 -0
  6. package/bun-tests/fake-snippets-api/routes/package_releases/create.test.ts +3 -3
  7. package/bun.lock +26 -37
  8. package/dist/bundle.js +590 -427
  9. package/dist/index.d.ts +83 -11
  10. package/dist/index.js +50 -2
  11. package/dist/schema.d.ts +116 -15
  12. package/dist/schema.js +17 -2
  13. package/fake-snippets-api/lib/db/db-client.ts +40 -0
  14. package/fake-snippets-api/lib/db/schema.ts +17 -1
  15. package/fake-snippets-api/lib/public-mapping/public-map-package-release.ts +14 -1
  16. package/fake-snippets-api/routes/api/_fake/ai_reviews/process_review.ts +31 -0
  17. package/fake-snippets-api/routes/api/ai_reviews/create.ts +22 -0
  18. package/fake-snippets-api/routes/api/ai_reviews/get.ts +24 -0
  19. package/fake-snippets-api/routes/api/ai_reviews/list.ts +14 -0
  20. package/fake-snippets-api/routes/api/package_releases/get.ts +11 -3
  21. package/fake-snippets-api/routes/api/package_releases/list.ts +8 -1
  22. package/package.json +4 -3
  23. package/src/App.tsx +0 -2
  24. package/src/ContextProviders.tsx +1 -1
  25. package/src/components/Header2.tsx +8 -18
  26. package/src/components/PackageBuildsPage/package-build-header.tsx +14 -2
  27. package/src/components/SearchComponent.tsx +46 -8
  28. package/src/components/ViewPackagePage/hooks/use-toast.tsx +70 -0
  29. package/src/components/ViewSnippetHeader.tsx +9 -6
  30. package/src/components/dialogs/edit-package-details-dialog.tsx +5 -10
  31. package/src/components/dialogs/{import-snippet-dialog.tsx → import-package-dialog.tsx} +25 -24
  32. package/src/components/package-port/CodeEditorHeader.tsx +7 -6
  33. package/src/components/ui/toaster.tsx +1 -33
  34. package/src/hooks/use-current-package-release.ts +10 -1
  35. package/src/hooks/use-fork-package-mutation.ts +4 -3
  36. package/src/hooks/use-package-release.ts +15 -14
  37. package/src/hooks/use-sign-in.ts +10 -8
  38. package/src/hooks/use-toast.tsx +50 -169
  39. package/src/hooks/useFileManagement.ts +74 -12
  40. package/src/hooks/useForkPackageMutation.ts +2 -1
  41. package/src/hooks/useForkSnippetMutation.ts +2 -1
  42. package/src/pages/authorize.tsx +164 -8
  43. package/src/pages/view-package.tsx +1 -0
  44. package/src/components/ViewPackagePage/hooks/use-toast.ts +0 -191
@@ -1,4 +1,4 @@
1
- import React, { useState, useEffect } from "react"
1
+ import { useState } from "react"
2
2
  import {
3
3
  Select,
4
4
  SelectContent,
@@ -110,12 +110,12 @@ export const EditPackageDetailsDialog = ({
110
110
 
111
111
  const response = await axios.post("/packages/update", {
112
112
  package_id: packageId,
113
- description: formData.description,
114
- website: formData.website,
113
+ description: formData.description.trim(),
114
+ website: formData.website.trim(),
115
115
  is_private: formData.visibility == "private",
116
116
  default_view: formData.defaultView,
117
117
  ...(formData.unscopedPackageName !== unscopedPackageName && {
118
- name: formData.unscopedPackageName,
118
+ name: formData.unscopedPackageName.trim(),
119
119
  }),
120
120
  })
121
121
  if (response.status !== 200)
@@ -148,12 +148,7 @@ export const EditPackageDetailsDialog = ({
148
148
  })
149
149
  }
150
150
  }
151
- console.log(
152
- "formData.unscopedPackageName",
153
- formData.unscopedPackageName,
154
- "unscopedPackageName",
155
- unscopedPackageName,
156
- )
151
+
157
152
  if (formData.unscopedPackageName !== unscopedPackageName) {
158
153
  // Use router for client-side navigation
159
154
  window.history.replaceState(
@@ -1,6 +1,6 @@
1
1
  import { useAxios } from "@/hooks/use-axios"
2
2
  import { useDebounce } from "@/hooks/use-debounce"
3
- import type { Snippet } from "fake-snippets-api/lib/db/schema"
3
+ import type { Package } from "fake-snippets-api/lib/db/schema"
4
4
  import { useState } from "react"
5
5
  import { useQuery } from "react-query"
6
6
  import { Button } from "../ui/button"
@@ -8,25 +8,25 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle } from "../ui/dialog"
8
8
  import { Input } from "../ui/input"
9
9
  import { createUseDialog } from "./create-use-dialog"
10
10
 
11
- export const ImportSnippetDialog = ({
11
+ export const ImportPackageDialog = ({
12
12
  open,
13
13
  onOpenChange,
14
- onSnippetSelected,
14
+ onPackageSelected,
15
15
  }: {
16
16
  open: boolean
17
17
  onOpenChange: (open: boolean) => any
18
- onSnippetSelected: (snippet: Snippet) => any
18
+ onPackageSelected: (pkg: Package) => any
19
19
  }) => {
20
20
  const [searchText, setSearchText] = useState("")
21
21
  const debouncedSearch = useDebounce(searchText, 300)
22
22
  const axios = useAxios()
23
23
  const { data: snippets, isLoading } = useQuery(
24
- ["snippetSearch", debouncedSearch],
24
+ ["packageSearch", debouncedSearch],
25
25
  async () => {
26
- const response = await axios.get(
27
- `/snippets/search?q=${encodeURIComponent(debouncedSearch)}`,
28
- )
29
- return response.data.snippets.slice(0, 12)
26
+ const response = await axios.post("/packages/search", {
27
+ query: debouncedSearch,
28
+ })
29
+ return response.data.packages
30
30
  },
31
31
  {
32
32
  enabled: debouncedSearch.length > 0,
@@ -35,41 +35,42 @@ export const ImportSnippetDialog = ({
35
35
 
36
36
  return (
37
37
  <Dialog open={open} onOpenChange={onOpenChange}>
38
- <DialogContent className="z-[100]">
38
+ <DialogContent className="z-[100] p-4 sm:p-6">
39
39
  <DialogHeader>
40
- <DialogTitle>Import Snippet</DialogTitle>
40
+ <DialogTitle>Import Package</DialogTitle>
41
41
  </DialogHeader>
42
42
  <Input
43
- placeholder="Search snippets..."
43
+ placeholder="Search packages..."
44
44
  value={searchText}
45
45
  onChange={(e) => setSearchText(e.target.value)}
46
+ className="w-full mb-4"
46
47
  />
47
48
  <div className="h-64 overflow-y-auto">
48
49
  {isLoading ? (
49
- <div>Loading...</div>
50
+ <div className="text-center">Loading...</div>
50
51
  ) : (
51
52
  <ul className="w-full">
52
- {snippets?.map((snippet: Snippet) => (
53
+ {snippets?.map((pkg: Package) => (
53
54
  <li
54
- className="flex items-center my-1 text-xs w-full"
55
- key={snippet.snippet_id}
55
+ className="flex flex-col sm:flex-row items-start sm:items-center my-2 text-sm w-full"
56
+ key={pkg.package_id}
56
57
  >
57
58
  <a
58
- href={`/${snippet.name}`}
59
+ href={`/${pkg.name}`}
59
60
  target="_blank"
60
- className="whitespace-nowrap mr-2 text-blue-500 hover:underline cursor-pointer flex-shrink-0"
61
+ className="text-blue-500 hover:underline cursor-pointer flex-shrink-0 mb-1 sm:mb-0 sm:mr-2"
61
62
  >
62
- {snippet.name}
63
+ {pkg.name}
63
64
  </a>
64
- <div className="text-xs text-gray-500 flex-grow overflow-hidden text-ellipsis whitespace-nowrap">
65
- {snippet.description}
65
+ <div className="text-gray-500 flex-grow overflow-hidden text-ellipsis whitespace-nowrap mb-1 sm:mb-0">
66
+ {pkg.description}
66
67
  </div>
67
68
  <Button
68
69
  size="sm"
69
- className="ml-2 flex-shrink-0"
70
+ className="flex-shrink-0"
70
71
  variant="outline"
71
72
  onClick={() => {
72
- onSnippetSelected(snippet)
73
+ onPackageSelected(pkg)
73
74
  onOpenChange(false)
74
75
  }}
75
76
  >
@@ -85,4 +86,4 @@ export const ImportSnippetDialog = ({
85
86
  )
86
87
  }
87
88
 
88
- export const useImportSnippetDialog = createUseDialog(ImportSnippetDialog)
89
+ export const useImportPackageDialog = createUseDialog(ImportPackageDialog)
@@ -1,7 +1,7 @@
1
1
  import React, { useState, useCallback } from "react"
2
2
  import { Button } from "@/components/ui/button"
3
3
  import { handleManualEditsImportWithSupportForMultipleFiles } from "@/lib/handleManualEditsImportWithSupportForMultipleFiles"
4
- import { useImportSnippetDialog } from "@/components/dialogs/import-snippet-dialog"
4
+ import { useImportPackageDialog } from "@/components/dialogs/import-package-dialog"
5
5
  import { useToast } from "@/hooks/use-toast"
6
6
  import {
7
7
  DropdownMenu,
@@ -19,6 +19,7 @@ import {
19
19
  SelectValue,
20
20
  } from "../ui/select"
21
21
  import { isHiddenFile } from "../ViewPackagePage/utils/is-hidden-file"
22
+ import { Package } from "fake-snippets-api/lib/db/schema"
22
23
 
23
24
  export type FileName = string
24
25
 
@@ -39,8 +40,8 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
39
40
  handleFileChange,
40
41
  entrypointFileName = "index.tsx",
41
42
  }) => {
42
- const { Dialog: ImportSnippetDialog, openDialog: openImportDialog } =
43
- useImportSnippetDialog()
43
+ const { Dialog: ImportPackageDialog, openDialog: openImportDialog } =
44
+ useImportPackageDialog()
44
45
  const { toast } = useToast()
45
46
  const [sidebarOpen, setSidebarOpen] = fileSidebarState
46
47
 
@@ -234,9 +235,9 @@ export const CodeEditorHeader: React.FC<CodeEditorHeaderProps> = ({
234
235
  Format
235
236
  </Button>
236
237
  </div>
237
- <ImportSnippetDialog
238
- onSnippetSelected={(snippet: any) => {
239
- const newContent = `import {} from "@tsci/${snippet.owner_name}.${snippet.unscoped_name}"\n${files[currentFile || ""]}`
238
+ <ImportPackageDialog
239
+ onPackageSelected={(pkg: Package) => {
240
+ const newContent = `import {} from "@tsci/${pkg.owner_github_username}.${pkg.unscoped_name}"\n${files[currentFile || ""]}`
240
241
  updateFileContent(currentFile, newContent)
241
242
  }}
242
243
  />
@@ -1,33 +1 @@
1
- import { useToast } from "@/hooks/use-toast"
2
- import {
3
- Toast,
4
- ToastClose,
5
- ToastDescription,
6
- ToastProvider,
7
- ToastTitle,
8
- ToastViewport,
9
- } from "@/components/ui/toast"
10
-
11
- export function Toaster() {
12
- const { toasts } = useToast()
13
-
14
- return (
15
- <ToastProvider>
16
- {toasts.map(function ({ id, title, description, action, ...props }) {
17
- return (
18
- <Toast key={id} {...props}>
19
- <div className="grid gap-1">
20
- {title && <ToastTitle>{title}</ToastTitle>}
21
- {description && (
22
- <ToastDescription>{description}</ToastDescription>
23
- )}
24
- </div>
25
- {action}
26
- <ToastClose />
27
- </Toast>
28
- )
29
- })}
30
- <ToastViewport />
31
- </ToastProvider>
32
- )
33
- }
1
+ export { Toaster } from "react-hot-toast"
@@ -4,6 +4,7 @@ import { usePackageRelease } from "./use-package-release"
4
4
  import { useUrlParams } from "./use-url-params"
5
5
 
6
6
  export const useCurrentPackageRelease = (options?: {
7
+ include_ai_review?: boolean
7
8
  include_logs?: boolean
8
9
  refetchInterval?: number
9
10
  }) => {
@@ -26,9 +27,17 @@ export const useCurrentPackageRelease = (options?: {
26
27
  query = { package_id: packageId, is_latest: true }
27
28
  }
28
29
 
30
+ if (query && options?.include_logs !== undefined) {
31
+ query.include_logs = options.include_logs
32
+ }
33
+
34
+ if (query && options?.include_ai_review !== undefined) {
35
+ query.include_ai_review = options.include_ai_review
36
+ }
37
+
29
38
  const { data: packageRelease, ...rest } = usePackageRelease(query, {
30
- include_logs: options?.include_logs ?? false,
31
39
  refetchInterval: options?.refetchInterval,
32
40
  })
41
+
33
42
  return { packageRelease, ...rest }
34
43
  }
@@ -42,16 +42,17 @@ export const useForkPackageMutation = ({
42
42
  onSuccess?.(result)
43
43
  },
44
44
  onError: (error: any) => {
45
- if (error.data.error_code === "cannot_fork_own_package") {
45
+ const message = error?.data?.error?.message
46
+ if (error?.data?.error_code === "cannot_fork_own_package") {
46
47
  toast({
47
48
  title: "Cannot Fork Package",
48
- description: "You cannot fork your own package.",
49
+ description: message || "You cannot fork your own package.",
49
50
  })
50
51
  return
51
52
  }
52
53
  toast({
53
54
  title: "Error",
54
- description: "Failed to fork package. Please try again.",
55
+ description: message || "Failed to fork package. Please try again.",
55
56
  variant: "destructive",
56
57
  })
57
58
  },
@@ -2,7 +2,7 @@ import type { PackageRelease } from "fake-snippets-api/lib/db/schema"
2
2
  import { type UseQueryOptions, useQuery } from "react-query"
3
3
  import { useAxios } from "./use-axios"
4
4
 
5
- type PackageReleaseQuery =
5
+ type PackageReleaseQuery = (
6
6
  | {
7
7
  package_release_id: string
8
8
  }
@@ -17,29 +17,30 @@ type PackageReleaseQuery =
17
17
  package_id: string
18
18
  is_latest: boolean
19
19
  }
20
+ ) & {
21
+ include_logs?: boolean | null | undefined
22
+ include_ai_review?: boolean | null | undefined
23
+ }
20
24
 
21
25
  export const usePackageRelease = (
22
26
  query: PackageReleaseQuery | null,
23
- options?: { include_logs?: boolean; refetchInterval?: number },
27
+ options?: {
28
+ refetchInterval?: number
29
+ },
24
30
  ) => {
25
31
  const axios = useAxios()
26
32
 
27
33
  return useQuery<PackageRelease, Error & { status: number }>(
28
- ["packageRelease", query, options?.include_logs],
34
+ ["packageRelease", query],
29
35
  async () => {
30
36
  if (!query) return
31
37
 
32
- const { data } = await axios.post(
33
- "/package_releases/get",
34
- query,
35
- options?.include_logs
36
- ? {
37
- params: {
38
- include_logs: true,
39
- },
40
- }
41
- : undefined,
42
- )
38
+ const { data } = await axios.post("/package_releases/get", query, {
39
+ params: {
40
+ include_logs: query.include_logs,
41
+ include_ai_review: query.include_ai_review,
42
+ },
43
+ })
43
44
 
44
45
  if (!data.package_release) {
45
46
  throw new Error("Package release not found")
@@ -7,16 +7,18 @@ export const useSignIn = () => {
7
7
  const isUsingFakeApi = useIsUsingFakeApi()
8
8
  const setSession = useGlobalStore((s) => s.setSession)
9
9
  return () => {
10
+ const currentUrl = window.location.href.replace("127.0.0.1", "localhost")
11
+ const nextUrl = `${window.location.origin.replace("127.0.0.1", "localhost")}/authorize?redirect=${encodeURIComponent(currentUrl)}`
10
12
  if (!isUsingFakeApi) {
11
- const nextUrl = window.location.origin.replace("127.0.0.1", "localhost")
12
- window.location.href = `${snippetsBaseApiUrl}/internal/oauth/github/authorize?next=${nextUrl}/authorize`
13
+ window.location.href = `${snippetsBaseApiUrl}/internal/oauth/github/authorize?next=${encodeURIComponent(nextUrl)}`
13
14
  } else {
14
- setSession({
15
- account_id: "account-1234",
16
- github_username: "testuser",
17
- token: "1234",
18
- session_id: "session-1234",
19
- })
15
+ window.location.href = nextUrl
16
+ // setSession({
17
+ // account_id: "account-1234",
18
+ // github_username: "testuser",
19
+ // token: "1234",
20
+ // session_id: "session-1234",
21
+ // })
20
22
  }
21
23
  }
22
24
  }
@@ -1,191 +1,72 @@
1
- import * as React from "react"
1
+ import toastLibrary, { Toaster, type Toast } from "react-hot-toast"
2
+ import React from "react"
2
3
 
3
- import type { ToastActionElement, ToastProps } from "@/components/ui/toast"
4
-
5
- const TOAST_LIMIT = 1
6
- const TOAST_REMOVE_DELAY = 1000000
7
-
8
- export type ToasterToast = ToastProps & {
9
- id: string
4
+ export interface ToasterToast {
10
5
  title?: React.ReactNode
11
6
  description?: React.ReactNode
12
- action?: ToastActionElement
13
- }
14
-
15
- const actionTypes = {
16
- ADD_TOAST: "ADD_TOAST",
17
- UPDATE_TOAST: "UPDATE_TOAST",
18
- DISMISS_TOAST: "DISMISS_TOAST",
19
- REMOVE_TOAST: "REMOVE_TOAST",
20
- } as const
21
-
22
- let count = 0
23
-
24
- function genId() {
25
- count = (count + 1) % Number.MAX_SAFE_INTEGER
26
- return count.toString()
7
+ variant?: "default" | "destructive"
8
+ duration?: number
27
9
  }
28
10
 
29
- type ActionType = typeof actionTypes
30
-
31
- type Action =
32
- | {
33
- type: ActionType["ADD_TOAST"]
34
- toast: ToasterToast
35
- }
36
- | {
37
- type: ActionType["UPDATE_TOAST"]
38
- toast: Partial<ToasterToast>
39
- }
40
- | {
41
- type: ActionType["DISMISS_TOAST"]
42
- toastId?: ToasterToast["id"]
43
- }
44
- | {
45
- type: ActionType["REMOVE_TOAST"]
46
- toastId?: ToasterToast["id"]
47
- }
48
-
49
- interface State {
50
- toasts: ToasterToast[]
11
+ function ToastContent({
12
+ title,
13
+ description,
14
+ variant,
15
+ t,
16
+ }: ToasterToast & { t: Toast }) {
17
+ return (
18
+ <div
19
+ className={`rounded-md border p-4 shadow-lg transition-all ${
20
+ t.visible
21
+ ? "animate-in fade-in slide-in-from-top-full"
22
+ : "animate-out fade-out slide-out-to-right-full"
23
+ } ${
24
+ variant === "destructive"
25
+ ? "border-red-500 bg-red-500 text-slate-50"
26
+ : "border-slate-200 bg-white text-slate-950 dark:bg-slate-950 dark:text-slate-50"
27
+ }`}
28
+ >
29
+ {title && <div className="text-sm font-semibold">{title}</div>}
30
+ {description && <div className="text-sm opacity-90">{description}</div>}
31
+ </div>
32
+ )
51
33
  }
52
34
 
53
- const toastTimeouts = new Map<string, ReturnType<typeof setTimeout>>()
54
-
55
- const addToRemoveQueue = (toastId: string) => {
56
- if (toastTimeouts.has(toastId)) {
57
- return
35
+ const toast = ({
36
+ duration,
37
+ description,
38
+ variant = "default",
39
+ title,
40
+ }: ToasterToast) => {
41
+ if (description) {
42
+ return toastLibrary.custom(
43
+ (t) => (
44
+ <ToastContent
45
+ title={title}
46
+ description={description}
47
+ variant={variant}
48
+ t={t}
49
+ />
50
+ ),
51
+ { duration },
52
+ )
58
53
  }
59
54
 
60
- const timeout = setTimeout(() => {
61
- toastTimeouts.delete(toastId)
62
- dispatch({
63
- type: "REMOVE_TOAST",
64
- toastId: toastId,
65
- })
66
- }, TOAST_REMOVE_DELAY)
67
-
68
- toastTimeouts.set(toastId, timeout)
69
- }
70
-
71
- export const reducer = (state: State, action: Action): State => {
72
- switch (action.type) {
73
- case "ADD_TOAST":
74
- return {
75
- ...state,
76
- toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT),
77
- }
78
-
79
- case "UPDATE_TOAST":
80
- return {
81
- ...state,
82
- toasts: state.toasts.map((t) =>
83
- t.id === action.toast.id ? { ...t, ...action.toast } : t,
84
- ),
85
- }
86
-
87
- case "DISMISS_TOAST": {
88
- const { toastId } = action
89
-
90
- // ! Side effects ! - This could be extracted into a dismissToast() action,
91
- // but I'll keep it here for simplicity
92
- if (toastId) {
93
- addToRemoveQueue(toastId)
94
- } else {
95
- state.toasts.forEach((toast) => {
96
- addToRemoveQueue(toast.id)
97
- })
98
- }
99
-
100
- return {
101
- ...state,
102
- toasts: state.toasts.map((t) =>
103
- t.id === toastId || toastId === undefined
104
- ? {
105
- ...t,
106
- open: false,
107
- }
108
- : t,
109
- ),
110
- }
111
- }
112
- case "REMOVE_TOAST":
113
- if (action.toastId === undefined) {
114
- return {
115
- ...state,
116
- toasts: [],
117
- }
118
- }
119
- return {
120
- ...state,
121
- toasts: state.toasts.filter((t) => t.id !== action.toastId),
122
- }
55
+ if (variant === "destructive") {
56
+ return toastLibrary.error(<>{title}</>, { duration })
123
57
  }
124
- }
125
-
126
- const listeners: Array<(state: State) => void> = []
127
-
128
- let memoryState: State = { toasts: [] }
129
-
130
- function dispatch(action: Action) {
131
- memoryState = reducer(memoryState, action)
132
- listeners.forEach((listener) => {
133
- listener(memoryState)
134
- })
135
- }
136
-
137
- type Toast = Omit<ToasterToast, "id">
138
58
 
139
- function toast({ ...props }: Toast) {
140
- const id = genId()
141
-
142
- const update = (props: ToasterToast) =>
143
- dispatch({
144
- type: "UPDATE_TOAST",
145
- toast: { ...props, id },
146
- })
147
- const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id })
148
-
149
- dispatch({
150
- type: "ADD_TOAST",
151
- toast: {
152
- ...props,
153
- id,
154
- open: true,
155
- onOpenChange: (open) => {
156
- if (!open) dismiss()
157
- },
158
- },
159
- })
160
-
161
- return {
162
- id: id,
163
- dismiss,
164
- update,
165
- }
59
+ return toastLibrary(<>{title}</>, { duration })
166
60
  }
167
61
 
168
62
  function useToast() {
169
- const [state, setState] = React.useState<State>(memoryState)
170
-
171
- React.useEffect(() => {
172
- listeners.push(setState)
173
- return () => {
174
- const index = listeners.indexOf(setState)
175
- if (index > -1) {
176
- listeners.splice(index, 1)
177
- }
178
- }
179
- }, [state])
180
-
181
63
  return {
182
- ...state,
183
64
  toast,
184
- dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }),
65
+ dismiss: toastLibrary.dismiss,
185
66
  }
186
67
  }
187
68
 
188
- export { useToast, toast }
69
+ export { useToast, toast, Toaster }
189
70
 
190
71
  export function useNotImplementedToast() {
191
72
  const { toast } = useToast()