Files
Krow-workspace/frontend-web/src/components/events/ShiftCard.jsx
2025-11-18 21:32:16 -05:00

160 lines
6.5 KiB
JavaScript

import React, { useState } from "react";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { Clock, MapPin, Users, DollarSign, UserPlus } from "lucide-react";
import SmartAssignModal from "./SmartAssignModal";
import AssignedStaffManager from "./AssignedStaffManager";
const convertTo12Hour = (time24) => {
if (!time24 || time24 === "—") return time24;
try {
const parts = time24.split(':');
if (!parts || parts.length < 2) return time24;
const hours = parseInt(parts[0], 10);
const minutes = parseInt(parts[1], 10);
if (isNaN(hours) || isNaN(minutes)) return time24;
const period = hours >= 12 ? 'PM' : 'AM';
const hours12 = hours % 12 || 12;
const minutesStr = minutes.toString().padStart(2, '0');
return `${hours12}:${minutesStr} ${period}`;
} catch (error) {
console.error('Error converting time:', error);
return time24;
}
};
export default function ShiftCard({ shift, event }) {
const [assignModal, setAssignModal] = useState({ open: false, role: null });
const roles = shift?.roles || [];
return (
<>
<Card className="bg-white border-2 border-slate-200 shadow-sm">
<CardHeader className="border-b border-slate-100 bg-slate-50">
<div className="flex items-center justify-between">
<div>
<CardTitle className="text-lg font-bold text-slate-900">
{shift.shift_name || "Shift"}
</CardTitle>
{shift.location && (
<div className="flex items-center gap-2 text-sm text-slate-600 mt-1">
<MapPin className="w-4 h-4" />
{shift.location}
</div>
)}
</div>
<Badge className="bg-[#0A39DF] text-white font-semibold px-3 py-1.5">
{roles.length} Role{roles.length !== 1 ? 's' : ''}
</Badge>
</div>
</CardHeader>
<CardContent className="p-6">
<div className="space-y-4">
{roles.map((role, idx) => {
const requiredCount = role.count || 1;
const assignedCount = event?.assigned_staff?.filter(s => s.role === role.role)?.length || 0;
const remainingCount = Math.max(requiredCount - assignedCount, 0);
// Consistent status color logic
const statusColor = remainingCount === 0
? "bg-green-100 text-green-700 border-green-300"
: assignedCount > 0
? "bg-blue-100 text-blue-700 border-blue-300"
: "bg-slate-100 text-slate-700 border-slate-300";
return (
<div
key={idx}
className="border-2 border-slate-200 rounded-xl p-4 hover:shadow-sm transition-shadow bg-white"
>
<div className="flex items-center justify-between mb-4">
<div className="flex-1">
<div className="flex items-center gap-3 mb-2">
<h4 className="font-bold text-slate-900 text-lg">{role.role}</h4>
<Badge className={`${statusColor} border-2 font-bold px-3 py-1`}>
{assignedCount} / {requiredCount} Assigned
</Badge>
</div>
<div className="flex items-center gap-4 text-sm text-slate-600">
{role.start_time && role.end_time && (
<span className="flex items-center gap-1.5">
<Clock className="w-4 h-4" />
{convertTo12Hour(role.start_time)} - {convertTo12Hour(role.end_time)}
</span>
)}
{role.department && (
<Badge variant="outline" className="text-xs border-slate-300">
{role.department}
</Badge>
)}
</div>
</div>
{remainingCount > 0 && (
<Button
onClick={() => setAssignModal({ open: true, role })}
className="bg-[#0A39DF] hover:bg-blue-700 gap-2 font-semibold"
>
<UserPlus className="w-4 h-4" />
Assign Staff ({remainingCount} needed)
</Button>
)}
</div>
{/* Show assigned staff */}
{assignedCount > 0 && (
<div className="border-t border-slate-200 pt-4 mt-4">
<p className="text-xs font-bold text-slate-700 mb-3 uppercase tracking-wide">
Assigned Staff
</p>
<AssignedStaffManager event={event} shift={shift} role={role} />
</div>
)}
{/* Additional role details */}
{(role.uniform || role.cost_per_hour) && (
<div className="grid grid-cols-2 gap-4 mt-4 pt-4 border-t border-slate-200">
{role.uniform && (
<div>
<p className="text-xs text-slate-500">Uniform</p>
<p className="text-sm font-medium text-slate-900">{role.uniform}</p>
</div>
)}
{role.cost_per_hour && (
<div className="flex items-center gap-2">
<DollarSign className="w-4 h-4 text-[#0A39DF]" />
<div>
<p className="text-xs text-slate-500">Rate</p>
<p className="text-sm font-bold text-slate-900">${role.cost_per_hour}/hr</p>
</div>
</div>
)}
</div>
)}
</div>
);
})}
</div>
</CardContent>
</Card>
{/* Smart Assignment Modal */}
<SmartAssignModal
open={assignModal.open}
onClose={() => setAssignModal({ open: false, role: null })}
event={event}
shift={shift}
role={assignModal.role}
/>
</>
);
}