diff options
Diffstat (limited to 'lib/menu-v2/components/unassigned-menus-panel.tsx')
| -rw-r--r-- | lib/menu-v2/components/unassigned-menus-panel.tsx | 178 |
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} > + </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> + ); +} |
