import React, { useState, useMemo } from "react"; import { base44 } from "@/api/base44Client"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent } from "@/components/ui/card"; import { Sparkles, Star, MapPin, Clock, Award, TrendingUp, AlertCircle, CheckCircle, Zap, Users, RefreshCw } from "lucide-react"; import { useToast } from "@/components/ui/use-toast"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; export default function SmartAssignModal({ isOpen, onClose, event, roleNeeded, countNeeded }) { const { toast } = useToast(); const queryClient = useQueryClient(); const [selectedWorkers, setSelectedWorkers] = useState([]); const [isAnalyzing, setIsAnalyzing] = useState(false); const [aiRecommendations, setAiRecommendations] = useState(null); const { data: allStaff = [] } = useQuery({ queryKey: ['staff-smart-assign'], queryFn: () => base44.entities.Staff.list(), }); const { data: allEvents = [] } = useQuery({ queryKey: ['events-conflict-check'], queryFn: () => base44.entities.Event.list(), }); // Smart filtering const eligibleStaff = useMemo(() => { if (!event || !roleNeeded) return []; return allStaff.filter(worker => { // Role match const hasRole = worker.position === roleNeeded || worker.position_2 === roleNeeded || worker.profile_type === "Cross-Trained"; // Availability check const isAvailable = worker.employment_type !== "Medical Leave" && worker.action !== "Inactive"; // Conflict check - check if worker is already assigned const eventDate = new Date(event.date); const hasConflict = allEvents.some(e => { if (e.id === event.id) return false; const eDate = new Date(e.date); return eDate.toDateString() === eventDate.toDateString() && e.assigned_staff?.some(s => s.staff_id === worker.id); }); return hasRole && isAvailable && !hasConflict; }); }, [allStaff, event, roleNeeded, allEvents]); // Run AI analysis const runSmartAnalysis = async () => { setIsAnalyzing(true); try { const prompt = `You are a workforce optimization AI. Analyze these workers and recommend the best ${countNeeded} for this job. Event: ${event.event_name} Location: ${event.event_location || event.hub} Role Needed: ${roleNeeded} Quantity: ${countNeeded} Workers (JSON): ${JSON.stringify(eligibleStaff.map(w => ({ id: w.id, name: w.employee_name, rating: w.rating || 0, reliability_score: w.reliability_score || 0, total_shifts: w.total_shifts || 0, no_show_count: w.no_show_count || 0, position: w.position, city: w.city, profile_type: w.profile_type })), null, 2)} Rank them by: 1. Skills match (exact role match gets priority) 2. Rating (higher is better) 3. Reliability (lower no-shows, higher reliability score) 4. Experience (more shifts completed) 5. Distance (prefer closer to location) Return the top ${countNeeded} worker IDs with brief reasoning.`; const response = await base44.integrations.Core.InvokeLLM({ prompt, response_json_schema: { type: "object", properties: { recommendations: { type: "array", items: { type: "object", properties: { worker_id: { type: "string" }, reason: { type: "string" }, score: { type: "number" } } } } } } }); const recommended = response.recommendations.map(rec => { const worker = eligibleStaff.find(w => w.id === rec.worker_id); return worker ? { ...worker, ai_reason: rec.reason, ai_score: rec.score } : null; }).filter(Boolean); setAiRecommendations(recommended); setSelectedWorkers(recommended.slice(0, countNeeded)); toast({ title: "✨ AI Analysis Complete", description: `Found ${recommended.length} optimal matches`, }); } catch (error) { toast({ title: "Analysis Failed", description: error.message, variant: "destructive", }); } finally { setIsAnalyzing(false); } }; const assignMutation = useMutation({ mutationFn: async () => { const assigned_staff = selectedWorkers.map(w => ({ staff_id: w.id, staff_name: w.employee_name, role: roleNeeded })); return base44.entities.Event.update(event.id, { assigned_staff: [...(event.assigned_staff || []), ...assigned_staff], status: "Confirmed" }); }, onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['events'] }); toast({ title: "✅ Staff Assigned Successfully", description: `${selectedWorkers.length} workers assigned to ${event.event_name}`, }); onClose(); }, }); React.useEffect(() => { if (isOpen && eligibleStaff.length > 0 && !aiRecommendations) { runSmartAnalysis(); } }, [isOpen, eligibleStaff.length]); const toggleWorker = (worker) => { setSelectedWorkers(prev => { const exists = prev.find(w => w.id === worker.id); if (exists) { return prev.filter(w => w.id !== worker.id); } else if (prev.length < countNeeded) { return [...prev, worker]; } return prev; }); }; return (
Smart Assign (AI Assisted)

AI selected the best {countNeeded} {roleNeeded}{countNeeded > 1 ? 's' : ''} for this event

{isAnalyzing ? (

Analyzing workforce...

AI is finding the optimal matches based on skills, ratings, and availability

) : ( <> {/* Summary */}

Selected

{selectedWorkers.length}/{countNeeded}

Avg Rating

{selectedWorkers.length > 0 ? (selectedWorkers.reduce((sum, w) => sum + (w.rating || 0), 0) / selectedWorkers.length).toFixed(1) : "—"}

Available

{eligibleStaff.length}

{/* AI Recommendations */} {aiRecommendations && aiRecommendations.length > 0 ? (

AI Recommendations

{aiRecommendations.map((worker, idx) => { const isSelected = selectedWorkers.some(w => w.id === worker.id); const isOverLimit = selectedWorkers.length >= countNeeded && !isSelected; return ( !isOverLimit && toggleWorker(worker)} >
{worker.employee_name?.charAt(0) || 'W'} {idx === 0 && (
)}

{worker.employee_name}

{idx === 0 && ( Top Pick )} {worker.position}
{worker.rating?.toFixed(1) || 'N/A'}
{worker.total_shifts || 0} shifts
{worker.city || 'N/A'}
{worker.ai_reason && (

AI Insight: {worker.ai_reason}

)}
{worker.ai_score && ( {Math.round(worker.ai_score)}/100 )} {isSelected && ( )}
); })}
) : (

No eligible staff found for this role

)} )}
); }