feat/admin #2

Merged
admin merged 10 commits from feat/admin into main 2026-03-07 20:51:13 +00:00
18 changed files with 492 additions and 244 deletions
Showing only changes of commit 2dc8878db7 - Show all commits

18
.mcp.json Normal file
View File

@@ -0,0 +1,18 @@
{
"mcpServers": {
"shadcn": {
"command": "npx",
"args": [
"shadcn@latest",
"mcp"
]
},
"hugeicons": {
"command": "npx",
"args": [
"-y",
"@hugeicons/mcp-server"
]
}
}
}

View File

@@ -1,16 +1,16 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"style": "radix-nova",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "src/app/globals.css",
"css": "app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"iconLibrary": "hugeicons",
"rtl": false,
"aliases": {
"components": "@/components",
@@ -19,5 +19,7 @@
"lib": "@/lib",
"hooks": "@/hooks"
},
"menuColor": "default",
"menuAccent": "subtle",
"registries": {}
}

View File

@@ -1,6 +1,6 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
/// <reference path="./.next/types/routes.d.ts" />
import "./.next/dev/types/routes.d.ts";
// NOTE: This file should not be edited
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

View File

@@ -10,7 +10,10 @@
"type-check": "tsc --noEmit"
},
"dependencies": {
"@base-ui/react": "^1.2.0",
"@clerk/nextjs": "^6.38.2",
"@hugeicons/core-free-icons": "^3.3.0",
"@hugeicons/react": "^1.1.5",
"@repo/convex": "*",
"@repo/types": "*",
"@repo/utils": "*",

View File

@@ -1,6 +1,7 @@
import { AdminUserSync } from "../../components/auth/AdminUserSync";
import { AdminAuthGate } from "../../components/auth/AdminAuthGate";
import { AppSidebar } from "../../components/layout/sidebar/app-sidebar";
import { DynamicBreadcrumb } from "../../components/layout/DynamicBreadcrumb";
import {
SidebarInset,
SidebarProvider,
@@ -27,6 +28,7 @@ export default function DashboardLayout({
orientation="vertical"
className="mr-2 data-[orientation=vertical]:h-4"
/>
<DynamicBreadcrumb />
</div>
</header>
<main className="flex flex-1 flex-col gap-4 p-4">

View File

@@ -1,7 +1,14 @@
export default function DashboardPage() {
return (
<main>
<h1>Admin Dashboard</h1>
<div className="flex flex-1 flex-col gap-4 p-4 pt-0">
<div className="grid auto-rows-min gap-4 md:grid-cols-3">
<div className="aspect-video rounded-xl bg-muted/50" />
<div className="aspect-video rounded-xl bg-muted/50" />
<div className="aspect-video rounded-xl bg-muted/50" />
</div>
<div className="min-h-[100vh] flex-1 rounded-xl bg-muted/50 md:min-h-min lg:min-h-[100vh]" />
</div>
</main>
);
}

View File

@@ -5,6 +5,39 @@
@custom-variant dark (&:is(.dark *));
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-sans);
--font-mono: var(--font-geist-mono);
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar: var(--sidebar);
--color-chart-5: var(--chart-5);
--color-chart-4: var(--chart-4);
--color-chart-3: var(--chart-3);
--color-chart-2: var(--chart-2);
--color-chart-1: var(--chart-1);
--color-ring: var(--ring);
--color-input: var(--input);
--color-border: var(--border);
--color-destructive: var(--destructive);
--color-accent-foreground: var(--accent-foreground);
--color-accent: var(--accent);
--color-muted-foreground: var(--muted-foreground);
--color-muted: var(--muted);
--color-secondary-foreground: var(--secondary-foreground);
--color-secondary: var(--secondary);
--color-primary-foreground: var(--primary-foreground);
--color-primary: var(--primary);
--color-popover-foreground: var(--popover-foreground);
--color-popover: var(--popover);
--color-card-foreground: var(--card-foreground);
--color-card: var(--card);
--radius-sm: calc(var(--radius) - 4px);
--radius-md: calc(var(--radius) - 2px);
--radius-lg: var(--radius);
@@ -12,68 +45,37 @@
--radius-2xl: calc(var(--radius) + 8px);
--radius-3xl: calc(var(--radius) + 12px);
--radius-4xl: calc(var(--radius) + 16px);
--color-background: var(--background);
--color-foreground: var(--foreground);
--color-card: var(--card);
--color-card-foreground: var(--card-foreground);
--color-popover: var(--popover);
--color-popover-foreground: var(--popover-foreground);
--color-primary: var(--primary);
--color-primary-foreground: var(--primary-foreground);
--color-secondary: var(--secondary);
--color-secondary-foreground: var(--secondary-foreground);
--color-muted: var(--muted);
--color-muted-foreground: var(--muted-foreground);
--color-accent: var(--accent);
--color-accent-foreground: var(--accent-foreground);
--color-destructive: var(--destructive);
--color-border: var(--border);
--color-input: var(--input);
--color-ring: var(--ring);
--color-chart-1: var(--chart-1);
--color-chart-2: var(--chart-2);
--color-chart-3: var(--chart-3);
--color-chart-4: var(--chart-4);
--color-chart-5: var(--chart-5);
--color-sidebar: var(--sidebar);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-ring: var(--sidebar-ring);
}
: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);
--primary: oklch(0.60 0.10 185);
--primary-foreground: oklch(0.98 0.01 181);
--secondary: oklch(0.967 0.001 286.375);
--secondary-foreground: oklch(0.21 0.006 285.885);
--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);
--destructive: oklch(0.58 0.22 27);
--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);
--chart-1: oklch(0.85 0.13 181);
--chart-2: oklch(0.78 0.13 182);
--chart-3: oklch(0.70 0.12 183);
--chart-4: oklch(0.60 0.10 185);
--chart-5: oklch(0.51 0.09 186);
--radius: 0.45rem;
--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-primary: oklch(0.60 0.10 185);
--sidebar-primary-foreground: oklch(0.98 0.01 181);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
@@ -87,27 +89,27 @@
--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);
--primary: oklch(0.70 0.12 183);
--primary-foreground: oklch(0.28 0.04 193);
--secondary: oklch(0.274 0.006 286.033);
--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: oklch(0.371 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);
--chart-1: oklch(0.85 0.13 181);
--chart-2: oklch(0.78 0.13 182);
--chart-3: oklch(0.70 0.12 183);
--chart-4: oklch(0.60 0.10 185);
--chart-5: oklch(0.51 0.09 186);
--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-primary: oklch(0.78 0.13 182);
--sidebar-primary-foreground: oklch(0.28 0.04 193);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);

View File

@@ -1,10 +1,20 @@
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import { DM_Sans, Geist, Geist_Mono } from "next/font/google";
import { ClerkProvider } from "@clerk/nextjs";
import { ConvexClientProvider } from "@repo/convex";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
const dmSans = DM_Sans({subsets:['latin'],variable:'--font-sans'});
const geistSans = Geist({
variable: "--font-geist-sans",
subsets: ["latin"],
});
const geistMono = Geist_Mono({
variable: "--font-geist-mono",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: {
@@ -21,7 +31,7 @@ export default function RootLayout({
}) {
return (
<html lang="en">
<body className={inter.className}>
<body className={ dmSans.className}>
<ClerkProvider>
<ConvexClientProvider>
{children}

View File

@@ -6,8 +6,8 @@ export default function NotFound() {
<main className="flex flex-1 items-center justify-center bg-[var(--background)] px-4 py-16 md:px-6 md:py-24">
<div className="mx-auto flex w-full max-w-4xl flex-col items-center gap-8 md:flex-row md:gap-12">
<div className="flex w-full flex-col items-center text-center md:w-1/2 md:items-start md:text-left">
<h2 className="text-2xl font-bold tracking-tight md:text-3xl flex flex-wrap gap-x-3 gap-y-0 items-baseline">
<span className="font-medium">Page</span>
<h2 className="text-2xl font-black tracking-tight md:text-3xl flex flex-wrap gap-x-3 gap-y-0 items-baseline">
<span className="font-extralight">Page</span>
<span>not found</span>
</h2>

View File

@@ -0,0 +1,69 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { HugeiconsIcon } from "@hugeicons/react";
import { Home01Icon } from "@hugeicons/core-free-icons";
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb";
import { ROUTE_LABELS } from "@/lib/constants/app.constants";
export function DynamicBreadcrumb() {
const pathname = usePathname();
const segments = pathname.split("/").filter(Boolean);
// Root path — render "Dashboard" as a single page crumb
if (segments.length === 0) {
return (
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbPage>Dashboard</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
);
}
return (
<Breadcrumb>
<BreadcrumbList>
{/* Home icon link */}
<BreadcrumbItem>
<BreadcrumbLink asChild>
<Link href="/">
<HugeiconsIcon icon={Home01Icon} size={16} />
</Link>
</BreadcrumbLink>
</BreadcrumbItem>
{segments.map((segment, index) => {
const href = "/" + segments.slice(0, index + 1).join("/");
const label = ROUTE_LABELS[segment] ?? segment;
const isLast = index === segments.length - 1;
return (
<span key={href} className="contents">
<BreadcrumbSeparator />
<BreadcrumbItem>
{isLast ? (
<BreadcrumbPage>{label}</BreadcrumbPage>
) : (
<BreadcrumbLink asChild>
<Link href={href}>{label}</Link>
</BreadcrumbLink>
)}
</BreadcrumbItem>
</span>
);
})}
</BreadcrumbList>
</Breadcrumb>
);
}

View File

@@ -1,16 +1,8 @@
"use client";
import * as React from "react";
import {
LayoutDashboard,
ShoppingCart,
Package,
Users,
Tag,
Star,
Settings,
PawPrint,
} from "lucide-react";
import { HugeiconsIcon } from "@hugeicons/react";
import { Store01Icon } from "@hugeicons/core-free-icons";
import {
Sidebar,
SidebarContent,
@@ -23,19 +15,8 @@ import {
} from "@/components/ui/sidebar";
import { NavMain } from "./nav-main";
import { NavUser } from "./nav-user";
const navItems = [
{ title: "Dashboard", url: "/", icon: LayoutDashboard },
{ title: "Orders", url: "/orders", icon: ShoppingCart },
{ title: "Products", url: "/products", icon: Package },
{ title: "Customers", url: "/customers", icon: Users },
{ title: "Categories", url: "/categories", icon: Tag },
{ title: "Reviews", url: "/reviews", icon: Star },
];
const settingsItems = [
{ title: "Settings", url: "/settings", icon: Settings },
];
import { NAV_LINKS } from "@/lib/constants/app.constants";
import { PawPrint } from "lucide-react";
export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
return (
@@ -57,8 +38,14 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
</SidebarHeader>
<SidebarContent>
<NavMain items={navItems} />
<NavMain items={settingsItems} />
{/* dashboard */}
<NavMain overview={NAV_LINKS.overview} navMain={[]} isOverview={true} />
{/* Application */}
<NavMain navMain={NAV_LINKS.navMain} overview={[]} isOverview={false} />
{/* Users */}
<NavMain overview={NAV_LINKS.users} navMain={[]} isOverview={true} />
</SidebarContent>
<SidebarFooter>

View File

@@ -1,34 +1,54 @@
"use client";
import Link from "next/link";
import { type LucideIcon } from "lucide-react";
import { usePathname } from "next/navigation";
import { HugeiconsIcon, type IconSvgElement } from "@hugeicons/react";
import { ArrowRight01Icon } from "@hugeicons/core-free-icons";
import {
SidebarGroup,
SidebarGroupLabel,
SidebarMenu,
SidebarMenuButton,
SidebarMenuItem,
SidebarMenuSub,
SidebarMenuSubButton,
SidebarMenuSubItem,
} from "@/components/ui/sidebar";
import { CollapsibleTrigger, CollapsibleContent, Collapsible } from "@/components/ui/collapsible";
export function NavMain({
items,
overview,
isOverview,
navMain,
}: {
items: {
overview: {
title: string;
url: string;
icon: LucideIcon;
isActive?: boolean;
icon: IconSvgElement;
}[];
isOverview?: boolean;
navMain: {
title: string;
url: string;
icon: IconSvgElement;
items?: {
title: string;
url: string;
}[];
}[];
}) {
const pathname = usePathname();
if (isOverview) {
return (
<SidebarGroup>
<SidebarGroupLabel>Platform</SidebarGroupLabel>
<SidebarMenu>
{items.map((item) => (
{overview.map((item) => (
<SidebarMenuItem key={item.title}>
<SidebarMenuButton asChild tooltip={item.title} isActive={item.isActive}>
<SidebarMenuButton asChild tooltip={item.title} isActive={pathname === item.url}>
<Link href={item.url}>
<item.icon />
<HugeiconsIcon icon={item.icon} size={16} />
<span>{item.title}</span>
</Link>
</SidebarMenuButton>
@@ -38,3 +58,46 @@ export function NavMain({
</SidebarGroup>
);
}
return (
<SidebarGroup>
<SidebarGroupLabel>Application</SidebarGroupLabel>
<SidebarMenu>
{navMain.map((item) => {
const isGroupActive = pathname.startsWith(item.url);
return (
<Collapsible
key={item.title}
asChild
defaultOpen={isGroupActive}
className="group/collapsible"
>
<SidebarMenuItem>
<CollapsibleTrigger asChild>
<SidebarMenuButton tooltip={item.title} isActive={isGroupActive}>
<HugeiconsIcon icon={item.icon} size={16} />
<span>{item.title}</span>
<HugeiconsIcon icon={ArrowRight01Icon} className="ml-auto transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90" />
</SidebarMenuButton>
</CollapsibleTrigger>
<CollapsibleContent>
<SidebarMenuSub>
{item.items?.map((subItem) => (
<SidebarMenuSubItem key={subItem.title}>
<SidebarMenuSubButton asChild isActive={pathname === subItem.url}>
<Link href={subItem.url}>
<span>{subItem.title}</span>
</Link>
</SidebarMenuSubButton>
</SidebarMenuSubItem>
))}
</SidebarMenuSub>
</CollapsibleContent>
</SidebarMenuItem>
</Collapsible>
);
})}
</SidebarMenu>
</SidebarGroup>
);
}

View File

@@ -6,9 +6,9 @@ export default function StillBuildingPlaceholder() {
<main className="flex flex-1 items-center justify-center bg-[var(--background)] px-4 py-16 md:px-6 md:py-24">
<div className="mx-auto flex w-full max-w-4xl flex-col items-center gap-8 md:flex-row md:gap-12">
<div className="flex w-full flex-col items-center text-center md:w-1/2 md:items-start md:text-left">
<h2 className="text-2xl font-bold tracking-tight md:text-3xl flex flex-wrap gap-x-3 gap-y-0 items-baseline">
<span className="font-medium">Building</span>
<span>in progress</span>
<h2 className="text-2xl font-black tracking-tight md:text-3xl flex flex-wrap gap-x-3 gap-y-0 items-baseline">
<span className="font-extralight">Building</span>
<span>in Progress</span>
</h2>
<p className="mt-4 max-w-md text-base leading-relaxed md:text-lg">

View File

@@ -1,30 +1,53 @@
import { BookOpen, Bot, ShoppingCart, LayoutDashboard, Users } from "lucide-react";
import {
DashboardSquare02Icon,
ShoppingCart01Icon,
PackageIcon,
UserMultipleIcon,
} from "@hugeicons/core-free-icons";
export const ROUTE_LABELS: Record<string, string> = {
orders: "Orders",
products: "Products",
categories: "Categories",
images: "Images",
variants: "Variants",
customers: "Customers",
reviews: "Reviews",
messages: "Messages",
newsletter: "Newsletter",
users: "Users",
settings: "Settings",
returns: "Returns",
};
export const NAV_LINKS = {
overview: [
{
title: "Dashboard",
url: "/",
icon: LayoutDashboard,
icon: DashboardSquare02Icon,
},
],
navMain: [
{
title: "Sales",
url: "/orders",
icon: ShoppingCart,
isActive: true,
icon: ShoppingCart01Icon,
items: [
{
title: "Orders",
url: "/orders",
},
{
title: "Returns",
url: "/returns",
},
],
},
{
title: "Products",
url: "/products",
icon: Bot,
icon: PackageIcon,
items: [
{
title: "Categories",
@@ -32,7 +55,7 @@ export const NAV_LINKS = {
},
{
title: "Products",
url: "/products/products",
url: "/products",
},
{
title: "Images",
@@ -44,54 +67,31 @@ export const NAV_LINKS = {
},
],
},
{
title: "Documentation",
url: "#",
icon: BookOpen,
items: [
{
title: "Introduction",
url: "#",
},
{
title: "Get Started",
url: "#",
},
{
title: "Tutorials",
url: "#",
},
{
title: "Changelog",
url: "#",
},
],
},
{
title: "Customers",
url: "/customers",
icon: Users,
icon: UserMultipleIcon,
items: [
{
title: "Reviews",
url: "#",
url: "/customers/reviews",
},
{
title: "Messages",
url: "#",
url: "/customers/messages",
},
{
title: "Newsletter",
url: "#",
url: "/customers/newsletter",
},
],
},
],
sales: [
users: [
{
title: "Orders",
url: "/orders",
icon: ShoppingCart,
title: "Users",
url: "/users",
icon: UserMultipleIcon,
},
],
};

View File

@@ -12,10 +12,10 @@
"incremental": true,
"module": "esnext",
"esModuleInterop": true,
"moduleResolution": "node",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"jsx": "react-jsx",
"plugins": [
{
"name": "next"
@@ -23,14 +23,17 @@
],
"target": "ES2017",
"paths": {
"@/*": ["./src/*"]
"@/*": [
"./src/*"
]
}
},
"include": [
"next-env.d.ts",
".next/types/**/*.ts",
"**/*.ts",
"**/*.tsx"
"**/*.tsx",
".next/dev/types/**/*.ts"
],
"exclude": [
"node_modules"

170
package-lock.json generated
View File

@@ -15,7 +15,7 @@
"@clerk/backend": "^2.32.1",
"@tailwindcss/postcss": "^4.2.0",
"convex": "^1.32.0",
"next": "^15.3.2",
"next": "16.1.6",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"stripe": "^20.4.0",
@@ -45,7 +45,10 @@
"apps/admin": {
"version": "0.0.1",
"dependencies": {
"@base-ui/react": "^1.2.0",
"@clerk/nextjs": "^6.38.2",
"@hugeicons/core-free-icons": "^3.3.0",
"@hugeicons/react": "^1.1.5",
"@repo/convex": "*",
"@repo/types": "*",
"@repo/utils": "*",
@@ -609,7 +612,6 @@
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
"integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -663,6 +665,59 @@
"node": ">=6.9.0"
}
},
"node_modules/@base-ui/react": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@base-ui/react/-/react-1.2.0.tgz",
"integrity": "sha512-O6aEQHcm+QyGTFY28xuwRD3SEJGZOBDpyjN2WvpfWYFVhg+3zfXPysAILqtM0C1kWC82MccOE/v1j+GHXE4qIw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.28.6",
"@base-ui/utils": "0.2.5",
"@floating-ui/react-dom": "^2.1.6",
"@floating-ui/utils": "^0.2.10",
"tabbable": "^6.4.0",
"use-sync-external-store": "^1.6.0"
},
"engines": {
"node": ">=14.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mui-org"
},
"peerDependencies": {
"@types/react": "^17 || ^18 || ^19",
"react": "^17 || ^18 || ^19",
"react-dom": "^17 || ^18 || ^19"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@base-ui/utils": {
"version": "0.2.5",
"resolved": "https://registry.npmjs.org/@base-ui/utils/-/utils-0.2.5.tgz",
"integrity": "sha512-oYC7w0gp76RI5MxprlGLV0wze0SErZaRl3AAkeP3OnNB/UBMb6RqNf6ZSIlxOc9Qp68Ab3C2VOcJQyRs7Xc7Vw==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.28.6",
"@floating-ui/utils": "^0.2.10",
"reselect": "^5.1.1",
"use-sync-external-store": "^1.6.0"
},
"peerDependencies": {
"@types/react": "^17 || ^18 || ^19",
"react": "^17 || ^18 || ^19",
"react-dom": "^17 || ^18 || ^19"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@clerk/backend": {
"version": "2.32.1",
"resolved": "https://registry.npmjs.org/@clerk/backend/-/backend-2.32.1.tgz",
@@ -1714,6 +1769,21 @@
"hono": "^4"
}
},
"node_modules/@hugeicons/core-free-icons": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/@hugeicons/core-free-icons/-/core-free-icons-3.3.0.tgz",
"integrity": "sha512-qYyr4JQ2eQIHTSTbITvnJvs6ERNK64D9gpwZnf2IyuG0exzqfyABLO/oTB71FB3RZPfu1GbwycdiGSo46apjMQ==",
"license": "MIT"
},
"node_modules/@hugeicons/react": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/@hugeicons/react/-/react-1.1.5.tgz",
"integrity": "sha512-JX/iDz3oO7hWdVqbjwFwRrAjHk8h2vI+mBkNzp4JcXG3t4idoupfjon73nLOA7cr27m0M8hrRC1Q2h6nEBGKVA==",
"license": "MIT",
"peerDependencies": {
"react": ">=16.0.0"
}
},
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -2498,9 +2568,9 @@
}
},
"node_modules/@next/env": {
"version": "15.5.12",
"resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.12.tgz",
"integrity": "sha512-pUvdJN1on574wQHjaBfNGDt9Mz5utDSZFsIIQkMzPgNS8ZvT4H2mwOrOIClwsQOb6EGx5M76/CZr6G8i6pSpLg==",
"version": "16.1.6",
"resolved": "https://registry.npmjs.org/@next/env/-/env-16.1.6.tgz",
"integrity": "sha512-N1ySLuZjnAtN3kFnwhAwPvZah8RJxKasD7x1f8shFqhncnWZn4JMfg37diLNuoHsLAlrDfM3g4mawVdtAG8XLQ==",
"license": "MIT"
},
"node_modules/@next/eslint-plugin-next": {
@@ -2514,9 +2584,9 @@
}
},
"node_modules/@next/swc-darwin-arm64": {
"version": "15.5.12",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.12.tgz",
"integrity": "sha512-RnRjBtH8S8eXCpUNkQ+543DUc7ys8y15VxmFU9HRqlo9BG3CcBUiwNtF8SNoi2xvGCVJq1vl2yYq+3oISBS0Zg==",
"version": "16.1.6",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.1.6.tgz",
"integrity": "sha512-wTzYulosJr/6nFnqGW7FrG3jfUUlEf8UjGA0/pyypJl42ExdVgC6xJgcXQ+V8QFn6niSG2Pb8+MIG1mZr2vczw==",
"cpu": [
"arm64"
],
@@ -2530,9 +2600,9 @@
}
},
"node_modules/@next/swc-darwin-x64": {
"version": "15.5.12",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.12.tgz",
"integrity": "sha512-nqa9/7iQlboF1EFtNhWxQA0rQstmYRSBGxSM6g3GxvxHxcoeqVXfGNr9stJOme674m2V7r4E3+jEhhGvSQhJRA==",
"version": "16.1.6",
"resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.1.6.tgz",
"integrity": "sha512-BLFPYPDO+MNJsiDWbeVzqvYd4NyuRrEYVB5k2N3JfWncuHAy2IVwMAOlVQDFjj+krkWzhY2apvmekMkfQR0CUQ==",
"cpu": [
"x64"
],
@@ -2546,9 +2616,9 @@
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
"version": "15.5.12",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.12.tgz",
"integrity": "sha512-dCzAjqhDHwmoB2M4eYfVKqXs99QdQxNQVpftvP1eGVppamXh/OkDAwV737Zr0KPXEqRUMN4uCjh6mjO+XtF3Mw==",
"version": "16.1.6",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.1.6.tgz",
"integrity": "sha512-OJYkCd5pj/QloBvoEcJ2XiMnlJkRv9idWA/j0ugSuA34gMT6f5b7vOiCQHVRpvStoZUknhl6/UxOXL4OwtdaBw==",
"cpu": [
"arm64"
],
@@ -2562,9 +2632,9 @@
}
},
"node_modules/@next/swc-linux-arm64-musl": {
"version": "15.5.12",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.12.tgz",
"integrity": "sha512-+fpGWvQiITgf7PUtbWY1H7qUSnBZsPPLyyq03QuAKpVoTy/QUx1JptEDTQMVvQhvizCEuNLEeghrQUyXQOekuw==",
"version": "16.1.6",
"resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.1.6.tgz",
"integrity": "sha512-S4J2v+8tT3NIO9u2q+S0G5KdvNDjXfAv06OhfOzNDaBn5rw84DGXWndOEB7d5/x852A20sW1M56vhC/tRVbccQ==",
"cpu": [
"arm64"
],
@@ -2578,9 +2648,9 @@
}
},
"node_modules/@next/swc-linux-x64-gnu": {
"version": "15.5.12",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.12.tgz",
"integrity": "sha512-jSLvgdRRL/hrFAPqEjJf1fFguC719kmcptjNVDJl26BnJIpjL3KH5h6mzR4mAweociLQaqvt4UyzfbFjgAdDcw==",
"version": "16.1.6",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.1.6.tgz",
"integrity": "sha512-2eEBDkFlMMNQnkTyPBhQOAyn2qMxyG2eE7GPH2WIDGEpEILcBPI/jdSv4t6xupSP+ot/jkfrCShLAa7+ZUPcJQ==",
"cpu": [
"x64"
],
@@ -2594,9 +2664,9 @@
}
},
"node_modules/@next/swc-linux-x64-musl": {
"version": "15.5.12",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.12.tgz",
"integrity": "sha512-/uaF0WfmYqQgLfPmN6BvULwxY0dufI2mlN2JbOKqqceZh1G4hjREyi7pg03zjfyS6eqNemHAZPSoP84x17vo6w==",
"version": "16.1.6",
"resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.1.6.tgz",
"integrity": "sha512-oicJwRlyOoZXVlxmIMaTq7f8pN9QNbdes0q2FXfRsPhfCi8n8JmOZJm5oo1pwDaFbnnD421rVU409M3evFbIqg==",
"cpu": [
"x64"
],
@@ -2610,9 +2680,9 @@
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
"version": "15.5.12",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.12.tgz",
"integrity": "sha512-xhsL1OvQSfGmlL5RbOmU+FV120urrgFpYLq+6U8C6KIym32gZT6XF/SDE92jKzzlPWskkbjOKCpqk5m4i8PEfg==",
"version": "16.1.6",
"resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.1.6.tgz",
"integrity": "sha512-gQmm8izDTPgs+DCWH22kcDmuUp7NyiJgEl18bcr8irXA5N2m2O+JQIr6f3ct42GOs9c0h8QF3L5SzIxcYAAXXw==",
"cpu": [
"arm64"
],
@@ -2626,9 +2696,9 @@
}
},
"node_modules/@next/swc-win32-x64-msvc": {
"version": "15.5.12",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.12.tgz",
"integrity": "sha512-Z1Dh6lhFkxvBDH1FoW6OU/L6prYwPSlwjLiZkExIAh8fbP6iI/M7iGTQAJPYJ9YFlWobCZ1PHbchFhFYb2ADkw==",
"version": "16.1.6",
"resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.1.6.tgz",
"integrity": "sha512-NRfO39AIrzBnixKbjuo2YiYhB6o9d8v/ymU9m/Xk8cyVk+k7XylniXkHwjs4s70wedVffc6bQNbufk5v0xEm0A==",
"cpu": [
"x64"
],
@@ -10110,7 +10180,6 @@
"version": "2.10.0",
"resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.0.tgz",
"integrity": "sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"baseline-browser-mapping": "dist/cli.cjs"
@@ -14337,14 +14406,15 @@
}
},
"node_modules/next": {
"version": "15.5.12",
"resolved": "https://registry.npmjs.org/next/-/next-15.5.12.tgz",
"integrity": "sha512-Fi/wQ4Etlrn60rz78bebG1i1SR20QxvV8tVp6iJspjLUSHcZoeUXCt+vmWoEcza85ElZzExK/jJ/F6SvtGktjA==",
"version": "16.1.6",
"resolved": "https://registry.npmjs.org/next/-/next-16.1.6.tgz",
"integrity": "sha512-hkyRkcu5x/41KoqnROkfTm2pZVbKxvbZRuNvKXLRXxs3VfyO0WhY50TQS40EuKO9SW3rBj/sF3WbVwDACeMZyw==",
"license": "MIT",
"peer": true,
"dependencies": {
"@next/env": "15.5.12",
"@next/env": "16.1.6",
"@swc/helpers": "0.5.15",
"baseline-browser-mapping": "^2.8.3",
"caniuse-lite": "^1.0.30001579",
"postcss": "8.4.31",
"styled-jsx": "5.1.6"
@@ -14353,18 +14423,18 @@
"next": "dist/bin/next"
},
"engines": {
"node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
"node": ">=20.9.0"
},
"optionalDependencies": {
"@next/swc-darwin-arm64": "15.5.12",
"@next/swc-darwin-x64": "15.5.12",
"@next/swc-linux-arm64-gnu": "15.5.12",
"@next/swc-linux-arm64-musl": "15.5.12",
"@next/swc-linux-x64-gnu": "15.5.12",
"@next/swc-linux-x64-musl": "15.5.12",
"@next/swc-win32-arm64-msvc": "15.5.12",
"@next/swc-win32-x64-msvc": "15.5.12",
"sharp": "^0.34.3"
"@next/swc-darwin-arm64": "16.1.6",
"@next/swc-darwin-x64": "16.1.6",
"@next/swc-linux-arm64-gnu": "16.1.6",
"@next/swc-linux-arm64-musl": "16.1.6",
"@next/swc-linux-x64-gnu": "16.1.6",
"@next/swc-linux-x64-musl": "16.1.6",
"@next/swc-win32-arm64-msvc": "16.1.6",
"@next/swc-win32-x64-msvc": "16.1.6",
"sharp": "^0.34.4"
},
"peerDependencies": {
"@opentelemetry/api": "^1.1.0",
@@ -15728,6 +15798,12 @@
"node": ">=0.10.0"
}
},
"node_modules/reselect": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.1.1.tgz",
"integrity": "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==",
"license": "MIT"
},
"node_modules/resolve": {
"version": "1.22.11",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
@@ -16798,6 +16874,12 @@
"react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/tabbable": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz",
"integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==",
"license": "MIT"
},
"node_modules/tagged-tag": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz",

View File

@@ -24,7 +24,7 @@
"@clerk/backend": "^2.32.1",
"@tailwindcss/postcss": "^4.2.0",
"convex": "^1.32.0",
"next": "^15.3.2",
"next": "16.1.6",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"stripe": "^20.4.0",