$request->session()->get('graduation_bulk_upload_preview'), 'documentTypes' => [ 'skl' => 'SKL', 'ijazah' => 'Ijazah', ], 'matchByOptions' => [ 'student_number' => 'NIS', 'nisn' => 'NISN', ], ]); } public function preview(Request $request): RedirectResponse { $validated = $request->validate([ 'document_type' => ['required', 'in:skl,ijazah'], 'match_by' => ['required', 'in:student_number,nisn'], 'documents' => ['required', 'array', 'min:1', 'max:10'], 'documents.*' => ['required', 'file', 'mimes:pdf,jpg,jpeg,png', 'max:2048'], ], [ 'documents.required' => 'Pilih minimal satu file dokumen untuk dipreview.', 'documents.max' => 'Maksimal 10 file per batch upload.', 'documents.*.mimes' => 'Dokumen hanya boleh berupa PDF, JPG, JPEG, atau PNG.', 'documents.*.max' => 'Ukuran tiap dokumen maksimal 2 MB.', ]); $graduations = StudentGraduation::query()->get()->keyBy($validated['match_by']); $rows = []; $summary = [ 'total_files' => 0, 'matched' => 0, 'unmatched' => 0, 'will_replace' => 0, 'ready_to_upload' => 0, ]; foreach ($request->file('documents', []) as $file) { $summary['total_files']++; $originalName = $file->getClientOriginalName(); $baseName = pathinfo($originalName, PATHINFO_FILENAME); $normalizedKey = trim($baseName); $graduation = $graduations->get($normalizedKey); $existingPathField = $validated['document_type'] === 'skl' ? 'skl_file_path' : 'ijazah_file_path'; $storedTempPath = $file->store('temp/graduation-bulk-upload', 'public'); $row = [ 'original_name' => $originalName, 'stored_temp_path' => $storedTempPath, 'mime_type' => $file->getMimeType(), 'file_size' => $file->getSize(), 'match_key' => $normalizedKey, 'graduation_id' => $graduation?->id, 'student_name' => $graduation?->full_name, 'identifier_value' => $graduation?->{$validated['match_by']}, 'status' => $graduation ? 'matched' : 'unmatched', 'will_replace' => $graduation ? filled($graduation->{$existingPathField}) : false, ]; if ($graduation) { $summary['matched']++; $summary['ready_to_upload']++; if ($row['will_replace']) { $summary['will_replace']++; } } else { $summary['unmatched']++; } $rows[] = $row; } $preview = [ 'token' => (string) Str::uuid(), 'document_type' => $validated['document_type'], 'document_label' => $validated['document_type'] === 'skl' ? 'SKL' : 'Ijazah', 'match_by' => $validated['match_by'], 'match_by_label' => $validated['match_by'] === 'student_number' ? 'NIS' : 'NISN', 'rows' => $rows, 'summary' => $summary, ]; $request->session()->put('graduation_bulk_upload_preview', $preview); return redirect()->route('graduated-students.bulk-upload.index'); } public function store(Request $request): RedirectResponse { $request->validate([ 'preview_token' => ['required', 'string'], ]); $preview = $request->session()->get('graduation_bulk_upload_preview'); if (! $preview || ($preview['token'] ?? null) !== $request->string('preview_token')->value()) { return back()->withErrors([ 'preview_token' => 'Preview bulk upload sudah tidak valid. Silakan upload ulang dokumen.', ]); } $documentType = $preview['document_type']; $fileNameField = $documentType === 'skl' ? 'skl_file_name' : 'ijazah_file_name'; $filePathField = $documentType === 'skl' ? 'skl_file_path' : 'ijazah_file_path'; $mimeTypeField = $documentType === 'skl' ? 'skl_mime_type' : 'ijazah_mime_type'; $fileSizeField = $documentType === 'skl' ? 'skl_file_size' : 'ijazah_file_size'; $processed = 0; $skipped = 0; DB::transaction(function () use ($preview, $documentType, $fileNameField, $filePathField, $mimeTypeField, $fileSizeField, &$processed, &$skipped) { foreach ($preview['rows'] as $row) { if (($row['status'] ?? null) !== 'matched' || empty($row['graduation_id'])) { $skipped++; continue; } $graduation = StudentGraduation::query()->find($row['graduation_id']); if (! $graduation) { $skipped++; continue; } if (! Storage::disk('public')->exists($row['stored_temp_path'])) { $skipped++; continue; } if ($graduation->{$filePathField} && Storage::disk('public')->exists($graduation->{$filePathField})) { Storage::disk('public')->delete($graduation->{$filePathField}); } $extension = pathinfo($row['original_name'], PATHINFO_EXTENSION); $finalPath = "student-graduation-documents/{$graduation->student_id}/{$documentType}/" . Str::uuid() . ($extension ? '.' . $extension : ''); Storage::disk('public')->move($row['stored_temp_path'], $finalPath); $graduation->update([ $fileNameField => $row['original_name'], $filePathField => $finalPath, $mimeTypeField => $row['mime_type'], $fileSizeField => $row['file_size'], ]); $processed++; } }); foreach ($preview['rows'] as $row) { if (! empty($row['stored_temp_path']) && Storage::disk('public')->exists($row['stored_temp_path'])) { Storage::disk('public')->delete($row['stored_temp_path']); } } $request->session()->forget('graduation_bulk_upload_preview'); Alert::success('Berhasil', "Bulk upload {$preview['document_label']} selesai. {$processed} dokumen diproses, {$skipped} dilewati."); return redirect() ->route('graduated-students.bulk-upload.index') ->with('success', "Bulk upload {$preview['document_label']} selesai. {$processed} dokumen diproses, {$skipped} dilewati."); } }