diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..ac72d37 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,192 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## Project Overview + +ScaleSite is a Next.js 16 frontend-only application for a digital agency service platform. It provides web design and AI automation services with a complete client portal. The application uses mock data only—no backend or database. + +**Tech Stack:** +- Next.js 16.1.6 with App Router and Turbopack +- React 19.2.3 +- TypeScript 5 +- Tailwind CSS v4 (CSS-based theme configuration) +- shadcn/ui components (Radix UI primitives) +- Lucide React icons +- React Hook Form + Zod validation + +## Development Commands + +```bash +# Start development server (runs on port 3000 by default) +npm run dev + +# Production build +npm run build + +# Start production server +npm start + +# Lint +npm run lint +``` + +## Architecture + +### Page Structure + +**Public Pages (Marketing Layout)** +- `/` - Landing page with hero, services, pricing, testimonials, FAQ +- `/services` - Service selection with toggle between Web Design and AI Automation +- `/login` and `/register` - Authentication pages + +**Protected Pages (Dashboard Layout)** +- `/dashboard` - Client dashboard with stats, projects, tickets +- `/billing` - Subscription management, payment methods, invoice history + +**Checkout Flow** +- `/checkout` - Multi-step checkout (Account → Billing → Payment) + +### Layouts + +Pages use one of two layout wrappers: + +**MarketingLayout** (`components/layouts/marketing-layout.tsx`) +- Used by: `/`, `/services` +- Includes: SiteHeader, SiteFooter +- Sticky header with glass morphism effect + +**Dashboard Layout** (sidebar layout) +- Used by: `/dashboard`, `/billing` +- Note: Dashboard pages currently render inline without a wrapper component +- Components reference: `DashboardSidebar` in `components/layouts/dashboard-sidebar.tsx` +- Navigation items: Dashboard, Projects, AI Automations, Support, Billing, Settings + +**Auth Layout** (`components/auth/auth-layout.tsx`) +- Used by: `/login`, `/register` +- Split screen: hero image (left) + form (right) +- No header/footer + +**Minimal Layout** +- Used by: `/checkout` +- Logo header only, no navigation + +### Data & Types + +**TypeScript Types** (`lib/types/index.ts`) +- `User`, `ServiceType`, `PricingTier`, `PricingPlan` +- `Project`, `ProjectStatus`, `ProjectStage` +- `SupportTicket`, `TicketStatus` +- `Invoice`, `InvoiceStatus`, `PaymentMethod`, `Subscription` +- `CheckoutSession`, `CheckoutStep`, `Address` +- `Testimonial`, `FAQ`, `DashboardStats` + +**Mock Data** (`lib/mock-data/`) +- All data is mocked—no API calls +- Exports: `mockProjects`, `mockSupportTickets`, `mockInvoices`, `mockTestimonials`, `mockPricingPlans`, etc. +- Helper functions: `getPricingPlanById()`, `getPricingPlansByServiceType()`, `getAllFAQs()` + +### Component Organization + +``` +components/ +├── auth/ # Auth-related components (social login, password input) +├── billing/ # Subscription, payment methods, invoice table +├── checkout/ # Checkout steps, summary, payment forms +├── dashboard/ # Stats cards, project cards, tickets +├── layouts/ # Marketing layout, header, footer, sidebar +├── marketing/ # Hero, service cards, pricing, testimonials, FAQ +├── mobile/ # Mobile-specific components (if implementing separate views) +└── ui/ # shadcn/ui base components (button, card, input, etc.) +``` + +### Icon System + +**Lucide React Only** +- All icons use Lucide React (`lucide-react` package) +- Icons passed as components: `icon={Rocket}` not `icon="rocket"` +- Do NOT use Material Symbols (previously caused display issues) +- Import pattern: `import { Rocket, Bot, ArrowRight } from 'lucide-react'` + +**Component Props Pattern** +For components that accept icons: +```tsx +import { LucideIcon } from 'lucide-react' + +interface Props { + icon: LucideIcon // Pass component, not string +} + +export function Component({ icon: Icon }: Props) { + return +} +``` + +### Styling & Theme + +**Tailwind CSS v4 Configuration** (app/globals.css) +- Theme defined inline with `@theme` directive +- Custom colors: + - `primary`: #8B5CF6 (Electric Violet) + - `background`: #0B0B0B (Deep Obsidian) + - `surface`: #1E1E1E (Slate Gray) +- Dark mode by default: `` in root layout + +**Utility Classes** +- `.glow-button` - Primary button with purple glow effect +- `.glass-card` - Glass morphism card background +- `.glass-nav` - Glass navigation header + +### Special Patterns + +**Checkout Suspense Boundary** +Checkout page uses `useSearchParams` which requires a Suspense boundary: +```tsx +// app/checkout/page.tsx +import { Suspense } from 'react' +import { CheckoutContent } from './checkout-content' + +export default function CheckoutPage() { + return ( + Loading...}> + + + ) +} +``` + +The actual client component with `useSearchParams` is in `checkout-content.tsx`. + +**Status Color Mapping** +Several components use Record types for status-based styling: +```tsx +const statusColors: Record = { + discovery: 'bg-blue-500/10 text-blue-400', + design: 'bg-purple-500/10 text-purple-400', + // ... +} +``` + +## Common Tasks + +**Adding a New Page** +1. Create `app/route-name/page.tsx` +2. For marketing pages: wrap with `MarketingLayout` +3. For dashboard pages: render content inline (layout wrapper not yet implemented) + +**Adding Mock Data** +1. Create file in `lib/mock-data/` +2. Export data and any helper functions +3. Re-export in `lib/mock-data/index.ts` + +**Updating Icons** +- Always use Lucide React components +- Search available icons: https://lucide.dev/icons/ +- Import and pass as component props + +## Known Limitations + +- No backend—all data is mocked +- Dashboard layout wrapper not implemented (pages render sidebar content inline) +- No authentication flow (login/register pages are UI only) +- No form validation connected (React Hook Form + Zod installed but not implemented) diff --git a/app/billing/layout.tsx b/app/billing/layout.tsx new file mode 100644 index 0000000..c8c7171 --- /dev/null +++ b/app/billing/layout.tsx @@ -0,0 +1,16 @@ +import { DashboardSidebar } from '@/components/layouts/dashboard-sidebar' + +export default function BillingLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( +
+ +
+ {children} +
+
+ ) +} diff --git a/app/billing/page.tsx b/app/billing/page.tsx new file mode 100644 index 0000000..5af5561 --- /dev/null +++ b/app/billing/page.tsx @@ -0,0 +1,34 @@ +import { SubscriptionCard } from '@/components/billing/subscription-card' +import { PaymentMethodList } from '@/components/billing/payment-method-list' +import { InvoiceTable } from '@/components/billing/invoice-table' +import { mockSubscription, mockPaymentMethods, mockInvoices } from '@/lib/mock-data' + +export default function BillingPage() { + return ( +
+ {/* Header */} +
+

+ Billing & Invoices +

+

+ Manage your subscription and payment methods +

+
+ + {/* Billing Content */} +
+ {/* Left Column - Subscription & Payment Methods */} +
+ + +
+ + {/* Right Column - Invoice History */} +
+ +
+
+
+ ) +} diff --git a/app/checkout/checkout-content.tsx b/app/checkout/checkout-content.tsx new file mode 100644 index 0000000..3e73f79 --- /dev/null +++ b/app/checkout/checkout-content.tsx @@ -0,0 +1,285 @@ +'use client' + +import { useState } from 'react' +import { useSearchParams } from 'next/navigation' +import { Card } from '@/components/ui/card' +import { CheckoutSteps } from '@/components/checkout/checkout-steps' +import { CheckoutSummary } from '@/components/checkout/checkout-summary' +import { PaymentMethodCard } from '@/components/checkout/payment-method-card' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' +import { getPricingPlanById } from '@/lib/mock-data' +import Link from 'next/link' +import { Rocket, ChevronRight } from 'lucide-react' + +export function CheckoutContent() { + const searchParams = useSearchParams() + const planId = searchParams.get('plan') || 'growth-web' + const step = (searchParams.get('step') as 'account' | 'billing' | 'payment') || 'account' + + const [currentStep, setCurrentStep] = useState<'account' | 'billing' | 'payment'>(step) + const [selectedPaymentMethod, setSelectedPaymentMethod] = useState('card') + + const plan = getPricingPlanById(planId) + + if (!plan) { + return ( +
+
+

Plan not found

+ +
+
+ ) + } + + const serviceName = plan.serviceType === 'web-design' ? 'Web Design' : 'AI Automation' + + return ( +
+ {/* Header */} +
+
+ +
+ +
+

ScaleSite

+ +
+
+ +
+ {/* Steps */} + + +
+ {/* Left side - Forms */} +
+ {/* Account Step */} + {currentStep === 'account' && ( + +

Account Information

+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+ )} + + {/* Billing Step */} + {currentStep === 'billing' && ( + +

Billing Address

+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+ )} + + {/* Payment Step */} + {currentStep === 'payment' && ( + +

Payment Method

+
+ setSelectedPaymentMethod('card')} + /> + setSelectedPaymentMethod('paypal')} + /> + setSelectedPaymentMethod('stripe-link')} + /> +
+ + {selectedPaymentMethod === 'card' && ( +
+
+ + +
+
+
+ + +
+
+ + +
+
+
+ + +
+
+ )} +
+ )} +
+ + {/* Right side - Summary */} +
+
+ +
+
+
+
+
+ ) +} diff --git a/app/checkout/page.tsx b/app/checkout/page.tsx new file mode 100644 index 0000000..0909d8d --- /dev/null +++ b/app/checkout/page.tsx @@ -0,0 +1,10 @@ +import { Suspense } from 'react' +import { CheckoutContent } from './checkout-content' + +export default function CheckoutPage() { + return ( + Loading...}> + + + ) +} diff --git a/app/dashboard/layout.tsx b/app/dashboard/layout.tsx new file mode 100644 index 0000000..1c386bd --- /dev/null +++ b/app/dashboard/layout.tsx @@ -0,0 +1,16 @@ +import { DashboardSidebar } from '@/components/layouts/dashboard-sidebar' + +export default function DashboardLayout({ + children, +}: { + children: React.ReactNode +}) { + return ( +
+ +
+ {children} +
+
+ ) +} diff --git a/app/dashboard/page.tsx b/app/dashboard/page.tsx new file mode 100644 index 0000000..96d0204 --- /dev/null +++ b/app/dashboard/page.tsx @@ -0,0 +1,106 @@ +import { StatsCard } from '@/components/dashboard/stats-card' +import { ProjectCard } from '@/components/dashboard/project-card' +import { SupportTicketItem } from '@/components/dashboard/support-ticket-item' +import { UpgradeCard } from '@/components/dashboard/upgrade-card' +import { mockProjects, mockSupportTickets, mockDashboardStats, mockCurrentUser } from '@/lib/mock-data' +import { Button } from '@/components/ui/button' +import Link from 'next/link' +import { Rocket, Bot, Ticket, Calendar, Plus } from 'lucide-react' + +export default function DashboardPage() { + const userProjects = mockProjects + const userTickets = mockSupportTickets.slice(0, 3) + const stats = mockDashboardStats + + return ( +
+ {/* Header */} +
+
+

+ Welcome back, {mockCurrentUser.name.split(' ')[0]} +

+

+ Here's what's happening with your digital products today. +

+
+ +
+ + {/* Stats Grid */} +
+ + + + +
+ + {/* Main Content Grid */} +
+ {/* Left Column - Projects */} +
+
+

Your Projects

+ +
+ +
+ {userProjects.map((project) => ( + + ))} +
+
+ + {/* Right Column - Support & Upgrade */} +
+ {/* Upgrade Card */} + + + {/* Support Tickets */} +
+
+

Support Tickets

+ +
+ +
+ {userTickets.map((ticket) => ( + + ))} +
+
+
+
+
+ ) +} diff --git a/app/globals.css b/app/globals.css index a2dc41e..8d90a38 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,26 +1,206 @@ +@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&display=swap"); @import "tailwindcss"; -:root { - --background: #ffffff; - --foreground: #171717; -} +@custom-variant dark (&:is(.dark *)); @theme inline { --color-background: var(--background); --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); + --font-sans: "Inter", sans-serif; --font-mono: var(--font-geist-mono); + --color-sidebar-ring: var(--sidebar-ring); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar: var(--sidebar); + --color-chart-5: var(--chart-5); + --color-chart-4: var(--chart-4); + --color-chart-3: var(--chart-3); + --color-chart-2: var(--chart-2); + --color-chart-1: var(--chart-1); + --color-ring: var(--ring); + --color-input: var(--input); + --color-border: var(--border); + --color-destructive: var(--destructive); + --color-accent-foreground: var(--accent-foreground); + --color-accent: var(--accent); + --color-muted-foreground: var(--muted-foreground); + --color-muted: var(--muted); + --color-secondary-foreground: var(--secondary-foreground); + --color-secondary: var(--secondary); + --color-primary-foreground: var(--primary-foreground); + --color-primary: var(--primary); + --color-popover-foreground: var(--popover-foreground); + --color-popover: var(--popover); + --color-card-foreground: var(--card-foreground); + --color-card: var(--card); + + /* ScaleSite Custom Colors */ + --color-primary-default: #8B5CF6; + --color-primary-hover: #7c3aed; + --color-primary-glow: #A78BFA; + --color-background-light: #f6f6f8; + --color-background-dark: #0B0B0B; + --color-surface: #1E1E1E; + --color-surface-border: #2E2E2E; + --color-surface-darker: #0f172a; + --color-text-muted: #9ca3af; + --color-text-dim: #6b7280; + --color-off-white: #F3F4F6; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --radius-2xl: calc(var(--radius) + 8px); + --radius-3xl: calc(var(--radius) + 12px); + --radius-4xl: calc(var(--radius) + 16px); } -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; +:root { + --radius: 0.625rem; + /* Light mode defaults */ + --background: #f6f6f8; + --foreground: #1a1a1a; + --card: #ffffff; + --card-foreground: #1a1a1a; + --popover: #ffffff; + --popover-foreground: #1a1a1a; + --primary: #8B5CF6; + --primary-foreground: #ffffff; + --secondary: #f3f4f6; + --secondary-foreground: #1a1a1a; + --muted: #f3f4f6; + --muted-foreground: #6b7280; + --accent: #f3f4f6; + --accent-foreground: #1a1a1a; + --destructive: #ef4444; + --destructive-foreground: #ffffff; + --border: #e5e7eb; + --input: #e5e7eb; + --ring: #8B5CF6; +} + +.dark { + /* ScaleSite Dark Theme */ + --background: #0B0B0B; + --foreground: #F3F4F6; + --card: #1E1E1E; + --card-foreground: #F3F4F6; + --popover: #1E1E1E; + --popover-foreground: #F3F4F6; + --primary: #8B5CF6; + --primary-foreground: #ffffff; + --secondary: #2E2E2E; + --secondary-foreground: #F3F4F6; + --muted: #1E1E1E; + --muted-foreground: #9ca3af; + --accent: #2E2E2E; + --accent-foreground: #F3F4F6; + --destructive: #ef4444; + --destructive-foreground: #ffffff; + --border: #2E2E2E; + --input: #2E2E2E; + --ring: #8B5CF6; +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; } } -body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; +@layer utilities { + /* Glass morphism effects */ + .glass-nav { + background: rgba(11, 11, 11, 0.85); + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + } + + .glass-card { + background: rgba(30, 30, 30, 0.6); + backdrop-filter: blur(8px); + border: 1px solid rgba(139, 92, 246, 0.1); + } + + /* Glow effects */ + .glow-button { + box-shadow: 0 0 15px rgba(139, 92, 246, 0.4); + transition: all 0.3s ease; + } + + .glow-button:hover { + box-shadow: 0 0 25px rgba(139, 92, 246, 0.6); + transform: translateY(-1px); + } + + .glow-card { + transition: all 0.3s ease; + } + + .glow-card:hover { + border-color: #8B5CF6; + box-shadow: 0 0 20px rgba(139, 92, 246, 0.15); + } + + /* Text gradient */ + .text-gradient { + @apply bg-clip-text text-transparent bg-gradient-to-r from-primary to-primary-glow; + } + + /* Custom scrollbar for webkit browsers */ + .custom-scrollbar::-webkit-scrollbar { + width: 8px; + } + + .custom-scrollbar::-webkit-scrollbar-track { + background: #0B0B0B; + } + + .custom-scrollbar::-webkit-scrollbar-thumb { + background: #2E2E2E; + border-radius: 4px; + } + + .custom-scrollbar::-webkit-scrollbar-thumb:hover { + background: #8B5CF6; + } +} + +/* Global scrollbar styling */ +::-webkit-scrollbar { + width: 8px; +} + +::-webkit-scrollbar-track { + background: #0B0B0B; +} + +::-webkit-scrollbar-thumb { + background: #2E2E2E; + border-radius: 4px; +} + +::-webkit-scrollbar-thumb:hover { + background: #8B5CF6; +} + +/* Accordion / Details summary styling */ +details > summary { + list-style: none; +} + +details > summary::-webkit-details-marker { + display: none; +} + +details[open] .toggle-icon { + transform: rotate(45deg); } diff --git a/app/layout.tsx b/app/layout.tsx index f7fa87e..8a5ba38 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,20 +1,16 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; +import { Inter } from "next/font/google"; +import { Toaster } from "@/components/ui/sonner"; import "./globals.css"; -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", +const inter = Inter({ subsets: ["latin"], + variable: "--font-inter", }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "ScaleSite - AI & Web Solutions", + description: "Premium websites and intelligent automations starting at just 50€. Built for growth, designed for the future.", }; export default function RootLayout({ @@ -23,11 +19,10 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - - + + {children} + ); diff --git a/app/login/page.tsx b/app/login/page.tsx new file mode 100644 index 0000000..459be06 --- /dev/null +++ b/app/login/page.tsx @@ -0,0 +1,90 @@ +import Link from 'next/link' +import { ArrowLeft } from 'lucide-react' +import { AuthLayout } from '@/components/auth/auth-layout' +import { SocialLoginButtons } from '@/components/auth/social-login-buttons' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' + +export default function LoginPage() { + return ( + +
+ {/* Social Login */} + + + {/* Divider */} +
+
+
+
+
+ + Or continue with email + +
+
+ + {/* Login Form */} +
+
+ + +
+ +
+
+ + + Forgot password? + +
+ +
+ + +
+ + {/* Sign up link */} +

+ Don't have an account?{' '} + + Sign up + +

+ + {/* Back to home */} +
+ + + Back to home + +
+
+ + ) +} diff --git a/app/page.tsx b/app/page.tsx index 295f8fd..2abe3e3 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,65 +1,158 @@ -import Image from "next/image"; +import { MarketingLayout } from '@/components/layouts/marketing-layout' +import { HeroSection } from '@/components/marketing/hero-section' +import { ServiceCard } from '@/components/marketing/service-card' +import { PricingCard } from '@/components/marketing/pricing-card' +import { TestimonialCard } from '@/components/marketing/testimonial-card' +import { FAQItem } from '@/components/marketing/faq-item' +import { mockTestimonials, getAllFAQs, getPricingPlansByServiceType } from '@/lib/mock-data' +import { Button } from '@/components/ui/button' +import Link from 'next/link' +import { Globe, Bot, Rocket, ArrowRight } from 'lucide-react' + +const services = [ + { + icon: Globe, + title: 'Web Design', + description: 'Modern, responsive websites built with the latest technologies. From landing pages to e-commerce platforms.', + }, + { + icon: Bot, + title: 'AI Automation', + description: 'Intelligent chatbots and automation workflows that save time and enhance customer experience.', + }, + { + icon: Rocket, + title: 'Growth Solutions', + description: 'Complete digital transformation packages combining web, AI, and marketing strategies.', + }, +] export default function Home() { + const webDesignPlans = getPricingPlansByServiceType('web-design') + const testimonials = mockTestimonials.slice(0, 4) + const faqs = getAllFAQs() + return ( -
-
- Next.js logo -
-

- To get started, edit the page.tsx file. -

-

- Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. + + + + {/* Services Section */} +

+
+
+
+

+ Our Services +

+

+ Everything you need to scale your business in the digital age. +

+
+ +
+ {services.map((service, index) => ( + + ))} +
+
+
+
+ + {/* Pricing Preview Section */} +
+
+
+
+

+ Simple, Transparent Pricing +

+

+ Choose the perfect plan for your needs. All plans include our core features. +

+
+ +
+ {webDesignPlans.map((plan) => ( + + ))} +
+ +
+ +
+
+
+
+ + {/* Testimonials Section */} +
+
+
+
+

+ Trusted by Growing Businesses +

+

+ See what our clients have to say about working with us. +

+
+ +
+ {testimonials.map((testimonial) => ( + + ))} +
+
+
+
+ + {/* FAQ Section */} +
+
+
+
+

+ Frequently Asked Questions +

+

+ Common questions about our services and process. +

+
+ +
+ {faqs.map((faq) => ( + + ))} +
+
+
+
+ + {/* CTA Section */} +
+
+

+ Ready to upgrade your business? +

+

+ Join hundreds of other businesses saving time and making money with ScaleSite's affordable tech solutions.

+
- -
-
- ); + + + ) } diff --git a/app/register/page.tsx b/app/register/page.tsx new file mode 100644 index 0000000..7035a60 --- /dev/null +++ b/app/register/page.tsx @@ -0,0 +1,110 @@ +import Link from 'next/link' +import { ArrowLeft } from 'lucide-react' +import { AuthLayout } from '@/components/auth/auth-layout' +import { SocialLoginButtons } from '@/components/auth/social-login-buttons' +import { Button } from '@/components/ui/button' +import { Input } from '@/components/ui/input' +import { Label } from '@/components/ui/label' + +export default function RegisterPage() { + return ( + +
+ {/* Social Login */} + + + {/* Divider */} +
+
+
+
+
+ + Or continue with email + +
+
+ + {/* Register Form */} +
+
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ + {/* Log in link */} +

+ Already have an account?{' '} + + Log in + +

+ + {/* Back to home */} +
+ + + Back to home + +
+
+ + ) +} diff --git a/app/services/page.tsx b/app/services/page.tsx new file mode 100644 index 0000000..5f801d0 --- /dev/null +++ b/app/services/page.tsx @@ -0,0 +1,82 @@ +'use client' + +import { useState, useMemo } from 'react' +import { MarketingLayout } from '@/components/layouts/marketing-layout' +import { ServiceTypeToggle } from '@/components/services/service-type-toggle' +import { ProgressStepper } from '@/components/services/progress-stepper' +import { PricingCard } from '@/components/marketing/pricing-card' +import { TrustBadges } from '@/components/services/trust-badges' +import { getPricingPlansByServiceType, type ServiceType } from '@/lib/mock-data' +import { Button } from '@/components/ui/button' +import Link from 'next/link' + +export default function ServicesPage() { + const [serviceType, setServiceType] = useState('web-design') + + const pricingPlans = useMemo( + () => getPricingPlansByServiceType(serviceType), + [serviceType] + ) + + return ( + +
+
+ {/* Progress Stepper */} + + + {/* Service Type Toggle */} + + + {/* Header */} +
+

+ {serviceType === 'web-design' ? 'Web Design Packages' : 'AI Automation Packages'} +

+

+ {serviceType === 'web-design' + ? 'Professional websites built to convert visitors into customers. Choose the package that fits your needs.' + : 'Intelligent automation solutions that save time and boost efficiency. Start automating today.'} +

+
+ + {/* Pricing Cards */} +
+ {pricingPlans.map((plan) => ( + + ))} +
+ + {/* Trust Badges */} + + + {/* Back Button */} +
+ +
+
+
+
+ ) +} diff --git a/components.json b/components.json new file mode 100644 index 0000000..f87021e --- /dev/null +++ b/components.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "rtl": false, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "registries": {} +} diff --git a/components/auth/auth-layout.tsx b/components/auth/auth-layout.tsx new file mode 100644 index 0000000..b9e8c9a --- /dev/null +++ b/components/auth/auth-layout.tsx @@ -0,0 +1,48 @@ +import { Rocket } from 'lucide-react' +import { ReactNode } from 'react' + +interface AuthLayoutProps { + children: ReactNode + title: string + subtitle: string +} + +export function AuthLayout({ children, title, subtitle }: AuthLayoutProps) { + return ( +
+ {/* Left side - Hero */} +
+
+
+
+
+ +
+

ScaleSite

+
+

+ {title} +

+

+ {subtitle} +

+
+
+
+ + {/* Right side - Form */} +
+
+ {children} +
+
+
+ ) +} diff --git a/components/auth/social-login-buttons.tsx b/components/auth/social-login-buttons.tsx new file mode 100644 index 0000000..6a921dc --- /dev/null +++ b/components/auth/social-login-buttons.tsx @@ -0,0 +1,44 @@ +'use client' + +import { Button } from '@/components/ui/button' + +export function SocialLoginButtons() { + return ( +
+ + + +
+ ) +} diff --git a/components/billing/invoice-table.tsx b/components/billing/invoice-table.tsx new file mode 100644 index 0000000..1d53f9d --- /dev/null +++ b/components/billing/invoice-table.tsx @@ -0,0 +1,89 @@ +'use client' + +import { Download } from 'lucide-react' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { Badge } from '@/components/ui/badge' +import { Button } from '@/components/ui/button' +import type { Invoice } from '@/lib/types' + +interface InvoiceTableProps { + invoices: Invoice[] +} + +const statusColors: Record = { + paid: 'bg-emerald-500/10 text-emerald-400', + pending: 'bg-yellow-500/10 text-yellow-400', + refunded: 'bg-red-500/10 text-red-400', + cancelled: 'bg-gray-500/10 text-gray-400', +} + +export function InvoiceTable({ invoices }: InvoiceTableProps) { + return ( + + + Invoice History + + +
+ + + + + + + + + + + + {invoices.map((invoice) => ( + + + + + + + + ))} + +
+ Description + + Date + + Amount + + Status + + Download +
+

{invoice.description}

+ {invoice.projectId && ( +

+ Project: {invoice.projectId} +

+ )} +
+ {new Date(invoice.date).toLocaleDateString()} + +

+ {invoice.total} {invoice.currency} +

+
+ + {invoice.status} + + + {invoice.downloadUrl && ( + + )} +
+
+
+
+ ) +} diff --git a/components/billing/payment-method-list.tsx b/components/billing/payment-method-list.tsx new file mode 100644 index 0000000..4c60c9b --- /dev/null +++ b/components/billing/payment-method-list.tsx @@ -0,0 +1,70 @@ +'use client' +import { CreditCard, Wallet, Edit, Trash2, Plus } from 'lucide-react' + +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { Button } from '@/components/ui/button' +import { Badge } from '@/components/ui/badge' +import type { PaymentMethod } from '@/lib/types' + +interface PaymentMethodListProps { + paymentMethods: PaymentMethod[] +} + +export function PaymentMethodList({ paymentMethods }: PaymentMethodListProps) { + return ( + + + Payment Methods + + + {paymentMethods.map((method) => ( +
+
+ {/* Icon */} +
+ {method.type === 'card' ? ( + + ) : ( + + )} +
+ + {/* Details */} +
+
+

+ {method.type === 'card' + ? `${method.brand?.toUpperCase()} •••• ${method.last4}` + : `PayPal - ${method.email}`} +

+ {method.isDefault && ( + Default + )} +
+
+
+ + {/* Actions */} +
+ + +
+
+ ))} + + {/* Add New */} + +
+
+ ) +} diff --git a/components/billing/subscription-card.tsx b/components/billing/subscription-card.tsx new file mode 100644 index 0000000..f1c5207 --- /dev/null +++ b/components/billing/subscription-card.tsx @@ -0,0 +1,60 @@ +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { Button } from '@/components/ui/button' +import type { Subscription } from '@/lib/types' + +interface SubscriptionCardProps { + subscription: Subscription +} + +export function SubscriptionCard({ subscription }: SubscriptionCardProps) { + return ( + + + Current Subscription + + + {/* Plan Details */} +
+
+

Growth Plan

+

AI Automation & Web Design

+
+
+

€150

+

/month

+
+
+ + {/* Status Badge */} +
+ + {subscription.status === 'active' ? 'Active' : subscription.status} + + {subscription.status === 'active' && ( + + Renews on {new Date(subscription.currentPeriodEnd).toLocaleDateString()} + + )} +
+ + {/* Actions */} +
+ + {subscription.status === 'active' && !subscription.cancelAtPeriodEnd && ( + + )} +
+
+
+ ) +} diff --git a/components/checkout/checkout-steps.tsx b/components/checkout/checkout-steps.tsx new file mode 100644 index 0000000..6bb9f2f --- /dev/null +++ b/components/checkout/checkout-steps.tsx @@ -0,0 +1,54 @@ +import Link from 'next/link' + +interface CheckoutStepsProps { + currentStep: 'account' | 'billing' | 'payment' +} + +const steps = [ + { id: 'account', label: 'Account' }, + { id: 'billing', label: 'Billing' }, + { id: 'payment', label: 'Payment' }, +] + +export function CheckoutSteps({ currentStep }: CheckoutStepsProps) { + const currentIndex = steps.findIndex((s) => s.id === currentStep) + + return ( +
+
+ {steps.map((step, index) => ( +
+ {index > 0 && ( +
+ )} + + {index < currentIndex ? '✓' : index + 1} + + + {step.label} + +
+ ))} +
+
+ ) +} diff --git a/components/checkout/checkout-summary.tsx b/components/checkout/checkout-summary.tsx new file mode 100644 index 0000000..7b81fc6 --- /dev/null +++ b/components/checkout/checkout-summary.tsx @@ -0,0 +1,79 @@ +import { Rocket, Lock, ShieldCheck } from 'lucide-react' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { Separator } from '@/components/ui/separator' +import { Button } from '@/components/ui/button' + +interface CheckoutSummaryProps { + serviceName: string + tier: string + price: number + currency: string +} + +export function CheckoutSummary({ serviceName, tier, price, currency }: CheckoutSummaryProps) { + const vat = Math.round(price * 0.2) + const total = price + vat + + return ( + + + Order Summary + + + {/* Selected Service */} +
+
+ +
+
+

{serviceName}

+

{tier} Tier

+
+

+ {price} {currency} +

+
+ + {/* Price Breakdown */} +
+
+ Subtotal + + {price} {currency} + +
+
+ VAT (20%) + + {vat} {currency} + +
+ +
+ Total + + {total} {currency} + +
+
+ + {/* Complete Purchase Button */} + + + {/* Trust Badges */} +
+
+ + Secure Payment +
+
+ + SSL Encrypted +
+
+
+
+ ) +} diff --git a/components/checkout/payment-method-card.tsx b/components/checkout/payment-method-card.tsx new file mode 100644 index 0000000..7ee5c3f --- /dev/null +++ b/components/checkout/payment-method-card.tsx @@ -0,0 +1,59 @@ +'use client' +import { CreditCard, Link as LinkIcon, Wallet } from 'lucide-react' +import { Card } from '@/components/ui/card' + +interface PaymentMethodCardProps { + id: string + icon: string + label: string + description: string + selected: boolean + onSelect: () => void +} + +const iconMap: Record> = { + card: CreditCard, + paypal: Wallet, + 'stripe-link': LinkIcon, +} + +export function PaymentMethodCard({ + id, + icon, + label, + description, + selected, + onSelect, +}: PaymentMethodCardProps) { + const IconComponent = iconMap[icon] || CreditCard + + return ( + +
+
+ {selected && ( +
+ )} +
+
+ +
+
+

{label}

+

{description}

+
+
+ + ) +} diff --git a/components/dashboard/project-card.tsx b/components/dashboard/project-card.tsx new file mode 100644 index 0000000..201b4d6 --- /dev/null +++ b/components/dashboard/project-card.tsx @@ -0,0 +1,82 @@ +'use client' +import { ArrowRight, Circle, CircleCheck } from 'lucide-react' + +import Link from 'next/link' +import { Card, CardContent } from '@/components/ui/card' +import { Badge } from '@/components/ui/badge' +import { Progress } from '@/components/ui/progress' +import type { Project } from '@/lib/types' + +interface ProjectCardProps { + project: Project +} + +const statusColors: Record = { + discovery: 'bg-blue-500/10 text-blue-400', + design: 'bg-purple-500/10 text-purple-400', + development: 'bg-yellow-500/10 text-yellow-400', + content: 'bg-orange-500/10 text-orange-400', + testing: 'bg-cyan-500/10 text-cyan-400', + completed: 'bg-emerald-500/10 text-emerald-400', +} + +export function ProjectCard({ project }: ProjectCardProps) { + return ( + + + + {/* Thumbnail */} +
+ + {/* Title and Status */} +
+
+

{project.title}

+

+ {project.description} +

+
+ + {project.status} + +
+ + {/* Progress */} +
+
+ Progress + {project.progress}% +
+ +
+ + {/* Stages */} +
+ {project.stages.map((stage, index) => ( +
+ {stage.completed ? ( + + ) : ( + + )} + + {stage.name} + + {index < project.stages.length - 1 && } +
+ ))} +
+ + {/* Updated */} +
+ Updated {new Date(project.updatedAt).toLocaleDateString()} + +
+ + + + ) +} diff --git a/components/dashboard/stats-card.tsx b/components/dashboard/stats-card.tsx new file mode 100644 index 0000000..f09b000 --- /dev/null +++ b/components/dashboard/stats-card.tsx @@ -0,0 +1,40 @@ +import { Rocket, Bot, Ticket, Calendar, LucideIcon, Plus } from 'lucide-react' +import { Card, CardContent } from '@/components/ui/card' +import { Badge } from '@/components/ui/badge' + +interface StatsCardProps { + value: string | number + label: string + icon: LucideIcon + iconColor?: string + trend?: string + trendColor?: string +} + +export function StatsCard({ + value, + label, + icon: Icon, + iconColor = 'bg-primary/10 text-primary', + trend, + trendColor = 'bg-emerald-500/10 text-emerald-400 border-emerald-500/10', +}: StatsCardProps) { + return ( + + +
+
+ +
+ {trend && ( + + {trend} + + )} +
+

{label}

+

{value}

+
+
+ ) +} diff --git a/components/dashboard/support-ticket-item.tsx b/components/dashboard/support-ticket-item.tsx new file mode 100644 index 0000000..6a6ba70 --- /dev/null +++ b/components/dashboard/support-ticket-item.tsx @@ -0,0 +1,71 @@ +'use client' +import { Circle, CircleCheck } from 'lucide-react' + +import Link from 'next/link' +import type { SupportTicket } from '@/lib/types' +import { Badge } from '@/components/ui/badge' + +const statusIcons: Record> = { + open: Circle, + 'in-progress': Circle, + resolved: CircleCheck, + closed: CircleCheck, +} + +const statusColors: Record = { + open: 'bg-emerald-500/10 text-emerald-400', + 'in-progress': 'bg-blue-500/10 text-blue-400', + resolved: 'bg-gray-500/10 text-gray-400', + closed: 'bg-gray-500/10 text-gray-400', +} + +const priorityColors: Record = { + low: '', + medium: 'bg-orange-500/10 text-orange-400', + high: 'bg-red-500/10 text-red-400', +} + +interface SupportTicketItemProps { + ticket: SupportTicket +} + +export function SupportTicketItem({ ticket }: SupportTicketItemProps) { + return ( + + {/* Status indicator */} +
+ {ticket.status === 'open' || ticket.status === 'in-progress' ? ( + + ) : ( + + )} +
+ + {/* Content */} +
+
+

{ticket.subject}

+ {ticket.priority === 'high' && ( + + High Priority + + )} +
+

+ {ticket.description} +

+
+ + {/* Ticket number */} +
+
#{ticket.id}
+ + {ticket.status} + +
+ + ) +} diff --git a/components/dashboard/upgrade-card.tsx b/components/dashboard/upgrade-card.tsx new file mode 100644 index 0000000..efdbb26 --- /dev/null +++ b/components/dashboard/upgrade-card.tsx @@ -0,0 +1,38 @@ +import { Award, CircleCheck } from 'lucide-react' +import { Card, CardContent } from '@/components/ui/card' +import { Button } from '@/components/ui/button' + +export function UpgradeCard() { + return ( + + +
+ +
+

Upgrade to Premium

+

Unlock all features

+
+
+ +
    +
  • + + Priority Support +
  • +
  • + + Unlimited Revisions +
  • +
  • + + Dedicated Account Manager +
  • +
+ + +
+
+ ) +} diff --git a/components/layouts/dashboard-sidebar.tsx b/components/layouts/dashboard-sidebar.tsx new file mode 100644 index 0000000..ccc588f --- /dev/null +++ b/components/layouts/dashboard-sidebar.tsx @@ -0,0 +1,122 @@ +'use client' + +import Link from 'next/link' +import { usePathname } from 'next/navigation' +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' +import { Badge } from '@/components/ui/badge' +import { mockCurrentUser, getActiveProjects } from '@/lib/mock-data' +import { Rocket, FolderOpen, Bot, Headphones, Receipt, Settings, LogOut, LayoutDashboard } from 'lucide-react' + +const navItems = [ + { + href: '/dashboard', + label: 'Dashboard', + icon: LayoutDashboard, + filled: true, + }, + { + href: '/dashboard/projects', + label: 'My Projects', + icon: FolderOpen, + badge: getActiveProjects().length, + }, + { + href: '/dashboard/automations', + label: 'AI Automations', + icon: Bot, + }, + { + href: '/dashboard/support', + label: 'Support', + icon: Headphones, + }, + { + href: '/billing', + label: 'Billing', + icon: Receipt, + }, + { + href: '/dashboard/settings', + label: 'Settings', + icon: Settings, + }, +] + +export function DashboardSidebar() { + const pathname = usePathname() + + return ( + + ) +} diff --git a/components/layouts/marketing-layout.tsx b/components/layouts/marketing-layout.tsx new file mode 100644 index 0000000..748e391 --- /dev/null +++ b/components/layouts/marketing-layout.tsx @@ -0,0 +1,14 @@ +import { SiteHeader } from './site-header' +import { SiteFooter } from './site-footer' + +export function MarketingLayout({ children }: { children: React.ReactNode }) { + return ( +
+ +
+ {children} +
+ +
+ ) +} diff --git a/components/layouts/site-footer.tsx b/components/layouts/site-footer.tsx new file mode 100644 index 0000000..2e55c9f --- /dev/null +++ b/components/layouts/site-footer.tsx @@ -0,0 +1,131 @@ +import Link from 'next/link' +import { Globe, Mail } from 'lucide-react' + +export function SiteFooter() { + return ( +
+
+
+ {/* Brand */} +
+
+
+ +
+ ScaleSite +
+

+ Making enterprise-grade web and AI technology accessible to everyone. +

+
+ + {/* Platform */} +
+

Platform

+
    +
  • + + Services + +
  • +
  • + + Pricing + +
  • +
  • + + Dashboard Login + +
  • +
+
+ + {/* Company */} +
+

Company

+ +
+ + {/* Legal */} + +
+ + {/* Bottom bar */} +
+

© 2024 ScaleSite. All rights reserved.

+ +
+
+
+ ) +} diff --git a/components/layouts/site-header.tsx b/components/layouts/site-header.tsx new file mode 100644 index 0000000..ac4e995 --- /dev/null +++ b/components/layouts/site-header.tsx @@ -0,0 +1,106 @@ +'use client' + +import Link from 'next/link' +import { useState } from 'react' +import { Button } from '@/components/ui/button' +import { Rocket, Menu, X } from 'lucide-react' + +export function SiteHeader() { + const [mobileMenuOpen, setMobileMenuOpen] = useState(false) + + return ( +
+
+
+ {/* Logo */} + +
+ +
+

+ ScaleSite +

+ + + {/* Desktop Navigation */} + + + {/* Right side */} +
+ + + {/* Mobile menu button */} + +
+
+
+ + {/* Mobile menu */} + {mobileMenuOpen && ( +
+ +
+ )} +
+ ) +} diff --git a/components/marketing/faq-item.tsx b/components/marketing/faq-item.tsx new file mode 100644 index 0000000..c5a1a6a --- /dev/null +++ b/components/marketing/faq-item.tsx @@ -0,0 +1,21 @@ +import { ChevronDown } from 'lucide-react' +import { FAQ } from '@/lib/types' +import { Card, CardContent } from '@/components/ui/card' + +interface FAQItemProps { + faq: FAQ +} + +export function FAQItem({ faq }: FAQItemProps) { + return ( +
+ + {faq.question} + + +
+ {faq.answer} +
+
+ ) +} diff --git a/components/marketing/hero-section.tsx b/components/marketing/hero-section.tsx new file mode 100644 index 0000000..b875d2f --- /dev/null +++ b/components/marketing/hero-section.tsx @@ -0,0 +1,57 @@ +import { Button } from '@/components/ui/button' + +export function HeroSection() { + return ( +
+ {/* Background glow effect */} +
+ +
+
+
+ {/* Hero image with gradient overlay */} +
+
+

+ Scale Your Business
+ + with AI & Web + +

+

+ Premium websites and intelligent automations starting at just 50€.{' '} +
+ Built for growth, designed for the future. +

+
+
+ + {/* CTA buttons */} +
+ + +
+
+
+
+
+ ) +} diff --git a/components/marketing/pricing-card.tsx b/components/marketing/pricing-card.tsx new file mode 100644 index 0000000..f44e566 --- /dev/null +++ b/components/marketing/pricing-card.tsx @@ -0,0 +1,69 @@ +'use client' + +import { Check } from 'lucide-react' +import { Button } from '@/components/ui/button' +import { Card, CardContent } from '@/components/ui/card' +import { Badge } from '@/components/ui/badge' +import Link from 'next/link' + +interface PricingCardProps { + tier: string + price: number + currency: string + features: string[] + popular?: boolean + planId: string +} + +export function PricingCard({ tier, price, currency, features, popular, planId }: PricingCardProps) { + return ( + + {popular && ( + + Most Popular + + )} + +
+
+

{tier}

+
+ + {price} + + {currency} +
+
+ +
    + {features.map((feature, index) => ( +
  • +
    + +
    + {feature} +
  • + ))} +
+ + +
+
+
+ ) +} diff --git a/components/marketing/service-card.tsx b/components/marketing/service-card.tsx new file mode 100644 index 0000000..0e98728 --- /dev/null +++ b/components/marketing/service-card.tsx @@ -0,0 +1,26 @@ +import { Card, CardContent } from '@/components/ui/card' +import { LucideIcon } from 'lucide-react' + +interface ServiceCardProps { + icon: LucideIcon + title: string + description: string +} + +export function ServiceCard({ icon: Icon, title, description }: ServiceCardProps) { + return ( + + +
+
+ +
+
+

{title}

+

{description}

+
+
+
+
+ ) +} diff --git a/components/marketing/testimonial-card.tsx b/components/marketing/testimonial-card.tsx new file mode 100644 index 0000000..6222b35 --- /dev/null +++ b/components/marketing/testimonial-card.tsx @@ -0,0 +1,51 @@ +import { Star } from 'lucide-react' +import { Card, CardContent } from '@/components/ui/card' +import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar' + +interface TestimonialCardProps { + name: string + role: string + company: string + avatar: string + rating: number + quote: string +} + +export function TestimonialCard({ name, role, company, avatar, rating, quote }: TestimonialCardProps) { + return ( + + +
+ {/* Rating stars */} +
+ {Array.from({ length: 5 }).map((_, i) => ( + + ))} +
+ + {/* Quote */} +

“{quote}”

+ + {/* Author */} +
+ + + {name.charAt(0)} + +
+

{name}

+

+ {role}, {company} +

+
+
+
+
+
+ ) +} diff --git a/components/services/progress-stepper.tsx b/components/services/progress-stepper.tsx new file mode 100644 index 0000000..778b17c --- /dev/null +++ b/components/services/progress-stepper.tsx @@ -0,0 +1,32 @@ +interface ProgressStepperProps { + currentStep: number + totalSteps: number + stepLabel: string +} + +export function ProgressStepper({ currentStep, totalSteps, stepLabel }: ProgressStepperProps) { + const progress = (currentStep / totalSteps) * 100 + + return ( +
+
+ Step {currentStep} of {totalSteps} +
+ +
+
+ Select Service + Complete +
+
+
+
+
+ +
{stepLabel}
+
+ ) +} diff --git a/components/services/service-type-toggle.tsx b/components/services/service-type-toggle.tsx new file mode 100644 index 0000000..8f6ef0d --- /dev/null +++ b/components/services/service-type-toggle.tsx @@ -0,0 +1,46 @@ +'use client' + +import { useState } from 'react' + +type ServiceType = 'web-design' | 'ai-automation' + +interface ServiceTypeToggleProps { + defaultValue: ServiceType + onChange: (value: ServiceType) => void +} + +export function ServiceTypeToggle({ defaultValue, onChange }: ServiceTypeToggleProps) { + const [value, setValue] = useState(defaultValue) + + const handleChange = (newValue: ServiceType) => { + setValue(newValue) + onChange(newValue) + } + + return ( +
+
+ + +
+
+ ) +} diff --git a/components/services/trust-badges.tsx b/components/services/trust-badges.tsx new file mode 100644 index 0000000..192f084 --- /dev/null +++ b/components/services/trust-badges.tsx @@ -0,0 +1,21 @@ +import { ShieldCheck, Clock, CalendarCheck, Headphones } from 'lucide-react' + +const badges = [ + { icon: ShieldCheck, text: 'Secure Payment' }, + { icon: Clock, text: '7-Day Delivery' }, + { icon: CalendarCheck, text: 'Cancel Anytime' }, + { icon: Headphones, text: '24/7 Support' }, +] + +export function TrustBadges() { + return ( +
+ {badges.map((badge, index) => ( +
+ + {badge.text} +
+ ))} +
+ ) +} diff --git a/components/ui/avatar.tsx b/components/ui/avatar.tsx new file mode 100644 index 0000000..a38fe5d --- /dev/null +++ b/components/ui/avatar.tsx @@ -0,0 +1,109 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +function Avatar({ + className, + size = "default", + ...props +}: React.ComponentProps & { + size?: "default" | "sm" | "lg" +}) { + return ( + + ) +} + +function AvatarImage({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarFallback({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarBadge({ className, ...props }: React.ComponentProps<"span">) { + return ( + svg]:hidden", + "group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2", + "group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2", + className + )} + {...props} + /> + ) +} + +function AvatarGroup({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function AvatarGroupCount({ + className, + ...props +}: React.ComponentProps<"div">) { + return ( +
svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3", + className + )} + {...props} + /> + ) +} + +export { + Avatar, + AvatarImage, + AvatarFallback, + AvatarBadge, + AvatarGroup, + AvatarGroupCount, +} diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx new file mode 100644 index 0000000..ba40cc1 --- /dev/null +++ b/components/ui/badge.tsx @@ -0,0 +1,48 @@ +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 badgeVariants = cva( + "inline-flex items-center justify-center rounded-full border border-transparent px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + secondary: + "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + destructive: + "bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + link: "text-primary underline-offset-4 [a&]:hover:underline", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Badge({ + className, + variant = "default", + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : "span" + + return ( + + ) +} + +export { Badge, badgeVariants } diff --git a/components/ui/button.tsx b/components/ui/button.tsx new file mode 100644 index 0000000..915ea2a --- /dev/null +++ b/components/ui/button.tsx @@ -0,0 +1,64 @@ +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 gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60", + outline: + "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: + "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2 has-[>svg]:px-3", + xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3", + sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5", + lg: "h-10 rounded-md px-6 has-[>svg]:px-4", + icon: "size-9", + "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3", + "icon-sm": "size-8", + "icon-lg": "size-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Button({ + className, + variant = "default", + size = "default", + asChild = false, + ...props +}: React.ComponentProps<"button"> & + VariantProps & { + asChild?: boolean + }) { + const Comp = asChild ? Slot : "button" + + return ( + + ) +} + +export { Button, buttonVariants } diff --git a/components/ui/card.tsx b/components/ui/card.tsx new file mode 100644 index 0000000..681ad98 --- /dev/null +++ b/components/ui/card.tsx @@ -0,0 +1,92 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +function Card({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardDescription({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardAction({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardContent({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function CardFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardAction, + CardDescription, + CardContent, +} diff --git a/components/ui/checkbox.tsx b/components/ui/checkbox.tsx new file mode 100644 index 0000000..cb0b07b --- /dev/null +++ b/components/ui/checkbox.tsx @@ -0,0 +1,32 @@ +"use client" + +import * as React from "react" +import * as CheckboxPrimitive from "@radix-ui/react-checkbox" +import { CheckIcon } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Checkbox({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + + ) +} + +export { Checkbox } diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx new file mode 100644 index 0000000..1f0e368 --- /dev/null +++ b/components/ui/dialog.tsx @@ -0,0 +1,158 @@ +"use client" + +import * as React from "react" +import * as DialogPrimitive from "@radix-ui/react-dialog" +import { XIcon } from "lucide-react" + +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" + +function Dialog({ + ...props +}: React.ComponentProps) { + return +} + +function DialogTrigger({ + ...props +}: React.ComponentProps) { + return +} + +function DialogPortal({ + ...props +}: React.ComponentProps) { + return +} + +function DialogClose({ + ...props +}: React.ComponentProps) { + return +} + +function DialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DialogContent({ + className, + children, + showCloseButton = true, + ...props +}: React.ComponentProps & { + showCloseButton?: boolean +}) { + return ( + + + + {children} + {showCloseButton && ( + + + Close + + )} + + + ) +} + +function DialogHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function DialogFooter({ + className, + showCloseButton = false, + children, + ...props +}: React.ComponentProps<"div"> & { + showCloseButton?: boolean +}) { + return ( +
+ {children} + {showCloseButton && ( + + + + )} +
+ ) +} + +function DialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogOverlay, + DialogPortal, + DialogTitle, + DialogTrigger, +} diff --git a/components/ui/dropdown-menu.tsx b/components/ui/dropdown-menu.tsx new file mode 100644 index 0000000..bbe6fb0 --- /dev/null +++ b/components/ui/dropdown-menu.tsx @@ -0,0 +1,257 @@ +"use client" + +import * as React from "react" +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" +import { CheckIcon, ChevronRightIcon, CircleIcon } from "lucide-react" + +import { cn } from "@/lib/utils" + +function DropdownMenu({ + ...props +}: React.ComponentProps) { + return +} + +function DropdownMenuPortal({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuContent({ + className, + sideOffset = 4, + ...props +}: React.ComponentProps) { + return ( + + + + ) +} + +function DropdownMenuGroup({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuItem({ + className, + inset, + variant = "default", + ...props +}: React.ComponentProps & { + inset?: boolean + variant?: "default" | "destructive" +}) { + return ( + + ) +} + +function DropdownMenuCheckboxItem({ + className, + children, + checked, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ) +} + +function DropdownMenuRadioGroup({ + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuRadioItem({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ) +} + +function DropdownMenuLabel({ + className, + inset, + ...props +}: React.ComponentProps & { + inset?: boolean +}) { + return ( + + ) +} + +function DropdownMenuSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function DropdownMenuShortcut({ + className, + ...props +}: React.ComponentProps<"span">) { + return ( + + ) +} + +function DropdownMenuSub({ + ...props +}: React.ComponentProps) { + return +} + +function DropdownMenuSubTrigger({ + className, + inset, + children, + ...props +}: React.ComponentProps & { + inset?: boolean +}) { + return ( + + {children} + + + ) +} + +function DropdownMenuSubContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + DropdownMenu, + DropdownMenuPortal, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuLabel, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioGroup, + DropdownMenuRadioItem, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuSub, + DropdownMenuSubTrigger, + DropdownMenuSubContent, +} diff --git a/components/ui/input.tsx b/components/ui/input.tsx new file mode 100644 index 0000000..8916905 --- /dev/null +++ b/components/ui/input.tsx @@ -0,0 +1,21 @@ +import * as React from "react" + +import { cn } from "@/lib/utils" + +function Input({ className, type, ...props }: React.ComponentProps<"input">) { + return ( + + ) +} + +export { Input } diff --git a/components/ui/label.tsx b/components/ui/label.tsx new file mode 100644 index 0000000..fb5fbc3 --- /dev/null +++ b/components/ui/label.tsx @@ -0,0 +1,24 @@ +"use client" + +import * as React from "react" +import * as LabelPrimitive from "@radix-ui/react-label" + +import { cn } from "@/lib/utils" + +function Label({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Label } diff --git a/components/ui/progress.tsx b/components/ui/progress.tsx new file mode 100644 index 0000000..e7a416c --- /dev/null +++ b/components/ui/progress.tsx @@ -0,0 +1,31 @@ +"use client" + +import * as React from "react" +import * as ProgressPrimitive from "@radix-ui/react-progress" + +import { cn } from "@/lib/utils" + +function Progress({ + className, + value, + ...props +}: React.ComponentProps) { + return ( + + + + ) +} + +export { Progress } diff --git a/components/ui/select.tsx b/components/ui/select.tsx new file mode 100644 index 0000000..88302a8 --- /dev/null +++ b/components/ui/select.tsx @@ -0,0 +1,190 @@ +"use client" + +import * as React from "react" +import * as SelectPrimitive from "@radix-ui/react-select" +import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Select({ + ...props +}: React.ComponentProps) { + return +} + +function SelectGroup({ + ...props +}: React.ComponentProps) { + return +} + +function SelectValue({ + ...props +}: React.ComponentProps) { + return +} + +function SelectTrigger({ + className, + size = "default", + children, + ...props +}: React.ComponentProps & { + size?: "sm" | "default" +}) { + return ( + + {children} + + + + + ) +} + +function SelectContent({ + className, + children, + position = "item-aligned", + align = "center", + ...props +}: React.ComponentProps) { + return ( + + + + + {children} + + + + + ) +} + +function SelectLabel({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function SelectItem({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ) +} + +function SelectSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function SelectScrollUpButton({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + ) +} + +function SelectScrollDownButton({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + ) +} + +export { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectScrollDownButton, + SelectScrollUpButton, + SelectSeparator, + SelectTrigger, + SelectValue, +} diff --git a/components/ui/separator.tsx b/components/ui/separator.tsx new file mode 100644 index 0000000..275381c --- /dev/null +++ b/components/ui/separator.tsx @@ -0,0 +1,28 @@ +"use client" + +import * as React from "react" +import * as SeparatorPrimitive from "@radix-ui/react-separator" + +import { cn } from "@/lib/utils" + +function Separator({ + className, + orientation = "horizontal", + decorative = true, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Separator } diff --git a/components/ui/sonner.tsx b/components/ui/sonner.tsx new file mode 100644 index 0000000..9b20afe --- /dev/null +++ b/components/ui/sonner.tsx @@ -0,0 +1,40 @@ +"use client" + +import { + CircleCheckIcon, + InfoIcon, + Loader2Icon, + OctagonXIcon, + TriangleAlertIcon, +} from "lucide-react" +import { useTheme } from "next-themes" +import { Toaster as Sonner, type ToasterProps } from "sonner" + +const Toaster = ({ ...props }: ToasterProps) => { + const { theme = "system" } = useTheme() + + return ( + , + info: , + warning: , + error: , + loading: , + }} + style={ + { + "--normal-bg": "var(--popover)", + "--normal-text": "var(--popover-foreground)", + "--normal-border": "var(--border)", + "--border-radius": "var(--radius)", + } as React.CSSProperties + } + {...props} + /> + ) +} + +export { Toaster } diff --git a/components/ui/tabs.tsx b/components/ui/tabs.tsx new file mode 100644 index 0000000..441c5d4 --- /dev/null +++ b/components/ui/tabs.tsx @@ -0,0 +1,91 @@ +"use client" + +import * as React from "react" +import * as TabsPrimitive from "@radix-ui/react-tabs" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +function Tabs({ + className, + orientation = "horizontal", + ...props +}: React.ComponentProps) { + return ( + + ) +} + +const tabsListVariants = cva( + "rounded-lg p-[3px] group-data-[orientation=horizontal]/tabs:h-9 data-[variant=line]:rounded-none group/tabs-list text-muted-foreground inline-flex w-fit items-center justify-center group-data-[orientation=vertical]/tabs:h-fit group-data-[orientation=vertical]/tabs:flex-col", + { + variants: { + variant: { + default: "bg-muted", + line: "gap-1 bg-transparent", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function TabsList({ + className, + variant = "default", + ...props +}: React.ComponentProps & + VariantProps) { + return ( + + ) +} + +function TabsTrigger({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function TabsContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Tabs, TabsList, TabsTrigger, TabsContent, tabsListVariants } diff --git a/lib/mock-data/index.ts b/lib/mock-data/index.ts new file mode 100644 index 0000000..ad4c4ab --- /dev/null +++ b/lib/mock-data/index.ts @@ -0,0 +1,9 @@ +// Export all mock data +export * from './pricing' +export * from './projects' +export * from './invoices' +export * from './testimonials' +export * from './support' + +// Re-export types +export type { ServiceType, PricingTier } from '../types' diff --git a/lib/mock-data/invoices.ts b/lib/mock-data/invoices.ts new file mode 100644 index 0000000..a144be9 --- /dev/null +++ b/lib/mock-data/invoices.ts @@ -0,0 +1,102 @@ +import { Invoice, PaymentMethod, Subscription } from '../types' + +export const mockInvoices: Invoice[] = [ + { + id: 'inv-1', + userId: 'user-1', + projectId: 'proj-1', + description: 'Landing Page Design - Growth Tier', + amount: 150, + currency: 'EUR', + vat: 30, + total: 180, + status: 'paid', + date: new Date('2024-10-01'), + dueDate: new Date('2024-10-15'), + downloadUrl: '/invoices/inv-1.pdf', + }, + { + id: 'inv-2', + userId: 'user-1', + projectId: 'proj-2', + description: 'Monthly Automation - Growth Tier', + amount: 50, + currency: 'EUR', + vat: 10, + total: 60, + status: 'paid', + date: new Date('2024-09-01'), + dueDate: new Date('2024-09-15'), + downloadUrl: '/invoices/inv-2.pdf', + }, + { + id: 'inv-3', + userId: 'user-1', + description: 'Consultation Hour', + amount: 75, + currency: 'EUR', + vat: 15, + total: 90, + status: 'refunded', + date: new Date('2024-07-15'), + dueDate: new Date('2024-07-15'), + downloadUrl: '/invoices/inv-3.pdf', + }, + { + id: 'inv-4', + userId: 'user-1', + description: 'AI Chatbot - Starter Tier', + amount: 50, + currency: 'EUR', + vat: 10, + total: 60, + status: 'pending', + date: new Date('2024-10-16'), + dueDate: new Date('2024-10-30'), + downloadUrl: '/invoices/inv-4.pdf', + }, +] + +export const mockPaymentMethods: PaymentMethod[] = [ + { + id: 'pm-1', + userId: 'user-1', + type: 'card', + isDefault: true, + last4: '4242', + brand: 'visa', + }, + { + id: 'pm-2', + userId: 'user-1', + type: 'paypal', + isDefault: false, + email: 'alex@bakery.com', + }, +] + +export const mockSubscription: Subscription = { + id: 'sub-1', + userId: 'user-1', + planId: 'growth-ai', + status: 'active', + currentPeriodStart: new Date('2024-10-01'), + currentPeriodEnd: new Date('2024-11-01'), + cancelAtPeriodEnd: false, +} + +export function getInvoicesByUserId(userId: string): Invoice[] { + return mockInvoices.filter(inv => inv.userId === userId) +} + +export function getInvoiceById(id: string): Invoice | undefined { + return mockInvoices.find(inv => inv.id === id) +} + +export function getPaymentMethodsByUserId(userId: string): PaymentMethod[] { + return mockPaymentMethods.filter(pm => pm.userId === userId) +} + +export function getDefaultPaymentMethod(userId: string): PaymentMethod | undefined { + return mockPaymentMethods.find(pm => pm.userId === userId && pm.isDefault) +} diff --git a/lib/mock-data/pricing.ts b/lib/mock-data/pricing.ts new file mode 100644 index 0000000..1da163a --- /dev/null +++ b/lib/mock-data/pricing.ts @@ -0,0 +1,114 @@ +import { PricingPlan } from '../types' + +export const mockPricingPlans: PricingPlan[] = [ + // Web Design Plans + { + id: 'starter-web', + tier: 'starter', + serviceType: 'web-design', + price: 50, + currency: 'EUR', + features: [ + '1-Page Landing Page', + 'Contact Form', + 'Mobile Responsive', + '2 Revisions', + ], + popular: false, + deliveryDays: 3, + revisions: 2, + }, + { + id: 'growth-web', + tier: 'growth', + serviceType: 'web-design', + price: 150, + currency: 'EUR', + features: [ + '5-Page Complete Site', + 'CMS Integration', + 'Basic SEO Setup', + 'Social Media Links', + '5 Revisions', + ], + popular: true, + deliveryDays: 7, + revisions: 5, + }, + { + id: 'pro-web', + tier: 'pro', + serviceType: 'web-design', + price: 300, + currency: 'EUR', + features: [ + 'E-commerce Ready', + 'Custom Animations', + 'Priority Support', + 'Advanced SEO', + 'Unlimited Revisions', + ], + popular: false, + deliveryDays: 14, + revisions: -1, + }, + // AI Automation Plans + { + id: 'starter-ai', + tier: 'starter', + serviceType: 'ai-automation', + price: 50, + currency: 'EUR', + features: [ + 'Basic Chatbot', + 'FAQ Integration', + 'Email Notifications', + 'Weekly Reports', + ], + popular: false, + deliveryDays: 2, + revisions: 1, + }, + { + id: 'growth-ai', + tier: 'growth', + serviceType: 'ai-automation', + price: 150, + currency: 'EUR', + features: [ + 'Advanced Chatbot', + 'Multi-language Support', + 'CRM Integration', + 'Analytics Dashboard', + 'Priority Support', + ], + popular: true, + deliveryDays: 5, + revisions: 3, + }, + { + id: 'pro-ai', + tier: 'pro', + serviceType: 'ai-automation', + price: 300, + currency: 'EUR', + features: [ + 'Full Automation Suite', + 'Custom AI Training', + 'Workflow Integration', + '24/7 Monitoring', + 'Dedicated Manager', + ], + popular: false, + deliveryDays: 10, + revisions: -1, + }, +] + +export function getPricingPlansByServiceType(serviceType: 'web-design' | 'ai-automation'): PricingPlan[] { + return mockPricingPlans.filter(plan => plan.serviceType === serviceType) +} + +export function getPricingPlanById(id: string): PricingPlan | undefined { + return mockPricingPlans.find(plan => plan.id === id) +} diff --git a/lib/mock-data/projects.ts b/lib/mock-data/projects.ts new file mode 100644 index 0000000..8ebd278 --- /dev/null +++ b/lib/mock-data/projects.ts @@ -0,0 +1,68 @@ +import { Project, User } from '../types' + +// Mock current user +export const mockCurrentUser: User = { + id: 'user-1', + email: 'alex@bakery.com', + name: 'Alex Johnson', + avatar: 'https://lh3.googleusercontent.com/a/default-user', + role: 'client', +} + +export const mockProjects: Project[] = [ + { + id: 'proj-1', + userId: 'user-1', + title: 'Bakery Website Redesign', + description: 'Modernizing the online presence with e-commerce integration for custom cake orders.', + type: 'web-design', + tier: 'growth', + status: 'development', + progress: 75, + thumbnail: 'https://images.unsplash.com/photo-1517433367423-c7e5b0f35086?w=400', + stages: [ + { name: 'Discovery', completed: true }, + { name: 'Design', completed: true }, + { name: 'Development', completed: false }, + { name: 'Content', completed: false }, + ], + createdAt: new Date('2024-10-01'), + updatedAt: new Date('2024-10-15'), + estimatedDelivery: new Date('2024-10-20'), + }, + { + id: 'proj-2', + userId: 'user-1', + title: 'Customer Service Bot', + description: 'Training the model on your FAQs to handle initial customer inquiries automatically.', + type: 'ai-automation', + tier: 'growth', + status: 'design', + progress: 20, + thumbnail: 'https://images.unsplash.com/photo-1677442136019-21780ecad995?w=400', + stages: [ + { name: 'Setup', completed: true }, + { name: 'Training', completed: false }, + { name: 'Testing', completed: false }, + ], + createdAt: new Date('2024-10-10'), + updatedAt: new Date('2024-10-14'), + estimatedDelivery: new Date('2024-10-25'), + }, +] + +export function getProjectsByUserId(userId: string): Project[] { + return mockProjects.filter(p => p.userId === userId) +} + +export function getProjectById(id: string): Project | undefined { + return mockProjects.find(p => p.id === id) +} + +export function getActiveProjects(): Project[] { + return mockProjects.filter(p => p.status !== 'completed') +} + +export function getCompletedProjects(): Project[] { + return mockProjects.filter(p => p.status === 'completed') +} diff --git a/lib/mock-data/support.ts b/lib/mock-data/support.ts new file mode 100644 index 0000000..c0ad966 --- /dev/null +++ b/lib/mock-data/support.ts @@ -0,0 +1,101 @@ +import { SupportTicket, DashboardStats, FAQ } from '../types' + +export const mockSupportTickets: SupportTicket[] = [ + { + id: 'ticket-1', + userId: 'user-1', + projectId: 'proj-1', + subject: 'Update product images', + description: 'We need to replace the product images with higher resolution versions.', + status: 'in-progress', + priority: 'medium', + createdAt: new Date('2024-10-12'), + updatedAt: new Date('2024-10-14'), + }, + { + id: 'ticket-2', + userId: 'user-1', + projectId: 'proj-2', + subject: 'Chatbot training data review', + description: 'Please review and update the FAQ responses for the customer service bot.', + status: 'open', + priority: 'low', + createdAt: new Date('2024-10-14'), + updatedAt: new Date('2024-10-14'), + }, + { + id: 'ticket-3', + userId: 'user-1', + subject: 'Billing inquiry', + description: 'Question about the invoice from September.', + status: 'resolved', + priority: 'low', + createdAt: new Date('2024-09-20'), + updatedAt: new Date('2024-09-22'), + }, +] + +export const mockDashboardStats: DashboardStats = { + totalProjects: 2, + activeProjects: 2, + pendingTickets: 1, + totalSpent: 330, // EUR +} + +export const mockFAQs: FAQ[] = [ + { + id: 'faq-1', + question: 'How long does it take to complete a website?', + answer: 'Delivery times depend on the tier you choose: Starter (3 days), Growth (7 days), and Pro (14 days). Complex projects may take longer.', + category: 'General', + }, + { + id: 'faq-2', + question: 'What payment methods do you accept?', + answer: 'We accept all major credit cards, PayPal, and Stripe Link for secure payments.', + category: 'Billing', + }, + { + id: 'faq-3', + question: 'Can I request revisions?', + answer: 'Yes! The Starter tier includes 2 revisions, Growth includes 5, and Pro offers unlimited revisions.', + category: 'Services', + }, + { + id: 'faq-4', + question: 'Do you offer ongoing support?', + answer: 'Yes, we provide various support packages. Pro tier customers get priority support and a dedicated account manager.', + category: 'Support', + }, + { + id: 'faq-5', + question: 'How does the AI automation work?', + answer: 'We train AI models on your specific business data and FAQs. The chatbot learns to handle customer inquiries automatically.', + category: 'AI Services', + }, + { + id: 'faq-6', + question: 'Is my data secure?', + answer: 'Absolutely. We use industry-standard encryption and security measures. Your data is never shared with third parties.', + category: 'Security', + }, +] + +export function getTicketsByUserId(userId: string): SupportTicket[] { + return mockSupportTickets.filter(ticket => ticket.userId === userId) +} + +export function getOpenTickets(userId: string): SupportTicket[] { + return mockSupportTickets.filter( + ticket => ticket.userId === userId && ticket.status !== 'closed' + ) +} + +export function getDashboardStats(userId: string): DashboardStats { + // In a real app, this would calculate stats based on actual data + return mockDashboardStats +} + +export function getAllFAQs(): FAQ[] { + return mockFAQs +} diff --git a/lib/mock-data/testimonials.ts b/lib/mock-data/testimonials.ts new file mode 100644 index 0000000..08bfc43 --- /dev/null +++ b/lib/mock-data/testimonials.ts @@ -0,0 +1,48 @@ +import { Testimonial } from '../types' + +export const mockTestimonials: Testimonial[] = [ + { + id: 'test-1', + name: 'Sarah Schmidt', + role: 'Marketing Director', + company: 'TechStart Berlin', + avatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=100', + rating: 5, + quote: 'ScaleSite transformed our online presence. The new website increased our conversion rate by 40% in just two months. Absolutely professional work!', + }, + { + id: 'test-2', + name: 'Michael Weber', + role: 'Founder', + company: 'Bakery Dreams', + avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100', + rating: 5, + quote: 'The AI chatbot handles 80% of our customer inquiries automatically. Saved us countless hours and improved customer satisfaction.', + }, + { + id: 'test-3', + name: 'Julia Hoffman', + role: 'E-commerce Manager', + company: 'Fashion Forward', + avatar: 'https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=100', + rating: 4, + quote: 'Fast delivery, great communication, and the design exceeded our expectations. The e-commerce integration works flawlessly.', + }, + { + id: 'test-4', + name: 'Thomas Klein', + role: 'CEO', + company: 'Startup Ventures', + avatar: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=100', + rating: 5, + quote: 'We needed a complete digital transformation and ScaleSite delivered. From website to AI automation, everything works perfectly together.', + }, +] + +export function getAllTestimonials(): Testimonial[] { + return mockTestimonials +} + +export function getTestimonialsByIds(ids: string[]): Testimonial[] { + return mockTestimonials.filter(t => ids.includes(t.id)) +} diff --git a/lib/types/index.ts b/lib/types/index.ts new file mode 100644 index 0000000..e4361d0 --- /dev/null +++ b/lib/types/index.ts @@ -0,0 +1,167 @@ +// User & Auth +export interface User { + id: string + email: string + name: string + avatar?: string + role: 'client' | 'admin' +} + +// Services & Pricing +export type ServiceType = 'web-design' | 'ai-automation' + +export type PricingTier = 'starter' | 'growth' | 'pro' + +export interface PricingPlan { + id: string + tier: PricingTier + serviceType: ServiceType + price: number + currency: string + features: string[] + popular?: boolean + deliveryDays: number + revisions: number +} + +export interface Service { + id: string + type: ServiceType + title: string + description: string + icon: string + image?: string +} + +// Projects & Dashboard +export type ProjectStatus = 'discovery' | 'design' | 'development' | 'content' | 'testing' | 'completed' + +export interface ProjectStage { + name: string + completed: boolean +} + +export interface Project { + id: string + userId: string + title: string + description: string + type: ServiceType + tier: PricingTier + status: ProjectStatus + progress: number + thumbnail: string + stages: ProjectStage[] + createdAt: Date + updatedAt: Date + estimatedDelivery?: Date +} + +// Support Tickets +export type TicketStatus = 'open' | 'in-progress' | 'resolved' | 'closed' + +export interface SupportTicket { + id: string + userId: string + projectId?: string + subject: string + description: string + status: TicketStatus + priority: 'low' | 'medium' | 'high' + createdAt: Date + updatedAt: Date +} + +// Billing & Invoices +export type InvoiceStatus = 'paid' | 'pending' | 'refunded' | 'cancelled' + +export interface Invoice { + id: string + userId: string + projectId?: string + description: string + amount: number + currency: string + vat: number + total: number + status: InvoiceStatus + date: Date + dueDate: Date + downloadUrl?: string +} + +export type PaymentMethodType = 'card' | 'paypal' | 'stripe-link' + +export interface PaymentMethod { + id: string + userId: string + type: PaymentMethodType + isDefault: boolean + last4?: string + brand?: string + email?: string +} + +export interface Subscription { + id: string + userId: string + planId: string + status: 'active' | 'cancelled' | 'past_due' + currentPeriodStart: Date + currentPeriodEnd: Date + cancelAtPeriodEnd: boolean +} + +// Testimonials & Marketing +export interface Testimonial { + id: string + name: string + role: string + company: string + avatar: string + rating: number + quote: string +} + +// Checkout +export type CheckoutStep = 'account' | 'billing' | 'payment' + +export interface CheckoutSession { + id: string + userId?: string + serviceType: ServiceType + tier: PricingTier + email: string + billingAddress?: Address + paymentMethodId?: string + amount: number + vat: number + total: number + status: 'pending' | 'processing' | 'completed' | 'failed' + currentStep: CheckoutStep + createdAt: Date +} + +export interface Address { + fullName: string + street: string + city: string + postalCode: string + country: string +} + +// FAQ +export interface FAQ { + id: string + question: string + answer: string + category?: string +} + +// Stats for dashboard +export interface DashboardStats { + totalProjects: number + activeProjects: number + pendingTickets: number + totalSpent: number +} diff --git a/lib/utils.ts b/lib/utils.ts new file mode 100644 index 0000000..bd0c391 --- /dev/null +++ b/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/logs/server.log b/logs/server.log new file mode 100644 index 0000000..23b1186 --- /dev/null +++ b/logs/server.log @@ -0,0 +1,34 @@ + +> scalesite-nextjs@0.1.0 dev +> next dev + +⚠ Port 3000 is in use by an unknown process, using available port 3001 instead. +▲ Next.js 16.1.6 (Turbopack) +- Local: http://localhost:3001 +- Network: http://192.168.178.115:3001 + +✓ Starting... +✓ Ready in 718ms + GET / 200 in 846ms (compile: 272ms, render: 574ms) + GET / 200 in 193ms (compile: 7ms, render: 186ms) + GET /login 200 in 739ms (compile: 703ms, render: 36ms) +✓ Compiled in 133ms + GET / 200 in 167ms (compile: 54ms, render: 113ms) + GET / 200 in 154ms (compile: 46ms, render: 108ms) + GET / 200 in 175ms (compile: 54ms, render: 122ms) + GET / 200 in 88ms (compile: 4ms, render: 85ms) + GET / 200 in 216ms (compile: 46ms, render: 170ms) +✓ Compiled in 45ms + GET / 200 in 77ms (compile: 4ms, render: 72ms) +✓ Compiled in 61ms + GET / 200 in 76ms (compile: 5ms, render: 71ms) +✓ Compiled in 85ms + GET / 200 in 77ms (compile: 3ms, render: 74ms) + GET / 200 in 182ms (compile: 46ms, render: 136ms) + GET /dashboard 200 in 1174ms (compile: 807ms, render: 366ms) + GET /dashboard/projects 404 in 104ms (compile: 79ms, render: 25ms) + GET /dashboard/projects 404 in 64ms (compile: 3ms, render: 61ms) + GET /dashboard 200 in 129ms (compile: 4ms, render: 125ms) +✓ Compiled in 73ms + GET /dashboard 200 in 67ms (compile: 3ms, render: 64ms) +[?25h diff --git a/package-lock.json b/package-lock.json index 1672ab4..b885c68 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,31 @@ "name": "scalesite-nextjs", "version": "0.1.0", "dependencies": { + "@hookform/resolvers": "^5.2.2", + "@radix-ui/react-avatar": "^1.1.11", + "@radix-ui/react-checkbox": "^1.3.3", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-label": "^2.1.8", + "@radix-ui/react-progress": "^1.1.8", + "@radix-ui/react-select": "^2.2.6", + "@radix-ui/react-separator": "^1.1.8", + "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-tabs": "^1.1.13", + "@radix-ui/react-toast": "^1.2.15", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "framer-motion": "^12.29.2", + "lucide-react": "^0.563.0", "next": "16.1.6", + "next-themes": "^0.4.6", "react": "19.2.3", - "react-dom": "19.2.3" + "react-dom": "19.2.3", + "react-hook-form": "^7.71.1", + "sonner": "^2.0.7", + "tailwind-merge": "^3.4.0", + "tailwindcss-animate": "^1.0.7", + "zod": "^4.3.6" }, "devDependencies": { "@tailwindcss/postcss": "^4", @@ -20,6 +42,7 @@ "eslint": "^9", "eslint-config-next": "16.1.6", "tailwindcss": "^4", + "tw-animate-css": "^1.4.0", "typescript": "^5" } }, @@ -453,6 +476,56 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.4.tgz", + "integrity": "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.5.tgz", + "integrity": "sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.4", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.7.tgz", + "integrity": "sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.5" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@hookform/resolvers": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.2.2.tgz", + "integrity": "sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==", + "license": "MIT", + "dependencies": { + "@standard-schema/utils": "^0.3.0" + }, + "peerDependencies": { + "react-hook-form": "^7.55.0" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -1226,6 +1299,1636 @@ "node": ">=12.4.0" } }, + "node_modules/@radix-ui/number": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", + "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", + "license": "MIT" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.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-arrow/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.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-arrow/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "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-avatar": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.11.tgz", + "integrity": "sha512-0Qk603AHGV28BOBO34p7IgD5m+V5Sg/YovfayABkoDDBM5d3NCx0Mp4gGrjzLGes1jV5eNOE1r3itqOR33VC6Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.3", + "@radix-ui/react-primitive": "2.1.4", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-is-hydrated": "0.1.0", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "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.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz", + "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "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/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "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-checkbox/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.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-checkbox/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "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-collection": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.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-collection/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "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-collection/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.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-collection/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "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.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.3.tgz", + "integrity": "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw==", + "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-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "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-dialog/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "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-dialog/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.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-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "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.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "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-dismissable-layer/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.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-dismissable-layer/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "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-dropdown-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", + "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.16", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.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-dropdown-menu/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "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-dropdown-menu/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.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-dropdown-menu/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "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-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "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.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "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-scope/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.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-focus-scope/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "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-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "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-label": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.8.tgz", + "integrity": "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.4" + }, + "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.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", + "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "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/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "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-menu/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.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-menu/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", + "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" + }, + "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/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "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-popper/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.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/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "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-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "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/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.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-portal/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "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-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "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.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", + "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.4" + }, + "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-progress": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.8.tgz", + "integrity": "sha512-+gISHcSPUJ7ktBy9RnTqbdKW78bcGke3t6taawyZ71pio1JewwGSJizycs7rLhGTvMJYCQB1DBK4KQsxs7U8dA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.3", + "@radix-ui/react-primitive": "2.1.4" + }, + "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.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.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/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "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-roving-focus/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.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-roving-focus/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "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-select": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", + "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3", + "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-select/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "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-select/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.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-select/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-separator": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.8.tgz", + "integrity": "sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.4" + }, + "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.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", + "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", + "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.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-tabs/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "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-tabs/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.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-tabs/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "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-toast": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.15.tgz", + "integrity": "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.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-toast/node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "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-toast/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.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-toast/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "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.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "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-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "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-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "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-is-hydrated": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz", + "integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.5.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.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "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.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", + "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.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "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-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "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-visually-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.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-visually-hidden/node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.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-visually-hidden/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "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.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", + "license": "MIT" + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -1233,6 +2936,12 @@ "dev": true, "license": "MIT" }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, "node_modules/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -1559,7 +3268,7 @@ "version": "19.2.10", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz", "integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.2.2" @@ -1569,7 +3278,7 @@ "version": "19.2.3", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", - "dev": true, + "devOptional": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.2.0" @@ -2176,6 +3885,18 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/aria-query": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", @@ -2570,12 +4291,33 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "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-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2629,7 +4371,7 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -2764,6 +4506,12 @@ "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/doctrine": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", @@ -3585,6 +5333,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/framer-motion": { + "version": "12.29.2", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.29.2.tgz", + "integrity": "sha512-lSNRzBJk4wuIy0emYQ/nfZ7eWhqud2umPKw2QAQki6uKhZPKm2hRQHeQoHTG9MIvfobb+A/LbEWPJU794ZUKrg==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.29.2", + "motion-utils": "^12.29.2", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -3671,6 +5446,15 @@ "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/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", @@ -4833,6 +6617,15 @@ "yallist": "^3.0.2" } }, + "node_modules/lucide-react": { + "version": "0.563.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.563.0.tgz", + "integrity": "sha512-8dXPB2GI4dI8jV4MgUDGBeLdGk8ekfqVZ0BdLcrRzocGgG75ltNEmWS+gE7uokKF/0oSUuczNDT+g9hFJ23FkA==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", @@ -4900,6 +6693,21 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/motion-dom": { + "version": "12.29.2", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.29.2.tgz", + "integrity": "sha512-/k+NuycVV8pykxyiTCoFzIVLA95Nb1BFIVvfSu9L50/6K6qNeAYtkxXILy/LRutt7AzaYDc2myj0wkCVVYAPPA==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.29.2" + } + }, + "node_modules/motion-utils": { + "version": "12.29.2", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.29.2.tgz", + "integrity": "sha512-G3kc34H2cX2gI63RqU+cZq+zWRRPSsNIOjpdl9TN4AQwC4sgwYPl/Q/Obf/d53nOm569T0fYK+tcoSV50BWx8A==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -5001,6 +6809,16 @@ } } }, + "node_modules/next-themes": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz", + "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==", + "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", @@ -5399,6 +7217,22 @@ "react": "^19.2.3" } }, + "node_modules/react-hook-form": { + "version": "7.71.1", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.71.1.tgz", + "integrity": "sha512-9SUJKCGKo8HUSsCO+y0CtqkqI5nNuaDqTxyqPsZPqIwudpj4rCrAz/jZV+jn57bx5gtZKOh3neQu94DXMc+w5w==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -5406,6 +7240,75 @@ "dev": true, "license": "MIT" }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "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/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -5803,6 +7706,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/sonner": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.7.tgz", + "integrity": "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==", + "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", @@ -6018,13 +7931,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/tailwind-merge": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz", + "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "4.1.18", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", - "dev": true, "license": "MIT" }, + "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==", + "license": "MIT", + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, "node_modules/tapable": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", @@ -6145,6 +8076,16 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tw-animate-css": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.4.0.tgz", + "integrity": "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Wombosvideo" + } + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -6376,6 +8317,58 @@ "punycode": "^2.1.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/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6515,7 +8508,6 @@ "version": "4.3.6", "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index 09bf407..7b4b6cf 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,31 @@ "lint": "eslint" }, "dependencies": { + "@hookform/resolvers": "^5.2.2", + "@radix-ui/react-avatar": "^1.1.11", + "@radix-ui/react-checkbox": "^1.3.3", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-label": "^2.1.8", + "@radix-ui/react-progress": "^1.1.8", + "@radix-ui/react-select": "^2.2.6", + "@radix-ui/react-separator": "^1.1.8", + "@radix-ui/react-slot": "^1.2.4", + "@radix-ui/react-tabs": "^1.1.13", + "@radix-ui/react-toast": "^1.2.15", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "framer-motion": "^12.29.2", + "lucide-react": "^0.563.0", "next": "16.1.6", + "next-themes": "^0.4.6", "react": "19.2.3", - "react-dom": "19.2.3" + "react-dom": "19.2.3", + "react-hook-form": "^7.71.1", + "sonner": "^2.0.7", + "tailwind-merge": "^3.4.0", + "tailwindcss-animate": "^1.0.7", + "zod": "^4.3.6" }, "devDependencies": { "@tailwindcss/postcss": "^4", @@ -21,6 +43,7 @@ "eslint": "^9", "eslint-config-next": "16.1.6", "tailwindcss": "^4", + "tw-animate-css": "^1.4.0", "typescript": "^5" } }