refactor: rename agreement to proof

This commit is contained in:
2025-11-29 14:56:33 +01:00
parent 5b84b67a65
commit 6801132921
10 changed files with 57 additions and 47 deletions

View File

@@ -61,7 +61,7 @@ class InternshipController extends Controller
return response()->json($internship); 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(); $user = auth()->user();
$internship = Internship::find($id); $internship = Internship::find($id);
@@ -78,7 +78,7 @@ class InternshipController extends Controller
$contact = User::find($internship->company->contact); $contact = User::find($internship->company->contact);
$html = view('agreement.default', [ $html = view('proof.default', [
'company' => $internship->company, 'company' => $internship->company,
'companyContact' => $contact, 'companyContact' => $contact,
'internship' => $internship, 'internship' => $internship,
@@ -93,10 +93,10 @@ class InternshipController extends Controller
return response($pdf->Output('', 'S'), 200) return response($pdf->Output('', 'S'), 200)
->header('Content-Type', 'application/pdf') ->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(); $user = auth()->user();
$internship = Internship::find($id); $internship = Internship::find($id);
@@ -107,9 +107,9 @@ class InternshipController extends Controller
], 400); ], 400);
} }
if (!$internship->agreement) { if (!$internship->proof) {
return response()->json([ return response()->json([
'message' => 'No agreement file exists for this internship.' 'message' => 'No proof file exists for this internship.'
], 404); ], 404);
} }
@@ -117,9 +117,9 @@ class InternshipController extends Controller
abort(403, 'Unauthorized'); abort(403, 'Unauthorized');
} }
return response($internship->agreement, 200) return response($internship->proof, 200)
->header('Content-Type', 'application/pdf') ->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) public function get_report(int $id)
@@ -182,7 +182,7 @@ class InternshipController extends Controller
'year_of_study' => $request->year_of_study, 'year_of_study' => $request->year_of_study,
'semester' => $request->semester, 'semester' => $request->semester,
'position_description' => $request->position_description, 'position_description' => $request->position_description,
'agreement' => null 'proof' => null
]); ]);
InternshipStatusData::create([ InternshipStatusData::create([
@@ -250,13 +250,13 @@ class InternshipController extends Controller
} }
$request->validate([ $request->validate([
'agreement' => ['nullable', 'file', 'mimes:pdf', 'max:10240'], 'proof' => ['nullable', 'file', 'mimes:pdf', 'max:10240'],
'report' => ['nullable', 'file', 'mimes:pdf', 'max:10240'], 'report' => ['nullable', 'file', 'mimes:pdf', 'max:10240'],
'report_confirmed' => ['required', 'boolean'], 'report_confirmed' => ['required', 'boolean'],
]); ]);
if ($request->hasFile('agreement')) { if ($request->hasFile('proof')) {
$internship->agreement = file_get_contents($request->file('agreement')->getRealPath()); $internship->proof = file_get_contents($request->file('proof')->getRealPath());
} }
if ($request->hasFile('report')) { if ($request->hasFile('report')) {
@@ -264,9 +264,9 @@ class InternshipController extends Controller
} }
if ($user->role === 'EMPLOYER') { if ($user->role === 'EMPLOYER') {
if ($request->report_confirmed && (!$internship->agreement || !$internship->report)) { if ($request->report_confirmed && (!$internship->proof || !$internship->report)) {
return response()->json([ return response()->json([
'message' => 'Report cannot be confirmed without an agreement and report.' 'message' => 'Report cannot be confirmed without an proof and report.'
], 400); ], 400);
} }

View File

@@ -25,7 +25,7 @@ class Internship extends Model
'year_of_study', 'year_of_study',
'semester', 'semester',
'position_description', 'position_description',
'agreement', 'proof',
'report', 'report',
'report_confirmed', 'report_confirmed',
]; ];
@@ -135,7 +135,7 @@ class Internship extends Model
'year_of_study' => $this->year_of_study, 'year_of_study' => $this->year_of_study,
'semester' => $this->semester, 'semester' => $this->semester,
'position_description' => $this->position_description, 'position_description' => $this->position_description,
'agreement' => $this->agreement !== null, 'proof' => $this->proof !== null,
'report' => $this->report !== null, 'report' => $this->report !== null,
'report_confirmed' => $this->report_confirmed, 'report_confirmed' => $this->report_confirmed,
'status' => $this->status, 'status' => $this->status,

View File

@@ -27,7 +27,7 @@ class InternshipFactory extends Factory
'year_of_study' => fake()->randomElement([1, 2, 3, 4, 5]), 'year_of_study' => fake()->randomElement([1, 2, 3, 4, 5]),
'semester' => fake()->randomElement(["WINTER", "SUMMER"]), 'semester' => fake()->randomElement(["WINTER", "SUMMER"]),
'position_description' => fake()->jobTitle(), 'position_description' => fake()->jobTitle(),
'agreement' => null, 'proof' => null,
'report' => null, 'report' => null,
'report_confirmed' => false, 'report_confirmed' => false,
]; ];

View File

@@ -4,8 +4,7 @@ use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint; use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema; use Illuminate\Support\Facades\Schema;
return new class extends Migration return new class extends Migration {
{
/** /**
* Run the migrations. * Run the migrations.
*/ */
@@ -20,7 +19,7 @@ return new class extends Migration
$table->unsignedSmallInteger("year_of_study")->nullable(false); $table->unsignedSmallInteger("year_of_study")->nullable(false);
$table->enum("semester", ["WINTER", "SUMMER"])->nullable(false); $table->enum("semester", ["WINTER", "SUMMER"])->nullable(false);
$table->string("position_description")->nullable(false); $table->string("position_description")->nullable(false);
$table->binary("agreement")->nullable(true); $table->binary("proof")->nullable(true);
$table->binary("report")->nullable(true); $table->binary("report")->nullable(true);
$table->boolean("report_confirmed")->nullable(false)->default(false); $table->boolean("report_confirmed")->nullable(false)->default(false);
$table->timestamps(); $table->timestamps();

View File

@@ -49,8 +49,8 @@ Route::prefix('/internships')->group(function () {
Route::put("/status", [InternshipStatusDataController::class, 'update'])->name("api.internships.status.update"); Route::put("/status", [InternshipStatusDataController::class, 'update'])->name("api.internships.status.update");
Route::get("/statuses", [InternshipStatusDataController::class, 'get'])->name("api.internships.get"); 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("/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("/default-proof", [InternshipController::class, 'get_default_proof'])->name("api.internships.proof.default.get");
Route::get("/agreement", [InternshipController::class, 'get_agreement'])->name("api.internships.agreement.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::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("/documents", [InternshipController::class, 'update_documents'])->name("api.internships.documents.set");
Route::post("/basic", [InternshipController::class, 'update_basic'])->name("api.internships.update.basic"); Route::post("/basic", [InternshipController::class, 'update_basic'])->name("api.internships.update.basic");

View File

@@ -13,8 +13,8 @@ async function requestDownload() {
loading.value = true; loading.value = true;
try { try {
const agreement = await client<Blob>(`/api/internships/${props.internship_id}/default-agreement`); const proof = await client<Blob>(`/api/internships/${props.internship_id}/default-proof`);
triggerDownload(agreement, `default-agreement-${props.internship_id}`); triggerDownload(proof, `default-proof-${props.internship_id}`);
} catch (e) { } catch (e) {
if (e instanceof FetchError) { if (e instanceof FetchError) {
alert(`Nepodarilo sa vygenerovať zmluvu: ${e.statusMessage}`); alert(`Nepodarilo sa vygenerovať zmluvu: ${e.statusMessage}`);

View File

@@ -18,7 +18,7 @@ const rules = {
const loading = ref(false); const loading = ref(false);
const error = ref<string | null>(null); const error = ref<string | null>(null);
const agreement = ref<File | null>(null); const proof = ref<File | null>(null);
const report = ref<File | null>(null); const report = ref<File | null>(null);
const report_confirmed = ref(props.internship.report_confirmed); const report_confirmed = ref(props.internship.report_confirmed);
@@ -35,9 +35,9 @@ function triggerDownload(file: Blob, file_name: string) {
window.URL.revokeObjectURL(url); window.URL.revokeObjectURL(url);
} }
async function downloadAgreement() { async function downloadProof() {
const agreement: Blob = await client(`/api/internships/${props.internship.id}/agreement`); const proof: Blob = await client(`/api/internships/${props.internship.id}/proof`);
triggerDownload(agreement, `agreement-${props.internship.id}`); triggerDownload(proof, `proof-${props.internship.id}`);
} }
async function downloadReport() { async function downloadReport() {
@@ -51,8 +51,8 @@ async function onSubmit() {
const formData = new FormData(); const formData = new FormData();
formData.append('report_confirmed', report_confirmed.value ? '1' : '0'); formData.append('report_confirmed', report_confirmed.value ? '1' : '0');
if (agreement.value) { if (proof.value) {
formData.append('agreement', agreement.value); formData.append('proof', proof.value);
} }
if (report.value) { if (report.value) {
formData.append('report', report.value); formData.append('report', report.value);
@@ -64,7 +64,7 @@ async function onSubmit() {
body: formData body: formData
}); });
agreement.value = null; proof.value = null;
report.value = null; report.value = null;
emit('successfulSubmit'); emit('successfulSubmit');
} catch (e) { } catch (e) {
@@ -87,18 +87,18 @@ async function onSubmit() {
<v-form @submit.prevent="onSubmit" :disabled="loading"> <v-form @submit.prevent="onSubmit" :disabled="loading">
<div> <div>
<h4 class="mb-2">Podpísaná zmluva / dohoda</h4> <h4 class="mb-2">Dokument na overenie praxe</h4>
<WarningAlert v-if="props.internship.agreement" title="Existujúci dokument" <WarningAlert v-if="props.internship.proof" title="Existujúci dokument"
text="V systéme je už nahratá zmluva/dohoda. Ak chcete nahradiť existujúcu verziu, vyberte súbor, alebo v opačnom prípade nechajte toto pole nevyplnené."> text="V systéme je už nahratý dokument k praxe. Ak chcete nahradiť existujúcu verziu, vyberte súbor, alebo v opačnom prípade nechajte toto pole nevyplnené.">
<br /> <br />
<v-btn prepend-icon="mdi-download" color="blue" class="mr-2 mt-2" @click="downloadAgreement"> <v-btn prepend-icon="mdi-download" color="blue" class="mr-2 mt-2" @click="downloadProof">
Stiahnuť Stiahnuť
</v-btn> </v-btn>
</WarningAlert> </WarningAlert>
<v-file-input v-model="agreement" :rules="[rules.isPdf, rules.maxSize]" accept=".pdf,application/pdf" <v-file-input v-model="proof" :rules="[rules.isPdf, rules.maxSize]" accept=".pdf,application/pdf"
prepend-icon="mdi-handshake" label="Nahrať PDF zmluvu" variant="outlined" show-size clearable prepend-icon="mdi-handshake" label="Nahrať PDF zmluvu" variant="outlined" show-size clearable
hint="Povolené: PDF, max 10 MB" persistent-hint /> hint="Povolené: PDF, max 10 MB" persistent-hint />
</div> </div>
@@ -123,14 +123,14 @@ async function onSubmit() {
hint="Povolené: PDF, max 10 MB" persistent-hint /> hint="Povolené: PDF, max 10 MB" persistent-hint />
<v-checkbox v-if="user?.role === Role.EMPLOYER" <v-checkbox v-if="user?.role === Role.EMPLOYER"
:disabled="!props.internship.agreement || !props.internship.report" v-model="report_confirmed" :disabled="!props.internship.proof || !props.internship.report" v-model="report_confirmed"
label="Výkaz je správny"></v-checkbox> label="Výkaz je správny"></v-checkbox>
</div> </div>
<br /> <br />
<v-btn type="submit" color="success" size="large" block <v-btn type="submit" color="success" size="large" block
:disabled="!agreement && !report && (!props.internship.agreement || !props.internship.report)"> :disabled="!proof && !report && (!props.internship.proof || !props.internship.report)">
Uloziť Uloziť
</v-btn> </v-btn>
</v-form> </v-form>

View File

@@ -8,8 +8,8 @@ const props = defineProps<{
const client = useSanctumClient(); const client = useSanctumClient();
async function downloadAgreement() { async function downloadAgreement() {
const agreement: Blob = await client(`/api/internships/${props.internship.id}/agreement`); const proof: Blob = await client(`/api/internships/${props.internship.id}/proof`);
triggerDownload(agreement, `agreement-${props.internship.id}`); triggerDownload(proof, `proof-${props.internship.id}`);
} }
async function downloadReport() { async function downloadReport() {
@@ -21,18 +21,21 @@ async function downloadReport() {
<template> <template>
<div> <div>
<v-row class="d-flex"> <v-row class="d-flex">
<!-- Podpísaná zmluva --> <!-- Podpísaný dokument k praxe -->
<v-col cols="12" md="6"> <v-col cols="12" md="6">
<v-card variant="outlined" class="h-100"> <v-card variant="outlined" class="h-100">
<v-card-title class="d-flex align-center ga-2"> <v-card-title class="d-flex align-center ga-2">
<v-icon icon="mdi mdi-file-document-outline" /> <v-icon icon="mdi mdi-file-document-outline" />
Podpísaná zmluva / dohoda Dokument o vykonaní praxe
</v-card-title> </v-card-title>
<v-card-text> <v-card-text>
<p>Zmluva/dohoda o brigádnickej praxi alebo 3 faktúry v pre živnostníkov.</p>
<InternshipAgreementDownloader :internship_id="internship.id" /> <InternshipAgreementDownloader :internship_id="internship.id" />
<WarningAlert v-if="!props.internship.agreement" title="Neodovzdané" <WarningAlert v-if="!props.internship.proof" title="Neodovzdané"
text="Zmluva zatiaľ nebola nahratá." /> text="Dokument zatiaľ nebol nahratý." />
<div v-else> <div v-else>
<SuccessAlert title="Odovzdané" text="Zmluva bola nahratá." /> <SuccessAlert title="Odovzdané" text="Zmluva bola nahratá." />
@@ -53,8 +56,16 @@ async function downloadReport() {
<v-icon icon="mdi-file-clock-outline" /> <v-icon icon="mdi-file-clock-outline" />
Výkaz Výkaz
</v-card-title> </v-card-title>
<v-card-text> <v-card-text>
<InfoAlert v-if="!props.internship.report" title="Neodovzdané" <p>Dokument o hodnotení praxe.</p>
<v-btn prepend-icon="mdi-download" color="blue" class="mr-2 mt-2" block target="_blank"
href="https://www.fpvai.ukf.sk/images/Organizacia_studia/odborna_prax/aplikovana_informatika/Priloha_Vykaz_o_vykonanej_odbornej_praxi-AI.docx">
<span>Stiahnuť šablónu na výkaz</span>
</v-btn>
<WarningAlert v-if="!props.internship.report" title="Neodovzdané"
text="Výkaz zatiaľ nebol nahratý." /> text="Výkaz zatiaľ nebol nahratý." />
<div v-else> <div v-else>

View File

@@ -11,7 +11,7 @@ export interface Internship {
year_of_study: number; year_of_study: number;
semester: string; semester: string;
position_description: string; position_description: string;
agreement: boolean; proof: boolean;
report: boolean; report: boolean;
report_confirmed: boolean; report_confirmed: boolean;
status: InternshipStatusData; status: InternshipStatusData;

View File

@@ -14,7 +14,7 @@ describe('Admin Student Document Downloads', () => {
cy.url().should('include', '/dashboard/admin/internships') cy.url().should('include', '/dashboard/admin/internships')
}) })
it('should be able to generate and download the default agreement', () => { it('should be able to generate and download the default proof', () => {
cy.get('table').within(() => { cy.get('table').within(() => {
cy.get('tbody tr') cy.get('tbody tr')
.then(rows => { .then(rows => {