Files
Krow-workspace/frontend-web-free/src/components/events/ShiftRolesTable.jsx
2025-12-04 18:02:28 -05:00

323 lines
11 KiB
JavaScript

import React from "react";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Plus, Minus, Pencil, Trash2, Search } from "lucide-react";
import {
Command,
CommandEmpty,
CommandGroup,
CommandInput,
CommandItem,
} from "@/components/ui/command";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
const ROLES = [
"Front Desk",
"Finance",
"Hospitality",
"Recruiter",
"Server",
"Bartender",
"Cook",
"Dishwasher",
"Security",
"Janitor"
];
const DEPARTMENTS = [
"Accounting",
"Operations",
"Sales",
"HR",
"Finance",
"IT",
"Marketing",
"Customer Service",
"Logistics"
];
const UNIFORMS = ["Type 1", "Type 2", "Type 3", "Casual", "Formal"];
const TIME_OPTIONS = [];
for (let h = 1; h <= 12; h++) {
for (let m of ['00', '30']) {
TIME_OPTIONS.push(`${h.toString().padStart(2, '0')}:${m} AM`);
}
}
for (let h = 1; h <= 12; h++) {
for (let m of ['00', '30']) {
TIME_OPTIONS.push(`${h.toString().padStart(2, '0')}:${m} PM`);
}
}
export default function ShiftRolesTable({ roles, onChange }) {
const handleAddRole = () => {
onChange([...roles, {
role: "",
department: "",
count: 1,
start_time: "12:00 PM",
end_time: "05:00 PM",
hours: 5,
uniform: "Type 1",
break_minutes: 30,
cost_per_hour: 45,
total_value: 0
}]);
};
const handleDeleteRole = (index) => {
onChange(roles.filter((_, i) => i !== index));
};
const handleRoleChange = (index, field, value) => {
const newRoles = [...roles];
newRoles[index] = { ...newRoles[index], [field]: value };
// Calculate hours if times changed
if (field === 'start_time' || field === 'end_time') {
const start = newRoles[index].start_time;
const end = newRoles[index].end_time;
const hours = calculateHours(start, end);
newRoles[index].hours = hours;
}
// Calculate total value
const count = newRoles[index].count || 0;
const hours = newRoles[index].hours || 0;
const cost = newRoles[index].cost_per_hour || 0;
newRoles[index].total_value = count * hours * cost;
onChange(newRoles);
};
const calculateHours = (start, end) => {
// Simple calculation - in production, use proper time library
const startHour = parseInt(start.split(':')[0]) + (start.includes('PM') && !start.startsWith('12') ? 12 : 0);
const endHour = parseInt(end.split(':')[0]) + (end.includes('PM') && !end.startsWith('12') ? 12 : 0);
return Math.max(0, endHour - startHour);
};
return (
<div className="space-y-4">
<div className="border border-slate-200 rounded-lg overflow-hidden">
<Table>
<TableHeader>
<TableRow className="bg-slate-50">
<TableHead className="w-12 text-center">#</TableHead>
<TableHead className="min-w-[150px]">Role</TableHead>
<TableHead className="min-w-[130px]">Department</TableHead>
<TableHead className="w-24 text-center">Count</TableHead>
<TableHead className="min-w-[120px]">Start Date</TableHead>
<TableHead className="min-w-[120px]">End Date</TableHead>
<TableHead className="w-20 text-center">Hours</TableHead>
<TableHead className="min-w-[100px]">Uniform</TableHead>
<TableHead className="w-24">Break</TableHead>
<TableHead className="w-20">Cost</TableHead>
<TableHead className="w-28 text-right">Value</TableHead>
<TableHead className="w-24 text-center">Actions</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{roles.map((role, index) => (
<TableRow key={index} className="hover:bg-slate-50">
<TableCell className="text-center font-medium text-slate-600">{index + 1}</TableCell>
{/* Role */}
<TableCell>
<Popover>
<PopoverTrigger asChild>
<Button variant="outline" className="w-full justify-between">
{role.role || "Role"}
<Search className="w-4 h-4 ml-2 text-slate-400" />
</Button>
</PopoverTrigger>
<PopoverContent className="w-64 p-0" align="start">
<Command>
<CommandInput placeholder="Search role..." />
<CommandEmpty>No role found.</CommandEmpty>
<CommandGroup className="max-h-64 overflow-auto">
{ROLES.map((r) => (
<CommandItem
key={r}
onSelect={() => handleRoleChange(index, 'role', r)}
>
{r}
</CommandItem>
))}
</CommandGroup>
</Command>
</PopoverContent>
</Popover>
</TableCell>
{/* Department */}
<TableCell>
<Select
value={role.department}
onValueChange={(value) => handleRoleChange(index, 'department', value)}
>
<SelectTrigger>
<SelectValue placeholder="Department" />
</SelectTrigger>
<SelectContent>
{DEPARTMENTS.map(dept => (
<SelectItem key={dept} value={dept}>{dept}</SelectItem>
))}
</SelectContent>
</Select>
</TableCell>
{/* Count */}
<TableCell>
<div className="flex items-center gap-1">
<Button
type="button"
variant="outline"
size="icon"
className="h-8 w-8"
onClick={() => handleRoleChange(index, 'count', Math.max(1, role.count - 1))}
>
<Minus className="w-3 h-3" />
</Button>
<Input
type="number"
value={role.count}
onChange={(e) => handleRoleChange(index, 'count', parseInt(e.target.value) || 1)}
className="w-12 h-8 text-center p-0"
/>
<Button
type="button"
variant="outline"
size="icon"
className="h-8 w-8"
onClick={() => handleRoleChange(index, 'count', role.count + 1)}
>
<Plus className="w-3 h-3" />
</Button>
</div>
</TableCell>
{/* Start Time */}
<TableCell>
<Select
value={role.start_time}
onValueChange={(value) => handleRoleChange(index, 'start_time', value)}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent className="max-h-64">
{TIME_OPTIONS.map(time => (
<SelectItem key={time} value={time}>{time}</SelectItem>
))}
</SelectContent>
</Select>
</TableCell>
{/* End Time */}
<TableCell>
<Select
value={role.end_time}
onValueChange={(value) => handleRoleChange(index, 'end_time', value)}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent className="max-h-64">
{TIME_OPTIONS.map(time => (
<SelectItem key={time} value={time}>{time}</SelectItem>
))}
</SelectContent>
</Select>
</TableCell>
{/* Hours */}
<TableCell className="text-center font-semibold">{role.hours}</TableCell>
{/* Uniform */}
<TableCell>
<Select
value={role.uniform}
onValueChange={(value) => handleRoleChange(index, 'uniform', value)}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
{UNIFORMS.map(u => (
<SelectItem key={u} value={u}>{u}</SelectItem>
))}
</SelectContent>
</Select>
</TableCell>
{/* Break */}
<TableCell>
<Input
type="number"
value={role.break_minutes}
onChange={(e) => handleRoleChange(index, 'break_minutes', parseInt(e.target.value) || 0)}
className="w-20 text-center"
placeholder="30"
/>
</TableCell>
{/* Cost */}
<TableCell>
<Input
type="number"
value={role.cost_per_hour}
onChange={(e) => handleRoleChange(index, 'cost_per_hour', parseFloat(e.target.value) || 0)}
className="w-20 text-center"
placeholder="45"
/>
</TableCell>
{/* Value */}
<TableCell className="text-right font-bold text-[#0A39DF]">
${role.total_value?.toFixed(2) || '0.00'}
</TableCell>
{/* Actions */}
<TableCell>
<div className="flex items-center justify-center gap-1">
<Button variant="ghost" size="icon" className="h-8 w-8">
<Pencil className="w-4 h-4 text-slate-600" />
</Button>
<Button
type="button"
variant="ghost"
size="icon"
className="h-8 w-8"
onClick={() => handleDeleteRole(index)}
>
<Trash2 className="w-4 h-4 text-red-600" />
</Button>
</div>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
{/* Add Role Button */}
<Button
type="button"
variant="outline"
onClick={handleAddRole}
className="border-dashed border-slate-300 hover:border-[#0A39DF] hover:text-[#0A39DF]"
>
<Plus className="w-4 h-4 mr-2" />
Add Role
</Button>
</div>
);
}