202 lines
9.8 KiB
TypeScript
202 lines
9.8 KiB
TypeScript
import { useState } from 'react';
|
|
import { X, Plus, Building2, Layers, ChevronRight, Check, MapPin } from 'lucide-react';
|
|
import { useLayoutStore } from '../../../stores/layoutStore';
|
|
|
|
interface FloorSelectorProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
onSelectFloor: (floorId: string, buildingId: string) => void;
|
|
onAddProperty?: () => void;
|
|
onAddBuilding?: () => void;
|
|
onAddFloor?: (buildingId: string) => void;
|
|
}
|
|
|
|
export function FloorSelector({
|
|
isOpen,
|
|
onClose,
|
|
onSelectFloor,
|
|
onAddProperty,
|
|
onAddBuilding,
|
|
onAddFloor
|
|
}: FloorSelectorProps) {
|
|
const { property, buildingId, floorId } = useLayoutStore();
|
|
const [expandedBuildings, setExpandedBuildings] = useState<Set<string>>(new Set());
|
|
|
|
if (!isOpen) return null;
|
|
|
|
const toggleBuilding = (id: string) => {
|
|
setExpandedBuildings(prev => {
|
|
const next = new Set(prev);
|
|
if (next.has(id)) {
|
|
next.delete(id);
|
|
} else {
|
|
next.add(id);
|
|
}
|
|
return next;
|
|
});
|
|
};
|
|
|
|
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">Select Floor</h2>
|
|
<p className="text-xs text-slate-400">Choose a floor to edit</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>
|
|
|
|
{/* Property Info */}
|
|
<div className="px-6 py-3 bg-slate-800/50 border-b border-white/10 flex items-center justify-between gap-3">
|
|
<div className="flex items-center gap-3">
|
|
<MapPin size={16} className="text-emerald-500" />
|
|
<div>
|
|
<p className="text-sm font-medium text-white">{property?.name || 'No Property'}</p>
|
|
{property?.address && (
|
|
<p className="text-xs text-slate-400">{property.address}</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
{onAddProperty && (
|
|
<button
|
|
onClick={() => {
|
|
onAddProperty();
|
|
onClose();
|
|
}}
|
|
className="text-xs px-2 py-1 bg-slate-700 hover:bg-slate-600 rounded text-slate-300 transition-colors"
|
|
>
|
|
Change/New
|
|
</button>
|
|
)}
|
|
</div>
|
|
|
|
{/* Buildings & Floors List */}
|
|
<div className="max-h-96 overflow-auto p-4">
|
|
{onAddBuilding && property && (
|
|
<button
|
|
onClick={() => {
|
|
onAddBuilding();
|
|
onClose();
|
|
}}
|
|
className="w-full flex items-center justify-center gap-2 px-3 py-2 mb-3 bg-slate-800 hover:bg-slate-700 border border-slate-700 border-dashed rounded-lg text-slate-400 hover:text-emerald-400 transition-colors"
|
|
>
|
|
<Plus size={16} />
|
|
<span className="text-sm font-medium">Add New Building</span>
|
|
</button>
|
|
)}
|
|
|
|
{property?.buildings.map(building => (
|
|
<div key={building.id} className="mb-2">
|
|
{/* Building Header */}
|
|
<button
|
|
onClick={() => toggleBuilding(building.id)}
|
|
className="w-full flex items-center gap-3 px-3 py-2.5 rounded-lg hover:bg-white/5 transition-colors"
|
|
>
|
|
<Building2 size={18} className="text-slate-400" />
|
|
<span className="flex-1 text-left font-medium text-white">
|
|
{building.name}
|
|
</span>
|
|
<span className="text-xs text-slate-500 mr-2">
|
|
{building.floors.length} floors
|
|
</span>
|
|
<ChevronRight
|
|
size={16}
|
|
className={`text-slate-500 transition-transform ${expandedBuildings.has(building.id) ? 'rotate-90' : ''
|
|
}`}
|
|
/>
|
|
</button>
|
|
|
|
{/* Floors */}
|
|
{expandedBuildings.has(building.id) && (
|
|
<div className="ml-6 mt-1 space-y-0.5">
|
|
{building.floors.map(floor => {
|
|
const isSelected = floorId === floor.id && buildingId === building.id;
|
|
|
|
return (
|
|
<button
|
|
key={floor.id}
|
|
onClick={() => {
|
|
onSelectFloor(floor.id, building.id);
|
|
onClose();
|
|
}}
|
|
className={`
|
|
w-full flex items-center gap-3 px-3 py-2.5 rounded-lg transition-colors
|
|
${isSelected
|
|
? 'bg-emerald-600/30 text-emerald-400'
|
|
: 'hover:bg-white/5 text-slate-300'
|
|
}
|
|
`}
|
|
>
|
|
<Layers size={16} className={isSelected ? 'text-emerald-400' : 'text-slate-500'} />
|
|
<span className="flex-1 text-left">{floor.name}</span>
|
|
<span className="text-xs text-slate-500">
|
|
{floor.rooms?.length || 0} rooms
|
|
</span>
|
|
{isSelected && <Check size={16} className="text-emerald-400" />}
|
|
</button>
|
|
);
|
|
})}
|
|
|
|
{/* Add Floor Button */}
|
|
{onAddFloor && (
|
|
<button
|
|
onClick={() => {
|
|
onAddFloor(building.id);
|
|
onClose();
|
|
}}
|
|
className="w-full flex items-center gap-3 px-3 py-2 text-slate-500 hover:text-emerald-400 hover:bg-white/5 rounded-lg transition-colors"
|
|
>
|
|
<Plus size={16} />
|
|
<span className="text-sm">Add Floor</span>
|
|
</button>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
|
|
{/* Empty State */}
|
|
{(!property || property.buildings.length === 0) && (
|
|
<div className="text-center py-8">
|
|
<Building2 size={32} className="text-slate-600 mx-auto mb-3" />
|
|
<p className="text-slate-400 mb-2">No buildings found</p>
|
|
<button
|
|
onClick={() => {
|
|
if (onAddProperty) {
|
|
onAddProperty();
|
|
onClose();
|
|
}
|
|
}}
|
|
className="text-sm text-emerald-400 hover:text-emerald-300"
|
|
>
|
|
Setup Property
|
|
</button>
|
|
</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>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|