park = $park; $this->parentId = $parentId; $this->loadAreas(); } /** * Load areas for the current context (either top-level or within a parent). */ protected function loadAreas(): void { $query = $this->park->areas() ->where('parent_id', $this->parentId) ->orderBy('position') ->select(['id', 'name', 'position', 'closing_date']); $this->areas = $query->get() ->map(fn ($area) => [ 'id' => $area->id, 'name' => $area->name, 'position' => $area->position, 'is_closed' => !is_null($area->closing_date), 'has_children' => $area->hasChildren(), ]) ->toArray(); } /** * Handle reordering of areas. */ public function reorder(array $orderedIds): void { // Validate that all IDs belong to this park and parent context $validIds = $this->park->areas() ->where('parent_id', $this->parentId) ->pluck('id') ->toArray(); $orderedIds = array_values(array_intersect($orderedIds, $validIds)); // Update positions foreach ($orderedIds as $position => $id) { ParkArea::where('id', $id)->update(['position' => $position]); } $this->loadAreas(); $this->dispatch('areas-reordered'); } /** * Move an area to a new parent. */ public function moveToParent(int $areaId, ?int $newParentId): void { $area = $this->park->areas()->findOrFail($areaId); // Prevent circular references if ($newParentId === $area->id) { return; } // If moving to a new parent, validate it exists and belongs to this park if ($newParentId) { $newParent = $this->park->areas()->findOrFail($newParentId); // Prevent moving to own descendant $ancestorIds = Collection::make(); $current = $newParent; while ($current) { if ($ancestorIds->contains($current->id)) { return; // Circular reference detected } $ancestorIds->push($current->id); if ($current->id === $area->id) { return; // Would create circular reference } $current = $current->parent; } } // Get the next position in the new parent context $maxPosition = $this->park->areas() ->where('parent_id', $newParentId) ->max('position'); $area->update([ 'parent_id' => $newParentId, 'position' => ($maxPosition ?? -1) + 1, ]); // Reorder the old parent's remaining areas to close gaps $this->park->areas() ->where('parent_id', $this->parentId) ->where('position', '>', $area->position) ->decrement('position'); $this->loadAreas(); $this->dispatch('area-moved'); } public function render() { return view('livewire.park-area-reorder-component', [ 'parentArea' => $this->parentId ? $this->park->areas()->find($this->parentId) : null, ]); } }