diff --git a/resources/views/components/infrastructure/⚡index.blade.php b/resources/views/components/infrastructure/⚡index.blade.php
index c4b807e..f7ae421 100644
--- a/resources/views/components/infrastructure/⚡index.blade.php
+++ b/resources/views/components/infrastructure/⚡index.blade.php
@@ -80,6 +80,42 @@ new class extends Component
session()->flash('status', "« {$document->name} » supprimé.");
}
+ public function moveDocument(int $documentId, ?int $targetFolderId): void
+ {
+ $document = $this->organization->documents()->where('type', 'diagram')->findOrFail($documentId);
+
+ if ($document->id === $targetFolderId) {
+ return;
+ }
+
+ if ($targetFolderId !== null) {
+ $targetFolder = $this->organization->documents()->where('type', 'diagram')->find($targetFolderId);
+
+ if (! $targetFolder || ! $targetFolder->isFolder()) {
+ return;
+ }
+
+ // Prevent moving a folder into itself or one of its own descendants.
+ if ($document->isFolder()) {
+ $walk = $targetFolder;
+ while ($walk) {
+ if ($walk->id === $document->id) {
+ return;
+ }
+ $walk = $walk->folder;
+ }
+ }
+ }
+
+ if ($document->folder_id === $targetFolderId) {
+ return;
+ }
+
+ $document->update(['folder_id' => $targetFolderId]);
+
+ session()->flash('status', "« {$document->name} » déplacé.");
+ }
+
private function folderTree(\Illuminate\Support\Collection $allFolders): \Illuminate\Support\Collection
{
$byParent = $allFolders->groupBy('folder_id');
@@ -190,7 +226,11 @@ new class extends Component
@endif
-
+ @if ($items->isNotEmpty())
+
Astuce : glissez-déposez un élément sur un dossier pour le déplacer.
+ @endif
+
+
@@ -203,7 +243,17 @@ new class extends Component
@forelse ($items as $item)
@php $isFolder = $item->isFolder(); @endphp
-
+
|
@if ($isFolder)
@@ -217,10 +267,40 @@ new class extends Component
|
{{ $isFolder ? '—' : ($item->location?->name ?? '—') }} |
{{ $item->updated_at->diffForHumans() }} |
-
+ |
@unless ($isFolder)
Ouvrir
@endunless
+ @php
+ // Exclude the item's own subtree (if it's a folder) so it can't be moved into itself or a descendant.
+ $excludedIds = [$item->id, $item->folder_id];
+ if ($isFolder) {
+ $stack = [$item->id];
+ while ($stack) {
+ $id = array_pop($stack);
+ foreach ($folderTree as $node) {
+ if ($node['folder']->folder_id === $id) {
+ $excludedIds[] = $node['folder']->id;
+ $stack[] = $node['folder']->id;
+ }
+ }
+ }
+ }
+ $moveTargets = $folderTree->reject(fn ($node) => in_array($node['folder']->id, $excludedIds, true));
+ @endphp
+ @if ($moveTargets->isNotEmpty() || $item->folder_id !== null)
+
+ @endif
|
+
+ @if ($currentFolder)
+
+ ⬆️ Glisser ici pour remonter au dossier parent
+
+ @endif
diff --git a/tests/Feature/InfrastructureTest.php b/tests/Feature/InfrastructureTest.php
index b0c9633..a63ca8b 100644
--- a/tests/Feature/InfrastructureTest.php
+++ b/tests/Feature/InfrastructureTest.php
@@ -159,4 +159,64 @@ class InfrastructureTest extends TestCase
Livewire::test('infrastructure.index', ['organization' => $org, 'locationId' => $otherLocation->id])
->assertDontSee('Schéma test');
}
+
+ public function test_can_move_a_diagram_into_a_folder_and_back_to_the_root(): void
+ {
+ $user = User::factory()->create();
+ $org = Organization::create(['name' => 'Test Commune', 'slug' => 'test-commune', 'org_type' => 'client']);
+
+ $folder = $org->documents()->create(['name' => 'Réseau', 'type' => 'diagram', 'is_folder' => true, 'created_by' => $user->id]);
+ $diagram = $org->documents()->create([
+ 'name' => 'Schéma test', 'type' => 'diagram', 'content' => '',
+ 'current_version' => 1, 'created_by' => $user->id,
+ ]);
+
+ $this->actingAs($user);
+
+ Livewire::test('infrastructure.index', ['organization' => $org])
+ ->call('moveDocument', $diagram->id, $folder->id);
+
+ $this->assertSame($folder->id, $diagram->refresh()->folder_id);
+
+ Livewire::test('infrastructure.index', ['organization' => $org])
+ ->call('moveDocument', $diagram->id, null);
+
+ $this->assertNull($diagram->refresh()->folder_id);
+ }
+
+ public function test_cannot_move_a_folder_into_its_own_descendant(): void
+ {
+ $user = User::factory()->create();
+ $org = Organization::create(['name' => 'Test Commune', 'slug' => 'test-commune', 'org_type' => 'client']);
+
+ $parent = $org->documents()->create(['name' => 'Parent', 'type' => 'diagram', 'is_folder' => true, 'created_by' => $user->id]);
+ $child = $org->documents()->create(['name' => 'Enfant', 'type' => 'diagram', 'is_folder' => true, 'folder_id' => $parent->id, 'created_by' => $user->id]);
+
+ $this->actingAs($user);
+
+ Livewire::test('infrastructure.index', ['organization' => $org])
+ ->call('moveDocument', $parent->id, $child->id);
+
+ $this->assertNull($parent->refresh()->folder_id);
+ }
+
+ public function test_moving_an_item_is_scoped_to_diagram_type_documents(): void
+ {
+ $user = User::factory()->create();
+ $org = Organization::create(['name' => 'Test Commune', 'slug' => 'test-commune', 'org_type' => 'client']);
+
+ $regularFolder = $org->documents()->create(['name' => 'Dossier documents', 'is_folder' => true, 'created_by' => $user->id]);
+ $diagram = $org->documents()->create([
+ 'name' => 'Schéma test', 'type' => 'diagram', 'content' => '',
+ 'current_version' => 1, 'created_by' => $user->id,
+ ]);
+
+ $this->actingAs($user);
+
+ // A plain (non-diagram) folder must not be a valid drop target from Infrastructure.
+ Livewire::test('infrastructure.index', ['organization' => $org])
+ ->call('moveDocument', $diagram->id, $regularFolder->id);
+
+ $this->assertNull($diagram->refresh()->folder_id);
+ }
}