ca-grow-ops-manager/frontend/src/features/layout-designer/components/AddFloorModal.tsx
fullsizemalt efffe3fb94
Some checks failed
Deploy to Production / deploy (push) Failing after 0s
Test / backend-test (push) Failing after 0s
Test / frontend-test (push) Failing after 0s
fix(frontend): Fix TypeScript inference for AddFloorModal state
2025-12-11 12:27:02 -08:00

159 lines
7.7 KiB
TypeScript

import { useState } from 'react';
import { X, Layers, ArrowRight, Loader2 } from 'lucide-react';
import { layoutApi } from '../../../lib/layoutApi';
interface AddFloorModalProps {
isOpen: boolean;
onClose: () => void;
buildingId: string | null;
onCreated: (floorId: string) => void;
}
export function AddFloorModal({ isOpen, onClose, buildingId, onCreated }: AddFloorModalProps) {
const [loading, setLoading] = useState(false);
const [formData, setFormData] = useState<{
name: string;
number: number;
width: number;
height: number;
ceilingHeight?: number;
defaultTiers: number;
}>({
name: '',
number: 1,
width: 100,
height: 100,
ceilingHeight: undefined,
defaultTiers: 1
});
if (!isOpen || !buildingId) return null;
const handleCreate = async () => {
if (!formData.name.trim()) return;
setLoading(true);
try {
const floor = await layoutApi.createFloor({
buildingId,
name: formData.name.trim(),
number: formData.number,
width: formData.width,
height: formData.height,
ceilingHeight: formData.ceilingHeight,
defaultTiers: formData.defaultTiers
});
onCreated(floor.id);
onClose();
// Reset form
setFormData({ name: '', number: formData.number + 1, width: 100, height: 100, ceilingHeight: undefined, defaultTiers: 1 });
} catch (error) {
console.error('Failed to create floor:', error);
} finally {
setLoading(false);
}
};
return (
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm">
<div className="bg-slate-900 border border-white/10 rounded-2xl w-full max-w-md mx-4 overflow-hidden shadow-2xl">
{/* Header */}
<div className="px-6 py-4 border-b border-white/10 flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-xl bg-emerald-600/20 flex items-center justify-center">
<Layers className="text-emerald-400" size={20} />
</div>
<div>
<h2 className="text-lg font-bold text-white">Add New Floor</h2>
<p className="text-xs text-slate-400">Define floor dimensions</p>
</div>
</div>
<button onClick={onClose} className="p-2 hover:bg-white/5 rounded-lg transition-colors">
<X size={20} className="text-slate-400" />
</button>
</div>
{/* Content */}
<div className="p-6 space-y-4">
<div>
<label className="block text-sm text-slate-400 mb-2">Floor Name</label>
<input
type="text"
value={formData.name}
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
placeholder="Floor 2"
className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-3 text-white focus:border-emerald-500 focus:ring-1 focus:ring-emerald-500 outline-none"
/>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm text-slate-400 mb-2">Number</label>
<input
type="number"
value={formData.number}
onChange={(e) => setFormData({ ...formData, number: parseInt(e.target.value) || 0 })}
className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-3 text-white focus:border-emerald-500 outline-none"
/>
</div>
<div>
<label className="block text-sm text-slate-400 mb-2">Default Tiers</label>
<input
type="number"
value={formData.defaultTiers}
onChange={(e) => setFormData({ ...formData, defaultTiers: parseInt(e.target.value) || 1 })}
className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-3 text-white focus:border-emerald-500 outline-none"
/>
</div>
</div>
<div className="grid grid-cols-3 gap-4">
<div>
<label className="block text-sm text-slate-400 mb-2">Width (ft)</label>
<input
type="number"
value={formData.width}
onChange={(e) => setFormData({ ...formData, width: parseInt(e.target.value) || 0 })}
className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-3 text-white focus:border-emerald-500 outline-none"
/>
</div>
<div>
<label className="block text-sm text-slate-400 mb-2">Length (ft)</label>
<input
type="number"
value={formData.height}
onChange={(e) => setFormData({ ...formData, height: parseInt(e.target.value) || 0 })}
className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-3 text-white focus:border-emerald-500 outline-none"
/>
</div>
<div>
<label className="block text-sm text-slate-400 mb-2">Ceiling (ft)</label>
<input
type="number"
value={formData.ceilingHeight ?? ''}
onChange={(e) => setFormData({ ...formData, ceilingHeight: e.target.value ? parseInt(e.target.value) : undefined })}
placeholder="Optional"
className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-3 text-white focus:border-emerald-500 outline-none"
/>
</div>
</div>
</div>
{/* Footer */}
<div className="px-6 py-4 border-t border-white/10 flex justify-end gap-3">
<button onClick={onClose} className="px-4 py-2 text-sm text-slate-400 hover:text-white transition-colors">
Cancel
</button>
<button
onClick={handleCreate}
disabled={!formData.name.trim() || loading}
className="flex items-center gap-2 px-5 py-2 bg-emerald-600 hover:bg-emerald-500 disabled:bg-slate-700 disabled:text-slate-500 rounded-lg text-sm font-medium transition-colors"
>
{loading ? <Loader2 size={16} className="animate-spin" /> : <ArrowRight size={16} />}
Create Floor
</button>
</div>
</div>
</div>
);
}