From 5b86f2e355ba2dcf68ab41ba2b733efe7c835371 Mon Sep 17 00:00:00 2001 From: br0kenpixel <23280129+br0kenpixel@users.noreply.github.com> Date: Sat, 29 Nov 2025 12:30:52 +0100 Subject: [PATCH 01/15] refactor: rename `InternshipStatusController` to `InternshipStatusDataController` --- ...sController.php => InternshipStatusDataController.php} | 3 +-- backend/routes/api.php | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) rename backend/app/Http/Controllers/{InternshipStatusController.php => InternshipStatusDataController.php} (98%) diff --git a/backend/app/Http/Controllers/InternshipStatusController.php b/backend/app/Http/Controllers/InternshipStatusDataController.php similarity index 98% rename from backend/app/Http/Controllers/InternshipStatusController.php rename to backend/app/Http/Controllers/InternshipStatusDataController.php index 5b0820e..ec72c7b 100644 --- a/backend/app/Http/Controllers/InternshipStatusController.php +++ b/backend/app/Http/Controllers/InternshipStatusDataController.php @@ -5,11 +5,10 @@ namespace App\Http\Controllers; use App\Mail\InternshipStatusUpdated; use App\Models\Internship; use App\Models\InternshipStatus; -use App\Models\User; use Illuminate\Http\Request; use Mail; -class InternshipStatusController extends Controller +class InternshipStatusDataController extends Controller { public function get(int $id) { diff --git a/backend/routes/api.php b/backend/routes/api.php index 61c7fff..a3310eb 100644 --- a/backend/routes/api.php +++ b/backend/routes/api.php @@ -4,7 +4,7 @@ use App\Http\Controllers\Auth\RegisteredUserController; use App\Http\Controllers\CompanyController; use App\Http\Controllers\InternshipController; use App\Http\Controllers\StudentDataController; -use App\Http\Controllers\InternshipStatusController; +use App\Http\Controllers\InternshipStatusDataController; use App\Models\Company; use App\Models\StudentData; use Illuminate\Http\Request; @@ -46,9 +46,9 @@ Route::prefix('/internships')->group(function () { //Route::middleware("auth:sanctum")->group(function () { Route::prefix('/{id}')->group(function () { Route::get("/", [InternshipController::class, 'get'])->name("api.internships.get"); - 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::put("/status", [InternshipStatusDataController::class, 'update'])->name("api.internships.status.update"); + Route::get("/statuses", [InternshipStatusDataController::class, 'get'])->name("api.internships.get"); + Route::get("/next-statuses", [InternshipStatusDataController::class, 'get_next_states'])->name("api.internships.status.next.get"); Route::get("/default-agreement", [InternshipController::class, 'get_default_agreement'])->name("api.internships.agreement.default.get"); Route::get("/agreement", [InternshipController::class, 'get_agreement'])->name("api.internships.agreement.get"); Route::get("/report", [InternshipController::class, 'get_report'])->name("api.internships.report.get"); From b79c76d94717596cace480d436ad89316f85609b Mon Sep 17 00:00:00 2001 From: br0kenpixel <23280129+br0kenpixel@users.noreply.github.com> Date: Sat, 29 Nov 2025 12:32:43 +0100 Subject: [PATCH 02/15] refactor: rename `InternshipStatus` model to `InternshipStatusData` --- backend/app/Http/Controllers/CompanyController.php | 4 ++-- .../app/Http/Controllers/InternshipController.php | 4 ++-- .../Controllers/InternshipStatusDataController.php | 12 ++++++------ .../app/Http/Controllers/StudentDataController.php | 4 ++-- backend/app/Models/Internship.php | 2 +- ...InternshipStatus.php => InternshipStatusData.php} | 2 +- .../database/factories/InternshipStatusFactory.php | 2 +- backend/database/seeders/DatabaseSeeder.php | 4 ++-- 8 files changed, 17 insertions(+), 17 deletions(-) rename backend/app/Models/{InternshipStatus.php => InternshipStatusData.php} (96%) diff --git a/backend/app/Http/Controllers/CompanyController.php b/backend/app/Http/Controllers/CompanyController.php index d37e2f6..6f20bae 100644 --- a/backend/app/Http/Controllers/CompanyController.php +++ b/backend/app/Http/Controllers/CompanyController.php @@ -5,7 +5,7 @@ namespace App\Http\Controllers; use App\Models\Company; use App\Models\User; use App\Models\Internship; -use App\Models\InternshipStatus; +use App\Models\InternshipStatusData; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; @@ -186,7 +186,7 @@ class CompanyController extends Controller // mazanie statusov $internships->each(function ($internship) { - InternshipStatus::whereInternshipId($internship->id)->delete(); + InternshipStatusData::whereInternshipId($internship->id)->delete(); }); // mazanie praxov diff --git a/backend/app/Http/Controllers/InternshipController.php b/backend/app/Http/Controllers/InternshipController.php index 9d5913d..0768d61 100644 --- a/backend/app/Http/Controllers/InternshipController.php +++ b/backend/app/Http/Controllers/InternshipController.php @@ -4,7 +4,7 @@ namespace App\Http\Controllers; use App\Models\Company; use App\Models\Internship; -use App\Models\InternshipStatus; +use App\Models\InternshipStatusData; use App\Models\User; use Illuminate\Http\Request; use Mpdf\Mpdf; @@ -185,7 +185,7 @@ class InternshipController extends Controller 'agreement' => null ]); - InternshipStatus::create([ + InternshipStatusData::create([ 'internship_id' => $Internship->id, 'status' => 'SUBMITTED', 'changed' => now(), diff --git a/backend/app/Http/Controllers/InternshipStatusDataController.php b/backend/app/Http/Controllers/InternshipStatusDataController.php index ec72c7b..fabfe1d 100644 --- a/backend/app/Http/Controllers/InternshipStatusDataController.php +++ b/backend/app/Http/Controllers/InternshipStatusDataController.php @@ -4,7 +4,7 @@ namespace App\Http\Controllers; use App\Mail\InternshipStatusUpdated; use App\Models\Internship; -use App\Models\InternshipStatus; +use App\Models\InternshipStatusData; use Illuminate\Http\Request; use Mail; @@ -13,7 +13,7 @@ class InternshipStatusDataController extends Controller public function get(int $id) { $user = auth()->user(); - $internship_statuses = InternshipStatus::whereInternshipId($id)->orderByDesc('changed')->get(); + $internship_statuses = InternshipStatusData::whereInternshipId($id)->orderByDesc('changed')->get(); if (!$internship_statuses) { return response()->json([ @@ -77,7 +77,7 @@ class InternshipStatusDataController extends Controller /** * Display the specified resource. */ - public function show(InternshipStatus $internshipStatus) + public function show(InternshipStatusData $internshipStatus) { // } @@ -85,7 +85,7 @@ class InternshipStatusDataController extends Controller /** * Show the form for editing the specified resource. */ - public function edit(InternshipStatus $internshipStatus) + public function edit(InternshipStatusData $internshipStatus) { // } @@ -116,7 +116,7 @@ class InternshipStatusDataController extends Controller 'note' => ['required', 'string', 'min:1'] ]); - InternshipStatus::create([ + InternshipStatusData::create([ 'internship_id' => $id, 'status' => $request->status, 'note' => $request->note, @@ -132,7 +132,7 @@ class InternshipStatusDataController extends Controller /** * Remove the specified resource from storage. */ - public function destroy(InternshipStatus $internshipStatus) + public function destroy(InternshipStatusData $internshipStatus) { // } diff --git a/backend/app/Http/Controllers/StudentDataController.php b/backend/app/Http/Controllers/StudentDataController.php index 1d24a25..f72f5ef 100644 --- a/backend/app/Http/Controllers/StudentDataController.php +++ b/backend/app/Http/Controllers/StudentDataController.php @@ -5,7 +5,7 @@ namespace App\Http\Controllers; use App\Models\Internship; use App\Models\StudentData; use App\Models\User; -use App\Models\InternshipStatus; +use App\Models\InternshipStatusData; use Illuminate\Http\Request; use Illuminate\Support\Facades\DB; @@ -208,7 +208,7 @@ class StudentDataController extends Controller // mazanie statusov $internships->each(function ($internship) { - InternshipStatus::whereInternshipId($internship->id)->delete(); + InternshipStatusData::whereInternshipId($internship->id)->delete(); }); // mazanie praxov diff --git a/backend/app/Models/Internship.php b/backend/app/Models/Internship.php index 4330fee..272b5f8 100644 --- a/backend/app/Models/Internship.php +++ b/backend/app/Models/Internship.php @@ -63,7 +63,7 @@ class Internship extends Model public function status() { - return $this->hasOne(InternshipStatus::class, 'internship_id')->latestOfMany(); + return $this->hasOne(InternshipStatusData::class, 'internship_id')->latestOfMany(); } /** diff --git a/backend/app/Models/InternshipStatus.php b/backend/app/Models/InternshipStatusData.php similarity index 96% rename from backend/app/Models/InternshipStatus.php rename to backend/app/Models/InternshipStatusData.php index fc41517..baaec6f 100644 --- a/backend/app/Models/InternshipStatus.php +++ b/backend/app/Models/InternshipStatusData.php @@ -5,7 +5,7 @@ namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; -class InternshipStatus extends Model +class InternshipStatusData extends Model { /** @use HasFactory<\Database\Factories\InternshipStatusFactory> */ use HasFactory; diff --git a/backend/database/factories/InternshipStatusFactory.php b/backend/database/factories/InternshipStatusFactory.php index 9826179..5bf5e89 100644 --- a/backend/database/factories/InternshipStatusFactory.php +++ b/backend/database/factories/InternshipStatusFactory.php @@ -5,7 +5,7 @@ namespace Database\Factories; use Illuminate\Database\Eloquent\Factories\Factory; /** - * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\InternshipStatus> + * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\InternshipStatusData> */ class InternshipStatusFactory extends Factory { diff --git a/backend/database/seeders/DatabaseSeeder.php b/backend/database/seeders/DatabaseSeeder.php index 1e4a572..6dd478b 100644 --- a/backend/database/seeders/DatabaseSeeder.php +++ b/backend/database/seeders/DatabaseSeeder.php @@ -4,7 +4,7 @@ namespace Database\Seeders; use App\Models\Company; use App\Models\Internship; -use App\Models\InternshipStatus; +use App\Models\InternshipStatusData; use App\Models\StudentData; use App\Models\User; // use Illuminate\Database\Console\Seeds\WithoutModelEvents; @@ -57,7 +57,7 @@ class DatabaseSeeder extends Seeder 'company_id' => Company::inRandomOrder()->value('id'), ]); - InternshipStatus::factory()->create([ + InternshipStatusData::factory()->create([ 'internship_id' => $internship->id, 'status' => "SUBMITTED", 'note' => 'made by seeder', From 6175f59fe94c5d577be4ed0dbdc99811a28c6c9c Mon Sep 17 00:00:00 2001 From: br0kenpixel <23280129+br0kenpixel@users.noreply.github.com> Date: Sat, 29 Nov 2025 13:15:00 +0100 Subject: [PATCH 03/15] feat: add `laravel/tinker` for debugging --- backend/composer.json | 2 +- backend/composer.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/backend/composer.json b/backend/composer.json index db682c5..dc92586 100644 --- a/backend/composer.json +++ b/backend/composer.json @@ -12,7 +12,7 @@ "php": "^8.2", "laravel/framework": "^12.0", "laravel/sanctum": "^4.0", - "laravel/tinker": "^2.10.1", + "laravel/tinker": "^2.10", "mpdf/mpdf": "^8.2" }, "require-dev": { diff --git a/backend/composer.lock b/backend/composer.lock index 19ead8e..4f9caa0 100644 --- a/backend/composer.lock +++ b/backend/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "9911f9a2eb5e48d459c109368a23930b", + "content-hash": "3b2e99453e86a7f518baa13eca7aa622", "packages": [ { "name": "brick/math", @@ -1458,16 +1458,16 @@ }, { "name": "laravel/tinker", - "version": "v2.10.1", + "version": "v2.10.2", "source": { "type": "git", "url": "https://github.com/laravel/tinker.git", - "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3" + "reference": "3bcb5f62d6f837e0f093a601e26badafb127bd4c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/laravel/tinker/zipball/22177cc71807d38f2810c6204d8f7183d88a57d3", - "reference": "22177cc71807d38f2810c6204d8f7183d88a57d3", + "url": "https://api.github.com/repos/laravel/tinker/zipball/3bcb5f62d6f837e0f093a601e26badafb127bd4c", + "reference": "3bcb5f62d6f837e0f093a601e26badafb127bd4c", "shasum": "" }, "require": { @@ -1518,9 +1518,9 @@ ], "support": { "issues": "https://github.com/laravel/tinker/issues", - "source": "https://github.com/laravel/tinker/tree/v2.10.1" + "source": "https://github.com/laravel/tinker/tree/v2.10.2" }, - "time": "2025-01-27T14:24:01+00:00" + "time": "2025-11-20T16:29:12+00:00" }, { "name": "league/commonmark", From f17316bfdf6457bb19156db1c598478f1b3b7afb Mon Sep 17 00:00:00 2001 From: br0kenpixel <23280129+br0kenpixel@users.noreply.github.com> Date: Sat, 29 Nov 2025 13:17:08 +0100 Subject: [PATCH 04/15] refactor: rename `InternshipStatusFactory` to `InternshipStatusDataFactory` --- ...shipStatusFactory.php => InternshipStatusDataFactory.php} | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) rename backend/database/factories/{InternshipStatusFactory.php => InternshipStatusDataFactory.php} (76%) diff --git a/backend/database/factories/InternshipStatusFactory.php b/backend/database/factories/InternshipStatusDataFactory.php similarity index 76% rename from backend/database/factories/InternshipStatusFactory.php rename to backend/database/factories/InternshipStatusDataFactory.php index 5bf5e89..2512c6e 100644 --- a/backend/database/factories/InternshipStatusFactory.php +++ b/backend/database/factories/InternshipStatusDataFactory.php @@ -2,12 +2,13 @@ namespace Database\Factories; +use App\Enums\InternshipStatus; use Illuminate\Database\Eloquent\Factories\Factory; /** * @extends \Illuminate\Database\Eloquent\Factories\Factory<\App\Models\InternshipStatusData> */ -class InternshipStatusFactory extends Factory +class InternshipStatusDataFactory extends Factory { /** * Define the model's default state. @@ -18,7 +19,7 @@ class InternshipStatusFactory extends Factory { return [ 'internship_id' => 0, - 'status' => fake()->randomElement(["SUBMITTED", "CONFIRMED", "DENIED", "DEFENDED", "NOT_DEFENDED"]), + 'status' => fake()->randomElement(InternshipStatus::all()), 'changed' => fake()->dateTime(), 'note' => null, 'modified_by' => 0, From 0ff193fd1e6984762f448a9e3717c30bbb882518 Mon Sep 17 00:00:00 2001 From: br0kenpixel <23280129+br0kenpixel@users.noreply.github.com> Date: Sat, 29 Nov 2025 13:17:56 +0100 Subject: [PATCH 05/15] refactor: update `status` column to use `InternshipStatus` enum --- .../2025_10_20_193408_create_internship_statuses_table.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/backend/database/migrations/2025_10_20_193408_create_internship_statuses_table.php b/backend/database/migrations/2025_10_20_193408_create_internship_statuses_table.php index a54ffa7..fe3086a 100644 --- a/backend/database/migrations/2025_10_20_193408_create_internship_statuses_table.php +++ b/backend/database/migrations/2025_10_20_193408_create_internship_statuses_table.php @@ -1,11 +1,11 @@ id(); $table->foreignId("internship_id")->nullable(false)->constrained("internships")->onDelete("cascade"); - $table->enum("status", ["SUBMITTED", "CONFIRMED", "DENIED", "DEFENDED", "NOT_DEFENDED"])->nullable(false)->default("SUBMITTED"); + $table->enum("status", InternshipStatus::all())->nullable(false)->default(InternshipStatus::SUBMITTED); $table->dateTimeTz("changed")->nullable(false); $table->string("note")->nullable(true)->default(null); $table->foreignId("modified_by")->nullable(false)->constrained("users")->onDelete("cascade"); From e309d8ea5d74127df426d1a8225756e14d262fe2 Mon Sep 17 00:00:00 2001 From: br0kenpixel <23280129+br0kenpixel@users.noreply.github.com> Date: Sat, 29 Nov 2025 14:33:31 +0100 Subject: [PATCH 06/15] feat: implement `InternshipStatus` enum --- backend/app/Enums/InternshipStatus.php | 22 ++++++++++++++++++ backend/app/Models/InternshipStatusData.php | 16 ++++++++++++- frontend/app/types/internship_status.ts | 25 ++++++++++++++------- 3 files changed, 54 insertions(+), 9 deletions(-) create mode 100644 backend/app/Enums/InternshipStatus.php diff --git a/backend/app/Enums/InternshipStatus.php b/backend/app/Enums/InternshipStatus.php new file mode 100644 index 0000000..93a34d4 --- /dev/null +++ b/backend/app/Enums/InternshipStatus.php @@ -0,0 +1,22 @@ + $case->value, self::cases()); + } +} diff --git a/backend/app/Models/InternshipStatusData.php b/backend/app/Models/InternshipStatusData.php index baaec6f..63c4c28 100644 --- a/backend/app/Models/InternshipStatusData.php +++ b/backend/app/Models/InternshipStatusData.php @@ -33,6 +33,20 @@ class InternshipStatusData extends Model 'updated_at', ]; + protected $table = 'internship_statuses'; + + /** + * Get the attributes that should be cast. + * + * @return array + */ + protected function casts(): array + { + return [ + 'status' => '\App\Enums\InternshipStatus', + ]; + } + public function modifiedBy() { return $this->belongsTo(User::class, 'modified_by'); @@ -43,7 +57,7 @@ class InternshipStatusData extends Model return [ 'id' => $this->id, 'internship_id' => $this->internship_id, - 'status' => $this->status, + 'status' => $this->status->value, 'changed' => $this->changed, 'note' => $this->note, 'modified_by' => $this->modifiedBy, diff --git a/frontend/app/types/internship_status.ts b/frontend/app/types/internship_status.ts index 33d027c..fff5ac6 100644 --- a/frontend/app/types/internship_status.ts +++ b/frontend/app/types/internship_status.ts @@ -14,20 +14,29 @@ export interface NewInternshipStatusData { export enum InternshipStatus { SUBMITTED = 'SUBMITTED', - CONFIRMED = 'CONFIRMED', - DENIED = 'DENIED', + + CONFIRMED_BY_COMPANY = 'CONFIRMED_BY_COMPANY', + CONFIRMED_BY_ADMIN = 'CONFIRMED_BY_ADMIN', + + DENIED_BY_COMPANY = 'DENIED_BY_COMPANY', + DENIED_BY_ADMIN = 'DENIED_BY_ADMIN', + DEFENDED = 'DEFENDED', - NOT_DEFENDED = 'NOT_DEFENDED' -}; + NOT_DEFENDED = 'NOT_DEFENDED', +} export function prettyInternshipStatus(status: InternshipStatus) { switch (status) { case InternshipStatus.SUBMITTED: return "Zadané"; - case InternshipStatus.CONFIRMED: - return "Potvrdené"; - case InternshipStatus.DENIED: - return "Zamietnuté"; + case InternshipStatus.CONFIRMED_BY_COMPANY: + return "Potvrdené firmou"; + case InternshipStatus.CONFIRMED_BY_ADMIN: + return "Potvrdené garantom"; + case InternshipStatus.DENIED_BY_COMPANY: + return "Zamietnuté firmou"; + case InternshipStatus.DENIED_BY_ADMIN: + return "Zamietnuté garantom"; case InternshipStatus.DEFENDED: return "Obhájené"; case InternshipStatus.NOT_DEFENDED: From fcb6cc13fab39e5921770aa9ea12569f9c243a79 Mon Sep 17 00:00:00 2001 From: br0kenpixel <23280129+br0kenpixel@users.noreply.github.com> Date: Sat, 29 Nov 2025 14:34:05 +0100 Subject: [PATCH 07/15] refactor: simplify status transition logic in `Internship` --- .../InternshipStatusDataController.php | 37 +------------ backend/app/Models/Internship.php | 53 +++++++++++++++++++ 2 files changed, 55 insertions(+), 35 deletions(-) diff --git a/backend/app/Http/Controllers/InternshipStatusDataController.php b/backend/app/Http/Controllers/InternshipStatusDataController.php index fabfe1d..b8709f4 100644 --- a/backend/app/Http/Controllers/InternshipStatusDataController.php +++ b/backend/app/Http/Controllers/InternshipStatusDataController.php @@ -44,9 +44,7 @@ class InternshipStatusDataController extends Controller abort(403, 'Unauthorized'); } - $currentStatus = $internship->status; - $nextPossibleStatuses = $this->possibleNewStatuses($currentStatus->status, $user->role, $internship->report_confirmed); - + $nextPossibleStatuses = $internship->nextStates($user->role); return response()->json($nextPossibleStatuses); } @@ -109,7 +107,7 @@ class InternshipStatusDataController extends Controller } $internshipStatus = $internship->status; - $newStatusValidator = 'in:' . implode(',', $this->possibleNewStatuses($internshipStatus->status, $user->role, $internship->report_confirmed)); + $newStatusValidator = 'in:' . implode(',', $internship->nextStates($user->role)); $request->validate([ 'status' => ['required', 'string', 'uppercase', $newStatusValidator], @@ -136,35 +134,4 @@ class InternshipStatusDataController extends Controller { // } - - private function possibleNewStatuses(string $current_status, string $userRole, bool $report_confirmed) - { - if ($userRole === "STUDENT") - return []; - - switch ($current_status) { - case 'SUBMITTED': - return ['CONFIRMED', 'DENIED']; - case 'CONFIRMED': - if ($userRole === 'EMPLOYER') { - return ['DENIED']; - } - - if ($report_confirmed) { - return ['SUBMITTED', 'DENIED', 'DEFENDED', 'NOT_DEFENDED']; - } - - return ['SUBMITTED', 'DENIED']; - case 'DENIED': - if ($userRole === 'EMPLOYER') { - return ['CONFIRMED']; - } - return ['SUBMITTED', 'CONFIRMED']; - case 'DEFENDED': - case 'NOT_DEFENDED': - return []; - default: - throw new \InvalidArgumentException('Unknown status'); - } - } } diff --git a/backend/app/Models/Internship.php b/backend/app/Models/Internship.php index 272b5f8..eb66005 100644 --- a/backend/app/Models/Internship.php +++ b/backend/app/Models/Internship.php @@ -2,6 +2,7 @@ namespace App\Models; +use App\Enums\InternshipStatus; use Carbon\Carbon; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; @@ -66,6 +67,58 @@ class Internship extends Model return $this->hasOne(InternshipStatusData::class, 'internship_id')->latestOfMany(); } + public function nextStates(string $userRole) + { + $current_status = $this->status->status; + $report_confirmed = $this->report_confirmed; + + // študent nemôže meniť stav + if ($userRole === 'STUDENT') + return []; + + /* + nasledujúci platný stav je určený podľa: + - aktuálneho stavu + - roly používateľa, ktorý ide meniť stav + - či bol výkaz potvrdený firmou + */ + return match (true) { + // prax bola iba vytvorená a ide ju meniť admin alebo firma + $current_status === InternshipStatus::SUBMITTED + => ['CONFIRMED_BY_COMPANY', 'DENIED_BY_COMPANY'], + + // prax bola potvrdená firmou a ide ju meniť admin + $current_status === InternshipStatus::CONFIRMED_BY_COMPANY && $userRole === "ADMIN" + => ['CONFIRMED_BY_ADMIN', 'DENIED_BY_ADMIN'], + + // prax bola potvrdená firmou a ide ju meniť firma + $current_status === InternshipStatus::CONFIRMED_BY_COMPANY && $userRole === "EMPLOYER" + => ['DENIED_BY_COMPANY'], + + // prax bola zamietnutá firmou a ide ju meniť admin + $current_status === InternshipStatus::DENIED_BY_COMPANY && $userRole === "ADMIN" + => ['CONFIRMED_BY_COMPANY', 'SUBMITTED'], + + // prax bola potvrdená garantom, ide ju meniť admin a výkaz bol potvrdený firmou + $current_status === InternshipStatus::CONFIRMED_BY_ADMIN && $userRole === "ADMIN" && $report_confirmed + => ['DENIED_BY_ADMIN', 'CONFIRMED_BY_COMPANY', 'DENIED_BY_COMPANY', 'DEFENDED', 'NOT_DEFENDED'], + + // prax bola potvrdená garantom, ide ju meniť admin a výkaz nebol potvrdený firmou + $current_status === InternshipStatus::CONFIRMED_BY_ADMIN && $userRole === "ADMIN" && !$report_confirmed + => ['DENIED_BY_ADMIN', 'CONFIRMED_BY_COMPANY', 'DENIED_BY_COMPANY'], + + // prax bola obhájená a ide ju meniť admin + $current_status === InternshipStatus::DEFENDED && $userRole === "ADMIN" + => ['NOT_DEFENDED'], + + // prax nebola obhájená a ide ju meniť admin + $current_status === InternshipStatus::NOT_DEFENDED && $userRole === "ADMIN" + => ['DEFENDED'], + + default => [] + }; + } + /** * Prepare the model for JSON serialization. * From 5b84b67a653c58f7d7abda1ab29294cfe27d02ba Mon Sep 17 00:00:00 2001 From: br0kenpixel <23280129+br0kenpixel@users.noreply.github.com> Date: Sat, 29 Nov 2025 14:34:24 +0100 Subject: [PATCH 08/15] fix: update status check for document upload to use new statuses --- frontend/app/pages/dashboard/company/internships/edit/[id].vue | 2 +- frontend/app/pages/dashboard/student/internship/edit/[id].vue | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/frontend/app/pages/dashboard/company/internships/edit/[id].vue b/frontend/app/pages/dashboard/company/internships/edit/[id].vue index 5a9fdab..74f31b2 100644 --- a/frontend/app/pages/dashboard/company/internships/edit/[id].vue +++ b/frontend/app/pages/dashboard/company/internships/edit/[id].vue @@ -95,7 +95,7 @@ async function handleUpdateOfBasicInfo(internship: NewInternship) {

Nahratie dokumentov

- diff --git a/frontend/app/pages/dashboard/student/internship/edit/[id].vue b/frontend/app/pages/dashboard/student/internship/edit/[id].vue index a7b8593..62c46e3 100644 --- a/frontend/app/pages/dashboard/student/internship/edit/[id].vue +++ b/frontend/app/pages/dashboard/student/internship/edit/[id].vue @@ -88,7 +88,7 @@ async function handleUpdateOfBasicInfo(internship: NewInternship) {

Nahratie dokumentov

- From 68011329211543c030af326940d67dae85d50d7d Mon Sep 17 00:00:00 2001 From: br0kenpixel <23280129+br0kenpixel@users.noreply.github.com> Date: Sat, 29 Nov 2025 14:56:33 +0100 Subject: [PATCH 09/15] refactor: rename `agreement` to `proof` --- .../Http/Controllers/InternshipController.php | 28 +++++++++---------- backend/app/Models/Internship.php | 4 +-- .../database/factories/InternshipFactory.php | 2 +- ..._10_20_193012_create_internships_table.php | 5 ++-- backend/routes/api.php | 4 +-- .../InternshipAgreementDownloader.vue | 4 +-- .../components/InternshipDocumentEditor.vue | 28 +++++++++---------- .../components/InternshipDocumentViewer.vue | 25 ++++++++++++----- frontend/app/types/internships.ts | 2 +- .../e2e/admin/internships/downloads.cy.ts | 2 +- 10 files changed, 57 insertions(+), 47 deletions(-) diff --git a/backend/app/Http/Controllers/InternshipController.php b/backend/app/Http/Controllers/InternshipController.php index 0768d61..a7d87ff 100644 --- a/backend/app/Http/Controllers/InternshipController.php +++ b/backend/app/Http/Controllers/InternshipController.php @@ -61,7 +61,7 @@ class InternshipController extends Controller return response()->json($internship); } - public function get_default_agreement(Request $request, int $id) + public function get_default_proof(Request $request, int $id) { $user = auth()->user(); $internship = Internship::find($id); @@ -78,7 +78,7 @@ class InternshipController extends Controller $contact = User::find($internship->company->contact); - $html = view('agreement.default', [ + $html = view('proof.default', [ 'company' => $internship->company, 'companyContact' => $contact, 'internship' => $internship, @@ -93,10 +93,10 @@ class InternshipController extends Controller return response($pdf->Output('', 'S'), 200) ->header('Content-Type', 'application/pdf') - ->header('Content-Disposition', 'attachment; filename="agreement_' . $id . '.pdf"'); + ->header('Content-Disposition', 'attachment; filename="proof_' . $id . '.pdf"'); } - public function get_agreement(int $id) + public function get_proof(int $id) { $user = auth()->user(); $internship = Internship::find($id); @@ -107,9 +107,9 @@ class InternshipController extends Controller ], 400); } - if (!$internship->agreement) { + if (!$internship->proof) { return response()->json([ - 'message' => 'No agreement file exists for this internship.' + 'message' => 'No proof file exists for this internship.' ], 404); } @@ -117,9 +117,9 @@ class InternshipController extends Controller abort(403, 'Unauthorized'); } - return response($internship->agreement, 200) + return response($internship->proof, 200) ->header('Content-Type', 'application/pdf') - ->header('Content-Disposition', 'attachment; filename="agreement_' . $id . '.pdf"'); + ->header('Content-Disposition', 'attachment; filename="proof_' . $id . '.pdf"'); } public function get_report(int $id) @@ -182,7 +182,7 @@ class InternshipController extends Controller 'year_of_study' => $request->year_of_study, 'semester' => $request->semester, 'position_description' => $request->position_description, - 'agreement' => null + 'proof' => null ]); InternshipStatusData::create([ @@ -250,13 +250,13 @@ class InternshipController extends Controller } $request->validate([ - 'agreement' => ['nullable', 'file', 'mimes:pdf', 'max:10240'], + 'proof' => ['nullable', 'file', 'mimes:pdf', 'max:10240'], 'report' => ['nullable', 'file', 'mimes:pdf', 'max:10240'], 'report_confirmed' => ['required', 'boolean'], ]); - if ($request->hasFile('agreement')) { - $internship->agreement = file_get_contents($request->file('agreement')->getRealPath()); + if ($request->hasFile('proof')) { + $internship->proof = file_get_contents($request->file('proof')->getRealPath()); } if ($request->hasFile('report')) { @@ -264,9 +264,9 @@ class InternshipController extends Controller } if ($user->role === 'EMPLOYER') { - if ($request->report_confirmed && (!$internship->agreement || !$internship->report)) { + if ($request->report_confirmed && (!$internship->proof || !$internship->report)) { return response()->json([ - 'message' => 'Report cannot be confirmed without an agreement and report.' + 'message' => 'Report cannot be confirmed without an proof and report.' ], 400); } diff --git a/backend/app/Models/Internship.php b/backend/app/Models/Internship.php index eb66005..fa77655 100644 --- a/backend/app/Models/Internship.php +++ b/backend/app/Models/Internship.php @@ -25,7 +25,7 @@ class Internship extends Model 'year_of_study', 'semester', 'position_description', - 'agreement', + 'proof', 'report', 'report_confirmed', ]; @@ -135,7 +135,7 @@ class Internship extends Model 'year_of_study' => $this->year_of_study, 'semester' => $this->semester, 'position_description' => $this->position_description, - 'agreement' => $this->agreement !== null, + 'proof' => $this->proof !== null, 'report' => $this->report !== null, 'report_confirmed' => $this->report_confirmed, 'status' => $this->status, diff --git a/backend/database/factories/InternshipFactory.php b/backend/database/factories/InternshipFactory.php index 4b0e117..f2dd600 100644 --- a/backend/database/factories/InternshipFactory.php +++ b/backend/database/factories/InternshipFactory.php @@ -27,7 +27,7 @@ class InternshipFactory extends Factory 'year_of_study' => fake()->randomElement([1, 2, 3, 4, 5]), 'semester' => fake()->randomElement(["WINTER", "SUMMER"]), 'position_description' => fake()->jobTitle(), - 'agreement' => null, + 'proof' => null, 'report' => null, 'report_confirmed' => false, ]; diff --git a/backend/database/migrations/2025_10_20_193012_create_internships_table.php b/backend/database/migrations/2025_10_20_193012_create_internships_table.php index 83571d4..b85d00c 100644 --- a/backend/database/migrations/2025_10_20_193012_create_internships_table.php +++ b/backend/database/migrations/2025_10_20_193012_create_internships_table.php @@ -4,8 +4,7 @@ use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; -return new class extends Migration -{ +return new class extends Migration { /** * Run the migrations. */ @@ -20,7 +19,7 @@ return new class extends Migration $table->unsignedSmallInteger("year_of_study")->nullable(false); $table->enum("semester", ["WINTER", "SUMMER"])->nullable(false); $table->string("position_description")->nullable(false); - $table->binary("agreement")->nullable(true); + $table->binary("proof")->nullable(true); $table->binary("report")->nullable(true); $table->boolean("report_confirmed")->nullable(false)->default(false); $table->timestamps(); diff --git a/backend/routes/api.php b/backend/routes/api.php index a3310eb..a21ab05 100644 --- a/backend/routes/api.php +++ b/backend/routes/api.php @@ -49,8 +49,8 @@ Route::prefix('/internships')->group(function () { Route::put("/status", [InternshipStatusDataController::class, 'update'])->name("api.internships.status.update"); Route::get("/statuses", [InternshipStatusDataController::class, 'get'])->name("api.internships.get"); Route::get("/next-statuses", [InternshipStatusDataController::class, 'get_next_states'])->name("api.internships.status.next.get"); - Route::get("/default-agreement", [InternshipController::class, 'get_default_agreement'])->name("api.internships.agreement.default.get"); - Route::get("/agreement", [InternshipController::class, 'get_agreement'])->name("api.internships.agreement.get"); + Route::get("/default-proof", [InternshipController::class, 'get_default_proof'])->name("api.internships.proof.default.get"); + Route::get("/proof", [InternshipController::class, 'get_proof'])->name("api.internships.proof.get"); Route::get("/report", [InternshipController::class, 'get_report'])->name("api.internships.report.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/InternshipAgreementDownloader.vue b/frontend/app/components/InternshipAgreementDownloader.vue index c82c0b9..ac18af3 100644 --- a/frontend/app/components/InternshipAgreementDownloader.vue +++ b/frontend/app/components/InternshipAgreementDownloader.vue @@ -13,8 +13,8 @@ async function requestDownload() { loading.value = true; try { - const agreement = await client(`/api/internships/${props.internship_id}/default-agreement`); - triggerDownload(agreement, `default-agreement-${props.internship_id}`); + const proof = await client(`/api/internships/${props.internship_id}/default-proof`); + triggerDownload(proof, `default-proof-${props.internship_id}`); } catch (e) { if (e instanceof FetchError) { alert(`Nepodarilo sa vygenerovať zmluvu: ${e.statusMessage}`); diff --git a/frontend/app/components/InternshipDocumentEditor.vue b/frontend/app/components/InternshipDocumentEditor.vue index d0dbd83..0524d42 100644 --- a/frontend/app/components/InternshipDocumentEditor.vue +++ b/frontend/app/components/InternshipDocumentEditor.vue @@ -18,7 +18,7 @@ const rules = { const loading = ref(false); const error = ref(null); -const agreement = ref(null); +const proof = ref(null); const report = ref(null); const report_confirmed = ref(props.internship.report_confirmed); @@ -35,9 +35,9 @@ function triggerDownload(file: Blob, file_name: string) { window.URL.revokeObjectURL(url); } -async function downloadAgreement() { - const agreement: Blob = await client(`/api/internships/${props.internship.id}/agreement`); - triggerDownload(agreement, `agreement-${props.internship.id}`); +async function downloadProof() { + const proof: Blob = await client(`/api/internships/${props.internship.id}/proof`); + triggerDownload(proof, `proof-${props.internship.id}`); } async function downloadReport() { @@ -51,8 +51,8 @@ async function onSubmit() { const formData = new FormData(); formData.append('report_confirmed', report_confirmed.value ? '1' : '0'); - if (agreement.value) { - formData.append('agreement', agreement.value); + if (proof.value) { + formData.append('proof', proof.value); } if (report.value) { formData.append('report', report.value); @@ -64,7 +64,7 @@ async function onSubmit() { body: formData }); - agreement.value = null; + proof.value = null; report.value = null; emit('successfulSubmit'); } catch (e) { @@ -87,18 +87,18 @@ async function onSubmit() {
-

Podpísaná zmluva / dohoda

+

Dokument na overenie praxe

- +
- + Stiahnuť
-
@@ -123,14 +123,14 @@ async function onSubmit() { hint="Povolené: PDF, max 10 MB" persistent-hint />

+ :disabled="!proof && !report && (!props.internship.proof || !props.internship.report)"> Uloziť diff --git a/frontend/app/components/InternshipDocumentViewer.vue b/frontend/app/components/InternshipDocumentViewer.vue index d4dbb63..cce2841 100644 --- a/frontend/app/components/InternshipDocumentViewer.vue +++ b/frontend/app/components/InternshipDocumentViewer.vue @@ -8,8 +8,8 @@ const props = defineProps<{ const client = useSanctumClient(); async function downloadAgreement() { - const agreement: Blob = await client(`/api/internships/${props.internship.id}/agreement`); - triggerDownload(agreement, `agreement-${props.internship.id}`); + const proof: Blob = await client(`/api/internships/${props.internship.id}/proof`); + triggerDownload(proof, `proof-${props.internship.id}`); } async function downloadReport() { @@ -21,18 +21,21 @@ async function downloadReport() {