new admin ui
This commit is contained in:
+48
-17
@@ -3,7 +3,9 @@
|
||||
import type React from "react";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
import AdminLeftNavigation from "@/components/AdminLeftNavigation";
|
||||
import AdminLeftNavigation, {
|
||||
type AdminNavGroup,
|
||||
} from "@/components/AdminLeftNavigation";
|
||||
import { useAuth } from "@/context/AuthContext";
|
||||
import {
|
||||
LayoutDashboard,
|
||||
@@ -13,24 +15,53 @@ import {
|
||||
Store,
|
||||
Users,
|
||||
Settings,
|
||||
LucideMail,
|
||||
Mail,
|
||||
Wand2,
|
||||
FolderKanban,
|
||||
Shield,
|
||||
} from "lucide-react";
|
||||
|
||||
const navItems = [
|
||||
{ label: "Dashboard", href: "/admin", icon: <LayoutDashboard className="h-4 w-4" /> },
|
||||
{ label: "Imports", href: "/admin/import-status", icon: <Download className="h-4 w-4" /> },
|
||||
{ label: "Mappings", href: "/admin/mapping", icon: <Layers className="h-4 w-4" /> },
|
||||
{ label: "Products", href: "/admin/products", icon: <Boxes className="h-4 w-4" /> },
|
||||
{ label: "Merchants", href: "/admin/merchants", icon: <Store className="h-4 w-4" /> },
|
||||
{ label: "Platforms", href: "/admin/platforms", icon: <Layers className="h-4 w-4" /> },
|
||||
{ label: "Enrichment", href: "/admin/enrichment", icon: <Wand2 className="h-4 w-4" /> },
|
||||
{ label: "Users", href: "/admin/users", icon: <Users className="h-4 w-4" /> },
|
||||
{ label: "Settings", href: "/admin/settings", icon: <Settings className="h-4 w-4" /> },
|
||||
{ label: "List Emails", href: "/admin/email", icon: <LucideMail className="h-4 w-4" /> },
|
||||
{ label: "Send an Email", href: "/admin/email/send", icon: <LucideMail className="h-4 w-4" /> },
|
||||
{ label: "Beta Invites", href: "/admin/beta-invites", icon: <LucideMail className="h-4 w-4" /> },
|
||||
|
||||
const navGroups: AdminNavGroup[] = [
|
||||
{
|
||||
label: "Overview",
|
||||
icon: <LayoutDashboard className="h-4 w-4" />,
|
||||
items: [{ label: "Dashboard", href: "/admin", icon: <LayoutDashboard className="h-4 w-4" /> }],
|
||||
},
|
||||
{
|
||||
label: "Catalog",
|
||||
icon: <FolderKanban className="h-4 w-4" />,
|
||||
items: [
|
||||
{ label: "Products", href: "/admin/products", icon: <Boxes className="h-4 w-4" /> },
|
||||
{ label: "Platforms", href: "/admin/platforms", icon: <Layers className="h-4 w-4" /> },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Data Ops",
|
||||
icon: <Download className="h-4 w-4" />,
|
||||
items: [
|
||||
{ label: "Imports", href: "/admin/import-status", icon: <Download className="h-4 w-4" /> },
|
||||
{ label: "Mappings", href: "/admin/mapping", icon: <Layers className="h-4 w-4" /> },
|
||||
{ label: "Enrichment", href: "/admin/enrichment", icon: <Wand2 className="h-4 w-4" /> },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Growth & Comms",
|
||||
icon: <Mail className="h-4 w-4" />,
|
||||
items: [
|
||||
{ label: "Beta Invites", href: "/admin/beta-invites", icon: <Mail className="h-4 w-4" /> },
|
||||
{ label: "List Emails", href: "/admin/email", icon: <Mail className="h-4 w-4" /> },
|
||||
{ label: "Send an Email", href: "/admin/email/send", icon: <Mail className="h-4 w-4" /> },
|
||||
],
|
||||
},
|
||||
{
|
||||
label: "Access & Config",
|
||||
icon: <Shield className="h-4 w-4" />,
|
||||
items: [
|
||||
{ label: "Users", href: "/admin/users", icon: <Users className="h-4 w-4" /> },
|
||||
{ label: "Merchants", href: "/admin/merchants", icon: <Store className="h-4 w-4" /> },
|
||||
{ label: "Settings", href: "/admin/settings", icon: <Settings className="h-4 w-4" /> },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default function AdminLayout({
|
||||
@@ -79,7 +110,7 @@ export default function AdminLayout({
|
||||
<AdminLeftNavigation
|
||||
collapsed={collapsed}
|
||||
onToggleCollapsed={() => setCollapsed((v) => !v)}
|
||||
items={navItems}
|
||||
groups={navGroups}
|
||||
/>
|
||||
|
||||
{/* Main column */}
|
||||
|
||||
@@ -1,109 +1,199 @@
|
||||
"use client";
|
||||
|
||||
import type React from "react";
|
||||
import * as React from "react";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
LayoutDashboard,
|
||||
Download,
|
||||
Layers,
|
||||
Boxes,
|
||||
Store,
|
||||
Users,
|
||||
Settings,
|
||||
LucideMail,
|
||||
Wand2,
|
||||
} from "lucide-react";
|
||||
import { usePathname } from "next/navigation";
|
||||
import { Disclosure, DisclosureButton, DisclosurePanel } from "@headlessui/react";
|
||||
import { ChevronRight, PanelLeftClose, PanelLeftOpen } from "lucide-react";
|
||||
|
||||
export type AdminNavItem = {
|
||||
label: string;
|
||||
href: string;
|
||||
icon: React.ReactNode;
|
||||
icon?: React.ReactNode;
|
||||
};
|
||||
|
||||
type AdminLeftNavigationProps = {
|
||||
collapsed: boolean;
|
||||
onToggleCollapsed: () => void;
|
||||
items?: AdminNavItem[]; // ✅ NEW
|
||||
export type AdminNavGroup = {
|
||||
label: string;
|
||||
icon?: React.ReactNode;
|
||||
items: AdminNavItem[];
|
||||
};
|
||||
|
||||
const defaultNavItems: AdminNavItem[] = [
|
||||
{ label: "Dashboard", href: "/admin", icon: <LayoutDashboard className="h-4 w-4" /> },
|
||||
{ label: "Imports", href: "/admin/import-status", icon: <Download className="h-4 w-4" /> },
|
||||
{ label: "Mappings", href: "/admin/mapping", icon: <Layers className="h-4 w-4" /> },
|
||||
{ label: "Products", href: "/admin/products", icon: <Boxes className="h-4 w-4" /> },
|
||||
{ label: "Merchants", href: "/admin/merchants", icon: <Store className="h-4 w-4" /> },
|
||||
{ label: "Platforms", href: "/admin/platforms", icon: <Layers className="h-4 w-4" /> },
|
||||
{ label: "Enrichment", href: "/admin/enrichment", icon: <Wand2 className="h-4 w-4" /> },
|
||||
{ label: "Users", href: "/admin/users", icon: <Users className="h-4 w-4" /> },
|
||||
{ label: "Settings", href: "/admin/settings", icon: <Settings className="h-4 w-4" /> },
|
||||
{ label: "Manage Emails", href: "/admin/email/manage", icon: <LucideMail className="h-4 w-4" /> },
|
||||
{ label: "Send an Email", href: "/admin/email/send", icon: <LucideMail className="h-4 w-4" /> },
|
||||
{ label: "Beta Invites", href: "/admin/beta-invites", icon: <LucideMail className="h-4 w-4" />,
|
||||
},
|
||||
];
|
||||
function cx(...classes: Array<string | false | undefined | null>) {
|
||||
return classes.filter(Boolean).join(" ");
|
||||
}
|
||||
|
||||
function isActiveRoute(pathname: string, href: string) {
|
||||
// Exact match for /admin, prefix match for nested pages
|
||||
if (href === "/admin") return pathname === "/admin";
|
||||
return pathname === href || pathname.startsWith(`${href}/`);
|
||||
}
|
||||
|
||||
function groupHasActiveItem(pathname: string, group: AdminNavGroup) {
|
||||
return group.items.some((i) => isActiveRoute(pathname, i.href));
|
||||
}
|
||||
|
||||
export default function AdminLeftNavigation({
|
||||
collapsed,
|
||||
onToggleCollapsed,
|
||||
items = defaultNavItems, // ✅ NEW default
|
||||
}: AdminLeftNavigationProps) {
|
||||
groups,
|
||||
}: {
|
||||
collapsed: boolean;
|
||||
onToggleCollapsed: () => void;
|
||||
groups: AdminNavGroup[];
|
||||
}) {
|
||||
const pathname = usePathname() || "/admin";
|
||||
|
||||
return (
|
||||
<aside
|
||||
className={`hidden border-r border-zinc-900 bg-zinc-950/80 px-3 py-6 md:flex md:flex-col transition-all duration-200 ${
|
||||
collapsed ? "w-16" : "w-60"
|
||||
}`}
|
||||
className={cx(
|
||||
"sticky top-0 flex h-screen shrink-0 flex-col border-r border-zinc-900 bg-zinc-950/60 backdrop-blur",
|
||||
collapsed ? "w-[68px]" : "w-[280px]"
|
||||
)}
|
||||
>
|
||||
<div className="mb-6 flex items-center gap-2">
|
||||
{/* Header / Brand */}
|
||||
<div className={cx("flex items-center justify-between px-3 py-3", collapsed && "justify-center")}>
|
||||
<div className={cx("flex items-center gap-2", collapsed && "hidden")}>
|
||||
<div className="h-8 w-8 rounded-lg bg-zinc-900/70 ring-1 ring-zinc-800" />
|
||||
<div className="leading-tight">
|
||||
<div className="text-xs uppercase tracking-[0.22em] text-zinc-500">
|
||||
Battl
|
||||
</div>
|
||||
<div className="text-sm font-medium text-zinc-100">
|
||||
Admin
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={onToggleCollapsed}
|
||||
className="inline-flex h-8 w-8 items-center justify-center rounded-md border border-zinc-800 bg-zinc-950 text-zinc-400 transition hover:border-amber-400/60 hover:text-amber-300"
|
||||
className={cx(
|
||||
"inline-flex h-9 w-9 items-center justify-center rounded-md border border-zinc-800 bg-zinc-900/40 text-zinc-200 hover:bg-zinc-900/70",
|
||||
collapsed && "mx-auto"
|
||||
)}
|
||||
aria-label={collapsed ? "Expand sidebar" : "Collapse sidebar"}
|
||||
title={collapsed ? "Expand" : "Collapse"}
|
||||
>
|
||||
<span className="sr-only">Toggle sidebar</span>
|
||||
<div className="space-y-0.5">
|
||||
<span className="block h-[1px] w-3 bg-current" />
|
||||
<span className="block h-[1px] w-3 bg-current" />
|
||||
<span className="block h-[1px] w-3 bg-current" />
|
||||
</div>
|
||||
{collapsed ? (
|
||||
<PanelLeftOpen className="h-4 w-4" />
|
||||
) : (
|
||||
<PanelLeftClose className="h-4 w-4" />
|
||||
)}
|
||||
</button>
|
||||
|
||||
{!collapsed && (
|
||||
<div>
|
||||
<p className="text-xs font-semibold uppercase tracking-[0.18em] text-zinc-500">
|
||||
<Link href="/" title="Back to Battl Dashboard">
|
||||
Battl Builders
|
||||
</Link>
|
||||
</p>
|
||||
<p className="text-[10px] text-zinc-600">Admin Command</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<nav className="flex-1 space-y-1 text-sm">
|
||||
{items.map((item) => (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className="flex items-center justify-between rounded-md px-2 py-1.5 text-zinc-300 transition hover:bg-zinc-900 hover:text-amber-300"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
{item.icon}
|
||||
{!collapsed && <span>{item.label}</span>}
|
||||
</div>
|
||||
{!collapsed && <span className="text-[10px] text-zinc-600">›</span>}
|
||||
</Link>
|
||||
))}
|
||||
{/* Nav */}
|
||||
<nav className="flex-1 overflow-y-auto px-2 pb-3">
|
||||
<ul className="space-y-1">
|
||||
{groups.map((group) => {
|
||||
const defaultOpen = groupHasActiveItem(pathname, group);
|
||||
|
||||
// In collapsed mode, we render items as a flat icon list (no accordion)
|
||||
if (collapsed) {
|
||||
return (
|
||||
<li key={group.label} className="pt-1">
|
||||
<div className="my-2 h-px bg-zinc-900" />
|
||||
<div className="flex flex-col gap-1">
|
||||
{group.items.map((item) => {
|
||||
const active = isActiveRoute(pathname, item.href);
|
||||
return (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
title={`${group.label} → ${item.label}`}
|
||||
className={cx(
|
||||
"flex h-10 items-center justify-center rounded-md border border-transparent",
|
||||
active
|
||||
? "bg-white/5 text-white ring-1 ring-zinc-800"
|
||||
: "text-zinc-400 hover:bg-white/5 hover:text-zinc-200"
|
||||
)}
|
||||
>
|
||||
<span className="inline-flex">{item.icon}</span>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<li key={group.label}>
|
||||
<Disclosure defaultOpen={defaultOpen}>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<DisclosureButton
|
||||
className={cx(
|
||||
"group flex w-full items-center justify-between rounded-md px-2 py-2 text-left text-xs font-semibold uppercase tracking-[0.18em]",
|
||||
open ? "bg-white/5 text-zinc-100" : "text-zinc-400 hover:bg-white/5 hover:text-zinc-200"
|
||||
)}
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
<span className="inline-flex text-zinc-400 group-hover:text-zinc-200">
|
||||
{group.icon}
|
||||
</span>
|
||||
{group.label}
|
||||
</span>
|
||||
|
||||
<ChevronRight
|
||||
className={cx(
|
||||
"h-4 w-4 text-zinc-500 transition-transform",
|
||||
open && "rotate-90"
|
||||
)}
|
||||
/>
|
||||
</DisclosureButton>
|
||||
|
||||
<DisclosurePanel className="mt-1 space-y-1 pl-1">
|
||||
{group.items.map((item) => {
|
||||
const active = isActiveRoute(pathname, item.href);
|
||||
return (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={cx(
|
||||
"flex items-center gap-2 rounded-md px-2 py-2 text-sm",
|
||||
active
|
||||
? "bg-white/5 text-white ring-1 ring-zinc-800"
|
||||
: "text-zinc-300 hover:bg-white/5 hover:text-white"
|
||||
)}
|
||||
>
|
||||
<span className="inline-flex h-5 w-5 items-center justify-center text-zinc-400">
|
||||
{item.icon}
|
||||
</span>
|
||||
<span className="truncate">{item.label}</span>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</DisclosurePanel>
|
||||
</>
|
||||
)}
|
||||
</Disclosure>
|
||||
</li>
|
||||
);
|
||||
})}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
{!collapsed && (
|
||||
<div className="mt-6 border-t border-zinc-900 pt-4 text-[11px] text-zinc-600">
|
||||
<p className="text-[10px] uppercase tracking-[0.16em] text-zinc-500">Status</p>
|
||||
<p className="mt-1">
|
||||
Import engine: <span className="text-emerald-400">online</span>
|
||||
</p>
|
||||
<p className="text-zinc-500">Builder UI: in progress</p>
|
||||
{/* Footer */}
|
||||
<div className={cx("border-t border-zinc-900 p-3", collapsed && "px-2")}>
|
||||
<div
|
||||
className={cx(
|
||||
"flex items-center gap-3 rounded-md bg-zinc-900/30 px-3 py-2 ring-1 ring-zinc-800",
|
||||
collapsed && "justify-center px-2"
|
||||
)}
|
||||
title="Admin user"
|
||||
>
|
||||
<div className="h-8 w-8 rounded-full bg-zinc-900 ring-1 ring-zinc-800" />
|
||||
{!collapsed && (
|
||||
<div className="min-w-0">
|
||||
<div className="text-xs uppercase tracking-[0.18em] text-zinc-500">
|
||||
Internal
|
||||
</div>
|
||||
<div className="truncate text-sm text-zinc-200">ADMIN</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</aside>
|
||||
);
|
||||
}
|
||||
Generated
+213
@@ -8,6 +8,7 @@
|
||||
"name": "gunbuilder-prototype",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^2.2.9",
|
||||
"@heroicons/react": "^2.2.0",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.555.0",
|
||||
@@ -140,6 +141,79 @@
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/core": {
|
||||
"version": "1.7.3",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
|
||||
"integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/utils": "^0.2.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/dom": {
|
||||
"version": "1.7.4",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
|
||||
"integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/core": "^1.7.3",
|
||||
"@floating-ui/utils": "^0.2.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/react": {
|
||||
"version": "0.26.28",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz",
|
||||
"integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/react-dom": "^2.1.2",
|
||||
"@floating-ui/utils": "^0.2.8",
|
||||
"tabbable": "^6.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/react-dom": {
|
||||
"version": "2.1.6",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.6.tgz",
|
||||
"integrity": "sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/dom": "^1.7.4"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0",
|
||||
"react-dom": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@floating-ui/utils": {
|
||||
"version": "0.2.10",
|
||||
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
|
||||
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@headlessui/react": {
|
||||
"version": "2.2.9",
|
||||
"resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.2.9.tgz",
|
||||
"integrity": "sha512-Mb+Un58gwBn0/yWZfyrCh0TJyurtT+dETj7YHleylHk5od3dv2XqETPGWMyQ5/7sYN7oWdyM1u9MvC0OC8UmzQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@floating-ui/react": "^0.26.16",
|
||||
"@react-aria/focus": "^3.20.2",
|
||||
"@react-aria/interactions": "^3.25.0",
|
||||
"@tanstack/react-virtual": "^3.13.9",
|
||||
"use-sync-external-store": "^1.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18 || ^19 || ^19.0.0-rc",
|
||||
"react-dom": "^18 || ^19 || ^19.0.0-rc"
|
||||
}
|
||||
},
|
||||
"node_modules/@heroicons/react": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz",
|
||||
@@ -505,6 +579,103 @@
|
||||
"node": ">=14"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/focus": {
|
||||
"version": "3.21.3",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.3.tgz",
|
||||
"integrity": "sha512-FsquWvjSCwC2/sBk4b+OqJyONETUIXQ2vM0YdPAuC+QFQh2DT6TIBo6dOZVSezlhudDla69xFBd6JvCFq1AbUw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@react-aria/interactions": "^3.26.0",
|
||||
"@react-aria/utils": "^3.32.0",
|
||||
"@react-types/shared": "^3.32.1",
|
||||
"@swc/helpers": "^0.5.0",
|
||||
"clsx": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
|
||||
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/interactions": {
|
||||
"version": "3.26.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.26.0.tgz",
|
||||
"integrity": "sha512-AAEcHiltjfbmP1i9iaVw34Mb7kbkiHpYdqieWufldh4aplWgsF11YQZOfaCJW4QoR2ML4Zzoa9nfFwLXA52R7Q==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@react-aria/ssr": "^3.9.10",
|
||||
"@react-aria/utils": "^3.32.0",
|
||||
"@react-stately/flags": "^3.1.2",
|
||||
"@react-types/shared": "^3.32.1",
|
||||
"@swc/helpers": "^0.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
|
||||
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/ssr": {
|
||||
"version": "3.9.10",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz",
|
||||
"integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/helpers": "^0.5.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 12"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-aria/utils": {
|
||||
"version": "3.32.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.32.0.tgz",
|
||||
"integrity": "sha512-/7Rud06+HVBIlTwmwmJa2W8xVtgxgzm0+kLbuFooZRzKDON6hhozS1dOMR/YLMxyJOaYOTpImcP4vRR9gL1hEg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@react-aria/ssr": "^3.9.10",
|
||||
"@react-stately/flags": "^3.1.2",
|
||||
"@react-stately/utils": "^3.11.0",
|
||||
"@react-types/shared": "^3.32.1",
|
||||
"@swc/helpers": "^0.5.0",
|
||||
"clsx": "^2.0.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1",
|
||||
"react-dom": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-stately/flags": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.2.tgz",
|
||||
"integrity": "sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/helpers": "^0.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-stately/utils": {
|
||||
"version": "3.11.0",
|
||||
"resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.11.0.tgz",
|
||||
"integrity": "sha512-8LZpYowJ9eZmmYLpudbo/eclIRnbhWIJZ994ncmlKlouNzKohtM8qTC6B1w1pwUbiwGdUoyzLuQbeaIor5Dvcw==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@swc/helpers": "^0.5.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@react-types/shared": {
|
||||
"version": "3.32.1",
|
||||
"resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.1.tgz",
|
||||
"integrity": "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==",
|
||||
"license": "Apache-2.0",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@rtsao/scc": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
||||
@@ -535,6 +706,33 @@
|
||||
"tslib": "^2.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-virtual": {
|
||||
"version": "3.13.14",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.13.14.tgz",
|
||||
"integrity": "sha512-WG0d7mBD54eA7dgA3+sO5csS0B49QKqM6Gy5Rf31+Oq/LTKROQSao9m2N/vz1IqVragOKU5t5k1LAcqh/DfTxw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/virtual-core": "3.13.14"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/virtual-core": {
|
||||
"version": "3.13.14",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.13.14.tgz",
|
||||
"integrity": "sha512-b5Uvd8J2dc7ICeX9SRb/wkCxWk7pUwN214eEPAQsqrsktSKTCmyLxOQWSMgogBByXclZeAdgZ3k4o0fIYUIBqQ==",
|
||||
"license": "MIT",
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tybys/wasm-util": {
|
||||
"version": "0.10.1",
|
||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
||||
@@ -5655,6 +5853,12 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/tabbable": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.3.0.tgz",
|
||||
"integrity": "sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tailwindcss": {
|
||||
"version": "3.4.3",
|
||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz",
|
||||
@@ -6094,6 +6298,15 @@
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/use-sync-external-store": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
|
||||
"integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^2.2.9",
|
||||
"@heroicons/react": "^2.2.0",
|
||||
"clsx": "^2.1.1",
|
||||
"lucide-react": "^0.555.0",
|
||||
|
||||
Reference in New Issue
Block a user