diff --git a/README.md b/README.md index e6b7ab2..bf73272 100644 --- a/README.md +++ b/README.md @@ -91,15 +91,19 @@ ### Steps cp .env.example .env sudo php artisan key\:generate ``` -5. Run migrations and seeders: +5. Create a symbolic link for public storage + ```bash + sudo php artisan storage:link + ``` +6. Run migrations and seeders: ```bash sudo php artisan migrate --seed ``` -6. Build frontend assets: +7. Build frontend assets: ```bash npm run build ``` -7. Start the development server: +8. Start the development server: ```bash php artisan serve ``` diff --git a/app/Http/Controllers/PipelineController.php b/app/Http/Controllers/PipelineController.php index f251cf6..88f949f 100644 --- a/app/Http/Controllers/PipelineController.php +++ b/app/Http/Controllers/PipelineController.php @@ -39,7 +39,12 @@ public function index() // Process items to add latest_todo_due_date and remove todos array $laneArray['items'] = collect($laneArray['items'])->map(function ($item) { - $item['next_todo_due_date'] = $item['todos'][0]['due_date'] ?? null; + // Find the first todo with status != 'COMPLETED' + $nextTodo = collect($item['todos'])->first(function ($todo) { + return strtolower($todo['status'] ?? '') !== 'completed'; + }); + + $item['next_todo_due_date'] = $nextTodo['due_date'] ?? null; unset($item['todos']); // Remove the todos array return $item; })->toArray(); diff --git a/app/Http/Controllers/PipelineItemController.php b/app/Http/Controllers/PipelineItemController.php index d4101fb..1326202 100644 --- a/app/Http/Controllers/PipelineItemController.php +++ b/app/Http/Controllers/PipelineItemController.php @@ -19,14 +19,16 @@ public function index() $query->where('notable_type', 'App\Models\PipelineItem'); }]) ->withCount(['todos' => function ($query) { - $query->where('todoable_type', 'App\Models\PipelineItem'); + $query + ->where('todoable_type', 'App\Models\PipelineItem') + ->whereNotIn('status', ['completed', 'COMPLETED']); }]) ->orderBy('position') ->get(); $pipelineItemsArray = $pipelineItems->map(function ($item) { $itemArray = $item->toArray(); - $itemArray['next_todo_due_date'] = $item->todos->first()?->due_date?->toISOString() ?? null; + $itemArray['next_todo_due_date'] = $item->todos()->where('status', '!=', 'COMPLETED')->first()?->due_date?->toISOString() ?? null; unset($itemArray['todos']); // Remove the todos array return $itemArray; }); @@ -43,7 +45,8 @@ public function single(int $id) ::with(['notes' => function ($query) { $query->where('notable_type', 'App\Models\PipelineItem')->orderBy('created_at', 'desc'); }])->with(['todos' => function ($query) { - $query->where('todoable_type', 'App\Models\PipelineItem')->orderBy('due_date', 'asc'); + $query + ->where('todoable_type', 'App\Models\PipelineItem')->orderBy('due_date', 'asc'); }])->orderBy('position')->findOrFail($id); return ApiDataTransformer::snakeToCamel($pipelineItem->toArray()); diff --git a/resources/js/pages/Pipeline.vue b/resources/js/pages/Pipeline.vue index 3eb2c57..35d9f86 100644 --- a/resources/js/pages/Pipeline.vue +++ b/resources/js/pages/Pipeline.vue @@ -133,6 +133,8 @@ const cardClasses = (item: PipelineItem): string => { if (item.dueDate && isSoon(item.dueDate) || item.nextTodoDueDate && isSoon(item.nextTodoDueDate) ) return "border-l-4 border-warning" + + return "" } const badgeVariant = (date: string | null): "default" | "secondary" | "destructive" | "warning" | "outline" | null | undefined => {