From ad366849cc7ba0371661df9c46511cf7e2b6ba87 Mon Sep 17 00:00:00 2001 From: Mateo Date: Mon, 20 Apr 2026 20:38:51 +0200 Subject: [PATCH] feat: implement column sequence update, users can now drag cols to change order --- app/Http/Controllers/ColumnController.php | 12 +++ .../Requests/ColumnSequenceUpdateRequest.php | 20 +++++ app/Services/ColumnService.php | 37 ++++++++ resources/js/components/tasks/KanbanBoard.vue | 88 +++++++++++++++---- .../js/components/tasks/KanbanColumn.vue | 36 +++++++- routes/tasks.php | 1 + tests/Feature/ColumnTest.php | 48 ++++++++++ 7 files changed, 220 insertions(+), 22 deletions(-) create mode 100644 app/Http/Requests/ColumnSequenceUpdateRequest.php diff --git a/app/Http/Controllers/ColumnController.php b/app/Http/Controllers/ColumnController.php index 14bbd56..86b7eb6 100644 --- a/app/Http/Controllers/ColumnController.php +++ b/app/Http/Controllers/ColumnController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers; +use App\Http\Requests\ColumnSequenceUpdateRequest; use App\Http\Requests\ColumnStoreRequest; use App\Http\Requests\ColumnUpdateRequest; use App\Models\Column; @@ -43,4 +44,15 @@ public function destroy(Request $request, Column $column): RedirectResponse return back(); } + + public function updateSequence(ColumnSequenceUpdateRequest $request, Column $column): RedirectResponse + { + if ($column->team_id !== $request->user()->team_id) { + abort(403); + } + + $this->columnService->updateSequence($column, $request->validated()['order']); + + return back(); + } } diff --git a/app/Http/Requests/ColumnSequenceUpdateRequest.php b/app/Http/Requests/ColumnSequenceUpdateRequest.php new file mode 100644 index 0000000..e7969fe --- /dev/null +++ b/app/Http/Requests/ColumnSequenceUpdateRequest.php @@ -0,0 +1,20 @@ +|string> + */ + public function rules(): array + { + return [ + 'order' => ['required', 'integer', 'min:0'], + ]; + } +} diff --git a/app/Services/ColumnService.php b/app/Services/ColumnService.php index b52e373..1e6a08f 100644 --- a/app/Services/ColumnService.php +++ b/app/Services/ColumnService.php @@ -4,6 +4,7 @@ use App\Models\Column; use App\Models\User; +use Illuminate\Support\Facades\DB; use Illuminate\Validation\ValidationException; class ColumnService @@ -31,6 +32,42 @@ public function updateColumn(Column $column, array $data): bool return $column->update($data); } + /** + * Update the sequence order of a column. + */ + public function updateSequence(Column $column, int $newOrder): void + { + DB::transaction(function () use ($column, $newOrder): void { + $columns = Column::query() + ->where('team_id', $column->team_id) + ->orderBy('order') + ->orderBy('id') + ->lockForUpdate() + ->get(['id']); + + $orderedIds = $columns->pluck('id')->values()->all(); + + $currentIndex = array_search($column->id, $orderedIds, true); + if ($currentIndex === false) { + return; + } + + $targetIndex = max(0, min($newOrder, count($orderedIds) - 1)); + if ($targetIndex === $currentIndex) { + return; + } + + array_splice($orderedIds, $currentIndex, 1); + array_splice($orderedIds, $targetIndex, 0, [$column->id]); + + foreach ($orderedIds as $index => $columnId) { + Column::query() + ->whereKey($columnId) + ->update(['order' => $index]); + } + }); + } + /** * Delete a column. */ diff --git a/resources/js/components/tasks/KanbanBoard.vue b/resources/js/components/tasks/KanbanBoard.vue index 9d25bf5..afd059e 100644 --- a/resources/js/components/tasks/KanbanBoard.vue +++ b/resources/js/components/tasks/KanbanBoard.vue @@ -1,10 +1,13 @@