new admin ui
This commit is contained in:
+48
-17
@@ -3,7 +3,9 @@
|
|||||||
import type React from "react";
|
import type React from "react";
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { usePathname, useRouter } from "next/navigation";
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
import AdminLeftNavigation from "@/components/AdminLeftNavigation";
|
import AdminLeftNavigation, {
|
||||||
|
type AdminNavGroup,
|
||||||
|
} from "@/components/AdminLeftNavigation";
|
||||||
import { useAuth } from "@/context/AuthContext";
|
import { useAuth } from "@/context/AuthContext";
|
||||||
import {
|
import {
|
||||||
LayoutDashboard,
|
LayoutDashboard,
|
||||||
@@ -13,24 +15,53 @@ import {
|
|||||||
Store,
|
Store,
|
||||||
Users,
|
Users,
|
||||||
Settings,
|
Settings,
|
||||||
LucideMail,
|
Mail,
|
||||||
Wand2,
|
Wand2,
|
||||||
|
FolderKanban,
|
||||||
|
Shield,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
const navItems = [
|
const navGroups: AdminNavGroup[] = [
|
||||||
{ 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: "Overview",
|
||||||
{ label: "Mappings", href: "/admin/mapping", icon: <Layers className="h-4 w-4" /> },
|
icon: <LayoutDashboard className="h-4 w-4" />,
|
||||||
{ label: "Products", href: "/admin/products", icon: <Boxes className="h-4 w-4" /> },
|
items: [{ label: "Dashboard", href: "/admin", icon: <LayoutDashboard 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: "Catalog",
|
||||||
{ label: "Users", href: "/admin/users", icon: <Users className="h-4 w-4" /> },
|
icon: <FolderKanban className="h-4 w-4" />,
|
||||||
{ label: "Settings", href: "/admin/settings", icon: <Settings className="h-4 w-4" /> },
|
items: [
|
||||||
{ label: "List Emails", href: "/admin/email", icon: <LucideMail className="h-4 w-4" /> },
|
{ label: "Products", href: "/admin/products", icon: <Boxes className="h-4 w-4" /> },
|
||||||
{ label: "Send an Email", href: "/admin/email/send", icon: <LucideMail className="h-4 w-4" /> },
|
{ label: "Platforms", href: "/admin/platforms", icon: <Layers className="h-4 w-4" /> },
|
||||||
{ label: "Beta Invites", href: "/admin/beta-invites", icon: <LucideMail 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({
|
export default function AdminLayout({
|
||||||
@@ -79,7 +110,7 @@ export default function AdminLayout({
|
|||||||
<AdminLeftNavigation
|
<AdminLeftNavigation
|
||||||
collapsed={collapsed}
|
collapsed={collapsed}
|
||||||
onToggleCollapsed={() => setCollapsed((v) => !v)}
|
onToggleCollapsed={() => setCollapsed((v) => !v)}
|
||||||
items={navItems}
|
groups={navGroups}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Main column */}
|
{/* Main column */}
|
||||||
|
|||||||
@@ -1,109 +1,199 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import type React from "react";
|
import * as React from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import {
|
import { usePathname } from "next/navigation";
|
||||||
LayoutDashboard,
|
import { Disclosure, DisclosureButton, DisclosurePanel } from "@headlessui/react";
|
||||||
Download,
|
import { ChevronRight, PanelLeftClose, PanelLeftOpen } from "lucide-react";
|
||||||
Layers,
|
|
||||||
Boxes,
|
|
||||||
Store,
|
|
||||||
Users,
|
|
||||||
Settings,
|
|
||||||
LucideMail,
|
|
||||||
Wand2,
|
|
||||||
} from "lucide-react";
|
|
||||||
|
|
||||||
export type AdminNavItem = {
|
export type AdminNavItem = {
|
||||||
label: string;
|
label: string;
|
||||||
href: string;
|
href: string;
|
||||||
icon: React.ReactNode;
|
icon?: React.ReactNode;
|
||||||
};
|
};
|
||||||
|
|
||||||
type AdminLeftNavigationProps = {
|
export type AdminNavGroup = {
|
||||||
collapsed: boolean;
|
label: string;
|
||||||
onToggleCollapsed: () => void;
|
icon?: React.ReactNode;
|
||||||
items?: AdminNavItem[]; // ✅ NEW
|
items: AdminNavItem[];
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultNavItems: AdminNavItem[] = [
|
function cx(...classes: Array<string | false | undefined | null>) {
|
||||||
{ label: "Dashboard", href: "/admin", icon: <LayoutDashboard className="h-4 w-4" /> },
|
return classes.filter(Boolean).join(" ");
|
||||||
{ 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" /> },
|
function isActiveRoute(pathname: string, href: string) {
|
||||||
{ label: "Merchants", href: "/admin/merchants", icon: <Store className="h-4 w-4" /> },
|
// Exact match for /admin, prefix match for nested pages
|
||||||
{ label: "Platforms", href: "/admin/platforms", icon: <Layers className="h-4 w-4" /> },
|
if (href === "/admin") return pathname === "/admin";
|
||||||
{ label: "Enrichment", href: "/admin/enrichment", icon: <Wand2 className="h-4 w-4" /> },
|
return pathname === href || pathname.startsWith(`${href}/`);
|
||||||
{ 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" /> },
|
function groupHasActiveItem(pathname: string, group: AdminNavGroup) {
|
||||||
{ label: "Send an Email", href: "/admin/email/send", icon: <LucideMail className="h-4 w-4" /> },
|
return group.items.some((i) => isActiveRoute(pathname, i.href));
|
||||||
{ label: "Beta Invites", href: "/admin/beta-invites", icon: <LucideMail className="h-4 w-4" />,
|
}
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function AdminLeftNavigation({
|
export default function AdminLeftNavigation({
|
||||||
collapsed,
|
collapsed,
|
||||||
onToggleCollapsed,
|
onToggleCollapsed,
|
||||||
items = defaultNavItems, // ✅ NEW default
|
groups,
|
||||||
}: AdminLeftNavigationProps) {
|
}: {
|
||||||
|
collapsed: boolean;
|
||||||
|
onToggleCollapsed: () => void;
|
||||||
|
groups: AdminNavGroup[];
|
||||||
|
}) {
|
||||||
|
const pathname = usePathname() || "/admin";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<aside
|
<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 ${
|
className={cx(
|
||||||
collapsed ? "w-16" : "w-60"
|
"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
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={onToggleCollapsed}
|
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>
|
{collapsed ? (
|
||||||
<div className="space-y-0.5">
|
<PanelLeftOpen className="h-4 w-4" />
|
||||||
<span className="block h-[1px] w-3 bg-current" />
|
) : (
|
||||||
<span className="block h-[1px] w-3 bg-current" />
|
<PanelLeftClose className="h-4 w-4" />
|
||||||
<span className="block h-[1px] w-3 bg-current" />
|
)}
|
||||||
</div>
|
|
||||||
</button>
|
</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>
|
</div>
|
||||||
|
|
||||||
<nav className="flex-1 space-y-1 text-sm">
|
{/* Nav */}
|
||||||
{items.map((item) => (
|
<nav className="flex-1 overflow-y-auto px-2 pb-3">
|
||||||
<Link
|
<ul className="space-y-1">
|
||||||
key={item.href}
|
{groups.map((group) => {
|
||||||
href={item.href}
|
const defaultOpen = groupHasActiveItem(pathname, group);
|
||||||
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"
|
|
||||||
>
|
// In collapsed mode, we render items as a flat icon list (no accordion)
|
||||||
<div className="flex items-center gap-2">
|
if (collapsed) {
|
||||||
{item.icon}
|
return (
|
||||||
{!collapsed && <span>{item.label}</span>}
|
<li key={group.label} className="pt-1">
|
||||||
</div>
|
<div className="my-2 h-px bg-zinc-900" />
|
||||||
{!collapsed && <span className="text-[10px] text-zinc-600">›</span>}
|
<div className="flex flex-col gap-1">
|
||||||
</Link>
|
{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>
|
</nav>
|
||||||
|
|
||||||
{!collapsed && (
|
{/* Footer */}
|
||||||
<div className="mt-6 border-t border-zinc-900 pt-4 text-[11px] text-zinc-600">
|
<div className={cx("border-t border-zinc-900 p-3", collapsed && "px-2")}>
|
||||||
<p className="text-[10px] uppercase tracking-[0.16em] text-zinc-500">Status</p>
|
<div
|
||||||
<p className="mt-1">
|
className={cx(
|
||||||
Import engine: <span className="text-emerald-400">online</span>
|
"flex items-center gap-3 rounded-md bg-zinc-900/30 px-3 py-2 ring-1 ring-zinc-800",
|
||||||
</p>
|
collapsed && "justify-center px-2"
|
||||||
<p className="text-zinc-500">Builder UI: in progress</p>
|
)}
|
||||||
|
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>
|
||||||
)}
|
</div>
|
||||||
</aside>
|
</aside>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Generated
+213
@@ -8,6 +8,7 @@
|
|||||||
"name": "gunbuilder-prototype",
|
"name": "gunbuilder-prototype",
|
||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@headlessui/react": "^2.2.9",
|
||||||
"@heroicons/react": "^2.2.0",
|
"@heroicons/react": "^2.2.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.555.0",
|
"lucide-react": "^0.555.0",
|
||||||
@@ -140,6 +141,79 @@
|
|||||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
"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": {
|
"node_modules/@heroicons/react": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz",
|
||||||
@@ -505,6 +579,103 @@
|
|||||||
"node": ">=14"
|
"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": {
|
"node_modules/@rtsao/scc": {
|
||||||
"version": "1.1.0",
|
"version": "1.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz",
|
||||||
@@ -535,6 +706,33 @@
|
|||||||
"tslib": "^2.4.0"
|
"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": {
|
"node_modules/@tybys/wasm-util": {
|
||||||
"version": "0.10.1",
|
"version": "0.10.1",
|
||||||
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
|
||||||
@@ -5655,6 +5853,12 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/tailwindcss": {
|
||||||
"version": "3.4.3",
|
"version": "3.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.3.tgz",
|
||||||
@@ -6094,6 +6298,15 @@
|
|||||||
"punycode": "^2.1.0"
|
"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": {
|
"node_modules/util-deprecate": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"lint": "next lint"
|
"lint": "next lint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@headlessui/react": "^2.2.9",
|
||||||
"@heroicons/react": "^2.2.0",
|
"@heroicons/react": "^2.2.0",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"lucide-react": "^0.555.0",
|
"lucide-react": "^0.555.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user