application [cat-admin-web] view page [pages/queue-management] development

This commit is contained in:
NVWA Code Agent
2025-12-11 16:46:18 +00:00
parent ae09db28cb
commit dd17bad4c4

View File

@@ -0,0 +1,277 @@
import React, { useState, useEffect } from 'react';
import { entities } from '@/lib/nvwa';
import { Button } from '@/components/ui/button';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Switch } from '@/components/ui/switch';
import { Badge } from '@/components/ui/badge';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
interface Cat {
id: number;
name: string;
age: number;
gender: string;
grade: string;
type: string;
is_available: boolean;
description?: string;
image_ids: number[];
created_at: string;
updated_at: string;
}
interface Reservation {
id: number;
user_id: string;
cat_id?: number;
deposit: string;
status: string;
queue_order?: number;
created_at: string;
updated_at: string;
user: { name: string };
cat?: { name: string };
}
interface QueueRules {
maxQueueLength: number;
defaultDeposit: number;
ruleType: 'fifo' | 'priority';
}
export default function QueueManagement() {
const [cats, setCats] = useState<Cat[]>([]);
const [reservations, setReservations] = useState<Reservation[]>([]);
const [rules, setRules] = useState<QueueRules>({
maxQueueLength: 10,
defaultDeposit: 100,
ruleType: 'fifo'
});
useEffect(() => {
fetchCats();
fetchReservations();
}, []);
const fetchCats = async () => {
const { data, error } = await entities.from('cat').select('*');
if (error) {
console.error(error);
return;
}
setCats(data || []);
};
const fetchReservations = async () => {
const { data, error } = await entities
.from('reservation')
.select('*, user!inner(name), cat(name)')
.eq('status', 'queuing')
.order('queue_order');
if (error) {
console.error(error);
return;
}
setReservations(data || []);
};
const toggleQueuing = async (cat: Cat) => {
// Assuming enabling queuing sets is_available to true
const { error } = await entities.from('cat').update({ is_available: !cat.is_available }).eq('id', cat.id);
if (error) {
console.error(error);
return;
}
setCats(cats.map(c => c.id === cat.id ? { ...c, is_available: !c.is_available } : c));
};
const updateRules = (key: keyof QueueRules, value: any) => {
setRules(prev => ({ ...prev, [key]: value }));
};
const moveQueue = async (reservation: Reservation, direction: 'up' | 'down') => {
const currentIndex = reservations.findIndex(r => r.id === reservation.id);
if (direction === 'up' && currentIndex > 0) {
const prev = reservations[currentIndex - 1];
await swapOrder(reservation, prev);
} else if (direction === 'down' && currentIndex < reservations.length - 1) {
const next = reservations[currentIndex + 1];
await swapOrder(reservation, next);
}
fetchReservations();
};
const swapOrder = async (res1: Reservation, res2: Reservation) => {
const { error: error1 } = await entities.from('reservation').update({ queue_order: res2.queue_order }).eq('id', res1.id);
const { error: error2 } = await entities.from('reservation').update({ queue_order: res1.queue_order }).eq('id', res2.id);
if (error1 || error2) {
console.error(error1 || error2);
}
};
const changeStatus = async (reservation: Reservation, newStatus: string) => {
const { error } = await entities.from('reservation').update({ status: newStatus }).eq('id', reservation.id);
if (error) {
console.error(error);
return;
}
fetchReservations();
};
return (
<div className="p-6">
<h1 className="text-2xl font-bold mb-6"></h1>
<Tabs defaultValue="enable" className="w-full">
<TabsList className="grid w-full grid-cols-3">
<TabsTrigger value="enable"></TabsTrigger>
<TabsTrigger value="rules"></TabsTrigger>
<TabsTrigger value="manage"></TabsTrigger>
</TabsList>
<TabsContent value="enable">
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{cats.map(cat => (
<TableRow key={cat.id}>
<TableCell>{cat.name}</TableCell>
<TableCell>{cat.age} </TableCell>
<TableCell>
<Badge variant={cat.gender === 'male' ? 'default' : 'secondary'}>
{cat.gender === 'male' ? '雄' : '雌'}
</Badge>
</TableCell>
<TableCell>
<Badge variant="outline">{cat.grade}</Badge>
</TableCell>
<TableCell>
<Badge variant="outline">
{cat.type === 'breeding_male' ? '种公' : cat.type === 'breeding_female' ? '种母' : '幼猫'}
</Badge>
</TableCell>
<TableCell>
<Switch
checked={cat.is_available}
onCheckedChange={() => toggleQueuing(cat)}
/>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="rules">
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div>
<Label htmlFor="maxQueue"></Label>
<Input
id="maxQueue"
type="number"
value={rules.maxQueueLength}
onChange={(e) => updateRules('maxQueueLength', parseInt(e.target.value))}
/>
</div>
<div>
<Label htmlFor="deposit"></Label>
<Input
id="deposit"
type="number"
value={rules.defaultDeposit}
onChange={(e) => updateRules('defaultDeposit', parseFloat(e.target.value))}
/>
</div>
<div>
<Label></Label>
<Select
value={rules.ruleType}
onValueChange={(value: 'fifo' | 'priority') => updateRules('ruleType', value)}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="fifo"></SelectItem>
<SelectItem value="priority"></SelectItem>
</SelectContent>
</Select>
</div>
</CardContent>
</Card>
</TabsContent>
<TabsContent value="manage">
<Card>
<CardHeader>
<CardTitle></CardTitle>
</CardHeader>
<CardContent>
<Table>
<TableHeader>
<TableRow>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
<TableHead></TableHead>
</TableRow>
</TableHeader>
<TableBody>
{reservations.map((res, index) => (
<TableRow key={res.id}>
<TableCell>{res.queue_order}</TableCell>
<TableCell>{res.user.name}</TableCell>
<TableCell>{res.cat?.name || 'N/A'}</TableCell>
<TableCell>{res.deposit}</TableCell>
<TableCell>
<Badge variant="outline">{res.status}</Badge>
</TableCell>
<TableCell>
<div className="flex space-x-2">
<Button size="sm" onClick={() => moveQueue(res, 'up')} disabled={index === 0}>
</Button>
<Button size="sm" onClick={() => moveQueue(res, 'down')} disabled={index === reservations.length - 1}>
</Button>
<Button size="sm" onClick={() => changeStatus(res, 'reserved')}>
</Button>
<Button size="sm" variant="destructive" onClick={() => changeStatus(res, 'cancelled')}>
</Button>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</CardContent>
</Card>
</TabsContent>
</Tabs>
</div>
);
}