From 3ff12fe57ec90886090bddee684cb5b2164d8361 Mon Sep 17 00:00:00 2001 From: dkecskes Date: Mon, 3 Nov 2025 00:35:11 +0100 Subject: [PATCH 1/3] feat: add student deletion functionality with confirmation dialog --- .../Auth/RegisteredUserController.php | 27 ++++- .../Controllers/StudentDataController.php | 66 +++++++++++++ backend/app/Models/Internship.php | 8 ++ backend/app/Models/User.php | 8 ++ backend/routes/api.php | 1 + .../dashboard/admin/students/edit/[id].vue | 91 +++++++++++++++++ .../pages/dashboard/admin/students/index.vue | 98 +++++++++++++++++++ 7 files changed, 295 insertions(+), 4 deletions(-) diff --git a/backend/app/Http/Controllers/Auth/RegisteredUserController.php b/backend/app/Http/Controllers/Auth/RegisteredUserController.php index 98509eb..ba2d421 100644 --- a/backend/app/Http/Controllers/Auth/RegisteredUserController.php +++ b/backend/app/Http/Controllers/Auth/RegisteredUserController.php @@ -26,7 +26,7 @@ class RegisteredUserController extends Controller $password = bin2hex(random_bytes(16)); $request->validate([ - 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:'.User::class], + 'email' => ['required', 'string', 'lowercase', 'email', 'max:255', 'unique:' . User::class], 'first_name' => ['required', 'string', 'max:64'], 'last_name' => ['required', 'string', 'max:64'], 'phone' => ['required', 'string', 'max:13'], @@ -56,14 +56,14 @@ class RegisteredUserController extends Controller 'password' => Hash::make($password), ]); - if($user->role === "STUDENT") { + if ($user->role === "STUDENT") { StudentData::create([ 'user_id' => $user->id, 'address' => $request->student_data['address'], 'personal_email' => $request->student_data['personal_email'], 'study_field' => $request->student_data['study_field'], ]); - } else if($user->role === "EMPLOYER") { + } else if ($user->role === "EMPLOYER") { Company::create([ 'name' => $request->company_data['name'], 'address' => $request->company_data['address'], @@ -79,7 +79,8 @@ class RegisteredUserController extends Controller return response()->noContent(); } - public function reset_password(Request $request): Response { + public function reset_password(Request $request): Response + { $request->validate([ 'email' => ['required', 'string', 'lowercase', 'email', 'max:255'], ]); @@ -97,4 +98,22 @@ class RegisteredUserController extends Controller return response()->noContent(); } + + public function reset_password_2(Request $request): Response + { + $request->validate([ + 'id' => ['required', 'string', 'lowercase', 'email', 'max:255'], + 'password' => ['required', 'string', 'lowercase', 'email', 'max:255'], + ]); + + $user = User::whereEmail($request->email)->first(); + if (!$user) { + return response(status: 400); + } + + $user->password = Hash::make($request->password); + $user->save(); + + return response()->noContent(); + } } \ No newline at end of file diff --git a/backend/app/Http/Controllers/StudentDataController.php b/backend/app/Http/Controllers/StudentDataController.php index 1187b0d..6506fa3 100644 --- a/backend/app/Http/Controllers/StudentDataController.php +++ b/backend/app/Http/Controllers/StudentDataController.php @@ -4,7 +4,9 @@ namespace App\Http\Controllers; use App\Models\StudentData; use App\Models\User; +use App\Models\InternshipStatus; use Illuminate\Http\Request; +use Illuminate\Support\Facades\DB; class StudentDataController extends Controller { @@ -168,4 +170,68 @@ class StudentDataController extends Controller { // } + + /** + * Delete a student and all related data. + */ + public function delete(int $id) + { + $user = auth()->user(); + + // Admin kontrola + if ($user->role !== 'ADMIN') { + abort(403, 'Unauthorized'); + } + + $student = User::find($id); + + if (!$student) { + return response()->json([ + 'message' => 'No such student exists.' + ], 400); + } + + if ($student->role !== 'STUDENT') { + return response()->json([ + 'message' => 'User is not a student.' + ], 400); + } + + try { + DB::beginTransaction(); + + // 1. Získaj internship IDs + $internshipIds = $student->internships()->pluck('id')->toArray(); + + // 2. Vymaž internship statuses + if (!empty($internshipIds)) { + InternshipStatus::whereIn('internship_id', $internshipIds)->delete(); + } + + // 3. Vymaž internships + $student->internships()->delete(); + + // 4. Vymaž student_data + if ($student->studentData) { + $student->studentData()->delete(); + } + + // 5. Vymaž usera + $student->delete(); + + DB::commit(); + + return response()->json([ + 'message' => 'Student successfully deleted.' + ], 200); + + } catch (\Exception $e) { + DB::rollBack(); + + return response()->json([ + 'message' => 'Error deleting student.', + 'error' => $e->getMessage() + ], 500); + } + } } diff --git a/backend/app/Models/Internship.php b/backend/app/Models/Internship.php index a549885..17cd8e7 100644 --- a/backend/app/Models/Internship.php +++ b/backend/app/Models/Internship.php @@ -25,4 +25,12 @@ class Internship extends Model 'position_description', 'agreement', ]; + + /** + * Get the statuses for the internship. + */ + public function statuses() + { + return $this->hasMany(InternshipStatus::class, 'internship_id'); + } } diff --git a/backend/app/Models/User.php b/backend/app/Models/User.php index a052880..de3bb49 100644 --- a/backend/app/Models/User.php +++ b/backend/app/Models/User.php @@ -57,4 +57,12 @@ class User extends Authenticatable { return $this->hasOne(StudentData::class, 'user_id'); } + + /** + * Get the internships for the user. + */ + public function internships() + { + return $this->hasMany(Internship::class, 'user_id'); + } } diff --git a/backend/routes/api.php b/backend/routes/api.php index 8eef125..6af5e6e 100644 --- a/backend/routes/api.php +++ b/backend/routes/api.php @@ -26,6 +26,7 @@ Route::middleware(['auth:sanctum'])->prefix('/students')->group(function () { Route::get('/', [StudentDataController::class, 'all']); Route::get('/{id}', [StudentDataController::class, 'get']); Route::post('/{id}', [StudentDataController::class, 'update_all']); + Route::delete('/{id}', [StudentDataController::class, 'delete']); }); Route::post('/password-reset', [RegisteredUserController::class, 'reset_password']) diff --git a/frontend/app/pages/dashboard/admin/students/edit/[id].vue b/frontend/app/pages/dashboard/admin/students/edit/[id].vue index 43c7d44..5ebb8e4 100644 --- a/frontend/app/pages/dashboard/admin/students/edit/[id].vue +++ b/frontend/app/pages/dashboard/admin/students/edit/[id].vue @@ -14,6 +14,12 @@ const student = ref(null); const loading = ref(true); const saving = ref(false); +// Delete state +const deleteDialog = ref(false); +const deleteLoading = ref(false); +const deleteError = ref(null); +const deleteSuccess = ref(false); + const form = ref({ name: '', email: '', @@ -70,6 +76,49 @@ async function saveChanges() { function cancel() { navigateTo('/dashboard/admin/students'); } + +// Funkcia na otvorenie delete dialogu +const openDeleteDialog = () => { + deleteDialog.value = true; + deleteError.value = null; +}; + +// Funkcia na zatvorenie dialogu +const closeDeleteDialog = () => { + deleteDialog.value = false; + deleteError.value = null; + deleteSuccess.value = false; +}; + +// Funkcia na vymazanie študenta +const deleteStudent = async () => { + if (!studentId) return; + + deleteLoading.value = true; + deleteError.value = null; + + try { + await client(`/api/students/${studentId}`, { + method: 'DELETE' + }); + + deleteSuccess.value = true; + + // Presmeruj na zoznam po 1.5 sekundách + setTimeout(() => { + navigateTo('/dashboard/admin/students'); + }, 1500); + + } catch (e) { + if (e instanceof FetchError) { + deleteError.value = e.response?._data?.message || 'Chyba pri mazaní študenta.'; + } else { + deleteError.value = 'Neznáma chyba pri mazaní študenta.'; + } + } finally { + deleteLoading.value = false; + } +}; diff --git a/frontend/app/pages/dashboard/admin/students/index.vue b/frontend/app/pages/dashboard/admin/students/index.vue index d3eb4c7..ebd743a 100644 --- a/frontend/app/pages/dashboard/admin/students/index.vue +++ b/frontend/app/pages/dashboard/admin/students/index.vue @@ -22,8 +22,66 @@ const headers = [ { title: 'Operácie', key: 'ops', align: 'middle' }, ]; +const client = useSanctumClient(); + // Načítame všetkých študentov const { data: students, error } = await useSanctumFetch('/api/students'); + +// State pre delete dialog +const deleteDialog = ref(false); +const studentToDelete = ref(null); +const deleteLoading = ref(false); +const deleteError = ref(null); +const deleteSuccess = ref(false); + +// Funkcia na otvorenie delete dialogu +const openDeleteDialog = (student: User) => { + studentToDelete.value = student; + deleteDialog.value = true; + deleteError.value = null; +}; + +// Funkcia na zatvorenie dialogu +const closeDeleteDialog = () => { + deleteDialog.value = false; + studentToDelete.value = null; + deleteError.value = null; + deleteSuccess.value = false; +}; + +// Funkcia na vymazanie študenta +const deleteStudent = async () => { + if (!studentToDelete.value) return; + + deleteLoading.value = true; + deleteError.value = null; + + try { + await client(`/api/students/${studentToDelete.value.id}`, { + method: 'DELETE' + }); + + // Odstránime študenta zo zoznamu + if (students.value) { + const index = students.value.findIndex(s => s.id === studentToDelete.value!.id); + if (index > -1) { + students.value.splice(index, 1); + } + } + + deleteSuccess.value = true; + + // Zavri dialog po 1.5 sekundách + setTimeout(() => { + closeDeleteDialog(); + }, 1500); + + } catch (err: any) { + deleteError.value = err.data?.message || 'Chyba pri mazaní študenta.'; + } finally { + deleteLoading.value = false; + } +}; From 1973ab6b7ff16c393c75a593fa7ebf3433e8ef0e Mon Sep 17 00:00:00 2001 From: dkecskes Date: Mon, 3 Nov 2025 19:37:04 +0100 Subject: [PATCH 2/3] feat: implement company deletion functionality with confirmation dialog --- .../Http/Controllers/CompanyController.php | 66 ++++++++++++ backend/app/Models/Company.php | 16 +++ backend/routes/api.php | 1 + .../dashboard/admin/companies/edit/[id].vue | 93 ++++++++++++++++ .../pages/dashboard/admin/companies/index.vue | 100 +++++++++++++++++- 5 files changed, 274 insertions(+), 2 deletions(-) diff --git a/backend/app/Http/Controllers/CompanyController.php b/backend/app/Http/Controllers/CompanyController.php index f8499d6..ac3ce8e 100644 --- a/backend/app/Http/Controllers/CompanyController.php +++ b/backend/app/Http/Controllers/CompanyController.php @@ -4,7 +4,10 @@ namespace App\Http\Controllers; use App\Models\Company; use App\Models\User; +use App\Models\Internship; +use App\Models\InternshipStatus; use Illuminate\Http\Request; +use Illuminate\Support\Facades\DB; class CompanyController extends Controller { @@ -152,4 +155,67 @@ class CompanyController extends Controller { // } + + /** + * Delete a company, its contact person and all related data. + */ + public function delete(int $id) + { + $user = auth()->user(); + + // Admin kontrola + if ($user->role !== 'ADMIN') { + abort(403, 'Unauthorized'); + } + + $company = Company::find($id); + + if (!$company) { + return response()->json([ + 'message' => 'No such company exists.' + ], 400); + } + + try { + DB::beginTransaction(); + + // 1. Získaj všetky internship IDs firmy + $internshipIds = Internship::where('company_id', $company->id) + ->pluck('id') + ->toArray(); + + // 2. Vymaž všetky internship statuses + if (!empty($internshipIds)) { + InternshipStatus::whereIn('internship_id', $internshipIds)->delete(); + } + + // 3. Vymaž všetky internships firmy + Internship::where('company_id', $company->id)->delete(); + + // 4. Získaj contact usera + $contactUser = User::find($company->contact); + + // 5. Vymaž company + $company->delete(); + + // 6. Vymaž contact usera (EMPLOYER) + if ($contactUser && $contactUser->role === 'EMPLOYER') { + $contactUser->delete(); + } + + DB::commit(); + + return response()->json([ + 'message' => 'Company successfully deleted.' + ], 200); + + } catch (\Exception $e) { + DB::rollBack(); + + return response()->json([ + 'message' => 'Error deleting company.', + 'error' => $e->getMessage() + ], 500); + } + } } diff --git a/backend/app/Models/Company.php b/backend/app/Models/Company.php index ea19407..6320e72 100644 --- a/backend/app/Models/Company.php +++ b/backend/app/Models/Company.php @@ -22,4 +22,20 @@ class Company extends Model 'contact', 'hiring' ]; + + /** + * Get the internships for the company. + */ + public function internships() + { + return $this->hasMany(Internship::class, 'company_id'); + } + + /** + * Get the contact person (user) for the company. + */ + public function contactPerson() + { + return $this->belongsTo(User::class, 'contact'); + } } diff --git a/backend/routes/api.php b/backend/routes/api.php index 6af5e6e..4e4e11c 100644 --- a/backend/routes/api.php +++ b/backend/routes/api.php @@ -54,4 +54,5 @@ Route::prefix('/companies')->middleware("auth:sanctum")->group(function () { Route::get("/simple", [CompanyController::class, 'all_simple']); Route::get("/{id}", [CompanyController::class, 'get']); Route::post("/{id}", [CompanyController::class, 'update_all']); + Route::delete("/{id}", [CompanyController::class, 'delete']); }); \ No newline at end of file diff --git a/frontend/app/pages/dashboard/admin/companies/edit/[id].vue b/frontend/app/pages/dashboard/admin/companies/edit/[id].vue index f0cca5c..c4c6c25 100644 --- a/frontend/app/pages/dashboard/admin/companies/edit/[id].vue +++ b/frontend/app/pages/dashboard/admin/companies/edit/[id].vue @@ -13,6 +13,13 @@ const companyId = route.params.id; const loading = ref(true); const saving = ref(false); +// Delete state +const deleteDialog = ref(false); +const deleteLoading = ref(false); +const deleteError = ref(null); +const deleteSuccess = ref(false); +const company = ref(null); + const form = ref({ name: '', address: '', @@ -30,6 +37,7 @@ const { data } = await useSanctumFetch(`/api/companies/${companyId} watch(data, (newData) => { if (newData) { + company.value = newData; form.value.name = newData.name; form.value.address = newData.address; form.value.ico = newData.ico; @@ -65,6 +73,49 @@ async function saveChanges() { function cancel() { navigateTo('/dashboard/admin/companies'); } + +// Funkcia na otvorenie delete dialogu +const openDeleteDialog = () => { + deleteDialog.value = true; + deleteError.value = null; +}; + +// Funkcia na zatvorenie dialogu +const closeDeleteDialog = () => { + deleteDialog.value = false; + deleteError.value = null; + deleteSuccess.value = false; +}; + +// Funkcia na vymazanie firmy +const deleteCompany = async () => { + if (!companyId) return; + + deleteLoading.value = true; + deleteError.value = null; + + try { + await client(`/api/companies/${companyId}`, { + method: 'DELETE' + }); + + deleteSuccess.value = true; + + // Presmeruj na zoznam po 1.5 sekundách + setTimeout(() => { + navigateTo('/dashboard/admin/companies'); + }, 1500); + + } catch (e) { + if (e instanceof FetchError) { + deleteError.value = e.response?._data?.message || 'Chyba pri mazaní firmy.'; + } else { + deleteError.value = 'Neznáma chyba pri mazaní firmy.'; + } + } finally { + deleteLoading.value = false; + } +}; diff --git a/frontend/app/pages/dashboard/admin/companies/index.vue b/frontend/app/pages/dashboard/admin/companies/index.vue index 6bd4cac..43622e1 100644 --- a/frontend/app/pages/dashboard/admin/companies/index.vue +++ b/frontend/app/pages/dashboard/admin/companies/index.vue @@ -22,7 +22,65 @@ const headers = [ { title: 'Operácie', key: 'ops', align: 'middle' }, ]; +const client = useSanctumClient(); + const { data, error } = await useSanctumFetch('/api/companies/simple'); + +// State pre delete dialog +const deleteDialog = ref(false); +const companyToDelete = ref(null); +const deleteLoading = ref(false); +const deleteError = ref(null); +const deleteSuccess = ref(false); + +// Funkcia na otvorenie delete dialogu +const openDeleteDialog = (company: CompanyData) => { + companyToDelete.value = company; + deleteDialog.value = true; + deleteError.value = null; +}; + +// Funkcia na zatvorenie dialogu +const closeDeleteDialog = () => { + deleteDialog.value = false; + companyToDelete.value = null; + deleteError.value = null; + deleteSuccess.value = false; +}; + +// Funkcia na vymazanie firmy +const deleteCompany = async () => { + if (!companyToDelete.value) return; + + deleteLoading.value = true; + deleteError.value = null; + + try { + await client(`/api/companies/${companyToDelete.value.id}`, { + method: 'DELETE' + }); + + // Odstránime firmu zo zoznamu + if (data.value) { + const index = data.value.findIndex(c => c.id === companyToDelete.value!.id); + if (index > -1) { + data.value.splice(index, 1); + } + } + + deleteSuccess.value = true; + + setTimeout(() => { + closeDeleteDialog(); + }, 1500); + + } catch (err: any) { + deleteError.value = err.data?.message || 'Chyba pri mazaní firmy.'; + } finally { + deleteLoading.value = false; + } +}; + From adc2e725bf58b728f25c7a1956b893c946c43ab2 Mon Sep 17 00:00:00 2001 From: dkecskes Date: Tue, 4 Nov 2025 10:00:03 +0100 Subject: [PATCH 3/3] feat: enhance deletion functionality for companies and students with improved data handling and confirmation dialogs --- .../Http/Controllers/CompanyController.php | 49 ++++++------------- .../Controllers/StudentDataController.php | 45 ++++++----------- .../pages/dashboard/admin/companies/index.vue | 38 +++++--------- .../pages/dashboard/admin/students/index.vue | 39 +++++---------- 4 files changed, 53 insertions(+), 118 deletions(-) diff --git a/backend/app/Http/Controllers/CompanyController.php b/backend/app/Http/Controllers/CompanyController.php index ac3ce8e..a2ed187 100644 --- a/backend/app/Http/Controllers/CompanyController.php +++ b/backend/app/Http/Controllers/CompanyController.php @@ -169,6 +169,7 @@ class CompanyController extends Controller } $company = Company::find($id); + $company_contact = User::find($company->contact); if (!$company) { return response()->json([ @@ -176,46 +177,26 @@ class CompanyController extends Controller ], 400); } - try { - DB::beginTransaction(); + DB::beginTransaction(); - // 1. Získaj všetky internship IDs firmy - $internshipIds = Internship::where('company_id', $company->id) - ->pluck('id') - ->toArray(); + $internships = Internship::whereCompanyId($company->id); - // 2. Vymaž všetky internship statuses - if (!empty($internshipIds)) { - InternshipStatus::whereIn('internship_id', $internshipIds)->delete(); - } + // mazanie statusov + $internships->each(function ($internship) { + InternshipStatus::whereInternshipId($internship->id)->delete(); + }); - // 3. Vymaž všetky internships firmy - Internship::where('company_id', $company->id)->delete(); + // mazanie praxov + $internships->delete(); - // 4. Získaj contact usera - $contactUser = User::find($company->contact); + // mazanie firmy + Company::whereContact($company_contact->id); - // 5. Vymaž company - $company->delete(); + // mazanie účtu firmy + $company_contact->delete(); - // 6. Vymaž contact usera (EMPLOYER) - if ($contactUser && $contactUser->role === 'EMPLOYER') { - $contactUser->delete(); - } + DB::commit(); - DB::commit(); - - return response()->json([ - 'message' => 'Company successfully deleted.' - ], 200); - - } catch (\Exception $e) { - DB::rollBack(); - - return response()->json([ - 'message' => 'Error deleting company.', - 'error' => $e->getMessage() - ], 500); - } + return response()->noContent(); } } diff --git a/backend/app/Http/Controllers/StudentDataController.php b/backend/app/Http/Controllers/StudentDataController.php index 6506fa3..913345c 100644 --- a/backend/app/Http/Controllers/StudentDataController.php +++ b/backend/app/Http/Controllers/StudentDataController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers; +use App\Models\Internship; use App\Models\StudentData; use App\Models\User; use App\Models\InternshipStatus; @@ -197,41 +198,27 @@ class StudentDataController extends Controller ], 400); } - try { - DB::beginTransaction(); + DB::beginTransaction(); - // 1. Získaj internship IDs - $internshipIds = $student->internships()->pluck('id')->toArray(); + // mazanie praxov + $internships = Internship::whereUserId($student->id); - // 2. Vymaž internship statuses - if (!empty($internshipIds)) { - InternshipStatus::whereIn('internship_id', $internshipIds)->delete(); - } + // mazanie statusov + $internships->each(function ($internship) { + InternshipStatus::whereInternshipId($internship->id)->delete(); + }); - // 3. Vymaž internships - $student->internships()->delete(); + // mazanie praxov + $internships->delete(); - // 4. Vymaž student_data - if ($student->studentData) { - $student->studentData()->delete(); - } + // mazanie firmy + StudentData::whereUserId($student->id); - // 5. Vymaž usera - $student->delete(); + // mazanie účtu firmy + $student->delete(); - DB::commit(); + DB::commit(); - return response()->json([ - 'message' => 'Student successfully deleted.' - ], 200); - - } catch (\Exception $e) { - DB::rollBack(); - - return response()->json([ - 'message' => 'Error deleting student.', - 'error' => $e->getMessage() - ], 500); - } + return response()->noContent(); } } diff --git a/frontend/app/pages/dashboard/admin/companies/index.vue b/frontend/app/pages/dashboard/admin/companies/index.vue index 43622e1..13c3dc8 100644 --- a/frontend/app/pages/dashboard/admin/companies/index.vue +++ b/frontend/app/pages/dashboard/admin/companies/index.vue @@ -1,5 +1,6 @@