diff --git a/sigap-website-v2/.env.example b/sigap-website-v2/.env.example new file mode 100644 index 0000000..6937031 --- /dev/null +++ b/sigap-website-v2/.env.example @@ -0,0 +1,4 @@ +# Update these with your Supabase details from your project settings > API +# https://app.supabase.com/project/_/settings/api +NEXT_PUBLIC_SUPABASE_URL=your-project-url +NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key diff --git a/sigap-website-v2/.gitignore b/sigap-website-v2/.gitignore new file mode 100644 index 0000000..13b10b8 --- /dev/null +++ b/sigap-website-v2/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# local env files +.env*.local +.env + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/sigap-website-v2/README.md b/sigap-website-v2/README.md new file mode 100644 index 0000000..bb6a4ed --- /dev/null +++ b/sigap-website-v2/README.md @@ -0,0 +1,104 @@ + + Next.js and Supabase Starter Kit - the fastest way to build apps with Next.js and Supabase +

Next.js and Supabase Starter Kit

+
+ +

+ The fastest way to build apps with Next.js and Supabase +

+ +

+ Features · + Demo · + Deploy to Vercel · + Clone and run locally · + Feedback and issues + More Examples +

+
+ +## Features + +- Works across the entire [Next.js](https://nextjs.org) stack + - App Router + - Pages Router + - Middleware + - Client + - Server + - It just works! +- supabase-ssr. A package to configure Supabase Auth to use cookies +- Styling with [Tailwind CSS](https://tailwindcss.com) +- Components with [shadcn/ui](https://ui.shadcn.com/) +- Optional deployment with [Supabase Vercel Integration and Vercel deploy](#deploy-your-own) + - Environment variables automatically assigned to Vercel project + +## Demo + +You can view a fully working demo at [demo-nextjs-with-supabase.vercel.app](https://demo-nextjs-with-supabase.vercel.app/). + +## Deploy to Vercel + +Vercel deployment will guide you through creating a Supabase account and project. + +After installation of the Supabase integration, all relevant environment variables will be assigned to the project so the deployment is fully functioning. + +[![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvercel%2Fnext.js%2Ftree%2Fcanary%2Fexamples%2Fwith-supabase&project-name=nextjs-with-supabase&repository-name=nextjs-with-supabase&demo-title=nextjs-with-supabase&demo-description=This+starter+configures+Supabase+Auth+to+use+cookies%2C+making+the+user%27s+session+available+throughout+the+entire+Next.js+app+-+Client+Components%2C+Server+Components%2C+Route+Handlers%2C+Server+Actions+and+Middleware.&demo-url=https%3A%2F%2Fdemo-nextjs-with-supabase.vercel.app%2F&external-id=https%3A%2F%2Fgithub.com%2Fvercel%2Fnext.js%2Ftree%2Fcanary%2Fexamples%2Fwith-supabase&demo-image=https%3A%2F%2Fdemo-nextjs-with-supabase.vercel.app%2Fopengraph-image.png) + +The above will also clone the Starter kit to your GitHub, you can clone that locally and develop locally. + +If you wish to just develop locally and not deploy to Vercel, [follow the steps below](#clone-and-run-locally). + +## Clone and run locally + +1. You'll first need a Supabase project which can be made [via the Supabase dashboard](https://database.new) + +2. Create a Next.js app using the Supabase Starter template npx command + + ```bash + npx create-next-app --example with-supabase with-supabase-app + ``` + + ```bash + yarn create next-app --example with-supabase with-supabase-app + ``` + + ```bash + pnpm create next-app --example with-supabase with-supabase-app + ``` + +3. Use `cd` to change into the app's directory + + ```bash + cd with-supabase-app + ``` + +4. Rename `.env.example` to `.env.local` and update the following: + + ``` + NEXT_PUBLIC_SUPABASE_URL=[INSERT SUPABASE PROJECT URL] + NEXT_PUBLIC_SUPABASE_ANON_KEY=[INSERT SUPABASE PROJECT API ANON KEY] + ``` + + Both `NEXT_PUBLIC_SUPABASE_URL` and `NEXT_PUBLIC_SUPABASE_ANON_KEY` can be found in [your Supabase project's API settings](https://app.supabase.com/project/_/settings/api) + +5. You can now run the Next.js local development server: + + ```bash + npm run dev + ``` + + The starter kit should now be running on [localhost:3000](http://localhost:3000/). + +6. This template comes with the default shadcn/ui style initialized. If you instead want other ui.shadcn styles, delete `components.json` and [re-install shadcn/ui](https://ui.shadcn.com/docs/installation/next) + +> Check out [the docs for Local Development](https://supabase.com/docs/guides/getting-started/local-development) to also run Supabase locally. + +## Feedback and issues + +Please file feedback and issues over on the [Supabase GitHub org](https://github.com/supabase/supabase/issues/new/choose). + +## More Supabase examples + +- [Next.js Subscription Payments Starter](https://github.com/vercel/nextjs-subscription-payments) +- [Cookie-based Auth and the Next.js 13 App Router (free course)](https://youtube.com/playlist?list=PL5S4mPUpp4OtMhpnp93EFSo42iQ40XjbF) +- [Supabase Auth and the Next.js App Router](https://github.com/supabase/supabase/tree/master/examples/auth/nextjs) diff --git a/sigap-website-v2/app/(auth-pages)/_actions/forgot-password.ts b/sigap-website-v2/app/(auth-pages)/_actions/forgot-password.ts new file mode 100644 index 0000000..b38936b --- /dev/null +++ b/sigap-website-v2/app/(auth-pages)/_actions/forgot-password.ts @@ -0,0 +1,40 @@ +"use server"; + +import { createClient } from "@/utils/supabase/server"; +import { encodedRedirect } from "@/utils/utils"; +import { headers } from "next/headers"; +import { redirect } from "next/navigation"; + +export const forgotPasswordAction = async (formData: FormData) => { + const email = formData.get("email")?.toString(); + const supabase = await createClient(); + const origin = (await headers()).get("origin"); + const callbackUrl = formData.get("callbackUrl")?.toString(); + + if (!email) { + return encodedRedirect("error", "/forgot-password", "Email is required"); + } + + const { error } = await supabase.auth.resetPasswordForEmail(email, { + redirectTo: `${origin}/auth/callback?redirect_to=/protected/reset-password`, + }); + + if (error) { + console.error(error.message); + return encodedRedirect( + "error", + "/forgot-password", + "Could not reset password", + ); + } + + if (callbackUrl) { + return redirect(callbackUrl); + } + + return encodedRedirect( + "success", + "/forgot-password", + "Check your email for a link to reset your password.", + ); +}; \ No newline at end of file diff --git a/sigap-website-v2/app/(auth-pages)/_actions/reset-password.ts b/sigap-website-v2/app/(auth-pages)/_actions/reset-password.ts new file mode 100644 index 0000000..3b58b68 --- /dev/null +++ b/sigap-website-v2/app/(auth-pages)/_actions/reset-password.ts @@ -0,0 +1,43 @@ +"use server"; + +import { encodedRedirect } from "@/utils/utils"; +import { createClient } from "@/utils/supabase/server"; +import { headers } from "next/headers"; +import { redirect } from "next/navigation"; + +export const resetPasswordAction = async (formData: FormData) => { + const supabase = await createClient(); + + const password = formData.get("password") as string; + const confirmPassword = formData.get("confirmPassword") as string; + + if (!password || !confirmPassword) { + encodedRedirect( + "error", + "/protected/reset-password", + "Password and confirm password are required" + ); + } + + if (password !== confirmPassword) { + encodedRedirect( + "error", + "/protected/reset-password", + "Passwords do not match" + ); + } + + const { error } = await supabase.auth.updateUser({ + password: password, + }); + + if (error) { + encodedRedirect( + "error", + "/protected/reset-password", + "Password update failed" + ); + } + + encodedRedirect("success", "/protected/reset-password", "Password updated"); +}; diff --git a/sigap-website-v2/app/(auth-pages)/_actions/send-contact-form.ts b/sigap-website-v2/app/(auth-pages)/_actions/send-contact-form.ts new file mode 100644 index 0000000..17c5a51 --- /dev/null +++ b/sigap-website-v2/app/(auth-pages)/_actions/send-contact-form.ts @@ -0,0 +1,100 @@ +// import { createClient } from "@/utils/supabase/server"; + +// export async function sendContactEmail(formData: { +// name: string; +// email: string; +// phone: string; +// typeMessage: string; +// message: string; +// }) { +// try { +// // Initialize Supabase +// const supabase = await createClient(); +// const { resend } = useResend(); + +// // Get message type label +// const messageTypeLabel = +// typeMessageMap.get(formData.typeMessage) || "Unknown"; + +// // Save to Supabase +// const { data: contactData, error: contactError } = await supabase +// .from("contact_messages") +// .insert([ +// { +// name: formData.name, +// email: formData.email, +// phone: formData.phone, +// message_type: formData.typeMessage, +// message_type_label: messageTypeLabel, +// message: formData.message, +// status: "new", +// }, +// ]) +// .select(); + +// if (contactError) { +// console.error("Error saving contact message to Supabase:", contactError); +// return { +// success: false, +// error: "Failed to save your message. Please try again later.", +// }; +// } + +// // Render admin email template +// const adminEmailHtml = await render( +// AdminNotification({ +// name: formData.name, +// email: formData.email, +// phone: formData.phone, +// messageType: messageTypeLabel, +// message: formData.message, +// }) +// ); + +// // Send email to admin +// const { data: emailData, error: emailError } = await resend.emails.send({ +// from: "Contact Form ", +// to: ["xdamazon17@gmail.com"], +// subject: `New Contact Form Submission: ${messageTypeLabel}`, +// html: adminEmailHtml, +// }); + +// if (emailError) { +// console.error("Error sending email via Resend:", emailError); +// // Note: We don't return error here since the data is already saved to Supabase +// } + +// const userEmailHtml = await render( +// UserConfirmation({ +// name: formData.name, +// messageType: messageTypeLabel, +// message: formData.message, +// }) +// ); + +// // Send confirmation email to user +// const { data: confirmationData, error: confirmationError } = +// await resend.emails.send({ +// from: "Your Company ", +// to: [formData.email], +// subject: "Thank you for contacting us", +// html: userEmailHtml, +// }); + +// if (confirmationError) { +// console.error("Error sending confirmation email:", confirmationError); +// // Note: We don't return error here either +// } + +// return { +// success: true, +// message: "Your message has been sent successfully!", +// }; +// } catch (error) { +// console.error("Unexpected error in sendContactEmail:", error); +// return { +// success: false, +// error: "An unexpected error occurred. Please try again later.", +// }; +// } +// } \ No newline at end of file diff --git a/sigap-website-v2/app/(auth-pages)/_actions/session.ts b/sigap-website-v2/app/(auth-pages)/_actions/session.ts new file mode 100644 index 0000000..a742b1d --- /dev/null +++ b/sigap-website-v2/app/(auth-pages)/_actions/session.ts @@ -0,0 +1,37 @@ +import { createClient } from "@/utils/supabase/server"; + +export const checkSession = async () => { + const supabase = await createClient(); + + try { + const { + data: { session }, + error, + } = await supabase.auth.getSession(); + + if (error) { + return { + success: false, + error: error.message, + }; + } + + if (session) { + return { + success: true, + session, + redirectTo: "/protected/dashboard", // or your preferred authenticated route + }; + } + + return { + success: false, + message: "No active session", + }; + } catch (error) { + return { + success: false, + error: "An unexpected error occurred", + }; + } +}; diff --git a/sigap-website-v2/app/(auth-pages)/_actions/sign-in.ts b/sigap-website-v2/app/(auth-pages)/_actions/sign-in.ts new file mode 100644 index 0000000..5c6ea79 --- /dev/null +++ b/sigap-website-v2/app/(auth-pages)/_actions/sign-in.ts @@ -0,0 +1,54 @@ +"use server"; + +import { createClient } from "@/utils/supabase/server"; +import { encodedRedirect } from "@/utils/utils"; +import { redirect } from "next/navigation"; +import { checkSession } from "./session"; + +export const signInAction = async (formData: FormData) => { + const supabase = await createClient(); + const email = formData.get("email") as string; + const encodeEmail = encodeURIComponent(email); + + try { + // First, check for existing session + const { session, error: sessionError } = await checkSession(); + + // If there's an active session and the email matches + if (session && session.user.email === email) { + return { + success: true, + message: "Already logged in", + redirectTo: "/protected/dashboard", // or wherever you want to redirect logged in users + }; + } + + // If no active session or different email, proceed with OTP + const { data, error } = await supabase.auth.signInWithOtp({ + email, + options: { + shouldCreateUser: false, + }, + }); + + if (error) { + return { + success: false, + error: error.message, + redirectTo: `/verify-otp?email=${encodeEmail}`, + }; + } + + return { + success: true, + message: "OTP has been sent to your email", + redirectTo: `/verify-otp?email=${encodeEmail}`, + }; + } catch (error) { + return { + success: false, + error: "An unexpected error occurred", + redirectTo: "/sign-in", + }; + } +}; diff --git a/sigap-website-v2/app/(auth-pages)/_actions/sign-out.ts b/sigap-website-v2/app/(auth-pages)/_actions/sign-out.ts new file mode 100644 index 0000000..0137a93 --- /dev/null +++ b/sigap-website-v2/app/(auth-pages)/_actions/sign-out.ts @@ -0,0 +1,10 @@ +"use server"; + +import { createClient } from "@/utils/supabase/server"; +import { redirect } from "next/navigation"; + +export const signOutAction = async () => { + const supabase = await createClient(); + await supabase.auth.signOut(); + return redirect("/sign-in"); +}; diff --git a/sigap-website-v2/app/(auth-pages)/_actions/sign-up.ts b/sigap-website-v2/app/(auth-pages)/_actions/sign-up.ts new file mode 100644 index 0000000..a4b2ef3 --- /dev/null +++ b/sigap-website-v2/app/(auth-pages)/_actions/sign-up.ts @@ -0,0 +1,39 @@ +"use server"; + +import { createClient } from "@/utils/supabase/server"; +import { encodedRedirect } from "@/utils/utils"; +import { headers } from "next/headers"; + +export const signUpAction = async (formData: FormData) => { + const email = formData.get("email")?.toString(); + const password = formData.get("password")?.toString(); + const supabase = await createClient(); + const origin = (await headers()).get("origin"); + + if (!email || !password) { + return encodedRedirect( + "error", + "/sign-up", + "Email and password are required" + ); + } + + const { error } = await supabase.auth.signUp({ + email, + password, + options: { + emailRedirectTo: `${origin}/auth/callback`, + }, + }); + + if (error) { + console.error(error.code + " " + error.message); + return encodedRedirect("error", "/sign-up", error.message); + } else { + return encodedRedirect( + "success", + "/sign-up", + "Thanks for signing up! Please check your email for a verification link." + ); + } +}; diff --git a/sigap-website-v2/app/(auth-pages)/_actions/verify-otp.ts b/sigap-website-v2/app/(auth-pages)/_actions/verify-otp.ts new file mode 100644 index 0000000..dee6f71 --- /dev/null +++ b/sigap-website-v2/app/(auth-pages)/_actions/verify-otp.ts @@ -0,0 +1,30 @@ +import { createClient } from "@/utils/supabase/server"; +import { redirect } from "next/navigation"; + +export const verifyOtpAction = async (formData: FormData) => { + const email = formData.get("email") as string; + const token = formData.get("token") as string; + const supabase = await createClient(); + + console.log("email", email); + console.log("token", token); + + if (!email || !token) { + redirect("/error?message=Email and OTP are required"); + } + + const { + data: { session }, + error, + } = await supabase.auth.verifyOtp({ + email, + token, + type: "email", + }); + + if (error) { + return redirect(`/verify-otp?error=${encodeURIComponent(error.message)}`); + } + + return redirect("/protected/dashboard?message=OTP verified successfully"); +}; diff --git a/sigap-website-v2/app/(auth-pages)/action.ts b/sigap-website-v2/app/(auth-pages)/action.ts new file mode 100644 index 0000000..1e57bea --- /dev/null +++ b/sigap-website-v2/app/(auth-pages)/action.ts @@ -0,0 +1,57 @@ +"use server"; + +import { SignInFormData } from "@/src/models/auth/sign-in.model"; +import { authRepository } from "@/src/repositories/authentication.repository"; + +export async function signIn( + data: SignInFormData +): Promise<{ success: boolean; message: string }> { + try { + await authRepository.signIn(data); + return { success: true, message: "Check your email for the login link!" }; + } catch (error) { + console.error("Authentication error:", error); + return { + success: false, + message: + error instanceof Error + ? error.message + : "Authentication failed. Please try again.", + }; + } +} + +export async function signOut(): Promise<{ + success: boolean; + message: string; +}> { + try { + await authRepository.signOut(); + return { success: true, message: "You have been signed out." }; + } catch (error) { + console.error("Sign out error:", error); + return { + success: false, + message: + error instanceof Error + ? error.message + : "Sign out failed. Please try again.", + }; + } +} + +export async function getUser() { + try { + const user = await authRepository.getUser(); + return { success: true, user }; + } catch (error) { + console.error("Get user error:", error); + return { + success: false, + message: + error instanceof Error + ? error.message + : "Failed to get user information.", + }; + } +} diff --git a/sigap-website-v2/app/(auth-pages)/layout.tsx b/sigap-website-v2/app/(auth-pages)/layout.tsx new file mode 100644 index 0000000..7102549 --- /dev/null +++ b/sigap-website-v2/app/(auth-pages)/layout.tsx @@ -0,0 +1,17 @@ +import { redirect } from "next/navigation"; +import { checkSession } from "./_actions/session"; + +export default async function Layout({ + children, +}: { + children: React.ReactNode; +}) { + const sessionResult = await checkSession(); + + // If there's an active session, redirect to dashboard + if (sessionResult.success && sessionResult.redirectTo) { + redirect(sessionResult.redirectTo); + } + + return
{children}
; +} diff --git a/sigap-website/app/(auth-pages)/contact-us/page.tsx b/sigap-website-v2/app/(auth-pages)/sign-in/page.tsx similarity index 85% rename from sigap-website/app/(auth-pages)/contact-us/page.tsx rename to sigap-website-v2/app/(auth-pages)/sign-in/page.tsx index 74a13d0..a85abd3 100644 --- a/sigap-website/app/(auth-pages)/contact-us/page.tsx +++ b/sigap-website-v2/app/(auth-pages)/sign-in/page.tsx @@ -1,8 +1,10 @@ -import { ContactUsForm } from "@/app/(auth-pages)/_components/auth/contact-us"; -import { Button } from "@/app/_components/ui/button"; +import { SignInForm } from "@/components/auth/signin-form"; +import { Message } from "@/components/form-message"; +import { Button } from "@/components/ui/button"; import { GalleryVerticalEnd, Globe } from "lucide-react"; -export default async function ContactAdminPage() { +export default async function Login(props: { searchParams: Promise }) { + const searchParams = await props.searchParams; return (
@@ -16,7 +18,7 @@ export default async function ContactAdminPage() {
- +
diff --git a/sigap-website-v2/app/(auth-pages)/smtp-message.tsx b/sigap-website-v2/app/(auth-pages)/smtp-message.tsx new file mode 100644 index 0000000..84c21fc --- /dev/null +++ b/sigap-website-v2/app/(auth-pages)/smtp-message.tsx @@ -0,0 +1,25 @@ +import { ArrowUpRight, InfoIcon } from "lucide-react"; +import Link from "next/link"; + +export function SmtpMessage() { + return ( +
+ +
+ + Note: Emails are rate limited. Enable Custom SMTP to + increase the rate limit. + +
+ + Learn more + +
+
+
+ ); +} diff --git a/sigap-website-v2/app/(auth-pages)/verify-otp/page.tsx b/sigap-website-v2/app/(auth-pages)/verify-otp/page.tsx new file mode 100644 index 0000000..04273f4 --- /dev/null +++ b/sigap-website-v2/app/(auth-pages)/verify-otp/page.tsx @@ -0,0 +1,23 @@ +// import { GalleryVerticalEnd } from "lucide-react"; + +// export default async function VerifyOtpPage() { +// return ( +//
+//
+// +//
+//
+// +//
+//
+//
+//
+// ); +// } diff --git a/sigap-website-v2/app/auth/callback/route.ts b/sigap-website-v2/app/auth/callback/route.ts new file mode 100644 index 0000000..dd415a4 --- /dev/null +++ b/sigap-website-v2/app/auth/callback/route.ts @@ -0,0 +1,24 @@ +import { createClient } from "@/utils/supabase/server"; +import { NextResponse } from "next/server"; + +export async function GET(request: Request) { + // The `/auth/callback` route is required for the server-side auth flow implemented + // by the SSR package. It exchanges an auth code for the user's session. + // https://supabase.com/docs/guides/auth/server-side/nextjs + const requestUrl = new URL(request.url); + const code = requestUrl.searchParams.get("code"); + const origin = requestUrl.origin; + const redirectTo = requestUrl.searchParams.get("redirect_to")?.toString(); + + if (code) { + const supabase = await createClient(); + await supabase.auth.exchangeCodeForSession(code); + } + + if (redirectTo) { + return NextResponse.redirect(`${origin}${redirectTo}`); + } + + // URL to redirect to after sign up process completes + return NextResponse.redirect(`${origin}/protected`); +} diff --git a/sigap-website-v2/app/favicon.ico b/sigap-website-v2/app/favicon.ico new file mode 100644 index 0000000..718d6fe Binary files /dev/null and b/sigap-website-v2/app/favicon.ico differ diff --git a/sigap-website-v2/app/globals.css b/sigap-website-v2/app/globals.css new file mode 100644 index 0000000..373267a --- /dev/null +++ b/sigap-website-v2/app/globals.css @@ -0,0 +1,96 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 0 0% 100%; + --foreground: 240 10% 3.9%; + --card: 0 0% 100%; + --card-foreground: 240 10% 3.9%; + --popover: 0 0% 100%; + --popover-foreground: 240 10% 3.9%; + --primary: 153 60% 53%; /* Supabase green */ + --primary-foreground: 0 0% 98%; + --secondary: 240 4.8% 95.9%; + --secondary-foreground: 240 5.9% 10%; + --muted: 240 4.8% 95.9%; + --muted-foreground: 240 3.8% 46.1%; + --accent: 240 4.8% 95.9%; + --accent-foreground: 240 5.9% 10%; + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + --border: 240 5.9% 90%; + --input: 240 5.9% 90%; + --ring: 153 60% 53%; /* Matching primary */ + --radius: 0.5rem; + --chart-1: 153 60% 53%; /* Supabase green */ + --chart-2: 183 65% 50%; + --chart-3: 213 70% 47%; + --chart-4: 243 75% 44%; + --chart-5: 273 80% 41%; + --sidebar-background: 0 0% 98%; + --sidebar-foreground: 240 5.3% 26.1%; + --sidebar-primary: 240 5.9% 10%; + --sidebar-primary-foreground: 0 0% 98%; + --sidebar-accent: 240 4.8% 95.9%; + --sidebar-accent-foreground: 240 5.9% 10%; + --sidebar-border: 220 13% 91%; + --sidebar-ring: 217.2 91.2% 59.8%; + } + + .dark { + --background: 0 0% 9%; /* #171717 */ + --foreground: 210 20% 98%; + --card: 0 0% 9%; /* #171717 */ + --card-foreground: 210 20% 98%; + --popover: 0 0% 9%; /* #171717 */ + --popover-foreground: 210 20% 98%; + --primary: 153 60% 53%; /* Supabase green */ + --primary-foreground: 210 20% 98%; + --secondary: 220 8% 15%; + --secondary-foreground: 210 20% 98%; + --muted: 220 8% 15%; + --muted-foreground: 217 10% 64%; + --accent: 220 8% 15%; + --accent-foreground: 210 20% 98%; + --destructive: 0 62.8% 30.6%; + --destructive-foreground: 210 20% 98%; + --border: 220 8% 15%; + --input: 220 8% 15%; + --ring: 153 60% 53%; /* Matching primary */ + --chart-1: 153 60% 53%; /* Supabase green */ + --chart-2: 183 65% 50%; + --chart-3: 213 70% 47%; + --chart-4: 243 75% 44%; + --chart-5: 273 80% 41%; + --sidebar-background: 240 5.9% 10%; + --sidebar-foreground: 240 4.8% 95.9%; + --sidebar-primary: 224.3 76.3% 48%; + --sidebar-primary-foreground: 0 0% 100%; + --sidebar-accent: 240 3.7% 15.9%; + --sidebar-accent-foreground: 240 4.8% 95.9%; + --sidebar-border: 240 3.7% 15.9%; + --sidebar-ring: 217.2 91.2% 59.8%; + } +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + } +} + + + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} diff --git a/sigap-website-v2/app/layout.tsx b/sigap-website-v2/app/layout.tsx new file mode 100644 index 0000000..60647fa --- /dev/null +++ b/sigap-website-v2/app/layout.tsx @@ -0,0 +1,86 @@ +import DeployButton from "@/components/deploy-button"; +import { EnvVarWarning } from "@/components/env-var-warning"; +import HeaderAuth from "@/components/header-auth"; +import { ThemeSwitcher } from "@/components/theme-switcher"; +import { hasEnvVars } from "@/utils/supabase/check-env-vars"; +import { Geist } from "next/font/google"; +import { ThemeProvider } from "next-themes"; +import Link from "next/link"; +import "./globals.css"; +import ReactQueryProvider from "@/providers/react-query-provider"; +import { Toaster } from "@/components/ui/sonner"; + +const defaultUrl = process.env.VERCEL_URL + ? `https://${process.env.VERCEL_URL}` + : "http://localhost:3000"; + +export const metadata = { + metadataBase: new URL(defaultUrl), + title: "Next.js and Supabase Starter Kit", + description: "The fastest way to build apps with Next.js and Supabase", +}; + +const geistSans = Geist({ + display: "swap", + subsets: ["latin"], +}); + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + + +
+
+ {/* */} +
+ {children} + +
+ + {/* */} +
+
+
+
+ + + ); +} diff --git a/sigap-website-v2/app/opengraph-image.png b/sigap-website-v2/app/opengraph-image.png new file mode 100644 index 0000000..57595e6 Binary files /dev/null and b/sigap-website-v2/app/opengraph-image.png differ diff --git a/sigap-website-v2/app/page.tsx b/sigap-website-v2/app/page.tsx new file mode 100644 index 0000000..9144694 --- /dev/null +++ b/sigap-website-v2/app/page.tsx @@ -0,0 +1,16 @@ +import Hero from "@/components/hero"; +import ConnectSupabaseSteps from "@/components/tutorial/connect-supabase-steps"; +import SignUpUserSteps from "@/components/tutorial/sign-up-user-steps"; +import { hasEnvVars } from "@/utils/supabase/check-env-vars"; + +export default async function Home() { + return ( + <> + +
+

Next steps

+ {hasEnvVars ? : } +
+ + ); +} diff --git a/sigap-website-v2/app/protected/page.tsx b/sigap-website-v2/app/protected/page.tsx new file mode 100644 index 0000000..5508aba --- /dev/null +++ b/sigap-website-v2/app/protected/page.tsx @@ -0,0 +1,38 @@ +import FetchDataSteps from "@/components/tutorial/fetch-data-steps"; +import { createClient } from "@/utils/supabase/server"; +import { InfoIcon } from "lucide-react"; +import { redirect } from "next/navigation"; + +export default async function ProtectedPage() { + const supabase = await createClient(); + + const { + data: { user }, + } = await supabase.auth.getUser(); + + if (!user) { + return redirect("/sign-in"); + } + + return ( +
+
+
+ + This is a protected page that you can only see as an authenticated + user +
+
+
+

Your user details

+
+          {JSON.stringify(user, null, 2)}
+        
+
+
+

Next steps

+ +
+
+ ); +} diff --git a/sigap-website-v2/app/twitter-image.png b/sigap-website-v2/app/twitter-image.png new file mode 100644 index 0000000..57595e6 Binary files /dev/null and b/sigap-website-v2/app/twitter-image.png differ diff --git a/sigap-website/components.example.json b/sigap-website-v2/components.json similarity index 100% rename from sigap-website/components.example.json rename to sigap-website-v2/components.json diff --git a/sigap-website/app/(auth-pages)/_components/auth/login-form-2.tsx b/sigap-website-v2/components/auth/signin-form.tsx similarity index 77% rename from sigap-website/app/(auth-pages)/_components/auth/login-form-2.tsx rename to sigap-website-v2/components/auth/signin-form.tsx index 10e075f..42baedf 100644 --- a/sigap-website/app/(auth-pages)/_components/auth/login-form-2.tsx +++ b/sigap-website-v2/components/auth/signin-form.tsx @@ -1,16 +1,16 @@ "use client"; -import { Button } from "@/app/_components/ui/button"; -import { Input } from "@/app/_components/ui/input"; +import type React from "react"; -import { Github, Lock } from "lucide-react"; +import { Lock } from "lucide-react"; +import { Button } from "../ui/button"; +import { Input } from "../ui/input"; +import { SubmitButton } from "../submit-button"; import Link from "next/link"; -import { SubmitButton } from "../../../_components/submit-button"; +import { FormField } from "../form-field"; +import { useSignInForm } from "@/src/controller/auth/sign-in-controller"; -import { FormField } from "../../../_components/form-field"; -import { useSignInForm } from "@/hooks/use-signin"; - -export function LoginForm2({ +export function SignInForm({ className, ...props }: React.ComponentPropsWithoutRef<"form">) { @@ -18,9 +18,8 @@ export function LoginForm2({ formData, errors, isSubmitting, - setFormData, + message, handleChange, - handleSelectChange, handleSubmit, } = useSignInForm(); @@ -35,15 +34,16 @@ export function LoginForm2({

Sign in to your account

-
- {/* */} + {message} +
+ )} + +
- {/* */} +
@@ -59,12 +60,12 @@ export default async function AuthButton() { ) : (
- - {/* */} +
); } diff --git a/sigap-website/app/_components/hero.tsx b/sigap-website-v2/components/hero.tsx similarity index 93% rename from sigap-website/app/_components/hero.tsx rename to sigap-website-v2/components/hero.tsx index 8dd9cb2..6afca6b 100644 --- a/sigap-website/app/_components/hero.tsx +++ b/sigap-website-v2/components/hero.tsx @@ -1,5 +1,5 @@ -import NextLogo from "./logo/next-logo"; -import SupabaseLogo from "./logo/supabase-logo"; +import NextLogo from "./next-logo"; +import SupabaseLogo from "./supabase-logo"; export default function Header() { return ( diff --git a/sigap-website/app/_components/logo/next-logo.tsx b/sigap-website-v2/components/next-logo.tsx similarity index 100% rename from sigap-website/app/_components/logo/next-logo.tsx rename to sigap-website-v2/components/next-logo.tsx diff --git a/sigap-website/app/_components/submit-button.tsx b/sigap-website-v2/components/submit-button.tsx similarity index 89% rename from sigap-website/app/_components/submit-button.tsx rename to sigap-website-v2/components/submit-button.tsx index 7b6ac04..c1cd9f8 100644 --- a/sigap-website/app/_components/submit-button.tsx +++ b/sigap-website-v2/components/submit-button.tsx @@ -1,6 +1,6 @@ "use client"; -import { Button } from "@/app/_components/ui/button"; +import { Button } from "@/components/ui/button"; import { type ComponentProps } from "react"; import { useFormStatus } from "react-dom"; diff --git a/sigap-website/app/_components/logo/supabase-logo.tsx b/sigap-website-v2/components/supabase-logo.tsx similarity index 100% rename from sigap-website/app/_components/logo/supabase-logo.tsx rename to sigap-website-v2/components/supabase-logo.tsx diff --git a/sigap-website-v2/components/theme-switcher.tsx b/sigap-website-v2/components/theme-switcher.tsx new file mode 100644 index 0000000..d838e40 --- /dev/null +++ b/sigap-website-v2/components/theme-switcher.tsx @@ -0,0 +1,78 @@ +"use client"; + +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Laptop, Moon, Sun } from "lucide-react"; +import { useTheme } from "next-themes"; +import { useEffect, useState } from "react"; + +const ThemeSwitcher = () => { + const [mounted, setMounted] = useState(false); + const { theme, setTheme } = useTheme(); + + // useEffect only runs on the client, so now we can safely show the UI + useEffect(() => { + setMounted(true); + }, []); + + if (!mounted) { + return null; + } + + const ICON_SIZE = 16; + + return ( + + + + + + setTheme(e)} + > + + {" "} + Light + + + {" "} + Dark + + + {" "} + System + + + + + ); +}; + +export { ThemeSwitcher }; diff --git a/sigap-website/app/_components/tutorial/code-block.tsx b/sigap-website-v2/components/tutorial/code-block.tsx similarity index 100% rename from sigap-website/app/_components/tutorial/code-block.tsx rename to sigap-website-v2/components/tutorial/code-block.tsx diff --git a/sigap-website/app/_components/tutorial/connect-supabase-steps.tsx b/sigap-website-v2/components/tutorial/connect-supabase-steps.tsx similarity index 100% rename from sigap-website/app/_components/tutorial/connect-supabase-steps.tsx rename to sigap-website-v2/components/tutorial/connect-supabase-steps.tsx diff --git a/sigap-website/app/_components/tutorial/fetch-data-steps.tsx b/sigap-website-v2/components/tutorial/fetch-data-steps.tsx similarity index 98% rename from sigap-website/app/_components/tutorial/fetch-data-steps.tsx rename to sigap-website-v2/components/tutorial/fetch-data-steps.tsx index f0193fe..23c7df5 100644 --- a/sigap-website/app/_components/tutorial/fetch-data-steps.tsx +++ b/sigap-website-v2/components/tutorial/fetch-data-steps.tsx @@ -16,7 +16,7 @@ values const server = `import { createClient } from '@/utils/supabase/server' export default async function Page() { - const supabase = createClient() + const supabase = await createClient() const { data: notes } = await supabase.from('notes').select() return
{JSON.stringify(notes, null, 2)}
diff --git a/sigap-website/app/_components/tutorial/sign-up-user-steps.tsx b/sigap-website-v2/components/tutorial/sign-up-user-steps.tsx similarity index 100% rename from sigap-website/app/_components/tutorial/sign-up-user-steps.tsx rename to sigap-website-v2/components/tutorial/sign-up-user-steps.tsx diff --git a/sigap-website/app/_components/tutorial/tutorial-step.tsx b/sigap-website-v2/components/tutorial/tutorial-step.tsx similarity index 100% rename from sigap-website/app/_components/tutorial/tutorial-step.tsx rename to sigap-website-v2/components/tutorial/tutorial-step.tsx diff --git a/sigap-website/app/_components/typography/inline-code.tsx b/sigap-website-v2/components/typography/inline-code.tsx similarity index 100% rename from sigap-website/app/_components/typography/inline-code.tsx rename to sigap-website-v2/components/typography/inline-code.tsx diff --git a/sigap-website/app/_components/ui/badge.tsx b/sigap-website-v2/components/ui/badge.tsx similarity index 100% rename from sigap-website/app/_components/ui/badge.tsx rename to sigap-website-v2/components/ui/badge.tsx diff --git a/sigap-website-v2/components/ui/button.tsx b/sigap-website-v2/components/ui/button.tsx new file mode 100644 index 0000000..57c9fe4 --- /dev/null +++ b/sigap-website-v2/components/ui/button.tsx @@ -0,0 +1,56 @@ +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "@/lib/utils"; + +const buttonVariants = cva( + "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: + "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button"; + return ( + + ); + }, +); +Button.displayName = "Button"; + +export { Button, buttonVariants }; diff --git a/sigap-website/app/_components/ui/checkbox.tsx b/sigap-website-v2/components/ui/checkbox.tsx similarity index 100% rename from sigap-website/app/_components/ui/checkbox.tsx rename to sigap-website-v2/components/ui/checkbox.tsx diff --git a/sigap-website/app/_components/ui/dropdown-menu.tsx b/sigap-website-v2/components/ui/dropdown-menu.tsx similarity index 100% rename from sigap-website/app/_components/ui/dropdown-menu.tsx rename to sigap-website-v2/components/ui/dropdown-menu.tsx diff --git a/sigap-website-v2/components/ui/input.tsx b/sigap-website-v2/components/ui/input.tsx new file mode 100644 index 0000000..9d631e7 --- /dev/null +++ b/sigap-website-v2/components/ui/input.tsx @@ -0,0 +1,25 @@ +import * as React from "react"; + +import { cn } from "@/lib/utils"; + +export interface InputProps + extends React.InputHTMLAttributes {} + +const Input = React.forwardRef( + ({ className, type, ...props }, ref) => { + return ( + + ); + }, +); +Input.displayName = "Input"; + +export { Input }; diff --git a/sigap-website-v2/components/ui/label.tsx b/sigap-website-v2/components/ui/label.tsx new file mode 100644 index 0000000..84f8b0c --- /dev/null +++ b/sigap-website-v2/components/ui/label.tsx @@ -0,0 +1,26 @@ +"use client"; + +import * as React from "react"; +import * as LabelPrimitive from "@radix-ui/react-label"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "@/lib/utils"; + +const labelVariants = cva( + "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70", +); + +const Label = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, ...props }, ref) => ( + +)); +Label.displayName = LabelPrimitive.Root.displayName; + +export { Label }; diff --git a/sigap-website-v2/components/ui/sonner.tsx b/sigap-website-v2/components/ui/sonner.tsx new file mode 100644 index 0000000..452f4d9 --- /dev/null +++ b/sigap-website-v2/components/ui/sonner.tsx @@ -0,0 +1,31 @@ +"use client" + +import { useTheme } from "next-themes" +import { Toaster as Sonner } from "sonner" + +type ToasterProps = React.ComponentProps + +const Toaster = ({ ...props }: ToasterProps) => { + const { theme = "system" } = useTheme() + + return ( + + ) +} + +export { Toaster } diff --git a/sigap-website/app/_hooks/use-debounce.ts b/sigap-website-v2/hooks/use-debounce.ts similarity index 100% rename from sigap-website/app/_hooks/use-debounce.ts rename to sigap-website-v2/hooks/use-debounce.ts diff --git a/sigap-website/app/_hooks/use-mobile.tsx b/sigap-website-v2/hooks/use-mobile.tsx similarity index 100% rename from sigap-website/app/_hooks/use-mobile.tsx rename to sigap-website-v2/hooks/use-mobile.tsx diff --git a/sigap-website/app/_hooks/use-navigations.ts b/sigap-website-v2/hooks/use-navigations.ts similarity index 100% rename from sigap-website/app/_hooks/use-navigations.ts rename to sigap-website-v2/hooks/use-navigations.ts diff --git a/sigap-website/app/_hooks/use-resend.ts b/sigap-website-v2/hooks/use-resend.ts similarity index 100% rename from sigap-website/app/_hooks/use-resend.ts rename to sigap-website-v2/hooks/use-resend.ts diff --git a/sigap-website/app/_hooks/use-toast.ts b/sigap-website-v2/hooks/use-toast.ts similarity index 99% rename from sigap-website/app/_hooks/use-toast.ts rename to sigap-website-v2/hooks/use-toast.ts index 234fe47..02e111d 100644 --- a/sigap-website/app/_hooks/use-toast.ts +++ b/sigap-website-v2/hooks/use-toast.ts @@ -6,7 +6,7 @@ import * as React from "react" import type { ToastActionElement, ToastProps, -} from "@/app/_components/ui/toast" +} from "@/components/ui/toast" const TOAST_LIMIT = 1 const TOAST_REMOVE_DELAY = 1000000 diff --git a/sigap-website-v2/lib/utils.ts b/sigap-website-v2/lib/utils.ts new file mode 100644 index 0000000..a5ef193 --- /dev/null +++ b/sigap-website-v2/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx"; +import { twMerge } from "tailwind-merge"; + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)); +} diff --git a/sigap-website-v2/middleware.ts b/sigap-website-v2/middleware.ts new file mode 100644 index 0000000..53428f8 --- /dev/null +++ b/sigap-website-v2/middleware.ts @@ -0,0 +1,20 @@ +import { type NextRequest } from "next/server"; +import { updateSession } from "@/utils/supabase/middleware"; + +export async function middleware(request: NextRequest) { + return await updateSession(request); +} + +export const config = { + matcher: [ + /* + * Match all request paths except: + * - _next/static (static files) + * - _next/image (image optimization files) + * - favicon.ico (favicon file) + * - images - .svg, .png, .jpg, .jpeg, .gif, .webp + * Feel free to modify this pattern to include more paths. + */ + "/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)", + ], +}; diff --git a/sigap-website-v2/next.config.ts b/sigap-website-v2/next.config.ts new file mode 100644 index 0000000..e9ffa30 --- /dev/null +++ b/sigap-website-v2/next.config.ts @@ -0,0 +1,7 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ +}; + +export default nextConfig; diff --git a/sigap-website-v2/package-lock.json b/sigap-website-v2/package-lock.json new file mode 100644 index 0000000..d8dce0c --- /dev/null +++ b/sigap-website-v2/package-lock.json @@ -0,0 +1,4358 @@ +{ + "name": "sigap-website-v2", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@prisma/client": "^6.4.1", + "@radix-ui/react-checkbox": "^1.1.1", + "@radix-ui/react-dropdown-menu": "^2.1.1", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-slot": "^1.1.0", + "@supabase/ssr": "latest", + "@supabase/supabase-js": "latest", + "@tabler/icons-react": "^3.30.0", + "@tanstack/react-query": "^5.66.9", + "autoprefixer": "10.4.20", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "lucide-react": "^0.468.0", + "next": "latest", + "next-themes": "^0.4.4", + "prettier": "^3.3.3", + "react": "19.0.0", + "react-dom": "19.0.0", + "sonner": "^2.0.1" + }, + "devDependencies": { + "@types/node": "^22.10.2", + "@types/react": "^19.0.2", + "@types/react-dom": "19.0.2", + "postcss": "8.4.49", + "prisma": "^6.4.1", + "tailwind-merge": "^2.5.2", + "tailwindcss": "3.4.17", + "tailwindcss-animate": "^1.0.7", + "ts-node": "^10.9.2", + "typescript": "^5.7.2" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", + "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", + "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", + "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", + "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", + "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", + "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", + "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", + "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", + "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", + "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", + "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", + "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", + "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", + "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", + "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", + "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", + "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", + "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", + "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", + "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", + "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", + "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", + "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", + "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", + "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", + "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "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==", + "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==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@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==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", + "license": "MIT" + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "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.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "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-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "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/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "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/@next/env": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.0.tgz", + "integrity": "sha512-eMgJu1RBXxxqqnuRJQh5RozhskoNUDHBFybvi+Z+yK9qzKeG7dadhv/Vp1YooSZmCnegf7JxWuapV77necLZNA==", + "license": "MIT" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.2.0.tgz", + "integrity": "sha512-rlp22GZwNJjFCyL7h5wz9vtpBVuCt3ZYjFWpEPBGzG712/uL1bbSkS675rVAUCRZ4hjoTJ26Q7IKhr5DfJrHDA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.2.0.tgz", + "integrity": "sha512-DiU85EqSHogCz80+sgsx90/ecygfCSGl5P3b4XDRVZpgujBm5lp4ts7YaHru7eVTyZMjHInzKr+w0/7+qDrvMA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.2.0.tgz", + "integrity": "sha512-VnpoMaGukiNWVxeqKHwi8MN47yKGyki5q+7ql/7p/3ifuU2341i/gDwGK1rivk0pVYbdv5D8z63uu9yMw0QhpQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.2.0.tgz", + "integrity": "sha512-ka97/ssYE5nPH4Qs+8bd8RlYeNeUVBhcnsNUmFM6VWEob4jfN9FTr0NBhXVi1XEJpj3cMfgSRW+LdE3SUZbPrw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.2.0.tgz", + "integrity": "sha512-zY1JduE4B3q0k2ZCE+DAF/1efjTXUsKP+VXRtrt/rJCTgDlUyyryx7aOgYXNc1d8gobys/Lof9P9ze8IyRDn7Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.2.0.tgz", + "integrity": "sha512-QqvLZpurBD46RhaVaVBepkVQzh8xtlUN00RlG4Iq1sBheNugamUNPuZEH1r9X1YGQo1KqAe1iiShF0acva3jHQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.2.0.tgz", + "integrity": "sha512-ODZ0r9WMyylTHAN6pLtvUtQlGXBL9voljv6ujSlcsjOxhtXPI1Ag6AhZK0SE8hEpR1374WZZ5w33ChpJd5fsjw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.2.0.tgz", + "integrity": "sha512-8+4Z3Z7xa13NdUuUAcpVNA6o76lNPniBd9Xbo02bwXQXnZgFvEopwY2at5+z7yHl47X9qbZpvwatZ2BRo3EdZw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@prisma/client": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-6.4.1.tgz", + "integrity": "sha512-A7Mwx44+GVZVexT5e2GF/WcKkEkNNKbgr059xpr5mn+oUm2ZW1svhe+0TRNBwCdzhfIZ+q23jEgsNPvKD9u+6g==", + "hasInstallScript": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "peerDependencies": { + "prisma": "*", + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "prisma": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/@prisma/debug": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-6.4.1.tgz", + "integrity": "sha512-Q9xk6yjEGIThjSD8zZegxd5tBRNHYd13GOIG0nLsanbTXATiPXCLyvlYEfvbR2ft6dlRsziQXfQGxAgv7zcMUA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/engines": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-6.4.1.tgz", + "integrity": "sha512-KldENzMHtKYwsOSLThghOIdXOBEsfDuGSrxAZjMnimBiDKd3AE4JQ+Kv+gBD/x77WoV9xIPf25GXMWffXZ17BA==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.4.1", + "@prisma/engines-version": "6.4.0-29.a9055b89e58b4b5bfb59600785423b1db3d0e75d", + "@prisma/fetch-engine": "6.4.1", + "@prisma/get-platform": "6.4.1" + } + }, + "node_modules/@prisma/engines-version": { + "version": "6.4.0-29.a9055b89e58b4b5bfb59600785423b1db3d0e75d", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-6.4.0-29.a9055b89e58b4b5bfb59600785423b1db3d0e75d.tgz", + "integrity": "sha512-Xq54qw55vaCGrGgIJqyDwOq0TtjZPJEWsbQAHugk99hpDf2jcEeQhUcF+yzEsSqegBaDNLA4IC8Nn34sXmkiTQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@prisma/fetch-engine": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-6.4.1.tgz", + "integrity": "sha512-uZ5hVeTmDspx7KcaRCNoXmcReOD+84nwlO2oFvQPRQh9xiFYnnUKDz7l9bLxp8t4+25CsaNlgrgilXKSQwrIGQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.4.1", + "@prisma/engines-version": "6.4.0-29.a9055b89e58b4b5bfb59600785423b1db3d0e75d", + "@prisma/get-platform": "6.4.1" + } + }, + "node_modules/@prisma/get-platform": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-6.4.1.tgz", + "integrity": "sha512-gXqZaDI5scDkBF8oza7fOD3Q3QMD0e0rBynlzDDZdTWbWmzjuW58PRZtj+jkvKje2+ZigCWkH8SsWZAsH6q1Yw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/debug": "6.4.1" + } + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", + "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.2.tgz", + "integrity": "sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.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-checkbox": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.4.tgz", + "integrity": "sha512-wP0CPAHq+P5I4INKe3hJrIa1WoNqqrejzW+zoU0rOvo1b9gDEJJFl2rYfO1PYJUQCc2H1WZxIJmyv9BS8i5fLw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-use-size": "1.1.0" + }, + "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-collection": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.2.tgz", + "integrity": "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.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-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "license": "MIT", + "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-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "license": "MIT", + "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.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", + "integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==", + "license": "MIT", + "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-dismissable-layer": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz", + "integrity": "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-escape-keydown": "1.1.0" + }, + "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-dropdown-menu": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.6.tgz", + "integrity": "sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-menu": "2.1.6", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "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-focus-guards": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", + "integrity": "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==", + "license": "MIT", + "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-focus-scope": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.2.tgz", + "integrity": "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "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-id": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", + "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "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-label": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.2.tgz", + "integrity": "sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.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-menu": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.6.tgz", + "integrity": "sha512-tBBb5CXDJW3t2mo9WlO7r6GTmWV0F0uzHZVFmlRmYpiSK1CDU5IKojP1pm7oknpBOrFZx/YgBRW9oorPO2S/Lg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collection": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.5", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.2", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.2", + "@radix-ui/react-portal": "1.1.4", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-roving-focus": "1.1.2", + "@radix-ui/react-slot": "1.1.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "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-popper": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.2.tgz", + "integrity": "sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-rect": "1.1.0", + "@radix-ui/react-use-size": "1.1.0", + "@radix-ui/rect": "1.1.0" + }, + "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-portal": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.4.tgz", + "integrity": "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "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-presence": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", + "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "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-primitive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", + "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.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-roving-focus": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.2.tgz", + "integrity": "sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collection": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "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-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "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.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", + "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", + "license": "MIT", + "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-controllable-state": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", + "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "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-escape-keydown": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", + "integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.0" + }, + "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-layout-effect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", + "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", + "license": "MIT", + "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-previous": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.0.tgz", + "integrity": "sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==", + "license": "MIT", + "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-rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", + "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.0" + }, + "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-size": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", + "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "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/rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", + "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==", + "license": "MIT" + }, + "node_modules/@supabase/auth-js": { + "version": "2.68.0", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.68.0.tgz", + "integrity": "sha512-odG7nb7aOmZPUXk6SwL2JchSsn36Ppx11i2yWMIc/meUO2B2HK9YwZHPK06utD9Ql9ke7JKDbwGin/8prHKxxQ==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.4.4.tgz", + "integrity": "sha512-WL2p6r4AXNGwop7iwvul2BvOtuJ1YQy8EbOd0dhG1oN1q8el/BIRSFCFnWAMM/vJJlHWLi4ad22sKbKr9mvjoA==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/node-fetch": { + "version": "2.6.15", + "resolved": "https://registry.npmjs.org/@supabase/node-fetch/-/node-fetch-2.6.15.tgz", + "integrity": "sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==", + "license": "MIT", + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + } + }, + "node_modules/@supabase/postgrest-js": { + "version": "1.19.2", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-1.19.2.tgz", + "integrity": "sha512-MXRbk4wpwhWl9IN6rIY1mR8uZCCG4MZAEji942ve6nMwIqnBgBnZhZlON6zTTs6fgveMnoCILpZv1+K91jN+ow==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14", + "@types/phoenix": "^1.5.4", + "@types/ws": "^8.5.10", + "ws": "^8.18.0" + } + }, + "node_modules/@supabase/ssr": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/@supabase/ssr/-/ssr-0.5.2.tgz", + "integrity": "sha512-n3plRhr2Bs8Xun1o4S3k1CDv17iH5QY9YcoEvXX3bxV1/5XSasA0mNXYycFmADIdtdE6BG9MRjP5CGIs8qxC8A==", + "license": "MIT", + "dependencies": { + "@types/cookie": "^0.6.0", + "cookie": "^0.7.0" + }, + "peerDependencies": { + "@supabase/supabase-js": "^2.43.4" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.7.1.tgz", + "integrity": "sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA==", + "license": "MIT", + "dependencies": { + "@supabase/node-fetch": "^2.6.14" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.49.1", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.49.1.tgz", + "integrity": "sha512-lKaptKQB5/juEF5+jzmBeZlz69MdHZuxf+0f50NwhL+IE//m4ZnOeWlsKRjjsM0fVayZiQKqLvYdBn0RLkhGiQ==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.68.0", + "@supabase/functions-js": "2.4.4", + "@supabase/node-fetch": "2.6.15", + "@supabase/postgrest-js": "1.19.2", + "@supabase/realtime-js": "2.11.2", + "@supabase/storage-js": "2.7.1" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@tabler/icons": { + "version": "3.30.0", + "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.30.0.tgz", + "integrity": "sha512-c8OKLM48l00u9TFbh2qhSODMONIzML8ajtCyq95rW8vzkWcBrKRPM61tdkThz2j4kd5u17srPGIjqdeRUZdfdw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/codecalm" + } + }, + "node_modules/@tabler/icons-react": { + "version": "3.30.0", + "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-3.30.0.tgz", + "integrity": "sha512-9KZ9D1UNAyjlLkkYp2HBPHdf6lAJ2aelDqh8YYAnnmLF3xwprWKxxW8+zw5jlI0IwdfN4XFFuzqePkaw+DpIOg==", + "license": "MIT", + "dependencies": { + "@tabler/icons": "3.30.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/codecalm" + }, + "peerDependencies": { + "react": ">= 16" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.66.4", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.66.4.tgz", + "integrity": "sha512-skM/gzNX4shPkqmdTCSoHtJAPMTtmIJNS0hE+xwTTUVYwezArCT34NMermABmBVUg5Ls5aiUXEDXfqwR1oVkcA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.66.9", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.66.9.tgz", + "integrity": "sha512-NRI02PHJsP5y2gAuWKP+awamTIBFBSKMnO6UVzi03GTclmHHHInH5UzVgzi5tpu4+FmGfsdT7Umqegobtsp23A==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.66.4" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/cookie": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", + "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.10.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", + "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", + "license": "MIT", + "dependencies": { + "undici-types": "~6.20.0" + } + }, + "node_modules/@types/phoenix": { + "version": "1.6.6", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.6.tgz", + "integrity": "sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.0.10", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.10.tgz", + "integrity": "sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.0.2", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.2.tgz", + "integrity": "sha512-c1s+7TKFaDRRxr1TxccIX2u7sfCnc3RxkVyBIUA2lCpyqCF+QoAwQ/CBg7bsMdVwP120HEH143VQezKtef5nCg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@types/ws": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.14.tgz", + "integrity": "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "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==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.20", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", + "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.23.3", + "caniuse-lite": "^1.0.30001646", + "fraction.js": "^4.3.7", + "normalize-range": "^0.1.2", + "picocolors": "^1.0.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", + "update-browserslist-db": "^1.1.1" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001701", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001701.tgz", + "integrity": "sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "optional": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "optional": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.109", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.109.tgz", + "integrity": "sha512-AidaH9JETVRr9DIPGfp1kAarm/W6hRJTPuCnkF+2MqhF4KaAgRIcBc8nvjk+YMXZhwfISof/7WG29eS4iGxQLQ==", + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", + "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.0", + "@esbuild/android-arm": "0.25.0", + "@esbuild/android-arm64": "0.25.0", + "@esbuild/android-x64": "0.25.0", + "@esbuild/darwin-arm64": "0.25.0", + "@esbuild/darwin-x64": "0.25.0", + "@esbuild/freebsd-arm64": "0.25.0", + "@esbuild/freebsd-x64": "0.25.0", + "@esbuild/linux-arm": "0.25.0", + "@esbuild/linux-arm64": "0.25.0", + "@esbuild/linux-ia32": "0.25.0", + "@esbuild/linux-loong64": "0.25.0", + "@esbuild/linux-mips64el": "0.25.0", + "@esbuild/linux-ppc64": "0.25.0", + "@esbuild/linux-riscv64": "0.25.0", + "@esbuild/linux-s390x": "0.25.0", + "@esbuild/linux-x64": "0.25.0", + "@esbuild/netbsd-arm64": "0.25.0", + "@esbuild/netbsd-x64": "0.25.0", + "@esbuild/openbsd-arm64": "0.25.0", + "@esbuild/openbsd-x64": "0.25.0", + "@esbuild/sunos-x64": "0.25.0", + "@esbuild/win32-arm64": "0.25.0", + "@esbuild/win32-ia32": "0.25.0", + "@esbuild/win32-x64": "0.25.0" + } + }, + "node_modules/esbuild-register": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/esbuild-register/-/esbuild-register-3.6.0.tgz", + "integrity": "sha512-H2/S7Pm8a9CL1uhp9OvjwrBh5Pvx0H8qVOxNu8Wed9Y7qv56MPtq+GGM8RJpq6glYJn9Wspr8uw7l55uyinNeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "peerDependencies": { + "esbuild": ">=0.12 <1" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fraction.js": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", + "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "patreon", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT", + "optional": true + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/lucide-react": { + "version": "0.468.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.468.0.tgz", + "integrity": "sha512-6koYRhnM2N0GGZIdXzSeiNwguv1gt/FAjZOiPl76roBi3xKEXa4WmfpxgQwTTL4KipXjefrnf3oV4IsYhi4JFA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.8", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz", + "integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/next": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/next/-/next-15.2.0.tgz", + "integrity": "sha512-VaiM7sZYX8KIAHBrRGSFytKknkrexNfGb8GlG6e93JqueCspuGte8i4ybn8z4ww1x3f2uzY4YpTaBEW4/hvsoQ==", + "license": "MIT", + "dependencies": { + "@next/env": "15.2.0", + "@swc/counter": "0.1.3", + "@swc/helpers": "0.5.15", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "15.2.0", + "@next/swc-darwin-x64": "15.2.0", + "@next/swc-linux-arm64-gnu": "15.2.0", + "@next/swc-linux-arm64-musl": "15.2.0", + "@next/swc-linux-x64-gnu": "15.2.0", + "@next/swc-linux-x64-musl": "15.2.0", + "@next/swc-win32-arm64-msvc": "15.2.0", + "@next/swc-win32-x64-msvc": "15.2.0", + "sharp": "^0.33.5" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/next-themes": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.4.tgz", + "integrity": "sha512-LDQ2qIOJF0VnuVrrMSMLrWGjRMkq+0mpgl6e0juCLqdJ+oo8Q84JRWT6Wh11VDQKkMMe+dVzDKLWs5n87T+PkQ==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/next/node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.6", + "picocolors": "^1.0.0", + "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/node-releases": { + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-range": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz", + "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", + "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.7", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", + "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", + "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.0.0", + "yaml": "^2.3.4" + }, + "engines": { + "node": ">= 14" + }, + "peerDependencies": { + "postcss": ">=8.0.9", + "ts-node": ">=9.0.0" + }, + "peerDependenciesMeta": { + "postcss": { + "optional": true + }, + "ts-node": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "node_modules/prettier": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.2.tgz", + "integrity": "sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/prisma": { + "version": "6.4.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-6.4.1.tgz", + "integrity": "sha512-q2uJkgXnua/jj66mk6P9bX/zgYJFI/jn4Yp0aS6SPRrjH/n6VyOV7RDe1vHD0DX8Aanx4MvgmUPPoYnR6MJnPg==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@prisma/engines": "6.4.1", + "esbuild": ">=0.12 <1", + "esbuild-register": "3.6.0" + }, + "bin": { + "prisma": "build/index.js" + }, + "engines": { + "node": ">=18.18" + }, + "optionalDependencies": { + "fsevents": "2.3.3" + }, + "peerDependencies": { + "typescript": ">=5.1.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", + "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.0.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", + "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.25.0" + }, + "peerDependencies": { + "react": "^19.0.0" + } + }, + "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==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", + "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", + "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==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/sonner": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.1.tgz", + "integrity": "sha512-FRBphaehZ5tLdLcQ8g2WOIRE+Y7BCfWi5Zyd8bCvBjiW8TxxAyoWZIxS661Yz6TGPqFQ4VLzOF89WEYhfynSFQ==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { + "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } + } + }, + "node_modules/sucrase": { + "version": "3.35.0", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", + "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "glob": "^10.3.10", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwind-merge": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", + "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.6", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss-animate": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "license": "MIT" + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", + "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/webidl-conversions": { + "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" + }, + "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", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/yaml": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", + "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + } + } +} diff --git a/sigap-website-v2/package.json b/sigap-website-v2/package.json new file mode 100644 index 0000000..d01c4ce --- /dev/null +++ b/sigap-website-v2/package.json @@ -0,0 +1,45 @@ +{ + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "db:seed": "npx prisma db seed" + }, + "prisma": { + "seed": "ts-node prisma/seed.ts" + }, + "dependencies": { + "@prisma/client": "^6.4.1", + "@radix-ui/react-checkbox": "^1.1.1", + "@radix-ui/react-dropdown-menu": "^2.1.1", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-slot": "^1.1.0", + "@supabase/ssr": "latest", + "@supabase/supabase-js": "latest", + "@tabler/icons-react": "^3.30.0", + "@tanstack/react-query": "^5.66.9", + "autoprefixer": "10.4.20", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "lucide-react": "^0.468.0", + "next": "latest", + "next-themes": "^0.4.4", + "prettier": "^3.3.3", + "react": "19.0.0", + "react-dom": "19.0.0", + "sonner": "^2.0.1" + }, + "devDependencies": { + "@types/node": "^22.10.2", + "@types/react": "^19.0.2", + "@types/react-dom": "19.0.2", + "postcss": "8.4.49", + "prisma": "^6.4.1", + "tailwind-merge": "^2.5.2", + "tailwindcss": "3.4.17", + "tailwindcss-animate": "^1.0.7", + "ts-node": "^10.9.2", + "typescript": "^5.7.2" + } +} diff --git a/sigap-website-v2/postcss.config.js b/sigap-website-v2/postcss.config.js new file mode 100644 index 0000000..12a703d --- /dev/null +++ b/sigap-website-v2/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/sigap-website-v2/prisma/data/crime-category.ts b/sigap-website-v2/prisma/data/crime-category.ts new file mode 100644 index 0000000..75f5432 --- /dev/null +++ b/sigap-website-v2/prisma/data/crime-category.ts @@ -0,0 +1,230 @@ + +const crimeCategories = [ + { + name: "TERHADAP KETERTIBAN UMUM", + description: + "Tindak pidana yang mengganggu ketertiban dan kenyamanan masyarakat secara umum.", + }, + { + name: "MEMBAHAYAKAN KAM UMUM", + description: + "Kejahatan yang berpotensi membahayakan keamanan dan keselamatan masyarakat.", + }, + { + name: "PEMBAKARAN", + description: + "Tindakan membakar properti atau bangunan secara sengaja yang dapat membahayakan orang lain.", + }, + { + name: "KEBAKARAN / MELETUS", + description: + "Kejadian kebakaran atau ledakan yang disebabkan oleh kelalaian atau tindakan kriminal.", + }, + { + name: "MEMBER SUAP", + description: + "Pemberian sesuatu kepada pejabat untuk mempengaruhi keputusan atau tindakan tertentu.", + }, + { + name: "SUMPAH PALSU", + description: + "Memberikan keterangan palsu di bawah sumpah, biasanya dalam proses hukum.", + }, + { + name: "PEMALSUAN MATERAI", + description: + "Tindakan memalsukan materai resmi dengan tujuan merugikan pihak lain.", + }, + { + name: "PEMALSUAN SURAT", + description: + "Membuat, mengubah, atau memalsukan surat yang memiliki kekuatan hukum.", + }, + { + name: "PERZINAHAN", + description: + "Hubungan seksual di luar pernikahan yang melanggar norma hukum dan sosial.", + }, + { + name: "PERKOSAAN", + description: "Tindak pemaksaan hubungan seksual tanpa persetujuan korban.", + }, + { + name: "PERJUDIAN", + description: + "Segala bentuk kegiatan taruhan atau perjudian yang melanggar hukum.", + }, + { + name: "PENGHINAAN", + description: "Ucapan atau tindakan yang merendahkan martabat seseorang.", + }, + { + name: "PENCULIKAN", + description: + "Mengambil atau menahan seseorang secara paksa dengan tujuan tertentu.", + }, + { + name: "PERBUATAN TIDAK MENYENANGKAN", + description: + "Tindakan yang mengakibatkan ketidaknyamanan atau kerugian emosional pada orang lain.", + }, + { + name: "PEMBUNUHAN", + description: "Menghilangkan nyawa seseorang secara sengaja.", + }, + { + name: "PENGANIAYAAN RINGAN", + description: + "Tindakan kekerasan yang mengakibatkan luka ringan pada korban.", + }, + { + name: "PENGANIAYAAN BERAT", + description: "Kekerasan yang mengakibatkan luka serius atau kematian.", + }, + { + name: "KELALAIAN AKIBATKAN ORANG MATI", + description: "Kelalaian yang menyebabkan kematian seseorang.", + }, + { + name: "KELALAIAN AKIBATKAN ORANG LUKA", + description: "Kelalaian yang mengakibatkan luka pada orang lain.", + }, + { + name: "PENCURIAN BIASA", + description: "Mengambil barang milik orang lain tanpa izin.", + }, + { + name: "CURAT", + description: + "Pencurian dengan pemberatan, seperti pembobolan rumah atau kendaraan.", + }, + { + name: "CURINGAN", + description: "Pencurian ringan dengan nilai kerugian yang kecil.", + }, + { name: "CURAS", description: "Pencurian dengan kekerasan terhadap korban." }, + { name: "CURANMOR", description: "Pencurian kendaraan bermotor." }, + { + name: "PENGEROYOKAN", + description: + "Penyerangan secara bersama-sama terhadap satu atau beberapa orang.", + }, + { + name: "PREMANISME", + description: + "Tindakan kekerasan, pemerasan, atau ancaman oleh kelompok tertentu untuk menguasai wilayah.", + }, + { + name: "PEMERASAN DAN PENGANCAMAN", + description: + "Tindakan meminta sesuatu dengan ancaman kekerasan atau pengungkapan informasi merugikan.", + }, + { + name: "PENGGELAPAN", + description: + "Mengambil barang atau uang yang dipercayakan untuk kepentingan pribadi.", + }, + { + name: "PENIPUAN", + description: + "Menipu orang lain dengan tujuan mendapatkan keuntungan secara melawan hukum.", + }, + { + name: "PENGRUSAKAN", + description: "Merusak properti orang lain secara sengaja.", + }, + { + name: "KENAKALAN REMAJA", + description: + "Perilaku menyimpang oleh remaja yang dapat meresahkan masyarakat.", + }, + { + name: "MENERIMA SUAP", + description: + "Menerima sesuatu dengan imbalan pengaruh keputusan atau tindakan tertentu.", + }, + { + name: "PENADAHAN", + description: "Menyimpan atau menjual barang hasil kejahatan.", + }, + { + name: "PEKERJAKAN ANAK", + description: + "Mempekerjakan anak di bawah umur dalam pekerjaan yang melanggar hukum.", + }, + { + name: "AGRARIA", + description: "Kejahatan terkait sengketa tanah dan sumber daya agraria.", + }, + { + name: "PERADILAN ANAK", + description: "Tindak pidana yang melibatkan anak dalam proses peradilan.", + }, + { + name: "PERLINDUNGAN ANAK", + description: + "Kejahatan yang melanggar hak-hak anak dan kesejahteraan mereka.", + }, + { + name: "PKDRT", + description: + "Tindak kekerasan dalam rumah tangga yang merugikan anggota keluarga.", + }, + { + name: "PERLINDUNGAN TKI", + description: + "Kejahatan yang melibatkan pelanggaran terhadap hak Tenaga Kerja Indonesia di luar negeri.", + }, + { + name: "PERLINDUNGAN SAKSI – KORBAN", + description: + "Tindakan yang mengancam keselamatan saksi atau korban dalam proses hukum.", + }, + { + name: "PTPPO", + description: + "Perdagangan orang, termasuk eksploitasi tenaga kerja dan seksual.", + }, + { + name: "PORNOGRAFI", + description: + "Produksi, distribusi, atau konsumsi materi pornografi yang melanggar hukum.", + }, + { + name: "SISTEM PERADILAN ANAK", + description: + "Pelaksanaan hukum dan keadilan yang berkaitan dengan anak sebagai pelaku kejahatan.", + }, + { + name: "PENYELENGGARAN PEMILU", + description: + "Kejahatan yang mengganggu proses pemilihan umum, seperti kecurangan suara.", + }, + { + name: "PEMERINTAH DAERAH", + description: + "Tindak pidana yang dilakukan oleh atau melibatkan aparat pemerintah daerah.", + }, + { + name: "KEIMIGRASIAN", + description: + "Pelanggaran hukum yang terkait dengan masuk dan keluarnya orang dari suatu negara.", + }, + { + name: "EKSTRADISI", + description: + "Proses penyerahan tersangka atau terpidana ke negara lain untuk diadili.", + }, + { + name: "LAHGUN SENPI/HANDAK/SAJAM", + description: + "Penyalahgunaan senjata api, bahan peledak, atau senjata tajam.", + }, + { + name: "PIDUM LAINNYA", + description: + "Tindak pidana umum lainnya yang tidak tercakup dalam kategori di atas.", + }, +]; + +export default crimeCategories; diff --git a/sigap-website-v2/prisma/data/nav.ts b/sigap-website-v2/prisma/data/nav.ts new file mode 100644 index 0000000..333a69d --- /dev/null +++ b/sigap-website-v2/prisma/data/nav.ts @@ -0,0 +1,294 @@ +import { + IconHome, + IconAlertTriangle, + IconSettings, + IconMap, + IconDatabase, + IconUsers, + IconMessageCircle, + IconMenu2, + IconAlbum, + IconMusicBolt, + IconCommand, + IconFrame, + IconChartPie, + IconRobot, + IconSearch, + IconDashboard, + IconRobotFace, + IconGavel, + IconMapPin2, + IconSlice, + IconWorldBolt, + IconWorld, + IconPin, + IconMapPin, + IconLayersDifference, + IconFriends, + IconDna, + IconDna2, + IconUsersGroup, + IconNavigation, + IconApps, +} from "@tabler/icons-react"; + +export const navData = { + user: { + name: "user", + email: "m@example.com", + avatar: "/avatars/shadcn.jpg", + }, + teams: [ + { + name: "Acme Inc", + icon: IconAlbum, + plan: "Enterprise", + }, + { + name: "Acme Corp.", + icon: IconMusicBolt, + plan: "Startup", + }, + { + name: "Evil Corp.", + icon: IconCommand, + plan: "Free", + }, + ], + NavPreMain: [ + { + title: "Welcome", + url: "/protected/welcome", + icon: IconHome, + }, + { + title: "Search", + url: "/search", + icon: IconSearch, + }, + { + title: "Sigap AI", + url: "/protected/sigap-ai", + icon: IconRobotFace, + }, + ], + navMain: [ + { + title: "Dashboard", + url: "/protected/dashboard", + slug: "dashboard", + orderSeq: 1, + icon: IconApps, + isActive: true, + subItems: [], + }, + { + title: "Crime Management", + url: "/crime-management", + slug: "crime-management", + orderSeq: 2, + icon: IconGavel, + isActive: true, + subItems: [ + { + title: "Crime Overview", + url: "/protected/crime-management/crime-overview", + slug: "crime-overview", + icon: IconMapPin2, + orderSeq: 1, + isActive: true, + }, + { + title: "Crime Categories", + url: "/crime-management/crime-categories", + slug: "crime-categories", + icon: IconSlice, + orderSeq: 2, + isActive: true, + }, + { + title: "Cases", + url: "/crime-management/crime-cases", + slug: "crime-cases", + icon: IconAlertTriangle, + orderSeq: 3, + isActive: true, + subSubItems: [ + { + title: "New Case", + url: "/crime-management/crime-cases/case-new", + slug: "new-case", + icon: IconAlertTriangle, + orderSeq: 1, + isActive: true, + }, + { + title: "Active Cases", + url: "/crime-management/crime-cases/case-active", + slug: "active-cases", + icon: IconAlertTriangle, + orderSeq: 2, + isActive: true, + }, + { + title: "Resolved Cases", + url: "/crime-management/crime-cases/case-closed", + slug: "resolved-cases", + icon: IconAlertTriangle, + orderSeq: 3, + isActive: true, + }, + ], + }, + ], + }, + { + title: "Geographic Data", + url: "/geographic-data", + slug: "geographic-data", + orderSeq: 3, + icon: IconWorld, + isActive: true, + subItems: [ + { + title: "Locations", + url: "/geographic-data/locations", + slug: "locations", + icon: IconMapPin, + orderSeq: 1, + isActive: true, + subSubItems: [ + { + title: "Cities", + url: "/geographic-data/cities", + slug: "cities", + icon: IconMap, + orderSeq: 1, + isActive: true, + }, + { + title: "Districts", + url: "/geographic-data/districts", + slug: "districts", + icon: IconMap, + orderSeq: 2, + isActive: true, + }, + ], + }, + { + title: "Geographic Info", + url: "/geographic-data/geographic-info", + slug: "geographic-info", + icon: IconLayersDifference, + orderSeq: 3, + isActive: true, + }, + ], + }, + { + title: "Demographics", + url: "/demographics", + slug: "demographics", + orderSeq: 4, + icon: IconFriends, + isActive: true, + subItems: [ + { + title: "Demographics Data", + url: "/demographics/demographics-data", + slug: "demographics-data", + icon: IconDna2, + orderSeq: 1, + isActive: true, + }, + ], + }, + { + title: "User Management", + url: "/user-management", + slug: "user-management", + orderSeq: 5, + icon: IconUsers, + isActive: true, + subItems: [ + { + title: "Users", + url: "/protected/user-management/users", + slug: "users", + icon: IconUsersGroup, + orderSeq: 1, + isActive: true, + }, + ], + }, + // { + // title: "Communication", + // url: "/communication", + // slug: "communication", + // orderSeq: 6, + // icon: IconMessageCircle, + // isActive: true, + // subItems: [ + // { + // title: "Contact Messages", + // url: "/communication/contact-messages", + // slug: "contact-messages", + // icon: IconMessageCircle, + // orderSeq: 1, + // isActive: true, + // }, + // ], + // }, + { + title: "Settings", + url: "/settings", + slug: "settings", + orderSeq: 6, + icon: IconSettings, + isActive: true, + subItems: [ + { + title: "Navigation", + url: "/settings/navigation", + slug: "navigation", + icon: IconNavigation, + orderSeq: 1, + isActive: true, + subSubItems: [ + { + title: "Nav Items", + url: "/settings/navigation/nav-items", + slug: "nav-items", + icon: IconMenu2, + orderSeq: 1, + isActive: true, + subSubItems: [ + { + title: "Nav Sub Items", + url: "/settings/navigation/nav-sub-items", + slug: "nav-sub-items", + icon: IconMenu2, + orderSeq: 1, + isActive: true, + }, + ], + }, + ], + }, + ], + }, + ], + reports: [ + { + name: "Crime Reports", + url: "#", + icon: IconFrame, + }, + { + name: "Demographics Reports", + url: "#", + icon: IconChartPie, + }, + ], +}; diff --git a/sigap-website/app/error/page.tsx b/sigap-website-v2/prisma/data/users.tsx similarity index 100% rename from sigap-website/app/error/page.tsx rename to sigap-website-v2/prisma/data/users.tsx diff --git a/sigap-website-v2/prisma/schema.prisma b/sigap-website-v2/prisma/schema.prisma new file mode 100644 index 0000000..48f3295 --- /dev/null +++ b/sigap-website-v2/prisma/schema.prisma @@ -0,0 +1,204 @@ +generator client { + provider = "prisma-client-js" + previewFeatures = ["postgresqlExtensions"] +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") + directUrl = env("DIRECT_URL") + extensions = [pgcrypto, uuid_ossp(map: "uuid-ossp", schema: "extensions")] +} + +model cities { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + geographic_id String? @db.Uuid + name String @db.VarChar(100) + code String @db.VarChar(10) + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @default(now()) @db.Timestamptz(6) + geographics geographics? @relation(fields: [geographic_id], references: [id]) + crimes crimes[] + demographics demographics[] + districts districts[] + + @@index([name]) +} + +model contact_messages { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + name String? @db.VarChar(255) + email String? @db.VarChar(255) + phone String? @db.VarChar(20) + message_type String? @db.VarChar(50) + message_type_label String? @db.VarChar(50) + message String? + status status_contact_messages @default(new) + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @db.Timestamptz(6) +} + +model crime_cases { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + crime_id String? @db.Uuid + crime_category_id String? @db.Uuid + date DateTime @db.Timestamptz(6) + time DateTime @db.Timestamptz(6) + location String @db.VarChar(255) + latitude Float + longitude Float + description String + victim_count Int + status crime_status @default(new) + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @default(now()) @db.Timestamptz(6) + crime_categories crime_categories? @relation(fields: [crime_category_id], references: [id]) + crimes crimes? @relation(fields: [crime_id], references: [id]) +} + +model crime_categories { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + name String @db.VarChar(255) + description String + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @default(now()) @db.Timestamptz(6) + crime_cases crime_cases[] +} + +model crimes { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + district_id String? @db.Uuid + city_id String? @db.Uuid + year Int + number_of_crime Int + rate crime_rates @default(low) + heat_map Json? + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @default(now()) @db.Timestamptz(6) + crime_cases crime_cases[] + cities cities? @relation(fields: [city_id], references: [id]) + districts districts? @relation(fields: [district_id], references: [id]) + + @@unique([city_id, year]) + @@unique([district_id, year]) +} + +model demographics { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + district_id String? @db.Uuid + city_id String? @db.Uuid + province_id String? @db.Uuid + year Int + population Int + population_density Float + poverty_rate Float + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @default(now()) @db.Timestamptz(6) + cities cities? @relation(fields: [city_id], references: [id]) + districts districts? @relation(fields: [district_id], references: [id]) + + @@unique([city_id, year]) + @@unique([district_id, year]) +} + +model districts { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + city_id String @db.Uuid + name String @db.VarChar(100) + code String @db.VarChar(10) + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @default(now()) @db.Timestamptz(6) + crimes crimes[] + demographics demographics[] + cities cities @relation(fields: [city_id], references: [id], onDelete: Cascade) + geographics geographics? + + @@index([name]) +} + +model geographics { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + district_id String? @unique @db.Uuid + latitude Float? + longitude Float? + land_area Float? + polygon Json? + created_at DateTime @default(now()) @db.Timestamptz(6) + updated_at DateTime @default(now()) @db.Timestamptz(6) + cities cities[] + districts districts? @relation(fields: [district_id], references: [id]) +} + +model profiles { + id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid + user_id String @unique @db.Uuid + bio String? + address String? @db.VarChar(255) + city String? @db.VarChar(100) + country String? @db.VarChar(100) + birth_date DateTime? + users users @relation(fields: [user_id], references: [id]) + + @@index([user_id]) +} + +model users { + id String @id @db.Uuid + email String @unique @db.VarChar(255) + email_verified Boolean @default(false) + first_name String? @db.VarChar(255) + last_name String? @db.VarChar(255) + avatar String? @db.VarChar(255) + role roles @default(user) + created_at DateTime @default(now()) + updated_at DateTime + banned_until DateTime? + confirmation_sent_at DateTime? + confirmation_token String? @db.VarChar(255) + deleted_at DateTime? + email_change String? @db.VarChar(255) + email_change_sent_at DateTime? + email_change_token String? @db.VarChar(255) + email_confirmed_at DateTime? + encrypted_password String? @db.VarChar(255) + is_anonymous Boolean? @default(false) + is_sso_user Boolean? @default(false) + last_sign_in_at DateTime? + phone String? @db.VarChar(20) + phone_confirmed_at DateTime? + raw_app_meta_data Json? + raw_user_meta_data Json? + reauthentication_sent_at DateTime? + reauthentication_token String? @db.VarChar(255) + recovery_sent_at DateTime? + recovery_token String? @db.VarChar(255) + providers Json? @default("[]") + profiles profiles? + + @@index([role]) +} + +enum crime_rates { + low + medium + high +} + +enum crime_status { + new + in_progress + resolved +} + +enum roles { + admin + staff + user +} + +enum status_contact_messages { + new + read + replied + resolved +} diff --git a/sigap-website-v2/providers/react-query-provider.tsx b/sigap-website-v2/providers/react-query-provider.tsx new file mode 100644 index 0000000..f452338 --- /dev/null +++ b/sigap-website-v2/providers/react-query-provider.tsx @@ -0,0 +1,14 @@ +"use client"; + +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; +import { useState } from "react"; + +const ReactQueryProvider = ({ children }: { children: React.ReactNode }) => { + const [queryClient] = useState(() => new QueryClient()); + + return ( + {children} + ); +}; + +export default ReactQueryProvider; diff --git a/sigap-website-v2/src/controller/auth/sign-in-controller.tsx b/sigap-website-v2/src/controller/auth/sign-in-controller.tsx new file mode 100644 index 0000000..4907cf9 --- /dev/null +++ b/sigap-website-v2/src/controller/auth/sign-in-controller.tsx @@ -0,0 +1,85 @@ +"use client"; + +import { signIn } from "@/app/(auth-pages)/action"; +import { + defaultSignInValues, + SignInFormData, + signInSchema, +} from "@/src/models/auth/sign-in.model"; +import { useState, type FormEvent, type ChangeEvent } from "react"; + +import { z } from "zod"; + +type SignInFormErrors = Partial>; + +export function useSignInForm() { + const [formData, setFormData] = useState(defaultSignInValues); + const [errors, setErrors] = useState({}); + const [isSubmitting, setIsSubmitting] = useState(false); + const [message, setMessage] = useState(null); + + const validateForm = (): boolean => { + try { + signInSchema.parse(formData); + setErrors({}); + return true; + } catch (error) { + if (error instanceof z.ZodError) { + const formattedErrors: SignInFormErrors = {}; + error.errors.forEach((err) => { + const path = err.path[0] as keyof SignInFormData; + formattedErrors[path] = err.message; + }); + setErrors(formattedErrors); + } + return false; + } + }; + + const handleChange = (e: ChangeEvent) => { + const { name, value } = e.target; + setFormData((prev) => ({ + ...prev, + [name]: value, + })); + }; + + const handleSubmit = async (e: FormEvent) => { + e.preventDefault(); + + if (!validateForm()) { + return; + } + + setIsSubmitting(true); + setMessage(null); + + try { + const result = await signIn(formData); + if (result.success) { + setMessage(result.message); + } else { + setErrors({ + email: result.message || "Sign in failed. Please try again.", + }); + } + } catch (error) { + console.error("Sign in failed", error); + setErrors({ + email: "An unexpected error occurred. Please try again.", + }); + } finally { + setIsSubmitting(false); + } + }; + + return { + formData, + errors, + isSubmitting, + message, + setFormData, + handleChange, + handleSubmit, + }; +} diff --git a/sigap-website-v2/src/models/auth/sign-in.model.ts b/sigap-website-v2/src/models/auth/sign-in.model.ts new file mode 100644 index 0000000..286f315 --- /dev/null +++ b/sigap-website-v2/src/models/auth/sign-in.model.ts @@ -0,0 +1,17 @@ +import { z } from "zod"; + +// Define the sign-in form schema using Zod +export const signInSchema = z.object({ + email: z + .string() + .min(1, { message: "Email is required" }) + .email({ message: "Invalid email address" }), +}); + +// Export the type derived from the schema +export type SignInFormData = z.infer; + +// Default values for the form +export const defaultSignInValues: SignInFormData = { + email: "", +}; diff --git a/sigap-website-v2/src/models/auth/verify-otp.model.ts b/sigap-website-v2/src/models/auth/verify-otp.model.ts new file mode 100644 index 0000000..bbc94be --- /dev/null +++ b/sigap-website-v2/src/models/auth/verify-otp.model.ts @@ -0,0 +1,13 @@ +import { z } from "zod"; + +// Define the verify OTP form schema using Zod +export const verifyOtpSchema = z.object({ + email: z + .string() + .min(1, { message: "Email is required" }) + .email({ message: "Invalid email address" }), + token: z.string().min(6, { message: "OTP is required" }), +}); + +// Export the type derived from the schema +export type VerifyOtpFormData = z.infer; diff --git a/sigap-website-v2/src/repositories/authentication.repository.ts b/sigap-website-v2/src/repositories/authentication.repository.ts new file mode 100644 index 0000000..3868bc4 --- /dev/null +++ b/sigap-website-v2/src/repositories/authentication.repository.ts @@ -0,0 +1,39 @@ +import { createClient } from "@/utils/supabase/server"; +import { SignInFormData } from "../models/auth/sign-in.model"; + +export class AuthRepository { + async signIn({ email }: SignInFormData) { + const supabase = await createClient(); + const { data, error } = await supabase.auth.signInWithOtp({ + email, + options: { + emailRedirectTo: `${window.location.origin}/auth/callback`, + }, + }); + + if (error) { + throw new Error(error.message); + } + + return data; + } + + async signOut() { + const supabase = await createClient(); + + const { error } = await supabase.auth.signOut(); + if (error) { + throw new Error(error.message); + } + } + + async getUser() { + const supabase = await createClient(); + const { + data: { user }, + } = await supabase.auth.getUser(); + return user; + } +} + +export const authRepository = new AuthRepository(); diff --git a/sigap-website-v2/tailwind.config.ts b/sigap-website-v2/tailwind.config.ts new file mode 100644 index 0000000..41668a3 --- /dev/null +++ b/sigap-website-v2/tailwind.config.ts @@ -0,0 +1,80 @@ +import type { Config } from "tailwindcss"; + +const config = { + darkMode: ["class"], + content: [ + "./pages/**/*.{ts,tsx}", + "./components/**/*.{ts,tsx}", + "./app/**/*.{ts,tsx}", + "./src/**/*.{ts,tsx}", + ], + prefix: "", + theme: { + container: { + center: true, + padding: "2rem", + screens: { + "2xl": "1400px", + }, + }, + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + keyframes: { + "accordion-down": { + from: { height: "0" }, + to: { height: "var(--radix-accordion-content-height)" }, + }, + "accordion-up": { + from: { height: "var(--radix-accordion-content-height)" }, + to: { height: "0" }, + }, + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out", + }, + }, + }, + plugins: [require("tailwindcss-animate")], +} satisfies Config; + +export default config; diff --git a/sigap-website-v2/tsconfig.json b/sigap-website-v2/tsconfig.json new file mode 100644 index 0000000..e06a445 --- /dev/null +++ b/sigap-website-v2/tsconfig.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "target": "es5", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "node", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "preserve", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./*"] + } + }, + "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/sigap-website-v2/utils/supabase/check-env-vars.ts b/sigap-website-v2/utils/supabase/check-env-vars.ts new file mode 100644 index 0000000..7180f45 --- /dev/null +++ b/sigap-website-v2/utils/supabase/check-env-vars.ts @@ -0,0 +1,6 @@ +// This check can be removed +// it is just for tutorial purposes + +export const hasEnvVars = + process.env.NEXT_PUBLIC_SUPABASE_URL && + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY; diff --git a/sigap-website-v2/utils/supabase/client.ts b/sigap-website-v2/utils/supabase/client.ts new file mode 100644 index 0000000..e2660d0 --- /dev/null +++ b/sigap-website-v2/utils/supabase/client.ts @@ -0,0 +1,7 @@ +import { createBrowserClient } from "@supabase/ssr"; + +export const createClient = () => + createBrowserClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, + ); diff --git a/sigap-website-v2/utils/supabase/middleware.ts b/sigap-website-v2/utils/supabase/middleware.ts new file mode 100644 index 0000000..8619ec0 --- /dev/null +++ b/sigap-website-v2/utils/supabase/middleware.ts @@ -0,0 +1,62 @@ +import { createServerClient } from "@supabase/ssr"; +import { type NextRequest, NextResponse } from "next/server"; + +export const updateSession = async (request: NextRequest) => { + // This `try/catch` block is only here for the interactive tutorial. + // Feel free to remove once you have Supabase connected. + try { + // Create an unmodified response + let response = NextResponse.next({ + request: { + headers: request.headers, + }, + }); + + const supabase = createServerClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, + { + cookies: { + getAll() { + return request.cookies.getAll(); + }, + setAll(cookiesToSet) { + cookiesToSet.forEach(({ name, value }) => + request.cookies.set(name, value), + ); + response = NextResponse.next({ + request, + }); + cookiesToSet.forEach(({ name, value, options }) => + response.cookies.set(name, value, options), + ); + }, + }, + }, + ); + + // This will refresh session if expired - required for Server Components + // https://supabase.com/docs/guides/auth/server-side/nextjs + const user = await supabase.auth.getUser(); + + // protected routes + if (request.nextUrl.pathname.startsWith("/protected") && user.error) { + return NextResponse.redirect(new URL("/sign-in", request.url)); + } + + if (request.nextUrl.pathname === "/" && !user.error) { + return NextResponse.redirect(new URL("/protected", request.url)); + } + + return response; + } catch (e) { + // If you are here, a Supabase client could not be created! + // This is likely because you have not set up environment variables. + // Check out http://localhost:3000 for Next Steps. + return NextResponse.next({ + request: { + headers: request.headers, + }, + }); + } +}; diff --git a/sigap-website-v2/utils/supabase/server.ts b/sigap-website-v2/utils/supabase/server.ts new file mode 100644 index 0000000..2c00bbc --- /dev/null +++ b/sigap-website-v2/utils/supabase/server.ts @@ -0,0 +1,29 @@ +import { createServerClient } from "@supabase/ssr"; +import { cookies } from "next/headers"; + +export const createClient = async () => { + const cookieStore = await cookies(); + + return createServerClient( + process.env.NEXT_PUBLIC_SUPABASE_URL!, + process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, + { + cookies: { + getAll() { + return cookieStore.getAll(); + }, + setAll(cookiesToSet) { + try { + cookiesToSet.forEach(({ name, value, options }) => { + cookieStore.set(name, value, options); + }); + } catch (error) { + // The `set` method was called from a Server Component. + // This can be ignored if you have middleware refreshing + // user sessions. + } + }, + }, + }, + ); +}; diff --git a/sigap-website-v2/utils/utils.ts b/sigap-website-v2/utils/utils.ts new file mode 100644 index 0000000..c9fbbe8 --- /dev/null +++ b/sigap-website-v2/utils/utils.ts @@ -0,0 +1,16 @@ +import { redirect } from "next/navigation"; + +/** + * Redirects to a specified path with an encoded message as a query parameter. + * @param {('error' | 'success')} type - The type of message, either 'error' or 'success'. + * @param {string} path - The path to redirect to. + * @param {string} message - The message to be encoded and added as a query parameter. + * @returns {never} This function doesn't return as it triggers a redirect. + */ +export function encodedRedirect( + type: "error" | "success", + path: string, + message: string, +) { + return redirect(`${path}?${type}=${encodeURIComponent(message)}`); +} diff --git a/sigap-website/.env.example b/sigap-website/.env.example new file mode 100644 index 0000000..6937031 --- /dev/null +++ b/sigap-website/.env.example @@ -0,0 +1,4 @@ +# Update these with your Supabase details from your project settings > API +# https://app.supabase.com/project/_/settings/api +NEXT_PUBLIC_SUPABASE_URL=your-project-url +NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key diff --git a/sigap-website/.vscode/extensions.json b/sigap-website/.vscode/extensions.json deleted file mode 100644 index 74baffc..0000000 --- a/sigap-website/.vscode/extensions.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "recommendations": ["denoland.vscode-deno"] -} diff --git a/sigap-website/.vscode/settings.json b/sigap-website/.vscode/settings.json deleted file mode 100644 index 0dccffe..0000000 --- a/sigap-website/.vscode/settings.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "deno.enablePaths": ["supabase/functions"], - "deno.lint": true, - "deno.unstable": [ - "bare-node-builtins", - "byonm", - "sloppy-imports", - "unsafe-proto", - "webgpu", - "broadcast-channel", - "worker-options", - "cron", - "kv", - "ffi", - "fs", - "http", - "net" - ], - "[typescript]": { - "editor.defaultFormatter": "esbenp.prettier-vscode" - } -} diff --git a/sigap-website/app/(auth-pages)/_actions/forgot-password.ts b/sigap-website/app/(auth-pages)/_actions/forgot-password.ts new file mode 100644 index 0000000..b38936b --- /dev/null +++ b/sigap-website/app/(auth-pages)/_actions/forgot-password.ts @@ -0,0 +1,40 @@ +"use server"; + +import { createClient } from "@/utils/supabase/server"; +import { encodedRedirect } from "@/utils/utils"; +import { headers } from "next/headers"; +import { redirect } from "next/navigation"; + +export const forgotPasswordAction = async (formData: FormData) => { + const email = formData.get("email")?.toString(); + const supabase = await createClient(); + const origin = (await headers()).get("origin"); + const callbackUrl = formData.get("callbackUrl")?.toString(); + + if (!email) { + return encodedRedirect("error", "/forgot-password", "Email is required"); + } + + const { error } = await supabase.auth.resetPasswordForEmail(email, { + redirectTo: `${origin}/auth/callback?redirect_to=/protected/reset-password`, + }); + + if (error) { + console.error(error.message); + return encodedRedirect( + "error", + "/forgot-password", + "Could not reset password", + ); + } + + if (callbackUrl) { + return redirect(callbackUrl); + } + + return encodedRedirect( + "success", + "/forgot-password", + "Check your email for a link to reset your password.", + ); +}; \ No newline at end of file diff --git a/sigap-website/app/(auth-pages)/_actions/reset-password.ts b/sigap-website/app/(auth-pages)/_actions/reset-password.ts new file mode 100644 index 0000000..3b58b68 --- /dev/null +++ b/sigap-website/app/(auth-pages)/_actions/reset-password.ts @@ -0,0 +1,43 @@ +"use server"; + +import { encodedRedirect } from "@/utils/utils"; +import { createClient } from "@/utils/supabase/server"; +import { headers } from "next/headers"; +import { redirect } from "next/navigation"; + +export const resetPasswordAction = async (formData: FormData) => { + const supabase = await createClient(); + + const password = formData.get("password") as string; + const confirmPassword = formData.get("confirmPassword") as string; + + if (!password || !confirmPassword) { + encodedRedirect( + "error", + "/protected/reset-password", + "Password and confirm password are required" + ); + } + + if (password !== confirmPassword) { + encodedRedirect( + "error", + "/protected/reset-password", + "Passwords do not match" + ); + } + + const { error } = await supabase.auth.updateUser({ + password: password, + }); + + if (error) { + encodedRedirect( + "error", + "/protected/reset-password", + "Password update failed" + ); + } + + encodedRedirect("success", "/protected/reset-password", "Password updated"); +}; diff --git a/sigap-website/app/(auth-pages)/_actions/send-contact-form.ts b/sigap-website/app/(auth-pages)/_actions/send-contact-form.ts new file mode 100644 index 0000000..17c5a51 --- /dev/null +++ b/sigap-website/app/(auth-pages)/_actions/send-contact-form.ts @@ -0,0 +1,100 @@ +// import { createClient } from "@/utils/supabase/server"; + +// export async function sendContactEmail(formData: { +// name: string; +// email: string; +// phone: string; +// typeMessage: string; +// message: string; +// }) { +// try { +// // Initialize Supabase +// const supabase = await createClient(); +// const { resend } = useResend(); + +// // Get message type label +// const messageTypeLabel = +// typeMessageMap.get(formData.typeMessage) || "Unknown"; + +// // Save to Supabase +// const { data: contactData, error: contactError } = await supabase +// .from("contact_messages") +// .insert([ +// { +// name: formData.name, +// email: formData.email, +// phone: formData.phone, +// message_type: formData.typeMessage, +// message_type_label: messageTypeLabel, +// message: formData.message, +// status: "new", +// }, +// ]) +// .select(); + +// if (contactError) { +// console.error("Error saving contact message to Supabase:", contactError); +// return { +// success: false, +// error: "Failed to save your message. Please try again later.", +// }; +// } + +// // Render admin email template +// const adminEmailHtml = await render( +// AdminNotification({ +// name: formData.name, +// email: formData.email, +// phone: formData.phone, +// messageType: messageTypeLabel, +// message: formData.message, +// }) +// ); + +// // Send email to admin +// const { data: emailData, error: emailError } = await resend.emails.send({ +// from: "Contact Form ", +// to: ["xdamazon17@gmail.com"], +// subject: `New Contact Form Submission: ${messageTypeLabel}`, +// html: adminEmailHtml, +// }); + +// if (emailError) { +// console.error("Error sending email via Resend:", emailError); +// // Note: We don't return error here since the data is already saved to Supabase +// } + +// const userEmailHtml = await render( +// UserConfirmation({ +// name: formData.name, +// messageType: messageTypeLabel, +// message: formData.message, +// }) +// ); + +// // Send confirmation email to user +// const { data: confirmationData, error: confirmationError } = +// await resend.emails.send({ +// from: "Your Company ", +// to: [formData.email], +// subject: "Thank you for contacting us", +// html: userEmailHtml, +// }); + +// if (confirmationError) { +// console.error("Error sending confirmation email:", confirmationError); +// // Note: We don't return error here either +// } + +// return { +// success: true, +// message: "Your message has been sent successfully!", +// }; +// } catch (error) { +// console.error("Unexpected error in sendContactEmail:", error); +// return { +// success: false, +// error: "An unexpected error occurred. Please try again later.", +// }; +// } +// } \ No newline at end of file diff --git a/sigap-website/app/(auth-pages)/_actions/session.ts b/sigap-website/app/(auth-pages)/_actions/session.ts new file mode 100644 index 0000000..a742b1d --- /dev/null +++ b/sigap-website/app/(auth-pages)/_actions/session.ts @@ -0,0 +1,37 @@ +import { createClient } from "@/utils/supabase/server"; + +export const checkSession = async () => { + const supabase = await createClient(); + + try { + const { + data: { session }, + error, + } = await supabase.auth.getSession(); + + if (error) { + return { + success: false, + error: error.message, + }; + } + + if (session) { + return { + success: true, + session, + redirectTo: "/protected/dashboard", // or your preferred authenticated route + }; + } + + return { + success: false, + message: "No active session", + }; + } catch (error) { + return { + success: false, + error: "An unexpected error occurred", + }; + } +}; diff --git a/sigap-website/app/(auth-pages)/_actions/sign-in.ts b/sigap-website/app/(auth-pages)/_actions/sign-in.ts new file mode 100644 index 0000000..5c6ea79 --- /dev/null +++ b/sigap-website/app/(auth-pages)/_actions/sign-in.ts @@ -0,0 +1,54 @@ +"use server"; + +import { createClient } from "@/utils/supabase/server"; +import { encodedRedirect } from "@/utils/utils"; +import { redirect } from "next/navigation"; +import { checkSession } from "./session"; + +export const signInAction = async (formData: FormData) => { + const supabase = await createClient(); + const email = formData.get("email") as string; + const encodeEmail = encodeURIComponent(email); + + try { + // First, check for existing session + const { session, error: sessionError } = await checkSession(); + + // If there's an active session and the email matches + if (session && session.user.email === email) { + return { + success: true, + message: "Already logged in", + redirectTo: "/protected/dashboard", // or wherever you want to redirect logged in users + }; + } + + // If no active session or different email, proceed with OTP + const { data, error } = await supabase.auth.signInWithOtp({ + email, + options: { + shouldCreateUser: false, + }, + }); + + if (error) { + return { + success: false, + error: error.message, + redirectTo: `/verify-otp?email=${encodeEmail}`, + }; + } + + return { + success: true, + message: "OTP has been sent to your email", + redirectTo: `/verify-otp?email=${encodeEmail}`, + }; + } catch (error) { + return { + success: false, + error: "An unexpected error occurred", + redirectTo: "/sign-in", + }; + } +}; diff --git a/sigap-website/app/(auth-pages)/_actions/sign-out.ts b/sigap-website/app/(auth-pages)/_actions/sign-out.ts new file mode 100644 index 0000000..0137a93 --- /dev/null +++ b/sigap-website/app/(auth-pages)/_actions/sign-out.ts @@ -0,0 +1,10 @@ +"use server"; + +import { createClient } from "@/utils/supabase/server"; +import { redirect } from "next/navigation"; + +export const signOutAction = async () => { + const supabase = await createClient(); + await supabase.auth.signOut(); + return redirect("/sign-in"); +}; diff --git a/sigap-website/app/(auth-pages)/_actions/sign-up.ts b/sigap-website/app/(auth-pages)/_actions/sign-up.ts new file mode 100644 index 0000000..a4b2ef3 --- /dev/null +++ b/sigap-website/app/(auth-pages)/_actions/sign-up.ts @@ -0,0 +1,39 @@ +"use server"; + +import { createClient } from "@/utils/supabase/server"; +import { encodedRedirect } from "@/utils/utils"; +import { headers } from "next/headers"; + +export const signUpAction = async (formData: FormData) => { + const email = formData.get("email")?.toString(); + const password = formData.get("password")?.toString(); + const supabase = await createClient(); + const origin = (await headers()).get("origin"); + + if (!email || !password) { + return encodedRedirect( + "error", + "/sign-up", + "Email and password are required" + ); + } + + const { error } = await supabase.auth.signUp({ + email, + password, + options: { + emailRedirectTo: `${origin}/auth/callback`, + }, + }); + + if (error) { + console.error(error.code + " " + error.message); + return encodedRedirect("error", "/sign-up", error.message); + } else { + return encodedRedirect( + "success", + "/sign-up", + "Thanks for signing up! Please check your email for a verification link." + ); + } +}; diff --git a/sigap-website/app/(auth-pages)/_actions/verify-otp.ts b/sigap-website/app/(auth-pages)/_actions/verify-otp.ts new file mode 100644 index 0000000..dee6f71 --- /dev/null +++ b/sigap-website/app/(auth-pages)/_actions/verify-otp.ts @@ -0,0 +1,30 @@ +import { createClient } from "@/utils/supabase/server"; +import { redirect } from "next/navigation"; + +export const verifyOtpAction = async (formData: FormData) => { + const email = formData.get("email") as string; + const token = formData.get("token") as string; + const supabase = await createClient(); + + console.log("email", email); + console.log("token", token); + + if (!email || !token) { + redirect("/error?message=Email and OTP are required"); + } + + const { + data: { session }, + error, + } = await supabase.auth.verifyOtp({ + email, + token, + type: "email", + }); + + if (error) { + return redirect(`/verify-otp?error=${encodeURIComponent(error.message)}`); + } + + return redirect("/protected/dashboard?message=OTP verified successfully"); +}; diff --git a/sigap-website/app/(auth-pages)/_components/auth/contact-us.tsx b/sigap-website/app/(auth-pages)/_components/auth/contact-us.tsx deleted file mode 100644 index 123201c..0000000 --- a/sigap-website/app/(auth-pages)/_components/auth/contact-us.tsx +++ /dev/null @@ -1,167 +0,0 @@ -"use client"; - -import * as React from "react"; -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "@/app/_components/ui/card"; -import { Input } from "@/app/_components/ui/input"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/app/_components/ui/select"; -import { Textarea } from "../../../_components/ui/textarea"; -import { SubmitButton } from "../../../_components/submit-button"; -import Link from "next/link"; -import { TValidator } from "@/utils/validator"; -import { FormField } from "../../../_components/form-field"; -import { typeMessage } from "@/src/entities/models/contact-us.model"; -import { Form } from "../../../_components/ui/form"; -import { useContactForm } from "@/hooks/use-contact-us-form"; - -export function ContactUsForm() { - const { - formData, - errors, - isSubmitting, - setFormData, - handleChange, - handleSelectChange, - handleSubmit, - } = useContactForm(); - - return ( - - - Contact Us - - Fill in the form below to contact the admin - - - -
- - } - error={errors.name} - /> - - } - error={errors.email} - /> - - } - error={errors.phone} - /> - - - - - - {typeMessage.map((message) => ( - - {message.label} - - ))} - - - } - error={errors.typeMessage} - /> - - } - error={errors.message} - /> - - - Send - -
- Already have an account? - - Login - -
-
- -
-
- ); -} diff --git a/sigap-website/app/(auth-pages)/_components/auth/email-recovery.tsx b/sigap-website/app/(auth-pages)/_components/auth/email-recovery.tsx deleted file mode 100644 index f87b6f2..0000000 --- a/sigap-website/app/(auth-pages)/_components/auth/email-recovery.tsx +++ /dev/null @@ -1,110 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { useRouter } from "next/router"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useForm } from "react-hook-form"; -import { z } from "zod"; -import { toast } from "@/app/_hooks/use-toast"; -import { Button } from "@/app/_components/ui/button"; -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/app/_components/ui/form"; -import { Input } from "@/app/_components/ui/input"; -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "@/app/_components/ui/card"; -import Link from "next/link"; -import { SubmitButton } from "@/app/_components/submit-button"; - -const FormSchema = z.object({ - email: z.string().email({ - message: "Please enter a valid email address.", - }), -}); - -export default function RecoveryEmailForm() { - // const router = useRouter(); - const form = useForm>({ - resolver: zodResolver(FormSchema), - defaultValues: { - email: "", - }, - }); - - function onSubmit(data: z.infer) { - setTimeout(() => { - toast({ - title: "Recovery email sent", - description: `We've sent a recovery link to ${data.email}`, - }); - // Redirect to a confirmation page or back to login - // router.push("/login"); - }, 2000); - } - - return ( -
- - - Account Recovery - - Enter your email to receive a recovery link - - - -
- - ( - - Email - - - - - We'll send a recovery link to this email - - - - )} - /> - - Send Recovery Link - - - -
- - - -
-
- ); -} diff --git a/sigap-website/app/(auth-pages)/_components/auth/login-form.tsx b/sigap-website/app/(auth-pages)/_components/auth/login-form.tsx deleted file mode 100644 index 6503c58..0000000 --- a/sigap-website/app/(auth-pages)/_components/auth/login-form.tsx +++ /dev/null @@ -1,103 +0,0 @@ -// import { cn } from "@/lib/utils"; -// import { Button } from "@/app/_components/ui/button"; -// import { -// Card, -// CardContent, -// CardDescription, -// CardHeader, -// CardTitle, -// } from "@/app/_components/ui/card"; -// import { Input } from "@/app/_components/ui/input"; -// import { Label } from "@/components/ui/label"; -// import { SubmitButton } from "../submit-button"; -// import { signInAction } from "@/actions/auth/sign-in"; - -// export function LoginForm({ -// className, -// ...props -// }: React.ComponentPropsWithoutRef<"div">) { -// return ( -//
-// -// -// Welcome back -// -// Login with your Apple or Google account -// -// -// -//
-//
-// {/*
-// -// -//
*/} -//
-// {/* -// Or continue with -// */} -//
-//
-//
-// -// -//
-// {/*
-//
-// -// -// Forgot your password? -// -//
-// -//
*/} -// -// Login -// -//
-//
-// Don't have an account?{" "} -// -// Contact Us -// -//
-//
-//
-//
-//
-//
-// By clicking continue, you agree to our Terms of Service{" "} -// and Privacy Policy. -//
-//
-// ); -// } diff --git a/sigap-website/app/(auth-pages)/_components/auth/otp-form.tsx b/sigap-website/app/(auth-pages)/_components/auth/otp-form.tsx deleted file mode 100644 index 24fc59f..0000000 --- a/sigap-website/app/(auth-pages)/_components/auth/otp-form.tsx +++ /dev/null @@ -1,115 +0,0 @@ -"use client"; - -import { zodResolver } from "@hookform/resolvers/zod"; -import { useForm } from "react-hook-form"; -import { z } from "zod"; - -import { toast } from "@/app/_hooks/use-toast"; -import { Button } from "@/app/_components/ui/button"; -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/app/_components/ui/form"; -import { - InputOTP, - InputOTPGroup, - InputOTPSlot, -} from "@/app/_components/ui/input-otp"; -import { SubmitButton } from "../../../_components/submit-button"; -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "../../../_components/ui/card"; -import { cn } from "@/lib/utils"; - -const FormSchema = z.object({ - pin: z.string().min(6, { - message: "Your one-time password must be 6 characters.", - }), -}); - -interface InputOTPFormProps { - className?: string; - [key: string]: any; -} - -export function InputOTPForm({ className, ...props }: InputOTPFormProps) { - const form = useForm>({ - resolver: zodResolver(FormSchema), - defaultValues: { - pin: "", - }, - }); - - function onSubmit(data: z.infer) { - toast({ - title: "You submitted the following values:", - description: ( -
-          {JSON.stringify(data, null, 2)}
-        
- ), - }); - } - - return ( -
- - - One-Time Password - - One time password is a security feature that helps protect your data - - - -
- - ( - - - - - {[...Array(6)].map((_, index) => ( - - ))} - - - - - Please enter the one-time password sent to your phone. - - - - )} - /> -
- - Submit - -
- - -
-
-
- By clicking continue, you agree to our Terms of Service{" "} - and Privacy Policy. -
-
- ); -} diff --git a/sigap-website/app/(auth-pages)/action.ts b/sigap-website/app/(auth-pages)/action.ts new file mode 100644 index 0000000..95c002c --- /dev/null +++ b/sigap-website/app/(auth-pages)/action.ts @@ -0,0 +1,71 @@ +// src/app/(auth-pages)/actions.ts +"use server"; + + +import { SignInFormData } from "@/src/models/auth/sign-in.model"; +import { VerifyOtpFormData } from "@/src/models/auth/verify-otp.model"; +import { authRepository } from "@/src/repositories/authentication.repository"; +import { redirect } from "next/navigation"; + +export async function signIn( + data: SignInFormData +): Promise<{ success: boolean; message: string; redirectTo?: string }> { + try { + const result = await authRepository.signIn(data); + return { + success: true, + message: "Check your email for the login link!", + redirectTo: result.redirectTo + }; + } catch (error) { + console.error("Authentication error:", error); + return { + success: false, + message: + error instanceof Error + ? error.message + : "Authentication failed. Please try again.", + }; + } +} + +export async function verifyOtp( + data: VerifyOtpFormData +): Promise<{ success: boolean; message: string; redirectTo?: string }> { + try { + const result = await authRepository.verifyOtp(data); + return { + success: true, + message: "Successfully authenticated!", + redirectTo: result.redirectTo + }; + } catch (error) { + console.error("OTP verification error:", error); + return { + success: false, + message: + error instanceof Error + ? error.message + : "OTP verification failed. Please try again.", + }; + } +} + +export async function signOut() { + try { + const result = await authRepository.signOut(); + return { + success: true, + redirectTo: result.redirectTo + }; + } catch (error) { + console.error("Sign out error:", error); + return { + success: false, + message: + error instanceof Error + ? error.message + : "Sign out failed. Please try again.", + }; + } +} diff --git a/sigap-website/app/(auth-pages)/actions.ts b/sigap-website/app/(auth-pages)/actions.ts deleted file mode 100644 index 3ecb5f0..0000000 --- a/sigap-website/app/(auth-pages)/actions.ts +++ /dev/null @@ -1,336 +0,0 @@ -"use server"; - -import { createClient } from "@/utils/supabase/server"; -import { encodedRedirect } from "@/utils/utils"; -import { headers } from "next/headers"; -import { redirect } from "next/navigation"; -import AdminNotification from "../_components/email-templates/admin-notification"; -import UserConfirmation from "../_components/email-templates/user-confirmation"; -import { render } from "@react-email/components"; -import { useResend } from "../_hooks/use-resend"; -import { typeMessageMap } from "@/src/entities/models/contact-us.model"; - -export const signInAction = async (formData: { email: string }) => { - const supabase = await createClient(); - const encodeEmail = encodeURIComponent(formData.email); - - try { - // First, check for existing session - const { - data: { session }, - error: sessionError, - } = await supabase.auth.getSession(); - - // If there's an active session and the email matches - if (session && session.user.email === formData.email) { - return { - success: true, - message: "Already logged in", - redirectTo: "/protected/dashboard", // or wherever you want to redirect logged in users - }; - } - - // If no active session or different email, proceed with OTP - const { data, error } = await supabase.auth.signInWithOtp({ - email: formData.email, - options: { - shouldCreateUser: false, - }, - }); - - if (error) { - return { - success: false, - error: error.message, - redirectTo: `/verify-otp?email=${encodeEmail}`, - }; - } - - return { - success: true, - message: "OTP has been sent to your email", - redirectTo: `/verify-otp?email=${encodeEmail}`, - }; - } catch (error) { - return { - success: false, - error: "An unexpected error occurred", - redirectTo: "/sign-in", - }; - } -}; - -export const signUpAction = async (formData: FormData) => { - const email = formData.get("email")?.toString(); - const password = formData.get("password")?.toString(); - const supabase = await createClient(); - const origin = (await headers()).get("origin"); - - if (!email || !password) { - return encodedRedirect( - "error", - "/sign-up", - "Email and password are required", - ); - } - - const { error } = await supabase.auth.signUp({ - email, - password, - options: { - emailRedirectTo: `${origin}/auth/callback`, - }, - }); - - if (error) { - console.error(error.code + " " + error.message); - return encodedRedirect("error", "/sign-up", error.message); - } else { - return encodedRedirect( - "success", - "/sign-up", - "Thanks for signing up! Please check your email for a verification link.", - ); - } - }; - - -export const checkSession = async () => { - const supabase = await createClient(); - - try { - const { - data: { session }, - error, - } = await supabase.auth.getSession(); - - if (error) { - return { - success: false, - error: error.message, - }; - } - - if (session) { - return { - success: true, - session, - redirectTo: "/protected/dashboard", // or your preferred authenticated route - }; - } - - return { - success: false, - message: "No active session", - }; - } catch (error) { - return { - success: false, - error: "An unexpected error occurred", - }; - } - }; - - export const forgotPasswordAction = async (formData: FormData) => { - const email = formData.get("email")?.toString(); - const supabase = await createClient(); - const origin = (await headers()).get("origin"); - const callbackUrl = formData.get("callbackUrl")?.toString(); - - if (!email) { - return encodedRedirect("error", "/forgot-password", "Email is required"); - } - - const { error } = await supabase.auth.resetPasswordForEmail(email, { - redirectTo: `${origin}/auth/callback?redirect_to=/protected/reset-password`, - }); - - if (error) { - console.error(error.message); - return encodedRedirect( - "error", - "/forgot-password", - "Could not reset password" - ); - } - - if (callbackUrl) { - return redirect(callbackUrl); - } - - return encodedRedirect( - "success", - "/forgot-password", - "Check your email for a link to reset your password." - ); - }; - - export const resetPasswordAction = async (formData: FormData) => { - const supabase = await createClient(); - - const password = formData.get("password") as string; - const confirmPassword = formData.get("confirmPassword") as string; - - if (!password || !confirmPassword) { - encodedRedirect( - "error", - "/protected/reset-password", - "Password and confirm password are required" - ); - } - - if (password !== confirmPassword) { - encodedRedirect( - "error", - "/protected/reset-password", - "Passwords do not match" - ); - } - - const { error } = await supabase.auth.updateUser({ - password: password, - }); - - if (error) { - encodedRedirect( - "error", - "/protected/reset-password", - "Password update failed" - ); - } - - encodedRedirect("success", "/protected/reset-password", "Password updated"); - }; - - export const verifyOtpAction = async (formData: FormData) => { - const email = formData.get("email") as string; - const token = formData.get("token") as string; - const supabase = await createClient(); - - console.log("email", email); - console.log("token", token); - - if (!email || !token) { - redirect("/error?message=Email and OTP are required"); - } - - const { - data: { session }, - error, - } = await supabase.auth.verifyOtp({ - email, - token, - type: "email", - }); - - if (error) { - return redirect(`/verify-otp?error=${encodeURIComponent(error.message)}`); - } - - return redirect("/protected/dashboard?message=OTP verified successfully"); - }; - - export async function sendContactEmail(formData: { - name: string; - email: string; - phone: string; - typeMessage: string; - message: string; - }) { - try { - // Initialize Supabase - const supabase = await createClient(); - const { resend } = useResend(); - - // Get message type label - const messageTypeLabel = - typeMessageMap.get(formData.typeMessage) || "Unknown"; - - // Save to Supabase - const { data: contactData, error: contactError } = await supabase - .from("contact_messages") - .insert([ - { - name: formData.name, - email: formData.email, - phone: formData.phone, - message_type: formData.typeMessage, - message_type_label: messageTypeLabel, - message: formData.message, - status: "new", - }, - ]) - .select(); - - if (contactError) { - console.error("Error saving contact message to Supabase:", contactError); - return { - success: false, - error: "Failed to save your message. Please try again later.", - }; - } - - // Render admin email template - const adminEmailHtml = await render( - AdminNotification({ - name: formData.name, - email: formData.email, - phone: formData.phone, - messageType: messageTypeLabel, - message: formData.message, - }) - ); - - // Send email to admin - const { data: emailData, error: emailError } = await resend.emails.send({ - from: "Contact Form ", - to: ["xdamazon17@gmail.com"], - subject: `New Contact Form Submission: ${messageTypeLabel}`, - html: adminEmailHtml, - }); - - if (emailError) { - console.error("Error sending email via Resend:", emailError); - // Note: We don't return error here since the data is already saved to Supabase - } - - const userEmailHtml = await render( - UserConfirmation({ - name: formData.name, - messageType: messageTypeLabel, - message: formData.message, - }) - ); - - // Send confirmation email to user - const { data: confirmationData, error: confirmationError } = - await resend.emails.send({ - from: "Your Company ", - to: [formData.email], - subject: "Thank you for contacting us", - html: userEmailHtml, - }); - - if (confirmationError) { - console.error("Error sending confirmation email:", confirmationError); - // Note: We don't return error here either - } - - return { - success: true, - message: "Your message has been sent successfully!", - }; - } catch (error) { - console.error("Unexpected error in sendContactEmail:", error); - return { - success: false, - error: "An unexpected error occurred. Please try again later.", - }; - } - } - - export const signOutAction = async () => { - const supabase = await createClient(); - await supabase.auth.signOut(); - return redirect("/sign-in"); - }; \ No newline at end of file diff --git a/sigap-website/app/(auth-pages)/email-recovery/page.tsx b/sigap-website/app/(auth-pages)/email-recovery/page.tsx deleted file mode 100644 index e11b1e0..0000000 --- a/sigap-website/app/(auth-pages)/email-recovery/page.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import RecoveryEmailForm from "@/app/(auth-pages)/_components/auth/email-recovery"; -import { GalleryVerticalEnd } from "lucide-react"; - -export default async function VerifyOtpPage() { - return ( -
-
- -
-
- -
-
-
-
- ); -} diff --git a/sigap-website/app/(auth-pages)/forgot-password/page.tsx b/sigap-website/app/(auth-pages)/forgot-password/page.tsx deleted file mode 100644 index 2c8bd5d..0000000 --- a/sigap-website/app/(auth-pages)/forgot-password/page.tsx +++ /dev/null @@ -1,39 +0,0 @@ - -import Link from "next/link"; -import { SmtpMessage } from "../smtp-message"; - -import { Input } from "@/app/_components/ui/input"; -import { Label } from "@/app/_components/ui/label"; -import { SubmitButton } from "@/app/_components/submit-button"; -import { FormMessage, Message } from "@/app/_components/form-message"; -import { forgotPasswordAction } from "../actions"; - -export default async function ForgotPassword(props: { - searchParams: Promise; -}) { - const searchParams = await props.searchParams; - return ( - <> -
-
-

Reset Password

-

- Already have an account?{" "} - - Sign in - -

-
-
- - - - Reset Password - - -
-
- - - ); -} diff --git a/sigap-website/app/(auth-pages)/layout.tsx b/sigap-website/app/(auth-pages)/layout.tsx index 66c2056..7102549 100644 --- a/sigap-website/app/(auth-pages)/layout.tsx +++ b/sigap-website/app/(auth-pages)/layout.tsx @@ -1,6 +1,5 @@ - import { redirect } from "next/navigation"; -import { checkSession } from "./actions"; +import { checkSession } from "./_actions/session"; export default async function Layout({ children, diff --git a/sigap-website/app/(auth-pages)/sign-in/page.tsx b/sigap-website/app/(auth-pages)/sign-in/page.tsx index 94a64f4..a85abd3 100644 --- a/sigap-website/app/(auth-pages)/sign-in/page.tsx +++ b/sigap-website/app/(auth-pages)/sign-in/page.tsx @@ -1,7 +1,7 @@ -import { Message } from "@/app/_components/form-message"; -import { Button } from "@/app/_components/ui/button"; +import { SignInForm } from "@/components/auth/signin-form"; +import { Message } from "@/components/form-message"; +import { Button } from "@/components/ui/button"; import { GalleryVerticalEnd, Globe } from "lucide-react"; -import { LoginForm2 } from "@/app/(auth-pages)/_components/auth/login-form-2"; export default async function Login(props: { searchParams: Promise }) { const searchParams = await props.searchParams; @@ -18,7 +18,7 @@ export default async function Login(props: { searchParams: Promise }) {
- +
diff --git a/sigap-website/app/(auth-pages)/sign-up/page.tsx b/sigap-website/app/(auth-pages)/sign-up/page.tsx deleted file mode 100644 index 81b6e43..0000000 --- a/sigap-website/app/(auth-pages)/sign-up/page.tsx +++ /dev/null @@ -1,51 +0,0 @@ -import { FormMessage, Message } from "@/app/_components/form-message"; -import { SubmitButton } from "@/app/_components/submit-button"; -import { Input } from "@/app/_components/ui/input"; -import { Label } from "@/app/_components/ui/label"; -import Link from "next/link"; -import { SmtpMessage } from "../smtp-message"; -import { signUpAction } from "@/actions/auth/sign-up-action"; - -export default async function SignupPage(props: { - searchParams: Promise; -}) { - const searchParams = await props.searchParams; - if ("message" in searchParams) { - return ( -
- -
- ); - } - - return ( - <> -
-

Sign up

-

- Already have an account?{" "} - - Sign in - -

-
- - - - - - Sign up - - -
-
- - - ); -} diff --git a/sigap-website/app/(auth-pages)/verify-otp/page.tsx b/sigap-website/app/(auth-pages)/verify-otp/page.tsx index 3bcf83b..12ecb88 100644 --- a/sigap-website/app/(auth-pages)/verify-otp/page.tsx +++ b/sigap-website/app/(auth-pages)/verify-otp/page.tsx @@ -1,5 +1,5 @@ -import { VerifyOtpForm } from "@/app/(auth-pages)/_components/auth/verify-otp-form"; +import { VerifyOtpForm } from "@/components/auth/verify-otp-form"; import { GalleryVerticalEnd } from "lucide-react"; export default async function VerifyOtpPage() { @@ -16,7 +16,7 @@ export default async function VerifyOtpPage() {
- +
diff --git a/sigap-website/app/_components/dynamic-icon.tsx b/sigap-website/app/_components/dynamic-icon.tsx deleted file mode 100644 index eceff9a..0000000 --- a/sigap-website/app/_components/dynamic-icon.tsx +++ /dev/null @@ -1,53 +0,0 @@ -// components/icons/DynamicIcon.tsx -import React from "react"; -import * as TablerIcons from "@tabler/icons-react"; - -export interface DynamicIconProps { - iconName: string; - size?: number; - color?: string; - className?: string; - stroke?: number; -} - -/** - * 🔥 DynamicIcon - Reusable component untuk merender ikon @tabler/icons-react secara dinamis - * @param iconName - Nama ikon (harus sesuai dengan nama ikon dari @tabler/icons-react, contoh: "IconUsers") - * @param size - Ukuran ikon (default: 24) - * @param color - Warna ikon (opsional) - * @param className - CSS class tambahan (opsional) - * @param stroke - Ketebalan garis (opsional, default dari library) - */ -const DynamicIcon: React.FC = ({ - iconName, - size = 32, - color, - className = "", // Empty string default, not an icon name - stroke, // This will be passed as strokeWidth -}) => { - // Safety check: Ensure the iconName exists in TablerIcons - if (!(iconName in TablerIcons)) { - console.warn(`Icon "${iconName}" not found in @tabler/icons-react library`); - return null; - } - - const IconComponent = TablerIcons[ - iconName as keyof typeof TablerIcons - ] as React.ComponentType<{ - size?: number; - color?: string; - className?: string; - strokeWidth?: number; // Correct prop name for Tabler icons - }>; - - return ( - - ); -}; - -export default DynamicIcon; \ No newline at end of file diff --git a/sigap-website/app/_components/email-templates/admin-notification.tsx b/sigap-website/app/_components/email-templates/admin-notification.tsx deleted file mode 100644 index 6adfa23..0000000 --- a/sigap-website/app/_components/email-templates/admin-notification.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import { - Body, - Container, - Head, - Heading, - Hr, - Html, - Preview, - Section, - Text, -} from "@react-email/components"; -import * as React from "react"; - -interface AdminNotificationProps { - name: string; - email: string; - phone: string; - messageType: string; - message: string; -} - -export const AdminNotification = ({ - name, - email, - phone, - messageType, - message, -}: AdminNotificationProps) => { - return ( - - - New Contact Form Submission: {messageType} - - - New Contact Form Submission - -
- Name: - {name} - - Email: - {email} - - Phone: - {phone} - - Type: - {messageType} -
- -
- - Message: - - {message} -
- -
- - This message was submitted through the contact form. - -
- - - ); -}; - -const main = { - backgroundColor: "#1C1C1C", - fontFamily: - '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif', -}; - -const container = { - backgroundColor: "#2E2E2E", - margin: "0 auto", - padding: "20px 0 48px", - marginBottom: "64px", - borderRadius: "8px", -}; - -const section = { - padding: "0 48px", -}; - -const h1 = { - color: "#10B981", // Emerald-500 - fontSize: "24px", - fontWeight: "bold", - margin: "40px 0", - padding: "0", - textAlign: "center" as const, -}; - -const h2 = { - color: "#10B981", // Emerald-500 - fontSize: "20px", - fontWeight: "bold", - margin: "24px 0", - padding: "0", -}; - -const detailLabel = { - color: "#A1A1AA", // Zinc-400 - fontSize: "14px", - margin: "8px 0 4px", -}; - -const detailValue = { - color: "#FFFFFF", - fontSize: "14px", - margin: "0 0 16px", -}; - -const messageBox = { - backgroundColor: "#3F3F46", // Zinc-700 - padding: "16px", - borderRadius: "4px", - color: "#FFFFFF", - fontSize: "14px", - lineHeight: "24px", - margin: "0", -}; - -const hr = { - borderColor: "#3F3F46", // Zinc-700 - margin: "20px 0", -}; - -const footer = { - color: "#71717A", // Zinc-500 - fontSize: "12px", - marginTop: "12px", - textAlign: "center" as const, -}; - -export default AdminNotification; diff --git a/sigap-website/app/_components/email-templates/user-confirmation.tsx b/sigap-website/app/_components/email-templates/user-confirmation.tsx deleted file mode 100644 index 1cc5bda..0000000 --- a/sigap-website/app/_components/email-templates/user-confirmation.tsx +++ /dev/null @@ -1,126 +0,0 @@ -import { - Body, - Container, - Head, - Heading, - Hr, - Html, - Preview, - Section, - Text, -} from "@react-email/components"; -import * as React from "react"; - -interface UserConfirmationProps { - name: string; - messageType: string; - message: string; -} - -export const UserConfirmation = ({ - name, - messageType, - message, -}: UserConfirmationProps) => { - return ( - - - Thank you for contacting us - - - Thank You for Contacting Us - -
- Dear {name}, - - - We have received your message regarding "{messageType}" and will - get back to you as soon as possible. - - - Here's a copy of your message: - {message} - - - If you have any further questions, please don't hesitate to - contact us. - -
- -
- - - Best regards, -
- The Support Team -
-
- - - ); -}; - -const main = { - backgroundColor: "#1C1C1C", - fontFamily: - '-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Ubuntu,sans-serif', -}; - -const container = { - backgroundColor: "#2E2E2E", - margin: "0 auto", - padding: "20px 0 48px", - marginBottom: "64px", - borderRadius: "8px", -}; - -const section = { - padding: "0 48px", -}; - -const h1 = { - color: "#10B981", // Emerald-500 - fontSize: "24px", - fontWeight: "bold", - margin: "40px 0", - padding: "0", - textAlign: "center" as const, -}; - -const text = { - color: "#FFFFFF", - fontSize: "14px", - lineHeight: "24px", - margin: "16px 0", -}; - -const messageLabel = { - color: "#A1A1AA", // Zinc-400 - fontSize: "14px", - margin: "24px 0 8px", -}; - -const messageBox = { - backgroundColor: "#3F3F46", // Zinc-700 - padding: "16px", - borderRadius: "4px", - color: "#FFFFFF", - fontSize: "14px", - lineHeight: "24px", - margin: "0 0 24px", -}; - -const hr = { - borderColor: "#3F3F46", // Zinc-700 - margin: "20px 0", -}; - -const signature = { - color: "#FFFFFF", - fontSize: "14px", - lineHeight: "24px", - margin: "16px 0", - textAlign: "center" as const, -}; - -export default UserConfirmation; diff --git a/sigap-website/app/_components/email-templates/verify-identity.tsx b/sigap-website/app/_components/email-templates/verify-identity.tsx deleted file mode 100644 index a5c139a..0000000 --- a/sigap-website/app/_components/email-templates/verify-identity.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import * as React from "react"; - -interface OTPEmailTemplateProps { - OTP: string; -} - -export const OTPEmailTemplate: React.FC> = ({ - OTP, -}) => ( -
- - - - - - - - - - - - -
-

Your OTP Code

-
-

- Use the following OTP to complete your verification process: -

-

- {OTP} -

-

- This OTP is valid for the next 10 minutes. Please do not share it - with anyone. -

-
-

- If you did not request this code, please ignore this email or - contact support. -

-

- © {new Date().getFullYear()} Your Company. All rights - reserved. -

-
-
-); diff --git a/sigap-website/app/_components/logo/sigap-logo.tsx b/sigap-website/app/_components/logo/sigap-logo.tsx deleted file mode 100644 index fdcf715..0000000 --- a/sigap-website/app/_components/logo/sigap-logo.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import React from "react"; - -const SigapLogo: React.FC = () => { - return ( -
- S -
- ); -}; - -export default SigapLogo; diff --git a/sigap-website/app/_components/state-screen.tsx b/sigap-website/app/_components/state-screen.tsx deleted file mode 100644 index 45db324..0000000 --- a/sigap-website/app/_components/state-screen.tsx +++ /dev/null @@ -1,74 +0,0 @@ -"use client"; - -import { useEffect } from "react"; -import { useRouter } from "next/navigation"; - -interface StateScreenProps { - type: "success" | "error" | "info" | "warning" | "empty"; - message: string; - redirectUrl?: string; - delay?: number; -} - -export function StateScreen({ - type, - message, - redirectUrl, - delay = 3000, -}: StateScreenProps) { - const router = useRouter(); - - useEffect(() => { - if (redirectUrl) { - const timer = setTimeout(() => { - router.push(redirectUrl); - }, delay); - - return () => clearTimeout(timer); - } - }, [redirectUrl, delay, router]); - - const getStyles = () => { - switch (type) { - case "success": - return "bg-green-100 text-green-800"; - case "error": - return "bg-red-100 text-red-800"; - case "info": - return "bg-blue-100 text-blue-800"; - case "warning": - return "bg-yellow-100 text-yellow-800"; - case "empty": - return "bg-gray-100 text-gray-800"; - default: - return ""; - } - }; - - const getTitle = () => { - switch (type) { - case "success": - return "Success!"; - case "error": - return "Error!"; - case "info": - return "Info"; - case "warning": - return "Warning!"; - case "empty": - return "No Content"; - default: - return ""; - } - }; - - return ( -
-

{getTitle()}

-

{message}

- {redirectUrl &&

Redirecting...

} -
- ); -} diff --git a/sigap-website/app/_components/ui/toast.tsx b/sigap-website/app/_components/ui/toast.tsx deleted file mode 100644 index d65e2da..0000000 --- a/sigap-website/app/_components/ui/toast.tsx +++ /dev/null @@ -1,168 +0,0 @@ -"use client"; - -import * as React from "react"; -import * as ToastPrimitives from "@radix-ui/react-toast"; -import { cva, type VariantProps } from "class-variance-authority"; -import { X } from "lucide-react"; -import { cn } from "@/lib/utils"; - -const ToastProvider = ToastPrimitives.Provider; - -const toastViewportVariants = cva( - "fixed z-[100] flex max-h-screen w-full flex-col-reverse p-4 md:max-w-[420px]", - { - variants: { - position: { - "top-right": "top-0 right-0", - "top-left": "top-0 left-0", - "bottom-right": "bottom-0 right-0 flex-col", - "bottom-left": "bottom-0 left-0 flex-col", - "top-center": "top-0 left-1/2 -translate-x-1/2", - "bottom-center": "bottom-0 left-1/2 -translate-x-1/2 flex-col", - }, - }, - defaultVariants: { - position: "top-right", - }, - } -); - -interface ToastViewportProps - extends React.ComponentPropsWithoutRef, - VariantProps {} - -const ToastViewport = React.forwardRef< - React.ElementRef, - ToastViewportProps ->(({ className, position, ...props }, ref) => ( - -)); -ToastViewport.displayName = ToastPrimitives.Viewport.displayName; - -const toastVariants = cva( - "group pointer-events-auto relative flex w-full items-center justify-between space-x-2 overflow-hidden rounded-md border p-4 pr-6 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80", - { - variants: { - variant: { - default: "border bg-background text-foreground", - destructive: - "destructive group border-destructive bg-destructive text-destructive-foreground", - success: "border-green-500 bg-green-500 text-white", - warning: "border-yellow-500 bg-yellow-500 text-white", - info: "border-blue-500 bg-blue-500 text-white", - }, - position: { - "top-right": - "data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-right-full", - "top-left": - "data-[state=closed]:slide-out-to-left-full data-[state=open]:slide-in-from-left-full", - "bottom-right": - "data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-right-full", - "bottom-left": - "data-[state=closed]:slide-out-to-left-full data-[state=open]:slide-in-from-left-full", - "top-center": - "data-[state=closed]:slide-out-to-top-full data-[state=open]:slide-in-from-top-full", - "bottom-center": - "data-[state=closed]:slide-out-to-bottom-full data-[state=open]:slide-in-from-bottom-full", - }, - }, - defaultVariants: { - variant: "default", - position: "top-right", - }, - } -); - -interface ToastProps - extends React.ComponentPropsWithoutRef, - VariantProps {} - -const Toast = React.forwardRef< - React.ElementRef, - ToastProps ->(({ className, variant, position, ...props }, ref) => { - return ( - - ); -}); -Toast.displayName = ToastPrimitives.Root.displayName; - -// Rest of the components remain the same -const ToastAction = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -ToastAction.displayName = ToastPrimitives.Action.displayName; - -const ToastClose = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - - - -)); -ToastClose.displayName = ToastPrimitives.Close.displayName; - -const ToastTitle = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -ToastTitle.displayName = ToastPrimitives.Title.displayName; - -const ToastDescription = React.forwardRef< - React.ElementRef, - React.ComponentPropsWithoutRef ->(({ className, ...props }, ref) => ( - -)); -ToastDescription.displayName = ToastPrimitives.Description.displayName; - -type ToastActionElement = React.ReactElement; - -export { - type ToastProps, - type ToastActionElement, - ToastProvider, - ToastViewport, - Toast, - ToastTitle, - ToastDescription, - ToastClose, - ToastAction, -}; diff --git a/sigap-website/app/_components/ui/toaster.tsx b/sigap-website/app/_components/ui/toaster.tsx deleted file mode 100644 index 7e5b0b1..0000000 --- a/sigap-website/app/_components/ui/toaster.tsx +++ /dev/null @@ -1,35 +0,0 @@ -"use client"; - -import { useToast } from "@/app/_hooks/use-toast"; -import { - Toast, - ToastClose, - ToastDescription, - ToastProvider, - ToastTitle, - ToastViewport, -} from "@/app/_components/ui/toast"; - -export function Toaster() { - const { toasts } = useToast(); - - return ( - - {toasts.map(function ({ id, title, description, action, ...props }) { - return ( - -
- {title && {title}} - {description && ( - {description} - )} -
- {action} - -
- ); - })} - -
- ); -} diff --git a/sigap-website/app/_hooks/use-supabase.ts b/sigap-website/app/_hooks/use-supabase.ts deleted file mode 100644 index 2aac5c2..0000000 --- a/sigap-website/app/_hooks/use-supabase.ts +++ /dev/null @@ -1,74 +0,0 @@ -/** - * Custom hook to interact with Supabase client. - * - * @returns {Object} An object containing the Supabase client and session management functions. - * - * @function - * @name useSupabase - * - * @example - * const { supabase, getSession, refreshSession, setSession } = useSupabase(); - * - * @property {Object} supabase - The Supabase client instance. - * @property {Function} getSession - Function to get the current session. - * @property {Function} refreshSession - Function to refresh the current session. - * @property {Function} setSession - Function to set a new session with access and refresh tokens. - * - * @typedef {Object} Session - * @property {string} access_token - The access token for the session. - * @property {string} refresh_token - The refresh token for the session. - * - * @throws {Error} Throws an error if there is an issue with getting or setting the session. - */ -// - -import { createClient } from "@/utils/supabase/client"; - -export const useSupabase = () => { - const supabase = createClient(); - - const getSession = async () => { - const { - data: { session }, - error, - } = await supabase.auth.getSession(); - - if (error) { - throw new Error(error.message); - } - - const { access_token, refresh_token }: any = session; - - await setSession(access_token, refresh_token); - - return session; - }; - - const refreshSession = async () => { - const { - data: { session }, - error, - } = await supabase.auth.refreshSession(); - - return session; - }; - - const setSession = async (access_token: string, refresh_token: string) => { - const { data, error } = await supabase.auth.setSession({ - access_token, - refresh_token, - }); - - if (error) { - throw new Error(error.message); - } - - return true; - }; - - return { - getSession, - refreshSession, - setSession, - }; -}; diff --git a/sigap-website/app/layout.tsx b/sigap-website/app/layout.tsx index f8159f6..4bf1fb9 100644 --- a/sigap-website/app/layout.tsx +++ b/sigap-website/app/layout.tsx @@ -1,9 +1,14 @@ +import DeployButton from "@/components/deploy-button"; +import { EnvVarWarning } from "@/components/env-var-warning"; +import HeaderAuth from "@/components/header-auth"; +import { ThemeSwitcher } from "@/components/theme-switcher"; import { hasEnvVars } from "@/utils/supabase/check-env-vars"; import { Geist } from "next/font/google"; import { ThemeProvider } from "next-themes"; import Link from "next/link"; import "./globals.css"; -import { Toaster } from "./_components/ui/toaster"; +import ReactQueryProvider from "@/providers/react-query-provider"; +import { Toaster } from "@/components/ui/sonner"; const defaultUrl = process.env.VERCEL_URL ? `https://${process.env.VERCEL_URL}` @@ -11,8 +16,8 @@ const defaultUrl = process.env.VERCEL_URL export const metadata = { metadataBase: new URL(defaultUrl), - title: "Sigap", - description: "Sigap is a platform for managing your data.", + title: "Next.js and Supabase Starter Kit", + description: "The fastest way to build apps with Next.js and Supabase", }; const geistSans = Geist({ @@ -34,9 +39,10 @@ export default function RootLayout({ enableSystem disableTransitionOnChange > -
-
- {/*
+
+ diff --git a/sigap-website/app/page.tsx b/sigap-website/app/page.tsx index c22047a..9144694 100644 --- a/sigap-website/app/page.tsx +++ b/sigap-website/app/page.tsx @@ -1,7 +1,7 @@ +import Hero from "@/components/hero"; +import ConnectSupabaseSteps from "@/components/tutorial/connect-supabase-steps"; +import SignUpUserSteps from "@/components/tutorial/sign-up-user-steps"; import { hasEnvVars } from "@/utils/supabase/check-env-vars"; -import SignUpUserSteps from "./_components/tutorial/sign-up-user-steps"; -import ConnectSupabaseSteps from "./_components/tutorial/connect-supabase-steps"; -import Hero from "@/app/_components/hero"; export default async function Home() { return ( diff --git a/sigap-website/app/success/page.tsx b/sigap-website/app/protected/(admin)/dashboard/page.tsx similarity index 100% rename from sigap-website/app/success/page.tsx rename to sigap-website/app/protected/(admin)/dashboard/page.tsx diff --git a/sigap-website/app/protected/(admin-pages)/layout.tsx b/sigap-website/app/protected/(admin)/layout.tsx similarity index 82% rename from sigap-website/app/protected/(admin-pages)/layout.tsx rename to sigap-website/app/protected/(admin)/layout.tsx index 6befcbb..a9b246f 100644 --- a/sigap-website/app/protected/(admin-pages)/layout.tsx +++ b/sigap-website/app/protected/(admin)/layout.tsx @@ -1,5 +1,5 @@ import type React from "react"; -import { AppSidebar } from "@/app/protected/(admin-pages)/_components/app-sidebar"; + import { Breadcrumb, BreadcrumbItem, @@ -7,24 +7,27 @@ import { BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, -} from "@/app/_components/ui/breadcrumb"; -import { Button } from "@/app/_components/ui/button"; +} from "@/components/ui/breadcrumb"; +import { Button } from "@/components/ui/button"; import { SidebarInset, SidebarProvider, SidebarTrigger, -} from "@/app/_components/ui/sidebar"; +} from "@/components/ui/sidebar"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, -} from "@/app/_components/ui/dropdown-menu"; +} from "@/components/ui/dropdown-menu"; import { MoreHorizontal } from "lucide-react"; -import { InboxDrawer } from "@/app/_components/inbox-drawer"; -import { ThemeSwitcher } from "@/app/_components/theme-switcher"; -import FloatingActionSearchBar from "@/app/_components/floating-action-search-bar"; -import { Separator } from "@/app/_components/ui/separator"; + +import { ThemeSwitcher } from "@/components/theme-switcher"; +import { Separator } from "@/components/ui/separator"; +import { InboxDrawer } from "@/components/inbox-drawer"; +import FloatingActionSearchBar from "@/components/floating-action-search-bar"; +import { AppSidebar } from "@/components/admin/app-sidebar"; + export default async function Layout({ children, @@ -32,6 +35,7 @@ export default async function Layout({ children: React.ReactNode; }) { return ( + <> @@ -78,5 +82,6 @@ export default async function Layout({ {children} + ); } diff --git a/sigap-website/app/protected/(admin-pages)/_components/users/add-user.tsx b/sigap-website/app/protected/(admin-pages)/_components/users/add-user.tsx deleted file mode 100644 index a8ea8fd..0000000 --- a/sigap-website/app/protected/(admin-pages)/_components/users/add-user.tsx +++ /dev/null @@ -1,235 +0,0 @@ -"use client"; - -import { useState } from "react"; -import { Button } from "@/app/_components/ui/button"; -import { - Sheet, - SheetContent, - SheetDescription, - SheetFooter, - SheetHeader, - SheetTitle, -} from "@/app/_components/ui/sheet"; -import { Input } from "@/app/_components/ui/input"; -import { Label } from "@/app/_components/ui/label"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/app/_components/ui/select"; -import { User } from "./users-table"; - -interface AddUserSheetProps { - open: boolean; - onOpenChange: (open: boolean) => void; - onSave: ( - userData: Omit & { password: string } - ) => void; -} - -export function AddUserSheet({ - open, - onOpenChange, - onSave, -}: AddUserSheetProps) { - const [userData, setUserData] = useState< - Omit & { password: string } - >({ - email: "", - firstName: "", - lastName: "", - avatar: "/placeholder.svg?height=40&width=40", - role: "user", - status: "active", - password: "", - }); - - const [errors, setErrors] = useState>({}); - - const validateForm = () => { - const newErrors: Record = {}; - - if (!userData.email) { - newErrors.email = "Email is required"; - } else if (!/\S+@\S+\.\S+/.test(userData.email)) { - newErrors.email = "Email is invalid"; - } - - if (!userData.password) { - newErrors.password = "Password is required"; - } else if (userData.password.length < 6) { - newErrors.password = "Password must be at least 6 characters"; - } - - if (!userData.firstName) { - newErrors.firstName = "First name is required"; - } - - if (!userData.lastName) { - newErrors.lastName = "Last name is required"; - } - - setErrors(newErrors); - return Object.keys(newErrors).length === 0; - }; - - const handleSubmit = () => { - if (validateForm()) { - onSave(userData); - // Reset form - setUserData({ - email: "", - firstName: "", - lastName: "", - avatar: "/placeholder.svg?height=40&width=40", - role: "user", - status: "active", - password: "", - }); - setErrors({}); - } - }; - - const handleInputChange = (field: keyof typeof userData, value: string) => { - setUserData((prev) => ({ ...prev, [field]: value })); - // Clear error when field is edited - if (errors[field]) { - setErrors((prev) => { - const newErrors = { ...prev }; - delete newErrors[field]; - return newErrors; - }); - } - }; - - return ( - - - - Add New User - - Create a new user account with Supabase authentication. - - -
-
- - handleInputChange("email", e.target.value)} - className="col-span-3" - /> - {errors.email && ( -

- {errors.email} -

- )} -
- -
- - handleInputChange("password", e.target.value)} - className="col-span-3" - /> - {errors.password && ( -

- {errors.password} -

- )} -
- -
- - handleInputChange("firstName", e.target.value)} - className="col-span-3" - /> - {errors.firstName && ( -

- {errors.firstName} -

- )} -
- -
- - handleInputChange("lastName", e.target.value)} - className="col-span-3" - /> - {errors.lastName && ( -

- {errors.lastName} -

- )} -
- -
- - -
- -
- - -
-
- - - -
-
- ); -} diff --git a/sigap-website/app/protected/(admin-pages)/_components/users/column.tsx b/sigap-website/app/protected/(admin-pages)/_components/users/column.tsx deleted file mode 100644 index 99e2c8d..0000000 --- a/sigap-website/app/protected/(admin-pages)/_components/users/column.tsx +++ /dev/null @@ -1,232 +0,0 @@ -"use client"; - -import { useMemo } from "react"; -import { type ColumnDef } from "@tanstack/react-table"; -import { ArrowUpDown, Filter, MoreVertical } from "lucide-react"; - -import { Button } from "@/app/_components/ui/button"; -import { Checkbox } from "@/app/_components/ui/checkbox"; -import { - DropdownMenu, - DropdownMenuCheckboxItem, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuLabel, - DropdownMenuSeparator, - DropdownMenuTrigger, -} from "@/app/_components/ui/dropdown-menu"; -import { - Avatar, - AvatarFallback, - AvatarImage, -} from "@/app/_components/ui/avatar"; - -import { User } from "@/types/user"; - -interface ColumnOptions { - openEditSheet: (user: User) => void; - handleRoleFilter: (role: string) => void; - handleStatusFilter: (status: string) => void; -} - -export const useColumns = ({ - openEditSheet, - handleRoleFilter, - handleStatusFilter, -}: ColumnOptions) => { - // Use useMemo to prevent unnecessary re-creation of columns array - const columns = useMemo[]>( - () => [ - { - id: "select", - header: ({ table }) => ( - - table.toggleAllPageRowsSelected(!!value) - } - aria-label="Select all" - /> - ), - cell: ({ row }) => ( - row.toggleSelected(!!value)} - aria-label="Select row" - /> - ), - enableSorting: false, - enableHiding: false, - }, - { - accessorKey: "usersColoumn", - header: ({ column }) => { - return ( - - ); - }, - cell: ({ row }) => { - const user = row.original; - return ( -
- - - - {user.firstName[0]} - {user.lastName[0]} - - -
-
- {user.firstName} {user.lastName} -
-
- {user.email} -
-
-
- ); - }, - filterFn: (row, id, filterValue) => { - const searchTerm = filterValue.toLowerCase(); - const user = row.original; - return ( - user.firstName.toLowerCase().includes(searchTerm) || - user.lastName.toLowerCase().includes(searchTerm) || - user.email.toLowerCase().includes(searchTerm) - ); - }, - }, - { - accessorKey: "role", - header: ({ column }) => { - return ( -
- Role - - - - - - Filter by Role - - handleRoleFilter("all")}> - All - - handleRoleFilter("admin")}> - Admin - - handleRoleFilter("staff")}> - Staff - - handleRoleFilter("user")}> - User - - - -
- ); - }, - }, - { - accessorKey: "status", - header: ({ column }) => { - return ( -
- Status - - - - - - Filter by Status - - handleStatusFilter("all")}> - All - - handleStatusFilter("active")} - > - Active - - handleStatusFilter("inactive")} - > - Inactive - - - -
- ); - }, - cell: ({ row }) => { - const status = row.getValue("status") as string; - return ( -
- {status} -
- ); - }, - }, - { - accessorKey: "lastSignedIn", - header: "Last Sign In", - }, - { - id: "actions", - cell: ({ row }) => { - const user = row.original; - - return ( - - - - - - Actions - navigator.clipboard.writeText(user.id)} - > - Copy user ID - - - openEditSheet(user)}> - Edit user - - - Delete user - - - - ); - }, - }, - ], - [openEditSheet, handleRoleFilter, handleStatusFilter] - ); - - return columns; -}; diff --git a/sigap-website/app/protected/(admin-pages)/_components/users/edit-user.tsx b/sigap-website/app/protected/(admin-pages)/_components/users/edit-user.tsx deleted file mode 100644 index 566ba38..0000000 --- a/sigap-website/app/protected/(admin-pages)/_components/users/edit-user.tsx +++ /dev/null @@ -1,482 +0,0 @@ -"use client"; - -import { useState, useEffect } from "react"; -import { z } from "zod"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { format } from "date-fns"; -import { CalendarIcon, X } from "lucide-react"; - -import { - Sheet, - SheetContent, - SheetDescription, - SheetHeader, - SheetTitle, - SheetFooter, - SheetClose, -} from "@/app/_components/ui/sheet"; -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/app/_components/ui/form"; -import { Input } from "@/app/_components/ui/input"; -import { Button } from "@/app/_components/ui/button"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/app/_components/ui/select"; -import { Textarea } from "@/app/_components/ui/textarea"; -import { - Avatar, - AvatarFallback, - AvatarImage, -} from "@/app/_components/ui/avatar"; -import { Calendar } from "@/app/_components/ui/calendar"; -import { - Popover, - PopoverContent, - PopoverTrigger, -} from "@/app/_components/ui/popover"; -import { Separator } from "@/app/_components/ui/separator"; -import { - Tabs, - TabsContent, - TabsList, - TabsTrigger, -} from "@/app/_components/ui/tabs"; -import { User } from "./users-table"; - -// Create a schema for form validation -const userFormSchema = z.object({ - // User fields - email: z.string().email({ message: "Please enter a valid email address" }), - firstName: z.string().min(1, { message: "First name is required" }), - lastName: z.string().min(1, { message: "Last name is required" }), - avatar: z.string().optional(), - role: z.enum(["admin", "staff", "user"]), - password: z - .string() - .min(8, { message: "Password must be at least 8 characters long" }) - .optional(), - - // Profile fields - bio: z.string().optional(), - phone: z.string().optional(), - address: z.string().optional(), - city: z.string().optional(), - country: z.string().optional(), - birthDate: z.date().optional(), -}); - -type UserFormValues = z.infer; - -// Extended user type to include profile information -type UserWithProfile = User & { - profile?: { - bio?: string; - phone?: string; - address?: string; - city?: string; - country?: string; - birthDate?: Date; - }; -}; - -interface EditUserSheetProps { - open: boolean; - onOpenChange: (open: boolean) => void; - user?: UserWithProfile | null; - onSave: (user: UserWithProfile) => void; - isNew?: boolean; -} - -export function EditUserSheet({ - open, - onOpenChange, - user, - onSave, - isNew = false, -}: EditUserSheetProps) { - const form = useForm({ - resolver: zodResolver(userFormSchema), - defaultValues: { - email: "", - firstName: "", - lastName: "", - avatar: "/placeholder.svg?height=40&width=40", - role: "user", - password: isNew ? "" : undefined, - bio: "", - phone: "", - address: "", - city: "", - country: "", - birthDate: undefined, - }, - }); - - // Update form when user changes - useEffect(() => { - if (user) { - const birthDate = user.profile?.birthDate - ? new Date(user.profile.birthDate) - : undefined; - - form.reset({ - email: user.email, - firstName: user.firstName, - lastName: user.lastName, - avatar: user.avatar, - role: user.role, - // Don't populate password for existing users - bio: user.profile?.bio || "", - phone: user.profile?.phone || "", - address: user.profile?.address || "", - city: user.profile?.city || "", - country: user.profile?.country || "", - birthDate: birthDate, - }); - } else if (isNew) { - form.reset({ - email: "", - firstName: "", - lastName: "", - avatar: "/placeholder.svg?height=40&width=40", - role: "user", - password: "", - bio: "", - phone: "", - address: "", - city: "", - country: "", - birthDate: undefined, - }); - } - }, [user, isNew, form]); - - const onSubmit = (data: UserFormValues) => { - // Prepare the user object with profile - const updatedUser: UserWithProfile = { - id: user?.id || "new-id", // In a real app, this would be handled by the backend - email: data.email, - firstName: data.firstName || "", - lastName: data.lastName || "", - avatar: data.avatar || "/placeholder.svg?height=40&width=40", - role: data.role, - status: user?.status || "active", - lastSignedIn: - user?.lastSignedIn || new Date().toISOString().split("T")[0], - profile: { - bio: data.bio, - phone: data.phone, - address: data.address, - city: data.city, - country: data.country, - birthDate: data.birthDate, - }, - }; - - // Save the user - onSave(updatedUser); - - // Close the sheet - onOpenChange(false); - }; - - return ( - - - - {isNew ? "Add New User" : "Edit User"} - - {isNew - ? "Fill in the details to create a new user account." - : "Make changes to the user profile here."} - - -
- - - User Details - Profile Information - -
- - -
- - - - {form.watch("firstName")?.charAt(0) || ""} - {form.watch("lastName")?.charAt(0) || ""} - - -
- -
- ( - - First Name - - - - - - )} - /> - ( - - Last Name - - - - - - )} - /> -
- - ( - - Email - - - - - - )} - /> - - {isNew && ( - ( - - Password - - - - - Minimum 8 characters - - - - )} - /> - )} - - ( - - Role - - - - )} - /> - - ( - - Avatar URL - - - - - - )} - /> -
- - - ( - - Bio - -