This commit is contained in:
Anang Yusman 2025-11-11 14:28:07 +08:00
parent 5e61adcd5a
commit 3905a40d9b
12 changed files with 680 additions and 12 deletions

View File

@ -1,10 +1,10 @@
import AdminNavbar from "@/components/dashboard/admin-navbar"; import DashboardNavbar from "@/components/dashboard/dashboard-navbar";
import AdminTable from "@/components/table/admin-table"; import AdminTable from "@/components/table/admin-table";
export default function AdminPage() { export default function AdminPage() {
return ( return (
<div className="min-h-screen bg-[#f8f9fa]"> <div className="min-h-screen bg-[#f8f9fa]">
<AdminNavbar /> <DashboardNavbar />
<div className="p-6"> <div className="p-6">
<AdminTable /> <AdminTable />
</div> </div>

View File

@ -1,11 +1,10 @@
import ApproverNavbar from "@/components/dashboard/approver-navbar"; import DashboardNavbar from "@/components/dashboard/dashboard-navbar";
import ApproverDetail from "@/components/form/approver-detail"; import ApproverDetail from "@/components/form/approver-detail";
import ApproverTable from "@/components/table/approver-table";
export default function ApproverDetailPage() { export default function ApproverDetailPage() {
return ( return (
<div className="min-h-screen bg-[#f8f9fa]"> <div className="min-h-screen bg-[#f8f9fa]">
<ApproverNavbar /> <DashboardNavbar />
<div className="p-6"> <div className="p-6">
<ApproverDetail /> <ApproverDetail />
</div> </div>

View File

@ -1,10 +1,11 @@
import ApproverNavbar from "@/components/dashboard/approver-navbar"; import ApproverNavbar from "@/components/dashboard/approver-navbar";
import DashboardNavbar from "@/components/dashboard/dashboard-navbar";
import ApproverTable from "@/components/table/approver-table"; import ApproverTable from "@/components/table/approver-table";
export default function ApproverPage() { export default function ApproverPage() {
return ( return (
<div className="min-h-screen bg-[#f8f9fa]"> <div className="min-h-screen bg-[#f8f9fa]">
<ApproverNavbar /> <DashboardNavbar />
<div className="p-6"> <div className="p-6">
<ApproverTable /> <ApproverTable />
</div> </div>

View File

@ -1,3 +1,4 @@
import DashboardNavbar from "@/components/dashboard/dashboard-navbar";
import UserNavbar from "@/components/dashboard/user-navbar"; import UserNavbar from "@/components/dashboard/user-navbar";
import SupervisorData from "@/components/table/supervisor-data"; import SupervisorData from "@/components/table/supervisor-data";
import UserTable from "@/components/table/user-table"; import UserTable from "@/components/table/user-table";
@ -5,7 +6,7 @@ import UserTable from "@/components/table/user-table";
export default function SupervisorPage() { export default function SupervisorPage() {
return ( return (
<div className="min-h-screen bg-[#f8f9fa]"> <div className="min-h-screen bg-[#f8f9fa]">
<UserNavbar /> <DashboardNavbar />
<div className="p-6"> <div className="p-6">
<SupervisorData /> <SupervisorData />
</div> </div>

View File

@ -1,10 +1,11 @@
import DashboardNavbar from "@/components/dashboard/dashboard-navbar";
import UserNavbar from "@/components/dashboard/user-navbar"; import UserNavbar from "@/components/dashboard/user-navbar";
import FormCampaign from "@/components/form/campaign-form"; import FormCampaign from "@/components/form/campaign-form";
export default function UserCreatePage() { export default function UserCreatePage() {
return ( return (
<div className="min-h-screen bg-[#f8f9fa]"> <div className="min-h-screen bg-[#f8f9fa]">
<UserNavbar /> <DashboardNavbar />
<div className="p-6"> <div className="p-6">
<FormCampaign /> <FormCampaign />
</div> </div>

View File

@ -1,10 +1,11 @@
import DashboardNavbar from "@/components/dashboard/dashboard-navbar";
import UserNavbar from "@/components/dashboard/user-navbar"; import UserNavbar from "@/components/dashboard/user-navbar";
import UserTable from "@/components/table/user-table"; import UserTable from "@/components/table/user-table";
export default function UserPage() { export default function UserPage() {
return ( return (
<div className="min-h-screen bg-[#f8f9fa]"> <div className="min-h-screen bg-[#f8f9fa]">
<UserNavbar /> <DashboardNavbar />
<div className="p-6"> <div className="p-6">
<UserTable /> <UserTable />
</div> </div>

View File

@ -0,0 +1,195 @@
"use client";
import Image from "next/image";
import { Bell, LogOut } from "lucide-react";
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
import { useEffect, useState } from "react";
import Cookies from "js-cookie";
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbList,
BreadcrumbSeparator,
BreadcrumbPage,
} from "@/components/ui/breadcrumb";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
export default function DashboardNavbar() {
const router = useRouter();
const pathname = usePathname();
const [role, setRole] = useState<string | null>(null);
const [activeTab, setActiveTab] = useState("Manajemen User");
const [approverTab, setApproverTab] = useState("Kurasi Konten");
const segments = pathname.split("/").filter(Boolean);
useEffect(() => {
const userRole = Cookies.get("userRole");
setRole(userRole || null);
}, []);
const handleLogout = () => {
Cookies.remove("userRole");
router.push("/auth");
};
if (!role) return null;
return (
<div className="w-full bg-white shadow-sm border-b border-gray-200 px-6 py-3 flex items-center justify-between">
{/* === Kiri: Logo + Breadcrumb === */}
<div className="flex items-center gap-4">
<Link href={"/"}>
<Image src="/campaign.png" alt="Logo" width={60} height={60} />
</Link>
{/* Breadcrumb selalu tampil */}
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem>
<BreadcrumbLink href="/">Home</BreadcrumbLink>
</BreadcrumbItem>
{segments.map((segment, index) => {
const href = "/" + segments.slice(0, index + 1).join("/");
const isLast = index === segments.length - 1;
const label =
segment.charAt(0).toUpperCase() +
segment.slice(1).replace(/-/g, " ");
return (
<div key={href} className="flex items-center">
<BreadcrumbSeparator />
<BreadcrumbItem>
{isLast ? (
<BreadcrumbPage>{label}</BreadcrumbPage>
) : (
<BreadcrumbLink href={href}>{label}</BreadcrumbLink>
)}
</BreadcrumbItem>
</div>
);
})}
</BreadcrumbList>
</Breadcrumb>
</div>
{/* === Tengah: Menu berdasarkan Role === */}
<div className="flex-1 flex justify-center">
{/* ADMIN */}
{role === "admin" && (
<div className="flex items-center gap-8">
<button
onClick={() => {
setActiveTab("Manajemen User");
router.push("/dashboard/admin/user-management");
}}
className={`flex flex-col items-center text-sm font-medium px-4 py-2 rounded-md transition ${
activeTab === "Manajemen User"
? "text-black border-b-2 border-[#C4A663]"
: "text-gray-500 hover:text-gray-800"
}`}
>
<span className="text-base">🧑💼</span>
<span>Manajemen User</span>
</button>
</div>
)}
{/* APPROVER */}
{role === "approver" && (
<div className="flex items-center gap-12">
{["Kurasi Konten", "Publish Konten"].map((tab) => (
<button
key={tab}
onClick={() => {
setApproverTab(tab);
router.push(
`/dashboard/approver/${
tab === "Kurasi Konten" ? "kurasi" : "publish"
}`
);
}}
className={`flex flex-col items-center text-sm font-medium transition ${
approverTab === tab
? "text-black border-b-2 border-[#C4A663]"
: "text-gray-500 hover:text-gray-800"
}`}
>
{tab === "Kurasi Konten" ? (
<span className="text-xl mb-1">🖼</span>
) : (
<span className="text-xl mb-1">🔁</span>
)}
<span>{tab}</span>
</button>
))}
</div>
)}
{/* SUPERVISOR */}
{role === "supervisor" && (
<div className="text-lg font-semibold text-gray-700">
Supervisor Dashboard
</div>
)}
{/* USER */}
{role === "user" && (
<div className="text-lg font-semibold text-gray-700">
User Dashboard
</div>
)}
</div>
{/* === Kanan: Notifikasi + Profil === */}
<div className="flex items-center gap-4">
<button className="relative text-gray-600 hover:text-gray-800">
<Bell className="w-5 h-5" />
<span className="absolute -top-1 -right-1 bg-blue-500 text-white text-[10px] w-4 h-4 flex items-center justify-center rounded-full">
6
</span>
</button>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<button className="flex items-center gap-2 hover:bg-gray-100 px-2 py-1 rounded-md">
<Image
src="/non-user.png"
alt="User"
width={36}
height={36}
className="rounded-full border"
/>
<span className="text-gray-700 text-sm font-medium capitalize">
{role}
</span>
</button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end" className="w-40">
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={handleLogout}
className="cursor-pointer text-red-600"
>
<LogOut className="w-4 h-4 mr-2" />
Logout
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</div>
</div>
);
}

View File

@ -1,6 +1,7 @@
"use client"; "use client";
import { useState } from "react"; import { useState } from "react";
import Cookies from "js-cookie";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
@ -54,10 +55,10 @@ export default function Login() {
return; return;
} }
// ✅ Simpan data user ke localStorage agar bisa diakses di halaman lain // ✅ Simpan data user ke cookies agar bisa diakses dari mana saja
localStorage.setItem("userRole", foundUser.role); Cookies.set("userRole", foundUser.role, { expires: 1 }); // expires 1 day
// ✅ Redirect ke halaman sesuai role // ✅ Redirect sesuai role
switch (foundUser.role) { switch (foundUser.role) {
case "admin": case "admin":
router.push("/dashboard/admin"); router.push("/dashboard/admin");

View File

@ -0,0 +1,109 @@
import * as React from "react"
import { Slot } from "@radix-ui/react-slot"
import { ChevronRight, MoreHorizontal } from "lucide-react"
import { cn } from "@/lib/utils"
function Breadcrumb({ ...props }: React.ComponentProps<"nav">) {
return <nav aria-label="breadcrumb" data-slot="breadcrumb" {...props} />
}
function BreadcrumbList({ className, ...props }: React.ComponentProps<"ol">) {
return (
<ol
data-slot="breadcrumb-list"
className={cn(
"text-muted-foreground flex flex-wrap items-center gap-1.5 text-sm break-words sm:gap-2.5",
className
)}
{...props}
/>
)
}
function BreadcrumbItem({ className, ...props }: React.ComponentProps<"li">) {
return (
<li
data-slot="breadcrumb-item"
className={cn("inline-flex items-center gap-1.5", className)}
{...props}
/>
)
}
function BreadcrumbLink({
asChild,
className,
...props
}: React.ComponentProps<"a"> & {
asChild?: boolean
}) {
const Comp = asChild ? Slot : "a"
return (
<Comp
data-slot="breadcrumb-link"
className={cn("hover:text-foreground transition-colors", className)}
{...props}
/>
)
}
function BreadcrumbPage({ className, ...props }: React.ComponentProps<"span">) {
return (
<span
data-slot="breadcrumb-page"
role="link"
aria-disabled="true"
aria-current="page"
className={cn("text-foreground font-normal", className)}
{...props}
/>
)
}
function BreadcrumbSeparator({
children,
className,
...props
}: React.ComponentProps<"li">) {
return (
<li
data-slot="breadcrumb-separator"
role="presentation"
aria-hidden="true"
className={cn("[&>svg]:size-3.5", className)}
{...props}
>
{children ?? <ChevronRight />}
</li>
)
}
function BreadcrumbEllipsis({
className,
...props
}: React.ComponentProps<"span">) {
return (
<span
data-slot="breadcrumb-ellipsis"
role="presentation"
aria-hidden="true"
className={cn("flex size-9 items-center justify-center", className)}
{...props}
>
<MoreHorizontal className="size-4" />
<span className="sr-only">More</span>
</span>
)
}
export {
Breadcrumb,
BreadcrumbList,
BreadcrumbItem,
BreadcrumbLink,
BreadcrumbPage,
BreadcrumbSeparator,
BreadcrumbEllipsis,
}

View File

@ -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<typeof DropdownMenuPrimitive.Root>) {
return <DropdownMenuPrimitive.Root data-slot="dropdown-menu" {...props} />
}
function DropdownMenuPortal({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Portal>) {
return (
<DropdownMenuPrimitive.Portal data-slot="dropdown-menu-portal" {...props} />
)
}
function DropdownMenuTrigger({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Trigger>) {
return (
<DropdownMenuPrimitive.Trigger
data-slot="dropdown-menu-trigger"
{...props}
/>
)
}
function DropdownMenuContent({
className,
sideOffset = 4,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Content>) {
return (
<DropdownMenuPrimitive.Portal>
<DropdownMenuPrimitive.Content
data-slot="dropdown-menu-content"
sideOffset={sideOffset}
className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
className
)}
{...props}
/>
</DropdownMenuPrimitive.Portal>
)
}
function DropdownMenuGroup({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Group>) {
return (
<DropdownMenuPrimitive.Group data-slot="dropdown-menu-group" {...props} />
)
}
function DropdownMenuItem({
className,
inset,
variant = "default",
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Item> & {
inset?: boolean
variant?: "default" | "destructive"
}) {
return (
<DropdownMenuPrimitive.Item
data-slot="dropdown-menu-item"
data-inset={inset}
data-variant={variant}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
/>
)
}
function DropdownMenuCheckboxItem({
className,
children,
checked,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.CheckboxItem>) {
return (
<DropdownMenuPrimitive.CheckboxItem
data-slot="dropdown-menu-checkbox-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
checked={checked}
{...props}
>
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<CheckIcon className="size-4" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.CheckboxItem>
)
}
function DropdownMenuRadioGroup({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioGroup>) {
return (
<DropdownMenuPrimitive.RadioGroup
data-slot="dropdown-menu-radio-group"
{...props}
/>
)
}
function DropdownMenuRadioItem({
className,
children,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.RadioItem>) {
return (
<DropdownMenuPrimitive.RadioItem
data-slot="dropdown-menu-radio-item"
className={cn(
"focus:bg-accent focus:text-accent-foreground relative flex cursor-default items-center gap-2 rounded-sm py-1.5 pr-2 pl-8 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
>
<span className="pointer-events-none absolute left-2 flex size-3.5 items-center justify-center">
<DropdownMenuPrimitive.ItemIndicator>
<CircleIcon className="size-2 fill-current" />
</DropdownMenuPrimitive.ItemIndicator>
</span>
{children}
</DropdownMenuPrimitive.RadioItem>
)
}
function DropdownMenuLabel({
className,
inset,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Label> & {
inset?: boolean
}) {
return (
<DropdownMenuPrimitive.Label
data-slot="dropdown-menu-label"
data-inset={inset}
className={cn(
"px-2 py-1.5 text-sm font-medium data-[inset]:pl-8",
className
)}
{...props}
/>
)
}
function DropdownMenuSeparator({
className,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Separator>) {
return (
<DropdownMenuPrimitive.Separator
data-slot="dropdown-menu-separator"
className={cn("bg-border -mx-1 my-1 h-px", className)}
{...props}
/>
)
}
function DropdownMenuShortcut({
className,
...props
}: React.ComponentProps<"span">) {
return (
<span
data-slot="dropdown-menu-shortcut"
className={cn(
"text-muted-foreground ml-auto text-xs tracking-widest",
className
)}
{...props}
/>
)
}
function DropdownMenuSub({
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.Sub>) {
return <DropdownMenuPrimitive.Sub data-slot="dropdown-menu-sub" {...props} />
}
function DropdownMenuSubTrigger({
className,
inset,
children,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubTrigger> & {
inset?: boolean
}) {
return (
<DropdownMenuPrimitive.SubTrigger
data-slot="dropdown-menu-sub-trigger"
data-inset={inset}
className={cn(
"focus:bg-accent focus:text-accent-foreground data-[state=open]:bg-accent data-[state=open]:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
className
)}
{...props}
>
{children}
<ChevronRightIcon className="ml-auto size-4" />
</DropdownMenuPrimitive.SubTrigger>
)
}
function DropdownMenuSubContent({
className,
...props
}: React.ComponentProps<typeof DropdownMenuPrimitive.SubContent>) {
return (
<DropdownMenuPrimitive.SubContent
data-slot="dropdown-menu-sub-content"
className={cn(
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 z-50 min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-hidden rounded-md border p-1 shadow-lg",
className
)}
{...props}
/>
)
}
export {
DropdownMenu,
DropdownMenuPortal,
DropdownMenuTrigger,
DropdownMenuContent,
DropdownMenuGroup,
DropdownMenuLabel,
DropdownMenuItem,
DropdownMenuCheckboxItem,
DropdownMenuRadioGroup,
DropdownMenuRadioItem,
DropdownMenuSeparator,
DropdownMenuShortcut,
DropdownMenuSub,
DropdownMenuSubTrigger,
DropdownMenuSubContent,
}

100
package-lock.json generated
View File

@ -9,15 +9,18 @@
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.7", "@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-progress": "^1.1.8", "@radix-ui/react-progress": "^1.1.8",
"@radix-ui/react-radio-group": "^1.3.8", "@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tabs": "^1.1.13",
"@types/js-cookie": "^3.0.6",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"js-cookie": "^3.0.5",
"lucide-react": "^0.552.0", "lucide-react": "^0.552.0",
"next": "16.0.1", "next": "16.0.1",
"react": "19.2.0", "react": "19.2.0",
@ -860,6 +863,34 @@
} }
} }
}, },
"node_modules/@radix-ui/react-dropdown-menu": {
"version": "2.1.16",
"resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz",
"integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==",
"dependencies": {
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-id": "1.1.1",
"@radix-ui/react-menu": "2.1.16",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-use-controllable-state": "1.2.2"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-focus-guards": { "node_modules/@radix-ui/react-focus-guards": {
"version": "1.1.3", "version": "1.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz",
@ -937,6 +968,62 @@
} }
} }
}, },
"node_modules/@radix-ui/react-menu": {
"version": "2.1.16",
"resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz",
"integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==",
"dependencies": {
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-collection": "1.1.7",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-direction": "1.1.1",
"@radix-ui/react-dismissable-layer": "1.1.11",
"@radix-ui/react-focus-guards": "1.1.3",
"@radix-ui/react-focus-scope": "1.1.7",
"@radix-ui/react-id": "1.1.1",
"@radix-ui/react-popper": "1.2.8",
"@radix-ui/react-portal": "1.1.9",
"@radix-ui/react-presence": "1.1.5",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-roving-focus": "1.1.11",
"@radix-ui/react-slot": "1.2.3",
"@radix-ui/react-use-callback-ref": "1.1.1",
"aria-hidden": "^1.2.4",
"react-remove-scroll": "^2.6.3"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-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==",
"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-popover": { "node_modules/@radix-ui/react-popover": {
"version": "1.1.15", "version": "1.1.15",
"resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz", "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz",
@ -1725,6 +1812,11 @@
"tailwindcss": "4.1.16" "tailwindcss": "4.1.16"
} }
}, },
"node_modules/@types/js-cookie": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-3.0.6.tgz",
"integrity": "sha512-wkw9yd1kEXOPnvEeEV1Go1MmxtBJL0RR79aOTAApecWFVu7w0NNXNqhcWgvw2YgZDYadliXkl14pa3WXw5jlCQ=="
},
"node_modules/@types/node": { "node_modules/@types/node": {
"version": "20.19.24", "version": "20.19.24",
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.24.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.24.tgz",
@ -1896,6 +1988,14 @@
"jiti": "lib/jiti-cli.mjs" "jiti": "lib/jiti-cli.mjs"
} }
}, },
"node_modules/js-cookie": {
"version": "3.0.5",
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz",
"integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==",
"engines": {
"node": ">=14"
}
},
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",

View File

@ -9,15 +9,18 @@
}, },
"dependencies": { "dependencies": {
"@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dialog": "^1.1.15",
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.7", "@radix-ui/react-label": "^2.1.7",
"@radix-ui/react-popover": "^1.1.15", "@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-progress": "^1.1.8", "@radix-ui/react-progress": "^1.1.8",
"@radix-ui/react-radio-group": "^1.3.8", "@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-slot": "^1.2.4",
"@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tabs": "^1.1.13",
"@types/js-cookie": "^3.0.6",
"class-variance-authority": "^0.7.1", "class-variance-authority": "^0.7.1",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"date-fns": "^4.1.0", "date-fns": "^4.1.0",
"js-cookie": "^3.0.5",
"lucide-react": "^0.552.0", "lucide-react": "^0.552.0",
"next": "16.0.1", "next": "16.0.1",
"react": "19.2.0", "react": "19.2.0",