From fae34b0f2da063d8d93686c1997201f67e6d7cf3 Mon Sep 17 00:00:00 2001 From: Visar Date: Sun, 18 May 2025 11:25:45 +0200 Subject: [PATCH 01/49] Added create Page --- src/app/home/create/page.tsx | 4 ++++ src/components/AppSidebar.tsx | 17 ++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 src/app/home/create/page.tsx diff --git a/src/app/home/create/page.tsx b/src/app/home/create/page.tsx new file mode 100644 index 0000000..eb93d0b --- /dev/null +++ b/src/app/home/create/page.tsx @@ -0,0 +1,4 @@ +const Page = () => { + return
Page
; +}; +export default Page; diff --git a/src/components/AppSidebar.tsx b/src/components/AppSidebar.tsx index 8c6d759..9070853 100644 --- a/src/components/AppSidebar.tsx +++ b/src/components/AppSidebar.tsx @@ -12,7 +12,7 @@ import { } from "@/components/ui/sidebar"; import { readCollections } from "@/lib/supabase/collection"; -import { ChevronUp, House, User2 } from "lucide-react"; +import { ChevronUp, House, Plus, User2 } from "lucide-react"; import Link from "next/link"; import CollectionSidebarItem from "./CollectionSidebarItem"; import { getAllSubCollections } from "@/lib/supabase/subCollection"; @@ -48,6 +48,21 @@ export default async function AppSidebar() { + + Actions + + + + + + + Create + + + + + + Premade Collections From 54e4cbddbaaf01dc5a9b91428be4ff2ed08233d0 Mon Sep 17 00:00:00 2001 From: Visar Date: Sun, 18 May 2025 15:15:33 +0200 Subject: [PATCH 02/49] Created the createCollection page with validation and iconSearch. --- package-lock.json | 7 + package.json | 1 + src/app/home/create/CreateCollectionForm.tsx | 203 +++++++++++++++++++ src/app/home/create/page.tsx | 18 +- src/components/SearchedIcons.tsx | 158 +++++++++++++++ src/lib/validation.ts | 18 ++ 6 files changed, 403 insertions(+), 2 deletions(-) create mode 100644 src/app/home/create/CreateCollectionForm.tsx create mode 100644 src/components/SearchedIcons.tsx create mode 100644 src/lib/validation.ts diff --git a/package-lock.json b/package-lock.json index 3eaa2f4..15585ca 100644 --- a/package-lock.json +++ b/package-lock.json @@ -44,6 +44,7 @@ "embla-carousel-react": "^8.6.0", "input-otp": "^1.4.2", "lucide-react": "^0.503.0", + "lucide-static": "^0.511.0", "next": "15.3.1", "next-themes": "^0.4.6", "react": "^19.0.0", @@ -6857,6 +6858,12 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/lucide-static": { + "version": "0.511.0", + "resolved": "https://registry.npmjs.org/lucide-static/-/lucide-static-0.511.0.tgz", + "integrity": "sha512-/MWOjEyGZO84B16BeqbeleinbmeYxOBsbYDt/Yk6xGwMYwDm6dx43jKHMxGDqW9U84qWL13dNvVW0SMADhUSyg==", + "license": "ISC" + }, "node_modules/markdown-table": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", diff --git a/package.json b/package.json index e1b9945..7b5ddef 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "embla-carousel-react": "^8.6.0", "input-otp": "^1.4.2", "lucide-react": "^0.503.0", + "lucide-static": "^0.511.0", "next": "15.3.1", "next-themes": "^0.4.6", "react": "^19.0.0", diff --git a/src/app/home/create/CreateCollectionForm.tsx b/src/app/home/create/CreateCollectionForm.tsx new file mode 100644 index 0000000..98ebadc --- /dev/null +++ b/src/app/home/create/CreateCollectionForm.tsx @@ -0,0 +1,203 @@ +"use client"; + +import SearchedIcons, { SvgIcon } from "@/components/SearchedIcons"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { CollectionType } from "@/lib/supabase/collection"; +import { formCollectionSchema } from "@/lib/validation"; +import { X } from "lucide-react"; +import { useActionState, useState } from "react"; +import { z } from "zod"; + +const CreateCollectionForm = ({ + collections, +}: { + collections: CollectionType[] | null; +}) => { + // TODO: Show the errors to the user + const [errors, setErrors] = useState>({}); + const [parentCollection, setParentCollection] = useState< + CollectionType | undefined + >(undefined); + const [iconSearch, setIconSearch] = useState(""); + const [selectedIcon, setSelectedIcon] = useState< + { selectedIconName: string; svgString: string } | undefined + >(undefined); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const handleFormSubmit = async (prevState: any, formData: FormData) => { + try { + const formValues = { + collectionId: parentCollection?.id, + name: formData.get("name") as string, + icon: selectedIcon?.selectedIconName, + subject: formData.get("subject") as string | undefined, + }; + + console.log(formValues); + await formCollectionSchema.parseAsync(formValues); + + // TODO: write the entry in the database. + let result; + + return result; + } catch (error) { + if (error instanceof z.ZodError) { + const fieldErorrs = error.flatten().fieldErrors; + + setErrors(fieldErorrs as unknown as Record); + console.log(fieldErorrs); + + return { ...prevState, error: "Validation failed", status: "ERROR" }; + } + console.log(error); + + return { + ...prevState, + error: "An unexpected error has occurred", + status: "ERROR", + }; + } + }; + + const [state, formAction, isPending] = useActionState(handleFormSubmit, { + error: "", + status: "INITIAL", + }); + + return ( +
+
+
+
+ +
+ + + + {parentCollection ? ( + + ) : ( + + )} +
+
+
+ + +
+
+ + +
+
+
+ {selectedIcon ? ( +
+
+ +
+ +
+ ) : ( + <> +
+ + setIconSearch(e.target.value)} + /> +
+ { + setIconSearch(selectedIconName); + setSelectedIcon({ selectedIconName, svgString }); + }} + /> + + )} +
+
+
+ +
+
+ ); +}; + +export default CreateCollectionForm; diff --git a/src/app/home/create/page.tsx b/src/app/home/create/page.tsx index eb93d0b..eab6c82 100644 --- a/src/app/home/create/page.tsx +++ b/src/app/home/create/page.tsx @@ -1,4 +1,18 @@ -const Page = () => { - return
Page
; +import { readCollections } from "@/lib/supabase/collection"; +import CreateCollectionForm from "./CreateCollectionForm"; + +const Page = async () => { + const collections = await readCollections(); + + return ( +
+
+

+ Create a Collection +

+
+ +
+ ); }; export default Page; diff --git a/src/components/SearchedIcons.tsx b/src/components/SearchedIcons.tsx new file mode 100644 index 0000000..11d66a4 --- /dev/null +++ b/src/components/SearchedIcons.tsx @@ -0,0 +1,158 @@ +"use client"; +import { useMemo } from "react"; +import * as iconData from "lucide-static"; // This imports all icons as an object +import { Button } from "./ui/button"; // Assuming you have this component + +// Helper component to render and style SVG strings from lucide-static +interface SvgIconProps extends React.HTMLAttributes { + svgString: string; + size?: number; + color?: string; + strokeWidth?: number; +} + +export const SvgIcon = ({ + svgString, + size = 24, + color = "currentColor", + strokeWidth = 2, + style, + ...props +}: SvgIconProps) => { + let modifiedSvgString = svgString; + + if (modifiedSvgString.includes('width="')) { + modifiedSvgString = modifiedSvgString.replace( + /width="[^"]*"/, + `width="${size}"` + ); + } else { + modifiedSvgString = modifiedSvgString.replace( + / + ); +}; + +// Get all icon names once +const ALL_ICON_NAMES = Object.keys(iconData); +const MAX_ICONS_TO_DISPLAY = 50; + +const SearchedIcons = ({ + iconSearch, + handleClick, // Not directly used for display here, but kept as per original props +}: { + iconSearch: string; + handleClick: (selectedIcon: string, svgString: string) => void; +}) => { + const normalizedSearchTerm = iconSearch.toLowerCase().trim(); + + const filteredAndLimitedIcons = useMemo(() => { + if (!normalizedSearchTerm) { + return []; + } + const results = ALL_ICON_NAMES.filter((name) => + name.toLowerCase().includes(normalizedSearchTerm) + ); + // Limit the results *before* mapping to save a bit of processing + return results.slice(0, MAX_ICONS_TO_DISPLAY).map((name) => ({ + name, + svgString: (iconData as Record)[name] as string, + })); + }, [normalizedSearchTerm]); + + if (!normalizedSearchTerm) { + return ( +
+ Please type in the search bar to find icons. +
+ ); + } + + if (filteredAndLimitedIcons.length === 0) { + return ( +
+ No icons found for "{iconSearch}". +
+ ); + } + + return ( +
+ {/* Added gap for better spacing */} + {filteredAndLimitedIcons.map(({ name, svgString }) => ( + + ))} +
+ ); +}; + +export default SearchedIcons; diff --git a/src/lib/validation.ts b/src/lib/validation.ts new file mode 100644 index 0000000..06a57da --- /dev/null +++ b/src/lib/validation.ts @@ -0,0 +1,18 @@ +import { z } from "zod"; + +/** + * Schema for a collection or sub-collection form + */ +export const formCollectionSchema = z.object({ + collectionId: z.number().optional(), + name: z + .string() + .min(3, { message: "Name must be at least 3 characters long" }) + .max(32, { message: "Name must be at most 32 characters long" }), + icon: z.string().min(1, { message: "Icon key is required" }), + subject: z + .string() + .min(3, { message: "Subject must be at least 3 characters long" }) + .max(16, { message: "Subject must be at most 16 characters long" }) + .optional(), +}); From 981522bffc705caef3122ae2501fc460dd8c0375 Mon Sep 17 00:00:00 2001 From: Visar Date: Mon, 19 May 2025 18:00:50 +0200 Subject: [PATCH 03/49] Added proper linking to navbar and rewrote some comments --- src/app/home/create/CreateCollectionForm.tsx | 1 + src/components/Navbar.tsx | 10 ++++++---- src/components/SearchedIcons.tsx | 7 ++++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/app/home/create/CreateCollectionForm.tsx b/src/app/home/create/CreateCollectionForm.tsx index 98ebadc..1ce64a9 100644 --- a/src/app/home/create/CreateCollectionForm.tsx +++ b/src/app/home/create/CreateCollectionForm.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-vars */ "use client"; import SearchedIcons, { SvgIcon } from "@/components/SearchedIcons"; diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx index 41a8f5c..2fb7728 100644 --- a/src/components/Navbar.tsx +++ b/src/components/Navbar.tsx @@ -10,10 +10,12 @@ const Navbar = () => {

Formulate

- + + +
diff --git a/src/components/SearchedIcons.tsx b/src/components/SearchedIcons.tsx index 11d66a4..00b293d 100644 --- a/src/components/SearchedIcons.tsx +++ b/src/components/SearchedIcons.tsx @@ -1,7 +1,9 @@ "use client"; import { useMemo } from "react"; import * as iconData from "lucide-static"; // This imports all icons as an object -import { Button } from "./ui/button"; // Assuming you have this component +import { Button } from "./ui/button"; + +// TODO: TRY TO OPTIMIZE // Helper component to render and style SVG strings from lucide-static interface SvgIconProps extends React.HTMLAttributes { @@ -98,7 +100,7 @@ const MAX_ICONS_TO_DISPLAY = 50; const SearchedIcons = ({ iconSearch, - handleClick, // Not directly used for display here, but kept as per original props + handleClick, }: { iconSearch: string; handleClick: (selectedIcon: string, svgString: string) => void; @@ -137,7 +139,6 @@ const SearchedIcons = ({ return (
- {/* Added gap for better spacing */} {filteredAndLimitedIcons.map(({ name, svgString }) => ( )}
+

{"\u00A0"}

-
+
+

+ {errors.name ? errors.name[0] : "\u00A0"} +

-
+
+

+ {errors.subject ? errors.subject[0] : "\u00A0"} +

@@ -178,11 +200,13 @@ const CreateCollectionForm = ({ id="icon" placeholder="Icon Name" value={iconSearch} + className={`${errors.icon ? "border-red-500" : ""}`} onChange={(e) => setIconSearch(e.target.value)} />
{ setIconSearch(selectedIconName); setSelectedIcon({ selectedIconName, svgString }); @@ -192,10 +216,16 @@ const CreateCollectionForm = ({ )}
-
- +
+ {isPending ? ( + + ) : ( + + )}
); diff --git a/src/components/SearchedIcons.tsx b/src/components/SearchedIcons.tsx index 00b293d..c9e033f 100644 --- a/src/components/SearchedIcons.tsx +++ b/src/components/SearchedIcons.tsx @@ -2,6 +2,7 @@ import { useMemo } from "react"; import * as iconData from "lucide-static"; // This imports all icons as an object import { Button } from "./ui/button"; +import { FormCollectionFieldErrors } from "@/lib/validation"; // TODO: TRY TO OPTIMIZE @@ -100,9 +101,11 @@ const MAX_ICONS_TO_DISPLAY = 50; const SearchedIcons = ({ iconSearch, + errors, handleClick, }: { iconSearch: string; + errors: FormCollectionFieldErrors; handleClick: (selectedIcon: string, svgString: string) => void; }) => { const normalizedSearchTerm = iconSearch.toLowerCase().trim(); @@ -121,19 +124,27 @@ const SearchedIcons = ({ })); }, [normalizedSearchTerm]); + if (errors.icon !== undefined && iconSearch === "") { + return ( +

+ {errors.icon[0]} +

+ ); + } + if (!normalizedSearchTerm) { return ( -
+

Please type in the search bar to find icons. -

+

); } if (filteredAndLimitedIcons.length === 0) { return ( -
+

No icons found for "{iconSearch}". -

+

); } diff --git a/src/lib/validation.ts b/src/lib/validation.ts index 06a57da..4c14d18 100644 --- a/src/lib/validation.ts +++ b/src/lib/validation.ts @@ -3,16 +3,38 @@ import { z } from "zod"; /** * Schema for a collection or sub-collection form */ -export const formCollectionSchema = z.object({ - collectionId: z.number().optional(), - name: z - .string() - .min(3, { message: "Name must be at least 3 characters long" }) - .max(32, { message: "Name must be at most 32 characters long" }), - icon: z.string().min(1, { message: "Icon key is required" }), - subject: z - .string() - .min(3, { message: "Subject must be at least 3 characters long" }) - .max(16, { message: "Subject must be at most 16 characters long" }) - .optional(), -}); + +export interface FormCollectionFieldErrors { + collectionId?: string[]; + name?: string[]; + icon?: string[]; + subject?: string[]; +} +export const formCollectionSchema = z + .object({ + collectionId: z.number().optional(), + name: z + .string() + .min(3, { message: "Name must be at least 3 characters long" }) + .max(32, { message: "Name must be at most 32 characters long" }), + icon: z + .string({ required_error: "An Icon is required" }) + .nonempty({ message: "An Icon is required" }), + subject: z + .string() + .min(3, { message: "Subject must be at least 3 characters long" }) + .max(16, { message: "Subject must be at most 16 characters long" }) + .optional(), + }) + .refine( + (data) => { + if (data.collectionId !== undefined) { + return data.subject && true; + } + return true; + }, + { + message: "Subject is required when a collection is provided", + path: ["subject"], + } + ); From cf3d75d11551917208ec3244b478944a13e8a5f5 Mon Sep 17 00:00:00 2001 From: Visar Date: Mon, 19 May 2025 20:07:59 +0200 Subject: [PATCH 05/49] Enabled collection creation with a server action that calls the supabase api --- src/app/home/create/CreateCollectionForm.tsx | 9 +++- src/app/home/create/action.ts | 50 ++++++++++++++++++++ src/lib/utils.ts | 10 ++-- src/lib/validation.ts | 6 +-- 4 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 src/app/home/create/action.ts diff --git a/src/app/home/create/CreateCollectionForm.tsx b/src/app/home/create/CreateCollectionForm.tsx index 95ef09b..67f3163 100644 --- a/src/app/home/create/CreateCollectionForm.tsx +++ b/src/app/home/create/CreateCollectionForm.tsx @@ -21,6 +21,7 @@ import { import { X } from "lucide-react"; import { useActionState, useState } from "react"; import { z } from "zod"; +import { createCollectionServerAction } from "./action"; const CreateCollectionForm = ({ collections, @@ -49,11 +50,14 @@ const CreateCollectionForm = ({ }; console.log(formValues); - await formCollectionSchema.parseAsync(formValues); + const parsedFormValues = await formCollectionSchema.parseAsync( + formValues + ); setErrors({}); // TODO: write the entry in the database. - let result; + const result = await createCollectionServerAction(parsedFormValues); + console.log(result); return result; } catch (error) { @@ -185,6 +189,7 @@ const CreateCollectionForm = ({ + +
+ ); + } + + return ( +
+
+

+ {collection.name} +

+ +
+
+ ); +}; +export default CollectionTitle; diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx index 7d1db61..f885809 100644 --- a/src/components/ui/button.tsx +++ b/src/components/ui/button.tsx @@ -27,6 +27,8 @@ const buttonVariants = cva( lg: "h-10 rounded-md px-6 has-[>svg]:px-4", xl: "h-12 rounded-md px-6 has-[>svg]:px-4", icon: "size-9", + iconLg: "size-10", + iconXl: "size-16", }, }, defaultVariants: { diff --git a/src/lib/actions.ts b/src/lib/actions.ts new file mode 100644 index 0000000..10f6889 --- /dev/null +++ b/src/lib/actions.ts @@ -0,0 +1,56 @@ +"use server"; + +import { createClient } from "@/supabase/server"; +import { revalidatePath } from "next/cache"; + +interface formValuesType { + name: string; + collectionId: number; + parentId?: number; +} + +export interface rType { + error: string; + data: string; + status: "SUCCESS" | "ERROR"; +} + +export const updateCollectionNameAction = async ( + formValues: formValuesType +): Promise => { + const supabase = await createClient(); + + if (formValues.parentId) { + const { error } = await supabase + .from("sub_collections") + .update({ name: formValues.name }) + .eq("id", formValues.collectionId); + + if (error) { + return { error: error.message, data: "", status: "ERROR" }; + } + + revalidatePath(`/home/${formValues.collectionId}/${formValues.parentId}`); + return { + error: "", + data: "Subcollection updated successfully", + status: "SUCCESS", + }; + } + + const { error } = await supabase + .from("collections") + .update({ name: formValues.name }) + .eq("id", formValues.collectionId); + + if (error) { + return { error: error.message, data: "", status: "ERROR" }; + } + + revalidatePath(`/home/${formValues.collectionId}`); + return { + error: "", + data: "Collection updated successfully", + status: "SUCCESS", + }; +}; From 4827e58518a46ff633cbb7fe4d5e8f48bc134757 Mon Sep 17 00:00:00 2001 From: Visar Date: Sun, 1 Jun 2025 12:00:58 +0200 Subject: [PATCH 21/49] Added subCollection Name Updating --- .../[collectionId]/[subCollectionId]/page.tsx | 10 ++++----- src/components/CollectionTitle.tsx | 21 +++++++++++++++++-- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/app/home/[collectionId]/[subCollectionId]/page.tsx b/src/app/home/[collectionId]/[subCollectionId]/page.tsx index cc44639..8ae72ff 100644 --- a/src/app/home/[collectionId]/[subCollectionId]/page.tsx +++ b/src/app/home/[collectionId]/[subCollectionId]/page.tsx @@ -4,6 +4,7 @@ import NoteCard from "@/components/NoteCard"; import { getCollectionById } from "@/supabase/db/collection"; import { getSubCollectionById } from "@/supabase/db/subCollection"; import { getNotesBySubCollectionId } from "@/supabase/db/notes"; +import CollectionTitle from "@/components/CollectionTitle"; export default async function Page({ params, @@ -23,11 +24,10 @@ export default async function Page({ return (
-
-

- {sub_collection.name} -

-
+
{notes && diff --git a/src/components/CollectionTitle.tsx b/src/components/CollectionTitle.tsx index be58c5a..b57928d 100644 --- a/src/components/CollectionTitle.tsx +++ b/src/components/CollectionTitle.tsx @@ -6,6 +6,7 @@ import { Check, LoaderCircle, PencilLine } from "lucide-react"; import { useActionState, useState } from "react"; import { z } from "zod"; import { updateCollectionNameAction } from "@/lib/actions"; +import { SubCollectionType } from "@/supabase/db/subCollection"; interface FormErrors { name?: string[]; @@ -22,7 +23,13 @@ const nameEditSchema = z.object({ parentId: z.number().optional(), }); -const CollectionTitle = ({ collection }: { collection: CollectionType }) => { +const CollectionTitle = ({ + collection, + parentCollection, +}: { + collection: CollectionType | SubCollectionType; + parentCollection?: CollectionType; +}) => { const [activeForm, setActiveForm] = useState(false); const [errors, setErrors] = useState({}); @@ -30,9 +37,19 @@ const CollectionTitle = ({ collection }: { collection: CollectionType }) => { const handleFormSubmit = async (prevState: any, formData: FormData) => { try { const name = formData.get("name") as string | undefined; + if (name == collection.name) { + setActiveForm(false); + return { + ...prevState, + error: "Name wasnt changed", + status: "ERROR", + }; + } + const formValues = { name: name, collectionId: collection.id, + parentId: parentCollection?.id, }; console.log(formValues); @@ -80,7 +97,7 @@ const CollectionTitle = ({ collection }: { collection: CollectionType }) => { if (activeForm) { return ( -
+
Date: Mon, 2 Jun 2025 18:17:38 +0200 Subject: [PATCH 22/49] readme update --- README.md | 39 +++------------------------------------ 1 file changed, 3 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index e215bc4..ef3faba 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,3 @@ -This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). - -## Getting Started - -First, run the development server: - -```bash -npm run dev -# or -yarn dev -# or -pnpm dev -# or -bun dev -``` - -Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. - -You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. - -This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. - -## Learn More - -To learn more about Next.js, take a look at the following resources: - -- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. -- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. - -You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! - -## Deploy on Vercel - -The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. - -Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. +## TODO NEXT +- [ ] Add the option to delete subCollections and Collections +- [ ] Add note creation \ No newline at end of file From 2618e5a42a8eea3c026cb9de40555dbefbf218f9 Mon Sep 17 00:00:00 2001 From: Visar Date: Mon, 2 Jun 2025 19:58:00 +0200 Subject: [PATCH 23/49] added deletion --- src/app/home/[collectionId]/page.tsx | 1 + src/components/CollectionCard.tsx | 7 +- src/components/CollectionCardOptions.tsx | 85 ++++++++++++++++++++++++ src/lib/actions.ts | 41 ++++++++++++ 4 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 src/components/CollectionCardOptions.tsx diff --git a/src/app/home/[collectionId]/page.tsx b/src/app/home/[collectionId]/page.tsx index f0a16d9..fb32b28 100644 --- a/src/app/home/[collectionId]/page.tsx +++ b/src/app/home/[collectionId]/page.tsx @@ -31,6 +31,7 @@ export default async function Page({ ); diff --git a/src/components/CollectionCard.tsx b/src/components/CollectionCard.tsx index f712bdf..f981429 100644 --- a/src/components/CollectionCard.tsx +++ b/src/components/CollectionCard.tsx @@ -3,16 +3,19 @@ import { SubCollectionType } from "@/supabase/db/subCollection"; import Link from "next/link"; import Icon from "./Icon"; +import CollectionCardOptions from "./CollectionCardOptions"; const CollectionCard = ({ collection, + parentId, href, }: { collection: CollectionType | SubCollectionType; + parentId?: number; href: string; }) => { return ( -
+
{collection.name}
+ +
); }; diff --git a/src/components/CollectionCardOptions.tsx b/src/components/CollectionCardOptions.tsx new file mode 100644 index 0000000..a0a2734 --- /dev/null +++ b/src/components/CollectionCardOptions.tsx @@ -0,0 +1,85 @@ +"use client"; + +import { Edit, MoreVertical, Trash } from "lucide-react"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "./ui/dropdown-menu"; +import { useActionState, useState } from "react"; +import { cn } from "@/lib/utils"; +import { deleteCollectionAction } from "@/lib/actions"; + +const CollectionCardOptions = ({ + collectionId, + parentId, +}: { + collectionId: number; + parentId?: number; +}) => { + const [open, setOpen] = useState(false); + + const handleDelete = async ( + // eslint-disable-next-line @typescript-eslint/no-unused-vars + prevState: { error: string; status: string }, + // eslint-disable-next-line @typescript-eslint/no-unused-vars + formData: FormData + ) => { + console.log(collectionId, parentId); + + const response = await deleteCollectionAction(collectionId, parentId); + + console.log(response); + + return { + error: response.error, + status: response.status, + }; + }; + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [state, formAction, isPending] = useActionState(handleDelete, { + error: "", + status: "INITIAL", + }); + + return ( +
+ + + + + + + + Edit + + + + + + + + +
+ ); +}; +export default CollectionCardOptions; diff --git a/src/lib/actions.ts b/src/lib/actions.ts index 10f6889..c6b1b9d 100644 --- a/src/lib/actions.ts +++ b/src/lib/actions.ts @@ -54,3 +54,44 @@ export const updateCollectionNameAction = async ( status: "SUCCESS", }; }; + +export const deleteCollectionAction = async ( + collectionId: number, + parentId?: number +): Promise => { + "use server"; + const supabase = await createClient(); + if (parentId) { + const { error } = await supabase + .from("sub_collections") + .delete() + .eq("id", collectionId); + + if (error) { + return { error: error.message, data: "", status: "ERROR" }; + } + + revalidatePath(`/home/${collectionId}`); + return { + error: "", + data: "Subcollection deleted successfully", + status: "SUCCESS", + }; + } + + const { error } = await supabase + .from("collections") + .delete() + .eq("id", collectionId); + + if (error) { + return { error: error.message, data: "", status: "ERROR" }; + } + revalidatePath("/home"); + revalidatePath(`/home/${collectionId}`); + return { + error: "", + data: "Collection deleted successfully", + status: "SUCCESS", + }; +}; From f0157284642c142d6988ee52d25c4ede76f2064b Mon Sep 17 00:00:00 2001 From: Visar Date: Mon, 2 Jun 2025 20:04:36 +0200 Subject: [PATCH 24/49] Added deletion ability --- src/app/home/MainCardList.tsx | 2 ++ src/app/home/[collectionId]/page.tsx | 1 + src/components/CollectionCard.tsx | 9 ++++++++- 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/app/home/MainCardList.tsx b/src/app/home/MainCardList.tsx index 9541214..c26c247 100644 --- a/src/app/home/MainCardList.tsx +++ b/src/app/home/MainCardList.tsx @@ -24,6 +24,7 @@ const CardList = async () => { ); @@ -43,6 +44,7 @@ const CardList = async () => { ); diff --git a/src/app/home/[collectionId]/page.tsx b/src/app/home/[collectionId]/page.tsx index fb32b28..d5875ab 100644 --- a/src/app/home/[collectionId]/page.tsx +++ b/src/app/home/[collectionId]/page.tsx @@ -31,6 +31,7 @@ export default async function Page({ diff --git a/src/components/CollectionCard.tsx b/src/components/CollectionCard.tsx index f981429..bba486e 100644 --- a/src/components/CollectionCard.tsx +++ b/src/components/CollectionCard.tsx @@ -8,10 +8,12 @@ import CollectionCardOptions from "./CollectionCardOptions"; const CollectionCard = ({ collection, parentId, + isPremade, href, }: { collection: CollectionType | SubCollectionType; parentId?: number; + isPremade: boolean; href: string; }) => { return ( @@ -28,7 +30,12 @@ const CollectionCard = ({
- + {!isPremade && ( + + )}
); }; From e9d333eec07e30ea936aca66e7c3c72fb4350941 Mon Sep 17 00:00:00 2001 From: Visar Date: Tue, 3 Jun 2025 18:46:09 +0200 Subject: [PATCH 25/49] added a dialog to confirm deletion --- README.md | 3 +- src/app/home/create/action.ts | 1 + src/components/CollectionCardOptions.tsx | 82 ++++++++++++++++-------- 3 files changed, 59 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index ef3faba..2548112 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ ## TODO NEXT - [ ] Add the option to delete subCollections and Collections -- [ ] Add note creation \ No newline at end of file +- [ ] Add note creation +- [ ] Add server side validation \ No newline at end of file diff --git a/src/app/home/create/action.ts b/src/app/home/create/action.ts index 1724b43..00c7190 100644 --- a/src/app/home/create/action.ts +++ b/src/app/home/create/action.ts @@ -22,6 +22,7 @@ export interface rType { status: "SUCCESS" | "ERROR"; } +// TODO: Make it so when creating a subcollection, a check is made to ensure the collectionId is one that the user created export async function createCollectionServerAction( formValues: FormValues ): Promise { diff --git a/src/components/CollectionCardOptions.tsx b/src/components/CollectionCardOptions.tsx index a0a2734..33c2f79 100644 --- a/src/components/CollectionCardOptions.tsx +++ b/src/components/CollectionCardOptions.tsx @@ -7,10 +7,23 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from "./ui/dropdown-menu"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from "@/components/ui/alert-dialog"; import { useActionState, useState } from "react"; import { cn } from "@/lib/utils"; import { deleteCollectionAction } from "@/lib/actions"; +import { Button } from "./ui/button"; +// TODO: Add a dialog for confirming delete const CollectionCardOptions = ({ collectionId, parentId, @@ -52,33 +65,50 @@ const CollectionCardOptions = ({ open && "opacity-100" )} > - - - - - - - - Edit - -
- - + + + + + + + + + Edit - - - + + + + Delete + + +
+
+ + + Are you absolutely sure? + + This action cannot be undone. This will permanently delete this + collection and allof its children + + + + Cancel +
+ + + +
+
+
+
); }; From b0b243b790acdf42b34a4177185e5be5b41f8fb4 Mon Sep 17 00:00:00 2001 From: Visar Date: Tue, 3 Jun 2025 19:22:40 +0200 Subject: [PATCH 26/49] refactor: enhance delete dialog functionality and state management --- src/components/CollectionCardOptions.tsx | 98 +++++++++++++++--------- 1 file changed, 63 insertions(+), 35 deletions(-) diff --git a/src/components/CollectionCardOptions.tsx b/src/components/CollectionCardOptions.tsx index 33c2f79..7b67cdc 100644 --- a/src/components/CollectionCardOptions.tsx +++ b/src/components/CollectionCardOptions.tsx @@ -9,16 +9,14 @@ import { } from "./ui/dropdown-menu"; import { AlertDialog, - AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, - AlertDialogTrigger, } from "@/components/ui/alert-dialog"; -import { useActionState, useState } from "react"; +import { useActionState, useEffect, useState } from "react"; import { cn } from "@/lib/utils"; import { deleteCollectionAction } from "@/lib/actions"; import { Button } from "./ui/button"; @@ -32,6 +30,9 @@ const CollectionCardOptions = ({ parentId?: number; }) => { const [open, setOpen] = useState(false); + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [isEditDialogOpen, setIsEditDialogOpen] = useState(false); + const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); const handleDelete = async ( // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -51,12 +52,22 @@ const CollectionCardOptions = ({ }; }; - // eslint-disable-next-line @typescript-eslint/no-unused-vars const [state, formAction, isPending] = useActionState(handleDelete, { error: "", status: "INITIAL", }); + useEffect(() => { + let timer: NodeJS.Timeout | undefined; + if (!isPending && state.status === "SUCCESS") { + timer = setTimeout(() => { + console.log("Timeout finished: Closing dialog."); + setIsDeleteDialogOpen(false); + }, 500); + } + return () => clearTimeout(timer); + }, [isPending, state.status]); + return (
- - - - - - - - - Edit - - - - - Delete - - - - + + + + + + + + Edit + + setIsDeleteDialogOpen(true)} + > + + Delete + + + + + {/* DELETE DIALOG */} + { + if (!isPending) { + setIsDeleteDialogOpen(open); + } + }} + > Are you absolutely sure? - This action cannot be undone. This will permanently delete this - collection and allof its children + {state.status === "ERROR" && state.error + ? `Error: ${state.error}` + : state.status === "INITIAL" || isPending + ? `This action cannot be undone. This will permanently delete this + collection and all of its children.` + : state.status === "SUCCESS" + ? "Collection deleted successfully!" + : "Preparing to delete..."} Cancel
- - - +
+ + {/* EDIT DIALOG */}
); }; From 64b5f7c121318e6d78e732fdcfa46fed565ed486 Mon Sep 17 00:00:00 2001 From: Visar Date: Tue, 3 Jun 2025 19:25:05 +0200 Subject: [PATCH 27/49] not show your collections if none already --- src/app/home/MainCardList.tsx | 42 ++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 18 deletions(-) diff --git a/src/app/home/MainCardList.tsx b/src/app/home/MainCardList.tsx index c26c247..2d84fef 100644 --- a/src/app/home/MainCardList.tsx +++ b/src/app/home/MainCardList.tsx @@ -12,24 +12,30 @@ const CardList = async () => { return (
-

Your Collections

-
- {userCollections && - userCollections - // .filter((collection) => - // collection.name.toLocaleLowerCase().includes(stringQuery) - // ) - .map((collection) => { - return ( - - ); - })} -
+ {userCollections?.length && ( + <> +

+ Your Collections +

+
+ {userCollections && + userCollections + // .filter((collection) => + // collection.name.toLocaleLowerCase().includes(stringQuery) + // ) + .map((collection) => { + return ( + + ); + })} +
+ + )}

Premade Collections

From 588118bb258409224ccacab7654cca2a19dc08b4 Mon Sep 17 00:00:00 2001 From: Visar Date: Thu, 5 Jun 2025 16:58:33 +0200 Subject: [PATCH 28/49] fixed not show userCollections if none already --- src/app/home/MainCardList.tsx | 42 +++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 17 deletions(-) diff --git a/src/app/home/MainCardList.tsx b/src/app/home/MainCardList.tsx index 2d84fef..6b96495 100644 --- a/src/app/home/MainCardList.tsx +++ b/src/app/home/MainCardList.tsx @@ -3,6 +3,7 @@ import { getPremadeCollections, } from "@/supabase/db/collection"; import CollectionCard from "../../components/CollectionCard"; +import { cn } from "@/lib/utils"; const CardList = async () => { const [premadeCollections, userCollections] = await Promise.all([ @@ -10,33 +11,40 @@ const CardList = async () => { getCollectionsForUserId(), ]); + const areThereAnyUserCollections = + userCollections && userCollections.length > 0; + return (
- {userCollections?.length && ( + {areThereAnyUserCollections && ( <>

Your Collections

- {userCollections && - userCollections - // .filter((collection) => - // collection.name.toLocaleLowerCase().includes(stringQuery) - // ) - .map((collection) => { - return ( - - ); - })} + {userCollections + // .filter((collection) => + // collection.name.toLocaleLowerCase().includes(stringQuery) + // ) + .map((collection) => { + return ( + + ); + })}
)} -

+

Premade Collections

From 4d4266c32f8fe3dede4a559ca91eef036f675050 Mon Sep 17 00:00:00 2001 From: Visar Date: Thu, 5 Jun 2025 16:59:49 +0200 Subject: [PATCH 29/49] changes script name from production to prod for faster --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 9323695..4c33d51 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "dev": "next dev --turbopack", "build": "next build", "start": "next start", - "production": "next build && next start", + "prod": "next build && next start", "lint": "next lint" }, "dependencies": { From 7625e5ec889e698582cf4d669093aed9429e7a50 Mon Sep 17 00:00:00 2001 From: Visar Date: Thu, 5 Jun 2025 17:48:30 +0200 Subject: [PATCH 30/49] Created edit dialog but not functional. Will fix after a refactor/rewrite --- README.md | 3 +- src/app/home/MainCardList.tsx | 2 + src/app/home/[collectionId]/page.tsx | 9 +- src/app/home/create/CreateCollectionForm.tsx | 71 ++-------------- src/components/CollectionCard.tsx | 5 +- src/components/CollectionCardOptions.tsx | 69 ++++++++++++++-- src/components/ParentCollectionSelector.tsx | 86 ++++++++++++++++++++ 7 files changed, 170 insertions(+), 75 deletions(-) create mode 100644 src/components/ParentCollectionSelector.tsx diff --git a/README.md b/README.md index 2548112..860b382 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ ## TODO NEXT - [ ] Add the option to delete subCollections and Collections - [ ] Add note creation -- [ ] Add server side validation \ No newline at end of file +- [ ] Add server side validation +- [ ] Persist Database Calls between browser sessions \ No newline at end of file diff --git a/src/app/home/MainCardList.tsx b/src/app/home/MainCardList.tsx index 6b96495..69204c1 100644 --- a/src/app/home/MainCardList.tsx +++ b/src/app/home/MainCardList.tsx @@ -33,6 +33,7 @@ const CardList = async () => { collection={collection} isPremade={false} href={`/home/${collection.id}`} + allUserCollections={userCollections} /> ); })} @@ -60,6 +61,7 @@ const CardList = async () => { collection={collection} isPremade={true} href={`/home/${collection.id}`} + allUserCollections={userCollections} /> ); })} diff --git a/src/app/home/[collectionId]/page.tsx b/src/app/home/[collectionId]/page.tsx index d5875ab..127de59 100644 --- a/src/app/home/[collectionId]/page.tsx +++ b/src/app/home/[collectionId]/page.tsx @@ -1,7 +1,10 @@ import { redirect } from "next/navigation"; import CollectionCard from "@/components/CollectionCard"; -import { getCollectionById } from "@/supabase/db/collection"; +import { + getCollectionById, + getCollectionsForUserId, +} from "@/supabase/db/collection"; import { getSubCollectionsByCollectionId } from "@/supabase/db/subCollection"; import CollectionTitle from "@/components/CollectionTitle"; @@ -13,7 +16,8 @@ export default async function Page({ const { collectionId } = await params; // Fetch collection and sub_collections in parallel - const [collection, sub_collections] = await Promise.all([ + const [allUserCollections, collection, sub_collections] = await Promise.all([ + getCollectionsForUserId(), getCollectionById(collectionId), getSubCollectionsByCollectionId(collectionId), ]); @@ -34,6 +38,7 @@ export default async function Page({ isPremade={collection.user_id === null} parentId={collection.id} href={`/home/${collection.id}/${subC.id}`} + allUserCollections={allUserCollections} /> ); })} diff --git a/src/app/home/create/CreateCollectionForm.tsx b/src/app/home/create/CreateCollectionForm.tsx index b16f921..98ca33e 100644 --- a/src/app/home/create/CreateCollectionForm.tsx +++ b/src/app/home/create/CreateCollectionForm.tsx @@ -4,15 +4,6 @@ import SearchedIcons, { SvgIcon } from "@/components/SearchedIcons"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; -import { - Select, - SelectContent, - SelectGroup, - SelectItem, - SelectLabel, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; import { CollectionType } from "@/supabase/db/collection"; import { FormCollectionFieldErrors, @@ -23,6 +14,7 @@ import { useActionState, useState } from "react"; import { z } from "zod"; import { createCollectionServerAction } from "./action"; import { useRouter } from "next/navigation"; +import ParentCollectionSelector from "@/components/ParentCollectionSelector"; const CreateCollectionForm = ({ collections, @@ -110,62 +102,11 @@ const CreateCollectionForm = ({
-
- -
- - - - {parentCollection ? ( - - ) : ( - - )} -
-

{"\u00A0"}

-
+
{ return ( @@ -32,8 +34,9 @@ const CollectionCard = ({ {!isPremade && ( )}
diff --git a/src/components/CollectionCardOptions.tsx b/src/components/CollectionCardOptions.tsx index 7b67cdc..7812c03 100644 --- a/src/components/CollectionCardOptions.tsx +++ b/src/components/CollectionCardOptions.tsx @@ -16,33 +16,51 @@ import { AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; import { useActionState, useEffect, useState } from "react"; import { cn } from "@/lib/utils"; import { deleteCollectionAction } from "@/lib/actions"; import { Button } from "./ui/button"; +import { Label } from "./ui/label"; +import { Input } from "./ui/input"; +import { SubCollectionType } from "@/supabase/db/subCollection"; +import { CollectionType } from "@/supabase/db/collection"; +import ParentCollectionSelector from "./ParentCollectionSelector"; // TODO: Add a dialog for confirming delete const CollectionCardOptions = ({ - collectionId, + collection, parentId, + allUserCollections, }: { - collectionId: number; + collection: CollectionType | SubCollectionType; parentId?: number; + allUserCollections: CollectionType[] | null; }) => { const [open, setOpen] = useState(false); - // eslint-disable-next-line @typescript-eslint/no-unused-vars const [isEditDialogOpen, setIsEditDialogOpen] = useState(false); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); + const [parentCollection, setParentCollection] = useState< + CollectionType | undefined + >(undefined); + const handleDelete = async ( // eslint-disable-next-line @typescript-eslint/no-unused-vars prevState: { error: string; status: string }, // eslint-disable-next-line @typescript-eslint/no-unused-vars formData: FormData ) => { - console.log(collectionId, parentId); + console.log(collection.id, parentId); - const response = await deleteCollectionAction(collectionId, parentId); + const response = await deleteCollectionAction(collection.id, parentId); console.log(response); @@ -81,7 +99,10 @@ const CollectionCardOptions = ({ - + setIsEditDialogOpen(true)} + > Edit @@ -137,6 +158,42 @@ const CollectionCardOptions = ({ {/* EDIT DIALOG */} + { + if (!isPending) { + setIsEditDialogOpen(open); + } + }} + > + + + + Edit + + Edit Collection ({collection.name}) + + +
+ +
+ + +
+
+ +
+ +
); }; diff --git a/src/components/ParentCollectionSelector.tsx b/src/components/ParentCollectionSelector.tsx new file mode 100644 index 0000000..72a0caa --- /dev/null +++ b/src/components/ParentCollectionSelector.tsx @@ -0,0 +1,86 @@ +"use client"; + +import { CollectionType } from "@/supabase/db/collection"; +import { Button } from "./ui/button"; +import { Label } from "./ui/label"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectTrigger, + SelectValue, +} from "./ui/select"; +import { Dispatch, SetStateAction } from "react"; +import { X } from "lucide-react"; + +const ParentCollectionSelector = ({ + parentCollection, + setParentCollection, + collections, +}: { + parentCollection: CollectionType | undefined; + setParentCollection: Dispatch>; + collections: CollectionType[] | null; +}) => { + return ( +
+ +
+ + + + {parentCollection ? ( + + ) : ( + + )} +
+

{"\u00A0"}

+
+ ); +}; +export default ParentCollectionSelector; From b6f2322ac9f55ca8dd89f694c724fcfc2a75a0c8 Mon Sep 17 00:00:00 2001 From: Visar Date: Tue, 10 Jun 2025 19:51:05 +0200 Subject: [PATCH 31/49] created sessionContext --- src/providers/session-data-provider.tsx | 45 +++++++++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 src/providers/session-data-provider.tsx diff --git a/src/providers/session-data-provider.tsx b/src/providers/session-data-provider.tsx new file mode 100644 index 0000000..bb71ba6 --- /dev/null +++ b/src/providers/session-data-provider.tsx @@ -0,0 +1,45 @@ +"use client"; + +import { CollectionType } from "@/supabase/db/collection"; +import { NoteType } from "@/supabase/db/notes"; +import type { User } from "@supabase/auth-js"; +import { SubCollectionType } from "@/supabase/db/subCollection"; +import { createContext, useContext, ReactNode } from "react"; + +export interface SessionDataContextType { + user: User; + collections: CollectionType[]; + subCollections: SubCollectionType[]; + notes: NoteType[]; +} + +// Create the context with a default value +const SessionDataContext = createContext(null); + +// Create the Provider component +interface SessionDataProviderProps { + children: ReactNode; + initialData: SessionDataContextType; // The data we fetch on the server +} + +export function SessionDataProvider({ + children, + initialData, +}: SessionDataProviderProps) { + // We simply provide the initialData received from the server. + // The value won't change unless the whole provider is re-rendered (e.g., on a hard refresh). + return ( + + {children} + + ); +} + +// Create a custom hook for easy access to the data +export function useSessionData() { + const context = useContext(SessionDataContext); + if (!context) { + throw new Error("useSessionData must be used within a SessionDataProvider"); + } + return context; +} From 88c806e61ec6c5845013efd45a39ad3f5bdb2d5f Mon Sep 17 00:00:00 2001 From: Visar Date: Tue, 10 Jun 2025 20:03:30 +0200 Subject: [PATCH 32/49] Added a util function to get all user data and used that data in a context provider in home layout --- src/app/home/layout.tsx | 20 +++++++++++++------- src/lib/data.ts | 24 ++++++++++++++++++++++++ src/providers/session-data-provider.tsx | 2 +- src/supabase/db/notes.ts | 19 +++++++++++++++++++ src/supabase/db/subCollection.ts | 19 +++++++++++++++++++ 5 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 src/lib/data.ts diff --git a/src/app/home/layout.tsx b/src/app/home/layout.tsx index 948773f..00fa42e 100644 --- a/src/app/home/layout.tsx +++ b/src/app/home/layout.tsx @@ -3,6 +3,8 @@ import { cookies } from "next/headers"; import { SidebarProvider } from "@/components/ui/sidebar"; import AppSidebar from "@/components/AppSidebar"; import Navbar from "@/components/Navbar"; +import { getUserData } from "@/lib/data"; +import { SessionDataProvider } from "@/providers/session-data-provider"; export default async function Layout({ children, @@ -15,13 +17,17 @@ export default async function Layout({ ? cookieSidebarState.value === "true" : true; + const allUserData = await getUserData(); + return ( - - -
- - {children} -
-
+ + + +
+ + {children} +
+
+
); } diff --git a/src/lib/data.ts b/src/lib/data.ts new file mode 100644 index 0000000..f4dd852 --- /dev/null +++ b/src/lib/data.ts @@ -0,0 +1,24 @@ +import { SessionDataContextType } from "@/providers/session-data-provider"; +import { getCollectionsForUserId } from "@/supabase/db/collection"; +import { getNotesForUserId } from "@/supabase/db/notes"; +import { getSubCollectionsForUserId } from "@/supabase/db/subCollection"; +import { getUser } from "@/supabase/db/user"; + +export async function getUserData(): Promise { + const [user, collections, subCollections, notes] = await Promise.all([ + getUser(), + getCollectionsForUserId(), + getSubCollectionsForUserId(), + getNotesForUserId(), + ]); + + if (user && collections && subCollections && notes) { + return { + user, + collections, + subCollections, + notes, + }; + } + return null; +} diff --git a/src/providers/session-data-provider.tsx b/src/providers/session-data-provider.tsx index bb71ba6..bb17d65 100644 --- a/src/providers/session-data-provider.tsx +++ b/src/providers/session-data-provider.tsx @@ -19,7 +19,7 @@ const SessionDataContext = createContext(null); // Create the Provider component interface SessionDataProviderProps { children: ReactNode; - initialData: SessionDataContextType; // The data we fetch on the server + initialData: SessionDataContextType | null; // The data we fetch on the server } export function SessionDataProvider({ diff --git a/src/supabase/db/notes.ts b/src/supabase/db/notes.ts index 9877cd3..d3eedf5 100644 --- a/src/supabase/db/notes.ts +++ b/src/supabase/db/notes.ts @@ -56,3 +56,22 @@ export async function getNotesBySubCollectionId( return notes as NoteType[]; } + +export async function getNotesForUserId() { + const supabase = await createClient(); + const userId = (await supabase.auth.getUser()).data.user?.id; + + if (userId) { + const { data: notes, error } = await supabase + .from("notes") + .select("*") + .eq("user_id", userId); + //TODO: handle errors better + if (error) { + return null; + } + + return notes as NoteType[]; + } + return null; +} diff --git a/src/supabase/db/subCollection.ts b/src/supabase/db/subCollection.ts index 238f9b3..2f6a32f 100644 --- a/src/supabase/db/subCollection.ts +++ b/src/supabase/db/subCollection.ts @@ -65,3 +65,22 @@ export async function getAllSubCollections(): Promise< } return sub_collections as SubCollectionType[]; } + +export async function getSubCollectionsForUserId() { + const supabase = await createClient(); + const userId = (await supabase.auth.getUser()).data.user?.id; + + if (userId) { + const { data: subCollections, error } = await supabase + .from("sub_collections") + .select("*") + .eq("user_id", userId); + //TODO: handle errors better + if (error) { + return null; + } + + return subCollections as SubCollectionType[]; + } + return null; +} From f42d54fe8355df469081897a36e13808f3da500f Mon Sep 17 00:00:00 2001 From: Visar Date: Tue, 10 Jun 2025 20:34:05 +0200 Subject: [PATCH 33/49] Updated sidebar to use context insead of db calls --- src/components/AppSidebar.tsx | 84 ++--------------------- src/components/SideBarCollectionGroup.tsx | 78 +++++++++++++++++++++ src/components/SidebarUserItem.tsx | 23 +++++++ src/lib/data.ts | 32 +++++++-- src/providers/session-data-provider.tsx | 2 + src/supabase/db/subCollection.ts | 15 ++++ 6 files changed, 153 insertions(+), 81 deletions(-) create mode 100644 src/components/SideBarCollectionGroup.tsx create mode 100644 src/components/SidebarUserItem.tsx diff --git a/src/components/AppSidebar.tsx b/src/components/AppSidebar.tsx index f83baa1..f51c2ef 100644 --- a/src/components/AppSidebar.tsx +++ b/src/components/AppSidebar.tsx @@ -10,34 +10,19 @@ import { SidebarMenuButton, SidebarMenuItem, } from "@/components/ui/sidebar"; -import { - getCollectionsForUserId, - getPremadeCollections, -} from "@/supabase/db/collection"; -import { ChevronUp, House, Plus, User2 } from "lucide-react"; +import { House, Plus } from "lucide-react"; import Link from "next/link"; -import CollectionSidebarItem from "./CollectionSidebarItem"; -import { getAllSubCollections } from "@/supabase/db/subCollection"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "./ui/dropdown-menu"; -import { getUser } from "@/supabase/db/user"; -import { Avatar, AvatarFallback } from "./ui/avatar"; -import { AvatarImage } from "@radix-ui/react-avatar"; - -export default async function AppSidebar() { - const [collections, userCollections, sub_collections, user] = - await Promise.all([ - getPremadeCollections(), - getCollectionsForUserId(), - getAllSubCollections(), - getUser(), - ]); +import SideBarCollectionGroup from "./SideBarCollectionGroup"; +import SidebarUserItem from "./SidebarUserItem"; +export default function AppSidebar() { return ( @@ -68,70 +53,15 @@ export default async function AppSidebar() { - {userCollections && userCollections.length >= 1 && ( - - Your Collections - - - {userCollections && - userCollections.map((collection) => { - const subCollectionsForCollection = sub_collections - ? sub_collections.filter( - (sub_collection) => - sub_collection.collection_id == collection.id - ) - : []; - return ( - - ); - })} - - - - )} - - Premade Collections - - - {collections && - collections.map((collection) => { - const subCollectionsForCollection = sub_collections - ? sub_collections.filter( - (sub_collection) => - sub_collection.collection_id == collection.id - ) - : []; - return ( - - ); - })} - - - + + - - - - - - - - {user?.user_metadata.full_name} - - + { + const { + collections, + subCollections, + premadeCollections, + premadeSubCollections, + } = useSessionData(); + return ( + <> + {type === "user-created" && collections && collections.length >= 1 && ( + + Your Collections + + + {collections.map((collection) => { + const subCollectionsForCollection = subCollections + ? subCollections.filter( + (sub_collection) => + sub_collection.collection_id == collection.id + ) + : []; + return ( + + ); + })} + + + + )} + {type === "premade" && + premadeCollections && + premadeCollections.length >= 1 && ( + + Premade Collections + + + {premadeCollections.map((collection) => { + const subCollectionsForCollection = premadeSubCollections + ? premadeSubCollections.filter( + (sub_collection) => + sub_collection.collection_id == collection.id + ) + : []; + return ( + + ); + })} + + + + )} + + ); +}; +export default SideBarCollectionGroup; diff --git a/src/components/SidebarUserItem.tsx b/src/components/SidebarUserItem.tsx new file mode 100644 index 0000000..48c41ce --- /dev/null +++ b/src/components/SidebarUserItem.tsx @@ -0,0 +1,23 @@ +"use client"; + +import { ChevronUp, User2 } from "lucide-react"; +import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar"; +import { SidebarMenuButton } from "./ui/sidebar"; +import { useSessionData } from "@/providers/session-data-provider"; + +const SidebarUserItem = () => { + const { user } = useSessionData(); + return ( + + + + + + + + {user?.user_metadata.full_name} + + + ); +}; +export default SidebarUserItem; diff --git a/src/lib/data.ts b/src/lib/data.ts index f4dd852..d13f9d1 100644 --- a/src/lib/data.ts +++ b/src/lib/data.ts @@ -1,22 +1,46 @@ import { SessionDataContextType } from "@/providers/session-data-provider"; -import { getCollectionsForUserId } from "@/supabase/db/collection"; +import { + getCollectionsForUserId, + getPremadeCollections, +} from "@/supabase/db/collection"; import { getNotesForUserId } from "@/supabase/db/notes"; -import { getSubCollectionsForUserId } from "@/supabase/db/subCollection"; +import { + getPremadeSubCollections, + getSubCollectionsForUserId, +} from "@/supabase/db/subCollection"; import { getUser } from "@/supabase/db/user"; export async function getUserData(): Promise { - const [user, collections, subCollections, notes] = await Promise.all([ + const [ + user, + collections, + subCollections, + notes, + premadeCollections, + premadeSubCollections, + ] = await Promise.all([ getUser(), getCollectionsForUserId(), getSubCollectionsForUserId(), getNotesForUserId(), + getPremadeCollections(), + getPremadeSubCollections(), ]); - if (user && collections && subCollections && notes) { + if ( + user && + collections && + subCollections && + notes && + premadeCollections && + premadeSubCollections + ) { return { user, collections, subCollections, + premadeCollections, + premadeSubCollections, notes, }; } diff --git a/src/providers/session-data-provider.tsx b/src/providers/session-data-provider.tsx index bb17d65..cb78be9 100644 --- a/src/providers/session-data-provider.tsx +++ b/src/providers/session-data-provider.tsx @@ -10,6 +10,8 @@ export interface SessionDataContextType { user: User; collections: CollectionType[]; subCollections: SubCollectionType[]; + premadeCollections: CollectionType[]; + premadeSubCollections: SubCollectionType[]; notes: NoteType[]; } diff --git a/src/supabase/db/subCollection.ts b/src/supabase/db/subCollection.ts index 2f6a32f..4ab608b 100644 --- a/src/supabase/db/subCollection.ts +++ b/src/supabase/db/subCollection.ts @@ -84,3 +84,18 @@ export async function getSubCollectionsForUserId() { } return null; } + +export async function getPremadeSubCollections() { + const supabase = await createClient(); + + const { data: subCollections, error } = await supabase + .from("sub_collections") + .select("*") + .is("user_id", null); + //TODO: handle errors better + if (error) { + return null; + } + + return subCollections as SubCollectionType[]; +} From ea47a9c85d3710c83248058438f2e9b916839de0 Mon Sep 17 00:00:00 2001 From: Visar Date: Wed, 11 Jun 2025 18:29:01 +0200 Subject: [PATCH 34/49] Finally fixed the error of no js being send to the client. Refactored Icon.tsx to be a propper client component and made the whole sidebar a client component aswell --- package-lock.json | 1677 +++++++++++---------- package.json | 3 +- src/components/AppSidebar.tsx | 84 +- src/components/CollectionCard.tsx | 2 +- src/components/CollectionSidebarItem.tsx | 12 +- src/components/Icon.tsx | 72 +- src/components/SideBarCollectionGroup.tsx | 78 - src/components/SidebarUserItem.tsx | 23 - src/lib/data.ts | 10 + src/supabase/db/notes.ts | 1 + src/supabase/schema.ts | 3 + 11 files changed, 996 insertions(+), 969 deletions(-) delete mode 100644 src/components/SideBarCollectionGroup.tsx delete mode 100644 src/components/SidebarUserItem.tsx diff --git a/package-lock.json b/package-lock.json index 27e83cb..16b9313 100644 --- a/package-lock.json +++ b/package-lock.json @@ -89,14 +89,25 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@babel/runtime": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", - "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", - "license": "MIT", + "node_modules/@ampproject/remapping": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", + "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", + "dev": true, + "license": "Apache-2.0", "dependencies": { - "regenerator-runtime": "^0.14.0" + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -135,9 +146,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.6.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.6.1.tgz", - "integrity": "sha512-KTsJMmobmbrFLe3LDh0PC2FXpcSYJt/MLjlkh/9LEnmKYLSYmT/0EW9JWANjeoemiuZrmogti0tW5Ch+qNUYDw==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, "license": "MIT", "dependencies": { @@ -192,9 +203,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", - "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz", + "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==", "dev": true, "license": "Apache-2.0", "engines": { @@ -202,9 +213,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.13.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", - "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", + "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -239,13 +250,16 @@ } }, "node_modules/@eslint/js": { - "version": "9.25.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.25.1.tgz", - "integrity": "sha512-dEIwmjntEx8u3Uvv+kr3PDeeArL8Hw07H9kyYxCjnM9pBjfEhk6uLXSchxxzgiwtRhhzVzqmUSDFBOi1TuZ7qg==", + "version": "9.28.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.28.0.tgz", + "integrity": "sha512-fnqSjGWd/CoIp4EXIxWVK/sHA6DOHN4+8Ix2cX5ycOY7LG0UY8nHCU5pIp2eaE1Mc7Qd8kHspYNzYXT2ojPLzg==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { @@ -259,13 +273,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.8", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", - "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", + "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.13.0", + "@eslint/core": "^0.14.0", "levn": "^0.4.1" }, "engines": { @@ -273,28 +287,28 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.6.9", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", - "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.1.tgz", + "integrity": "sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==", "license": "MIT", "dependencies": { "@floating-ui/utils": "^0.2.9" } }, "node_modules/@floating-ui/dom": { - "version": "1.6.13", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", - "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.1.tgz", + "integrity": "sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==", "license": "MIT", "dependencies": { - "@floating-ui/core": "^1.6.0", + "@floating-ui/core": "^1.7.1", "@floating-ui/utils": "^0.2.9" } }, "node_modules/@floating-ui/react-dom": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", - "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.3.tgz", + "integrity": "sha512-huMBfiU9UnQ2oBwIhgzyIiSpVgvlDstU8CX0AF+wS+KzmYMs0J2a3GwuFHV1Lz+jlrQGeC1fF+Nv0QoumyV0bA==", "license": "MIT", "dependencies": { "@floating-ui/dom": "^1.0.0" @@ -311,9 +325,9 @@ "license": "MIT" }, "node_modules/@hookform/resolvers": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.0.1.tgz", - "integrity": "sha512-u/+Jp83luQNx9AdyW2fIPGY6Y7NG68eN2ZW8FOJYL+M0i4s49+refdJdOp/A9n9HFQtQs3HIDHQvX3ZET2o7YA==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.1.1.tgz", + "integrity": "sha512-J/NVING3LMAEvexJkyTLjruSm7aOFx7QX21pzkiJfMoNG0wl5aFEjLTl7ay7IQb9EWY6AkrBy7tHL2Alijpdcg==", "license": "MIT", "dependencies": { "@standard-schema/utils": "^0.3.0" @@ -375,9 +389,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -389,9 +403,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.1.tgz", - "integrity": "sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz", + "integrity": "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==", "cpu": [ "arm64" ], @@ -411,9 +425,9 @@ } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.1.tgz", - "integrity": "sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz", + "integrity": "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==", "cpu": [ "x64" ], @@ -577,9 +591,9 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.1.tgz", - "integrity": "sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.2.tgz", + "integrity": "sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==", "cpu": [ "arm" ], @@ -599,9 +613,9 @@ } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.1.tgz", - "integrity": "sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.2.tgz", + "integrity": "sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==", "cpu": [ "arm64" ], @@ -621,9 +635,9 @@ } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.1.tgz", - "integrity": "sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.2.tgz", + "integrity": "sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==", "cpu": [ "s390x" ], @@ -643,9 +657,9 @@ } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.1.tgz", - "integrity": "sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.2.tgz", + "integrity": "sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==", "cpu": [ "x64" ], @@ -665,9 +679,9 @@ } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.1.tgz", - "integrity": "sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.2.tgz", + "integrity": "sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==", "cpu": [ "arm64" ], @@ -687,9 +701,9 @@ } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.1.tgz", - "integrity": "sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.2.tgz", + "integrity": "sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==", "cpu": [ "x64" ], @@ -709,16 +723,16 @@ } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.1.tgz", - "integrity": "sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz", + "integrity": "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==", "cpu": [ "wasm32" ], "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.4.0" + "@emnapi/runtime": "^1.4.3" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -727,10 +741,29 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.2.tgz", + "integrity": "sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.1.tgz", - "integrity": "sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.2.tgz", + "integrity": "sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==", "cpu": [ "ia32" ], @@ -747,9 +780,9 @@ } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.1.tgz", - "integrity": "sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.2.tgz", + "integrity": "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==", "cpu": [ "x64" ], @@ -778,16 +811,69 @@ "node": ">=18.0.0" } }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", + "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/set-array": "^1.2.1", + "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/trace-mapping": "^0.3.24" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/set-array": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", + "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", + "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.25", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", + "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@napi-rs/wasm-runtime": { - "version": "0.2.9", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.9.tgz", - "integrity": "sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg==", + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz", + "integrity": "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.4.0", - "@emnapi/runtime": "^1.4.0", + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.9.0" } }, @@ -996,19 +1082,19 @@ "license": "MIT" }, "node_modules/@radix-ui/react-accordion": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.8.tgz", - "integrity": "sha512-c7OKBvO36PfQIUGIjj1Wko0hH937pYFU2tR5zbIJDUsmTzHoZVHHt4bmb7OOJbzTaWJtVELKWojBHa7OcnUHmQ==", + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.11.tgz", + "integrity": "sha512-l3W5D54emV2ues7jjeG1xcyN7S3jnK3zE2zHqgn0CmMsy9lNJwmgcrmaxS+7ipw15FAivzKNzH3d5EcGoFKw0A==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collapsible": "1.1.8", - "@radix-ui/react-collection": "1.1.4", + "@radix-ui/react-collapsible": "1.1.11", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { @@ -1027,17 +1113,17 @@ } }, "node_modules/@radix-ui/react-alert-dialog": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.11.tgz", - "integrity": "sha512-4KfkwrFnAw3Y5Jeoq6G+JYSKW0JfIS3uDdFC/79Jw9AsMayZMizSSMxk1gkrolYXsa/WzbbDfOA7/D8N5D+l1g==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.14.tgz", + "integrity": "sha512-IOZfZ3nPvN6lXpJTBCunFQPRSvK8MDgSc1FB85xnIpUKOw9en0dJj8JmCAxV7BiZdtYlUpmrQjoTFkVYtdoWzQ==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dialog": "1.1.11", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-slot": "1.2.0" + "@radix-ui/react-dialog": "1.1.14", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -1054,31 +1140,13 @@ } } }, - "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-slot": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", - "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-arrow": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.4.tgz", - "integrity": "sha512-qz+fxrqgNxG0dYew5l7qR3c7wdgRu1XVUHGnGYX7rg5HM4p9SWaRmJwfgR3J0SgyUKayLmzQIun+N6rWRgiRKw==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.0" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -1096,12 +1164,12 @@ } }, "node_modules/@radix-ui/react-aspect-ratio": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-aspect-ratio/-/react-aspect-ratio-1.1.4.tgz", - "integrity": "sha512-ie2mUDtM38LBqVU+Xn+GIY44tWM5yVbT5uXO+th85WZxUUsgEdWNNZWecqqGzkQ4Af+Fq1mYT6TyQ/uUf5gfcw==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-aspect-ratio/-/react-aspect-ratio-1.1.7.tgz", + "integrity": "sha512-Yq6lvO9HQyPwev1onK1daHCHqXVLzPhSVjmsNjCa2Zcxy2f7uJD2itDtxknv6FzAKCwD1qQkeVDmX/cev13n/g==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.0" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -1119,13 +1187,13 @@ } }, "node_modules/@radix-ui/react-avatar": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.7.tgz", - "integrity": "sha512-V7ODUt4mUoJTe3VUxZw6nfURxaPALVqmDQh501YmaQsk3D8AZQrOPRnfKn4H7JGDLBc0KqLhT94H79nV88ppNg==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.10.tgz", + "integrity": "sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==", "license": "MIT", "dependencies": { "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-is-hydrated": "0.1.0", "@radix-ui/react-use-layout-effect": "1.1.1" @@ -1146,16 +1214,16 @@ } }, "node_modules/@radix-ui/react-checkbox": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.2.3.tgz", - "integrity": "sha512-pHVzDYsnaDmBlAuwim45y3soIN8H4R7KbkSVirGhXO+R/kO2OLCe0eucUEbddaTcdMHHdzcIGHtZSMSQlA+apw==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.2.tgz", + "integrity": "sha512-yd+dI56KZqawxKZrJ31eENUwqc1QSqg4OZ15rybGjF2ZNwMO+wCyHzAVLRp9qoYJf7kYy0YpZ2b0JCzJ42HZpA==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" @@ -1176,9 +1244,9 @@ } }, "node_modules/@radix-ui/react-collapsible": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.8.tgz", - "integrity": "sha512-hxEsLvK9WxIAPyxdDRULL4hcaSjMZCfP7fHB0Z1uUnDoDBat1Zh46hwYfa69DeZAbJrPckjf0AGAtEZyvDyJbw==", + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.11.tgz", + "integrity": "sha512-2qrRsVGSCYasSz1RFOorXwl0H7g7J1frQtgpQgYrt+MOidtPAINHn9CPovQXb83r8ahapdx3Tu0fa/pdFFSdPg==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", @@ -1186,7 +1254,7 @@ "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, @@ -1206,15 +1274,15 @@ } }, "node_modules/@radix-ui/react-collection": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.4.tgz", - "integrity": "sha512-cv4vSf7HttqXilDnAnvINd53OTl1/bjUYVZrkFnA7nwmY9Ob2POUy0WY0sfqBAe1s5FyKsyceQlqiEGPYNTadg==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-slot": "1.2.0" + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -1231,24 +1299,6 @@ } } }, - "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", - "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", @@ -1280,15 +1330,15 @@ } }, "node_modules/@radix-ui/react-context-menu": { - "version": "2.2.12", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.2.12.tgz", - "integrity": "sha512-5UFKuTMX8F2/KjHvyqu9IYT8bEtDSCJwwIx1PghBo4jh9S6jJVsceq9xIjqsOVcxsynGwV5eaqPE3n/Cu+DrSA==", + "version": "2.2.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.2.15.tgz", + "integrity": "sha512-UsQUMjcYTsBjTSXw0P3GO0werEQvUY2plgRQuKoCTtkNr45q1DiL51j4m7gxhABzZ0BadoXNsIbg7F3KwiUBbw==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-menu": "2.1.12", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-menu": "2.1.15", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, @@ -1308,22 +1358,22 @@ } }, "node_modules/@radix-ui/react-dialog": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.11.tgz", - "integrity": "sha512-yI7S1ipkP5/+99qhSI6nthfo/tR6bL6Zgxi/+1UO6qPa6UeM6nlafWcQ65vB4rU2XjgjMfMhI3k9Y5MztA62VQ==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.14.tgz", + "integrity": "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.7", + "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.4", + "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-portal": "1.1.6", + "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-slot": "1.2.0", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" @@ -1343,24 +1393,6 @@ } } }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", - "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-direction": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", @@ -1377,14 +1409,14 @@ } }, "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.7.tgz", - "integrity": "sha512-j5+WBUdhccJsmH5/H0K6RncjDtoALSEr6jbkaZu+bjw6hOPOhHycr6vEUujl+HBK8kjUfWcoCJXxP6e4lUlMZw==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz", + "integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, @@ -1404,17 +1436,17 @@ } }, "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.12", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.12.tgz", - "integrity": "sha512-VJoMs+BWWE7YhzEQyVwvF9n22Eiyr83HotCVrMQzla/OwRovXCgah7AcaEr4hMNj4gJxSdtIbcHGvmJXOoJVHA==", + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.15.tgz", + "integrity": "sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-menu": "2.1.12", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-menu": "2.1.15", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { @@ -1448,13 +1480,13 @@ } }, "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.4.tgz", - "integrity": "sha512-r2annK27lIW5w9Ho5NyQgqs0MmgZSTIKXWpVCJaLC1q2kZrZkcqnmHkCHMEmv8XLvsLlurKMPT+kbKkRkm/xVA==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { @@ -1473,19 +1505,19 @@ } }, "node_modules/@radix-ui/react-hover-card": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.11.tgz", - "integrity": "sha512-q9h9grUpGZKR3MNhtVCLVnPGmx1YnzBgGR+O40mhSNGsUnkR+LChVH8c7FB0mkS+oudhd8KAkZGTJPJCjdAPIg==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.14.tgz", + "integrity": "sha512-CPYZ24Mhirm+g6D8jArmLzjYu4Eyg3TTUHswR26QgzXBHBe64BO/RHOJKzmF/Dxb4y4f9PKyJdwm/O/AhNkb+Q==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.7", - "@radix-ui/react-popper": "1.2.4", - "@radix-ui/react-portal": "1.1.6", + "@radix-ui/react-dismissable-layer": "1.1.10", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { @@ -1522,35 +1554,12 @@ } }, "node_modules/@radix-ui/react-label": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.6.tgz", - "integrity": "sha512-S/hv1mTlgcPX2gCTJrWuTjSXf7ER3Zf7zWGtOprxhIIY93Qin3n5VgNA0Ez9AgrK/lEtlYgzLd4f5x6AVar4Yw==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-primitive": "2.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "@types/react-dom": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - }, - "@types/react-dom": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.2.tgz", - "integrity": "sha512-uHa+l/lKfxuDD2zjN/0peM/RhhSmRjr5YWdk/37EnSv1nJ88uvG85DPexSm8HdFQROd2VdERJ6ynXbkCFi+APw==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz", + "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.2.2" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -1568,26 +1577,26 @@ } }, "node_modules/@radix-ui/react-menu": { - "version": "2.1.12", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.12.tgz", - "integrity": "sha512-+qYq6LfbiGo97Zz9fioX83HCiIYYFNs8zAsVCMQrIakoNYylIzWuoD/anAD3UzvvR6cnswmfRFJFq/zYYq/k7Q==", + "version": "2.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.15.tgz", + "integrity": "sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.4", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.7", + "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.4", + "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.4", - "@radix-ui/react-portal": "1.1.6", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-roving-focus": "1.1.7", - "@radix-ui/react-slot": "1.2.0", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", + "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" @@ -1607,39 +1616,21 @@ } } }, - "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", - "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-menubar": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.1.12.tgz", - "integrity": "sha512-bM2vT5nxRqJH/d1vFQ9jLsW4qR70yFQw2ZD1TUPWUNskDsV0eYeMbbNJqxNjGMOVogEkOJaHtu11kzYdTJvVJg==", + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.1.15.tgz", + "integrity": "sha512-Z71C7LGD+YDYo3TV81paUs8f3Zbmkvg6VLRQpKYfzioOE6n7fOhA3ApK/V/2Odolxjoc4ENk8AYCjohCNayd5A==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.4", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-menu": "2.1.12", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-roving-focus": "1.1.7", + "@radix-ui/react-menu": "2.1.15", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { @@ -1658,25 +1649,25 @@ } }, "node_modules/@radix-ui/react-navigation-menu": { - "version": "1.2.10", - "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.10.tgz", - "integrity": "sha512-kGDqMVPj2SRB1vJmXN/jnhC66REAXNyDmDRubbbmJ+360zSIJUDmWGMKIJOf72PHMwPENrbtJVb3CMAUJDjEIA==", + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.13.tgz", + "integrity": "sha512-WG8wWfDiJlSF5hELjwfjSGOXcBR/ZMhBFCGYe8vERpC39CQYZeq1PQ2kaYHdye3V95d06H89KGMsVCIE4LWo3g==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.4", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.7", + "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-visually-hidden": "1.2.0" + "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -1694,23 +1685,23 @@ } }, "node_modules/@radix-ui/react-popover": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.11.tgz", - "integrity": "sha512-yFMfZkVA5G3GJnBgb2PxrrcLKm1ZLWXrbYVgdyTl//0TYEIHS9LJbnyz7WWcZ0qCq7hIlJZpRtxeSeIG5T5oJw==", + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.14.tgz", + "integrity": "sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.7", + "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.4", + "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.4", - "@radix-ui/react-portal": "1.1.6", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-slot": "1.2.0", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" @@ -1730,35 +1721,17 @@ } } }, - "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", - "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-popper": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.4.tgz", - "integrity": "sha512-3p2Rgm/a1cK0r/UVkx5F/K9v/EplfjAeIFCGOPYPO4lZ0jtg4iSQXt/YGTSLWaf4x7NG6Z4+uKFcylcTZjeqDA==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz", + "integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==", "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.4", + "@radix-ui/react-arrow": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-rect": "1.1.1", @@ -1781,12 +1754,12 @@ } }, "node_modules/@radix-ui/react-portal": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.6.tgz", - "integrity": "sha512-XmsIl2z1n/TsYFLIdYam2rmFwf9OC/Sh2avkbmVMDuBZIe7hSpM0cYnWPAo7nHOVx8zTuwDZGByfcqLdnzp3Vw==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { @@ -1829,12 +1802,12 @@ } }, "node_modules/@radix-ui/react-primitive": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.0.tgz", - "integrity": "sha512-/J/FhLdK0zVcILOwt5g+dH4KnkonCtkVJsa2G6JmvbbtZfBEI1gMsO3QMjseL4F/SwfAMt1Vc/0XKYKq+xJ1sw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", "license": "MIT", "dependencies": { - "@radix-ui/react-slot": "1.2.0" + "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -1851,32 +1824,14 @@ } } }, - "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", - "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-progress": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.4.tgz", - "integrity": "sha512-8rl9w7lJdcVPor47Dhws9mUHRHLE+8JEgyJRdNWCpGPa6HIlr3eh+Yn9gyx1CnCLbw5naHsI2gaO9dBWO50vzw==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.7.tgz", + "integrity": "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==", "license": "MIT", "dependencies": { "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.0" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -1894,9 +1849,9 @@ } }, "node_modules/@radix-ui/react-radio-group": { - "version": "1.3.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.4.tgz", - "integrity": "sha512-N4J9QFdW5zcJNxxY/zwTXBN4Uc5VEuRM7ZLjNfnWoKmNvgrPtNNw4P8zY532O3qL6aPkaNO+gY9y6bfzmH4U1g==", + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.7.tgz", + "integrity": "sha512-9w5XhD0KPOrm92OTTE0SysH3sYzHsSTHNvZgUBo/VZ80VdYyB5RneDbc0dKpURS24IxkoFRu/hI0i4XyfFwY6g==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", @@ -1904,8 +1859,8 @@ "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-roving-focus": "1.1.7", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" @@ -1926,18 +1881,18 @@ } }, "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.7.tgz", - "integrity": "sha512-C6oAg451/fQT3EGbWHbCQjYTtbyjNO1uzQgMzwyivcHT3GKNEmu1q3UuREhN+HzHAVtv3ivMVK08QlC+PkYw9Q==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.10.tgz", + "integrity": "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.4", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, @@ -1957,9 +1912,9 @@ } }, "node_modules/@radix-ui/react-scroll-area": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.6.tgz", - "integrity": "sha512-lj8OMlpPERXrQIHlEQdlXHJoRT52AMpBrgyPYylOhXYq5e/glsEdtOc/kCQlsTdtgN5U0iDbrrolDadvektJGQ==", + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.9.tgz", + "integrity": "sha512-YSjEfBXnhUELsO2VzjdtYYD4CfQjvao+lhhrX5XsHD7/cyUNzljF1FHEbgTPN7LH2MClfwRMIsYlqTYpKTTe2A==", "license": "MIT", "dependencies": { "@radix-ui/number": "1.1.1", @@ -1968,7 +1923,7 @@ "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1" }, @@ -1988,30 +1943,30 @@ } }, "node_modules/@radix-ui/react-select": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.2.tgz", - "integrity": "sha512-HjkVHtBkuq+r3zUAZ/CvNWUGKPfuicGDbgtZgiQuFmNcV5F+Tgy24ep2nsAW2nFgvhGPJVqeBZa6KyVN0EyrBA==", + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.5.tgz", + "integrity": "sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==", "license": "MIT", "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.4", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.7", + "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-focus-guards": "1.1.2", - "@radix-ui/react-focus-scope": "1.1.4", + "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.4", - "@radix-ui/react-portal": "1.1.6", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-slot": "1.2.0", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", - "@radix-ui/react-visually-hidden": "1.2.0", + "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, @@ -2030,31 +1985,13 @@ } } }, - "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", - "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-separator": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.4.tgz", - "integrity": "sha512-2fTm6PSiUm8YPq9W0E4reYuv01EE3aFSzt8edBiXqPHshF8N9+Kymt/k0/R+F3dkY5lQyB/zPtrP82phskLi7w==", + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz", + "integrity": "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.0" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -2072,18 +2009,18 @@ } }, "node_modules/@radix-ui/react-slider": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.3.2.tgz", - "integrity": "sha512-oQnqfgSiYkxZ1MrF6672jw2/zZvpB+PJsrIc3Zm1zof1JHf/kj7WhmROw7JahLfOwYQ5/+Ip0rFORgF1tjSiaQ==", + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.3.5.tgz", + "integrity": "sha512-rkfe2pU2NBAYfGaxa3Mqosi7VZEWX5CxKaanRv0vZd4Zhl9fvQrg0VM93dv3xGLGfrHuoTRF3JXH8nb9g+B3fw==", "license": "MIT", "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-collection": "1.1.4", + "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", @@ -2105,9 +2042,9 @@ } }, "node_modules/@radix-ui/react-slot": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.2.tgz", - "integrity": "sha512-y7TBO4xN4Y94FvcWIOIh18fM4R1A8S4q1jhoz4PNzOoHsFcN8pogcFmZrTYAm4F9VRUrWP/Mw7xSKybIeRI+CQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", "license": "MIT", "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" @@ -2123,15 +2060,15 @@ } }, "node_modules/@radix-ui/react-switch": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.2.tgz", - "integrity": "sha512-7Z8n6L+ifMIIYZ83f28qWSceUpkXuslI2FJ34+kDMTiyj91ENdpdQ7VCidrzj5JfwfZTeano/BnGBbu/jqa5rQ==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.5.tgz", + "integrity": "sha512-5ijLkak6ZMylXsaImpZ8u4Rlf5grRmoc0p0QeX9VJtlrM4f5m3nCTX8tWga/zOA8PZYIR/t0p2Mnvd7InrJ6yQ==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-use-size": "1.1.1" @@ -2152,9 +2089,9 @@ } }, "node_modules/@radix-ui/react-tabs": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.9.tgz", - "integrity": "sha512-KIjtwciYvquiW/wAFkELZCVnaNLBsYNhTNcvl+zfMAbMhRkcvNuCLXDDd22L0j7tagpzVh/QwbFpwAATg7ILPw==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.12.tgz", + "integrity": "sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", @@ -2162,8 +2099,8 @@ "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-roving-focus": "1.1.7", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { @@ -2182,13 +2119,13 @@ } }, "node_modules/@radix-ui/react-toggle": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.6.tgz", - "integrity": "sha512-3SeJxKeO3TO1zVw1Nl++Cp0krYk6zHDHMCUXXVkosIzl6Nxcvb07EerQpyD2wXQSJ5RZajrYAmPaydU8Hk1IyQ==", + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.9.tgz", + "integrity": "sha512-ZoFkBBz9zv9GWer7wIjvdRxmh2wyc2oKWw6C6CseWd6/yq1DK/l5lJ+wnsmFwJZbBYqr02mrf8A2q/CVCuM3ZA==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", - "@radix-ui/react-primitive": "2.1.0", + "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { @@ -2207,17 +2144,17 @@ } }, "node_modules/@radix-ui/react-toggle-group": { - "version": "1.1.7", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.7.tgz", - "integrity": "sha512-GRaPJhxrRSOqAcmcX3MwRL/SZACkoYdmoY9/sg7Bd5DhBYsB2t4co0NxTvVW8H7jUmieQDQwRtUlZ5Ta8UbgJA==", + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.10.tgz", + "integrity": "sha512-kiU694Km3WFLTC75DdqgM/3Jauf3rD9wxeS9XtyWFKsBUeZA337lC+6uUazT7I1DhanZ5gyD5Stf8uf2dbQxOQ==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-roving-focus": "1.1.7", - "@radix-ui/react-toggle": "1.1.6", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.10", + "@radix-ui/react-toggle": "1.1.9", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { @@ -2236,23 +2173,23 @@ } }, "node_modules/@radix-ui/react-tooltip": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.4.tgz", - "integrity": "sha512-DyW8VVeeMSSLFvAmnVnCwvI3H+1tpJFHT50r+tdOoMse9XqYDBCcyux8u3G2y+LOpt7fPQ6KKH0mhs+ce1+Z5w==", + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.7.tgz", + "integrity": "sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw==", "license": "MIT", "dependencies": { "@radix-ui/primitive": "1.1.2", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", - "@radix-ui/react-dismissable-layer": "1.1.7", + "@radix-ui/react-dismissable-layer": "1.1.10", "@radix-ui/react-id": "1.1.1", - "@radix-ui/react-popper": "1.2.4", - "@radix-ui/react-portal": "1.1.6", + "@radix-ui/react-popper": "1.2.7", + "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.4", - "@radix-ui/react-primitive": "2.1.0", - "@radix-ui/react-slot": "1.2.0", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", - "@radix-ui/react-visually-hidden": "1.2.0" + "@radix-ui/react-visually-hidden": "1.2.3" }, "peerDependencies": { "@types/react": "*", @@ -2269,24 +2206,6 @@ } } }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.0.tgz", - "integrity": "sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.2" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", @@ -2442,12 +2361,12 @@ } }, "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.0.tgz", - "integrity": "sha512-rQj0aAWOpCdCMRbI6pLQm8r7S2BM3YhTa0SzOYD55k+hJA8oo9J+H+9wLM9oMlZWOX/wJWPTzfDfmZkf7LvCfg==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.1.0" + "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", @@ -2491,9 +2410,9 @@ "license": "MIT" }, "node_modules/@supabase/auth-js": { - "version": "2.69.1", - "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.69.1.tgz", - "integrity": "sha512-FILtt5WjCNzmReeRLq5wRs3iShwmnWgBvxHfqapC/VoljJl+W8hDAyFmf1NVw3zH+ZjZ05AKxiKxVeb0HNWRMQ==", + "version": "2.70.0", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.70.0.tgz", + "integrity": "sha512-BaAK/tOAZFJtzF1sE3gJ2FwTjLf4ky3PSvcvLGEgEmO4BSBkwWKu8l67rLLIBZPDnCyV7Owk2uPyKHa0kj5QGg==", "license": "MIT", "peer": true, "dependencies": { @@ -2534,16 +2453,16 @@ } }, "node_modules/@supabase/realtime-js": { - "version": "2.11.2", - "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.11.2.tgz", - "integrity": "sha512-u/XeuL2Y0QEhXSoIPZZwR6wMXgB+RQbJzG9VErA3VghVt7uRfSVsjeqd7m5GhX3JR6dM/WRmLbVR8URpDWG4+w==", + "version": "2.11.10", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.11.10.tgz", + "integrity": "sha512-SJKVa7EejnuyfImrbzx+HaD9i6T784khuw1zP+MBD7BmJYChegGxYigPzkKX8CK8nGuDntmeSD3fvriaH0EGZA==", "license": "MIT", "peer": true, "dependencies": { - "@supabase/node-fetch": "^2.6.14", - "@types/phoenix": "^1.5.4", - "@types/ws": "^8.5.10", - "ws": "^8.18.0" + "@supabase/node-fetch": "^2.6.13", + "@types/phoenix": "^1.6.6", + "@types/ws": "^8.18.1", + "ws": "^8.18.2" } }, "node_modules/@supabase/ssr": { @@ -2569,17 +2488,17 @@ } }, "node_modules/@supabase/supabase-js": { - "version": "2.49.4", - "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.49.4.tgz", - "integrity": "sha512-jUF0uRUmS8BKt37t01qaZ88H9yV1mbGYnqLeuFWLcdV+x1P4fl0yP9DGtaEhFPZcwSom7u16GkLEH9QJZOqOkw==", + "version": "2.50.0", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.50.0.tgz", + "integrity": "sha512-M1Gd5tPaaghYZ9OjeO1iORRqbTWFEz/cF3pPubRnMPzA+A8SiUsXXWDP+DWsASZcjEcVEcVQIAF38i5wrijYOg==", "license": "MIT", "peer": true, "dependencies": { - "@supabase/auth-js": "2.69.1", + "@supabase/auth-js": "2.70.0", "@supabase/functions-js": "2.4.4", "@supabase/node-fetch": "2.6.15", "@supabase/postgrest-js": "1.19.4", - "@supabase/realtime-js": "2.11.2", + "@supabase/realtime-js": "2.11.10", "@supabase/storage-js": "2.7.1" } }, @@ -2599,46 +2518,54 @@ } }, "node_modules/@tailwindcss/node": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.4.tgz", - "integrity": "sha512-MT5118zaiO6x6hNA04OWInuAiP1YISXql8Z+/Y8iisV5nuhM8VXlyhRuqc2PEviPszcXI66W44bCIk500Oolhw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.8.tgz", + "integrity": "sha512-OWwBsbC9BFAJelmnNcrKuf+bka2ZxCE2A4Ft53Tkg4uoiE67r/PMEYwCsourC26E+kmxfwE0hVzMdxqeW+xu7Q==", "dev": true, "license": "MIT", "dependencies": { + "@ampproject/remapping": "^2.3.0", "enhanced-resolve": "^5.18.1", "jiti": "^2.4.2", - "lightningcss": "1.29.2", - "tailwindcss": "4.1.4" + "lightningcss": "1.30.1", + "magic-string": "^0.30.17", + "source-map-js": "^1.2.1", + "tailwindcss": "4.1.8" } }, "node_modules/@tailwindcss/oxide": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.4.tgz", - "integrity": "sha512-p5wOpXyOJx7mKh5MXh5oKk+kqcz8T+bA3z/5VWWeQwFrmuBItGwz8Y2CHk/sJ+dNb9B0nYFfn0rj/cKHZyjahQ==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.8.tgz", + "integrity": "sha512-d7qvv9PsM5N3VNKhwVUhpK6r4h9wtLkJ6lz9ZY9aeZgrUWk1Z8VPyqyDT9MZlem7GTGseRQHkeB1j3tC7W1P+A==", "dev": true, + "hasInstallScript": true, "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.4", + "tar": "^7.4.3" + }, "engines": { "node": ">= 10" }, "optionalDependencies": { - "@tailwindcss/oxide-android-arm64": "4.1.4", - "@tailwindcss/oxide-darwin-arm64": "4.1.4", - "@tailwindcss/oxide-darwin-x64": "4.1.4", - "@tailwindcss/oxide-freebsd-x64": "4.1.4", - "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.4", - "@tailwindcss/oxide-linux-arm64-gnu": "4.1.4", - "@tailwindcss/oxide-linux-arm64-musl": "4.1.4", - "@tailwindcss/oxide-linux-x64-gnu": "4.1.4", - "@tailwindcss/oxide-linux-x64-musl": "4.1.4", - "@tailwindcss/oxide-wasm32-wasi": "4.1.4", - "@tailwindcss/oxide-win32-arm64-msvc": "4.1.4", - "@tailwindcss/oxide-win32-x64-msvc": "4.1.4" + "@tailwindcss/oxide-android-arm64": "4.1.8", + "@tailwindcss/oxide-darwin-arm64": "4.1.8", + "@tailwindcss/oxide-darwin-x64": "4.1.8", + "@tailwindcss/oxide-freebsd-x64": "4.1.8", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.8", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.8", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.8", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.8", + "@tailwindcss/oxide-linux-x64-musl": "4.1.8", + "@tailwindcss/oxide-wasm32-wasi": "4.1.8", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.8", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.8" } }, "node_modules/@tailwindcss/oxide-android-arm64": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.4.tgz", - "integrity": "sha512-xMMAe/SaCN/vHfQYui3fqaBDEXMu22BVwQ33veLc8ep+DNy7CWN52L+TTG9y1K397w9nkzv+Mw+mZWISiqhmlA==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.8.tgz", + "integrity": "sha512-Fbz7qni62uKYceWYvUjRqhGfZKwhZDQhlrJKGtnZfuNtHFqa8wmr+Wn74CTWERiW2hn3mN5gTpOoxWKk0jRxjg==", "cpu": [ "arm64" ], @@ -2653,9 +2580,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-arm64": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.4.tgz", - "integrity": "sha512-JGRj0SYFuDuAGilWFBlshcexev2hOKfNkoX+0QTksKYq2zgF9VY/vVMq9m8IObYnLna0Xlg+ytCi2FN2rOL0Sg==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.8.tgz", + "integrity": "sha512-RdRvedGsT0vwVVDztvyXhKpsU2ark/BjgG0huo4+2BluxdXo8NDgzl77qh0T1nUxmM11eXwR8jA39ibvSTbi7A==", "cpu": [ "arm64" ], @@ -2670,9 +2597,9 @@ } }, "node_modules/@tailwindcss/oxide-darwin-x64": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.4.tgz", - "integrity": "sha512-sdDeLNvs3cYeWsEJ4H1DvjOzaGios4QbBTNLVLVs0XQ0V95bffT3+scptzYGPMjm7xv4+qMhCDrkHwhnUySEzA==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.8.tgz", + "integrity": "sha512-t6PgxjEMLp5Ovf7uMb2OFmb3kqzVTPPakWpBIFzppk4JE4ix0yEtbtSjPbU8+PZETpaYMtXvss2Sdkx8Vs4XRw==", "cpu": [ "x64" ], @@ -2687,9 +2614,9 @@ } }, "node_modules/@tailwindcss/oxide-freebsd-x64": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.4.tgz", - "integrity": "sha512-VHxAqxqdghM83HslPhRsNhHo91McsxRJaEnShJOMu8mHmEj9Ig7ToHJtDukkuLWLzLboh2XSjq/0zO6wgvykNA==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.8.tgz", + "integrity": "sha512-g8C8eGEyhHTqwPStSwZNSrOlyx0bhK/V/+zX0Y+n7DoRUzyS8eMbVshVOLJTDDC+Qn9IJnilYbIKzpB9n4aBsg==", "cpu": [ "x64" ], @@ -2704,9 +2631,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.4.tgz", - "integrity": "sha512-OTU/m/eV4gQKxy9r5acuesqaymyeSCnsx1cFto/I1WhPmi5HDxX1nkzb8KYBiwkHIGg7CTfo/AcGzoXAJBxLfg==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.8.tgz", + "integrity": "sha512-Jmzr3FA4S2tHhaC6yCjac3rGf7hG9R6Gf2z9i9JFcuyy0u79HfQsh/thifbYTF2ic82KJovKKkIB6Z9TdNhCXQ==", "cpu": [ "arm" ], @@ -2721,9 +2648,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.4.tgz", - "integrity": "sha512-hKlLNvbmUC6z5g/J4H+Zx7f7w15whSVImokLPmP6ff1QqTVE+TxUM9PGuNsjHvkvlHUtGTdDnOvGNSEUiXI1Ww==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.8.tgz", + "integrity": "sha512-qq7jXtO1+UEtCmCeBBIRDrPFIVI4ilEQ97qgBGdwXAARrUqSn/L9fUrkb1XP/mvVtoVeR2bt/0L77xx53bPZ/Q==", "cpu": [ "arm64" ], @@ -2738,9 +2665,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-arm64-musl": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.4.tgz", - "integrity": "sha512-X3As2xhtgPTY/m5edUtddmZ8rCruvBvtxYLMw9OsZdH01L2gS2icsHRwxdU0dMItNfVmrBezueXZCHxVeeb7Aw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.8.tgz", + "integrity": "sha512-O6b8QesPbJCRshsNApsOIpzKt3ztG35gfX9tEf4arD7mwNinsoCKxkj8TgEE0YRjmjtO3r9FlJnT/ENd9EVefQ==", "cpu": [ "arm64" ], @@ -2755,9 +2682,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-gnu": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.4.tgz", - "integrity": "sha512-2VG4DqhGaDSmYIu6C4ua2vSLXnJsb/C9liej7TuSO04NK+JJJgJucDUgmX6sn7Gw3Cs5ZJ9ZLrnI0QRDOjLfNQ==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.8.tgz", + "integrity": "sha512-32iEXX/pXwikshNOGnERAFwFSfiltmijMIAbUhnNyjFr3tmWmMJWQKU2vNcFX0DACSXJ3ZWcSkzNbaKTdngH6g==", "cpu": [ "x64" ], @@ -2772,9 +2699,9 @@ } }, "node_modules/@tailwindcss/oxide-linux-x64-musl": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.4.tgz", - "integrity": "sha512-v+mxVgH2kmur/X5Mdrz9m7TsoVjbdYQT0b4Z+dr+I4RvreCNXyCFELZL/DO0M1RsidZTrm6O1eMnV6zlgEzTMQ==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.8.tgz", + "integrity": "sha512-s+VSSD+TfZeMEsCaFaHTaY5YNj3Dri8rST09gMvYQKwPphacRG7wbuQ5ZJMIJXN/puxPcg/nU+ucvWguPpvBDg==", "cpu": [ "x64" ], @@ -2789,9 +2716,9 @@ } }, "node_modules/@tailwindcss/oxide-wasm32-wasi": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.4.tgz", - "integrity": "sha512-2TLe9ir+9esCf6Wm+lLWTMbgklIjiF0pbmDnwmhR9MksVOq+e8aP3TSsXySnBDDvTTVd/vKu1aNttEGj3P6l8Q==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.8.tgz", + "integrity": "sha512-CXBPVFkpDjM67sS1psWohZ6g/2/cd+cq56vPxK4JeawelxwK4YECgl9Y9TjkE2qfF+9/s1tHHJqrC4SS6cVvSg==", "bundleDependencies": [ "@napi-rs/wasm-runtime", "@emnapi/core", @@ -2807,10 +2734,10 @@ "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.4.0", - "@emnapi/runtime": "^1.4.0", - "@emnapi/wasi-threads": "^1.0.1", - "@napi-rs/wasm-runtime": "^0.2.8", + "@emnapi/core": "^1.4.3", + "@emnapi/runtime": "^1.4.3", + "@emnapi/wasi-threads": "^1.0.2", + "@napi-rs/wasm-runtime": "^0.2.10", "@tybys/wasm-util": "^0.9.0", "tslib": "^2.8.0" }, @@ -2819,9 +2746,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.4.tgz", - "integrity": "sha512-VlnhfilPlO0ltxW9/BgfLI5547PYzqBMPIzRrk4W7uupgCt8z6Trw/tAj6QUtF2om+1MH281Pg+HHUJoLesmng==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.8.tgz", + "integrity": "sha512-7GmYk1n28teDHUjPlIx4Z6Z4hHEgvP5ZW2QS9ygnDAdI/myh3HTHjDqtSqgu1BpRoI4OiLx+fThAyA1JePoENA==", "cpu": [ "arm64" ], @@ -2836,9 +2763,9 @@ } }, "node_modules/@tailwindcss/oxide-win32-x64-msvc": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.4.tgz", - "integrity": "sha512-+7S63t5zhYjslUGb8NcgLpFXD+Kq1F/zt5Xv5qTv7HaFTG/DHyHD9GA6ieNAxhgyA4IcKa/zy7Xx4Oad2/wuhw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.8.tgz", + "integrity": "sha512-fou+U20j+Jl0EHwK92spoWISON2OBnCazIc038Xj2TdweYV33ZRkS9nwqiUi2d/Wba5xg5UoHfvynnb/UB49cQ==", "cpu": [ "x64" ], @@ -2853,17 +2780,17 @@ } }, "node_modules/@tailwindcss/postcss": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.4.tgz", - "integrity": "sha512-bjV6sqycCEa+AQSt2Kr7wpGF1bOZJ5wsqnLEkqSbM/JEHxx/yhMH8wHmdkPyApF9xhHeMSwnnkDUUMMM/hYnXw==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.8.tgz", + "integrity": "sha512-vB/vlf7rIky+w94aWMw34bWW1ka6g6C3xIOdICKX2GC0VcLtL6fhlLiafF0DVIwa9V6EHz8kbWMkS2s2QvvNlw==", "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", - "@tailwindcss/node": "4.1.4", - "@tailwindcss/oxide": "4.1.4", + "@tailwindcss/node": "4.1.8", + "@tailwindcss/oxide": "4.1.8", "postcss": "^8.4.41", - "tailwindcss": "4.1.4" + "tailwindcss": "4.1.8" } }, "node_modules/@tailwindcss/typography": { @@ -2965,9 +2892,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "license": "MIT" }, "node_modules/@types/estree-jsx": { @@ -3024,12 +2951,12 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.17.31", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.31.tgz", - "integrity": "sha512-quODOCNXQAbNf1Q7V+fI8WyErOCh0D5Yd31vHnKu4GkSztGQ7rlltAaqXhHhLl33tlVyUXs2386MkANSwgDn6A==", + "version": "20.19.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.0.tgz", + "integrity": "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==", "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.21.0" } }, "node_modules/@types/phoenix": { @@ -3040,18 +2967,18 @@ "peer": true }, "node_modules/@types/react": { - "version": "19.1.2", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.2.tgz", - "integrity": "sha512-oxLPMytKchWGbnQM9O7D67uPa9paTNxO7jVoNMXgkkErULBPhPARCfkKL9ytcIJJRGjbsVwW4ugJzyFFvm/Tiw==", + "version": "19.1.7", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.7.tgz", + "integrity": "sha512-BnsPLV43ddr05N71gaGzyZ5hzkCmGwhMvYc8zmvI8Ci1bRkkDSzDDVfAXfN2tk748OwI7ediiPX6PfT9p0QGVg==", "license": "MIT", "dependencies": { "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "19.1.2", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.2.tgz", - "integrity": "sha512-XGJkWF41Qq305SKWEILa1O8vzhb3aOo3ogBlSmiqNko/WmRb6QIaweuZCXjKygVDXpzXb5wyxKTSOsmkuqj+Qw==", + "version": "19.1.6", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz", + "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==", "devOptional": true, "license": "MIT", "peerDependencies": { @@ -3075,21 +3002,21 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.31.0.tgz", - "integrity": "sha512-evaQJZ/J/S4wisevDvC1KFZkPzRetH8kYZbkgcTRyql3mcKsf+ZFDV1BVWUGTCAW5pQHoqn5gK5b8kn7ou9aFQ==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.0.tgz", + "integrity": "sha512-QXwAlHlbcAwNlEEMKQS2RCgJsgXrTJdjXT08xEgbPFa2yYQgVjBymxP5DrfrE7X7iodSzd9qBUHUycdyVJTW1w==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.31.0", - "@typescript-eslint/type-utils": "8.31.0", - "@typescript-eslint/utils": "8.31.0", - "@typescript-eslint/visitor-keys": "8.31.0", + "@typescript-eslint/scope-manager": "8.34.0", + "@typescript-eslint/type-utils": "8.34.0", + "@typescript-eslint/utils": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0", "graphemer": "^1.4.0", - "ignore": "^5.3.1", + "ignore": "^7.0.0", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3099,22 +3026,32 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "@typescript-eslint/parser": "^8.34.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/@typescript-eslint/parser": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.31.0.tgz", - "integrity": "sha512-67kYYShjBR0jNI5vsf/c3WG4u+zDnCTHTPqVMQguffaWWFs7artgwKmfwdifl+r6XyM5LYLas/dInj2T0SgJyw==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.0.tgz", + "integrity": "sha512-vxXJV1hVFx3IXz/oy2sICsJukaBrtDEQSBiV48/YIV5KWjX1dO+bcIr/kCPrW6weKXvsaGKFNlwH0v2eYdRRbA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.31.0", - "@typescript-eslint/types": "8.31.0", - "@typescript-eslint/typescript-estree": "8.31.0", - "@typescript-eslint/visitor-keys": "8.31.0", + "@typescript-eslint/scope-manager": "8.34.0", + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/typescript-estree": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0", "debug": "^4.3.4" }, "engines": { @@ -3129,15 +3066,37 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.0.tgz", + "integrity": "sha512-iEgDALRf970/B2YExmtPMPF54NenZUf4xpL3wsCRx/lgjz6ul/l13R81ozP/ZNuXfnLCS+oPmG7JIxfdNYKELw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.34.0", + "@typescript-eslint/types": "^8.34.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.31.0.tgz", - "integrity": "sha512-knO8UyF78Nt8O/B64i7TlGXod69ko7z6vJD9uhSlm0qkAbGeRUSudcm0+K/4CrRjrpiHfBCjMWlc08Vav1xwcw==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.0.tgz", + "integrity": "sha512-9Ac0X8WiLykl0aj1oYQNcLZjHgBojT6cW68yAgZ19letYu+Hxd0rE0veI1XznSSst1X5lwnxhPbVdwjDRIomRw==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.31.0", - "@typescript-eslint/visitor-keys": "8.31.0" + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3147,17 +3106,34 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.0.tgz", + "integrity": "sha512-+W9VYHKFIzA5cBeooqQxqNriAP0QeQ7xTiDuIOr71hzgffm3EL2hxwWBIIj4GuofIbKxGNarpKqIq6Q6YrShOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.31.0.tgz", - "integrity": "sha512-DJ1N1GdjI7IS7uRlzJuEDCgDQix3ZVYVtgeWEyhyn4iaoitpMBX6Ndd488mXSx0xah/cONAkEaYyylDyAeHMHg==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.0.tgz", + "integrity": "sha512-n7zSmOcUVhcRYC75W2pnPpbO1iwhJY3NLoHEtbJwJSNlVAZuwqu05zY3f3s2SDWWDSo9FdN5szqc73DCtDObAg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.31.0", - "@typescript-eslint/utils": "8.31.0", + "@typescript-eslint/typescript-estree": "8.34.0", + "@typescript-eslint/utils": "8.34.0", "debug": "^4.3.4", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3172,9 +3148,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.31.0.tgz", - "integrity": "sha512-Ch8oSjVyYyJxPQk8pMiP2FFGYatqXQfQIaMp+TpuuLlDachRWpUAeEu1u9B/v/8LToehUIWyiKcA/w5hUFRKuQ==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.0.tgz", + "integrity": "sha512-9V24k/paICYPniajHfJ4cuAWETnt7Ssy+R0Rbcqo5sSFr3QEZ/8TSoUi9XeXVBGXCaLtwTOKSLGcInCAvyZeMA==", "dev": true, "license": "MIT", "engines": { @@ -3186,20 +3162,22 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.31.0.tgz", - "integrity": "sha512-xLmgn4Yl46xi6aDSZ9KkyfhhtnYI15/CvHbpOy/eR5NWhK/BK8wc709KKwhAR0m4ZKRP7h07bm4BWUYOCuRpQQ==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.0.tgz", + "integrity": "sha512-rOi4KZxI7E0+BMqG7emPSK1bB4RICCpF7QD3KCLXn9ZvWoESsOMlHyZPAHyG04ujVplPaHbmEvs34m+wjgtVtg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.31.0", - "@typescript-eslint/visitor-keys": "8.31.0", + "@typescript-eslint/project-service": "8.34.0", + "@typescript-eslint/tsconfig-utils": "8.34.0", + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/visitor-keys": "8.34.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", - "ts-api-utils": "^2.0.1" + "ts-api-utils": "^2.1.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3213,9 +3191,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { @@ -3269,16 +3247,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.31.0.tgz", - "integrity": "sha512-qi6uPLt9cjTFxAb1zGNgTob4x9ur7xC6mHQJ8GwEzGMGE9tYniublmJaowOJ9V2jUzxrltTPfdG2nKlWsq0+Ww==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.0.tgz", + "integrity": "sha512-8L4tWatGchV9A1cKbjaavS6mwYwp39jql8xUmIIKJdm+qiaeHy5KMKlBrf30akXAWBzn2SqKsNOtSENWUwg7XQ==", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.31.0", - "@typescript-eslint/types": "8.31.0", - "@typescript-eslint/typescript-estree": "8.31.0" + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.34.0", + "@typescript-eslint/types": "8.34.0", + "@typescript-eslint/typescript-estree": "8.34.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3293,13 +3271,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.31.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.31.0.tgz", - "integrity": "sha512-QcGHmlRHWOl93o64ZUMNewCdwKGU6WItOU52H0djgNmn1EOrhVudrDzXz4OycCRSCPwFCDrE2iIt5vmuUdHxuQ==", + "version": "8.34.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.0.tgz", + "integrity": "sha512-qHV7pW7E85A0x6qyrFn+O+q1k1p3tQCsqIZ1KZ5ESLXY57aTvUd3/a4rdPTeXisvhXn2VQG0VSKUqs8KHF2zcA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.31.0", + "@typescript-eslint/types": "8.34.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -3317,9 +3295,9 @@ "license": "ISC" }, "node_modules/@unrs/resolver-binding-darwin-arm64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.7.0.tgz", - "integrity": "sha512-vIWAU56r2lZAmUsljp6m9+hrTlwNkZH6pqnSPff2WxzofV+jWRSHLmZRUS+g+VE+LlyPByifmGGHpJmhWetatg==", + "version": "1.7.13", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.7.13.tgz", + "integrity": "sha512-LIKeCzNSkTWwGHjtiUIfvS96+7kpuyrKq2pzw/0XT2S8ykczj40Hh27oLTbXguCX8tGrCoaD2yXxzwqMMhAzhA==", "cpu": [ "arm64" ], @@ -3331,9 +3309,9 @@ ] }, "node_modules/@unrs/resolver-binding-darwin-x64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.7.0.tgz", - "integrity": "sha512-+bShFLgtdwuNteQbKq3X230754AouNMXSLDZ56EssgDyckDt6Ld7wRaJjZF0pY671HnY2pk9/amO4amAFzfN1A==", + "version": "1.7.13", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.7.13.tgz", + "integrity": "sha512-GB5G3qUNrdo2l6xaZehpz1ln4wCQ75tr51HZ8OQEcX6XkBIFVL9E4ikCZvCmRmUgKGR+zP5ogyFib7ZbIMWKWA==", "cpu": [ "x64" ], @@ -3345,9 +3323,9 @@ ] }, "node_modules/@unrs/resolver-binding-freebsd-x64": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.7.0.tgz", - "integrity": "sha512-HJjXb3aIptDZQ0saSmk2S4W1pWNVZ2iNpAbNGZOfsUXbi8xwCmHdVjErNS92hRp7djuDLup1OLrzOMtTdw5BmA==", + "version": "1.7.13", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.7.13.tgz", + "integrity": "sha512-rb8gzoBgqVhDkQiKaq+MrFPhNK3x8XkSFhgU55LfgOa5skv7KIdM3dELKzQVNZNlY49DuZmm0FsEfHK5xPKKiA==", "cpu": [ "x64" ], @@ -3359,9 +3337,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-gnueabihf": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.7.0.tgz", - "integrity": "sha512-NF3lk7KHulLD97UE+MHjH0mrOjeZG8Hz10h48YcFz2V0rlxBdRSRcMbGer8iH/1mIlLqxtvXJfGLUr4SMj0XZg==", + "version": "1.7.13", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.7.13.tgz", + "integrity": "sha512-bqdzngbTGzhsqhTV3SWECyZUAyvtewKtrCW4E8QPcK6yHSaN0k1h9gKwNOBxFwIqkQRsAibpm18XDum8M5AiCw==", "cpu": [ "arm" ], @@ -3373,9 +3351,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm-musleabihf": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.7.0.tgz", - "integrity": "sha512-Gn1c/t24irDgU8yYj4vVG6qHplwUM42ti9/zYWgfmFjoXCH6L4Ab9hh6HuO7bfDSvGDRGWQt1IVaBpgbKHdh3Q==", + "version": "1.7.13", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.7.13.tgz", + "integrity": "sha512-vkoL3DSS5tsUNLhNtBJWaqDJNNEQsMCr0o2N02sLCSpe5S8TQHz+klQT42Qgj4PqATMwnG3OF0QQ5BH0oAKIPg==", "cpu": [ "arm" ], @@ -3387,9 +3365,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-gnu": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.7.0.tgz", - "integrity": "sha512-XRrVXRIUP++qyqAqgiXUpOv0GP3cHx7aA7NrzVFf6Cc8FoYuwtnmT+vctfSo4wRZN71MNU4xq2BEFxI4qvSerg==", + "version": "1.7.13", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.7.13.tgz", + "integrity": "sha512-uNpLKxlDF+NF6aUztbAVhhFSF65zf/6QEfk5NifUgYFbpBObzvMnl2ydEsXV96spwPcmeNTpG9byvq+Twwd3HQ==", "cpu": [ "arm64" ], @@ -3401,9 +3379,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-arm64-musl": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.7.0.tgz", - "integrity": "sha512-Sligg+vTDAYTXkUtgviPjGEFIh57pkvlfdyRw21i9gkjp/eCNOAi2o5e7qLGTkoYdJHZJs5wVMViPEmAbw2/Tg==", + "version": "1.7.13", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.7.13.tgz", + "integrity": "sha512-mEFL6q7vtxA6YJ9sLbxCnKOBynOvClVOcqwUErmaCxA94hgP11rlstouySxJCGeFAb8KfUX9mui82waYrqoBlQ==", "cpu": [ "arm64" ], @@ -3415,9 +3393,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-ppc64-gnu": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.7.0.tgz", - "integrity": "sha512-Apek8/x+7Rg33zUJlQV44Bvq8/t1brfulk0veNJrk9wprF89bCYFMUHF7zQYcpf2u+m1+qs3mYQrBd43fGXhMA==", + "version": "1.7.13", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.7.13.tgz", + "integrity": "sha512-MjJaNk8HK3rCOIPS6AQPJXlrDfG1LaePum+CZddHZygPqDNZyVrVdWTadT+U51vIx5QOdEE0oXcgTY+7VYsU1g==", "cpu": [ "ppc64" ], @@ -3429,9 +3407,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-gnu": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.7.0.tgz", - "integrity": "sha512-kBale8CFX5clfV9VmI9EwKw2ZACMEx1ecjV92F9SeWTUoxl9d+LGzS6zMSX3kGYqcfJB3NXMwLCTwIDBLG1y4g==", + "version": "1.7.13", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.7.13.tgz", + "integrity": "sha512-9gAuT1+ed2eIuOXHSu4SdJOe7SUEzPTpOTEuTjGePvMEoWHywY5pvlcY7xMn3d8rhKHpwMzEhl8F8Oy+rkudzA==", "cpu": [ "riscv64" ], @@ -3443,9 +3421,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-riscv64-musl": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.7.0.tgz", - "integrity": "sha512-s/Q33xQjeFHSCvGl1sZztFZF6xhv7coMvFz6wa/x/ZlEArjiQoMMwGa/Aieq1Kp/6+S13iU3/IJF0ga6/451ow==", + "version": "1.7.13", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.7.13.tgz", + "integrity": "sha512-CNrJythJN9jC8SIJGoawebYylzGNJuWAWTKxxxx5Fr3DGEXbex/We4U7N4u6/dQAK3cLVOuAE/9a4D2JH35JIA==", "cpu": [ "riscv64" ], @@ -3457,9 +3435,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-s390x-gnu": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.7.0.tgz", - "integrity": "sha512-7PuNXAo97ydaxVNrIYJzPipvINJafDpB8pt5CoZHfu8BmqcU6d7kl6/SABTnqNffNkd6Cfhuo70jvGB2P7oJ/Q==", + "version": "1.7.13", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.7.13.tgz", + "integrity": "sha512-J0MVXXPvM2Bv+f+gzOZHLHEmXUJNKwJqkfMDTwE763w/tD+OA7UlTMLQihrcYRXwW5jZ8nbM2cEWTeFsTiH2JQ==", "cpu": [ "s390x" ], @@ -3471,9 +3449,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-gnu": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.7.0.tgz", - "integrity": "sha512-fNosEzDMYItA4It+R0tioHwKlEfx/3TkkJdP2x9B5o9R946NDC4ZZj5ZjA+Y4NQD2V/imB3QPAKmeh3vHQGQyA==", + "version": "1.7.13", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.7.13.tgz", + "integrity": "sha512-Ii2WhtIpeWUe6XG/YhPUX3JNL3PiyXe56PJzqAYDUyB0gctkk/nngpuPnNKlLMcN9FID0T39mIJPhA6YpRcGDQ==", "cpu": [ "x64" ], @@ -3485,9 +3463,9 @@ ] }, "node_modules/@unrs/resolver-binding-linux-x64-musl": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.7.0.tgz", - "integrity": "sha512-gHIw42dmnVcw7osjNPRybaXhONhggWkkzqiOZzXco1q3OKkn4KsbDylATeemnq3TP+L1BrzSqzl0H9UTJ6ji+w==", + "version": "1.7.13", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.7.13.tgz", + "integrity": "sha512-8F5E9EhtGYkfEM1OhyVgq76+SnMF5NfZS4v5Rq9JlfuqPnqXWgUjg903hxnG54PQr4I3jmG5bEeT77pGAA3Vvg==", "cpu": [ "x64" ], @@ -3499,9 +3477,9 @@ ] }, "node_modules/@unrs/resolver-binding-wasm32-wasi": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.7.0.tgz", - "integrity": "sha512-yq7POusv63/yTkNTaNsnXU/SAcBzckHyk1oYrDXqjS1m/goaWAaU9J9HrsovgTHkljxTcDd6PMAsJ5WZVBuGEQ==", + "version": "1.7.13", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.7.13.tgz", + "integrity": "sha512-7RXGTyDtyR/5o1FlBcjEaQQmQ2rKvu5Jq0Uhvce3PsbreZ61M4LQ5Mey2OMomIq4opphAkfDdm/lkHhWJNKNrw==", "cpu": [ "wasm32" ], @@ -3509,16 +3487,16 @@ "license": "MIT", "optional": true, "dependencies": { - "@napi-rs/wasm-runtime": "^0.2.9" + "@napi-rs/wasm-runtime": "^0.2.11" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@unrs/resolver-binding-win32-arm64-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.7.0.tgz", - "integrity": "sha512-/IPZPbdri9jglHonwB3F7EpQZvBK3ObH+g4ma/KDrqTEAECwvgE10Unvo0ox3LQFR/iMMAkVY+sGNMrMiIV/QQ==", + "version": "1.7.13", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.7.13.tgz", + "integrity": "sha512-MomJVcaVZe3j+CvkcfIVEcQyOOzauKpJYGY8d6PoKXn1FalMVGHX9/c0kXCI0WCK+CRGMExAiQhD8jkhyUVKxg==", "cpu": [ "arm64" ], @@ -3530,9 +3508,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-ia32-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.7.0.tgz", - "integrity": "sha512-NGVKbHEdrLuJdpcuGqV5zXO3v8t4CWOs0qeCGjO47RiwwufOi/yYcrtxtCzZAaMPBrffHL7c6tJ1Hxr17cPUGg==", + "version": "1.7.13", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.7.13.tgz", + "integrity": "sha512-pnHfzbFj6e4gUARI1Yvz0TUhmFZae248O7JOMCSmSBN3R35RJiKyHmsMuIiPrUYWDzm5jUMPTxSs+b3Ipawusw==", "cpu": [ "ia32" ], @@ -3544,9 +3522,9 @@ ] }, "node_modules/@unrs/resolver-binding-win32-x64-msvc": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.7.0.tgz", - "integrity": "sha512-Jf14pKofg58DIwcZv4Wt9AyVVe7bSJP8ODz+EP9nG/rho08FQzan0VOJk1g6/BNE1RkoYd+lRTWK+/BgH12qoQ==", + "version": "1.7.13", + "resolved": "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.7.13.tgz", + "integrity": "sha512-tI0+FTntE3BD0UxhTP12F/iTtkeMK+qh72/2aSxPZnTlOcMR9CTJid8CdppbSjj9wenq7PNcqScLtpPENH3Lvg==", "cpu": [ "x64" ], @@ -3558,9 +3536,9 @@ ] }, "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", "bin": { @@ -3631,9 +3609,9 @@ "license": "Python-2.0" }, "node_modules/aria-hidden": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", - "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", "license": "MIT", "dependencies": { "tslib": "^2.0.0" @@ -3670,18 +3648,20 @@ } }, "node_modules/array-includes": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", - "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.9.tgz", + "integrity": "sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==", "dev": true, "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", "define-properties": "^1.2.1", - "es-abstract": "^1.23.2", - "es-object-atoms": "^1.0.0", - "get-intrinsic": "^1.2.4", - "is-string": "^1.0.7" + "es-abstract": "^1.24.0", + "es-object-atoms": "^1.1.1", + "get-intrinsic": "^1.3.0", + "is-string": "^1.1.1", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -3898,9 +3878,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -3993,9 +3973,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001715", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001715.tgz", - "integrity": "sha512-7ptkFGMm2OAOgvZpwgA4yjQ5SQbrNVGdRjzH0pBdy1Fasvcr+KAeECmbCAECzTuDuoX0FCY8KzUxjf9+9kfZEw==", + "version": "1.0.30001722", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001722.tgz", + "integrity": "sha512-DCQHBBZtiK6JVkAGw7drvAMK0Q0POD/xZvEmDp6baiMMP6QXXk9HpD6mNYBZWhOPG6LvIDb82ITqtWjhDckHCA==", "funding": [ { "type": "opencollective", @@ -4458,9 +4438,9 @@ } }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -4662,9 +4642,9 @@ } }, "node_modules/entities": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.0.tgz", - "integrity": "sha512-aKstq2TDOndCn4diEyp9Uq/Flu2i1GlLkc6XIDQSDMuaFE3OPW5OphLCyQ5SpSJZTb4reN+kTcYru5yIfXoRPw==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", "license": "BSD-2-Clause", "engines": { "node": ">=0.12" @@ -4674,9 +4654,9 @@ } }, "node_modules/es-abstract": { - "version": "1.23.9", - "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", - "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", "dev": true, "license": "MIT", "dependencies": { @@ -4684,18 +4664,18 @@ "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", - "call-bound": "^1.0.3", + "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", - "es-object-atoms": "^1.0.0", + "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", - "get-intrinsic": "^1.2.7", - "get-proto": "^1.0.0", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", @@ -4707,21 +4687,24 @@ "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", + "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", - "is-weakref": "^1.1.0", + "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", - "object-inspect": "^1.13.3", + "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", - "regexp.prototype.flags": "^1.5.3", + "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", @@ -4730,7 +4713,7 @@ "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", - "which-typed-array": "^1.1.18" + "which-typed-array": "^1.1.19" }, "engines": { "node": ">= 0.4" @@ -4861,9 +4844,9 @@ } }, "node_modules/eslint": { - "version": "9.25.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.25.1.tgz", - "integrity": "sha512-E6Mtz9oGQWDCpV12319d59n4tx9zOTXSTmc8BLVxBx+G/0RdM5MvEEJLU9c0+aleoePYYgVTOsRblx433qmhWQ==", + "version": "9.28.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.28.0.tgz", + "integrity": "sha512-ocgh41VhRlf9+fVpe7QKzwLj9c92fDiqOj8Y3Sd4/ZmVA4Btx4PlUYPq4pp9JDyupkf1upbEXecxL2mwNV7jPQ==", "dev": true, "license": "MIT", "dependencies": { @@ -4871,10 +4854,10 @@ "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.20.0", "@eslint/config-helpers": "^0.2.1", - "@eslint/core": "^0.13.0", + "@eslint/core": "^0.14.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.25.1", - "@eslint/plugin-kit": "^0.2.8", + "@eslint/js": "9.28.0", + "@eslint/plugin-kit": "^0.3.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -5193,9 +5176,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", - "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -5210,9 +5193,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -5223,15 +5206,15 @@ } }, "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -5603,9 +5586,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", - "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.1.tgz", + "integrity": "sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==", "dev": true, "license": "MIT", "dependencies": { @@ -6315,6 +6298,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -6671,9 +6667,9 @@ } }, "node_modules/lightningcss": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz", - "integrity": "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz", + "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==", "dev": true, "license": "MPL-2.0", "dependencies": { @@ -6687,22 +6683,22 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "lightningcss-darwin-arm64": "1.29.2", - "lightningcss-darwin-x64": "1.29.2", - "lightningcss-freebsd-x64": "1.29.2", - "lightningcss-linux-arm-gnueabihf": "1.29.2", - "lightningcss-linux-arm64-gnu": "1.29.2", - "lightningcss-linux-arm64-musl": "1.29.2", - "lightningcss-linux-x64-gnu": "1.29.2", - "lightningcss-linux-x64-musl": "1.29.2", - "lightningcss-win32-arm64-msvc": "1.29.2", - "lightningcss-win32-x64-msvc": "1.29.2" + "lightningcss-darwin-arm64": "1.30.1", + "lightningcss-darwin-x64": "1.30.1", + "lightningcss-freebsd-x64": "1.30.1", + "lightningcss-linux-arm-gnueabihf": "1.30.1", + "lightningcss-linux-arm64-gnu": "1.30.1", + "lightningcss-linux-arm64-musl": "1.30.1", + "lightningcss-linux-x64-gnu": "1.30.1", + "lightningcss-linux-x64-musl": "1.30.1", + "lightningcss-win32-arm64-msvc": "1.30.1", + "lightningcss-win32-x64-msvc": "1.30.1" } }, "node_modules/lightningcss-darwin-arm64": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.2.tgz", - "integrity": "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz", + "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==", "cpu": [ "arm64" ], @@ -6721,9 +6717,9 @@ } }, "node_modules/lightningcss-darwin-x64": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.2.tgz", - "integrity": "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz", + "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==", "cpu": [ "x64" ], @@ -6742,9 +6738,9 @@ } }, "node_modules/lightningcss-freebsd-x64": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.2.tgz", - "integrity": "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz", + "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==", "cpu": [ "x64" ], @@ -6763,9 +6759,9 @@ } }, "node_modules/lightningcss-linux-arm-gnueabihf": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.2.tgz", - "integrity": "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz", + "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==", "cpu": [ "arm" ], @@ -6784,9 +6780,9 @@ } }, "node_modules/lightningcss-linux-arm64-gnu": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.2.tgz", - "integrity": "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz", + "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==", "cpu": [ "arm64" ], @@ -6805,9 +6801,9 @@ } }, "node_modules/lightningcss-linux-arm64-musl": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.2.tgz", - "integrity": "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz", + "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==", "cpu": [ "arm64" ], @@ -6826,9 +6822,9 @@ } }, "node_modules/lightningcss-linux-x64-gnu": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.2.tgz", - "integrity": "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz", + "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==", "cpu": [ "x64" ], @@ -6847,9 +6843,9 @@ } }, "node_modules/lightningcss-linux-x64-musl": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.2.tgz", - "integrity": "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz", + "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==", "cpu": [ "x64" ], @@ -6868,9 +6864,9 @@ } }, "node_modules/lightningcss-win32-arm64-msvc": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.2.tgz", - "integrity": "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz", + "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==", "cpu": [ "arm64" ], @@ -6889,9 +6885,9 @@ } }, "node_modules/lightningcss-win32-x64-msvc": { - "version": "1.29.2", - "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz", - "integrity": "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==", + "version": "1.30.1", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz", + "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==", "cpu": [ "x64" ], @@ -6986,6 +6982,16 @@ "integrity": "sha512-/MWOjEyGZO84B16BeqbeleinbmeYxOBsbYDt/Yk6xGwMYwDm6dx43jKHMxGDqW9U84qWL13dNvVW0SMADhUSyg==", "license": "ISC" }, + "node_modules/magic-string": { + "version": "0.30.17", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", + "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0" + } + }, "node_modules/markdown-table": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz", @@ -8000,9 +8006,9 @@ } }, "node_modules/napi-postinstall": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.1.6.tgz", - "integrity": "sha512-w1bClprmjwpybo+7M1Rd0N4QK5Ein8kH/1CQ0Wv8Q9vrLbDMakxc4rZpv8zYc8RVErUELJlFhM8UzOF3IqlYKw==", + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.2.4.tgz", + "integrity": "sha512-ZEzHJwBhZ8qQSbknHqYcdtQVr8zUgGyM/q6h6qAyhtyVMNrSgDhrC4disf03dYW0e+czXyLnZINnCTEkWy0eJg==", "dev": true, "license": "MIT", "bin": { @@ -8461,9 +8467,9 @@ } }, "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "version": "8.5.4", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.4.tgz", + "integrity": "sha512-QSa9EBe+uwlGTFmHsPKokv3B/oEMQZxfqW0QqNCyhpa6mB1afzulwn8hihglqAb2pOw+BJgNlmXQ8la2VeHB7w==", "dev": true, "funding": [ { @@ -8481,7 +8487,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.8", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -8534,9 +8540,9 @@ } }, "node_modules/property-information": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.0.0.tgz", - "integrity": "sha512-7D/qOz/+Y4X/rzSB6jKxKUsQnphO046ei8qxG59mtM3RG3DHgTK81HrxrmoDVINJb8NKT5ZsRbwHvQ6B68Iyhg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz", + "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", "license": "MIT", "funding": { "type": "github", @@ -8610,9 +8616,9 @@ } }, "node_modules/react-hook-form": { - "version": "7.56.1", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.56.1.tgz", - "integrity": "sha512-qWAVokhSpshhcEuQDSANHx3jiAEFzu2HAaaQIzi/r9FNPm1ioAvuJSD4EuZzWd7Al7nTRKcKPnBKO7sRn+zavQ==", + "version": "7.57.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.57.0.tgz", + "integrity": "sha512-RbEks3+cbvTP84l/VXGUZ+JMrKOS8ykQCRYdm5aYsxnDquL0vspsyNhGRO7pcH6hsZqWlPOjLye7rJqdtdAmlg==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -8659,9 +8665,9 @@ } }, "node_modules/react-remove-scroll": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", - "integrity": "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==", + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz", + "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==", "license": "MIT", "dependencies": { "react-remove-scroll-bar": "^2.3.7", @@ -8706,9 +8712,9 @@ } }, "node_modules/react-resizable-panels": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-2.1.8.tgz", - "integrity": "sha512-oDvD0sw34Ecx00cQFLiRJpAE2fCgNLBr8DMrBzkrsaUiLpAycIQoY3eAWfMblDql3pTIMZ60wJ/P89RO1htM2w==", + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-2.1.9.tgz", + "integrity": "sha512-z77+X08YDIrgAes4jl8xhnUu1LNIRp4+E7cv4xHmLOxxUPO/ML7PSrE813b90vj7xvQ1lcf7g2uA9GeMZonjhQ==", "license": "MIT", "peerDependencies": { "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", @@ -8839,12 +8845,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "license": "MIT" - }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", @@ -9105,9 +9105,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.7.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", - "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "devOptional": true, "license": "ISC", "bin": { @@ -9167,16 +9167,16 @@ } }, "node_modules/sharp": { - "version": "0.34.1", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.1.tgz", - "integrity": "sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==", + "version": "0.34.2", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.2.tgz", + "integrity": "sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==", "hasInstallScript": true, "license": "Apache-2.0", "optional": true, "dependencies": { "color": "^4.2.3", - "detect-libc": "^2.0.3", - "semver": "^7.7.1" + "detect-libc": "^2.0.4", + "semver": "^7.7.2" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" @@ -9185,8 +9185,8 @@ "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.1", - "@img/sharp-darwin-x64": "0.34.1", + "@img/sharp-darwin-arm64": "0.34.2", + "@img/sharp-darwin-x64": "0.34.2", "@img/sharp-libvips-darwin-arm64": "1.1.0", "@img/sharp-libvips-darwin-x64": "1.1.0", "@img/sharp-libvips-linux-arm": "1.1.0", @@ -9196,15 +9196,16 @@ "@img/sharp-libvips-linux-x64": "1.1.0", "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", "@img/sharp-libvips-linuxmusl-x64": "1.1.0", - "@img/sharp-linux-arm": "0.34.1", - "@img/sharp-linux-arm64": "0.34.1", - "@img/sharp-linux-s390x": "0.34.1", - "@img/sharp-linux-x64": "0.34.1", - "@img/sharp-linuxmusl-arm64": "0.34.1", - "@img/sharp-linuxmusl-x64": "0.34.1", - "@img/sharp-wasm32": "0.34.1", - "@img/sharp-win32-ia32": "0.34.1", - "@img/sharp-win32-x64": "0.34.1" + "@img/sharp-linux-arm": "0.34.2", + "@img/sharp-linux-arm64": "0.34.2", + "@img/sharp-linux-s390x": "0.34.2", + "@img/sharp-linux-x64": "0.34.2", + "@img/sharp-linuxmusl-arm64": "0.34.2", + "@img/sharp-linuxmusl-x64": "0.34.2", + "@img/sharp-wasm32": "0.34.2", + "@img/sharp-win32-arm64": "0.34.2", + "@img/sharp-win32-ia32": "0.34.2", + "@img/sharp-win32-x64": "0.34.2" } }, "node_modules/shebang-command": { @@ -9330,9 +9331,9 @@ } }, "node_modules/sonner": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.3.tgz", - "integrity": "sha512-njQ4Hht92m0sMqqHVDL32V2Oun9W1+PHO9NDv9FHfJjT3JT22IG4Jpo3FPQy+mouRKCXFWO+r67v6MrHX2zeIA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.5.tgz", + "integrity": "sha512-YwbHQO6cSso3HBXlbCkgrgzDNIhws14r4MO87Ofy+cV2X7ES4pOoAK3+veSmVTvqNx1BWUxlhPmZzP00Crk2aQ==", "license": "MIT", "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", @@ -9365,6 +9366,20 @@ "dev": true, "license": "MIT" }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/streamsearch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", @@ -9565,9 +9580,9 @@ } }, "node_modules/supabase": { - "version": "2.23.4", - "resolved": "https://registry.npmjs.org/supabase/-/supabase-2.23.4.tgz", - "integrity": "sha512-zuGxvy9qQqZtbc3ehZ2pP6j0r/dcNOW0hU93XPBJ7zSWfLlmQ/erlQHa/o36R4f2zpfR0REVF6023yuNCUdpMA==", + "version": "2.24.3", + "resolved": "https://registry.npmjs.org/supabase/-/supabase-2.24.3.tgz", + "integrity": "sha512-mh9pi4C5pM159GyYE+Rv9qL8kg1kqzimQ4FBr6UH/WhzB/VcDaA+vpn/VeRC3CGVKr+d89Ra5MjDWg+/UoPpXg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -9611,9 +9626,9 @@ } }, "node_modules/tailwind-merge": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.2.0.tgz", - "integrity": "sha512-FQT/OVqCD+7edmmJpsgCsY820RTD5AkBryuG5IUqR5YQZSdj5xlH5nLgH7YPths7WsLPSpSBNneJdM8aS8aeFA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", + "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==", "license": "MIT", "funding": { "type": "github", @@ -9621,15 +9636,15 @@ } }, "node_modules/tailwindcss": { - "version": "4.1.4", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.4.tgz", - "integrity": "sha512-1ZIUqtPITFbv/DxRmDr5/agPqJwF69d24m9qmM1939TJehgY539CtzeZRjbLt5G6fSy/7YqqYsfvoTEw9xUI2A==", + "version": "4.1.8", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.8.tgz", + "integrity": "sha512-kjeW8gjdxasbmFKpVGrGd5T4i40mV5J2Rasw48QARfYeQ8YS9x02ON9SFWax3Qf616rt4Cp3nVNIj6Hd1mP3og==", "license": "MIT" }, "node_modules/tapable": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", - "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz", + "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==", "dev": true, "license": "MIT", "engines": { @@ -9661,9 +9676,9 @@ "license": "MIT" }, "node_modules/tinyglobby": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", - "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", "dev": true, "license": "MIT", "dependencies": { @@ -9678,9 +9693,9 @@ } }, "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", - "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", "dev": true, "license": "MIT", "peerDependencies": { @@ -9778,9 +9793,9 @@ "license": "0BSD" }, "node_modules/tw-animate-css": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.2.8.tgz", - "integrity": "sha512-AxSnYRvyFnAiZCUndS3zQZhNfV/B77ZhJ+O7d3K6wfg/jKJY+yv6ahuyXwnyaYA9UdLqnpCwhTRv9pPTBnPR2g==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.4.tgz", + "integrity": "sha512-dd1Ht6/YQHcNbq0znIT6dG8uhO7Ce+VIIhZUhjsryXsMPJQz3bZg7Q2eNzLwipb25bRZslGb2myio5mScd1TFg==", "dev": true, "license": "MIT", "funding": { @@ -9912,9 +9927,9 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, "node_modules/unified": { @@ -10033,36 +10048,36 @@ } }, "node_modules/unrs-resolver": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.7.0.tgz", - "integrity": "sha512-b76tVoT9KPniDY1GoYghDUQX20gjzXm/TONfHfgayLaiuo+oGyT9CsQkGCEJs+1/uryVBEOGOt3yYWDXbJhL7g==", + "version": "1.7.13", + "resolved": "https://registry.npmjs.org/unrs-resolver/-/unrs-resolver-1.7.13.tgz", + "integrity": "sha512-QUjCYKAgrdJpf3wA73zWjOrO7ra19lfnwQ8HRkNOLah5AVDqOS38UunnyhzsSL8AE+2/AGnAHxlr8cGshCP35A==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "napi-postinstall": "^0.1.6" + "napi-postinstall": "^0.2.2" }, "funding": { - "url": "https://github.com/sponsors/JounQin" + "url": "https://opencollective.com/unrs-resolver" }, "optionalDependencies": { - "@unrs/resolver-binding-darwin-arm64": "1.7.0", - "@unrs/resolver-binding-darwin-x64": "1.7.0", - "@unrs/resolver-binding-freebsd-x64": "1.7.0", - "@unrs/resolver-binding-linux-arm-gnueabihf": "1.7.0", - "@unrs/resolver-binding-linux-arm-musleabihf": "1.7.0", - "@unrs/resolver-binding-linux-arm64-gnu": "1.7.0", - "@unrs/resolver-binding-linux-arm64-musl": "1.7.0", - "@unrs/resolver-binding-linux-ppc64-gnu": "1.7.0", - "@unrs/resolver-binding-linux-riscv64-gnu": "1.7.0", - "@unrs/resolver-binding-linux-riscv64-musl": "1.7.0", - "@unrs/resolver-binding-linux-s390x-gnu": "1.7.0", - "@unrs/resolver-binding-linux-x64-gnu": "1.7.0", - "@unrs/resolver-binding-linux-x64-musl": "1.7.0", - "@unrs/resolver-binding-wasm32-wasi": "1.7.0", - "@unrs/resolver-binding-win32-arm64-msvc": "1.7.0", - "@unrs/resolver-binding-win32-ia32-msvc": "1.7.0", - "@unrs/resolver-binding-win32-x64-msvc": "1.7.0" + "@unrs/resolver-binding-darwin-arm64": "1.7.13", + "@unrs/resolver-binding-darwin-x64": "1.7.13", + "@unrs/resolver-binding-freebsd-x64": "1.7.13", + "@unrs/resolver-binding-linux-arm-gnueabihf": "1.7.13", + "@unrs/resolver-binding-linux-arm-musleabihf": "1.7.13", + "@unrs/resolver-binding-linux-arm64-gnu": "1.7.13", + "@unrs/resolver-binding-linux-arm64-musl": "1.7.13", + "@unrs/resolver-binding-linux-ppc64-gnu": "1.7.13", + "@unrs/resolver-binding-linux-riscv64-gnu": "1.7.13", + "@unrs/resolver-binding-linux-riscv64-musl": "1.7.13", + "@unrs/resolver-binding-linux-s390x-gnu": "1.7.13", + "@unrs/resolver-binding-linux-x64-gnu": "1.7.13", + "@unrs/resolver-binding-linux-x64-musl": "1.7.13", + "@unrs/resolver-binding-wasm32-wasi": "1.7.13", + "@unrs/resolver-binding-win32-arm64-msvc": "1.7.13", + "@unrs/resolver-binding-win32-ia32-msvc": "1.7.13", + "@unrs/resolver-binding-win32-x64-msvc": "1.7.13" } }, "node_modules/uri-js": { @@ -10423,9 +10438,9 @@ } }, "node_modules/zod": { - "version": "3.24.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", - "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", + "version": "3.25.61", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.61.tgz", + "integrity": "sha512-fzfJgUw78LTNnHujj9re1Ov/JJQkRZZGDMcYqSx7Hp4rPOkKywaFHq0S6GoHeXs0wGNE/sIOutkXgnwzrVOGCQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index 4c33d51..ccbe937 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,8 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev --turbopack", + "dev": "next dev", + "devt": "next dev --turbopack", "build": "next build", "start": "next start", "prod": "next build && next start", diff --git a/src/components/AppSidebar.tsx b/src/components/AppSidebar.tsx index f51c2ef..5246a16 100644 --- a/src/components/AppSidebar.tsx +++ b/src/components/AppSidebar.tsx @@ -1,3 +1,5 @@ +"use client"; + import { Sidebar, SidebarContent, @@ -11,7 +13,7 @@ import { SidebarMenuItem, } from "@/components/ui/sidebar"; -import { House, Plus } from "lucide-react"; +import { ChevronUp, House, Plus, User2 } from "lucide-react"; import Link from "next/link"; import { DropdownMenu, @@ -19,10 +21,18 @@ import { DropdownMenuItem, DropdownMenuTrigger, } from "./ui/dropdown-menu"; -import SideBarCollectionGroup from "./SideBarCollectionGroup"; -import SidebarUserItem from "./SidebarUserItem"; +import { useSessionData } from "@/providers/session-data-provider"; +import CollectionSidebarItem from "./CollectionSidebarItem"; +import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar"; export default function AppSidebar() { + const { + collections, + subCollections, + premadeCollections, + premadeSubCollections, + user, + } = useSessionData(); return ( @@ -53,15 +63,77 @@ export default function AppSidebar() { - - + + {/* User created sidebar group */} + {collections && collections.length >= 1 && ( + + Your Collections + + + {collections.map((collection) => { + const subCollectionsForCollection = subCollections + ? subCollections.filter( + (sub_collection) => + sub_collection.collection_id == collection.id + ) + : []; + // console.log(collection, subCollectionsForCollection); + return ( + + ); + })} + + + + )} + + {/* Premade sidebar group */} + {premadeCollections && premadeCollections.length >= 1 && ( + + Premade Collections + + + {premadeCollections.map((collection) => { + const subCollectionsForCollection = premadeSubCollections + ? premadeSubCollections.filter( + (sub_collection) => + sub_collection.collection_id == collection.id + ) + : []; + return ( + + ); + })} + + + + )} + + {/* SIDEBAR FOOTER */} - + + + + + + + + {user?.user_metadata.full_name} + +
- +

{collection.name}

diff --git a/src/components/CollectionSidebarItem.tsx b/src/components/CollectionSidebarItem.tsx index 90615c8..5d5ce8c 100644 --- a/src/components/CollectionSidebarItem.tsx +++ b/src/components/CollectionSidebarItem.tsx @@ -1,3 +1,5 @@ +"use client"; + import Link from "next/link"; import { Collapsible, @@ -15,7 +17,7 @@ import { CollectionType } from "@/supabase/db/collection"; import { SubCollectionType } from "@/supabase/db/subCollection"; import Icon from "./Icon"; -const CollectionSidebarItem = async ({ +const CollectionSidebarItem = ({ collection, sub_collections, }: { @@ -38,11 +40,9 @@ const CollectionSidebarItem = async ({ - -
- - {collection.name} -
+ + + {collection.name}
diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index 49061d4..dc5189b 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -1,6 +1,9 @@ -import dynamic from "next/dynamic"; -import { LucideProps } from "lucide-react"; +"use client"; + +import { useState, useEffect, memo } from "react"; +import { LucideProps, Circle } from "lucide-react"; // Import a default fallback icon import dynamicIconImports from "lucide-react/dynamicIconImports"; +import { Skeleton } from "./ui/skeleton"; const pascalToKebabCase = (str: string): string => { if (!str) return ""; @@ -16,29 +19,52 @@ interface CustomIconProps extends Omit { } const Icon = ({ iconName, className, ...props }: CustomIconProps) => { - const kebabCaseName = pascalToKebabCase( - iconName - ) as keyof typeof dynamicIconImports; - - const fallbackIconKey = "circle" as keyof typeof dynamicIconImports; - - let TargetIcon; - - if (iconName && dynamicIconImports[kebabCaseName]) { - // console.log(`icon "${kebabCaseName}" found`); - TargetIcon = dynamic(dynamicIconImports[kebabCaseName]); - } else { - // Log a warning if a specific iconName was provided but not found (and it's not already the fallback) - if (iconName && iconName.toLowerCase() !== "circle") { - console.warn( - `Icon "${iconName}" (attempted as "${kebabCaseName}") not found. Falling back to "circle" icon.` - ); + const [LoadedIcon, setLoadedIcon] = + useState | null>(null); + + useEffect(() => { + const kebabCaseName = pascalToKebabCase( + iconName + ) as keyof typeof dynamicIconImports; + const importer = dynamicIconImports[kebabCaseName]; + + if (importer) { + importer() + .then((mod) => { + // The dynamically imported module has a 'default' export which is the icon component + setLoadedIcon(() => mod.default); + }) + .catch((err) => { + // Handle potential import errors + console.error(`Failed to load icon: ${iconName}`, err); + setLoadedIcon(() => Circle); // Fallback on error + }); + } else { + // If the icon name is not found in the dynamic imports map + if (iconName && iconName.toLowerCase() !== "circle") { + console.warn( + `Icon "${iconName}" not found. Falling back to "circle" icon.` + ); + } + setLoadedIcon(() => Circle); // Set fallback immediately } - TargetIcon = dynamic(dynamicIconImports[fallbackIconKey]); + }, [iconName]); // 2. Re-run this effect only when the iconName prop changes + + // 3. Render a fallback while loading or if it fails, ensuring no server/client mismatch. + // On initial render, LoadedIcon is null, so this returns a placeholder. + // This prevents hydration errors. + if (!LoadedIcon) { + // You can return null or a placeholder element. A placeholder is often better for layout stability. + return ( + + ); } - // Apply the provided className or the default "size-20" - return ; + return ; }; -export default Icon; +// Use memo to prevent re-renders if props haven't changed +export default memo(Icon); diff --git a/src/components/SideBarCollectionGroup.tsx b/src/components/SideBarCollectionGroup.tsx deleted file mode 100644 index 530556c..0000000 --- a/src/components/SideBarCollectionGroup.tsx +++ /dev/null @@ -1,78 +0,0 @@ -"use client"; - -import { - SidebarGroup, - SidebarGroupContent, - SidebarGroupLabel, - SidebarMenu, -} from "./ui/sidebar"; -import CollectionSidebarItem from "./CollectionSidebarItem"; -import { useSessionData } from "@/providers/session-data-provider"; - -const SideBarCollectionGroup = ({ - type, -}: { - type: "user-created" | "premade"; -}) => { - const { - collections, - subCollections, - premadeCollections, - premadeSubCollections, - } = useSessionData(); - return ( - <> - {type === "user-created" && collections && collections.length >= 1 && ( - - Your Collections - - - {collections.map((collection) => { - const subCollectionsForCollection = subCollections - ? subCollections.filter( - (sub_collection) => - sub_collection.collection_id == collection.id - ) - : []; - return ( - - ); - })} - - - - )} - {type === "premade" && - premadeCollections && - premadeCollections.length >= 1 && ( - - Premade Collections - - - {premadeCollections.map((collection) => { - const subCollectionsForCollection = premadeSubCollections - ? premadeSubCollections.filter( - (sub_collection) => - sub_collection.collection_id == collection.id - ) - : []; - return ( - - ); - })} - - - - )} - - ); -}; -export default SideBarCollectionGroup; diff --git a/src/components/SidebarUserItem.tsx b/src/components/SidebarUserItem.tsx deleted file mode 100644 index 48c41ce..0000000 --- a/src/components/SidebarUserItem.tsx +++ /dev/null @@ -1,23 +0,0 @@ -"use client"; - -import { ChevronUp, User2 } from "lucide-react"; -import { Avatar, AvatarFallback, AvatarImage } from "./ui/avatar"; -import { SidebarMenuButton } from "./ui/sidebar"; -import { useSessionData } from "@/providers/session-data-provider"; - -const SidebarUserItem = () => { - const { user } = useSessionData(); - return ( - - - - - - - - {user?.user_metadata.full_name} - - - ); -}; -export default SidebarUserItem; diff --git a/src/lib/data.ts b/src/lib/data.ts index d13f9d1..73a2367 100644 --- a/src/lib/data.ts +++ b/src/lib/data.ts @@ -27,6 +27,16 @@ export async function getUserData(): Promise { getPremadeSubCollections(), ]); + // TODO: Make it not break when one of these is false + console.log( + user && "yes", + collections && "yes", + subCollections && "yes", + notes && "yes", + premadeCollections && "yes", + premadeSubCollections && "yes" + ); + if ( user && collections && diff --git a/src/supabase/db/notes.ts b/src/supabase/db/notes.ts index d3eedf5..806594c 100644 --- a/src/supabase/db/notes.ts +++ b/src/supabase/db/notes.ts @@ -13,6 +13,7 @@ export interface NoteType { symbol: string; explanation: string; }>; + user_id: string; } export async function getNoteById(id: string): Promise { diff --git a/src/supabase/schema.ts b/src/supabase/schema.ts index e0332c4..9d7b138 100644 --- a/src/supabase/schema.ts +++ b/src/supabase/schema.ts @@ -41,6 +41,7 @@ export type Database = { sub_collection_id: number; symbols: Json; theory: string; + user_id: string | null; }; Insert: { id?: number; @@ -49,6 +50,7 @@ export type Database = { sub_collection_id: number; symbols?: Json; theory: string; + user_id?: string | null; }; Update: { id?: number; @@ -57,6 +59,7 @@ export type Database = { sub_collection_id?: number; symbols?: Json; theory?: string; + user_id?: string | null; }; Relationships: [ { From 1385ae1013850fc26ac29ed11143ffb2e7c6ca3f Mon Sep 17 00:00:00 2001 From: Visar Date: Wed, 11 Jun 2025 18:32:23 +0200 Subject: [PATCH 35/49] Refactor CardList to use session data and remove async loading. Simplified page structure by eliminating Suspense and LoadingCards. --- src/app/home/MainCardList.tsx | 14 +++++--------- src/app/home/page.tsx | 7 +------ 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/src/app/home/MainCardList.tsx b/src/app/home/MainCardList.tsx index 69204c1..86f831f 100644 --- a/src/app/home/MainCardList.tsx +++ b/src/app/home/MainCardList.tsx @@ -1,15 +1,11 @@ -import { - getCollectionsForUserId, - getPremadeCollections, -} from "@/supabase/db/collection"; +"use client"; + import CollectionCard from "../../components/CollectionCard"; import { cn } from "@/lib/utils"; +import { useSessionData } from "@/providers/session-data-provider"; -const CardList = async () => { - const [premadeCollections, userCollections] = await Promise.all([ - getPremadeCollections(), - getCollectionsForUserId(), - ]); +const CardList = () => { + const { premadeCollections, collections: userCollections } = useSessionData(); const areThereAnyUserCollections = userCollections && userCollections.length > 0; diff --git a/src/app/home/page.tsx b/src/app/home/page.tsx index 41f8dda..61f01d6 100644 --- a/src/app/home/page.tsx +++ b/src/app/home/page.tsx @@ -1,7 +1,5 @@ import SearchBar from "@/components/SearchBar"; -import { Suspense } from "react"; import CardList from "@/app/home/MainCardList"; -import LoadingCards from "@/components/LoadingCards"; const Page = async ({ searchParams, @@ -24,10 +22,7 @@ const Page = async ({ {/* Content Page */} - - }> - - +
); }; From a8fc2857ed553aad2d69d2a404079655c2a4c6ee Mon Sep 17 00:00:00 2001 From: Visar Date: Thu, 12 Jun 2025 11:31:30 +0200 Subject: [PATCH 36/49] changed schema --- src/supabase/schema.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/supabase/schema.ts b/src/supabase/schema.ts index 9d7b138..fda1a89 100644 --- a/src/supabase/schema.ts +++ b/src/supabase/schema.ts @@ -108,7 +108,17 @@ export type Database = { [_ in never]: never; }; Functions: { - [_ in never]: never; + get_user_and_collections: { + Args: { user_id: number }; + Returns: { + user_data: Json; + collections: Json; + sub_collections: Json; + notes: Json; + premade_collections: Json; + premade_sub_collections: Json; + }[]; + }; }; Enums: { [_ in never]: never; From 61ffec1a5c601eb6acc8e6f99f8964c713043b1c Mon Sep 17 00:00:00 2001 From: Visar Date: Thu, 12 Jun 2025 12:17:34 +0200 Subject: [PATCH 37/49] Some refactoring to how getAllUserData works, and how the collection page works, making it fetch once from the db to validate if the collection is correct --- src/app/home/MainCardList.tsx | 2 - src/app/home/[collectionId]/page.tsx | 13 ++---- src/components/CollectionCard.tsx | 8 +--- src/components/CollectionCardOptions.tsx | 3 -- src/components/ParentCollectionSelector.tsx | 5 ++- src/lib/data.ts | 45 ++++++--------------- src/supabase/db/user.ts | 15 +++++++ src/supabase/schema.ts | 15 +++---- 8 files changed, 43 insertions(+), 63 deletions(-) diff --git a/src/app/home/MainCardList.tsx b/src/app/home/MainCardList.tsx index 86f831f..87c6f74 100644 --- a/src/app/home/MainCardList.tsx +++ b/src/app/home/MainCardList.tsx @@ -29,7 +29,6 @@ const CardList = () => { collection={collection} isPremade={false} href={`/home/${collection.id}`} - allUserCollections={userCollections} /> ); })} @@ -57,7 +56,6 @@ const CardList = () => { collection={collection} isPremade={true} href={`/home/${collection.id}`} - allUserCollections={userCollections} /> ); })} diff --git a/src/app/home/[collectionId]/page.tsx b/src/app/home/[collectionId]/page.tsx index 127de59..cd1d1f2 100644 --- a/src/app/home/[collectionId]/page.tsx +++ b/src/app/home/[collectionId]/page.tsx @@ -1,10 +1,7 @@ -import { redirect } from "next/navigation"; +import { notFound } from "next/navigation"; import CollectionCard from "@/components/CollectionCard"; -import { - getCollectionById, - getCollectionsForUserId, -} from "@/supabase/db/collection"; +import { getCollectionById } from "@/supabase/db/collection"; import { getSubCollectionsByCollectionId } from "@/supabase/db/subCollection"; import CollectionTitle from "@/components/CollectionTitle"; @@ -16,13 +13,12 @@ export default async function Page({ const { collectionId } = await params; // Fetch collection and sub_collections in parallel - const [allUserCollections, collection, sub_collections] = await Promise.all([ - getCollectionsForUserId(), + const [collection, sub_collections] = await Promise.all([ getCollectionById(collectionId), getSubCollectionsByCollectionId(collectionId), ]); - if (!collection) redirect("/not-found"); + if (!collection) notFound(); return (
@@ -38,7 +34,6 @@ export default async function Page({ isPremade={collection.user_id === null} parentId={collection.id} href={`/home/${collection.id}/${subC.id}`} - allUserCollections={allUserCollections} /> ); })} diff --git a/src/components/CollectionCard.tsx b/src/components/CollectionCard.tsx index c23f22a..ca0a458 100644 --- a/src/components/CollectionCard.tsx +++ b/src/components/CollectionCard.tsx @@ -9,13 +9,11 @@ const CollectionCard = ({ collection, parentId, isPremade, - allUserCollections, href, }: { collection: CollectionType | SubCollectionType; parentId?: number; isPremade: boolean; - allUserCollections: CollectionType[] | null; href: string; }) => { return ( @@ -33,11 +31,7 @@ const CollectionCard = ({ {!isPremade && ( - + )}
); diff --git a/src/components/CollectionCardOptions.tsx b/src/components/CollectionCardOptions.tsx index 7812c03..6e90226 100644 --- a/src/components/CollectionCardOptions.tsx +++ b/src/components/CollectionCardOptions.tsx @@ -38,11 +38,9 @@ import ParentCollectionSelector from "./ParentCollectionSelector"; const CollectionCardOptions = ({ collection, parentId, - allUserCollections, }: { collection: CollectionType | SubCollectionType; parentId?: number; - allUserCollections: CollectionType[] | null; }) => { const [open, setOpen] = useState(false); const [isEditDialogOpen, setIsEditDialogOpen] = useState(false); @@ -176,7 +174,6 @@ const CollectionCardOptions = ({
diff --git a/src/components/ParentCollectionSelector.tsx b/src/components/ParentCollectionSelector.tsx index 72a0caa..149f123 100644 --- a/src/components/ParentCollectionSelector.tsx +++ b/src/components/ParentCollectionSelector.tsx @@ -14,16 +14,17 @@ import { } from "./ui/select"; import { Dispatch, SetStateAction } from "react"; import { X } from "lucide-react"; +import { useSessionData } from "@/providers/session-data-provider"; const ParentCollectionSelector = ({ parentCollection, setParentCollection, - collections, }: { parentCollection: CollectionType | undefined; setParentCollection: Dispatch>; - collections: CollectionType[] | null; }) => { + const { collections } = useSessionData(); + return (
diff --git a/src/lib/data.ts b/src/lib/data.ts index 73a2367..d811ab5 100644 --- a/src/lib/data.ts +++ b/src/lib/data.ts @@ -1,35 +1,21 @@ import { SessionDataContextType } from "@/providers/session-data-provider"; -import { - getCollectionsForUserId, - getPremadeCollections, -} from "@/supabase/db/collection"; -import { getNotesForUserId } from "@/supabase/db/notes"; -import { - getPremadeSubCollections, - getSubCollectionsForUserId, -} from "@/supabase/db/subCollection"; -import { getUser } from "@/supabase/db/user"; +import { getAllUserData, getUser } from "@/supabase/db/user"; export async function getUserData(): Promise { - const [ - user, + const [data, userData] = await Promise.all([getAllUserData(), getUser()]); + if (!data) return null; + + const { collections, - subCollections, + sub_collections: subCollections, + premade_collections: premadeCollections, + premade_sub_collections: premadeSubCollections, notes, - premadeCollections, - premadeSubCollections, - ] = await Promise.all([ - getUser(), - getCollectionsForUserId(), - getSubCollectionsForUserId(), - getNotesForUserId(), - getPremadeCollections(), - getPremadeSubCollections(), - ]); + } = data; // TODO: Make it not break when one of these is false console.log( - user && "yes", + userData && "yes", collections && "yes", subCollections && "yes", notes && "yes", @@ -37,16 +23,9 @@ export async function getUserData(): Promise { premadeSubCollections && "yes" ); - if ( - user && - collections && - subCollections && - notes && - premadeCollections && - premadeSubCollections - ) { + if (userData && data) { return { - user, + user: userData, collections, subCollections, premadeCollections, diff --git a/src/supabase/db/user.ts b/src/supabase/db/user.ts index fd2ca34..e7e3bb8 100644 --- a/src/supabase/db/user.ts +++ b/src/supabase/db/user.ts @@ -9,3 +9,18 @@ export async function getUser() { return user; } + +export async function getAllUserData() { + const supabase = await createClient(); + const user = await getUser(); + + if (!user) return null; + + const { data, error } = await supabase.rpc("get_user_and_collections"); + + if (error) { + console.error("RPC error:", error.message); + return null; + } + return data?.[0]; // <- safely extract the single result row +} diff --git a/src/supabase/schema.ts b/src/supabase/schema.ts index fda1a89..e9a0f71 100644 --- a/src/supabase/schema.ts +++ b/src/supabase/schema.ts @@ -1,3 +1,5 @@ +import { NoteType } from "./db/notes"; + export type Json = | string | number @@ -109,14 +111,13 @@ export type Database = { }; Functions: { get_user_and_collections: { - Args: { user_id: number }; + Args: Record; Returns: { - user_data: Json; - collections: Json; - sub_collections: Json; - notes: Json; - premade_collections: Json; - premade_sub_collections: Json; + collections: Database["public"]["Tables"]["collections"]["Row"][]; + sub_collections: Database["public"]["Tables"]["sub_collections"]["Row"][]; + notes: NoteType[]; + premade_collections: Database["public"]["Tables"]["collections"]["Row"][]; + premade_sub_collections: Database["public"]["Tables"]["sub_collections"]["Row"][]; }[]; }; }; From 7bde0d76570b40e55ed46daf98718c7610a6f5ce Mon Sep 17 00:00:00 2001 From: Visar Date: Thu, 12 Jun 2025 12:18:09 +0200 Subject: [PATCH 38/49] readme Update --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 860b382..7ba3968 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ ## TODO NEXT -- [ ] Add the option to delete subCollections and Collections +- [x] Add the option to delete subCollections and Collections - [ ] Add note creation - [ ] Add server side validation -- [ ] Persist Database Calls between browser sessions \ No newline at end of file +- [x] Persist Database Calls between browser sessions \ No newline at end of file From f1c20b64ac9c111fe6c6c44cd7e01825573e36e8 Mon Sep 17 00:00:00 2001 From: Visar Date: Thu, 12 Jun 2025 12:20:38 +0200 Subject: [PATCH 39/49] Updated create page to not fetch user collections --- src/app/home/create/CreateCollectionForm.tsx | 7 +------ src/app/home/create/page.tsx | 7 ++----- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/src/app/home/create/CreateCollectionForm.tsx b/src/app/home/create/CreateCollectionForm.tsx index 98ca33e..41aac79 100644 --- a/src/app/home/create/CreateCollectionForm.tsx +++ b/src/app/home/create/CreateCollectionForm.tsx @@ -16,11 +16,7 @@ import { createCollectionServerAction } from "./action"; import { useRouter } from "next/navigation"; import ParentCollectionSelector from "@/components/ParentCollectionSelector"; -const CreateCollectionForm = ({ - collections, -}: { - collections: CollectionType[] | null; -}) => { +const CreateCollectionForm = () => { // TODO: Show the errors to the user const [errors, setErrors] = useState({}); const [parentCollection, setParentCollection] = useState< @@ -105,7 +101,6 @@ const CreateCollectionForm = ({
diff --git a/src/app/home/create/page.tsx b/src/app/home/create/page.tsx index 5e0dc73..72f7186 100644 --- a/src/app/home/create/page.tsx +++ b/src/app/home/create/page.tsx @@ -1,9 +1,6 @@ -import { getCollectionsForUserId } from "@/supabase/db/collection"; import CreateCollectionForm from "./CreateCollectionForm"; -const Page = async () => { - const collections = await getCollectionsForUserId(); - +const Page = () => { return (
@@ -11,7 +8,7 @@ const Page = async () => { Create a Collection
- +
); }; From aa0c6c765ef7aaa195ed5eddb7e61aab4428f634 Mon Sep 17 00:00:00 2001 From: Visar Date: Thu, 12 Jun 2025 12:57:21 +0200 Subject: [PATCH 40/49] Implement collection ownership check when creating subcollections and add form validation in server actions --- src/app/home/create/action.ts | 54 ++++++++++++++++++++++++++--------- src/supabase/db/user.ts | 21 ++++++++++++++ 2 files changed, 62 insertions(+), 13 deletions(-) diff --git a/src/app/home/create/action.ts b/src/app/home/create/action.ts index 00c7190..361e580 100644 --- a/src/app/home/create/action.ts +++ b/src/app/home/create/action.ts @@ -8,6 +8,8 @@ import { } from "@/supabase/db/subCollection"; import { parseServerActionResponse } from "@/lib/utils"; import { revalidatePath } from "next/cache"; +import { formCollectionSchema } from "@/lib/validation"; +import { doesUserOwnThisCollection } from "@/supabase/db/user"; interface FormValues { collectionId?: number | undefined; @@ -22,10 +24,26 @@ export interface rType { status: "SUCCESS" | "ERROR"; } -// TODO: Make it so when creating a subcollection, a check is made to ensure the collectionId is one that the user created export async function createCollectionServerAction( formValues: FormValues ): Promise { + // Validate the form values on the server for security and consistency + const validation = formCollectionSchema.safeParse(formValues); + if (!validation.success) { + console.error("Validation Error:", validation.error); + return parseServerActionResponse({ + error: "Invalid data provided.", + data: "", + status: "ERROR", + }); + } + + // Create a Supabase client instance and get the current user + const supabase = await createClient(); + const { + data: { user }, + } = await supabase.auth.getUser(); + // If the parent Collection was not specified it creates a collection if (!formValues.collectionId) { const row: CollectionInsertType = { @@ -35,19 +53,16 @@ export async function createCollectionServerAction( }; console.log(row); - const supabase = await createClient(); - const { - data: { user }, - } = await supabase.auth.getUser(); - const { data, error } = await supabase .from("collections") .insert({ ...row, user_id: user?.id }) - .select(); + .select() + .single(); if (error) { + console.error(error); return parseServerActionResponse({ - error: error, + error: "An unexpected error occurred", data: "", status: "ERROR", }); @@ -56,10 +71,12 @@ export async function createCollectionServerAction( revalidatePath("/home", "layout"); return parseServerActionResponse({ error: "", - data: data[0], + data: data, status: "SUCCESS", }); } + + // If the parent Collection was specified it creates a subcollection const row: SubCollectionInsertType = { name: formValues.name, icon: formValues.icon, @@ -67,15 +84,26 @@ export async function createCollectionServerAction( }; console.log(row); - const supabase = await createClient(); + const hasCollection = await doesUserOwnThisCollection(row.collection_id); + if (!hasCollection) { + return parseServerActionResponse({ + error: + "You do not have permission to create a subcollection in this collection", + data: "", + status: "ERROR", + }); + } + const { data, error } = await supabase .from("sub_collections") .insert(row) - .select(); + .select() + .single(); if (error) { + console.error(error); return parseServerActionResponse({ - error: error, + error: "An unexpected Error Occurred", data: "", status: "ERROR", }); @@ -84,7 +112,7 @@ export async function createCollectionServerAction( return parseServerActionResponse({ error: "", - data: data[0], + data: data, status: "SUCCESS", }); } diff --git a/src/supabase/db/user.ts b/src/supabase/db/user.ts index e7e3bb8..c6344cf 100644 --- a/src/supabase/db/user.ts +++ b/src/supabase/db/user.ts @@ -24,3 +24,24 @@ export async function getAllUserData() { } return data?.[0]; // <- safely extract the single result row } + +export async function doesUserOwnThisCollection(collectionId: number) { + const user = await getUser(); + if (!user) { + return false; + } + + const supabase = await createClient(); + const { data: collection, error: checkError } = await supabase + .from("collections") + .select("id") // We only need to know if it exists, so just select 'id' + .eq("id", collectionId) + .eq("user_id", user.id) + .maybeSingle(); // Returns one record or null, but not an error if not found + + if (checkError || !collection) { + // If there's an error OR if no collection was found, the user is not authorized + return false; + } + return true; // The user owns the collection +} From 529cd6cdbe441b617f911215bae641e19397329b Mon Sep 17 00:00:00 2001 From: Visar Date: Sat, 14 Jun 2025 14:31:08 +0200 Subject: [PATCH 41/49] Updated collection title to include validation in the server --- .gitignore | 1 + src/components/CollectionTitle.tsx | 16 +----- src/lib/actions.ts | 78 +++++++++++++++++++++++------- src/lib/validation.ts | 22 ++++++++- 4 files changed, 84 insertions(+), 33 deletions(-) diff --git a/.gitignore b/.gitignore index 5ef6a52..167061f 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ # testing /coverage +/tests # next.js /.next/ diff --git a/src/components/CollectionTitle.tsx b/src/components/CollectionTitle.tsx index b57928d..b8f067d 100644 --- a/src/components/CollectionTitle.tsx +++ b/src/components/CollectionTitle.tsx @@ -8,20 +8,8 @@ import { z } from "zod"; import { updateCollectionNameAction } from "@/lib/actions"; import { SubCollectionType } from "@/supabase/db/subCollection"; -interface FormErrors { - name?: string[]; - parentId?: string[]; - collectionId?: string[]; -} - -const nameEditSchema = z.object({ - name: z - .string() - .min(3, { message: "Name must be at least 3 characters long" }) - .max(32, { message: "Name must be at most 32 characters long" }), - collectionId: z.number(), - parentId: z.number().optional(), -}); +import { nameEditSchema } from "@/lib/validation"; +import { FormCollectionNameEditErrors as FormErrors } from "@/lib/validation"; const CollectionTitle = ({ collection, diff --git a/src/lib/actions.ts b/src/lib/actions.ts index c6b1b9d..8b20a0a 100644 --- a/src/lib/actions.ts +++ b/src/lib/actions.ts @@ -2,6 +2,8 @@ import { createClient } from "@/supabase/server"; import { revalidatePath } from "next/cache"; +import { deleteCollectionSchema, nameEditSchema } from "./validation"; +import { parseServerActionResponse } from "./utils"; interface formValuesType { name: string; @@ -20,6 +22,17 @@ export const updateCollectionNameAction = async ( ): Promise => { const supabase = await createClient(); + // Validate the form values on the server for security and consistency + const validation = nameEditSchema.safeParse(formValues); + if (!validation.success) { + console.error("Validation Error:", validation.error); + return parseServerActionResponse({ + error: "Invalid data provided.", + data: "", + status: "ERROR", + }); + } + if (formValues.parentId) { const { error } = await supabase .from("sub_collections") @@ -27,15 +40,19 @@ export const updateCollectionNameAction = async ( .eq("id", formValues.collectionId); if (error) { - return { error: error.message, data: "", status: "ERROR" }; + return parseServerActionResponse({ + error: "Failed to update subcollection", + data: "", + status: "ERROR", + }); } - revalidatePath(`/home/${formValues.collectionId}/${formValues.parentId}`); - return { + revalidatePath("/home", "layout"); + return parseServerActionResponse({ error: "", data: "Subcollection updated successfully", status: "SUCCESS", - }; + }); } const { error } = await supabase @@ -44,22 +61,39 @@ export const updateCollectionNameAction = async ( .eq("id", formValues.collectionId); if (error) { - return { error: error.message, data: "", status: "ERROR" }; + return parseServerActionResponse({ + error: "Failed to update collection", + data: "", + status: "ERROR", + }); } - revalidatePath(`/home/${formValues.collectionId}`); - return { + revalidatePath("/home", "layout"); + return parseServerActionResponse({ error: "", data: "Collection updated successfully", status: "SUCCESS", - }; + }); }; +// Function to delete a collection or subcollection export const deleteCollectionAction = async ( collectionId: number, parentId?: number ): Promise => { - "use server"; + const validation = deleteCollectionSchema.safeParse({ + collectionId, + parentId, + }); + if (!validation.success) { + console.error("Validation Error:", validation.error); + return parseServerActionResponse({ + error: "Invalid data provided.", + data: "", + status: "ERROR", + }); + } + const supabase = await createClient(); if (parentId) { const { error } = await supabase @@ -68,15 +102,19 @@ export const deleteCollectionAction = async ( .eq("id", collectionId); if (error) { - return { error: error.message, data: "", status: "ERROR" }; + return parseServerActionResponse({ + error: "Failed to delete subcollection", + data: "", + status: "ERROR", + }); } - revalidatePath(`/home/${collectionId}`); - return { + revalidatePath("/home", "layout"); + return parseServerActionResponse({ error: "", data: "Subcollection deleted successfully", status: "SUCCESS", - }; + }); } const { error } = await supabase @@ -85,13 +123,17 @@ export const deleteCollectionAction = async ( .eq("id", collectionId); if (error) { - return { error: error.message, data: "", status: "ERROR" }; + return parseServerActionResponse({ + error: "Failed to delete collection", + data: "", + status: "ERROR", + }); } - revalidatePath("/home"); - revalidatePath(`/home/${collectionId}`); - return { + + revalidatePath("/home", "layout"); + return parseServerActionResponse({ error: "", data: "Collection deleted successfully", status: "SUCCESS", - }; + }); }; diff --git a/src/lib/validation.ts b/src/lib/validation.ts index 11d83b2..c91160b 100644 --- a/src/lib/validation.ts +++ b/src/lib/validation.ts @@ -12,7 +12,7 @@ export interface FormCollectionFieldErrors { } export const formCollectionSchema = z .object({ - collectionId: z.number().optional(), + collectionId: z.number().int().positive().optional(), name: z .string() .min(3, { message: "Name must be at least 3 characters long" }) @@ -38,3 +38,23 @@ export const formCollectionSchema = z path: ["subject"], } ); + +export interface FormCollectionNameEditErrors { + name?: string[]; + parentId?: string[]; + collectionId?: string[]; +} + +export const nameEditSchema = z.object({ + name: z + .string() + .min(3, { message: "Name must be at least 3 characters long" }) + .max(32, { message: "Name must be at most 32 characters long" }), + collectionId: z.number().int().positive(), + parentId: z.number().int().positive().optional(), +}); + +export const deleteCollectionSchema = z.object({ + collectionId: z.number().int().positive(), + parentId: z.number().int().positive().optional(), +}); From 2bbfcc33ea4a7306c12bd4ee0212b819b8ac3c11 Mon Sep 17 00:00:00 2001 From: Visar Date: Sat, 14 Jun 2025 22:54:32 +0200 Subject: [PATCH 42/49] Update README with new features and remove unused effect in CollectionCardOptions component --- README.md | 5 +++-- src/components/CollectionCardOptions.tsx | 13 +------------ 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 7ba3968..2ddafe6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ ## TODO NEXT - [x] Add the option to delete subCollections and Collections - [ ] Add note creation -- [ ] Add server side validation -- [x] Persist Database Calls between browser sessions \ No newline at end of file +- [x] Add server side validation +- [x] Persist Database Calls between browser sessions +- [ ] On edit, allow collection switching only between the same rank. Meaning you shouldnt be able to move a collection inside another collection, or move a subCollection as a collection. \ No newline at end of file diff --git a/src/components/CollectionCardOptions.tsx b/src/components/CollectionCardOptions.tsx index 6e90226..63ac126 100644 --- a/src/components/CollectionCardOptions.tsx +++ b/src/components/CollectionCardOptions.tsx @@ -24,7 +24,7 @@ import { DialogHeader, DialogTitle, } from "@/components/ui/dialog"; -import { useActionState, useEffect, useState } from "react"; +import { useActionState, useState } from "react"; import { cn } from "@/lib/utils"; import { deleteCollectionAction } from "@/lib/actions"; import { Button } from "./ui/button"; @@ -73,17 +73,6 @@ const CollectionCardOptions = ({ status: "INITIAL", }); - useEffect(() => { - let timer: NodeJS.Timeout | undefined; - if (!isPending && state.status === "SUCCESS") { - timer = setTimeout(() => { - console.log("Timeout finished: Closing dialog."); - setIsDeleteDialogOpen(false); - }, 500); - } - return () => clearTimeout(timer); - }, [isPending, state.status]); - return (
Date: Mon, 16 Jun 2025 00:51:25 +0200 Subject: [PATCH 43/49] Add edit collection functionality. --- package-lock.json | 20 +--- package.json | 1 + src/components/CollectionCardOptions.tsx | 128 ++++++++++++++++++----- src/lib/actions.ts | 78 +++++++++++++- src/lib/validation.ts | 15 +++ 5 files changed, 201 insertions(+), 41 deletions(-) diff --git a/package-lock.json b/package-lock.json index 16b9313..62f478b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -36,6 +36,7 @@ "@radix-ui/react-toggle-group": "^1.1.7", "@radix-ui/react-tooltip": "^1.2.4", "@supabase/ssr": "^0.6.1", + "@supabase/supabase-js": "^2.50.0", "@tailwindcss/typography": "^0.5.16", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -2414,7 +2415,6 @@ "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.70.0.tgz", "integrity": "sha512-BaAK/tOAZFJtzF1sE3gJ2FwTjLf4ky3PSvcvLGEgEmO4BSBkwWKu8l67rLLIBZPDnCyV7Owk2uPyKHa0kj5QGg==", "license": "MIT", - "peer": true, "dependencies": { "@supabase/node-fetch": "^2.6.14" } @@ -2424,7 +2424,6 @@ "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.4.tgz", "integrity": "sha512-WL2p6r4AXNGwop7iwvul2BvOtuJ1YQy8EbOd0dhG1oN1q8el/BIRSFCFnWAMM/vJJlHWLi4ad22sKbKr9mvjoA==", "license": "MIT", - "peer": true, "dependencies": { "@supabase/node-fetch": "^2.6.14" } @@ -2434,7 +2433,6 @@ "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", "license": "MIT", - "peer": true, "dependencies": { "whatwg-url": "^5.0.0" }, @@ -2447,7 +2445,6 @@ "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.19.4.tgz", "integrity": "sha512-O4soKqKtZIW3olqmbXXbKugUtByD2jPa8kL2m2c1oozAO11uCcGrRhkZL0kVxjBLrXHE0mdSkFsMj7jDSfyNpw==", "license": "MIT", - "peer": true, "dependencies": { "@supabase/node-fetch": "^2.6.14" } @@ -2457,7 +2454,6 @@ "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.11.10.tgz", "integrity": "sha512-SJKVa7EejnuyfImrbzx+HaD9i6T784khuw1zP+MBD7BmJYChegGxYigPzkKX8CK8nGuDntmeSD3fvriaH0EGZA==", "license": "MIT", - "peer": true, "dependencies": { "@supabase/node-fetch": "^2.6.13", "@types/phoenix": "^1.6.6", @@ -2482,7 +2478,6 @@ "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.7.1.tgz", "integrity": "sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA==", "license": "MIT", - "peer": true, "dependencies": { "@supabase/node-fetch": "^2.6.14" } @@ -2492,7 +2487,6 @@ "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.50.0.tgz", "integrity": "sha512-M1Gd5tPaaghYZ9OjeO1iORRqbTWFEz/cF3pPubRnMPzA+A8SiUsXXWDP+DWsASZcjEcVEcVQIAF38i5wrijYOg==", "license": "MIT", - "peer": true, "dependencies": { "@supabase/auth-js": "2.70.0", "@supabase/functions-js": "2.4.4", @@ -2963,8 +2957,7 @@ "version": "1.6.6", "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz", "integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/react": { "version": "19.1.7", @@ -2996,7 +2989,6 @@ "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", "license": "MIT", - "peer": true, "dependencies": { "@types/node": "*" } @@ -9737,8 +9729,7 @@ "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/trim-lines": { "version": "3.0.1", @@ -10249,15 +10240,13 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", - "license": "BSD-2-Clause", - "peer": true + "license": "BSD-2-Clause" }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "license": "MIT", - "peer": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" @@ -10397,7 +10386,6 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.2.tgz", "integrity": "sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=10.0.0" }, diff --git a/package.json b/package.json index ccbe937..152d9af 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@radix-ui/react-toggle-group": "^1.1.7", "@radix-ui/react-tooltip": "^1.2.4", "@supabase/ssr": "^0.6.1", + "@supabase/supabase-js": "^2.50.0", "@tailwindcss/typography": "^0.5.16", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/src/components/CollectionCardOptions.tsx b/src/components/CollectionCardOptions.tsx index 63ac126..810c014 100644 --- a/src/components/CollectionCardOptions.tsx +++ b/src/components/CollectionCardOptions.tsx @@ -1,6 +1,6 @@ "use client"; -import { Edit, MoreVertical, Trash } from "lucide-react"; +import { Edit, Edit2, MoreVertical, Trash } from "lucide-react"; import { DropdownMenu, DropdownMenuContent, @@ -26,13 +26,19 @@ import { } from "@/components/ui/dialog"; import { useActionState, useState } from "react"; import { cn } from "@/lib/utils"; -import { deleteCollectionAction } from "@/lib/actions"; +import { deleteCollectionAction, editCollectionAction } from "@/lib/actions"; import { Button } from "./ui/button"; import { Label } from "./ui/label"; import { Input } from "./ui/input"; import { SubCollectionType } from "@/supabase/db/subCollection"; import { CollectionType } from "@/supabase/db/collection"; import ParentCollectionSelector from "./ParentCollectionSelector"; +import { useSessionData } from "@/providers/session-data-provider"; +import z from "zod"; +import { + collectionEditSchema, + FormCollectionEditErrors, +} from "@/lib/validation"; // TODO: Add a dialog for confirming delete const CollectionCardOptions = ({ @@ -42,13 +48,17 @@ const CollectionCardOptions = ({ collection: CollectionType | SubCollectionType; parentId?: number; }) => { + const [errors, setErrors] = useState({}); + const [open, setOpen] = useState(false); const [isEditDialogOpen, setIsEditDialogOpen] = useState(false); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); + const { collections } = useSessionData(); + const [parentCollection, setParentCollection] = useState< CollectionType | undefined - >(undefined); + >(collections.find((collection) => collection.id === parentId) || undefined); const handleDelete = async ( // eslint-disable-next-line @typescript-eslint/no-unused-vars @@ -68,7 +78,62 @@ const CollectionCardOptions = ({ }; }; - const [state, formAction, isPending] = useActionState(handleDelete, { + const handleEdit = async ( + prevState: { error: string; status: string }, + formData: FormData + ) => { + try { + const requestValues = { + name: formData.get("name") as string, + parentId: parentCollection?.id || undefined, + collectionId: collection.id, + }; + + const validation = await collectionEditSchema.parseAsync(requestValues); + + // TODO: Make the request if the new values are different than the current ones + const response = await editCollectionAction(validation); + + console.log(response); + + if (response.status === "SUCCESS") { + setIsEditDialogOpen(false); + setOpen(false); + } + + return { + error: response.error, + status: response.status, + }; + } catch (error) { + if (error instanceof z.ZodError) { + const fieldErorrs = error.flatten().fieldErrors; + + setErrors(fieldErorrs as FormCollectionEditErrors); + console.log(fieldErorrs); + + return { ...prevState, error: "Validation failed", status: "ERROR" }; + } + console.log(error); + + return { + ...prevState, + error: "An unexpected error has occurred", + status: "ERROR", + }; + } + }; + + const [deleteState, deleteAction, isDeletePending] = useActionState( + handleDelete, + { + error: "", + status: "INITIAL", + } + ); + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const [editState, editAction, isEditPending] = useActionState(handleEdit, { error: "", status: "INITIAL", }); @@ -87,7 +152,7 @@ const CollectionCardOptions = ({ setIsEditDialogOpen(true)} > @@ -95,7 +160,7 @@ const CollectionCardOptions = ({ setIsDeleteDialogOpen(true)} > @@ -108,7 +173,7 @@ const CollectionCardOptions = ({ { - if (!isPending) { + if (!isDeletePending) { setIsDeleteDialogOpen(open); } }} @@ -117,27 +182,27 @@ const CollectionCardOptions = ({ Are you absolutely sure? - {state.status === "ERROR" && state.error - ? `Error: ${state.error}` - : state.status === "INITIAL" || isPending + {deleteState.status === "ERROR" && deleteState.error + ? `Error: ${deleteState.error}` + : deleteState.status === "INITIAL" || isDeletePending ? `This action cannot be undone. This will permanently delete this collection and all of its children.` - : state.status === "SUCCESS" + : deleteState.status === "SUCCESS" ? "Collection deleted successfully!" : "Preparing to delete..."} Cancel -
+
@@ -148,13 +213,13 @@ const CollectionCardOptions = ({ { - if (!isPending) { + if (!isEditPending) { setIsEditDialogOpen(open); } }} > -
- + + Edit @@ -162,10 +227,12 @@ const CollectionCardOptions = ({
- + {parentId && ( + + )}
+

+ {errors.name ? errors.name[0] : "\u00A0"} +

- -
- + + + + +
); diff --git a/src/lib/actions.ts b/src/lib/actions.ts index 8b20a0a..b57ae11 100644 --- a/src/lib/actions.ts +++ b/src/lib/actions.ts @@ -2,8 +2,13 @@ import { createClient } from "@/supabase/server"; import { revalidatePath } from "next/cache"; -import { deleteCollectionSchema, nameEditSchema } from "./validation"; +import { + collectionEditSchema, + deleteCollectionSchema, + nameEditSchema, +} from "./validation"; import { parseServerActionResponse } from "./utils"; +import { doesUserOwnThisCollection } from "@/supabase/db/user"; interface formValuesType { name: string; @@ -137,3 +142,74 @@ export const deleteCollectionAction = async ( status: "SUCCESS", }); }; + +export const editCollectionAction = async ( + formValues: formValuesType +): Promise => { + const validation = collectionEditSchema.safeParse(formValues); + if (!validation.success) { + console.error("Validation Error:", validation.error); + return parseServerActionResponse({ + error: "Invalid data provided.", + data: "", + status: "ERROR", + }); + } + + const supabase = await createClient(); + + if (formValues.parentId) { + const hasCollection = await doesUserOwnThisCollection(formValues.parentId); + if (!hasCollection) { + console.error("User does not own this collection:", formValues.parentId); + return parseServerActionResponse({ + error: + "You do not have permission to move this subcollection to the selected parent collection.", + data: "", + status: "ERROR", + }); + } + const { error } = await supabase + .from("sub_collections") + .update({ name: formValues.name, collection_id: formValues.parentId }) + .eq("id", formValues.collectionId); + + if (error) { + console.error("Error updating subcollection:", error); + return parseServerActionResponse({ + error: "Failed to update subcollection", + data: "", + status: "ERROR", + }); + } + + revalidatePath("/home", "layout"); + return parseServerActionResponse({ + error: "", + data: "Subcollection updated successfully", + status: "SUCCESS", + }); + } + + const { error } = await supabase + .from("collections") + .update({ name: formValues.name }) + .eq("id", formValues.collectionId); + + if (error) { + console.error("Error updating subcollection:", error); + + return parseServerActionResponse({ + error: "Failed to update collection", + data: "", + status: "ERROR", + }); + } + + revalidatePath("/home", "layout"); + return parseServerActionResponse({ + error: "", + data: "Collection updated successfully", + status: "SUCCESS", + }); +}; diff --git a/src/lib/validation.ts b/src/lib/validation.ts index c91160b..f21d83e 100644 --- a/src/lib/validation.ts +++ b/src/lib/validation.ts @@ -58,3 +58,18 @@ export const deleteCollectionSchema = z.object({ collectionId: z.number().int().positive(), parentId: z.number().int().positive().optional(), }); + +export interface FormCollectionEditErrors { + name?: string[]; + newParentId?: string[]; + collectionId?: string[]; +} + +export const collectionEditSchema = z.object({ + name: z + .string() + .min(3, { message: "Name must be at least 3 characters long" }) + .max(32, { message: "Name must be at most 32 characters long" }), + parentId: z.number().int().positive().optional(), + collectionId: z.number().int().positive(), +}); From dba5e5588c3637aca305bdde4f2e9048945dce0a Mon Sep 17 00:00:00 2001 From: Visar Date: Mon, 16 Jun 2025 12:19:01 +0200 Subject: [PATCH 44/49] Fix ui bug in COllectionTitle and added some typesafety --- src/components/CollectionTitle.tsx | 40 +++++++++++++++++++----------- 1 file changed, 26 insertions(+), 14 deletions(-) diff --git a/src/components/CollectionTitle.tsx b/src/components/CollectionTitle.tsx index b8f067d..dc55b3e 100644 --- a/src/components/CollectionTitle.tsx +++ b/src/components/CollectionTitle.tsx @@ -10,6 +10,12 @@ import { SubCollectionType } from "@/supabase/db/subCollection"; import { nameEditSchema } from "@/lib/validation"; import { FormCollectionNameEditErrors as FormErrors } from "@/lib/validation"; +import { Input } from "./ui/input"; + +interface StateType { + error: string; + status: "INITIAL" | "ERROR" | "SUCCESS"; +} const CollectionTitle = ({ collection, @@ -21,8 +27,7 @@ const CollectionTitle = ({ const [activeForm, setActiveForm] = useState(false); const [errors, setErrors] = useState({}); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const handleFormSubmit = async (prevState: any, formData: FormData) => { + const handleFormSubmit = async (prevState: StateType, formData: FormData) => { try { const name = formData.get("name") as string | undefined; if (name == collection.name) { @@ -31,7 +36,7 @@ const CollectionTitle = ({ ...prevState, error: "Name wasnt changed", status: "ERROR", - }; + } as StateType; } const formValues = { @@ -57,7 +62,7 @@ const CollectionTitle = ({ setActiveForm(false); - return result; + return result as StateType; } catch (error) { if (error instanceof z.ZodError) { const fieldErorrs = error.flatten().fieldErrors; @@ -65,7 +70,11 @@ const CollectionTitle = ({ setErrors(fieldErorrs as FormErrors); console.log(fieldErorrs); - return { ...prevState, error: "Validation failed", status: "ERROR" }; + return { + ...prevState, + error: "Validation failed", + status: "ERROR", + } as StateType; } console.log(error); @@ -73,22 +82,25 @@ const CollectionTitle = ({ ...prevState, error: "An unexpected error has occurred", status: "ERROR", - }; + } as StateType; } }; // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [state, formAction, isPending] = useActionState(handleFormSubmit, { - error: "", - status: "INITIAL", - }); + const [state, formAction, isPending] = useActionState( + handleFormSubmit, + { + error: "", + status: "INITIAL", + } + ); if (activeForm) { return (
-
- + {isPending ? ( From dc6efb0cf5a601c2c9dc845679be363c29b061c8 Mon Sep 17 00:00:00 2001 From: Visar Date: Mon, 16 Jun 2025 13:17:17 +0200 Subject: [PATCH 45/49] Updated noteList to use session data --- .../[subCollectionId]/NoteList.tsx | 48 ++++++++++++++++++ .../[collectionId]/[subCollectionId]/page.tsx | 23 ++------- src/components/NoteCard.tsx | 2 +- src/lib/data.ts | 15 +++--- src/providers/session-data-provider.tsx | 3 +- src/supabase/schema.ts | Bin 7094 -> 14750 bytes 6 files changed, 64 insertions(+), 27 deletions(-) create mode 100644 src/app/home/[collectionId]/[subCollectionId]/NoteList.tsx diff --git a/src/app/home/[collectionId]/[subCollectionId]/NoteList.tsx b/src/app/home/[collectionId]/[subCollectionId]/NoteList.tsx new file mode 100644 index 0000000..d611056 --- /dev/null +++ b/src/app/home/[collectionId]/[subCollectionId]/NoteList.tsx @@ -0,0 +1,48 @@ +"use client"; + +import { useSessionData } from "@/providers/session-data-provider"; +import { CollectionType } from "@/supabase/db/collection"; +import { SubCollectionType } from "@/supabase/db/subCollection"; + +import { NoteType } from "@/supabase/db/notes"; +import NoteCard from "@/components/NoteCard"; + +const NoteList = ({ + sub_collection, + collection, +}: { + sub_collection: SubCollectionType; + collection: CollectionType; +}) => { + const { notes: allUserNotes, premadeNotes } = useSessionData(); + + let notes: NoteType[]; + if (sub_collection.user_id === null) { + // If the sub-collection is a premade one, we use the premade notes + notes = premadeNotes.filter( + (note) => note.sub_collection_id === sub_collection.id + ); + } else { + // If the sub-collection is a user-created one, we use the user's notes + notes = allUserNotes.filter( + (note) => note.sub_collection_id === sub_collection.id + ); + } + + return ( +
+ {notes && + notes.map((note) => { + return ( + + ); + })} +
+ ); +}; +export default NoteList; diff --git a/src/app/home/[collectionId]/[subCollectionId]/page.tsx b/src/app/home/[collectionId]/[subCollectionId]/page.tsx index 8ae72ff..28e1a44 100644 --- a/src/app/home/[collectionId]/[subCollectionId]/page.tsx +++ b/src/app/home/[collectionId]/[subCollectionId]/page.tsx @@ -1,10 +1,9 @@ -import { redirect } from "next/navigation"; +import { notFound } from "next/navigation"; -import NoteCard from "@/components/NoteCard"; import { getCollectionById } from "@/supabase/db/collection"; import { getSubCollectionById } from "@/supabase/db/subCollection"; -import { getNotesBySubCollectionId } from "@/supabase/db/notes"; import CollectionTitle from "@/components/CollectionTitle"; +import NoteList from "./NoteList"; export default async function Page({ params, @@ -18,9 +17,7 @@ export default async function Page({ getSubCollectionById(subCollectionId), ]); - if (!collection || !sub_collection) redirect("/not-found"); - - const notes = await getNotesBySubCollectionId(subCollectionId); + if (!collection || !sub_collection) notFound(); return (
@@ -29,19 +26,7 @@ export default async function Page({ parentCollection={collection} />
-
- {notes && - notes.map((note) => { - return ( - - ); - })} -
+
); diff --git a/src/components/NoteCard.tsx b/src/components/NoteCard.tsx index 0e06fc0..ae8dc83 100644 --- a/src/components/NoteCard.tsx +++ b/src/components/NoteCard.tsx @@ -18,7 +18,7 @@ const NoteCard = ({ className="bg-zinc-800 rounded-md p-6 py-8 flex justify-start items-center gap-8 hover:scale-102 transition-all" >
- +

{note.name}

diff --git a/src/lib/data.ts b/src/lib/data.ts index d811ab5..140ef90 100644 --- a/src/lib/data.ts +++ b/src/lib/data.ts @@ -11,16 +11,18 @@ export async function getUserData(): Promise { premade_collections: premadeCollections, premade_sub_collections: premadeSubCollections, notes, + premade_notes: premadeNotes, } = data; // TODO: Make it not break when one of these is false console.log( userData && "yes", - collections && "yes", - subCollections && "yes", - notes && "yes", - premadeCollections && "yes", - premadeSubCollections && "yes" + collections.length, + subCollections.length, + notes.length, + premadeCollections.length, + premadeSubCollections.length, + premadeNotes.length ); if (userData && data) { @@ -28,9 +30,10 @@ export async function getUserData(): Promise { user: userData, collections, subCollections, + notes, premadeCollections, premadeSubCollections, - notes, + premadeNotes, }; } return null; diff --git a/src/providers/session-data-provider.tsx b/src/providers/session-data-provider.tsx index cb78be9..2760c10 100644 --- a/src/providers/session-data-provider.tsx +++ b/src/providers/session-data-provider.tsx @@ -10,9 +10,10 @@ export interface SessionDataContextType { user: User; collections: CollectionType[]; subCollections: SubCollectionType[]; + notes: NoteType[]; premadeCollections: CollectionType[]; premadeSubCollections: SubCollectionType[]; - notes: NoteType[]; + premadeNotes: NoteType[]; } // Create the context with a default value diff --git a/src/supabase/schema.ts b/src/supabase/schema.ts index e9a0f71039643e97a6b677e6083826acf4e06e34..1d249dc7174e50a2fe6eb0ba76e0190b21bf74e7 100644 GIT binary patch literal 14750 zcmeHOU2oeq6y@`P{Rblt!v+-H`_`o>+78GDG)30z#R!5pu~ToUZCK8>pvzzHc8=(4 zeu)w#%60+-fntdwFYo8Mm!$pQm#O-s7HY1RYNZD1Gp=`dTB))6pziQyjFx*`6ZDv) z??4@>=jwZPrAGKaTlX!{`lI?2GC0yB=l^Hg1y+?Y3%>O(n_vKAF@+2gX* z1$$g#o@uG!SYP%(L65&9wD_KX9K~~z6kwbo5yV9%=YiAlEQ+F-vJIZk~UoKEh7S#9^L_bM5+# zkBd28(+1zVMV8NPTuS1so!xYNH0yMGHcjQA53BZz25nHbn_WYG_hDJhMVEN(V%s({ zyD=M%%!%42te2|O#(Lg=-qfT18$M4;TpA;5T%+pw714Nyb%Lqk4$!Js4FxK#=BPUd zMO~50x}j=Tw(07Y>i||;M2sQU8qG5tY-;lu2-yF?PFR*=dMsg}UXQBrAcx za4k_e=$s(WZVdW!{9NuB+N%?e4(q9F&|YCa6I0y1yh2O0#;d8&7hZwK>$Oa*I5>U@ zN-xxVe9p^VTkcBTtt+)>j&z5Sx`vci;BE=7V)~K_AcIG=%=cRMR==^Z)!D%$9n2*c{Not`FaQ2**ovmJ2N8Z0+t%10=$6b$F*LRBX2)8 z@p-M+;={6$&wE#!-NWyz{MN@4|M@NAk(GWdCi}p!*YG@j4paaQZ6Dm$tRkg+a$Rap zir&DZxenuL&Px3TgyY^AeO;fPgdY7CXHGmtxH<2Y6j(O)H$<3v7yR1=XsPZ$+y6+m}aU_b)dc}<*yrK zawO5~yI_8vpB0t`7@amxzAboK_H08X)POH9?GxiK9NnxxKlirb-DG-qrTo&9nRakb z^@f>J=v@ut=d?w2rld`)c5!ycQJu%oJ4_{|qt!fr#Q z99q?x;MN&5L{v05<;NNE%AEeNrq;V-Y&CJkUSVvVI3Df#kl0991^pt0jbmM~Rd|z{ zEbh~c?^xW3_r+w3A$#8Jxrp^JJi=g$_A%-tC41ZZXZy(H^`2t#9#`IHPIRy)c*@BG zoaNWoA-VpYo{{Z1bg}C(<-gtK8{fOT=NmhUsd+o=95rU0BPXt0%ZW|5oprk50DZsR z$lPd1L;Ky>qbugyyZTRg!$z@T1rHxVg7FH(FXI)j%fq(hw8&$o~7CGAByFd2v zp?k9g@p@jyhF0kV*@e2;B~7<^ds%JrVl#iKnj3EXTVypW*ZMlTnTODu?Y)~WQP*5U z3s>4N>MCgIDRPH4b~Vuk&Fao(48Fa`S&96LBt9a}pReEU7#E*CjIaE?jQDIKt1sCA zr{w+W%HIjy#)(51(YaE@qdKnB(1Pg*nagiz$1b~I;@3L*{WF|g9=q#wsNz}|Pj)dR zm)B6A>4+NV)%B2OjBTPc%#K!9Yt6`eiL-Ei1BSncBzO9r-_=>6Rj=;Mw|f>AmM|8~ d8?Fo`FUTpneKIQd_vpu82I}uR>)G^P_W#kXwp{=K literal 7094 zcmeHMTW{J(6n^Jd9QmPYrD*$3(x9sCO4*f?iY9%r5Hfg3ylZBV$KD1J{(H|{J!6k? z3`y08MG-jm%y(|zxeR3M4J%66!8I$%?RG<8537Q$A^PKex_D3VE|1PfBl0NMm14zT zoKg5N0(b&m78$*lm$X_hNFgs5jO8Su`gWeniyg%OlI>V;nT-|96I3)MtBjHq_Hvy- zk7o^$pOZ3KB%Htp*x{;8waByOSY89%CX1YK^B=&H{O$iP2j8w|eSk&3EywPdtw zMVl7G!f%--h-<_Np=i^*rlRCQn^k2Cg)&?L+rB zRUBeqV0*8H&x&}Emfa>=%wugtfX*m(8X1&&3a_0iv2(5gR;(b|J^h<(uVuZ8B;V() zP|a6Zj3d_xvTjyuQ8VMHwLY?$r1B}_Hg|m$vN?%3bG;vp-YmQj2uG)RJ0X^ zrQYi;pIC16$WmXhXc6jV^|6`l>wLKM_vYox^oA2t^#m=0H0^3(Lzo!KH&4%?WZ}+W z5)5q=LsT`?vDIFq*}Y8Un?Y>o5El`#avXHrKiQ)NzXXn# z@tLfWDlfk+AJBt1{#&Pgc`S=$SzcftV=H_63=AhOXN@z2V#ozn0S{B&7R~aexY@|r z5y)dnXv$^1cmgHht+%P>8@%!1;z;IGG<&DF?|@6)#~R2O0Q)msVWk_PKAL^pQcjn! zFt$i0)hZ`eeSLz8Jv-X?*!!ir(acFIcq5GwxntBHgyXqH7Zr@9EH0x<5mh(JZKbSp zx}Av%PT=iP+DUmlnprE4LNPYRR{dG6wsxO9Yh)B>Bbe%3S3c4UeTI zub5>gDR(G6%$Fc4L$3S`xpK|I>!BIa9rlTzYc>Ce^LwBL-4s`2_Wu;8JlK$2F1$t~ zc{PfgAZ{co1S$n#B7y1XQTyO+z!G$!7%_`yPM&&h++K$tsxj$Tpp9DmxvE$5@3T8y03 From fc37d49faf371858da53e426f7d8aeacb3cbe19a Mon Sep 17 00:00:00 2001 From: Visar Date: Mon, 16 Jun 2025 13:22:49 +0200 Subject: [PATCH 46/49] update schema encoding bug --- src/supabase/schema.ts | Bin 14750 -> 7131 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/src/supabase/schema.ts b/src/supabase/schema.ts index 1d249dc7174e50a2fe6eb0ba76e0190b21bf74e7..4c0acee40a4efed3583fd426b60a86033839baf9 100644 GIT binary patch literal 7131 zcmeHMTW{J(6n^Jd9QmPYrD*$3(x9sCO4*f?iY9%r5Hfg3ylZBV$KD1J{(H|{J!6k? z3`y08MG-jm%y(|zxeR3M4J%66!8I$%?RG<8537Q$A^PKex_D3VE|1PfBl0NMm14zT zoKg5N0(b&m78$*lm$X_hNFgs5jO8Su`gWeniyg%OlI>V;nT-|96I3)MtBjHq_Hvy- zk7o^$pOZ3KB%Htp*x{;8waByOSY89%CX1YK^B=&H{O$iP2j8w|eSk&3EywPdtw zMVl7G!f%--h-<_Np=i^*rlRCQn^k2Cg)&?L+rB zRUBeqV0*8H&x&}Emfa>=%wugtfX*m(8X1&&3a_0iv2(5gR;(b|J^h<(uVuZ8B;V() zP|a6Zj3d_xvTjyuQ8VMHwLY?$r1B}_Hg|m$vN?%3bG;vp-YmQj2uG)RJ0X^ zrQYi;pIC16$WmXhXc6jV^|6`l>wLKM_vYox^oA2t^#m=0H0^3(Lzo!KH&4%?WZ}+W z5)5q=LsT`?vDIFq*}Y8Un?Y>o5El`#avXme@|moXDlfk+AJENsMzEg!@>mwhvb?}%$5!_C85rVR&Kd^|MWYKW6&|L%EjsE= zakG&FCXmOH(3H!f@&ro2TW?cMN_gYL#gR<9X!cHT-vO7pk2R1n0QP6N!b&$neKh;H zrJSyFVQi6cs-I4*`udm^dv>((aX^sjMl&b#;Egm!B$!cu5RT^(U3oB;vI31RMO57= zx0SNa>2@Y6IDxlEX(#3JXlAWE3dPtMTlHtP+S+~cK$1}$pkS&iuY59rsr=Ng!E4O) zXS$@1o?5gA=aE#n&6VYH`|##I4xHuy-JiVgCse4^iTc<~?l4xG`h;JXgj4F*rt47* zG(47;ykeG}q}-wOFyEA@47u_%uGRb>&hLR1bW>c-;{Q{e@?b-9 zx$qi|xiVw6PL~%Jpwzw_s_RnFBG?q}&OggTwcpCw>Eqhqz z>8X=em^v+VkJhdgy@k64W$o$iUFaXIa={U>1sBpRHmiiE8 k;Cn(#jvZ`=-r0CReaSq*Uo+$}5#RQZaQyuXx15iD0xzPd2><{9 literal 14750 zcmeHOU2oeq6y@`P{Rblt!v+-H`_`o>+78GDG)30z#R!5pu~ToUZCK8>pvzzHc8=(4 zeu)w#%60+-fntdwFYo8Mm!$pQm#O-s7HY1RYNZD1Gp=`dTB))6pziQyjFx*`6ZDv) z??4@>=jwZPrAGKaTlX!{`lI?2GC0yB=l^Hg1y+?Y3%>O(n_vKAF@+2gX* z1$$g#o@uG!SYP%(L65&9wD_KX9K~~z6kwbo5yV9%=YiAlEQ+F-vJIZk~UoKEh7S#9^L_bM5+# zkBd28(+1zVMV8NPTuS1so!xYNH0yMGHcjQA53BZz25nHbn_WYG_hDJhMVEN(V%s({ zyD=M%%!%42te2|O#(Lg=-qfT18$M4;TpA;5T%+pw714Nyb%Lqk4$!Js4FxK#=BPUd zMO~50x}j=Tw(07Y>i||;M2sQU8qG5tY-;lu2-yF?PFR*=dMsg}UXQBrAcx za4k_e=$s(WZVdW!{9NuB+N%?e4(q9F&|YCa6I0y1yh2O0#;d8&7hZwK>$Oa*I5>U@ zN-xxVe9p^VTkcBTtt+)>j&z5Sx`vci;BE=7V)~K_AcIG=%=cRMR==^Z)!D%$9n2*c{Not`FaQ2**ovmJ2N8Z0+t%10=$6b$F*LRBX2)8 z@p-M+;={6$&wE#!-NWyz{MN@4|M@NAk(GWdCi}p!*YG@j4paaQZ6Dm$tRkg+a$Rap zir&DZxenuL&Px3TgyY^AeO;fPgdY7CXHGmtxH<2Y6j(O)H$<3v7yR1=XsPZ$+y6+m}aU_b)dc}<*yrK zawO5~yI_8vpB0t`7@amxzAboK_H08X)POH9?GxiK9NnxxKlirb-DG-qrTo&9nRakb z^@f>J=v@ut=d?w2rld`)c5!ycQJu%oJ4_{|qt!fr#Q z99q?x;MN&5L{v05<;NNE%AEeNrq;V-Y&CJkUSVvVI3Df#kl0991^pt0jbmM~Rd|z{ zEbh~c?^xW3_r+w3A$#8Jxrp^JJi=g$_A%-tC41ZZXZy(H^`2t#9#`IHPIRy)c*@BG zoaNWoA-VpYo{{Z1bg}C(<-gtK8{fOT=NmhUsd+o=95rU0BPXt0%ZW|5oprk50DZsR z$lPd1L;Ky>qbugyyZTRg!$z@T1rHxVg7FH(FXI)j%fq(hw8&$o~7CGAByFd2v zp?k9g@p@jyhF0kV*@e2;B~7<^ds%JrVl#iKnj3EXTVypW*ZMlTnTODu?Y)~WQP*5U z3s>4N>MCgIDRPH4b~Vuk&Fao(48Fa`S&96LBt9a}pReEU7#E*CjIaE?jQDIKt1sCA zr{w+W%HIjy#)(51(YaE@qdKnB(1Pg*nagiz$1b~I;@3L*{WF|g9=q#wsNz}|Pj)dR zm)B6A>4+NV)%B2OjBTPc%#K!9Yt6`eiL-Ei1BSncBzO9r-_=>6Rj=;Mw|f>AmM|8~ d8?Fo`FUTpneKIQd_vpu82I}uR>)G^P_W#kXwp{=K From 3984539d27034e1988bab150750b550b43db9765 Mon Sep 17 00:00:00 2001 From: Visar Date: Mon, 16 Jun 2025 15:10:57 +0200 Subject: [PATCH 47/49] Update NoteList to render a create btn when there are no note for the selected subCollection and created a createNote page --- .../[subCollectionId]/NoteList.tsx | 34 ++++++++++++------- src/app/home/create/note/CreateNoteForm.tsx | 21 ++++++++++++ src/app/home/create/note/loading.tsx | 18 ++++++++++ src/app/home/create/note/page.tsx | 13 +++++++ src/components/Icon.tsx | 2 +- 5 files changed, 75 insertions(+), 13 deletions(-) create mode 100644 src/app/home/create/note/CreateNoteForm.tsx create mode 100644 src/app/home/create/note/loading.tsx create mode 100644 src/app/home/create/note/page.tsx diff --git a/src/app/home/[collectionId]/[subCollectionId]/NoteList.tsx b/src/app/home/[collectionId]/[subCollectionId]/NoteList.tsx index d611056..af168ed 100644 --- a/src/app/home/[collectionId]/[subCollectionId]/NoteList.tsx +++ b/src/app/home/[collectionId]/[subCollectionId]/NoteList.tsx @@ -6,6 +6,8 @@ import { SubCollectionType } from "@/supabase/db/subCollection"; import { NoteType } from "@/supabase/db/notes"; import NoteCard from "@/components/NoteCard"; +import { Button } from "@/components/ui/button"; +import Link from "next/link"; const NoteList = ({ sub_collection, @@ -29,19 +31,27 @@ const NoteList = ({ ); } - return ( + return notes.length > 0 ? (
- {notes && - notes.map((note) => { - return ( - - ); - })} + {notes.map((note) => { + return ( + + ); + })} +
+ ) : ( +
+

This collection is empty.

+
); }; diff --git a/src/app/home/create/note/CreateNoteForm.tsx b/src/app/home/create/note/CreateNoteForm.tsx new file mode 100644 index 0000000..86c177f --- /dev/null +++ b/src/app/home/create/note/CreateNoteForm.tsx @@ -0,0 +1,21 @@ +"use client"; + +import { useSessionData } from "@/providers/session-data-provider"; +import { useSearchParams } from "next/navigation"; + +const CraeteNoteForm = () => { + const { subCollections } = useSessionData(); + const searchParams = useSearchParams(); + + const subCollectionId = Number(searchParams.get("subCollectionId")); + const subCollection = subCollections.find( + (sub) => sub.id === subCollectionId + ); + + return ( +
+ Creating a note for {subCollection?.name} +
+ ); +}; +export default CraeteNoteForm; diff --git a/src/app/home/create/note/loading.tsx b/src/app/home/create/note/loading.tsx new file mode 100644 index 0000000..baf8b71 --- /dev/null +++ b/src/app/home/create/note/loading.tsx @@ -0,0 +1,18 @@ +import { Skeleton } from "@/components/ui/skeleton"; + +const loading = () => { + return ( +
+
+

+ Create a Collection +

+
+
+ + +
+
+ ); +}; +export default loading; diff --git a/src/app/home/create/note/page.tsx b/src/app/home/create/note/page.tsx new file mode 100644 index 0000000..2b5f8ab --- /dev/null +++ b/src/app/home/create/note/page.tsx @@ -0,0 +1,13 @@ +import CreateNoteForm from "./CreateNoteForm"; + +const Page = () => { + return ( +
+
+

Create a Note

+
+ +
+ ); +}; +export default Page; diff --git a/src/components/Icon.tsx b/src/components/Icon.tsx index dc5189b..a49abf4 100644 --- a/src/components/Icon.tsx +++ b/src/components/Icon.tsx @@ -57,7 +57,7 @@ const Icon = ({ iconName, className, ...props }: CustomIconProps) => { // You can return null or a placeholder element. A placeholder is often better for layout stability. return ( ); From e915dee409a75e5eb9940ae229e0d10ca49e9c75 Mon Sep 17 00:00:00 2001 From: Visar Date: Mon, 16 Jun 2025 23:47:01 +0200 Subject: [PATCH 48/49] Made NoteCreation quicker by immidiatelly creating a note when the user presses the createNote button --- .../[subCollectionId]/NoteList.tsx | 25 +++++-- .../createUntitledNoteAction.ts | 69 +++++++++++++++++++ src/app/home/create/note/CreateNoteForm.tsx | 21 ------ src/app/home/create/note/loading.tsx | 18 ----- src/app/home/create/note/page.tsx | 13 ---- src/supabase/db/user.ts | 21 ++++++ src/supabase/schema.ts | 6 +- 7 files changed, 112 insertions(+), 61 deletions(-) create mode 100644 src/app/home/[collectionId]/[subCollectionId]/createUntitledNoteAction.ts delete mode 100644 src/app/home/create/note/CreateNoteForm.tsx delete mode 100644 src/app/home/create/note/loading.tsx delete mode 100644 src/app/home/create/note/page.tsx diff --git a/src/app/home/[collectionId]/[subCollectionId]/NoteList.tsx b/src/app/home/[collectionId]/[subCollectionId]/NoteList.tsx index af168ed..46d6e82 100644 --- a/src/app/home/[collectionId]/[subCollectionId]/NoteList.tsx +++ b/src/app/home/[collectionId]/[subCollectionId]/NoteList.tsx @@ -7,7 +7,8 @@ import { SubCollectionType } from "@/supabase/db/subCollection"; import { NoteType } from "@/supabase/db/notes"; import NoteCard from "@/components/NoteCard"; import { Button } from "@/components/ui/button"; -import Link from "next/link"; +import { createUntitledNoteAction } from "./createUntitledNoteAction"; +import { useRouter } from "next/navigation"; const NoteList = ({ sub_collection, @@ -17,6 +18,7 @@ const NoteList = ({ collection: CollectionType; }) => { const { notes: allUserNotes, premadeNotes } = useSessionData(); + const router = useRouter(); let notes: NoteType[]; if (sub_collection.user_id === null) { @@ -45,12 +47,23 @@ const NoteList = ({ })}
) : ( -
+

This collection is empty.

-
); diff --git a/src/app/home/[collectionId]/[subCollectionId]/createUntitledNoteAction.ts b/src/app/home/[collectionId]/[subCollectionId]/createUntitledNoteAction.ts new file mode 100644 index 0000000..fbd5591 --- /dev/null +++ b/src/app/home/[collectionId]/[subCollectionId]/createUntitledNoteAction.ts @@ -0,0 +1,69 @@ +"use server"; + +import { rType } from "@/lib/actions"; +import { parseServerActionResponse } from "@/lib/utils"; +import { doesUserOwnThisSubCollection, getUser } from "@/supabase/db/user"; +import { createClient } from "@/supabase/server"; +import { revalidatePath } from "next/cache"; +import z from "zod"; + +export async function createUntitledNoteAction( + subCollectionId: number +): Promise { + console.log("Creating untitled note for subCollectionId:", subCollectionId); + const validation = z.number().int().positive().safeParse(subCollectionId); + if (!validation.success) { + console.log("Invalid subCollectionId:", subCollectionId); + return parseServerActionResponse({ + error: "Invalid subCollectionId", + status: "ERROR", + }); + } + + const supabase = await createClient(); + const user = await getUser(); + if (!user) { + return parseServerActionResponse({ + error: "User not authenticated", + status: "ERROR", + }); + } + + // Check if the sub-collection exists and belongs to the user + const isSobCollectionOwnedByUser = await doesUserOwnThisSubCollection( + subCollectionId + ); + if (!isSobCollectionOwnedByUser) { + return parseServerActionResponse({ + error: "You do not own this sub-collection", + status: "ERROR", + }); + } + + const { data, error } = await supabase + .from("notes") + .insert({ + name: "Untitled Note", + theory: "An untitled note is a note that has not been given a name yet.", + sections: [], + symbols: [], + sub_collection_id: subCollectionId, + user_id: user.id, + }) + .select() + .single(); + + if (error) { + console.error("Error creating untitled note:", error); + return parseServerActionResponse({ + error: "Failed to create untitled note", + status: "ERROR", + }); + } + + revalidatePath("/home", "layout"); + return parseServerActionResponse({ + data: data.id, + status: "SUCCESS", + }); +} diff --git a/src/app/home/create/note/CreateNoteForm.tsx b/src/app/home/create/note/CreateNoteForm.tsx deleted file mode 100644 index 86c177f..0000000 --- a/src/app/home/create/note/CreateNoteForm.tsx +++ /dev/null @@ -1,21 +0,0 @@ -"use client"; - -import { useSessionData } from "@/providers/session-data-provider"; -import { useSearchParams } from "next/navigation"; - -const CraeteNoteForm = () => { - const { subCollections } = useSessionData(); - const searchParams = useSearchParams(); - - const subCollectionId = Number(searchParams.get("subCollectionId")); - const subCollection = subCollections.find( - (sub) => sub.id === subCollectionId - ); - - return ( -
- Creating a note for {subCollection?.name} -
- ); -}; -export default CraeteNoteForm; diff --git a/src/app/home/create/note/loading.tsx b/src/app/home/create/note/loading.tsx deleted file mode 100644 index baf8b71..0000000 --- a/src/app/home/create/note/loading.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { Skeleton } from "@/components/ui/skeleton"; - -const loading = () => { - return ( -
-
-

- Create a Collection -

-
-
- - -
-
- ); -}; -export default loading; diff --git a/src/app/home/create/note/page.tsx b/src/app/home/create/note/page.tsx deleted file mode 100644 index 2b5f8ab..0000000 --- a/src/app/home/create/note/page.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import CreateNoteForm from "./CreateNoteForm"; - -const Page = () => { - return ( -
-
-

Create a Note

-
- -
- ); -}; -export default Page; diff --git a/src/supabase/db/user.ts b/src/supabase/db/user.ts index c6344cf..10d52f6 100644 --- a/src/supabase/db/user.ts +++ b/src/supabase/db/user.ts @@ -45,3 +45,24 @@ export async function doesUserOwnThisCollection(collectionId: number) { } return true; // The user owns the collection } + +export async function doesUserOwnThisSubCollection(subCollectionId: number) { + const user = await getUser(); + if (!user) { + return false; + } + + const supabase = await createClient(); + const { data: collection, error: checkError } = await supabase + .from("sub_collections") + .select("id") // We only need to know if it exists, so just select 'id' + .eq("id", subCollectionId) + .eq("user_id", user.id) + .maybeSingle(); // Returns one record or null, but not an error if not found + + if (checkError || !collection) { + // If there's an error OR if no collection was found, the user is not authorized + return false; + } + return true; // The user owns the collection +} diff --git a/src/supabase/schema.ts b/src/supabase/schema.ts index 4c0acee..182d60b 100644 --- a/src/supabase/schema.ts +++ b/src/supabase/schema.ts @@ -42,7 +42,7 @@ export type Database = { sections: Json; sub_collection_id: number; symbols: Json; - theory: string; + theory: string | null; user_id: string | null; }; Insert: { @@ -51,7 +51,7 @@ export type Database = { sections?: Json; sub_collection_id: number; symbols?: Json; - theory: string; + theory?: string | null; user_id?: string | null; }; Update: { @@ -60,7 +60,7 @@ export type Database = { sections?: Json; sub_collection_id?: number; symbols?: Json; - theory?: string; + theory?: string | null; user_id?: string | null; }; Relationships: [ From 37508d4352c521a68d1db95452ee4760cfcaab23 Mon Sep 17 00:00:00 2001 From: Visar Date: Thu, 19 Jun 2025 18:28:11 +0200 Subject: [PATCH 49/49] Granted the ability to create notes even if the subCOllection is not empty, by using AddNoteButton for creating notes. --- .../[subCollectionId]/NoteList.tsx | 27 ++------- .../[collectionId]/[subCollectionId]/page.tsx | 10 +++- src/components/AddNoteButton.tsx | 57 +++++++++++++++++++ 3 files changed, 72 insertions(+), 22 deletions(-) create mode 100644 src/components/AddNoteButton.tsx diff --git a/src/app/home/[collectionId]/[subCollectionId]/NoteList.tsx b/src/app/home/[collectionId]/[subCollectionId]/NoteList.tsx index 46d6e82..96f6473 100644 --- a/src/app/home/[collectionId]/[subCollectionId]/NoteList.tsx +++ b/src/app/home/[collectionId]/[subCollectionId]/NoteList.tsx @@ -6,9 +6,7 @@ import { SubCollectionType } from "@/supabase/db/subCollection"; import { NoteType } from "@/supabase/db/notes"; import NoteCard from "@/components/NoteCard"; -import { Button } from "@/components/ui/button"; -import { createUntitledNoteAction } from "./createUntitledNoteAction"; -import { useRouter } from "next/navigation"; +import AddNoteButton from "@/components/AddNoteButton"; const NoteList = ({ sub_collection, @@ -18,7 +16,6 @@ const NoteList = ({ collection: CollectionType; }) => { const { notes: allUserNotes, premadeNotes } = useSessionData(); - const router = useRouter(); let notes: NoteType[]; if (sub_collection.user_id === null) { @@ -34,7 +31,7 @@ const NoteList = ({ } return notes.length > 0 ? ( -
+
{notes.map((note) => { return (

This collection is empty.

- +
); }; diff --git a/src/app/home/[collectionId]/[subCollectionId]/page.tsx b/src/app/home/[collectionId]/[subCollectionId]/page.tsx index 28e1a44..279ea3a 100644 --- a/src/app/home/[collectionId]/[subCollectionId]/page.tsx +++ b/src/app/home/[collectionId]/[subCollectionId]/page.tsx @@ -4,6 +4,7 @@ import { getCollectionById } from "@/supabase/db/collection"; import { getSubCollectionById } from "@/supabase/db/subCollection"; import CollectionTitle from "@/components/CollectionTitle"; import NoteList from "./NoteList"; +import AddNoteButton from "@/components/AddNoteButton"; export default async function Page({ params, @@ -25,7 +26,14 @@ export default async function Page({ collection={sub_collection} parentCollection={collection} /> -
+
+
+ +
diff --git a/src/components/AddNoteButton.tsx b/src/components/AddNoteButton.tsx new file mode 100644 index 0000000..a3e6ad1 --- /dev/null +++ b/src/components/AddNoteButton.tsx @@ -0,0 +1,57 @@ +"use client"; + +import { Plus } from "lucide-react"; +import { Button } from "./ui/button"; +import { useTransition } from "react"; +import { VariantProps } from "class-variance-authority"; +import { buttonVariants } from "./ui/button"; +import { AppRouterInstance } from "next/dist/shared/lib/app-router-context.shared-runtime"; +import { createUntitledNoteAction } from "@/app/home/[collectionId]/[subCollectionId]/createUntitledNoteAction"; +import { useRouter } from "next/navigation"; + +export const createNoteHandler = async ( + collectionId: number, + subCollectionId: number, + router: AppRouterInstance +) => { + const response = await createUntitledNoteAction(subCollectionId); + if (response.status === "SUCCESS") { + router.push(`/home/${collectionId}/${subCollectionId}/${response.data}`); + } else { + console.error("Failed to create note:", response.error); + } +}; + +const AddNoteButton = ({ + className, + variant, + size, + collectionId, + subCollectionId, + ...props +}: React.ComponentProps & + VariantProps & { + collectionId: number; + subCollectionId: number; + }) => { + const [isPending, startTransition] = useTransition(); + const router = useRouter(); + + return ( + + ); +}; +export default AddNoteButton;