From eca96161ba6f999857605674ec38fae5522be907 Mon Sep 17 00:00:00 2001 From: br0kenpixel <23280129+br0kenpixel@users.noreply.github.com> Date: Mon, 3 Nov 2025 19:36:10 +0100 Subject: [PATCH] feat: implement document uploading for students --- .../Http/Controllers/InternshipController.php | 50 +++++ backend/routes/api.php | 1 + .../components/InternshipDocumentEditor.vue | 100 +++++++++ .../student/internship/edit/[id].vue | 203 ++---------------- frontend/app/types/internships.ts | 5 +- 5 files changed, 167 insertions(+), 192 deletions(-) create mode 100644 frontend/app/components/InternshipDocumentEditor.vue diff --git a/backend/app/Http/Controllers/InternshipController.php b/backend/app/Http/Controllers/InternshipController.php index 1eeb796..62b7bea 100644 --- a/backend/app/Http/Controllers/InternshipController.php +++ b/backend/app/Http/Controllers/InternshipController.php @@ -46,6 +46,11 @@ class InternshipController extends Controller $internship->end = Carbon::parse($internship->end)->format('d.m.Y'); }); + $internships->each(function ($internship) { + $internship->agreement = $internship->agreement !== null; + $internship->report = $internship->report !== null; + }); + return response()->json($internships); } @@ -73,6 +78,11 @@ class InternshipController extends Controller $internship->end = Carbon::parse($internship->end)->format('d.m.Y'); }); + $internships->each(function ($internship) { + $internship->agreement = $internship->agreement !== null; + $internship->report = $internship->report !== null; + }); + return response()->json($internships); } @@ -100,6 +110,9 @@ class InternshipController extends Controller $internship->status = InternshipStatus::whereColumn('internship_id', '=', $internship->id)->orderByDesc('changed')->get()->first()->makeHidden(['created_at', 'updated_at', 'id']); $internship->status->modified_by = User::find($internship->status->modified_by)->makeHidden(['created_at', 'updated_at', 'email_verified_at']); + $internship->agreement = $internship->agreement !== null; + $internship->report = $internship->report !== null; + return response()->json($internship); } @@ -189,6 +202,43 @@ class InternshipController extends Controller return response()->noContent(); } + public function update_documents(int $id, Request $request) { + $user = auth()->user(); + $internship = Internship::find($id); + + if(!$internship) { + return response()->json([ + 'message' => 'No such internship exists.' + ], 400); + } + + if ($user->role !== 'ADMIN' && $internship->user_id !== $user->id && $user->id !== $internship->contact) { + abort(403, 'Unauthorized'); + } + + $request->validate([ + 'agreement' => ['nullable', 'file', 'mimes:pdf', 'max:10240'], + 'report' => ['nullable', 'file', 'mimes:pdf', 'max:10240'] + ]); + + if (!$request->hasFile('agreement') && !$request->hasFile('report')) { + return response()->json([ + 'message' => 'At least one document (agreement or report) must be provided.' + ], 400); + } + + if ($request->hasFile('agreement')) { + $internship->agreement = file_get_contents($request->file('agreement')->getRealPath()); + } + + if ($request->hasFile('report')) { + $internship->report = file_get_contents($request->file('report')->getRealPath()); + } + + $internship->save(); + return response()->noContent(); + } + /** * Update the specified resource in storage. */ diff --git a/backend/routes/api.php b/backend/routes/api.php index 8eef125..367f259 100644 --- a/backend/routes/api.php +++ b/backend/routes/api.php @@ -42,6 +42,7 @@ Route::prefix('/internships')->group(function () { Route::put("/status", [InternshipStatusController::class, 'update'])->name("api.internships.status.update"); Route::get("/statuses", [InternshipStatusController::class, 'get'])->name("api.internships.get"); Route::get("/next-statuses", [InternshipStatusController::class, 'get_next_states'])->name("api.internships.status.next.get"); + Route::post("/documents", [InternshipController::class, 'update_documents'])->name("api.internships.documents.set"); Route::post("/basic", [InternshipController::class, 'update_basic'])->name("api.internships.update.basic"); }); diff --git a/frontend/app/components/InternshipDocumentEditor.vue b/frontend/app/components/InternshipDocumentEditor.vue new file mode 100644 index 0000000..deaa7c0 --- /dev/null +++ b/frontend/app/components/InternshipDocumentEditor.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/frontend/app/pages/dashboard/student/internship/edit/[id].vue b/frontend/app/pages/dashboard/student/internship/edit/[id].vue index 49cd7ca..363ea28 100644 --- a/frontend/app/pages/dashboard/student/internship/edit/[id].vue +++ b/frontend/app/pages/dashboard/student/internship/edit/[id].vue @@ -1,10 +1,6 @@ \ No newline at end of file diff --git a/frontend/app/types/internships.ts b/frontend/app/types/internships.ts index 59a3776..96e285a 100644 --- a/frontend/app/types/internships.ts +++ b/frontend/app/types/internships.ts @@ -12,8 +12,8 @@ export interface Internship { year_of_study: number; semester: string; position_description: string; - agreement?: Uint8Array; - report?: Uint8Array; + agreement: boolean; + report: boolean; report_confirmed: boolean; status: InternshipStatusData; }; @@ -26,5 +26,4 @@ export interface NewInternship { year_of_study: number; semester: string; position_description: string; - agreement?: Uint8Array; }; \ No newline at end of file