theming, date, time localisation, additional features, seeding initial cleanup

This commit is contained in:
mikicvi
2025-09-26 21:12:59 +01:00
parent b89d91ade2
commit 22c462c61c
43 changed files with 2647 additions and 550 deletions
+81 -73
View File
@@ -10,6 +10,7 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from
import { Switch } from '@/components/ui/switch';
import { Plus, Edit, Trash2, Clock } from 'lucide-react';
import { useToast } from '@/hooks/use-toast';
import { getWeekDays } from '@/lib/utils';
interface TimeSlot {
id: string;
@@ -21,7 +22,9 @@ interface TimeSlot {
updatedAt: string;
}
const DAYS = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
// Use Irish week order (Monday first)
const DAYS = getWeekDays().map((day) => day.label);
const IRISH_DAY_ORDER = [1, 2, 3, 4, 5, 6, 0]; // Monday-Sunday in JS getDay() values
export function AdminTimeSlotManagement() {
const [timeSlots, setTimeSlots] = useState<TimeSlot[]>([]);
@@ -29,7 +32,7 @@ export function AdminTimeSlotManagement() {
const [showDialog, setShowDialog] = useState(false);
const [editingSlot, setEditingSlot] = useState<TimeSlot | null>(null);
const [formData, setFormData] = useState({
dayOfWeek: 0,
dayOfWeek: 1, // Default to Monday (Irish standard)
startTime: '',
endTime: '',
isActive: true,
@@ -208,7 +211,7 @@ export function AdminTimeSlotManagement() {
const resetForm = () => {
setEditingSlot(null);
setFormData({
dayOfWeek: 0,
dayOfWeek: 1, // Default to Monday (Irish standard)
startTime: '',
endTime: '',
isActive: true,
@@ -255,9 +258,9 @@ export function AdminTimeSlotManagement() {
<SelectValue placeholder='Select day' />
</SelectTrigger>
<SelectContent>
{DAYS.map((day, index) => (
<SelectItem key={index} value={index.toString()}>
{day}
{IRISH_DAY_ORDER.map((jsDayOfWeek, displayIndex) => (
<SelectItem key={jsDayOfWeek} value={jsDayOfWeek.toString()}>
{DAYS[displayIndex]}
</SelectItem>
))}
</SelectContent>
@@ -309,75 +312,80 @@ export function AdminTimeSlotManagement() {
<div className='text-center py-4'>Loading time slots...</div>
) : (
<div className='space-y-6'>
{DAYS.map((day, dayIndex) => (
<div key={dayIndex} className='space-y-2'>
<div className='flex justify-between items-center'>
<h3 className='font-semibold text-lg'>{day}</h3>
{groupedTimeSlots[dayIndex]?.length > 0 && (
<Button
size='sm'
variant='outline'
onClick={() => handleWipeDay(dayIndex)}
className='text-red-600 hover:text-red-700 hover:bg-red-50'
disabled={loading}
>
<Trash2 className='h-4 w-4 mr-1' />
Wipe All
</Button>
{IRISH_DAY_ORDER.map((jsDayOfWeek, displayIndex) => {
const dayName = DAYS[displayIndex];
return (
<div key={jsDayOfWeek} className='space-y-2'>
<div className='flex justify-between items-center'>
<h3 className='font-semibold text-lg'>{dayName}</h3>
{groupedTimeSlots[jsDayOfWeek]?.length > 0 && (
<Button
size='sm'
variant='outline'
onClick={() => handleWipeDay(jsDayOfWeek)}
className='text-destructive hover:text-destructive/80 hover:bg-destructive/10'
disabled={loading}
>
<Trash2 className='h-4 w-4 mr-1' />
Wipe All
</Button>
)}
</div>
{groupedTimeSlots[jsDayOfWeek]?.length > 0 ? (
<div className='grid gap-2'>
{groupedTimeSlots[jsDayOfWeek]
.sort((a, b) => a.startTime.localeCompare(b.startTime))
.map((slot) => (
<div
key={slot.id}
className={`flex items-center justify-between p-3 border rounded-lg ${
slot.isActive
? 'bg-green-50 border-green-200 dark:bg-green-950 dark:border-green-800'
: 'bg-muted border-border'
}`}
>
<div className='flex items-center space-x-3'>
<div className='font-medium'>
{slot.startTime} - {slot.endTime}
</div>
<div
className={`px-2 py-1 rounded-full text-xs ${
slot.isActive
? 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200'
: 'bg-muted text-muted-foreground'
}`}
>
{slot.isActive ? 'Active' : 'Inactive'}
</div>
</div>
<div className='flex space-x-2'>
<Button
size='sm'
variant='outline'
onClick={() => handleEdit(slot)}
>
<Edit className='h-4 w-4' />
</Button>
<Button
size='sm'
variant='outline'
onClick={() => handleDelete(slot.id)}
className='text-destructive hover:text-destructive/80'
>
<Trash2 className='h-4 w-4' />
</Button>
</div>
</div>
))}
</div>
) : (
<p className='text-muted-foreground italic'>
No time slots configured for {dayName}
</p>
)}
</div>
{groupedTimeSlots[dayIndex]?.length > 0 ? (
<div className='grid gap-2'>
{groupedTimeSlots[dayIndex]
.sort((a, b) => a.startTime.localeCompare(b.startTime))
.map((slot) => (
<div
key={slot.id}
className={`flex items-center justify-between p-3 border rounded-lg ${
slot.isActive
? 'bg-green-50 border-green-200'
: 'bg-gray-50 border-gray-200'
}`}
>
<div className='flex items-center space-x-3'>
<div className='font-medium'>
{slot.startTime} - {slot.endTime}
</div>
<div
className={`px-2 py-1 rounded-full text-xs ${
slot.isActive
? 'bg-green-100 text-green-800'
: 'bg-gray-100 text-gray-800'
}`}
>
{slot.isActive ? 'Active' : 'Inactive'}
</div>
</div>
<div className='flex space-x-2'>
<Button
size='sm'
variant='outline'
onClick={() => handleEdit(slot)}
>
<Edit className='h-4 w-4' />
</Button>
<Button
size='sm'
variant='outline'
onClick={() => handleDelete(slot.id)}
className='text-red-600 hover:text-red-700'
>
<Trash2 className='h-4 w-4' />
</Button>
</div>
</div>
))}
</div>
) : (
<p className='text-gray-500 italic'>No time slots configured for {day}</p>
)}
</div>
))}
);
})}
</div>
)}
</CardContent>