diff --git a/be/app/Http/Controllers/SubTaskController.php b/be/app/Http/Controllers/SubTaskController.php index a46c82f..200bdcd 100644 --- a/be/app/Http/Controllers/SubTaskController.php +++ b/be/app/Http/Controllers/SubTaskController.php @@ -30,6 +30,11 @@ class SubTaskController extends Controller return $this->indexOngoing($request); } + // draft tasks (koncepty) + if (isset($parameters['type']) && $parameters['type'] == 'draft') { + return $this->indexDrafts($request); + } + $parameters['user_id'] = ($parameters['employee_id'] ?? null); $validator = Validator::make($request->all(), [ @@ -59,13 +64,15 @@ class SubTaskController extends Controller $query->where('team_id', $parameters['team_id']); })->get()->pluck('id')->toArray(); - $subtasks = SubTask::whereIn('user_id', $userIdArray) + $subtasks = SubTask::published() + ->whereIn('user_id', $userIdArray) ->whereHas('timeblocks', function ($query) use ($start, $end) { $query->where('start', '>=', $start); $query->where('end', '<', $end); })->get(); } elseif (isset($parameters['user_id'])) { - $subtasks = SubTask::where('user_id', $parameters['user_id'])->whereHas('timeblocks', + $subtasks = SubTask::published() + ->where('user_id', $parameters['user_id'])->whereHas('timeblocks', function ($query) use ($start, $end) { $query->where('start', '>', $start); $query->where('end', '<', $end); @@ -102,8 +109,11 @@ class SubTaskController extends Controller $parameters['user_id'] = $parameters['employee_id']; - $subtasks = SubTask::where('user_id', $parameters['user_id'])->where('is_finished', - 0)->whereDoesntHave('timeblocks')->get(); + $subtasks = SubTask::published() + ->where('user_id', $parameters['user_id']) + ->where('is_finished', 0) + ->whereDoesntHave('timeblocks') + ->get(); return response()->json([ 'subtasks' => SubTaskResource::collection($subtasks) @@ -125,16 +135,42 @@ class SubTaskController extends Controller $parameters['user_id'] = $parameters['employee_id']; - $subtasks = SubTask::where('user_id', $parameters['user_id'])->where('is_finished', 0)->get(); + $subtasks = SubTask::published() + ->where('user_id', $parameters['user_id']) + ->where('is_finished', 0) + ->get(); return response()->json([ 'subtasks' => SubTaskResource::collection($subtasks) ], 200); } + /** + * Zoznam všetkých konceptov (draftov) + */ + private function indexDrafts(Request $request) + { + $parameters = $request->query(); + + $query = SubTask::draft() + ->with(['mastertask', 'user', 'timeblocks', 'attachments']) + ->orderBy('created_at', 'desc'); + + // ak je zadané employee_id, filtrovať podľa neho + if (isset($parameters['employee_id'])) { + $query->where('user_id', $parameters['employee_id']); + } + + return response()->json([ + 'subtasks' => SubTaskResource::collection($query->get()) + ], 200); + } public function store($mastertask_id, Request $request) { + // zistenie, či ide o draft + $isDraft = isset($request->all()['data']['is_draft']) && $request->all()['data']['is_draft'] == true; + $validator = Validator::make($request->all(), [ 'data.employees' => 'required', ]); @@ -173,6 +209,7 @@ class SubTaskController extends Controller $data['data']['employees'][$key]['created_by'] = User::getUserIdFromBearerToken($request->bearerToken()); $data['data']['employees'][$key]['description'] = $request->all()['data']['description']; $data['data']['employees'][$key]['deadline'] = $request->all()['data']['deadline']; + $data['data']['employees'][$key]['is_draft'] = $isDraft; } $request->merge($data); @@ -203,9 +240,11 @@ class SubTaskController extends Controller } } + $message = $isDraft ? 'Draft created successfully' : 'Sub Task created successfully'; + return response()->json([ - 'message' => 'Sub Task created successfully', - 'subtasks' => $subtasks + 'message' => $message, + 'subtasks' => SubTaskResource::collection($subtasks) ], 201); } @@ -214,6 +253,12 @@ class SubTaskController extends Controller { $subtask = SubTask::find($id); + if (!$subtask) { + return response()->json([ + 'message' => 'Subtask not found.' + ], 404); + } + // ak je sub task v stave ukončený, nedovolime update. vynimkou je iba znovuotvorenie mastertasku if ($subtask->is_finished == 1 && isset($request->all()['data']['is_finished']) && $request->all()['data']['is_finished'] == 1) { return response()->json([ @@ -228,6 +273,27 @@ class SubTaskController extends Controller ], 400); } + // validácia pri publikovaní draftu (is_draft: false) + $isPublishing = $subtask->is_draft && isset($request->all()['data']['is_draft']) && $request->all()['data']['is_draft'] == false; + if ($isPublishing) { + $publishValidator = Validator::make([ + 'master_task_id' => $subtask->master_task_id, + 'user_id' => $request->all()['data']['employee_id'] ?? $subtask->user_id, + 'estimated_time' => $request->all()['data']['duration'] ?? $subtask->estimated_time, + ], [ + 'master_task_id' => 'required', + 'user_id' => 'required', + 'estimated_time' => 'required|numeric|min:0', + ]); + + if ($publishValidator->fails()) { + return response()->json([ + 'message' => 'Draft cannot be published. Missing required fields.', + 'errors' => $publishValidator->errors() + ], 400); + } + } + $validator = Validator::make($request->all(), [ 'data.duration' => 'required', ]); @@ -254,6 +320,11 @@ class SubTaskController extends Controller $data['data']['deadline'] = isset($request->all()['data']['deadline']) ? $request->all()['data']['deadline'] : null; $data['data']['estimated_time'] = $request->all()['data']['duration']; $data['data']['updated_by'] = User::getUserIdFromBearerToken($request->bearerToken()); + + // publikovanie draftu + if (isset($request->all()['data']['is_draft'])) { + $data['data']['is_draft'] = $request->all()['data']['is_draft']; + } $request->merge($data); @@ -276,8 +347,10 @@ class SubTaskController extends Controller $subtask->syncTimeblocks($data['data']['timeblocks']); } + $message = $isPublishing ? 'Draft published successfully' : 'Subtask updated successfully'; + return response()->json([ - 'message' => 'Subtask updated successfully', + 'message' => $message, 'subtask' => new SubTaskResource($subtask) ]); } diff --git a/be/app/Http/Resources/SubTaskResource.php b/be/app/Http/Resources/SubTaskResource.php index 2d66758..b36f8dd 100644 --- a/be/app/Http/Resources/SubTaskResource.php +++ b/be/app/Http/Resources/SubTaskResource.php @@ -17,6 +17,7 @@ class SubTaskResource extends JsonResource { return [ 'id' => $this->id, + 'is_draft' => $this->is_draft, 'type' => $this->type, 'created_by' => $this->created_by, 'master_task' => $this->mastertask->title, diff --git a/be/app/Models/SubTask.php b/be/app/Models/SubTask.php index cc02db2..1df528b 100644 --- a/be/app/Models/SubTask.php +++ b/be/app/Models/SubTask.php @@ -13,6 +13,22 @@ class SubTask extends Model public $timestamps = true; public $guarded = ['id']; + /** + * Scope pre koncepty (drafty) - nezverejnené subtasky + */ + public function scopeDraft($query) + { + return $query->where('is_draft', true); + } + + /** + * Scope pre publikované subtasky (nie drafty) + */ + public function scopePublished($query) + { + return $query->where('is_draft', false); + } + public function mastertask() { return $this->belongsTo(MasterTask::class, 'master_task_id'); diff --git a/be/database/migrations/2025_12_03_120000_add_is_draft_to_subtasks_table.php b/be/database/migrations/2025_12_03_120000_add_is_draft_to_subtasks_table.php new file mode 100644 index 0000000..e9b9d62 --- /dev/null +++ b/be/database/migrations/2025_12_03_120000_add_is_draft_to_subtasks_table.php @@ -0,0 +1,33 @@ +boolean('is_draft')->default(false)->after('is_finished'); + }); + } + + /** + * Reverse the migrations. + * + * @return void + */ + public function down() + { + Schema::table('sub_tasks', function (Blueprint $table) { + $table->dropColumn('is_draft'); + }); + } +}; + diff --git a/be/routes/api.php b/be/routes/api.php index b81eb69..3d2bae2 100644 --- a/be/routes/api.php +++ b/be/routes/api.php @@ -79,7 +79,7 @@ Route::middleware(['auth:sanctum', 'abilities:full-access'])->group(function () Route::delete('/v1/mastertasks/{id}', MasterTaskController::class . '@delete'); Route::get('/v1/mastertasks/{id}/subtasks', function ($id) { - return new SubTaskCollection( SubTask::whereHas('mastertask', function ($query) use ($id) { + return new SubTaskCollection( SubTask::published()->whereHas('mastertask', function ($query) use ($id) { $query->where('id', $id); })->get()); }); @@ -88,7 +88,7 @@ Route::middleware(['auth:sanctum', 'abilities:full-access'])->group(function () Route::put('/v1/subtasks/{id}', SubTaskController::class . '@update'); Route::get('/v1/subtasks/{id}', function ($id) { - return new SubTaskResource( SubTask::find($id)); + return new SubTaskResource( SubTask::published()->find($id)); }); Route::post('/v1/subtasks/{id}/timesheet', SubTaskController::class . '@submitTimesheet'); @@ -117,7 +117,9 @@ Route::middleware(['auth:sanctum', 'abilities:full-access'])->group(function () $q->whereIn('user_id', $userIDs); })->orWhere('name', 'like', '%' . $search . '%' )->get(); - $mastertasks = MasterTask::with('subtasks')->where('title', 'like', '%' . $search . '%')->orWhereIn('project_id', $projectIDs)->orWhereIn('team_id', $teams->pluck('id'))->get(); + $mastertasks = MasterTask::with(['subtasks' => function($query) { + $query->where('is_draft', false); + }])->where('title', 'like', '%' . $search . '%')->orWhereIn('project_id', $projectIDs)->orWhereIn('team_id', $teams->pluck('id'))->get(); $subtasks = $mastertasks->pluck('subtasks')->flatten();