Spaces:
Sleeping
Sleeping
| import React from 'react'; | |
| import { useGridStore } from '../../store/gridStore'; | |
| import { Card, CardContent, CardHeader, CardTitle } from '../ui/card'; | |
| import { Button } from '../ui/button'; | |
| import { | |
| X, | |
| Truck, | |
| Package, | |
| Route, | |
| Clock, | |
| Activity, | |
| HardDrive, | |
| ArrowRight, | |
| CheckCircle, | |
| Eye, | |
| EyeOff, | |
| } from 'lucide-react'; | |
| // Colors matching the store | |
| const PATH_COLORS = [ | |
| '#f97316', // orange | |
| '#06b6d4', // cyan | |
| '#ec4899', // pink | |
| '#84cc16', // lime | |
| '#a855f7', // purple | |
| '#14b8a6', // teal | |
| '#f43f5e', // rose | |
| '#eab308', // yellow | |
| '#6366f1', // indigo | |
| '#22c55e', // green | |
| ]; | |
| interface PlanResultsModalProps { | |
| isOpen: boolean; | |
| onClose: () => void; | |
| } | |
| export const PlanResultsModal: React.FC<PlanResultsModalProps> = ({ isOpen, onClose }) => { | |
| const { planResult, selectedAlgorithm, showPlanPaths, setShowPlanPaths } = useGridStore(); | |
| if (!isOpen || !planResult) return null; | |
| return ( | |
| <div className="fixed inset-0 z-50 flex items-center justify-center"> | |
| {/* Backdrop */} | |
| <div | |
| className="absolute inset-0 bg-black/70 backdrop-blur-sm" | |
| onClick={onClose} | |
| /> | |
| {/* Modal */} | |
| <div className="relative bg-zinc-900 border border-zinc-800 rounded-xl shadow-2xl w-full max-w-3xl max-h-[85vh] overflow-hidden m-4"> | |
| {/* Header */} | |
| <div className="flex items-center justify-between px-6 py-4 border-b border-zinc-800 bg-zinc-800/30"> | |
| <div className="flex items-center gap-3"> | |
| <div className="p-2 bg-zinc-800 rounded-lg"> | |
| <Truck className="w-5 h-5 text-zinc-300" /> | |
| </div> | |
| <div> | |
| <h2 className="text-sm font-semibold text-zinc-200">Delivery Plan Results</h2> | |
| <p className="text-xs text-zinc-500">Algorithm: {selectedAlgorithm}</p> | |
| </div> | |
| </div> | |
| <Button variant="ghost" size="icon" onClick={onClose}> | |
| <X className="w-4 h-4" /> | |
| </Button> | |
| </div> | |
| {/* Content */} | |
| <div className="overflow-y-auto max-h-[calc(85vh-140px)] p-6 space-y-4"> | |
| {/* Summary Stats */} | |
| <div className="grid grid-cols-4 gap-3"> | |
| <Card> | |
| <CardContent className="p-3"> | |
| <div className="flex items-center gap-2 text-zinc-500 text-xs mb-1"> | |
| <Route className="w-3.5 h-3.5" /> | |
| Total Cost | |
| </div> | |
| <p className="text-lg font-semibold text-zinc-200 font-mono"> | |
| {planResult.totalCost} | |
| </p> | |
| </CardContent> | |
| </Card> | |
| <Card> | |
| <CardContent className="p-3"> | |
| <div className="flex items-center gap-2 text-zinc-500 text-xs mb-1"> | |
| <Activity className="w-3.5 h-3.5" /> | |
| Nodes Expanded | |
| </div> | |
| <p className="text-lg font-semibold text-zinc-200 font-mono"> | |
| {planResult.totalNodesExpanded.toLocaleString()} | |
| </p> | |
| </CardContent> | |
| </Card> | |
| <Card> | |
| <CardContent className="p-3"> | |
| <div className="flex items-center gap-2 text-zinc-500 text-xs mb-1"> | |
| <Clock className="w-3.5 h-3.5" /> | |
| Runtime | |
| </div> | |
| <p className="text-lg font-semibold text-zinc-200 font-mono"> | |
| {planResult.runtimeMs.toFixed(2)}ms | |
| </p> | |
| </CardContent> | |
| </Card> | |
| <Card> | |
| <CardContent className="p-3"> | |
| <div className="flex items-center gap-2 text-zinc-500 text-xs mb-1"> | |
| <HardDrive className="w-3.5 h-3.5" /> | |
| Memory | |
| </div> | |
| <p className="text-lg font-semibold text-zinc-200 font-mono"> | |
| {planResult.memoryKb.toFixed(2)}KB | |
| </p> | |
| </CardContent> | |
| </Card> | |
| </div> | |
| {/* Plan Output */} | |
| <Card> | |
| <CardHeader className="pb-2"> | |
| <CardTitle className="text-xs text-zinc-400 font-medium">Plan Output</CardTitle> | |
| </CardHeader> | |
| <CardContent> | |
| <div className="bg-zinc-950 rounded-lg p-3 font-mono text-xs text-zinc-300 whitespace-pre-wrap overflow-x-auto"> | |
| {planResult.output || 'No output'} | |
| </div> | |
| </CardContent> | |
| </Card> | |
| {/* Assignments */} | |
| <Card> | |
| <CardHeader className="pb-2"> | |
| <CardTitle className="text-xs text-zinc-400 font-medium flex items-center gap-2"> | |
| <Package className="w-3.5 h-3.5" /> | |
| Delivery Assignments ({planResult.assignments.length}) | |
| </CardTitle> | |
| </CardHeader> | |
| <CardContent className="space-y-2"> | |
| {planResult.assignments.length === 0 ? ( | |
| <p className="text-zinc-500 text-sm">No assignments made</p> | |
| ) : ( | |
| planResult.assignments.map((assignment, index) => { | |
| const pathColor = PATH_COLORS[index % PATH_COLORS.length]; | |
| return ( | |
| <div | |
| key={index} | |
| className="bg-zinc-800/50 rounded-lg p-3 border border-zinc-800" | |
| style={{ borderLeftWidth: 4, borderLeftColor: pathColor }} | |
| > | |
| <div className="flex items-center justify-between mb-2"> | |
| <div className="flex items-center gap-3"> | |
| {/* Color indicator */} | |
| <div | |
| className="w-3 h-3 rounded-full" | |
| style={{ backgroundColor: pathColor }} | |
| /> | |
| <div className="flex items-center gap-2"> | |
| <div className="w-6 h-6 rounded bg-blue-500/20 border border-blue-500/30 flex items-center justify-center"> | |
| <span className="text-xs font-mono text-blue-400">S{assignment.store_id}</span> | |
| </div> | |
| <ArrowRight className="w-4 h-4 text-zinc-600" /> | |
| <div className="w-6 h-6 rounded-full bg-emerald-500/20 border border-emerald-500/30 flex items-center justify-center"> | |
| <span className="text-xs font-mono text-emerald-400">D{assignment.destination_id}</span> | |
| </div> | |
| </div> | |
| <CheckCircle className="w-4 h-4 text-emerald-500" /> | |
| </div> | |
| <div className="flex items-center gap-4 text-xs text-zinc-500"> | |
| <span>Cost: <span className="text-zinc-300 font-mono">{assignment.path.cost}</span></span> | |
| <span>Nodes: <span className="text-zinc-300 font-mono">{assignment.path.nodes_expanded}</span></span> | |
| </div> | |
| </div> | |
| <div className="text-xs text-zinc-500"> | |
| <span className="text-zinc-600">Path: </span> | |
| <span className="font-mono text-zinc-400">{assignment.path.plan || 'Direct'}</span> | |
| </div> | |
| </div> | |
| ); | |
| }) | |
| )} | |
| </CardContent> | |
| </Card> | |
| </div> | |
| {/* Footer */} | |
| <div className="px-6 py-3 border-t border-zinc-800 bg-zinc-800/20 flex justify-between"> | |
| <Button | |
| variant={showPlanPaths ? 'primary' : 'outline'} | |
| size="sm" | |
| onClick={() => setShowPlanPaths(!showPlanPaths)} | |
| className="gap-2" | |
| > | |
| {showPlanPaths ? <EyeOff className="w-4 h-4" /> : <Eye className="w-4 h-4" />} | |
| {showPlanPaths ? 'Hide Paths on Grid' : 'Show Paths on Grid'} | |
| </Button> | |
| <Button variant="secondary" size="sm" onClick={onClose}> | |
| Close | |
| </Button> | |
| </div> | |
| </div> | |
| </div> | |
| ); | |
| }; | |
| export default PlanResultsModal; | |