diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx new file mode 100644 index 0000000..5ac83a1 --- /dev/null +++ b/app/[locale]/layout.tsx @@ -0,0 +1,48 @@ +import type { Metadata } from "next"; +import { Geist, Geist_Mono, Nunito_Sans } from "next/font/google"; +import { NextIntlClientProvider } from "next-intl"; +import { getMessages } from "next-intl/server"; +import { ThemeProvider } from "@/components/theme-provider"; +import "../globals.css"; + +const nunitoSans = Nunito_Sans({ + variable: "--font-nunito-sans", + subsets: ["latin"], + weight: ["200", "300", "400", "500", "600", "700", "800", "900"], +}); + +const geistMono = Geist_Mono({ + variable: "--font-geist-mono", + subsets: ["latin"], +}); + +export const metadata: Metadata = { + title: "Pagodera Trade", + description: "Markeplace servidor pagodera", +}; + +export default async function LocaleLayout({ + children, + params, +}: { + children: React.ReactNode; + params: Promise<{ locale: string }>; +}) { + const { locale } = await params; + const messages = await getMessages(); + + return ( + + + + + {children} + + + + + ); +} + diff --git a/app/[locale]/page.tsx b/app/[locale]/page.tsx new file mode 100644 index 0000000..7826884 --- /dev/null +++ b/app/[locale]/page.tsx @@ -0,0 +1,25 @@ +import Header from "@/components/Header"; +import { EscrowBanner } from "@/components/sections/escrow-banner"; +import { EscrowExplanation } from "@/components/sections/escrow-explanation"; +import { HeroSection } from "@/components/sections/hero-section"; +import { HowItWorks } from "@/components/sections/how-it-works"; +import { FeaturedItems } from "@/components/sections/featured-items"; +import { MarketOffers } from "@/components/sections/market-offers"; +import { MarketStats } from "@/components/sections/market-stats"; + +export default function Home() { + return ( +
+
+ +
+ + + + + +
+ +
+ ); +} diff --git a/app/error.tsx b/app/error.tsx new file mode 100644 index 0000000..70db949 --- /dev/null +++ b/app/error.tsx @@ -0,0 +1,28 @@ +"use client"; + +import { useEffect } from "react"; +import { Button } from "@/components/ui/button"; + +export default function Error({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { + useEffect(() => { + console.error(error); + }, [error]); + + return ( +
+
+

Algo deu errado!

+

+ Ocorreu um erro inesperado. Tente novamente. +

+ +
+
+ ); +} diff --git a/app/globals.css b/app/globals.css index a2dc41e..42ec947 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,26 +1,318 @@ @import "tailwindcss"; +@import "tw-animate-css"; -: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-mono: var(--font-geist-mono); -} - -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; + --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); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --animate-shimmer-slide: shimmer-slide var(--speed) ease-in-out infinite + alternate; + --animate-spin-around: spin-around calc(var(--speed) * 2) infinite linear; + @keyframes shimmer-slide { + to { + transform: translate(calc(100cqw - 100%), 0); + } + } + @keyframes spin-around { + 0% { + transform: translateZ(0) rotate(0); + } + 15%, + 35% { + transform: translateZ(0) rotate(90deg); + } + 65%, + 85% { + transform: translateZ(0) rotate(270deg); + } + 100% { + transform: translateZ(0) rotate(360deg); + } + } + --animate-gradient: gradient 8s linear infinite; + @keyframes gradient { + to { + background-position: var(--bg-size, 300%) 0; + } + } + --animate-shine: shine var(--duration) infinite linear; + @keyframes shine { + 0% { + background-position: 0% 0%; + } + 50% { + background-position: 100% 100%; + } + to { + background-position: 0% 0%; + } + } + --animate-background-position-spin: background-position-spin 3000ms infinite + alternate; + @keyframes background-position-spin { + 0% { + background-position: top center; + } + 100% { + background-position: bottom center; + } } } -body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; +:root { + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } + html, + body { + height: 100%; + } + #__next { + height: 100%; + } + .container { + width: 100%; + margin-left: auto; + margin-right: auto; + padding-left: 1rem; + padding-right: 1rem; + } + @media (min-width: 640px) { + .container { + max-width: 640px; + padding-left: 1.5rem; + padding-right: 1.5rem; + } + } + @media (min-width: 768px) { + .container { + max-width: 768px; + } + } + @media (min-width: 1024px) { + .container { + max-width: 1024px; + padding-left: 2rem; + padding-right: 2rem; + } + } + @media (min-width: 1280px) { + .container { + max-width: 1280px; + } + } + @media (min-width: 1536px) { + .container { + max-width: 1280px; /* Manter 1280px como máximo */ + } + } +} + +/* Tibia-themed colors */ +:root { + --tibia-green: oklch(0.6 0.15 145); + --tibia-gold: oklch(0.75 0.15 85); + --tibia-mystic: oklch(0.55 0.2 250); + --tibia-legendary: #ff8c00; + --tibia-epic: #9b59b6; + --tibia-rare: #3498db; + --tibia-uncommon: #2ecc71; + --tibia-common: #95a5a6; + --tibia-gold: #ffd700; +} + +.dark { + --tibia-green: oklch(0.7 0.2 145); + --tibia-gold: oklch(0.8 0.2 85); + --tibia-mystic: oklch(0.6 0.25 250); + --tibia-legendary: #ff8c00; + --tibia-epic: #9b59b6; + --tibia-rare: #3498db; + --tibia-uncommon: #2ecc71; + --tibia-common: #95a5a6; + --tibia-gold: #ffd700; +} + +/* Tibia glow effects */ +@keyframes glow-legendary { + 0%, + 100% { + box-shadow: 0 0 15px rgba(255, 140, 0, 0.5); + } + 50% { + box-shadow: 0 0 25px rgba(255, 140, 0, 0.8); + } +} + +@keyframes glow-epic { + 0%, + 100% { + box-shadow: 0 0 15px rgba(155, 89, 182, 0.5); + } + 50% { + box-shadow: 0 0 25px rgba(155, 89, 182, 0.8); + } +} + +@keyframes glow-rare { + 0%, + 100% { + box-shadow: 0 0 15px rgba(52, 152, 219, 0.5); + } + 50% { + box-shadow: 0 0 25px rgba(52, 152, 219, 0.8); + } +} + +@keyframes glow-uncommon { + 0%, + 100% { + box-shadow: 0 0 15px rgba(46, 204, 113, 0.5); + } + 50% { + box-shadow: 0 0 25px rgba(46, 204, 113, 0.8); + } +} + +@keyframes accordion-down { + from { + height: 0; + } + to { + height: var(--radix-accordion-content-height); + } +} + +@keyframes accordion-up { + from { + height: var(--radix-accordion-content-height); + } + to { + height: 0; + } +} + +/* Utility classes for Tibia effects */ +.glow-legendary { + animation: glow-legendary 2s ease-in-out infinite; +} + +.glow-epic { + animation: glow-epic 2s ease-in-out infinite; +} + +.glow-rare { + animation: glow-rare 2s ease-in-out infinite; +} + +.glow-uncommon { + animation: glow-uncommon 2s ease-in-out infinite; } diff --git a/app/layout.tsx b/app/layout.tsx deleted file mode 100644 index f7fa87e..0000000 --- a/app/layout.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; -import "./globals.css"; - -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); - -export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", -}; - -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - - {children} - - - ); -} diff --git a/app/not-found.tsx b/app/not-found.tsx new file mode 100644 index 0000000..f260038 --- /dev/null +++ b/app/not-found.tsx @@ -0,0 +1,18 @@ +import Link from "next/link"; +import { Button } from "@/components/ui/button"; + +export default function NotFound() { + return ( +
+
+

Página não encontrada

+

+ A página que você está procurando não existe. +

+ + + +
+
+ ); +} diff --git a/app/page.tsx b/app/page.tsx deleted file mode 100644 index 21b686d..0000000 --- a/app/page.tsx +++ /dev/null @@ -1,103 +0,0 @@ -import Image from "next/image"; - -export default function Home() { - return ( -
-
- Next.js logo -
    -
  1. - Get started by editing{" "} - - app/page.tsx - - . -
  2. -
  3. - Save and see your changes instantly. -
  4. -
- -
- - Vercel logomark - Deploy now - - - Read our docs - -
-
- -
- ); -} diff --git a/components.json b/components.json new file mode 100644 index 0000000..65109c6 --- /dev/null +++ b/components.json @@ -0,0 +1,24 @@ +{ + "$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", + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "registries": { + "@magicui": "https://magicui.design/r/{name}.json" + } +} diff --git a/components/Header.tsx b/components/Header.tsx new file mode 100644 index 0000000..9e9648d --- /dev/null +++ b/components/Header.tsx @@ -0,0 +1,74 @@ +"use client"; + +import { + NavigationMenu, + NavigationMenuItem, + NavigationMenuLink, + NavigationMenuList, + navigationMenuTriggerStyle, +} from "@/components/ui/navigation-menu"; +import { WordRotate } from "@/components/ui/word-rotate"; +import { ThemeToggle } from "./theme-toggle"; +import { LanguageSwitcher } from "./language-switcher"; +import { MobileMenu } from "./mobile-menu"; +import { cn } from "@/lib/utils"; +import { useTranslations } from "next-intl"; +import Link from "next/link"; + +export default function Header() { + const t = useTranslations("header.nav"); + + const links = [ + { href: "/market", label: t("market") }, + { href: "/sobre", label: t("about") }, + { href: "/precos", label: t("prices") }, + ]; + + return ( +
+
+ {/* Logo */} +
+ + + +
+ + {/* Desktop Navigation */} + + + {links.map(({ href, label }) => ( + + + + {label} + + + + ))} + + + + {/* Desktop Controls */} +
+ + +
+ + {/* Mobile Menu */} +
+ +
+
+
+ ); +} diff --git a/components/Hero.tsx b/components/Hero.tsx new file mode 100644 index 0000000..e69de29 diff --git a/components/item-card.tsx b/components/item-card.tsx new file mode 100644 index 0000000..3a8a125 --- /dev/null +++ b/components/item-card.tsx @@ -0,0 +1,354 @@ +"use client"; + +import { TibiaItem } from "@/types/item"; +import { MagicCard } from "@/components/ui/magic-card"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { PriceDisplay } from "@/components/ui/price-display"; +import { useTranslations } from "next-intl"; +import { + TrendingUp, + TrendingDown, + Sword, + Shield, + Weight, + Heart, + Star, + Clock, + Coins, + ArrowUp, + ArrowDown, + Minus, + Zap, + Flame, + Snowflake, + Zap as Energy, + Skull, + Cross, +} from "lucide-react"; +import { useState } from "react"; +import { + getRarityColor, + getRarityBgColor, + getRarityGlow, +} from "@/lib/currency-utils"; + +interface ItemCardProps { + item: TibiaItem; +} + +export function ItemCard({ item }: ItemCardProps) { + const t = useTranslations("home.item"); + const [isFavorited, setIsFavorited] = useState(false); + const [isHovered, setIsHovered] = useState(false); + + const rarityColors = { + common: "bg-gray-500", + uncommon: "bg-green-500", + rare: "bg-blue-500", + epic: "bg-purple-500", + legendary: "bg-orange-500", + }; + + const categoryIcons = { + weapon: Sword, + armor: Shield, + helmet: Shield, + legs: Shield, + boots: Shield, + shield: Shield, + amulet: "💎", + ring: "💍", + potion: "🧪", + rune: "📜", + tool: "🔧", + ammunition: "🏹", + other: "📦", + }; + + const CategoryIcon = categoryIcons[item.category]; + + // Calcular diferença percentual entre compra e venda + const priceDifference = + ((item.price.buy - item.price.sell) / item.price.sell) * 100; + const isHotDeal = priceDifference > 20; // Hot deal se diferença > 20% + + // Determinar tendência de preço + const getPriceTrend = () => { + if (item.priceHistory.highest > item.priceHistory.average * 1.1) + return "up"; + if (item.priceHistory.lowest < item.priceHistory.average * 0.9) + return "down"; + return "stable"; + }; + + const trend = getPriceTrend(); + const TrendIcon = + trend === "up" ? ArrowUp : trend === "down" ? ArrowDown : Minus; + const trendColor = + trend === "up" + ? "text-green-500" + : trend === "down" + ? "text-red-500" + : "text-gray-500"; + + return ( +
setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + > + + {/* Header com ícone e badge de raridade */} +
+ {/* Badge de raridade com glow */} + + {item.rarity} + + + {/* Botão de favorito */} + + + {/* Hot Deal Badge */} + {isHotDeal && ( + + 🔥 Hot Deal + + )} + + {/* Ícone do item */} +
+
+ {typeof CategoryIcon === "string" ? ( + CategoryIcon + ) : ( + + )} +
+
+
+ + {/* Conteúdo do card */} +
+ {/* Nome do item */} +
+

+ {item.name} +

+ + {/* Timestamp */} +
+ + Atualizado há 5 min +
+
+ + {/* Estatísticas agrupadas */} + {item.stats && ( +
+
+ {item.stats.attack && ( +
+ + + ⚔️ {item.stats.attack} ATK + +
+ )} + {item.stats.defense && ( +
+ + + 🛡️ {item.stats.defense} DEF + +
+ )} + {item.stats.armor && ( +
+ + + 🛡️ {item.stats.armor} ARM + +
+ )} +
+ + {/* Proteções elementais */} + {item.protections && ( +
+ {item.protections.fire && ( +
+ + 🔥 Fire +{item.protections.fire}% +
+ )} + {item.protections.ice && ( +
+ + ❄️ Ice +{item.protections.ice}% +
+ )} + {item.protections.energy && ( +
+ + ⚡ Energy +{item.protections.energy}% +
+ )} +
+ )} + + {/* Requisitos */} + {item.requirements && ( +
+ {item.requirements.level && ( +
+ 📊 Level {item.requirements.level}+ +
+ )} + {item.requirements.vocation && ( +
+ 👤 Best for {item.requirements.vocation} +
+ )} +
+ )} + + {item.stats.weight && ( +
+ + ⚖️ {item.stats.weight} oz +
+ )} +
+ )} + + {/* Divisor visual */} +
+ + {/* Preços com ícones e formatação melhorada */} +
+
+
+
+ + {t("buyPrice")} +
+ +
+
+
+ + {t("sellPrice")} +
+ +
+
+ + {/* Diferença percentual */} +
+ 0 ? "text-green-600" : "text-red-600" + }`} + > + {priceDifference > 0 ? "+" : ""} + {priceDifference.toFixed(1)}% diferença + +
+
+ + {/* Divisor visual */} +
+ + {/* Histórico de preço com sparkline */} +
+
+ + {t("priceHistory")} + +
+ + {trend} +
+
+ +
+
+
+ + {t("highest")} +
+
+ 🪙 {item.priceHistory.highest.toLocaleString()} gp +
+
+
+
+ + {t("lowest")} +
+
+ 🪙 {item.priceHistory.lowest.toLocaleString()} gp +
+
+
+
+ + {t("average")} +
+
+ 🪙 {item.priceHistory.average.toLocaleString()} gp +
+
+
+
+ + {/* Botões de ação */} +
+ + +
+
+ +
+ ); +} diff --git a/components/language-switcher.tsx b/components/language-switcher.tsx new file mode 100644 index 0000000..183fad3 --- /dev/null +++ b/components/language-switcher.tsx @@ -0,0 +1,58 @@ +"use client"; + +import { Globe } from "lucide-react"; +import { useLocale } from "next-intl"; +import { usePathname, useRouter } from "next/navigation"; +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { useTranslations } from "next-intl"; + +const languages = [ + { code: "pt", name: "Português", flag: "🇧🇷" }, + { code: "en", name: "English", flag: "🇺🇸" }, +]; + +export function LanguageSwitcher() { + const locale = useLocale(); + const router = useRouter(); + const pathname = usePathname(); + const t = useTranslations("header"); + + const handleLanguageChange = (newLocale: string) => { + // Remove the current locale from the pathname + const pathWithoutLocale = pathname.replace(`/${locale}`, "") || "/"; + // Navigate to the new locale + router.push(`/${newLocale}${pathWithoutLocale}`); + }; + + const currentLanguage = languages.find((lang) => lang.code === locale); + + return ( + + + + + + {languages.map((language) => ( + handleLanguageChange(language.code)} + className={locale === language.code ? "bg-accent" : ""} + > + {language.flag} + {language.name} + + ))} + + + ); +} + diff --git a/components/marketplace-filters.tsx b/components/marketplace-filters.tsx new file mode 100644 index 0000000..e81f3e6 --- /dev/null +++ b/components/marketplace-filters.tsx @@ -0,0 +1,292 @@ +"use client"; + +import { useState } from "react"; +import { useTranslations } from "next-intl"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Badge } from "@/components/ui/badge"; +import { + Search, + Filter, + SortAsc, + SortDesc, + X, + Star, + TrendingUp, + Clock, +} from "lucide-react"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; + +interface MarketplaceFiltersProps { + onSearch: (query: string) => void; + onFilter: (filters: FilterState) => void; + onSort: (sortBy: string) => void; + totalItems: number; +} + +interface FilterState { + rarity: string[]; + category: string[]; + priceRange: { min: number; max: number }; +} + +export function MarketplaceFilters({ + onSearch, + onFilter, + onSort, + totalItems, +}: MarketplaceFiltersProps) { + const t = useTranslations("home"); + const [searchQuery, setSearchQuery] = useState(""); + const [activeFilters, setActiveFilters] = useState({ + rarity: [], + category: [], + priceRange: { min: 0, max: 100000 }, + }); + const [sortBy, setSortBy] = useState("name"); + + const rarityOptions = [ + { value: "common", label: "Comum", color: "bg-gray-500" }, + { value: "uncommon", label: "Incomum", color: "bg-green-500" }, + { value: "rare", label: "Raro", color: "bg-blue-500" }, + { value: "epic", label: "Épico", color: "bg-purple-500" }, + { value: "legendary", label: "Lendário", color: "bg-orange-500" }, + ]; + + const categoryOptions = [ + { value: "weapon", label: "Arma" }, + { value: "armor", label: "Armadura" }, + { value: "amulet", label: "Amuleto" }, + { value: "ring", label: "Anel" }, + { value: "potion", label: "Poção" }, + { value: "rune", label: "Runa" }, + { value: "other", label: "Outros" }, + ]; + + const sortOptions = [ + { value: "name", label: "Nome", icon: SortAsc }, + { value: "price-asc", label: "Preço (Menor)", icon: SortAsc }, + { value: "price-desc", label: "Preço (Maior)", icon: SortDesc }, + { value: "rarity", label: "Raridade", icon: Star }, + { value: "trending", label: "Tendência", icon: TrendingUp }, + { value: "recent", label: "Recente", icon: Clock }, + ]; + + const handleSearch = (query: string) => { + setSearchQuery(query); + onSearch(query); + }; + + const toggleRarityFilter = (rarity: string) => { + const newRarity = activeFilters.rarity.includes(rarity) + ? activeFilters.rarity.filter((r) => r !== rarity) + : [...activeFilters.rarity, rarity]; + + const newFilters = { ...activeFilters, rarity: newRarity }; + setActiveFilters(newFilters); + onFilter(newFilters); + }; + + const toggleCategoryFilter = (category: string) => { + const newCategory = activeFilters.category.includes(category) + ? activeFilters.category.filter((c) => c !== category) + : [...activeFilters.category, category]; + + const newFilters = { ...activeFilters, category: newCategory }; + setActiveFilters(newFilters); + onFilter(newFilters); + }; + + const handleSort = (sortValue: string) => { + setSortBy(sortValue); + onSort(sortValue); + }; + + const clearFilters = () => { + setActiveFilters({ + rarity: [], + category: [], + priceRange: { min: 0, max: 100000 }, + }); + setSearchQuery(""); + onFilter({ + rarity: [], + category: [], + priceRange: { min: 0, max: 100000 }, + }); + onSearch(""); + }; + + const activeFiltersCount = + activeFilters.rarity.length + activeFilters.category.length; + + return ( +
+ {/* Barra de busca */} +
+ + handleSearch(e.target.value)} + className="pl-10 pr-4" + /> +
+ + {/* Filtros e ordenação */} +
+ {/* Filtro por raridade */} + + + + + + Filtrar por Raridade + + {rarityOptions.map((option) => ( + toggleRarityFilter(option.value)} + className="flex items-center gap-2" + > +
+ {option.label} + {activeFilters.rarity.includes(option.value) && ( + + )} + + ))} + + + + {/* Filtro por categoria */} + + + + + + Filtrar por Categoria + + {categoryOptions.map((option) => ( + toggleCategoryFilter(option.value)} + className="flex items-center gap-2" + > + {option.label} + {activeFilters.category.includes(option.value) && ( + + )} + + ))} + + + + {/* Ordenação */} + + + + + + Ordenar por + + {sortOptions.map((option) => { + const Icon = option.icon; + return ( + handleSort(option.value)} + className="flex items-center gap-2" + > + + {option.label} + {sortBy === option.value && ( +
+ )} + + ); + })} + + + + {/* Limpar filtros */} + {activeFiltersCount > 0 && ( + + )} +
+ + {/* Badges de filtros ativos */} + {activeFiltersCount > 0 && ( +
+ {activeFilters.rarity.map((rarity) => { + const option = rarityOptions.find((opt) => opt.value === rarity); + return ( + toggleRarityFilter(rarity)} + > +
+ {option?.label} + + + ); + })} + {activeFilters.category.map((category) => { + const option = categoryOptions.find( + (opt) => opt.value === category + ); + return ( + toggleCategoryFilter(category)} + > + {option?.label} + + + ); + })} +
+ )} + + {/* Contador de resultados */} +
+ {totalItems}{" "} + {totalItems === 1 ? "item encontrado" : "itens encontrados"} +
+
+ ); +} + diff --git a/components/mobile-menu.tsx b/components/mobile-menu.tsx new file mode 100644 index 0000000..e84b9eb --- /dev/null +++ b/components/mobile-menu.tsx @@ -0,0 +1,64 @@ +"use client"; + +import { Menu } from "lucide-react"; +import { useState } from "react"; +import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"; +import { Button } from "@/components/ui/button"; +import { Separator } from "@/components/ui/separator"; +import { ThemeToggle } from "./theme-toggle"; +import { LanguageSwitcher } from "./language-switcher"; +import { useTranslations } from "next-intl"; +import Link from "next/link"; + +interface MobileMenuProps { + links: Array<{ href: string; label: string }>; +} + +export function MobileMenu({ links }: MobileMenuProps) { + const [open, setOpen] = useState(false); + const t = useTranslations("header"); + + return ( + + + + + +
+ {/* Navigation Links */} +
+ {links.map(({ href, label }) => ( + setOpen(false)} + > + {label} + + ))} +
+ + + + {/* Theme and Language Controls */} +
+
+ {t("theme.light")} + +
+ +
+ {t("language")} + +
+
+
+
+
+ ); +} + diff --git a/components/offer-card.tsx b/components/offer-card.tsx new file mode 100644 index 0000000..d50daee --- /dev/null +++ b/components/offer-card.tsx @@ -0,0 +1,246 @@ +"use client"; + +import { MarketOffer } from "@/types/item"; +import { Avatar, AvatarFallback } from "@/components/ui/avatar"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { PriceDisplay } from "@/components/ui/price-display"; +import { useTranslations } from "next-intl"; +import { + Clock, + User, + Coins, + TrendingUp, + TrendingDown, + Star, + Heart, + Shield, + Sword, +} from "lucide-react"; +import { useState } from "react"; +import { getRarityColor, getRarityBgColor } from "@/lib/currency-utils"; + +interface OfferCardProps { + offer: MarketOffer; +} + +export function OfferCard({ offer }: OfferCardProps) { + const t = useTranslations("home.stats"); + const tButtons = useTranslations("home.buttons"); + const [isFavorited, setIsFavorited] = useState(false); + const [isHovered, setIsHovered] = useState(false); + + const formatTimeAgo = (timestamp: Date) => { + const now = new Date(); + const diff = now.getTime() - timestamp.getTime(); + const minutes = Math.floor(diff / (1000 * 60)); + + if (minutes < 1) return "Agora"; + if (minutes < 60) return `há ${minutes} ${t("minutes")}`; + const hours = Math.floor(minutes / 60); + if (hours < 24) return `há ${hours} ${t("hours")}`; + const days = Math.floor(hours / 24); + return `há ${days} dias`; + }; + + const getInitials = (name: string) => { + return name + .split(" ") + .map((word) => word[0]) + .join("") + .toUpperCase() + .slice(0, 2); + }; + + const getCategoryIcon = (category: string) => { + switch (category) { + case "weapon": + return ; + case "armor": + return ; + default: + return "📦"; + } + }; + + const getRarityColor = (rarity: string) => { + switch (rarity) { + case "common": + return "bg-gray-500"; + case "uncommon": + return "bg-green-500"; + case "rare": + return "bg-blue-500"; + case "epic": + return "bg-purple-500"; + case "legendary": + return "bg-orange-500"; + default: + return "bg-gray-500"; + } + }; + + const isHotDeal = offer.price < offer.item.priceHistory.average * 0.8; + const isNewOffer = + new Date().getTime() - offer.timestamp.getTime() < 1000 * 60 * 30; // 30 min + + return ( +
setIsHovered(true)} + onMouseLeave={() => setIsHovered(false)} + > + {/* Badges de status */} +
+ {isNewOffer && ( + + ⭐ {t("status.new")} + + )} + {isHotDeal && ( + + 🔥 {t("status.hotDeal")} + + )} +
+ + {/* Botão de favorito */} + + +
+ {/* Ícone do item com raridade */} +
+
+ {getCategoryIcon(offer.item.category)} +
+ + {offer.item.rarity} + +
+ + {/* Informações do item */} +
+
+

+ {offer.item.name} +

+
+
+ + + {getInitials(offer.seller)} + + + {offer.seller} +
+
+ + {formatTimeAgo(offer.timestamp)} +
+
+
+ + {/* Estatísticas do item */} + {offer.item.stats && ( +
+ {offer.item.stats.attack && ( +
+ + {offer.item.stats.attack} ATK +
+ )} + {offer.item.stats.defense && ( +
+ + {offer.item.stats.defense} DEF +
+ )} + {offer.item.stats.armor && ( +
+ + {offer.item.stats.armor} ARM +
+ )} +
+ )} +
+ + {/* Preço e ações */} +
+
+
+
+ + Preço +
+ +

+ {offer.item.stackable + ? `Stack: ${offer.quantity}x` + : `Qtd: ${offer.quantity}`} + {offer.item.stackable && offer.quantity > 1 && ( + + {Math.round(offer.price / offer.quantity).toLocaleString()}{" "} + gp cada + + )} +

+
+ + + {offer.type === "buy" ? "Comprando" : "Vendendo"} + +
+ +
+ + +
+
+
+
+ ); +} diff --git a/components/sections/escrow-banner.tsx b/components/sections/escrow-banner.tsx new file mode 100644 index 0000000..d789651 --- /dev/null +++ b/components/sections/escrow-banner.tsx @@ -0,0 +1,40 @@ +"use client"; + +import { useTranslations } from "next-intl"; +import { Button } from "@/components/ui/button"; +import { Shield, Coins } from "lucide-react"; + +export function EscrowBanner() { + const t = useTranslations("home.escrow"); + + return ( +
+
+
+
+
+ +
+
+

+ {t("title")} +

+

{t("description")}

+
+
+ +
+
+

Escrow Balance

+

🪙 0 gp

+
+ +
+
+
+
+ ); +} diff --git a/components/sections/escrow-explanation.tsx b/components/sections/escrow-explanation.tsx new file mode 100644 index 0000000..0e38dbd --- /dev/null +++ b/components/sections/escrow-explanation.tsx @@ -0,0 +1,258 @@ +"use client"; + +import { useTranslations } from "next-intl"; +import { BlurFade } from "@/components/ui/blur-fade"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion"; +import { Dialog, DialogContent, DialogTrigger } from "@/components/ui/dialog"; +import { NumberTicker } from "@/components/ui/number-ticker"; +import { + Wallet, + Handshake, + CheckCircle, + Shield, + Zap, + BarChart, + Lock, +} from "lucide-react"; + +export function EscrowExplanation() { + const t = useTranslations("home.escrowExplanation"); + + const benefits = [ + { + icon: Shield, + title: t("benefits.protection.title"), + description: t("benefits.protection.description"), + color: "text-green-500", + }, + { + icon: Zap, + title: t("benefits.fast.title"), + description: t("benefits.fast.description"), + color: "text-blue-500", + }, + { + icon: BarChart, + title: t("benefits.history.title"), + description: t("benefits.history.description"), + color: "text-purple-500", + }, + { + icon: Lock, + title: t("benefits.trusted.title"), + description: t("benefits.trusted.description"), + color: "text-orange-500", + }, + ]; + + const faqItems = [ + { + question: t("faq.q1"), + answer: t("faq.a1"), + }, + { + question: t("faq.q2"), + answer: t("faq.a2"), + }, + { + question: t("faq.q3"), + answer: t("faq.a3"), + }, + { + question: t("faq.q4"), + answer: t("faq.a4"), + }, + { + question: t("faq.q5"), + answer: t("faq.a5"), + }, + ]; + + const stats = [ + { + value: 208, + suffix: "M", + label: t("stats.volume"), + }, + { + value: 8763, + suffix: "", + label: t("stats.transactions"), + }, + { + value: 99.8, + suffix: "%", + label: t("stats.satisfaction"), + }, + { + value: 2, + suffix: "min", + label: t("stats.avgTime"), + }, + ]; + + return ( +
+
+ {/* Título e Subtítulo */} + +

{t("title")}

+

+ {t("subtitle")} +

+
+ + {/* Cards de Etapas */} +
+ {/* Card 1: Depósito */} + +
+
+ 1 +
+ +

{t("step1.title")}

+

+ {t("step1.description")} +

+
+ + {t("step1.example")} + +
+
+
+ + {/* Card 2: Negociação */} + +
+
+ 2 +
+ +

{t("step2.title")}

+

+ {t("step2.description")} +

+
+ + {t("step2.example")} + +
+
+
+ + {/* Card 3: Conclusão */} + +
+
+ 3 +
+ +

{t("step3.title")}

+

+ {t("step3.description")} +

+
+ + {t("step3.example")} + +
+
+
+
+ + {/* Grid de Benefícios */} +
+ {benefits.map((benefit, idx) => ( + +
+ +

{benefit.title}

+

{benefit.description}

+
+
+ ))} +
+ + {/* FAQ */} + +

+ {t("faq.title")} +

+ + {faqItems.map((item, idx) => ( + + + {item.question} + + + {item.answer} + + + ))} + +
+ + {/* CTA Section */} + +
+ + ✨ Primeiras 3 transações sem taxa! + +

{t("cta.title")}

+
+ + + + + + +
+

+ Vídeo explicativo aqui +

+
+
+
+
+
+
+ + {/* Barra de Estatísticas */} +
+ {stats.map((stat, idx) => ( + +
+

+ + {stat.suffix} +

+

{stat.label}

+
+
+ ))} +
+
+
+ ); +} diff --git a/components/sections/featured-items.tsx b/components/sections/featured-items.tsx new file mode 100644 index 0000000..73dcd7d --- /dev/null +++ b/components/sections/featured-items.tsx @@ -0,0 +1,186 @@ +"use client"; + +import { useState, useMemo } from "react"; +import { useTranslations } from "next-intl"; +import { ItemCard } from "@/components/item-card"; +import { MarketplaceFilters } from "@/components/marketplace-filters"; +import { featuredItems } from "@/lib/mock-data"; +import { BlurFade } from "@/components/ui/blur-fade"; +import { Skeleton } from "@/components/ui/skeleton"; + +export function FeaturedItems() { + const t = useTranslations("home.featured"); + const [searchQuery, setSearchQuery] = useState(""); + const [filters, setFilters] = useState<{ + rarity: string[]; + category: string[]; + priceRange: { min: number; max: number }; + }>({ + rarity: [], + category: [], + priceRange: { min: 0, max: 100000 }, + }); + const [sortBy, setSortBy] = useState("name"); + const [isLoading, setIsLoading] = useState(false); + + // Filtrar e ordenar itens + const filteredItems = useMemo(() => { + let filtered = [...featuredItems]; + + // Filtro por busca + if (searchQuery) { + filtered = filtered.filter((item) => + item.name.toLowerCase().includes(searchQuery.toLowerCase()) + ); + } + + // Filtro por raridade + if (filters.rarity.length > 0) { + filtered = filtered.filter((item) => + filters.rarity.includes(item.rarity) + ); + } + + // Filtro por categoria + if (filters.category.length > 0) { + filtered = filtered.filter((item) => + filters.category.includes(item.category) + ); + } + + // Filtro por faixa de preço + filtered = filtered.filter( + (item) => + item.price.buy >= filters.priceRange.min && + item.price.buy <= filters.priceRange.max + ); + + // Ordenação + filtered.sort((a, b) => { + switch (sortBy) { + case "name": + return a.name.localeCompare(b.name); + case "price-asc": + return a.price.buy - b.price.buy; + case "price-desc": + return b.price.buy - a.price.buy; + case "rarity": + const rarityOrder = { + common: 0, + uncommon: 1, + rare: 2, + epic: 3, + legendary: 4, + }; + return rarityOrder[a.rarity] - rarityOrder[b.rarity]; + case "trending": + return b.priceHistory.highest - a.priceHistory.highest; + case "recent": + // Simular ordenação por data (mais recente primeiro) + return Math.random() - 0.5; + default: + return 0; + } + }); + + return filtered; + }, [searchQuery, filters, sortBy]); + + const handleSearch = (query: string) => { + setSearchQuery(query); + }; + + const handleFilter = (newFilters: any) => { + setFilters(newFilters); + }; + + const handleSort = (sortValue: string) => { + setSortBy(sortValue); + }; + + // Skeleton para loading + const LoadingSkeleton = () => ( +
+ {Array.from({ length: 8 }).map((_, index) => ( +
+ +
+ + + +
+
+ + +
+
+ ))} +
+ ); + + return ( +
+
+ +
+

{t("title")}

+

+ Descubra os melhores itens do servidor com preços atualizados em + tempo real +

+
+
+ + {/* Filtros e busca */} +
+ +
+ + {/* Grid de itens */} + {isLoading ? ( + + ) : ( +
+ {filteredItems.map((item, index) => ( + + + + ))} +
+ )} + + {/* Mensagem quando não há resultados */} + {filteredItems.length === 0 && !isLoading && ( +
+
🔍
+

+ Nenhum item encontrado +

+

+ Tente ajustar os filtros ou fazer uma nova busca +

+ +
+ )} +
+
+ ); +} diff --git a/components/sections/hero-section.tsx b/components/sections/hero-section.tsx new file mode 100644 index 0000000..2cd2107 --- /dev/null +++ b/components/sections/hero-section.tsx @@ -0,0 +1,39 @@ +"use client"; + +import { useTranslations } from "next-intl"; +import { AnimatedGradientText } from "@/components/ui/animated-gradient-text"; +import { ShimmerButton } from "@/components/ui/shimmer-button"; +import { BlurFade } from "@/components/ui/blur-fade"; +import Link from "next/link"; + +export function HeroSection() { + const t = useTranslations("home.hero"); + + return ( +
+
+ + + {t("title")} + + + + +

+ {t("subtitle")} +

+
+ + +
+ + + {t("cta")} + + +
+
+
+
+ ); +} diff --git a/components/sections/how-it-works.tsx b/components/sections/how-it-works.tsx new file mode 100644 index 0000000..b0dbb79 --- /dev/null +++ b/components/sections/how-it-works.tsx @@ -0,0 +1,65 @@ +"use client"; + +import { useTranslations } from "next-intl"; +import { BentoGrid, BentoCard } from "@/components/ui/bento-grid"; +import { ShoppingBag, Coins, Shield } from "lucide-react"; + +export function HowItWorks() { + const t = useTranslations("home.howItWorks"); + + const features = [ + { + name: t("buy"), + description: "Encontre os melhores itens com preços justos", + className: "md:col-span-1", + background: ( +
+ ), + Icon: ShoppingBag, + href: "/market", + cta: "Ver Mercado", + }, + { + name: t("sell"), + description: "Venda seu loot e ganhe gold rapidamente", + className: "md:col-span-1", + background: ( +
+ ), + Icon: Coins, + href: "/market", + cta: "Vender Agora", + }, + { + name: t("trade"), + description: "Sistema seguro de escrow protege suas transações", + className: "md:col-span-2", + background: ( +
+ ), + Icon: Shield, + href: "/market", + cta: "Negociar", + }, + ]; + + return ( +
+
+
+

{t("title")}

+

+ Descubra como funciona nossa plataforma de trading segura e + eficiente +

+
+ + + {features.map((feature, idx) => ( + + ))} + +
+
+ ); +} diff --git a/components/sections/market-offers.tsx b/components/sections/market-offers.tsx new file mode 100644 index 0000000..fd6ba4d --- /dev/null +++ b/components/sections/market-offers.tsx @@ -0,0 +1,44 @@ +"use client"; + +import { useTranslations } from "next-intl"; +import { AnimatedList } from "@/components/ui/animated-list"; +import { OfferCard } from "@/components/offer-card"; +import { buyOffers, sellOffers } from "@/lib/mock-data"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; + +export function MarketOffers() { + const t = useTranslations("home.offers"); + + return ( +
+
+

+ Ofertas do Mercado +

+ + + + {t("buying")} + {t("selling")} + + + + + {buyOffers.map((offer) => ( + + ))} + + + + + + {sellOffers.map((offer) => ( + + ))} + + + +
+
+ ); +} diff --git a/components/sections/market-stats.tsx b/components/sections/market-stats.tsx new file mode 100644 index 0000000..f1667b2 --- /dev/null +++ b/components/sections/market-stats.tsx @@ -0,0 +1,209 @@ +"use client"; + +import { useTranslations } from "next-intl"; +import { NumberTicker } from "@/components/ui/number-ticker"; +import { Badge } from "@/components/ui/badge"; +import { + ShoppingCart, + TrendingUp, + Users, + DollarSign, + ArrowUp, + ArrowDown, +} from "lucide-react"; +import { AreaChart, Area, ResponsiveContainer } from "recharts"; +import { buyOffers, sellOffers } from "@/lib/mock-data"; + +interface StatData { + icon: typeof ShoppingCart; + value: number; + label: string; + change: number; // percentual + trend: "up" | "down"; + sparklineData: number[]; // 7 pontos + iconColor: string; + iconBg: string; + description: string; + lastUpdated: string; +} + +// Componente Mini Sparkline +function MiniSparkline({ data, color }: { data: number[]; color: string }) { + const chartData = data.map((value, index) => ({ + value, + index, + })); + + return ( +
+ + + + + + + + + + + +
+ ); +} + +// Componente StatCard individual +function StatCard({ stat }: { stat: StatData }) { + const TrendIcon = stat.trend === "up" ? ArrowUp : ArrowDown; + const isPositive = stat.trend === "up"; + + return ( +
+ {/* Ícone com círculo colorido */} +
+ +
+ + {/* Número principal */} +
+

+ +

+
+ + {/* Badge de variação */} +
+ + + {stat.change}% + +
+ + {/* Label e descrição */} +
+

{stat.label}

+

{stat.description}

+
+ + {/* Mini Sparkline */} +
+ +
+ + {/* Timestamp */} +

{stat.lastUpdated}

+
+ ); +} + +export function MarketStats() { + const t = useTranslations("home.stats"); + + // Calcular volume total baseado nos dados mock + const calculateTotalVolume = () => { + const buyVolume = buyOffers.reduce( + (sum, offer) => sum + offer.price * offer.quantity, + 0 + ); + const sellVolume = sellOffers.reduce( + (sum, offer) => sum + offer.price * offer.quantity, + 0 + ); + return Math.round((buyVolume + sellVolume) / 1000); // Dividir por 1000 para número mais legível + }; + + const stats: StatData[] = [ + { + icon: ShoppingCart, + value: 1542, + label: t("activeOffers"), + change: 8.5, + trend: "up", + sparklineData: [1200, 1300, 1400, 1350, 1450, 1500, 1542], + iconColor: "text-orange-500", + iconBg: "bg-orange-500/10", + description: "esta semana", + lastUpdated: `${t("updated")} 5 ${t("minutes")}`, + }, + { + icon: TrendingUp, + value: 8763, + label: t("itemsSold"), + change: 15.3, + trend: "up", + sparklineData: [7200, 7500, 7800, 8000, 8200, 8500, 8763], + iconColor: "text-green-500", + iconBg: "bg-green-500/10", + description: `${t("averagePerDay")}: 292 ${t("salesPerDay")}`, + lastUpdated: `${t("updated")} 5 ${t("minutes")}`, + }, + { + icon: Users, + value: 3421, + label: t("users"), + change: 22.1, + trend: "up", + sparklineData: [3100, 3200, 3250, 3300, 3350, 3400, 3421], + iconColor: "text-blue-500", + iconBg: "bg-blue-500/10", + description: `${t("onlineNow")}: 847 ${t("traders")}`, + lastUpdated: `${t("updated")} 2 ${t("minutes")}`, + }, + { + icon: DollarSign, + value: 208, + label: t("totalVolume"), + change: 18.7, + trend: "up", + sparklineData: [180, 200, 220, 240, 260, 280, 300], + iconColor: "text-purple-500", + iconBg: "bg-purple-500/10", + description: "em gold pieces", + lastUpdated: `${t("today")}: 12.5M gp`, + }, + ]; + + return ( +
+
+

{t("title")}

+ +
+ {stats.map((stat, index) => ( + + ))} +
+
+
+ ); +} diff --git a/components/theme-provider.tsx b/components/theme-provider.tsx new file mode 100644 index 0000000..fca1b71 --- /dev/null +++ b/components/theme-provider.tsx @@ -0,0 +1,10 @@ +"use client"; + +import { ThemeProvider as NextThemesProvider } from "next-themes"; +import { type ComponentProps } from "react"; + +type ThemeProviderProps = ComponentProps; + +export function ThemeProvider({ children, ...props }: ThemeProviderProps) { + return {children}; +} diff --git a/components/theme-toggle.tsx b/components/theme-toggle.tsx new file mode 100644 index 0000000..258cf81 --- /dev/null +++ b/components/theme-toggle.tsx @@ -0,0 +1,44 @@ +"use client"; + +import { Moon, Sun, Monitor } from "lucide-react"; +import { useTheme } from "next-themes"; +import { Button } from "@/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { useTranslations } from "next-intl"; + +export function ThemeToggle() { + const { setTheme, theme } = useTheme(); + const t = useTranslations("header.theme"); + + return ( + + + + + + setTheme("light")}> + + {t("light")} + + setTheme("dark")}> + + {t("dark")} + + setTheme("system")}> + + {t("system")} + + + + ); +} + diff --git a/components/ui/accordion.tsx b/components/ui/accordion.tsx new file mode 100644 index 0000000..c6fec50 --- /dev/null +++ b/components/ui/accordion.tsx @@ -0,0 +1,57 @@ +"use client"; + +import * as React from "react"; +import * as AccordionPrimitive from "@radix-ui/react-accordion"; +import { ChevronDown } from "lucide-react"; +import { cn } from "@/lib/utils"; + +const Accordion = AccordionPrimitive.Root; + +const AccordionItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AccordionItem.displayName = "AccordionItem"; + +const AccordionTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + svg]:rotate-180", + className + )} + {...props} + > + {children} + + + +)); +AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName; + +const AccordionContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + +
{children}
+
+)); + +AccordionContent.displayName = AccordionPrimitive.Content.displayName; + +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }; diff --git a/components/ui/animated-gradient-text.tsx b/components/ui/animated-gradient-text.tsx new file mode 100644 index 0000000..07214a8 --- /dev/null +++ b/components/ui/animated-gradient-text.tsx @@ -0,0 +1,38 @@ +import { ComponentPropsWithoutRef } from "react" + +import { cn } from "@/lib/utils" + +export interface AnimatedGradientTextProps + extends ComponentPropsWithoutRef<"div"> { + speed?: number + colorFrom?: string + colorTo?: string +} + +export function AnimatedGradientText({ + children, + className, + speed = 1, + colorFrom = "#ffaa40", + colorTo = "#9c40ff", + ...props +}: AnimatedGradientTextProps) { + return ( + + {children} + + ) +} diff --git a/components/ui/animated-list.tsx b/components/ui/animated-list.tsx new file mode 100644 index 0000000..1e29b9f --- /dev/null +++ b/components/ui/animated-list.tsx @@ -0,0 +1,73 @@ +"use client" + +import React, { + ComponentPropsWithoutRef, + useEffect, + useMemo, + useState, +} from "react" +import { AnimatePresence, motion, MotionProps } from "motion/react" + +import { cn } from "@/lib/utils" + +export function AnimatedListItem({ children }: { children: React.ReactNode }) { + const animations: MotionProps = { + initial: { scale: 0, opacity: 0 }, + animate: { scale: 1, opacity: 1, originY: 0 }, + exit: { scale: 0, opacity: 0 }, + transition: { type: "spring", stiffness: 350, damping: 40 }, + } + + return ( + + {children} + + ) +} + +export interface AnimatedListProps extends ComponentPropsWithoutRef<"div"> { + children: React.ReactNode + delay?: number +} + +export const AnimatedList = React.memo( + ({ children, className, delay = 1000, ...props }: AnimatedListProps) => { + const [index, setIndex] = useState(0) + const childrenArray = useMemo( + () => React.Children.toArray(children), + [children] + ) + + useEffect(() => { + if (index < childrenArray.length - 1) { + const timeout = setTimeout(() => { + setIndex((prevIndex) => (prevIndex + 1) % childrenArray.length) + }, delay) + + return () => clearTimeout(timeout) + } + }, [index, delay, childrenArray.length]) + + const itemsToShow = useMemo(() => { + const result = childrenArray.slice(0, index + 1).reverse() + return result + }, [index, childrenArray]) + + return ( +
+ + {itemsToShow.map((item) => ( + + {item} + + ))} + +
+ ) + } +) + +AnimatedList.displayName = "AnimatedList" diff --git a/components/ui/avatar.tsx b/components/ui/avatar.tsx new file mode 100644 index 0000000..71e428b --- /dev/null +++ b/components/ui/avatar.tsx @@ -0,0 +1,53 @@ +"use client" + +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +function Avatar({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarImage({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function AvatarFallback({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Avatar, AvatarImage, AvatarFallback } diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx new file mode 100644 index 0000000..fd3a406 --- /dev/null +++ b/components/ui/badge.tsx @@ -0,0 +1,46 @@ +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 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: + "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + secondary: + "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + destructive: + "border-transparent 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: + "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +function Badge({ + className, + variant, + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot : "span" + + return ( + + ) +} + +export { Badge, badgeVariants } diff --git a/components/ui/bento-grid.tsx b/components/ui/bento-grid.tsx new file mode 100644 index 0000000..cef1e22 --- /dev/null +++ b/components/ui/bento-grid.tsx @@ -0,0 +1,96 @@ +import { ComponentPropsWithoutRef, ReactNode } from "react"; +import { ArrowRightIcon } from "@radix-ui/react-icons"; + +import { cn } from "@/lib/utils"; +import { Button } from "@/components/ui/button"; + +interface BentoGridProps extends ComponentPropsWithoutRef<"div"> { + children: ReactNode; + className?: string; +} + +interface BentoCardProps extends ComponentPropsWithoutRef<"div"> { + name: string; + className: string; + background: ReactNode; + Icon: React.ElementType; + description: string; + href: string; + cta: string; +} + +const BentoGrid = ({ children, className, ...props }: BentoGridProps) => { + return ( +
+ {children} +
+ ); +}; + +const BentoCard = ({ + name, + className, + background, + Icon, + description, + href, + cta, + ...props +}: BentoCardProps) => ( +
+
{background}
+
+
+
+
+ +
+

{name}

+
+

{description}

+
+ + +
+
+); + +export { BentoCard, BentoGrid }; diff --git a/components/ui/blur-fade.tsx b/components/ui/blur-fade.tsx new file mode 100644 index 0000000..6609f5a --- /dev/null +++ b/components/ui/blur-fade.tsx @@ -0,0 +1,81 @@ +"use client" + +import { useRef } from "react" +import { + AnimatePresence, + motion, + MotionProps, + useInView, + UseInViewOptions, + Variants, +} from "motion/react" + +type MarginType = UseInViewOptions["margin"] + +interface BlurFadeProps extends MotionProps { + children: React.ReactNode + className?: string + variant?: { + hidden: { y: number } + visible: { y: number } + } + duration?: number + delay?: number + offset?: number + direction?: "up" | "down" | "left" | "right" + inView?: boolean + inViewMargin?: MarginType + blur?: string +} + +export function BlurFade({ + children, + className, + variant, + duration = 0.4, + delay = 0, + offset = 6, + direction = "down", + inView = false, + inViewMargin = "-50px", + blur = "6px", + ...props +}: BlurFadeProps) { + const ref = useRef(null) + const inViewResult = useInView(ref, { once: true, margin: inViewMargin }) + const isInView = !inView || inViewResult + const defaultVariants: Variants = { + hidden: { + [direction === "left" || direction === "right" ? "x" : "y"]: + direction === "right" || direction === "down" ? -offset : offset, + opacity: 0, + filter: `blur(${blur})`, + }, + visible: { + [direction === "left" || direction === "right" ? "x" : "y"]: 0, + opacity: 1, + filter: `blur(0px)`, + }, + } + const combinedVariants = variant || defaultVariants + return ( + + + {children} + + + ) +} diff --git a/components/ui/button.tsx b/components/ui/button.tsx new file mode 100644 index 0000000..21409a0 --- /dev/null +++ b/components/ui/button.tsx @@ -0,0 +1,60 @@ +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", + 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-sm": "size-8", + "icon-lg": "size-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +function Button({ + className, + variant, + size, + 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/dialog.tsx b/components/ui/dialog.tsx new file mode 100644 index 0000000..fdfe99c --- /dev/null +++ b/components/ui/dialog.tsx @@ -0,0 +1,121 @@ +"use client"; + +import * as React from "react"; +import * as DialogPrimitive from "@radix-ui/react-dialog"; +import { X } from "lucide-react"; +import { cn } from "@/lib/utils"; + +const Dialog = DialogPrimitive.Root; + +const DialogTrigger = DialogPrimitive.Trigger; + +const DialogPortal = DialogPrimitive.Portal; + +const DialogClose = DialogPrimitive.Close; + +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; + +const DialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)); +DialogContent.displayName = DialogPrimitive.Content.displayName; + +const DialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +DialogHeader.displayName = "DialogHeader"; + +const DialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +DialogFooter.displayName = "DialogFooter"; + +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogTitle.displayName = DialogPrimitive.Title.displayName; + +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogDescription.displayName = DialogPrimitive.Description.displayName; + +export { + Dialog, + DialogPortal, + DialogOverlay, + DialogClose, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +}; diff --git a/components/ui/dot-pattern.tsx b/components/ui/dot-pattern.tsx new file mode 100644 index 0000000..17d6567 --- /dev/null +++ b/components/ui/dot-pattern.tsx @@ -0,0 +1,158 @@ +"use client" + +import React, { useEffect, useId, useRef, useState } from "react" +import { motion } from "motion/react" + +import { cn } from "@/lib/utils" + +/** + * DotPattern Component Props + * + * @param {number} [width=16] - The horizontal spacing between dots + * @param {number} [height=16] - The vertical spacing between dots + * @param {number} [x=0] - The x-offset of the entire pattern + * @param {number} [y=0] - The y-offset of the entire pattern + * @param {number} [cx=1] - The x-offset of individual dots + * @param {number} [cy=1] - The y-offset of individual dots + * @param {number} [cr=1] - The radius of each dot + * @param {string} [className] - Additional CSS classes to apply to the SVG container + * @param {boolean} [glow=false] - Whether dots should have a glowing animation effect + */ +interface DotPatternProps extends React.SVGProps { + width?: number + height?: number + x?: number + y?: number + cx?: number + cy?: number + cr?: number + className?: string + glow?: boolean + [key: string]: unknown +} + +/** + * DotPattern Component + * + * A React component that creates an animated or static dot pattern background using SVG. + * The pattern automatically adjusts to fill its container and can optionally display glowing dots. + * + * @component + * + * @see DotPatternProps for the props interface. + * + * @example + * // Basic usage + * + * + * // With glowing effect and custom spacing + * + * + * @notes + * - The component is client-side only ("use client") + * - Automatically responds to container size changes + * - When glow is enabled, dots will animate with random delays and durations + * - Uses Motion for animations + * - Dots color can be controlled via the text color utility classes + */ + +export function DotPattern({ + width = 16, + height = 16, + x = 0, + y = 0, + cx = 1, + cy = 1, + cr = 1, + className, + glow = false, + ...props +}: DotPatternProps) { + const id = useId() + const containerRef = useRef(null) + const [dimensions, setDimensions] = useState({ width: 0, height: 0 }) + + useEffect(() => { + const updateDimensions = () => { + if (containerRef.current) { + const { width, height } = containerRef.current.getBoundingClientRect() + setDimensions({ width, height }) + } + } + + updateDimensions() + window.addEventListener("resize", updateDimensions) + return () => window.removeEventListener("resize", updateDimensions) + }, []) + + const dots = Array.from( + { + length: + Math.ceil(dimensions.width / width) * + Math.ceil(dimensions.height / height), + }, + (_, i) => { + const col = i % Math.ceil(dimensions.width / width) + const row = Math.floor(i / Math.ceil(dimensions.width / width)) + return { + x: col * width + cx, + y: row * height + cy, + delay: Math.random() * 5, + duration: Math.random() * 3 + 2, + } + } + ) + + return ( + + ) +} 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/globe.tsx b/components/ui/globe.tsx new file mode 100644 index 0000000..fa1e30d --- /dev/null +++ b/components/ui/globe.tsx @@ -0,0 +1,128 @@ +"use client" + +import { useEffect, useRef } from "react" +import createGlobe, { COBEOptions } from "cobe" +import { useMotionValue, useSpring } from "motion/react" + +import { cn } from "@/lib/utils" + +const MOVEMENT_DAMPING = 1400 + +const GLOBE_CONFIG: COBEOptions = { + width: 800, + height: 800, + onRender: () => {}, + devicePixelRatio: 2, + phi: 0, + theta: 0.3, + dark: 0, + diffuse: 0.4, + mapSamples: 16000, + mapBrightness: 1.2, + baseColor: [1, 1, 1], + markerColor: [251 / 255, 100 / 255, 21 / 255], + glowColor: [1, 1, 1], + markers: [ + { location: [14.5995, 120.9842], size: 0.03 }, + { location: [19.076, 72.8777], size: 0.1 }, + { location: [23.8103, 90.4125], size: 0.05 }, + { location: [30.0444, 31.2357], size: 0.07 }, + { location: [39.9042, 116.4074], size: 0.08 }, + { location: [-23.5505, -46.6333], size: 0.1 }, + { location: [19.4326, -99.1332], size: 0.1 }, + { location: [40.7128, -74.006], size: 0.1 }, + { location: [34.6937, 135.5022], size: 0.05 }, + { location: [41.0082, 28.9784], size: 0.06 }, + ], +} + +export function Globe({ + className, + config = GLOBE_CONFIG, +}: { + className?: string + config?: COBEOptions +}) { + let phi = 0 + let width = 0 + const canvasRef = useRef(null) + const pointerInteracting = useRef(null) + const pointerInteractionMovement = useRef(0) + + const r = useMotionValue(0) + const rs = useSpring(r, { + mass: 1, + damping: 30, + stiffness: 100, + }) + + const updatePointerInteraction = (value: number | null) => { + pointerInteracting.current = value + if (canvasRef.current) { + canvasRef.current.style.cursor = value !== null ? "grabbing" : "grab" + } + } + + const updateMovement = (clientX: number) => { + if (pointerInteracting.current !== null) { + const delta = clientX - pointerInteracting.current + pointerInteractionMovement.current = delta + r.set(r.get() + delta / MOVEMENT_DAMPING) + } + } + + useEffect(() => { + const onResize = () => { + if (canvasRef.current) { + width = canvasRef.current.offsetWidth + } + } + + window.addEventListener("resize", onResize) + onResize() + + const globe = createGlobe(canvasRef.current!, { + ...config, + width: width * 2, + height: width * 2, + onRender: (state) => { + if (!pointerInteracting.current) phi += 0.005 + state.phi = phi + rs.get() + state.width = width * 2 + state.height = width * 2 + }, + }) + + setTimeout(() => (canvasRef.current!.style.opacity = "1"), 0) + return () => { + globe.destroy() + window.removeEventListener("resize", onResize) + } + }, [rs, config]) + + return ( +
+ { + pointerInteracting.current = e.clientX + updatePointerInteraction(e.clientX) + }} + onPointerUp={() => updatePointerInteraction(null)} + onPointerOut={() => updatePointerInteraction(null)} + onMouseMove={(e) => updateMovement(e.clientX)} + onTouchMove={(e) => + e.touches[0] && updateMovement(e.touches[0].clientX) + } + /> +
+ ) +} diff --git a/components/ui/input.tsx b/components/ui/input.tsx new file mode 100644 index 0000000..03295ca --- /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/magic-card.tsx b/components/ui/magic-card.tsx new file mode 100644 index 0000000..c78a5f8 --- /dev/null +++ b/components/ui/magic-card.tsx @@ -0,0 +1,103 @@ +"use client" + +import React, { useCallback, useEffect } from "react" +import { motion, useMotionTemplate, useMotionValue } from "motion/react" + +import { cn } from "@/lib/utils" + +interface MagicCardProps { + children?: React.ReactNode + className?: string + gradientSize?: number + gradientColor?: string + gradientOpacity?: number + gradientFrom?: string + gradientTo?: string +} + +export function MagicCard({ + children, + className, + gradientSize = 200, + gradientColor = "#262626", + gradientOpacity = 0.8, + gradientFrom = "#9E7AFF", + gradientTo = "#FE8BBB", +}: MagicCardProps) { + const mouseX = useMotionValue(-gradientSize) + const mouseY = useMotionValue(-gradientSize) + const reset = useCallback(() => { + mouseX.set(-gradientSize) + mouseY.set(-gradientSize) + }, [gradientSize, mouseX, mouseY]) + + const handlePointerMove = useCallback( + (e: React.PointerEvent) => { + const rect = e.currentTarget.getBoundingClientRect() + mouseX.set(e.clientX - rect.left) + mouseY.set(e.clientY - rect.top) + }, + [mouseX, mouseY] + ) + + useEffect(() => { + reset() + }, [reset]) + + useEffect(() => { + const handleGlobalPointerOut = (e: PointerEvent) => { + if (!e.relatedTarget) { + reset() + } + } + + const handleVisibility = () => { + if (document.visibilityState !== "visible") { + reset() + } + } + + window.addEventListener("pointerout", handleGlobalPointerOut) + window.addEventListener("blur", reset) + document.addEventListener("visibilitychange", handleVisibility) + + return () => { + window.removeEventListener("pointerout", handleGlobalPointerOut) + window.removeEventListener("blur", reset) + document.removeEventListener("visibilitychange", handleVisibility) + } + }, [reset]) + + return ( +
+ +
+ +
{children}
+
+ ) +} diff --git a/components/ui/morphing-text.tsx b/components/ui/morphing-text.tsx new file mode 100644 index 0000000..5ac10f9 --- /dev/null +++ b/components/ui/morphing-text.tsx @@ -0,0 +1,149 @@ +"use client" + +import { useCallback, useEffect, useRef } from "react" + +import { cn } from "@/lib/utils" + +const morphTime = 1.5 +const cooldownTime = 0.5 + +const useMorphingText = (texts: string[]) => { + const textIndexRef = useRef(0) + const morphRef = useRef(0) + const cooldownRef = useRef(0) + const timeRef = useRef(new Date()) + + const text1Ref = useRef(null) + const text2Ref = useRef(null) + + const setStyles = useCallback( + (fraction: number) => { + const [current1, current2] = [text1Ref.current, text2Ref.current] + if (!current1 || !current2) return + + current2.style.filter = `blur(${Math.min(8 / fraction - 8, 100)}px)` + current2.style.opacity = `${Math.pow(fraction, 0.4) * 100}%` + + const invertedFraction = 1 - fraction + current1.style.filter = `blur(${Math.min( + 8 / invertedFraction - 8, + 100 + )}px)` + current1.style.opacity = `${Math.pow(invertedFraction, 0.4) * 100}%` + + current1.textContent = texts[textIndexRef.current % texts.length] + current2.textContent = texts[(textIndexRef.current + 1) % texts.length] + }, + [texts] + ) + + const doMorph = useCallback(() => { + morphRef.current -= cooldownRef.current + cooldownRef.current = 0 + + let fraction = morphRef.current / morphTime + + if (fraction > 1) { + cooldownRef.current = cooldownTime + fraction = 1 + } + + setStyles(fraction) + + if (fraction === 1) { + textIndexRef.current++ + } + }, [setStyles]) + + const doCooldown = useCallback(() => { + morphRef.current = 0 + const [current1, current2] = [text1Ref.current, text2Ref.current] + if (current1 && current2) { + current2.style.filter = "none" + current2.style.opacity = "100%" + current1.style.filter = "none" + current1.style.opacity = "0%" + } + }, []) + + useEffect(() => { + let animationFrameId: number + + const animate = () => { + animationFrameId = requestAnimationFrame(animate) + + const newTime = new Date() + const dt = (newTime.getTime() - timeRef.current.getTime()) / 1000 + timeRef.current = newTime + + cooldownRef.current -= dt + + if (cooldownRef.current <= 0) doMorph() + else doCooldown() + } + + animate() + return () => { + cancelAnimationFrame(animationFrameId) + } + }, [doMorph, doCooldown]) + + return { text1Ref, text2Ref } +} + +interface MorphingTextProps { + className?: string + texts: string[] +} + +const Texts: React.FC> = ({ texts }) => { + const { text1Ref, text2Ref } = useMorphingText(texts) + return ( + <> + + + + ) +} + +const SvgFilters: React.FC = () => ( + + + + + + + +) + +export const MorphingText: React.FC = ({ + texts, + className, +}) => ( +
+ + +
+) diff --git a/components/ui/navigation-menu.tsx b/components/ui/navigation-menu.tsx new file mode 100644 index 0000000..1199945 --- /dev/null +++ b/components/ui/navigation-menu.tsx @@ -0,0 +1,168 @@ +import * as React from "react" +import * as NavigationMenuPrimitive from "@radix-ui/react-navigation-menu" +import { cva } from "class-variance-authority" +import { ChevronDownIcon } from "lucide-react" + +import { cn } from "@/lib/utils" + +function NavigationMenu({ + className, + children, + viewport = true, + ...props +}: React.ComponentProps & { + viewport?: boolean +}) { + return ( + + {children} + {viewport && } + + ) +} + +function NavigationMenuList({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function NavigationMenuItem({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +const navigationMenuTriggerStyle = cva( + "group inline-flex h-9 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground disabled:pointer-events-none disabled:opacity-50 data-[state=open]:hover:bg-accent data-[state=open]:text-accent-foreground data-[state=open]:focus:bg-accent data-[state=open]:bg-accent/50 focus-visible:ring-ring/50 outline-none transition-[color,box-shadow] focus-visible:ring-[3px] focus-visible:outline-1" +) + +function NavigationMenuTrigger({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + {children}{" "} + + ) +} + +function NavigationMenuContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function NavigationMenuViewport({ + className, + ...props +}: React.ComponentProps) { + return ( +
+ +
+ ) +} + +function NavigationMenuLink({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function NavigationMenuIndicator({ + className, + ...props +}: React.ComponentProps) { + return ( + +
+ + ) +} + +export { + NavigationMenu, + NavigationMenuList, + NavigationMenuItem, + NavigationMenuContent, + NavigationMenuTrigger, + NavigationMenuLink, + NavigationMenuIndicator, + NavigationMenuViewport, + navigationMenuTriggerStyle, +} diff --git a/components/ui/neon-gradient-card.tsx b/components/ui/neon-gradient-card.tsx new file mode 100644 index 0000000..5454f65 --- /dev/null +++ b/components/ui/neon-gradient-card.tsx @@ -0,0 +1,148 @@ +"use client" + +import { + CSSProperties, + ReactElement, + ReactNode, + useEffect, + useRef, + useState, +} from "react" + +import { cn } from "@/lib/utils" + +interface NeonColorsProps { + firstColor: string + secondColor: string +} + +interface NeonGradientCardProps extends React.HTMLAttributes { + /** + * @default
+ * @type ReactElement + * @description + * The component to be rendered as the card + * */ + as?: ReactElement + /** + * @default "" + * @type string + * @description + * The className of the card + */ + className?: string + + /** + * @default "" + * @type ReactNode + * @description + * The children of the card + * */ + children?: ReactNode + + /** + * @default 5 + * @type number + * @description + * The size of the border in pixels + * */ + borderSize?: number + + /** + * @default 20 + * @type number + * @description + * The size of the radius in pixels + * */ + borderRadius?: number + + /** + * @default "{ firstColor: '#ff00aa', secondColor: '#00FFF1' }" + * @type string + * @description + * The colors of the neon gradient + * */ + neonColors?: NeonColorsProps +} + +export const NeonGradientCard: React.FC = ({ + className, + children, + borderSize = 2, + borderRadius = 20, + neonColors = { + firstColor: "#ff00aa", + secondColor: "#00FFF1", + }, + ...props +}) => { + const containerRef = useRef(null) + const [dimensions, setDimensions] = useState({ width: 0, height: 0 }) + + useEffect(() => { + const updateDimensions = () => { + if (containerRef.current) { + const { offsetWidth, offsetHeight } = containerRef.current + setDimensions({ width: offsetWidth, height: offsetHeight }) + } + } + + updateDimensions() + window.addEventListener("resize", updateDimensions) + + return () => { + window.removeEventListener("resize", updateDimensions) + } + }, []) + + useEffect(() => { + if (containerRef.current) { + const { offsetWidth, offsetHeight } = containerRef.current + setDimensions({ width: offsetWidth, height: offsetHeight }) + } + }, [children]) + + return ( +
+
+ {children} +
+
+ ) +} diff --git a/components/ui/number-ticker.tsx b/components/ui/number-ticker.tsx new file mode 100644 index 0000000..0f12b4f --- /dev/null +++ b/components/ui/number-ticker.tsx @@ -0,0 +1,67 @@ +"use client" + +import { ComponentPropsWithoutRef, useEffect, useRef } from "react" +import { useInView, useMotionValue, useSpring } from "motion/react" + +import { cn } from "@/lib/utils" + +interface NumberTickerProps extends ComponentPropsWithoutRef<"span"> { + value: number + startValue?: number + direction?: "up" | "down" + delay?: number + decimalPlaces?: number +} + +export function NumberTicker({ + value, + startValue = 0, + direction = "up", + delay = 0, + className, + decimalPlaces = 0, + ...props +}: NumberTickerProps) { + const ref = useRef(null) + const motionValue = useMotionValue(direction === "down" ? value : startValue) + const springValue = useSpring(motionValue, { + damping: 60, + stiffness: 100, + }) + const isInView = useInView(ref, { once: true, margin: "0px" }) + + useEffect(() => { + if (isInView) { + const timer = setTimeout(() => { + motionValue.set(direction === "down" ? startValue : value) + }, delay * 1000) + return () => clearTimeout(timer) + } + }, [motionValue, isInView, delay, value, direction, startValue]) + + useEffect( + () => + springValue.on("change", (latest) => { + if (ref.current) { + ref.current.textContent = Intl.NumberFormat("en-US", { + minimumFractionDigits: decimalPlaces, + maximumFractionDigits: decimalPlaces, + }).format(Number(latest.toFixed(decimalPlaces))) + } + }), + [springValue, decimalPlaces] + ) + + return ( + + {startValue} + + ) +} diff --git a/components/ui/particles.tsx b/components/ui/particles.tsx new file mode 100644 index 0000000..5b9da79 --- /dev/null +++ b/components/ui/particles.tsx @@ -0,0 +1,314 @@ +"use client" + +import React, { + ComponentPropsWithoutRef, + useEffect, + useRef, + useState, +} from "react" + +import { cn } from "@/lib/utils" + +interface MousePosition { + x: number + y: number +} + +function MousePosition(): MousePosition { + const [mousePosition, setMousePosition] = useState({ + x: 0, + y: 0, + }) + + useEffect(() => { + const handleMouseMove = (event: MouseEvent) => { + setMousePosition({ x: event.clientX, y: event.clientY }) + } + + window.addEventListener("mousemove", handleMouseMove) + + return () => { + window.removeEventListener("mousemove", handleMouseMove) + } + }, []) + + return mousePosition +} + +interface ParticlesProps extends ComponentPropsWithoutRef<"div"> { + className?: string + quantity?: number + staticity?: number + ease?: number + size?: number + refresh?: boolean + color?: string + vx?: number + vy?: number +} + +function hexToRgb(hex: string): number[] { + hex = hex.replace("#", "") + + if (hex.length === 3) { + hex = hex + .split("") + .map((char) => char + char) + .join("") + } + + const hexInt = parseInt(hex, 16) + const red = (hexInt >> 16) & 255 + const green = (hexInt >> 8) & 255 + const blue = hexInt & 255 + return [red, green, blue] +} + +type Circle = { + x: number + y: number + translateX: number + translateY: number + size: number + alpha: number + targetAlpha: number + dx: number + dy: number + magnetism: number +} + +export const Particles: React.FC = ({ + className = "", + quantity = 100, + staticity = 50, + ease = 50, + size = 0.4, + refresh = false, + color = "#ffffff", + vx = 0, + vy = 0, + ...props +}) => { + const canvasRef = useRef(null) + const canvasContainerRef = useRef(null) + const context = useRef(null) + const circles = useRef([]) + const mousePosition = MousePosition() + const mouse = useRef<{ x: number; y: number }>({ x: 0, y: 0 }) + const canvasSize = useRef<{ w: number; h: number }>({ w: 0, h: 0 }) + const dpr = typeof window !== "undefined" ? window.devicePixelRatio : 1 + const rafID = useRef(null) + const resizeTimeout = useRef(null) + + useEffect(() => { + if (canvasRef.current) { + context.current = canvasRef.current.getContext("2d") + } + initCanvas() + animate() + + const handleResize = () => { + if (resizeTimeout.current) { + clearTimeout(resizeTimeout.current) + } + resizeTimeout.current = setTimeout(() => { + initCanvas() + }, 200) + } + + window.addEventListener("resize", handleResize) + + return () => { + if (rafID.current != null) { + window.cancelAnimationFrame(rafID.current) + } + if (resizeTimeout.current) { + clearTimeout(resizeTimeout.current) + } + window.removeEventListener("resize", handleResize) + } + }, [color]) + + useEffect(() => { + onMouseMove() + }, [mousePosition.x, mousePosition.y]) + + useEffect(() => { + initCanvas() + }, [refresh]) + + const initCanvas = () => { + resizeCanvas() + drawParticles() + } + + const onMouseMove = () => { + if (canvasRef.current) { + const rect = canvasRef.current.getBoundingClientRect() + const { w, h } = canvasSize.current + const x = mousePosition.x - rect.left - w / 2 + const y = mousePosition.y - rect.top - h / 2 + const inside = x < w / 2 && x > -w / 2 && y < h / 2 && y > -h / 2 + if (inside) { + mouse.current.x = x + mouse.current.y = y + } + } + } + + const resizeCanvas = () => { + if (canvasContainerRef.current && canvasRef.current && context.current) { + canvasSize.current.w = canvasContainerRef.current.offsetWidth + canvasSize.current.h = canvasContainerRef.current.offsetHeight + + canvasRef.current.width = canvasSize.current.w * dpr + canvasRef.current.height = canvasSize.current.h * dpr + canvasRef.current.style.width = `${canvasSize.current.w}px` + canvasRef.current.style.height = `${canvasSize.current.h}px` + context.current.scale(dpr, dpr) + + // Clear existing particles and create new ones with exact quantity + circles.current = [] + for (let i = 0; i < quantity; i++) { + const circle = circleParams() + drawCircle(circle) + } + } + } + + const circleParams = (): Circle => { + const x = Math.floor(Math.random() * canvasSize.current.w) + const y = Math.floor(Math.random() * canvasSize.current.h) + const translateX = 0 + const translateY = 0 + const pSize = Math.floor(Math.random() * 2) + size + const alpha = 0 + const targetAlpha = parseFloat((Math.random() * 0.6 + 0.1).toFixed(1)) + const dx = (Math.random() - 0.5) * 0.1 + const dy = (Math.random() - 0.5) * 0.1 + const magnetism = 0.1 + Math.random() * 4 + return { + x, + y, + translateX, + translateY, + size: pSize, + alpha, + targetAlpha, + dx, + dy, + magnetism, + } + } + + const rgb = hexToRgb(color) + + const drawCircle = (circle: Circle, update = false) => { + if (context.current) { + const { x, y, translateX, translateY, size, alpha } = circle + context.current.translate(translateX, translateY) + context.current.beginPath() + context.current.arc(x, y, size, 0, 2 * Math.PI) + context.current.fillStyle = `rgba(${rgb.join(", ")}, ${alpha})` + context.current.fill() + context.current.setTransform(dpr, 0, 0, dpr, 0, 0) + + if (!update) { + circles.current.push(circle) + } + } + } + + const clearContext = () => { + if (context.current) { + context.current.clearRect( + 0, + 0, + canvasSize.current.w, + canvasSize.current.h + ) + } + } + + const drawParticles = () => { + clearContext() + const particleCount = quantity + for (let i = 0; i < particleCount; i++) { + const circle = circleParams() + drawCircle(circle) + } + } + + const remapValue = ( + value: number, + start1: number, + end1: number, + start2: number, + end2: number + ): number => { + const remapped = + ((value - start1) * (end2 - start2)) / (end1 - start1) + start2 + return remapped > 0 ? remapped : 0 + } + + const animate = () => { + clearContext() + circles.current.forEach((circle: Circle, i: number) => { + // Handle the alpha value + const edge = [ + circle.x + circle.translateX - circle.size, // distance from left edge + canvasSize.current.w - circle.x - circle.translateX - circle.size, // distance from right edge + circle.y + circle.translateY - circle.size, // distance from top edge + canvasSize.current.h - circle.y - circle.translateY - circle.size, // distance from bottom edge + ] + const closestEdge = edge.reduce((a, b) => Math.min(a, b)) + const remapClosestEdge = parseFloat( + remapValue(closestEdge, 0, 20, 0, 1).toFixed(2) + ) + if (remapClosestEdge > 1) { + circle.alpha += 0.02 + if (circle.alpha > circle.targetAlpha) { + circle.alpha = circle.targetAlpha + } + } else { + circle.alpha = circle.targetAlpha * remapClosestEdge + } + circle.x += circle.dx + vx + circle.y += circle.dy + vy + circle.translateX += + (mouse.current.x / (staticity / circle.magnetism) - circle.translateX) / + ease + circle.translateY += + (mouse.current.y / (staticity / circle.magnetism) - circle.translateY) / + ease + + drawCircle(circle, true) + + // circle gets out of the canvas + if ( + circle.x < -circle.size || + circle.x > canvasSize.current.w + circle.size || + circle.y < -circle.size || + circle.y > canvasSize.current.h + circle.size + ) { + // remove the circle from the array + circles.current.splice(i, 1) + // create a new circle + const newCircle = circleParams() + drawCircle(newCircle) + } + }) + rafID.current = window.requestAnimationFrame(animate) + } + + return ( + + ) +} diff --git a/components/ui/price-display.tsx b/components/ui/price-display.tsx new file mode 100644 index 0000000..1362bf2 --- /dev/null +++ b/components/ui/price-display.tsx @@ -0,0 +1,37 @@ +"use client"; + +import { Currency } from "@/types/currency"; +import { formatPrice, formatPriceWithTc } from "@/lib/currency-utils"; +import { cn } from "@/lib/utils"; + +interface PriceDisplayProps { + value: number; + currency?: Currency; + showTc?: boolean; + className?: string; + size?: "sm" | "md" | "lg"; +} + +export function PriceDisplay({ + value, + currency = "gp", + showTc = false, + className, + size = "md", +}: PriceDisplayProps) { + const sizeClasses = { + sm: "text-sm", + md: "text-lg", + lg: "text-2xl", + }; + + const formattedPrice = showTc + ? formatPriceWithTc(value) + : formatPrice(value, currency); + + return ( + + 🪙 {formattedPrice} + + ); +} 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/sheet.tsx b/components/ui/sheet.tsx new file mode 100644 index 0000000..84649ad --- /dev/null +++ b/components/ui/sheet.tsx @@ -0,0 +1,139 @@ +"use client" + +import * as React from "react" +import * as SheetPrimitive from "@radix-ui/react-dialog" +import { XIcon } from "lucide-react" + +import { cn } from "@/lib/utils" + +function Sheet({ ...props }: React.ComponentProps) { + return +} + +function SheetTrigger({ + ...props +}: React.ComponentProps) { + return +} + +function SheetClose({ + ...props +}: React.ComponentProps) { + return +} + +function SheetPortal({ + ...props +}: React.ComponentProps) { + return +} + +function SheetOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function SheetContent({ + className, + children, + side = "right", + ...props +}: React.ComponentProps & { + side?: "top" | "right" | "bottom" | "left" +}) { + return ( + + + + {children} + + + Close + + + + ) +} + +function SheetHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function SheetFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ) +} + +function SheetTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function SheetDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { + Sheet, + SheetTrigger, + SheetClose, + SheetContent, + SheetHeader, + SheetFooter, + SheetTitle, + SheetDescription, +} diff --git a/components/ui/shimmer-button.tsx b/components/ui/shimmer-button.tsx new file mode 100644 index 0000000..131f73c --- /dev/null +++ b/components/ui/shimmer-button.tsx @@ -0,0 +1,96 @@ +import React, { ComponentPropsWithoutRef, CSSProperties } from "react" + +import { cn } from "@/lib/utils" + +export interface ShimmerButtonProps extends ComponentPropsWithoutRef<"button"> { + shimmerColor?: string + shimmerSize?: string + borderRadius?: string + shimmerDuration?: string + background?: string + className?: string + children?: React.ReactNode +} + +export const ShimmerButton = React.forwardRef< + HTMLButtonElement, + ShimmerButtonProps +>( + ( + { + shimmerColor = "#ffffff", + shimmerSize = "0.05em", + shimmerDuration = "3s", + borderRadius = "100px", + background = "rgba(0, 0, 0, 1)", + className, + children, + ...props + }, + ref + ) => { + return ( + + ) + } +) + +ShimmerButton.displayName = "ShimmerButton" diff --git a/components/ui/shine-border.tsx b/components/ui/shine-border.tsx new file mode 100644 index 0000000..1a55bfc --- /dev/null +++ b/components/ui/shine-border.tsx @@ -0,0 +1,63 @@ +"use client" + +import * as React from "react" + +import { cn } from "@/lib/utils" + +interface ShineBorderProps extends React.HTMLAttributes { + /** + * Width of the border in pixels + * @default 1 + */ + borderWidth?: number + /** + * Duration of the animation in seconds + * @default 14 + */ + duration?: number + /** + * Color of the border, can be a single color or an array of colors + * @default "#000000" + */ + shineColor?: string | string[] +} + +/** + * Shine Border + * + * An animated background border effect component with configurable properties. + */ +export function ShineBorder({ + borderWidth = 1, + duration = 14, + shineColor = "#000000", + className, + style, + ...props +}: ShineBorderProps) { + return ( +
+ ) +} diff --git a/components/ui/skeleton.tsx b/components/ui/skeleton.tsx new file mode 100644 index 0000000..2c8324d --- /dev/null +++ b/components/ui/skeleton.tsx @@ -0,0 +1,16 @@ +import { cn } from "@/lib/utils"; + +function Skeleton({ + className, + ...props +}: React.HTMLAttributes) { + return ( +
+ ); +} + +export { Skeleton }; + diff --git a/components/ui/tabs.tsx b/components/ui/tabs.tsx new file mode 100644 index 0000000..497ba5e --- /dev/null +++ b/components/ui/tabs.tsx @@ -0,0 +1,66 @@ +"use client" + +import * as React from "react" +import * as TabsPrimitive from "@radix-ui/react-tabs" + +import { cn } from "@/lib/utils" + +function Tabs({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function TabsList({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function TabsTrigger({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +function TabsContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + ) +} + +export { Tabs, TabsList, TabsTrigger, TabsContent } diff --git a/components/ui/word-rotate.tsx b/components/ui/word-rotate.tsx new file mode 100644 index 0000000..68aa49c --- /dev/null +++ b/components/ui/word-rotate.tsx @@ -0,0 +1,50 @@ +"use client" + +import { useEffect, useState } from "react" +import { AnimatePresence, motion, MotionProps } from "motion/react" + +import { cn } from "@/lib/utils" + +interface WordRotateProps { + words: string[] + duration?: number + motionProps?: MotionProps + className?: string +} + +export function WordRotate({ + words, + duration = 2500, + motionProps = { + initial: { opacity: 0, y: -50 }, + animate: { opacity: 1, y: 0 }, + exit: { opacity: 0, y: 50 }, + transition: { duration: 0.25, ease: "easeOut" }, + }, + className, +}: WordRotateProps) { + const [index, setIndex] = useState(0) + + useEffect(() => { + const interval = setInterval(() => { + setIndex((prevIndex) => (prevIndex + 1) % words.length) + }, duration) + + // Clean up interval on unmount + return () => clearInterval(interval) + }, [words, duration]) + + return ( +
+ + + {words[index]} + + +
+ ) +} diff --git a/i18n/request.ts b/i18n/request.ts new file mode 100644 index 0000000..0727f1c --- /dev/null +++ b/i18n/request.ts @@ -0,0 +1,14 @@ +import { getRequestConfig } from "next-intl/server"; +import { routing } from "./routing"; + +export default getRequestConfig(async ({ requestLocale }) => { + let locale = await requestLocale; + if (!locale || !routing.locales.includes(locale as any)) { + locale = routing.defaultLocale; + } + return { + locale, + messages: (await import(`../messages/${locale}.json`)).default, + }; +}); + diff --git a/i18n/routing.ts b/i18n/routing.ts new file mode 100644 index 0000000..aa119e3 --- /dev/null +++ b/i18n/routing.ts @@ -0,0 +1,7 @@ +import { defineRouting } from "next-intl/routing"; + +export const routing = defineRouting({ + locales: ["pt", "en"], + defaultLocale: "pt", +}); + diff --git a/lib/currency-utils.ts b/lib/currency-utils.ts new file mode 100644 index 0000000..34e5ec7 --- /dev/null +++ b/lib/currency-utils.ts @@ -0,0 +1,90 @@ +import { Currency } from "@/types/currency"; + +export function formatPrice(value: number, currency: Currency = "gp"): string { + if (value >= 1000000) { + const millions = value / 1000000; + return `${millions.toFixed(1)}kk ${currency}`; + } + + if (value >= 1000) { + const thousands = value / 1000; + return `${thousands.toFixed(0)}k ${currency}`; + } + + return `${value.toLocaleString("pt-BR")} ${currency}`; +} + +export function convertGpToTc(gp: number): number { + return Math.round(gp / 35000); // 1 TC ≈ 35.000 gp +} + +export function formatLargeNumber(value: number): string { + if (value >= 1000000) { + const millions = value / 1000000; + return `${millions.toFixed(1)}kk`; + } + + if (value >= 1000) { + const thousands = value / 1000; + return `${thousands.toFixed(0)}k`; + } + + return value.toLocaleString("pt-BR"); +} + +export function formatPriceWithTc(gp: number): string { + const tc = convertGpToTc(gp); + if (tc >= 1) { + return `${formatPrice(gp)} (${tc} TC)`; + } + return formatPrice(gp); +} + +export function getRarityColor(rarity: string): string { + switch (rarity) { + case "legendary": + return "text-orange-500"; + case "epic": + return "text-purple-500"; + case "rare": + return "text-blue-500"; + case "uncommon": + return "text-green-500"; + case "common": + return "text-gray-500"; + default: + return "text-gray-500"; + } +} + +export function getRarityBgColor(rarity: string): string { + switch (rarity) { + case "legendary": + return "bg-orange-500/10"; + case "epic": + return "bg-purple-500/10"; + case "rare": + return "bg-blue-500/10"; + case "uncommon": + return "bg-green-500/10"; + case "common": + return "bg-gray-500/10"; + default: + return "bg-gray-500/10"; + } +} + +export function getRarityGlow(rarity: string): string { + switch (rarity) { + case "legendary": + return "shadow-[0_0_15px_rgba(255,140,0,0.5)]"; + case "epic": + return "shadow-[0_0_15px_rgba(155,89,182,0.5)]"; + case "rare": + return "shadow-[0_0_15px_rgba(52,152,219,0.5)]"; + case "uncommon": + return "shadow-[0_0_15px_rgba(46,204,113,0.5)]"; + default: + return ""; + } +} diff --git a/lib/mock-data.ts b/lib/mock-data.ts new file mode 100644 index 0000000..1522edf --- /dev/null +++ b/lib/mock-data.ts @@ -0,0 +1,198 @@ +import { TibiaItem, MarketOffer } from "@/types/item"; + +export const featuredItems: TibiaItem[] = [ + { + id: "1", + name: "Armor of Fire", + image: "/items/armor-of-fire.png", + category: "armor", + rarity: "legendary", + stats: { armor: 16, weight: 120 }, + requirements: { level: 200, vocation: "Knight" }, + protections: { fire: 10 }, + price: { buy: 2500000, sell: 2200000, bid: 2350000 }, + priceHistory: { highest: 2800000, lowest: 2000000, average: 2350000 }, + dropLocation: "Fire Dragon Lair", + }, + { + id: "2", + name: "Demon Helmet", + image: "/items/demon-helmet.png", + category: "helmet", + rarity: "epic", + stats: { armor: 10, weight: 29 }, + requirements: { level: 100, vocation: "Knight" }, + price: { buy: 50000, sell: 45000, bid: 47500 }, + priceHistory: { highest: 55000, lowest: 42000, average: 48500 }, + dropLocation: "Demon Hell", + }, + { + id: "3", + name: "Vampire Shield", + image: "/items/vampire-shield.png", + category: "shield", + rarity: "epic", + stats: { defense: 38, weight: 48 }, + requirements: { level: 150, vocation: "Knight" }, + price: { buy: 450000, sell: 420000, bid: 435000 }, + priceHistory: { highest: 480000, lowest: 400000, average: 435000 }, + dropLocation: "Vampire Crypt", + }, + { + id: "4", + name: "Zaoan Helmet", + image: "/items/zaoan-helmet.png", + category: "helmet", + rarity: "rare", + stats: { armor: 9, weight: 43 }, + requirements: { level: 80, vocation: "Knight" }, + price: { buy: 85000, sell: 75000, bid: 80000 }, + priceHistory: { highest: 95000, lowest: 70000, average: 80000 }, + dropLocation: "Zao Palace", + }, + { + id: "5", + name: "Magic Sword", + image: "/items/magic-sword.png", + category: "weapon", + rarity: "rare", + stats: { attack: 48, defense: 35, weight: 36 }, + requirements: { level: 75, vocation: "Knight" }, + price: { buy: 22000, sell: 20000, bid: 21000 }, + priceHistory: { highest: 28000, lowest: 20000, average: 24000 }, + dropLocation: "Magic Shop", + }, + { + id: "6", + name: "Demonbone Amulet", + image: "/items/demonbone-amulet.png", + category: "amulet", + rarity: "epic", + stats: { armor: 2, weight: 8.5 }, + protections: { fire: 10 }, + price: { buy: 180000, sell: 165000, bid: 172500 }, + priceHistory: { highest: 200000, lowest: 150000, average: 175000 }, + dropLocation: "Demon Hell", + }, + { + id: "7", + name: "Ornate Chestplate", + image: "/items/ornate-chestplate.png", + category: "armor", + rarity: "legendary", + stats: { armor: 18, weight: 185 }, + requirements: { level: 300, vocation: "Knight" }, + protections: { fire: 5, ice: 5 }, + price: { buy: 8500000, sell: 8200000, bid: 8350000 }, + priceHistory: { highest: 9000000, lowest: 8000000, average: 8400000 }, + dropLocation: "Ornate Chest", + }, + { + id: "8", + name: "Ultimate Health Potion", + image: "/items/uhp.png", + category: "potion", + rarity: "common", + stats: { weight: 2 }, + stackable: true, + price: { buy: 310, sell: 280, bid: 295 }, + priceHistory: { highest: 350, lowest: 280, average: 300 }, + dropLocation: "Magic Shop", + }, +]; + +export const buyOffers: MarketOffer[] = [ + { + id: "1", + item: featuredItems[7], // Ultimate Health Potion + seller: "EternalOblivion", + price: 280000, + quantity: 1000, + type: "buy", + timestamp: new Date(Date.now() - 1000 * 60 * 60 * 2), // 2 hours ago + }, + { + id: "2", + item: featuredItems[1], // Demon Helmet + seller: "PallySupreme", + price: 48000, + quantity: 1, + type: "buy", + timestamp: new Date(Date.now() - 1000 * 60 * 45), // 45 min ago + }, + { + id: "3", + item: featuredItems[5], // Demonbone Amulet + seller: "MageDestroyer", + price: 175000, + quantity: 1, + type: "buy", + timestamp: new Date(Date.now() - 1000 * 60 * 60), // 1 hour ago + }, + { + id: "4", + item: featuredItems[4], // Magic Sword + seller: "KnightLegacy", + price: 25000, + quantity: 1, + type: "buy", + timestamp: new Date(Date.now() - 1000 * 60 * 60 * 3), // 3 hours ago + }, + { + id: "5", + item: featuredItems[6], // Ornate Chestplate + seller: "TibiaCollector", + price: 8300000, + quantity: 1, + type: "buy", + timestamp: new Date(Date.now() - 1000 * 60 * 30), // 30 min ago + }, +]; + +export const sellOffers: MarketOffer[] = [ + { + id: "6", + item: featuredItems[7], // Ultimate Health Potion + seller: "EternalOblivion", + price: 280000, + quantity: 1000, + type: "sell", + timestamp: new Date(Date.now() - 1000 * 60 * 60 * 2), // 2 hours ago + }, + { + id: "7", + item: featuredItems[1], // Demon Helmet + seller: "PallySupreme", + price: 48000, + quantity: 1, + type: "sell", + timestamp: new Date(Date.now() - 1000 * 60 * 45), // 45 min ago + }, + { + id: "8", + item: featuredItems[5], // Demonbone Amulet + seller: "MageDestroyer", + price: 175000, + quantity: 1, + type: "sell", + timestamp: new Date(Date.now() - 1000 * 60 * 60), // 1 hour ago + }, + { + id: "9", + item: featuredItems[4], // Magic Sword + seller: "KnightLegacy", + price: 25000, + quantity: 1, + type: "sell", + timestamp: new Date(Date.now() - 1000 * 60 * 60 * 3), // 3 hours ago + }, + { + id: "10", + item: featuredItems[6], // Ornate Chestplate + seller: "TibiaCollector", + price: 8300000, + quantity: 1, + type: "sell", + timestamp: new Date(Date.now() - 1000 * 60 * 30), // 30 min ago + }, +]; 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/messages/en.json b/messages/en.json new file mode 100644 index 0000000..492a67f --- /dev/null +++ b/messages/en.json @@ -0,0 +1,177 @@ +{ + "header": { + "nav": { + "market": "Market", + "about": "About", + "prices": "Prices" + }, + "theme": { + "light": "Light Theme", + "dark": "Dark Theme", + "system": "System" + }, + "language": "Language", + "menu": "Menu" + }, + "home": { + "hero": { + "title": "Buy and Sell Tibia Items", + "subtitle": "Safe and fast marketplace for Pagodera server", + "cta": "View Market" + }, + "howItWorks": { + "title": "How It Works", + "buy": "Buy rare items", + "sell": "Sell your loot", + "trade": "Trade safely" + }, + "featured": { + "title": "Featured Items" + }, + "offers": { + "buying": "Looking to Buy", + "selling": "Selling Now" + }, + "stats": { + "title": "Market Statistics", + "activeOffers": "Active Offers", + "itemsSold": "Items Sold", + "users": "Active Users", + "totalVolume": "Total Volume Traded", + "vsLastMonth": "vs last month", + "updated": "Updated", + "minutes": "minutes", + "hours": "hours", + "onlineNow": "Online now", + "traders": "traders", + "averagePerDay": "Average", + "salesPerDay": "sales/day", + "today": "Today" + }, + "currency": { + "gp": "gold pieces", + "tc": "Tibia Coins", + "kk": "kk" + }, + "vocation": { + "knight": "Knight", + "paladin": "Paladin", + "sorcerer": "Sorcerer", + "druid": "Druid", + "noVocation": "No Vocation" + }, + "protection": { + "fire": "Fire Protection", + "ice": "Ice Protection", + "energy": "Energy Protection", + "earth": "Earth Protection", + "death": "Death Protection", + "holy": "Holy Protection" + }, + "attributes": { + "speedBoost": "Speed Boost", + "manaRegen": "Mana Regen", + "hpRegen": "HP Regen", + "skillBoost": "Skill Boost" + }, + "buttons": { + "deposit": "Deposit", + "negotiate": "Negotiate", + "viewProfile": "View Profile", + "buyNow": "Buy Now", + "viewDetails": "View Details" + }, + "status": { + "hotDeal": "Hot Deal", + "new": "New", + "onlineNow": "Online now" + }, + "categories": { + "armors": "Armors", + "helmets": "Helmets", + "weapons": "Weapons", + "shields": "Shields", + "potions": "Potions", + "runes": "Runes", + "tools": "Tools" + }, + "escrow": { + "title": "Trade safely", + "description": "Secure escrow system protects your transactions", + "deposit": "Deposit" + }, + "escrowExplanation": { + "title": "Trade with Total Security", + "subtitle": "Escrow system protects 100% of your transactions", + "step1": { + "title": "You Deposit", + "description": "Deposit your gold pieces or Tibia Coins on the platform. Your money stays safe in custody until the negotiation is completed.", + "example": "8.500.000 gp → Escrow Account" + }, + "step2": { + "title": "Secure Trade", + "description": "The seller receives notification and delivers the item in-game. Both are protected: you have guaranteed money, they know they'll be paid.", + "example": "Ornate Chestplate → In-game transfer" + }, + "step3": { + "title": "Automatic Payment", + "description": "After confirming receipt, the system automatically releases payment to the seller. If there's a problem, you can open a dispute.", + "example": "8.500.000 gp → Seller + 2% Fee" + }, + "benefits": { + "protection": { + "title": "Scam Protection", + "description": "Impossible to lose your gold. Money is only released after confirmation." + }, + "fast": { + "title": "Fast Transactions", + "description": "Automated process. Most negotiations completed in minutes." + }, + "history": { + "title": "Complete History", + "description": "All transactions recorded. Access your history anytime." + }, + "trusted": { + "title": "Trusted System", + "description": "Used by 3.421+ players. 99.8% success rate in transactions." + } + }, + "faq": { + "title": "Frequently Asked Questions", + "q1": "What happens if the seller doesn't deliver the item?", + "a1": "You can open a dispute within 24 hours. Our team analyzes the case and, if proven, the money returns 100% to your account.", + "q2": "How long is the money held in escrow?", + "a2": "Until you confirm receipt of the item or until the 48-hour deadline. After this period, if there's no dispute, payment is released automatically.", + "q3": "What's the escrow system fee?", + "a3": "We charge only 2% of the transaction value, split between buyer and seller (1% each). It's the lowest fee in the market.", + "q4": "Can I cancel a transaction?", + "a4": "Yes, before the seller sends the item. After delivery is confirmed by the seller, you have 24h to refuse if there's a problem.", + "q5": "Does escrow accept Tibia Coins or just gold?", + "a5": "We accept both! You can deposit gold pieces (gp) or Tibia Coins (TC). Conversion is done automatically by Market rates." + }, + "cta": { + "title": "Ready to trade safely?", + "createOffer": "Create My First Offer", + "watchVideo": "See How It Works" + }, + "stats": { + "volume": "gp transacted safely", + "transactions": "transactions completed", + "satisfaction": "user satisfaction", + "avgTime": "average completion time" + } + }, + "item": { + "buyPrice": "Buy", + "sellPrice": "Sell", + "bidPrice": "Bid", + "priceHistory": "Price History", + "highest": "Highest", + "lowest": "Lowest", + "average": "Average", + "stats": "Statistics", + "viewDetails": "View Details", + "buyNow": "Buy Now" + } + } +} diff --git a/messages/pt.json b/messages/pt.json new file mode 100644 index 0000000..36bde26 --- /dev/null +++ b/messages/pt.json @@ -0,0 +1,177 @@ +{ + "header": { + "nav": { + "market": "Mercado", + "about": "Sobre", + "prices": "Preços" + }, + "theme": { + "light": "Tema Claro", + "dark": "Tema Escuro", + "system": "Sistema" + }, + "language": "Idioma", + "menu": "Menu" + }, + "home": { + "hero": { + "title": "Compre e Venda Itens de Tibia", + "subtitle": "Marketplace seguro e rápido para o servidor Pagodera", + "cta": "Ver Mercado" + }, + "howItWorks": { + "title": "Como Funciona", + "buy": "Compre itens raros", + "sell": "Venda seu loot", + "trade": "Negocie com segurança" + }, + "featured": { + "title": "Itens em Destaque" + }, + "offers": { + "buying": "Procurando Comprar", + "selling": "Vendendo Agora" + }, + "stats": { + "title": "Estatísticas do Mercado", + "activeOffers": "Ofertas Ativas", + "itemsSold": "Itens Vendidos", + "users": "Usuários Ativos", + "totalVolume": "Volume Total Negociado", + "vsLastMonth": "vs mês anterior", + "updated": "Atualizado há", + "minutes": "minutos", + "hours": "horas", + "onlineNow": "Online agora", + "traders": "traders", + "averagePerDay": "Média", + "salesPerDay": "vendas/dia", + "today": "Hoje" + }, + "currency": { + "gp": "gold pieces", + "tc": "Tibia Coins", + "kk": "kk" + }, + "vocation": { + "knight": "Knight", + "paladin": "Paladin", + "sorcerer": "Sorcerer", + "druid": "Druid", + "noVocation": "No Vocation" + }, + "protection": { + "fire": "Fire Protection", + "ice": "Ice Protection", + "energy": "Energy Protection", + "earth": "Earth Protection", + "death": "Death Protection", + "holy": "Holy Protection" + }, + "attributes": { + "speedBoost": "Speed Boost", + "manaRegen": "Mana Regen", + "hpRegen": "HP Regen", + "skillBoost": "Skill Boost" + }, + "buttons": { + "deposit": "Depositar", + "negotiate": "Negociar", + "viewProfile": "Ver Perfil", + "buyNow": "Comprar Agora", + "viewDetails": "Ver Detalhes" + }, + "status": { + "hotDeal": "Hot Deal", + "new": "Novo", + "onlineNow": "Online agora" + }, + "categories": { + "armors": "Armors", + "helmets": "Helmets", + "weapons": "Weapons", + "shields": "Shields", + "potions": "Potions", + "runes": "Runes", + "tools": "Tools" + }, + "escrow": { + "title": "Negocie com segurança", + "description": "Sistema seguro de escrow protege suas transações", + "deposit": "Depositar" + }, + "escrowExplanation": { + "title": "Negocie com Segurança Total", + "subtitle": "Sistema de Escrow protege 100% das suas transações", + "step1": { + "title": "Você Deposita", + "description": "Deposite seus gold pieces ou Tibia Coins na plataforma. Seu dinheiro fica seguro em custódia até a conclusão da negociação.", + "example": "8.500.000 gp → Conta Escrow" + }, + "step2": { + "title": "Troca Segura", + "description": "O vendedor recebe notificação e entrega o item in-game. Ambos estão protegidos: você tem o dinheiro garantido, ele sabe que será pago.", + "example": "Ornate Chestplate → Transferência no jogo" + }, + "step3": { + "title": "Pagamento Automático", + "description": "Após confirmar o recebimento, o sistema libera automaticamente o pagamento para o vendedor. Se houver problema, você pode abrir disputa.", + "example": "8.500.000 gp → Vendedor + Taxa 2%" + }, + "benefits": { + "protection": { + "title": "Proteção Contra Scam", + "description": "Impossível perder seu gold. O dinheiro só é liberado após confirmação." + }, + "fast": { + "title": "Transações Rápidas", + "description": "Processo automatizado. Maioria das negociações concluídas em minutos." + }, + "history": { + "title": "Histórico Completo", + "description": "Todas as transações registradas. Acesse seu histórico a qualquer momento." + }, + "trusted": { + "title": "Sistema Confiável", + "description": "Usado por 3.421+ jogadores. Taxa de sucesso de 99.8% nas transações." + } + }, + "faq": { + "title": "Perguntas Frequentes", + "q1": "O que acontece se o vendedor não entregar o item?", + "a1": "Você pode abrir uma disputa em até 24 horas. Nossa equipe analisa o caso e, se comprovado, o dinheiro retorna 100% para sua conta.", + "q2": "Quanto tempo o dinheiro fica retido no escrow?", + "a2": "Até você confirmar o recebimento do item ou até o prazo de 48 horas. Após esse período, se não houver disputa, o pagamento é liberado automaticamente.", + "q3": "Qual a taxa do sistema escrow?", + "a3": "Cobramos apenas 2% sobre o valor da transação, dividido entre comprador e vendedor (1% cada). É a menor taxa do mercado.", + "q4": "Posso cancelar uma transação?", + "a4": "Sim, antes do vendedor enviar o item. Após a entrega ser confirmada pelo vendedor, você tem 24h para recusar se houver problema.", + "q5": "O escrow aceita Tibia Coins ou apenas gold?", + "a5": "Aceitamos ambos! Você pode depositar gold pieces (gp) ou Tibia Coins (TC). A conversão é feita automaticamente pela cotação do Market." + }, + "cta": { + "title": "Pronto para negociar com segurança?", + "createOffer": "Criar Minha Primeira Oferta", + "watchVideo": "Ver Como Funciona" + }, + "stats": { + "volume": "gp transacionados com segurança", + "transactions": "transações completadas", + "satisfaction": "de satisfação dos usuários", + "avgTime": "tempo médio de conclusão" + } + }, + "item": { + "buyPrice": "Compra", + "sellPrice": "Venda", + "bidPrice": "Lance", + "priceHistory": "Histórico de Preço", + "highest": "Mais Alto", + "lowest": "Mais Baixo", + "average": "Média", + "stats": "Estatísticas", + "viewDetails": "Ver Detalhes", + "buyNow": "Comprar Agora" + } + } +} diff --git a/middleware.ts b/middleware.ts new file mode 100644 index 0000000..7f0b18d --- /dev/null +++ b/middleware.ts @@ -0,0 +1,8 @@ +import createMiddleware from "next-intl/middleware"; +import { routing } from "./i18n/routing"; + +export default createMiddleware(routing); + +export const config = { + matcher: ["/", "/(pt|en)/:path*"], +}; diff --git a/next.config.ts b/next.config.ts index e9ffa30..7a48280 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,7 +1,10 @@ import type { NextConfig } from "next"; +import createNextIntlPlugin from "next-intl/plugin"; + +const withNextIntl = createNextIntlPlugin("./i18n/request.ts"); const nextConfig: NextConfig = { /* config options here */ }; -export default nextConfig; +export default withNextIntl(nextConfig); diff --git a/package-lock.json b/package-lock.json index d88f9f4..e2758cd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,9 +8,17 @@ "name": "pagodera", "version": "0.1.0", "dependencies": { + "@radix-ui/react-navigation-menu": "^1.2.14", + "@radix-ui/react-slot": "^1.2.3", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cobe": "^0.6.5", + "lucide-react": "^0.540.0", + "motion": "^12.23.24", "next": "15.5.0", "react": "19.1.0", - "react-dom": "19.1.0" + "react-dom": "19.1.0", + "tailwind-merge": "^3.3.1" }, "devDependencies": { "@biomejs/biome": "2.2.0", @@ -19,6 +27,7 @@ "@types/react": "^19", "@types/react-dom": "^19", "tailwindcss": "^4", + "tw-animate-css": "^1.3.7", "typescript": "^5" } }, @@ -823,6 +832,352 @@ "node": ">= 10" } }, + "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-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-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.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-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-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-navigation-menu": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.14.tgz", + "integrity": "sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==", + "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-id": "1.1.1", + "@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-use-previous": "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-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.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-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-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-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/@swc/helpers": { "version": "0.5.15", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", @@ -1122,7 +1477,7 @@ "version": "19.1.10", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.10.tgz", "integrity": "sha512-EhBeSYX0Y6ye8pNebpKrwFJq7BoQ8J5SO6NlvNwwHjSj6adXJViPQrKlsyPw7hLBLvckEMO1yxeGdR82YBBlDg==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "csstype": "^3.0.2" @@ -1132,7 +1487,7 @@ "version": "19.1.7", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.7.tgz", "integrity": "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==", - "dev": true, + "devOptional": true, "license": "MIT", "peerDependencies": { "@types/react": "^19.0.0" @@ -1168,12 +1523,42 @@ "node": ">=18" } }, + "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/cobe": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cobe/-/cobe-0.6.5.tgz", + "integrity": "sha512-MA8bu81EFY6JjQpj+FovEuhyJ25khx2Q7Lh+ot/UkCJe5yKyDgzdc6u2lGZIOmsZTXK6Itg1i4lQZIJZbPWnAg==", + "license": "MIT", + "dependencies": { + "phenomenon": "^1.6.0" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -1223,7 +1608,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, + "devOptional": true, "license": "MIT" }, "node_modules/detect-libc": { @@ -1250,6 +1635,33 @@ "node": ">=10.13.0" } }, + "node_modules/framer-motion": { + "version": "12.23.24", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.24.tgz", + "integrity": "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.23.23", + "motion-utils": "^12.23.6", + "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/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", @@ -1513,6 +1925,15 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lucide-react": { + "version": "0.540.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.540.0.tgz", + "integrity": "sha512-armkCAqQvO62EIX4Hq7hqX/q11WSZu0Jd23cnnqx0/49yIxGXyL/zyZfBxNN9YDx0ensPTb4L+DjTh3yQXUxtQ==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/magic-string": { "version": "0.30.17", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz", @@ -1562,6 +1983,47 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/motion": { + "version": "12.23.24", + "resolved": "https://registry.npmjs.org/motion/-/motion-12.23.24.tgz", + "integrity": "sha512-Rc5E7oe2YZ72N//S3QXGzbnXgqNrTESv8KKxABR20q2FLch9gHLo0JLyYo2hZ238bZ9Gx6cWhj9VO0IgwbMjCw==", + "license": "MIT", + "dependencies": { + "framer-motion": "^12.23.24", + "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/motion-dom": { + "version": "12.23.23", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.23.tgz", + "integrity": "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.23.6" + } + }, + "node_modules/motion-utils": { + "version": "12.23.6", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz", + "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==", + "license": "MIT" + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -1660,6 +2122,12 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/phenomenon": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/phenomenon/-/phenomenon-1.6.0.tgz", + "integrity": "sha512-7h9/fjPD3qNlgggzm88cY58l9sudZ6Ey+UmZsizfhtawO6E3srZQXywaNm2lBwT72TbpHYRPy7ytIHeBUD/G0A==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -1820,6 +2288,16 @@ } } }, + "node_modules/tailwind-merge": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz", + "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, "node_modules/tailwindcss": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.12.tgz", @@ -1861,6 +2339,16 @@ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, + "node_modules/tw-animate-css": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.7.tgz", + "integrity": "sha512-lvLb3hTIpB5oGsk8JmLoAjeCHV58nKa2zHYn8yWOoG5JJusH3bhJlF2DLAZ/5NmJ+jyH3ssiAx/2KmbhavJy/A==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Wombosvideo" + } + }, "node_modules/typescript": { "version": "5.9.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", diff --git a/package.json b/package.json index 12c6465..865f444 100644 --- a/package.json +++ b/package.json @@ -10,17 +10,36 @@ "format": "biome format --write" }, "dependencies": { + "@radix-ui/react-accordion": "^1.2.12", + "@radix-ui/react-avatar": "^1.1.10", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-icons": "^1.3.2", + "@radix-ui/react-navigation-menu": "^1.2.14", + "@radix-ui/react-separator": "^1.1.7", + "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-tabs": "^1.1.13", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cobe": "^0.6.5", + "lucide-react": "^0.540.0", + "motion": "^12.23.24", + "next": "15.5.0", + "next-intl": "^4.4.0", + "next-themes": "^0.4.6", "react": "19.1.0", "react-dom": "19.1.0", - "next": "15.5.0" + "recharts": "^3.3.0", + "tailwind-merge": "^3.3.1" }, "devDependencies": { - "typescript": "^5", + "@biomejs/biome": "2.2.0", + "@tailwindcss/postcss": "^4", "@types/node": "^20", "@types/react": "^19", "@types/react-dom": "^19", - "@tailwindcss/postcss": "^4", "tailwindcss": "^4", - "@biomejs/biome": "2.2.0" + "tw-animate-css": "^1.3.7", + "typescript": "^5" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..d18b300 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,2515 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@radix-ui/react-accordion': + specifier: ^1.2.12 + version: 1.2.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-avatar': + specifier: ^1.1.10 + version: 1.1.10(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dialog': + specifier: ^1.1.15 + version: 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-dropdown-menu': + specifier: ^2.1.16 + version: 2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-icons': + specifier: ^1.3.2 + version: 1.3.2(react@19.1.0) + '@radix-ui/react-navigation-menu': + specifier: ^1.2.14 + version: 1.2.14(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-separator': + specifier: ^1.1.7 + version: 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': + specifier: ^1.2.3 + version: 1.2.3(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-tabs': + specifier: ^1.1.13 + version: 1.1.13(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + cobe: + specifier: ^0.6.5 + version: 0.6.5 + lucide-react: + specifier: ^0.540.0 + version: 0.540.0(react@19.1.0) + motion: + specifier: ^12.23.24 + version: 12.23.24(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next: + specifier: 15.5.0 + version: 15.5.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + next-intl: + specifier: ^4.4.0 + version: 4.4.0(next@15.5.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(typescript@5.9.3) + next-themes: + specifier: ^0.4.6 + version: 0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: + specifier: 19.1.0 + version: 19.1.0 + react-dom: + specifier: 19.1.0 + version: 19.1.0(react@19.1.0) + recharts: + specifier: ^3.3.0 + version: 3.3.0(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react-is@19.2.0)(react@19.1.0)(redux@5.0.1) + tailwind-merge: + specifier: ^3.3.1 + version: 3.3.1 + devDependencies: + '@biomejs/biome': + specifier: 2.2.0 + version: 2.2.0 + '@tailwindcss/postcss': + specifier: ^4 + version: 4.1.15 + '@types/node': + specifier: ^20 + version: 20.19.23 + '@types/react': + specifier: ^19 + version: 19.2.2 + '@types/react-dom': + specifier: ^19 + version: 19.2.2(@types/react@19.2.2) + tailwindcss: + specifier: ^4 + version: 4.1.15 + tw-animate-css: + specifier: ^1.3.7 + version: 1.4.0 + typescript: + specifier: ^5 + version: 5.9.3 + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@biomejs/biome@2.2.0': + resolution: {integrity: sha512-3On3RSYLsX+n9KnoSgfoYlckYBoU6VRM22cw1gB4Y0OuUVSYd/O/2saOJMrA4HFfA1Ff0eacOvMN1yAAvHtzIw==} + engines: {node: '>=14.21.3'} + hasBin: true + + '@biomejs/cli-darwin-arm64@2.2.0': + resolution: {integrity: sha512-zKbwUUh+9uFmWfS8IFxmVD6XwqFcENjZvEyfOxHs1epjdH3wyyMQG80FGDsmauPwS2r5kXdEM0v/+dTIA9FXAg==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [darwin] + + '@biomejs/cli-darwin-x64@2.2.0': + resolution: {integrity: sha512-+OmT4dsX2eTfhD5crUOPw3RPhaR+SKVspvGVmSdZ9y9O/AgL8pla6T4hOn1q+VAFBHuHhsdxDRJgFCSC7RaMOw==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [darwin] + + '@biomejs/cli-linux-arm64-musl@2.2.0': + resolution: {integrity: sha512-egKpOa+4FL9YO+SMUMLUvf543cprjevNc3CAgDNFLcjknuNMcZ0GLJYa3EGTCR2xIkIUJDVneBV3O9OcIlCEZQ==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-arm64@2.2.0': + resolution: {integrity: sha512-6eoRdF2yW5FnW9Lpeivh7Mayhq0KDdaDMYOJnH9aT02KuSIX5V1HmWJCQQPwIQbhDh68Zrcpl8inRlTEan0SXw==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [linux] + + '@biomejs/cli-linux-x64-musl@2.2.0': + resolution: {integrity: sha512-I5J85yWwUWpgJyC1CcytNSGusu2p9HjDnOPAFG4Y515hwRD0jpR9sT9/T1cKHtuCvEQ/sBvx+6zhz9l9wEJGAg==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-linux-x64@2.2.0': + resolution: {integrity: sha512-5UmQx/OZAfJfi25zAnAGHUMuOd+LOsliIt119x2soA2gLggQYrVPA+2kMUxR6Mw5M1deUF/AWWP2qpxgH7Nyfw==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [linux] + + '@biomejs/cli-win32-arm64@2.2.0': + resolution: {integrity: sha512-n9a1/f2CwIDmNMNkFs+JI0ZjFnMO0jdOyGNtihgUNFnlmd84yIYY2KMTBmMV58ZlVHjgmY5Y6E1hVTnSRieggA==} + engines: {node: '>=14.21.3'} + cpu: [arm64] + os: [win32] + + '@biomejs/cli-win32-x64@2.2.0': + resolution: {integrity: sha512-Nawu5nHjP/zPKTIryh2AavzTc/KEg4um/MxWdXW0A6P/RZOyIpa7+QSjeXwAwX/utJGaCoXRPWtF3m5U/bB3Ww==} + engines: {node: '>=14.21.3'} + cpu: [x64] + os: [win32] + + '@emnapi/runtime@1.6.0': + resolution: {integrity: sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA==} + + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + + '@floating-ui/react-dom@2.1.6': + resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + + '@formatjs/ecma402-abstract@2.3.6': + resolution: {integrity: sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==} + + '@formatjs/fast-memoize@2.2.7': + resolution: {integrity: sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==} + + '@formatjs/icu-messageformat-parser@2.11.4': + resolution: {integrity: sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==} + + '@formatjs/icu-skeleton-parser@1.8.16': + resolution: {integrity: sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==} + + '@formatjs/intl-localematcher@0.5.10': + resolution: {integrity: sha512-af3qATX+m4Rnd9+wHcjJ4w2ijq+rAVP3CCinJQvFv1kgSu1W6jypUmvleJxcewdxmutM8dmIRZFxO/IQBZmP2Q==} + + '@formatjs/intl-localematcher@0.6.2': + resolution: {integrity: sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==} + + '@img/colour@1.0.0': + resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.4': + resolution: {integrity: sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.4': + resolution: {integrity: sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.3': + resolution: {integrity: sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.3': + resolution: {integrity: sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.3': + resolution: {integrity: sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.2.3': + resolution: {integrity: sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.2.3': + resolution: {integrity: sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.3': + resolution: {integrity: sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.2.3': + resolution: {integrity: sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.3': + resolution: {integrity: sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.2.3': + resolution: {integrity: sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.4': + resolution: {integrity: sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.34.4': + resolution: {integrity: sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.34.4': + resolution: {integrity: sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.4': + resolution: {integrity: sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.34.4': + resolution: {integrity: sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.4': + resolution: {integrity: sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.4': + resolution: {integrity: sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.34.4': + resolution: {integrity: sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.4': + resolution: {integrity: sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.4': + resolution: {integrity: sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.4': + resolution: {integrity: sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@next/env@15.5.0': + resolution: {integrity: sha512-sDaprBAfzCQiOgo2pO+LhnV0Wt2wBgartjrr+dpcTORYVnnXD0gwhHhiiyIih9hQbq+JnbqH4odgcFWhqCGidw==} + + '@next/swc-darwin-arm64@15.5.0': + resolution: {integrity: sha512-v7Jj9iqC6enxIRBIScD/o0lH7QKvSxq2LM8UTyqJi+S2w2QzhMYjven4vgu/RzgsdtdbpkyCxBTzHl/gN5rTRg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@15.5.0': + resolution: {integrity: sha512-s2Nk6ec+pmYmAb/utawuURy7uvyYKDk+TRE5aqLRsdnj3AhwC9IKUBmhfnLmY/+P+DnwqpeXEFIKe9tlG0p6CA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@15.5.0': + resolution: {integrity: sha512-mGlPJMZReU4yP5fSHjOxiTYvZmwPSWn/eF/dcg21pwfmiUCKS1amFvf1F1RkLHPIMPfocxLViNWFvkvDB14Isg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-arm64-musl@15.5.0': + resolution: {integrity: sha512-biWqIOE17OW/6S34t1X8K/3vb1+svp5ji5QQT/IKR+VfM3B7GvlCwmz5XtlEan2ukOUf9tj2vJJBffaGH4fGRw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-x64-gnu@15.5.0': + resolution: {integrity: sha512-zPisT+obYypM/l6EZ0yRkK3LEuoZqHaSoYKj+5jiD9ESHwdr6QhnabnNxYkdy34uCigNlWIaCbjFmQ8FY5AlxA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-linux-x64-musl@15.5.0': + resolution: {integrity: sha512-+t3+7GoU9IYmk+N+FHKBNFdahaReoAktdOpXHFIPOU1ixxtdge26NgQEEkJkCw2dHT9UwwK5zw4mAsURw4E8jA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-win32-arm64-msvc@15.5.0': + resolution: {integrity: sha512-d8MrXKh0A+c9DLiy1BUFwtg3Hu90Lucj3k6iKTUdPOv42Ve2UiIG8HYi3UAb8kFVluXxEfdpCoPPCSODk5fDcw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@15.5.0': + resolution: {integrity: sha512-Fe1tGHxOWEyQjmygWkkXSwhFcTJuimrNu52JEuwItrKJVV4iRjbWp9I7zZjwqtiNnQmxoEvoisn8wueFLrNpvQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@radix-ui/primitive@1.1.3': + resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} + + '@radix-ui/react-accordion@1.2.12': + resolution: {integrity: sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==} + 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 + + '@radix-ui/react-arrow@1.1.7': + resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} + 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 + + '@radix-ui/react-avatar@1.1.10': + resolution: {integrity: sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==} + 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 + + '@radix-ui/react-collapsible@1.1.12': + resolution: {integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==} + 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 + + '@radix-ui/react-collection@1.1.7': + resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} + 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 + + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dialog@1.1.15': + resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} + 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 + + '@radix-ui/react-direction@1.1.1': + resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.11': + resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} + 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 + + '@radix-ui/react-dropdown-menu@2.1.16': + resolution: {integrity: sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==} + 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 + + '@radix-ui/react-focus-guards@1.1.3': + resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.7': + resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} + 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 + + '@radix-ui/react-icons@1.3.2': + resolution: {integrity: sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==} + peerDependencies: + react: ^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc + + '@radix-ui/react-id@1.1.1': + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-menu@2.1.16': + resolution: {integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==} + 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 + + '@radix-ui/react-navigation-menu@1.2.14': + resolution: {integrity: sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==} + 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 + + '@radix-ui/react-popper@1.2.8': + resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} + 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 + + '@radix-ui/react-portal@1.1.9': + resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} + 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 + + '@radix-ui/react-presence@1.1.5': + resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} + 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 + + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + 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 + + '@radix-ui/react-roving-focus@1.1.11': + resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} + 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 + + '@radix-ui/react-separator@1.1.7': + resolution: {integrity: sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==} + 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 + + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-tabs@1.1.13': + resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==} + 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 + + '@radix-ui/react-use-callback-ref@1.1.1': + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.1': + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-is-hydrated@0.1.0': + resolution: {integrity: sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-previous@1.1.1': + resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.1': + resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.1': + resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.2.3': + resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} + 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 + + '@radix-ui/rect@1.1.1': + resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + + '@reduxjs/toolkit@2.9.2': + resolution: {integrity: sha512-ZAYu/NXkl/OhqTz7rfPaAhY0+e8Fr15jqNxte/2exKUxvHyQ/hcqmdekiN1f+Lcw3pE+34FCgX+26zcUE3duCg==} + peerDependencies: + react: ^16.9.0 || ^17.0.0 || ^18 || ^19 + react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 + peerDependenciesMeta: + react: + optional: true + react-redux: + optional: true + + '@schummar/icu-type-parser@1.21.5': + resolution: {integrity: sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==} + + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + + '@tailwindcss/node@4.1.15': + resolution: {integrity: sha512-HF4+7QxATZWY3Jr8OlZrBSXmwT3Watj0OogeDvdUY/ByXJHQ+LBtqA2brDb3sBxYslIFx6UP94BJ4X6a4L9Bmw==} + + '@tailwindcss/oxide-android-arm64@4.1.15': + resolution: {integrity: sha512-TkUkUgAw8At4cBjCeVCRMc/guVLKOU1D+sBPrHt5uVcGhlbVKxrCaCW9OKUIBv1oWkjh4GbunD/u/Mf0ql6kEA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.15': + resolution: {integrity: sha512-xt5XEJpn2piMSfvd1UFN6jrWXyaKCwikP4Pidcf+yfHTSzSpYhG3dcMktjNkQO3JiLCp+0bG0HoWGvz97K162w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.15': + resolution: {integrity: sha512-TnWaxP6Bx2CojZEXAV2M01Yl13nYPpp0EtGpUrY+LMciKfIXiLL2r/SiSRpagE5Fp2gX+rflp/Os1VJDAyqymg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.15': + resolution: {integrity: sha512-quISQDWqiB6Cqhjc3iWptXVZHNVENsWoI77L1qgGEHNIdLDLFnw3/AfY7DidAiiCIkGX/MjIdB3bbBZR/G2aJg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.15': + resolution: {integrity: sha512-ObG76+vPlab65xzVUQbExmDU9FIeYLQ5k2LrQdR2Ud6hboR+ZobXpDoKEYXf/uOezOfIYmy2Ta3w0ejkTg9yxg==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.15': + resolution: {integrity: sha512-4WbBacRmk43pkb8/xts3wnOZMDKsPFyEH/oisCm2q3aLZND25ufvJKcDUpAu0cS+CBOL05dYa8D4U5OWECuH/Q==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.15': + resolution: {integrity: sha512-AbvmEiteEj1nf42nE8skdHv73NoR+EwXVSgPY6l39X12Ex8pzOwwfi3Kc8GAmjsnsaDEbk+aj9NyL3UeyHcTLg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.15': + resolution: {integrity: sha512-+rzMVlvVgrXtFiS+ES78yWgKqpThgV19ISKD58Ck+YO5pO5KjyxLt7AWKsWMbY0R9yBDC82w6QVGz837AKQcHg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.15': + resolution: {integrity: sha512-fPdEy7a8eQN9qOIK3Em9D3TO1z41JScJn8yxl/76mp4sAXFDfV4YXxsiptJcOwy6bGR+70ZSwFIZhTXzQeqwQg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.15': + resolution: {integrity: sha512-sJ4yd6iXXdlgIMfIBXuVGp/NvmviEoMVWMOAGxtxhzLPp9LOj5k0pMEMZdjeMCl4C6Up+RM8T3Zgk+BMQ0bGcQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.15': + resolution: {integrity: sha512-sJGE5faXnNQ1iXeqmRin7Ds/ru2fgCiaQZQQz3ZGIDtvbkeV85rAZ0QJFMDg0FrqsffZG96H1U9AQlNBRLsHVg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.15': + resolution: {integrity: sha512-NLeHE7jUV6HcFKS504bpOohyi01zPXi2PXmjFfkzTph8xRxDdxkRsXm/xDO5uV5K3brrE1cCwbUYmFUSHR3u1w==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.15': + resolution: {integrity: sha512-krhX+UOOgnsUuks2SR7hFafXmLQrKxB4YyRTERuCE59JlYL+FawgaAlSkOYmDRJdf1Q+IFNDMl9iRnBW7QBDfQ==} + engines: {node: '>= 10'} + + '@tailwindcss/postcss@4.1.15': + resolution: {integrity: sha512-IZh8IT76KujRz6d15wZw4eoeViT4TqmzVWNNfpuNCTKiaZUwgr5vtPqO4HjuYDyx3MgGR5qgPt1HMzTeLJyA3g==} + + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-shape@3.1.7': + resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + + '@types/node@20.19.23': + resolution: {integrity: sha512-yIdlVVVHXpmqRhtyovZAcSy0MiPcYWGkoO4CGe/+jpP0hmNuihm4XhHbADpK++MsiLHP5MVlv+bcgdF99kSiFQ==} + + '@types/react-dom@19.2.2': + resolution: {integrity: sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.2': + resolution: {integrity: sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==} + + '@types/use-sync-external-store@0.0.6': + resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} + + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + + caniuse-lite@1.0.30001751: + resolution: {integrity: sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==} + + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + cobe@0.6.5: + resolution: {integrity: sha512-MA8bu81EFY6JjQpj+FovEuhyJ25khx2Q7Lh+ot/UkCJe5yKyDgzdc6u2lGZIOmsZTXK6Itg1i4lQZIJZbPWnAg==} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-format@3.1.0: + resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + decimal.js-light@2.5.1: + resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + + decimal.js@10.6.0: + resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + engines: {node: '>=10.13.0'} + + es-toolkit@1.40.0: + resolution: {integrity: sha512-8o6w0KFmU0CiIl0/Q/BCEOabF2IJaELM1T2PWj6e8KqzHv1gdx+7JtFnDwOx1kJH/isJ5NwlDG1nCr1HrRF94Q==} + + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + framer-motion@12.23.24: + resolution: {integrity: sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==} + 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 + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + immer@10.1.3: + resolution: {integrity: sha512-tmjF/k8QDKydUlm3mZU+tjM6zeq9/fFpPqH9SzWmBnVVKsPBg/V66qsMwb3/Bo90cgUN+ghdVBess+hPsxUyRw==} + + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + + intl-messageformat@10.7.18: + resolution: {integrity: sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==} + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + lightningcss-android-arm64@1.30.2: + resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.30.2: + resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.2: + resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.2: + resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.2: + resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.2: + resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.2: + resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.2: + resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.2: + resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.2: + resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.2: + resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.2: + resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} + engines: {node: '>= 12.0.0'} + + lucide-react@0.540.0: + resolution: {integrity: sha512-armkCAqQvO62EIX4Hq7hqX/q11WSZu0Jd23cnnqx0/49yIxGXyL/zyZfBxNN9YDx0ensPTb4L+DjTh3yQXUxtQ==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + magic-string@0.30.19: + resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==} + + motion-dom@12.23.23: + resolution: {integrity: sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==} + + motion-utils@12.23.6: + resolution: {integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==} + + motion@12.23.24: + resolution: {integrity: sha512-Rc5E7oe2YZ72N//S3QXGzbnXgqNrTESv8KKxABR20q2FLch9gHLo0JLyYo2hZ238bZ9Gx6cWhj9VO0IgwbMjCw==} + 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 + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + negotiator@1.0.0: + resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==} + engines: {node: '>= 0.6'} + + next-intl@4.4.0: + resolution: {integrity: sha512-QHqnP9V9Pe7Tn0PdVQ7u1Z8k9yCkW5SJKeRy2g5gxzhSt/C01y3B9qNxuj3Fsmup/yreIHe6osxU6sFa+9WIkQ==} + peerDependencies: + next: ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0 + typescript: ^5.0.0 + peerDependenciesMeta: + typescript: + optional: true + + next-themes@0.4.6: + resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==} + peerDependencies: + react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc + + next@15.5.0: + resolution: {integrity: sha512-N1lp9Hatw3a9XLt0307lGB4uTKsXDhyOKQo7uYMzX4i0nF/c27grcGXkLdb7VcT8QPYLBa8ouIyEoUQJ2OyeNQ==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + + phenomenon@1.6.0: + resolution: {integrity: sha512-7h9/fjPD3qNlgggzm88cY58l9sudZ6Ey+UmZsizfhtawO6E3srZQXywaNm2lBwT72TbpHYRPy7ytIHeBUD/G0A==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + react-dom@19.1.0: + resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} + peerDependencies: + react: ^19.1.0 + + react-is@19.2.0: + resolution: {integrity: sha512-x3Ax3kNSMIIkyVYhWPyO09bu0uttcAIoecO/um/rKGQ4EltYWVYtyiGkS/3xMynrbVQdS69Jhlv8FXUEZehlzA==} + + react-redux@9.2.0: + resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==} + peerDependencies: + '@types/react': ^18.2.25 || ^19 + react: ^18.0 || ^19 + redux: ^5.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + redux: + optional: true + + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.1: + resolution: {integrity: sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==} + 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 + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + 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 + + react@19.1.0: + resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} + engines: {node: '>=0.10.0'} + + recharts@3.3.0: + resolution: {integrity: sha512-Vi0qmTB0iz1+/Cz9o5B7irVyUjX2ynvEgImbgMt/3sKRREcUM07QiYjS1QpAVrkmVlXqy5gykq4nGWMz9AS4Rg==} + engines: {node: '>=18'} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-is: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + redux-thunk@3.1.0: + resolution: {integrity: sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==} + peerDependencies: + redux: ^5.0.0 + + redux@5.0.1: + resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} + + reselect@5.1.1: + resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + + scheduler@0.26.0: + resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + sharp@0.34.4: + resolution: {integrity: sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + + tailwind-merge@3.3.1: + resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==} + + tailwindcss@4.1.15: + resolution: {integrity: sha512-k2WLnWkYFkdpRv+Oby3EBXIyQC8/s1HOFMBUViwtAh6Z5uAozeUSMQlIsn/c6Q2iJzqG6aJT3wdPaRNj70iYxQ==} + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tw-animate-css@1.4.0: + resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + 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 + + use-intl@4.4.0: + resolution: {integrity: sha512-smFekJWtokDRBLC5/ZumlBREzdXOkw06+56Ifj2uRe9266Mk+yWQm2PcJO+EwlOE5sHIXHixOTzN6V8E0RGUbw==} + peerDependencies: + react: ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0 + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + 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 + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + victory-vendor@37.3.6: + resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==} + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@biomejs/biome@2.2.0': + optionalDependencies: + '@biomejs/cli-darwin-arm64': 2.2.0 + '@biomejs/cli-darwin-x64': 2.2.0 + '@biomejs/cli-linux-arm64': 2.2.0 + '@biomejs/cli-linux-arm64-musl': 2.2.0 + '@biomejs/cli-linux-x64': 2.2.0 + '@biomejs/cli-linux-x64-musl': 2.2.0 + '@biomejs/cli-win32-arm64': 2.2.0 + '@biomejs/cli-win32-x64': 2.2.0 + + '@biomejs/cli-darwin-arm64@2.2.0': + optional: true + + '@biomejs/cli-darwin-x64@2.2.0': + optional: true + + '@biomejs/cli-linux-arm64-musl@2.2.0': + optional: true + + '@biomejs/cli-linux-arm64@2.2.0': + optional: true + + '@biomejs/cli-linux-x64-musl@2.2.0': + optional: true + + '@biomejs/cli-linux-x64@2.2.0': + optional: true + + '@biomejs/cli-win32-arm64@2.2.0': + optional: true + + '@biomejs/cli-win32-x64@2.2.0': + optional: true + + '@emnapi/runtime@1.6.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@floating-ui/core@1.7.3': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.7.4': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/react-dom@2.1.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@floating-ui/dom': 1.7.4 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + + '@floating-ui/utils@0.2.10': {} + + '@formatjs/ecma402-abstract@2.3.6': + dependencies: + '@formatjs/fast-memoize': 2.2.7 + '@formatjs/intl-localematcher': 0.6.2 + decimal.js: 10.6.0 + tslib: 2.8.1 + + '@formatjs/fast-memoize@2.2.7': + dependencies: + tslib: 2.8.1 + + '@formatjs/icu-messageformat-parser@2.11.4': + dependencies: + '@formatjs/ecma402-abstract': 2.3.6 + '@formatjs/icu-skeleton-parser': 1.8.16 + tslib: 2.8.1 + + '@formatjs/icu-skeleton-parser@1.8.16': + dependencies: + '@formatjs/ecma402-abstract': 2.3.6 + tslib: 2.8.1 + + '@formatjs/intl-localematcher@0.5.10': + dependencies: + tslib: 2.8.1 + + '@formatjs/intl-localematcher@0.6.2': + dependencies: + tslib: 2.8.1 + + '@img/colour@1.0.0': + optional: true + + '@img/sharp-darwin-arm64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.3 + optional: true + + '@img/sharp-darwin-x64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.3 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.3': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.3': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.3': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.3': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.3': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.3': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.3': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.3': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.3': + optional: true + + '@img/sharp-linux-arm64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.3 + optional: true + + '@img/sharp-linux-arm@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.3 + optional: true + + '@img/sharp-linux-ppc64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.3 + optional: true + + '@img/sharp-linux-s390x@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.3 + optional: true + + '@img/sharp-linux-x64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.3 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.3 + optional: true + + '@img/sharp-wasm32@0.34.4': + dependencies: + '@emnapi/runtime': 1.6.0 + optional: true + + '@img/sharp-win32-arm64@0.34.4': + optional: true + + '@img/sharp-win32-ia32@0.34.4': + optional: true + + '@img/sharp-win32-x64@0.34.4': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@next/env@15.5.0': {} + + '@next/swc-darwin-arm64@15.5.0': + optional: true + + '@next/swc-darwin-x64@15.5.0': + optional: true + + '@next/swc-linux-arm64-gnu@15.5.0': + optional: true + + '@next/swc-linux-arm64-musl@15.5.0': + optional: true + + '@next/swc-linux-x64-gnu@15.5.0': + optional: true + + '@next/swc-linux-x64-musl@15.5.0': + optional: true + + '@next/swc-win32-arm64-msvc@15.5.0': + optional: true + + '@next/swc-win32-x64-msvc@15.5.0': + optional: true + + '@radix-ui/primitive@1.1.3': {} + + '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-avatar@1.1.10(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.2)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-context@1.1.2(@types/react@19.2.2)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.1.0) + aria-hidden: 1.2.6 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-remove-scroll: 2.7.1(@types/react@19.2.2)(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-direction@1.1.1(@types/react@19.2.2)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.2)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.2)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-icons@1.3.2(react@19.1.0)': + dependencies: + react: 19.1.0 + + '@radix-ui/react-id@1.1.1(@types/react@19.2.2)(react@19.1.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.1.0) + aria-hidden: 1.2.6 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-remove-scroll: 2.7.1(@types/react@19.2.2)(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-navigation-menu@1.2.14(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@floating-ui/react-dom': 2.1.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/rect': 1.1.1 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-separator@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-slot@1.2.3(@types/react@19.2.2)(react@19.1.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-direction': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.2)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.2)(react@19.1.0)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.2)(react@19.1.0) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.2)(react@19.1.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.2)(react@19.1.0)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.2)(react@19.1.0)': + dependencies: + react: 19.1.0 + use-sync-external-store: 1.6.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.2)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.2)(react@19.1.0)': + dependencies: + react: 19.1.0 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.2)(react@19.1.0)': + dependencies: + '@radix-ui/rect': 1.1.1 + react: 19.1.0 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-use-size@1.1.1(@types/react@19.2.2)(react@19.1.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/rect@1.1.1': {} + + '@reduxjs/toolkit@2.9.2(react-redux@9.2.0(@types/react@19.2.2)(react@19.1.0)(redux@5.0.1))(react@19.1.0)': + dependencies: + '@standard-schema/spec': 1.0.0 + '@standard-schema/utils': 0.3.0 + immer: 10.1.3 + redux: 5.0.1 + redux-thunk: 3.1.0(redux@5.0.1) + reselect: 5.1.1 + optionalDependencies: + react: 19.1.0 + react-redux: 9.2.0(@types/react@19.2.2)(react@19.1.0)(redux@5.0.1) + + '@schummar/icu-type-parser@1.21.5': {} + + '@standard-schema/spec@1.0.0': {} + + '@standard-schema/utils@0.3.0': {} + + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 + + '@tailwindcss/node@4.1.15': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.18.3 + jiti: 2.6.1 + lightningcss: 1.30.2 + magic-string: 0.30.19 + source-map-js: 1.2.1 + tailwindcss: 4.1.15 + + '@tailwindcss/oxide-android-arm64@4.1.15': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.15': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.15': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.15': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.15': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.15': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.15': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.15': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.15': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.15': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.15': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.15': + optional: true + + '@tailwindcss/oxide@4.1.15': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.15 + '@tailwindcss/oxide-darwin-arm64': 4.1.15 + '@tailwindcss/oxide-darwin-x64': 4.1.15 + '@tailwindcss/oxide-freebsd-x64': 4.1.15 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.15 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.15 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.15 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.15 + '@tailwindcss/oxide-linux-x64-musl': 4.1.15 + '@tailwindcss/oxide-wasm32-wasi': 4.1.15 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.15 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.15 + + '@tailwindcss/postcss@4.1.15': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.1.15 + '@tailwindcss/oxide': 4.1.15 + postcss: 8.5.6 + tailwindcss: 4.1.15 + + '@types/d3-array@3.2.2': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-shape@3.1.7': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + + '@types/node@20.19.23': + dependencies: + undici-types: 6.21.0 + + '@types/react-dom@19.2.2(@types/react@19.2.2)': + dependencies: + '@types/react': 19.2.2 + + '@types/react@19.2.2': + dependencies: + csstype: 3.1.3 + + '@types/use-sync-external-store@0.0.6': {} + + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + + caniuse-lite@1.0.30001751: {} + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + client-only@0.0.1: {} + + clsx@2.1.1: {} + + cobe@0.6.5: + dependencies: + phenomenon: 1.6.0 + + csstype@3.1.3: {} + + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-color@3.1.0: {} + + d3-ease@3.0.1: {} + + d3-format@3.1.0: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@3.1.0: {} + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + decimal.js-light@2.5.1: {} + + decimal.js@10.6.0: {} + + detect-libc@2.1.2: {} + + detect-node-es@1.1.0: {} + + enhanced-resolve@5.18.3: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + + es-toolkit@1.40.0: {} + + eventemitter3@5.0.1: {} + + framer-motion@12.23.24(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + motion-dom: 12.23.23 + motion-utils: 12.23.6 + tslib: 2.8.1 + optionalDependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + + get-nonce@1.0.1: {} + + graceful-fs@4.2.11: {} + + immer@10.1.3: {} + + internmap@2.0.3: {} + + intl-messageformat@10.7.18: + dependencies: + '@formatjs/ecma402-abstract': 2.3.6 + '@formatjs/fast-memoize': 2.2.7 + '@formatjs/icu-messageformat-parser': 2.11.4 + tslib: 2.8.1 + + jiti@2.6.1: {} + + lightningcss-android-arm64@1.30.2: + optional: true + + lightningcss-darwin-arm64@1.30.2: + optional: true + + lightningcss-darwin-x64@1.30.2: + optional: true + + lightningcss-freebsd-x64@1.30.2: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.2: + optional: true + + lightningcss-linux-arm64-gnu@1.30.2: + optional: true + + lightningcss-linux-arm64-musl@1.30.2: + optional: true + + lightningcss-linux-x64-gnu@1.30.2: + optional: true + + lightningcss-linux-x64-musl@1.30.2: + optional: true + + lightningcss-win32-arm64-msvc@1.30.2: + optional: true + + lightningcss-win32-x64-msvc@1.30.2: + optional: true + + lightningcss@1.30.2: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.30.2 + lightningcss-darwin-arm64: 1.30.2 + lightningcss-darwin-x64: 1.30.2 + lightningcss-freebsd-x64: 1.30.2 + lightningcss-linux-arm-gnueabihf: 1.30.2 + lightningcss-linux-arm64-gnu: 1.30.2 + lightningcss-linux-arm64-musl: 1.30.2 + lightningcss-linux-x64-gnu: 1.30.2 + lightningcss-linux-x64-musl: 1.30.2 + lightningcss-win32-arm64-msvc: 1.30.2 + lightningcss-win32-x64-msvc: 1.30.2 + + lucide-react@0.540.0(react@19.1.0): + dependencies: + react: 19.1.0 + + magic-string@0.30.19: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + motion-dom@12.23.23: + dependencies: + motion-utils: 12.23.6 + + motion-utils@12.23.6: {} + + motion@12.23.24(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + framer-motion: 12.23.24(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + tslib: 2.8.1 + optionalDependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + + nanoid@3.3.11: {} + + negotiator@1.0.0: {} + + next-intl@4.4.0(next@15.5.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(react@19.1.0)(typescript@5.9.3): + dependencies: + '@formatjs/intl-localematcher': 0.5.10 + negotiator: 1.0.0 + next: 15.5.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + use-intl: 4.4.0(react@19.1.0) + optionalDependencies: + typescript: 5.9.3 + + next-themes@0.4.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + + next@15.5.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + '@next/env': 15.5.0 + '@swc/helpers': 0.5.15 + caniuse-lite: 1.0.30001751 + postcss: 8.4.31 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + styled-jsx: 5.1.6(react@19.1.0) + optionalDependencies: + '@next/swc-darwin-arm64': 15.5.0 + '@next/swc-darwin-x64': 15.5.0 + '@next/swc-linux-arm64-gnu': 15.5.0 + '@next/swc-linux-arm64-musl': 15.5.0 + '@next/swc-linux-x64-gnu': 15.5.0 + '@next/swc-linux-x64-musl': 15.5.0 + '@next/swc-win32-arm64-msvc': 15.5.0 + '@next/swc-win32-x64-msvc': 15.5.0 + sharp: 0.34.4 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + + phenomenon@1.6.0: {} + + picocolors@1.1.1: {} + + postcss@8.4.31: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + react-dom@19.1.0(react@19.1.0): + dependencies: + react: 19.1.0 + scheduler: 0.26.0 + + react-is@19.2.0: {} + + react-redux@9.2.0(@types/react@19.2.2)(react@19.1.0)(redux@5.0.1): + dependencies: + '@types/use-sync-external-store': 0.0.6 + react: 19.1.0 + use-sync-external-store: 1.6.0(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + redux: 5.0.1 + + react-remove-scroll-bar@2.3.8(@types/react@19.2.2)(react@19.1.0): + dependencies: + react: 19.1.0 + react-style-singleton: 2.2.3(@types/react@19.2.2)(react@19.1.0) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.2 + + react-remove-scroll@2.7.1(@types/react@19.2.2)(react@19.1.0): + dependencies: + react: 19.1.0 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.2)(react@19.1.0) + react-style-singleton: 2.2.3(@types/react@19.2.2)(react@19.1.0) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.2.2)(react@19.1.0) + use-sidecar: 1.1.3(@types/react@19.2.2)(react@19.1.0) + optionalDependencies: + '@types/react': 19.2.2 + + react-style-singleton@2.2.3(@types/react@19.2.2)(react@19.1.0): + dependencies: + get-nonce: 1.0.1 + react: 19.1.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.2 + + react@19.1.0: {} + + recharts@3.3.0(@types/react@19.2.2)(react-dom@19.1.0(react@19.1.0))(react-is@19.2.0)(react@19.1.0)(redux@5.0.1): + dependencies: + '@reduxjs/toolkit': 2.9.2(react-redux@9.2.0(@types/react@19.2.2)(react@19.1.0)(redux@5.0.1))(react@19.1.0) + clsx: 2.1.1 + decimal.js-light: 2.5.1 + es-toolkit: 1.40.0 + eventemitter3: 5.0.1 + immer: 10.1.3 + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + react-is: 19.2.0 + react-redux: 9.2.0(@types/react@19.2.2)(react@19.1.0)(redux@5.0.1) + reselect: 5.1.1 + tiny-invariant: 1.3.3 + use-sync-external-store: 1.6.0(react@19.1.0) + victory-vendor: 37.3.6 + transitivePeerDependencies: + - '@types/react' + - redux + + redux-thunk@3.1.0(redux@5.0.1): + dependencies: + redux: 5.0.1 + + redux@5.0.1: {} + + reselect@5.1.1: {} + + scheduler@0.26.0: {} + + semver@7.7.3: + optional: true + + sharp@0.34.4: + dependencies: + '@img/colour': 1.0.0 + detect-libc: 2.1.2 + semver: 7.7.3 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.4 + '@img/sharp-darwin-x64': 0.34.4 + '@img/sharp-libvips-darwin-arm64': 1.2.3 + '@img/sharp-libvips-darwin-x64': 1.2.3 + '@img/sharp-libvips-linux-arm': 1.2.3 + '@img/sharp-libvips-linux-arm64': 1.2.3 + '@img/sharp-libvips-linux-ppc64': 1.2.3 + '@img/sharp-libvips-linux-s390x': 1.2.3 + '@img/sharp-libvips-linux-x64': 1.2.3 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 + '@img/sharp-libvips-linuxmusl-x64': 1.2.3 + '@img/sharp-linux-arm': 0.34.4 + '@img/sharp-linux-arm64': 0.34.4 + '@img/sharp-linux-ppc64': 0.34.4 + '@img/sharp-linux-s390x': 0.34.4 + '@img/sharp-linux-x64': 0.34.4 + '@img/sharp-linuxmusl-arm64': 0.34.4 + '@img/sharp-linuxmusl-x64': 0.34.4 + '@img/sharp-wasm32': 0.34.4 + '@img/sharp-win32-arm64': 0.34.4 + '@img/sharp-win32-ia32': 0.34.4 + '@img/sharp-win32-x64': 0.34.4 + optional: true + + source-map-js@1.2.1: {} + + styled-jsx@5.1.6(react@19.1.0): + dependencies: + client-only: 0.0.1 + react: 19.1.0 + + tailwind-merge@3.3.1: {} + + tailwindcss@4.1.15: {} + + tapable@2.3.0: {} + + tiny-invariant@1.3.3: {} + + tslib@2.8.1: {} + + tw-animate-css@1.4.0: {} + + typescript@5.9.3: {} + + undici-types@6.21.0: {} + + use-callback-ref@1.3.3(@types/react@19.2.2)(react@19.1.0): + dependencies: + react: 19.1.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.2 + + use-intl@4.4.0(react@19.1.0): + dependencies: + '@formatjs/fast-memoize': 2.2.7 + '@schummar/icu-type-parser': 1.21.5 + intl-messageformat: 10.7.18 + react: 19.1.0 + + use-sidecar@1.1.3(@types/react@19.2.2)(react@19.1.0): + dependencies: + detect-node-es: 1.1.0 + react: 19.1.0 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.2 + + use-sync-external-store@1.6.0(react@19.1.0): + dependencies: + react: 19.1.0 + + victory-vendor@37.3.6: + dependencies: + '@types/d3-array': 3.2.2 + '@types/d3-ease': 3.0.2 + '@types/d3-interpolate': 3.0.4 + '@types/d3-scale': 4.0.9 + '@types/d3-shape': 3.1.7 + '@types/d3-time': 3.0.4 + '@types/d3-timer': 3.0.2 + d3-array: 3.2.4 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-scale: 4.0.2 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-timer: 3.0.1 diff --git a/types/currency.ts b/types/currency.ts new file mode 100644 index 0000000..623ed3a --- /dev/null +++ b/types/currency.ts @@ -0,0 +1,39 @@ +export type Currency = "gp" | "tc"; + +export interface Price { + value: number; + currency: Currency; + tcEquivalent?: number; // 1 TC ≈ 35.000 gp +} + +export interface TibiaProtection { + fire?: number; + ice?: number; + energy?: number; + earth?: number; + death?: number; + holy?: number; +} + +export interface TibiaStats { + attack?: number; + defense?: number; + armor?: number; + weight?: number; + speed?: number; + manaRegen?: number; + hpRegen?: number; + skillBoost?: { + sword?: number; + axe?: number; + club?: number; + distance?: number; + shielding?: number; + fishing?: number; + }; +} + +export interface TibiaRequirements { + level?: number; + vocation?: "Knight" | "Paladin" | "Sorcerer" | "Druid" | "No Vocation"; +} diff --git a/types/item.ts b/types/item.ts new file mode 100644 index 0000000..c6fbf50 --- /dev/null +++ b/types/item.ts @@ -0,0 +1,48 @@ +import { TibiaStats, TibiaRequirements, TibiaProtection } from "./currency"; + +export interface TibiaItem { + id: string; + name: string; + image: string; + category: + | "weapon" + | "armor" + | "helmet" + | "legs" + | "boots" + | "shield" + | "amulet" + | "ring" + | "potion" + | "rune" + | "tool" + | "ammunition" + | "other"; + rarity: "common" | "uncommon" | "rare" | "epic" | "legendary"; + stats?: TibiaStats; + requirements?: TibiaRequirements; + protections?: TibiaProtection; + stackable?: boolean; + dropLocation?: string; + npcPrice?: number; + price: { + buy: number; + sell: number; + bid?: number; + }; + priceHistory: { + highest: number; + lowest: number; + average: number; + }; +} + +export interface MarketOffer { + id: string; + item: TibiaItem; + seller: string; + price: number; + quantity: number; + type: "buy" | "sell"; + timestamp: Date; +} diff --git a/void-extensions.txt b/void-extensions.txt new file mode 100644 index 0000000..ab8a94a --- /dev/null +++ b/void-extensions.txt @@ -0,0 +1,29 @@ +aaron-bond.better-comments +abians.prisma-generate-uml +alefragnani.project-manager +christian-kohler.path-intellisense +codezombiech.gitignore +dbaeumer.vscode-eslint +donjayamanne.githistory +eamodio.gitlens +ecmel.vscode-html-css +equinusocio.vsc-material-theme +equinusocio.vsc-material-theme-icons +esbenp.prettier-vscode +formulahendry.auto-rename-tag +github.remotehub +github.vscode-github-actions +github.vscode-pull-request-github +mhutchie.git-graph +miguelsolorio.symbols +ms-vscode-remote.remote-ssh +ms-vscode-remote.remote-ssh-edit +ms-vscode.azure-repos +ms-vscode.remote-explorer +ms-vscode.remote-repositories +ms-vscode.vscode-typescript-next +prisma.prisma +redhat.vscode-yaml +tal7aouy.rainbow-bracket +usernamehw.errorlens +wix.vscode-import-cost