import {
type BuildingNode,
LevelNode,
useScene,
type ZoneNode,
} from "@pascal-app/core";
import { useViewer } from "@pascal-app/viewer";
import {
Building2,
ChevronDown,
Hexagon,
Layers,
Plus,
Trash2,
} from "lucide-react";
import { cn } from "@/lib/utils";
import useEditor from "@/store/use-editor";
import { TreeNode } from "./tree-node";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/primitives/popover";
// Preset colors for zones
const PRESET_COLORS = [
"#3b82f6", // blue
"#22c55e", // green
"#eab308", // yellow
"#f97316", // orange
"#ef4444", // red
"#a855f7", // purple
"#ec4899", // pink
"#06b6d4", // cyan
];
// ============================================================================
// SITE PHASE VIEW - Simple building buttons
// ============================================================================
function SitePhaseView() {
const nodes = useScene((state) => state.nodes);
const rootNodeIds = useScene((state) => state.rootNodeIds);
const selectedBuildingId = useViewer((state) => state.selection.buildingId);
const setSelection = useViewer((state) => state.setSelection);
const buildings = rootNodeIds
.map((id) => nodes[id])
.filter((node): node is BuildingNode => node?.type === "building");
if (buildings.length === 0) {
return (
No buildings yet
);
}
return (
{buildings.map((building) => (
))}
);
}
// ============================================================================
// STRUCTURE/FURNISH PHASE VIEW - Building dropdown + Levels + Content
// ============================================================================
function BuildingSelector() {
const nodes = useScene((state) => state.nodes);
const rootNodeIds = useScene((state) => state.rootNodeIds);
const selectedBuildingId = useViewer((state) => state.selection.buildingId);
const setSelection = useViewer((state) => state.setSelection);
const buildings = rootNodeIds
.map((id) => nodes[id])
.filter((node): node is BuildingNode => node?.type === "building");
const selectedBuilding = selectedBuildingId
? (nodes[selectedBuildingId] as BuildingNode)
: null;
if (buildings.length === 0) return null;
// If only one building, just show it as a header
if (buildings.length === 1) {
return (
{buildings[0]?.name || "Building"}
);
}
return (
{buildings.map((building) => (
))}
);
}
function LevelsSection() {
const nodes = useScene((state) => state.nodes);
const createNode = useScene((state) => state.createNode);
const selectedBuildingId = useViewer((state) => state.selection.buildingId);
const selectedLevelId = useViewer((state) => state.selection.levelId);
const setSelection = useViewer((state) => state.setSelection);
const building = selectedBuildingId
? (nodes[selectedBuildingId] as BuildingNode)
: null;
if (!building) return null;
const levels = building.children
.map((id) => nodes[id])
.filter((node): node is LevelNode => node?.type === "level");
const handleAddLevel = () => {
const newLevel = LevelNode.parse({
level: levels.length,
children: [],
parentId: building.id,
});
createNode(newLevel, building.id);
setSelection({ levelId: newLevel.id });
};
return (
{/* Header */}
{/* Level buttons */}
{levels.map((level) => (
))}
{levels.length === 0 && (
No levels yet
)}
);
}
function LayerToggle() {
const structureLayer = useEditor((state) => state.structureLayer);
const setStructureLayer = useEditor((state) => state.setStructureLayer);
return (
);
}
function ZoneItem({ zone }: { zone: ZoneNode }) {
const deleteNode = useScene((state) => state.deleteNode);
const updateNode = useScene((state) => state.updateNode);
const selectedZoneId = useViewer((state) => state.selection.zoneId);
const hoveredId = useViewer((state) => state.hoveredId);
const setSelection = useViewer((state) => state.setSelection);
const setHoveredId = useViewer((state) => state.setHoveredId);
const setPhase = useEditor((state) => state.setPhase);
const setMode = useEditor((state) => state.setMode);
const isSelected = selectedZoneId === zone.id;
const isHovered = hoveredId === zone.id;
const handleClick = () => {
setSelection({ zoneId: zone.id });
setPhase("structure");
setMode("select");
};
const handleDelete = (e: React.MouseEvent) => {
e.stopPropagation();
deleteNode(zone.id);
if (isSelected) {
setSelection({ zoneId: null });
}
};
const handleColorChange = (color: string) => {
updateNode(zone.id, { color });
};
return (
setHoveredId(zone.id)}
onMouseLeave={() => setHoveredId(null)}
>
e.stopPropagation()}
>
{PRESET_COLORS.map((color) => (
{zone.name}
);
}
function ContentSection() {
const nodes = useScene((state) => state.nodes);
const selectedLevelId = useViewer((state) => state.selection.levelId);
const structureLayer = useEditor((state) => state.structureLayer);
const setPhase = useEditor((state) => state.setPhase);
const setMode = useEditor((state) => state.setMode);
const setTool = useEditor((state) => state.setTool);
const level = selectedLevelId ? (nodes[selectedLevelId] as LevelNode) : null;
if (!level) {
return (
Select a level to view content
);
}
if (structureLayer === "zones") {
// Show zones for this level
const levelZones = Object.values(nodes).filter(
(node): node is ZoneNode =>
node.type === "zone" && node.parentId === selectedLevelId
);
const handleAddZone = () => {
setPhase("structure");
setMode("build");
setTool("zone");
};
if (levelZones.length === 0) {
return (
No zones on this level.{" "}
);
}
return (
{levelZones.map((zone) => (
))}
);
}
// Show elements (walls, items, etc.) for this level
if (level.children.length === 0) {
return (
No elements on this level
);
}
return (
{level.children.map((childId) => (
))}
);
}
function StructurePhaseView() {
const phase = useEditor((state) => state.phase);
return (
{/* Only show layer toggle in structure phase, furnish is always elements */}
{phase === "structure" &&
}
);
}
// ============================================================================
// MAIN SITE PANEL
// ============================================================================
export function SitePanel() {
const phase = useEditor((state) => state.phase);
if (phase === "site") {
return ;
}
return ;
}