summaryrefslogtreecommitdiff
path: root/lib/menu-v2/components/unassigned-menus-panel.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'lib/menu-v2/components/unassigned-menus-panel.tsx')
-rw-r--r--lib/menu-v2/components/unassigned-menus-panel.tsx178
1 files changed, 178 insertions, 0 deletions
diff --git a/lib/menu-v2/components/unassigned-menus-panel.tsx b/lib/menu-v2/components/unassigned-menus-panel.tsx
new file mode 100644
index 00000000..2c914f2a
--- /dev/null
+++ b/lib/menu-v2/components/unassigned-menus-panel.tsx
@@ -0,0 +1,178 @@
+"use client";
+
+import { useState } from "react";
+import { cn } from "@/lib/utils";
+import { Button } from "@/components/ui/button";
+import { Input } from "@/components/ui/input";
+import { Badge } from "@/components/ui/badge";
+import { ScrollArea } from "@/components/ui/scroll-area";
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
+import { Search, FileQuestion, ArrowRight, Pencil, Link } from "lucide-react";
+import type { MenuTreeNode } from "../types";
+
+interface UnassignedMenusPanelProps {
+ menus: MenuTreeNode[];
+ onAssign: (menuId: number, groupId: number) => void;
+ onActivateAsTopLevel: (menuId: number) => void;
+ onEdit: (menu: MenuTreeNode) => void;
+ availableGroups: { id: number; title: string; parentTitle?: string }[];
+}
+
+export function UnassignedMenusPanel({
+ menus,
+ onAssign,
+ onActivateAsTopLevel,
+ onEdit,
+ availableGroups,
+}: UnassignedMenusPanelProps) {
+ const [searchTerm, setSearchTerm] = useState("");
+ const [selectedMenu, setSelectedMenu] = useState<number | null>(null);
+
+ const filteredMenus = menus.filter(
+ (menu) =>
+ menu.titleKo.toLowerCase().includes(searchTerm.toLowerCase()) ||
+ menu.menuPath?.toLowerCase().includes(searchTerm.toLowerCase())
+ );
+
+ return (
+ <Card className="h-full">
+ <CardHeader className="pb-3">
+ <CardTitle className="text-base flex items-center gap-2">
+ <FileQuestion className="h-4 w-4" />
+ Unassigned Menus ({menus.length})
+ </CardTitle>
+ <CardDescription>
+ Assign to a group or activate as a top-level link.
+ </CardDescription>
+ </CardHeader>
+ <CardContent className="space-y-3">
+ {/* Search */}
+ <div className="relative">
+ <Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
+ <Input
+ placeholder="Search..."
+ value={searchTerm}
+ onChange={(e) => setSearchTerm(e.target.value)}
+ className="pl-8"
+ />
+ </div>
+
+ {/* Menu List */}
+ <ScrollArea className="h-[400px]">
+ <div className="space-y-2">
+ {filteredMenus.length === 0 ? (
+ <p className="text-sm text-muted-foreground text-center py-4">
+ {searchTerm ? "No results found." : "No unassigned menus."}
+ </p>
+ ) : (
+ filteredMenus.map((menu) => (
+ <div
+ key={menu.id}
+ className={cn(
+ "p-3 rounded-md border bg-background hover:bg-accent/50 transition-colors",
+ selectedMenu === menu.id && "ring-2 ring-primary"
+ )}
+ >
+ <div className="flex items-start justify-between gap-2">
+ <div className="flex-1 min-w-0">
+ <div className="flex items-center gap-2">
+ <span className="font-medium text-sm">{menu.titleKo}</span>
+ <Badge variant="secondary" className="text-xs">
+ Inactive
+ </Badge>
+ </div>
+ <p className="text-xs text-muted-foreground truncate mt-1">
+ {menu.menuPath}
+ </p>
+ </div>
+ <Button
+ variant="ghost"
+ size="icon"
+ className="h-7 w-7 shrink-0"
+ onClick={() => onEdit(menu)}
+ >
+ <Pencil className="h-3.5 w-3.5" />
+ </Button>
+ </div>
+
+ {/* Group Selection (expanded) */}
+ {selectedMenu === menu.id ? (
+ <div className="mt-3 pt-3 border-t space-y-2">
+ {/* Activate as Top-Level */}
+ <div>
+ <p className="text-xs text-muted-foreground mb-2">
+ Activate as top-level link:
+ </p>
+ <Button
+ variant="default"
+ size="sm"
+ className="text-xs h-7"
+ onClick={() => {
+ onActivateAsTopLevel(menu.id);
+ setSelectedMenu(null);
+ }}
+ >
+ <Link className="mr-1 h-3 w-3" />
+ Activate as Top-Level
+ </Button>
+ </div>
+
+ {/* Assign to Group */}
+ {availableGroups.length > 0 && (
+ <div>
+ <p className="text-xs text-muted-foreground mb-2">
+ Or assign to group:
+ </p>
+ <div className="flex flex-wrap gap-1">
+ {availableGroups.map((group) => (
+ <Button
+ key={group.id}
+ variant="outline"
+ size="sm"
+ className="text-xs h-7"
+ onClick={() => {
+ onAssign(menu.id, group.id);
+ setSelectedMenu(null);
+ }}
+ >
+ {group.parentTitle && (
+ <span className="text-muted-foreground mr-1">
+ {group.parentTitle} &gt;
+ </span>
+ )}
+ {group.title}
+ <ArrowRight className="ml-1 h-3 w-3" />
+ </Button>
+ ))}
+ </div>
+ </div>
+ )}
+
+ <Button
+ variant="ghost"
+ size="sm"
+ className="text-xs"
+ onClick={() => setSelectedMenu(null)}
+ >
+ Cancel
+ </Button>
+ </div>
+ ) : (
+ <Button
+ variant="ghost"
+ size="sm"
+ className="mt-2 text-xs w-full"
+ onClick={() => setSelectedMenu(menu.id)}
+ >
+ Assign / Activate
+ </Button>
+ )}
+ </div>
+ ))
+ )}
+ </div>
+ </ScrollArea>
+ </CardContent>
+ </Card>
+ );
+}