'use client' export const runtime = 'edge' import { useState, useEffect } from 'react' import { useParams, useRouter } from 'next/navigation' import { Button } from "@/registry/new-york/ui/button" import { useToast } from "@/registry/new-york/hooks/use-toast" import { Icons } from "@/components/icons" import { NavigationItem, NavigationSubItem } from '@/types/navigation' import { Input } from "@/registry/new-york/ui/input" import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger, DialogDescription, DialogFooter, } from "@/registry/new-york/ui/dialog" import { AddItemForm } from '../../components/AddItemForm' import { DragDropContext, Droppable, Draggable, DropResult } from '@hello-pangea/dnd'; import { Skeleton } from "@/registry/new-york/ui/skeleton" import { Select, SelectTrigger, SelectValue, SelectContent, SelectItem } from "@/registry/new-york/ui/select" import { Badge } from "@/registry/new-york/ui/badge" interface EditingItem { index: number item: NavigationSubItem } function LoadingSkeleton() { return (
{[1, 2, 3].map((i) => (
))}
) } export default function ItemsPage() { const params = useParams<{ id: string }>() const router = useRouter() const { toast } = useToast() const [navigation, setNavigation] = useState(null) const [searchQuery, setSearchQuery] = useState("") const [enabledFilter, setEnabledFilter] = useState<'all' | 'enabled' | 'disabled'>('all') const [editingItem, setEditingItem] = useState(null) const [deletingItem, setDeletingItem] = useState(null) const [isLoading, setIsLoading] = useState(true) const [isAddDialogOpen, setIsAddDialogOpen] = useState(false) useEffect(() => { if (!params?.id) { router.push('/admin/navigation') return } fetchNavigation() }, [params?.id, router]) const fetchNavigation = async () => { setIsLoading(true) try { if (!params?.id) { throw new Error('Navigation ID not found') } const navigationId = params?.id if (!navigationId) { throw new Error('Navigation ID is missing') } const response = await fetch(`/api/navigation/${navigationId}`) if (!response.ok) throw new Error('Failed to fetch') const data = await response.json() setNavigation(data) } catch (error) { toast({ title: "错误", description: "获取数据失败", variant: "destructive" }) } finally { setIsLoading(false) } } const addItem = async (values: NavigationSubItem) => { try { if (!params?.id || !navigation) { throw new Error('Navigation ID or data not found') } const navigationId = params?.id if (!navigationId) { throw new Error('Navigation ID is missing') } const response = await fetch(`/api/navigation/${navigationId}/items`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(values) }) if (!response.ok) throw new Error('Failed to save') await fetchNavigation() toast({ title: "成功", description: "添加成功" }) setIsAddDialogOpen(false) } catch (error) { toast({ title: "错误", description: "保存失败", variant: "destructive" }) } finally { setIsAddDialogOpen(false) } } const updateItem = async (index: number, values: NavigationSubItem) => { try { if (!params?.id || !navigation) { throw new Error('Navigation ID or data not found') } const navigationId = params?.id if (!navigationId) { throw new Error('Navigation ID is missing') } const items = [...(navigation.items || [])] items[index] = values const updatedNavigation: NavigationItem = { id: navigation.id || navigationId, title: navigation.title || '', description: navigation.description || '', items, subCategories: navigation.subCategories || [] } const response = await fetch(`/api/navigation/${navigationId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(updatedNavigation) }) if (!response.ok) throw new Error('Failed to save') setNavigation(updatedNavigation) toast({ title: "成功", description: "保存成功" }) setEditingItem(null) } catch (error) { toast({ title: "错误", description: "保存失败", variant: "destructive" }) } } const deleteItem = async (index: number) => { try { if (!params?.id) { throw new Error('Navigation ID not found') } const navigationId = params?.id if (!navigationId) { throw new Error('Navigation ID is missing') } const response = await fetch(`/api/navigation/${navigationId}/items`, { method: 'DELETE', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ index }) }) if (!response.ok) throw new Error('Failed to delete') await fetchNavigation() toast({ title: "成功", description: "删除成功" }) setDeletingItem(null) } catch (error) { toast({ title: "错误", description: "删除失败", variant: "destructive" }) } } const onDragEnd = async (result: DropResult) => { if (!result.destination) return const items = Array.from(navigation?.items || []) const [reorderedItem] = items.splice(result.source.index, 1) items.splice(result.destination.index, 0, reorderedItem) try { const navigationId = params?.id if (!navigationId) { throw new Error('Navigation ID is missing') } const response = await fetch(`/api/navigation/${navigationId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...navigation, items }) }) if (!response.ok) throw new Error('Failed to save order') await fetchNavigation() // Refresh data toast({ title: "成功", description: "项目顺序已更新" }) } catch (error) { toast({ title: "错误", description: "保存顺序失败", variant: "destructive" }) } } const moveToTop = async (index: number) => { if (index > 0) { const items = Array.from(navigation?.items || []) const [reorderedItem] = items.splice(index, 1) items.unshift(reorderedItem) try { const navigationId = params?.id if (!navigationId) { throw new Error('Navigation ID is missing') } const response = await fetch(`/api/navigation/${navigationId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...navigation, items }) }) if (!response.ok) throw new Error('Failed to save order') await fetchNavigation() // Refresh data toast({ title: "成功", description: "项目顺序已更新" }) } catch (error) { toast({ title: "错误", description: "保存顺序失败", variant: "destructive" }) } } } const moveToBottom = async (index: number) => { if (!navigation?.items) return if (index < navigation.items.length - 1) { const items = Array.from(navigation?.items || []) const [reorderedItem] = items.splice(index, 1) items.push(reorderedItem) try { const navigationId = params?.id if (!navigationId) { throw new Error('Navigation ID is missing') } const response = await fetch(`/api/navigation/${navigationId}`, { method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ ...navigation, items }) }) if (!response.ok) throw new Error('Failed to save order') await fetchNavigation() // Refresh data toast({ title: "成功", description: "项目顺序已更新" }) } catch (error) { toast({ title: "错误", description: "保存顺序失败", variant: "destructive" }) } } } const filteredItems = navigation?.items?.filter(item => { const matchesSearch = item.title.toLowerCase().includes(searchQuery.toLowerCase()) || item.href.toLowerCase().includes(searchQuery.toLowerCase()) || item.description?.toLowerCase().includes(searchQuery.toLowerCase()) const matchesEnabled = enabledFilter === "all" ? true : enabledFilter === "enabled" ? item.enabled : enabledFilter === "disabled" ? !item.enabled : true return matchesSearch && matchesEnabled }) || [] if (isLoading) { return } if (!navigation) { return (

导航不存在

) } return (
{navigation?.title}
setSearchQuery(e.target.value)} className="pl-8" /> {searchQuery && ( )}
添加站点 { await addItem(values) setIsAddDialogOpen(false) }} onCancel={() => setIsAddDialogOpen(false)} />
{navigation?.items && navigation.items.length > 0 ? ( {(provided) => (
{filteredItems.map((item, index) => ( {(provided) => (
{item.icon ? ( {item.title} ) : ( )}
{item.title} {!item.enabled && ( 已禁用 )}
{item.description && (
{item.description}
)}
)}
))} {filteredItems.length === 0 && (
{navigation?.items?.length === 0 ? (

暂无站点

) : (

未找到匹配的站点

)}
)} {provided.placeholder}
)}
) : (
暂无站点
)} !open && setEditingItem(null)}> 编辑站点 { if (editingItem) { return updateItem(editingItem.index, values) } return Promise.resolve() }} onCancel={() => setEditingItem(null)} /> !open && setDeletingItem(null)}> 删除确认 确定要删除站点 “{deletingItem?.item.title}” 吗?此操作无法撤销。
) }