diff --git a/app/Http/Controllers/DashboardController.php b/app/Http/Controllers/DashboardController.php index 1334370..55d4e56 100644 --- a/app/Http/Controllers/DashboardController.php +++ b/app/Http/Controllers/DashboardController.php @@ -295,18 +295,21 @@ class DashboardController extends Controller $zipName = 'files_' . time() . '.zip'; $zipPath = public_path('zip/' . $zipName); $zip = new ZipArchive; + $tempFiles = []; if($zip->open($zipPath, ZipArchive::CREATE) === TRUE){ foreach ($paths as $path) { $fullPath = public_path('file/' . $path); - if(file_exists($fullPath)){ - $relativePathInZip = $path; - $zip->addFile($fullPath, $relativePathInZip); - }else{ + if(!file_exists($fullPath)){ throw new \Exception("File tidak ditemukan: " . $path); } + + $relativePathInZip = $path; + $fileToAdd = $this->prepareFileWithWatermark($fullPath, $tempFiles); + $zip->addFile($fileToAdd, $relativePathInZip); } $zip->close(); } + $this->cleanupTempFiles($tempFiles); return response()->download(public_path('zip/' . $zipName))->deleteFileAfterSend(true); //code... } catch (\Throwable $th) { @@ -333,18 +336,21 @@ class DashboardController extends Controller $zipName = 'files_' . time() . '.zip'; $zipPath = public_path('zip/' . $zipName); $zip = new ZipArchive; + $tempFiles = []; if($zip->open($zipPath, ZipArchive::CREATE) === TRUE){ foreach ($data as $path) { $fullPath = public_path('file/' . $path); - if(file_exists($fullPath)){ - $relativePathInZip = $path; - $zip->addFile($fullPath, $relativePathInZip); - }else{ + if(!file_exists($fullPath)){ throw new \Exception("File tidak ditemukan: " . $path); } + + $relativePathInZip = $path; + $fileToAdd = $this->prepareFileWithWatermark($fullPath, $tempFiles); + $zip->addFile($fileToAdd, $relativePathInZip); } $zip->close(); } + $this->cleanupTempFiles($tempFiles); return response()->download(public_path('zip/' . $zipName))->deleteFileAfterSend(true); } catch (\Throwable $th) { return response()->json([ @@ -370,10 +376,27 @@ class DashboardController extends Controller } public function dataDocumentLast(){ - $perPage = request('per_page', 10); + $perPage = (int) request('per_page', 10); + $authUnitId = auth()->user()->dataUser?->mappingUnitKerjaPegawai[0]?->objectunitkerjapegawaifk; + $keyword = request('keyword'); + $data = FileDirectory::where('statusenabled', true) + ->where(function ($query) use ($authUnitId) { + $query->where('permission_file', true) + ->orWhere(function ($sub) use ($authUnitId) { + $sub->where('permission_file', false) + ->where('id_unit_kerja', $authUnitId); + }); + }) + ->when($keyword, function ($q) use ($keyword) { + $q->where(function ($sub) use ($keyword) { + $sub->where('file', 'ILIKE', "%{$keyword}%") + ->orWhere('no_dokumen', 'ILIKE', "%{$keyword}%"); + }); + }) ->orderBy('entry_at', 'desc') ->paginate($perPage); + $payload = [ 'status' => true, 'message' => 'Berhasil mendapatkan data', @@ -381,7 +404,10 @@ class DashboardController extends Controller 'pagination' => [ 'current_page' => $data->currentPage(), 'next_page' => $data->hasMorePages() ? $data->currentPage() + 1 : null, - 'has_more' => $data->hasMorePages() + 'has_more' => $data->hasMorePages(), + 'last_page' => $data->lastPage(), + 'per_page' => $data->perPage(), + 'total' => $data->total(), ] ]; return response()->json($payload); @@ -391,10 +417,16 @@ class DashboardController extends Controller DB::connection('dbDirectory')->beginTransaction(); try { $datas = request('data'); - foreach ($datas as $data) { + foreach ($datas as $index => $data) { list($id_unit_kerja, $nama_unit_kerja) = explode('/', $data['id_unit_kerja'],2); list($id_sub_unit_kerja, $nama_sub_unit_kerja) = explode('/', $data['id_sub_unit_kerja'],2); list($master_kategori_directory_id, $nama_kategori) = explode('/', $data['master_kategori_directory_id'],2); + + $uploadedFile = request()->file("data.$index.file"); + if(!$uploadedFile){ + throw new \RuntimeException('File wajib diunggah pada baris ke-' . ($index+1)); + } + $payload = [ 'id_unit_kerja' => $id_unit_kerja, 'id_sub_unit_kerja' => $id_sub_unit_kerja, @@ -403,16 +435,16 @@ class DashboardController extends Controller 'pegawai_nama_entry' => auth()->user()->dataUser->namalengkap ?? null, 'tanggal_terbit' => $data['date_active'] ?? null, 'no_dokumen' => $data['no_dokumen'] ?? null, - 'permission_file' => $data['is_permission'] === "1" ? true : false, + 'permission_file' => ($data['is_permission'] ?? null) == "1", ]; - if(!empty($data['file'])){ - $file = $data['file']; - $imageName = $file->getClientOriginalName(); - $path = "{$nama_unit_kerja}/{$nama_sub_unit_kerja}/{$nama_kategori}"; - $file->storeAs($path, $imageName, 'file_directory'); - $payload['file'] =$path .'/' .$imageName; - } + + $imageName = $uploadedFile->getClientOriginalName(); + $path = "{$nama_unit_kerja}/{$nama_sub_unit_kerja}/{$nama_kategori}"; + $uploadedFile->storeAs($path, $imageName, 'file_directory'); + $payload['file'] =$path .'/' .$imageName; + $fd = FileDirectory::create($payload); + $payloadLog = [ 'file_directory_id' => $fd->file_directory_id, 'pegawai_id_entry' => $fd->pegawai_id_entry, @@ -513,6 +545,71 @@ class DashboardController extends Controller ]); } + /** + * Generate watermarked PDF to temp file (or return original) for zipping. + */ + private function prepareFileWithWatermark(string $fullPath, array &$tempFiles = []) + { + $stampFile = public_path('assets/copy.png'); + $ext = strtolower(pathinfo($fullPath, PATHINFO_EXTENSION)); + + if ($ext !== 'pdf' || !file_exists($stampFile)) { + return $fullPath; + } + + $tempDir = storage_path('app/temp'); + if (!is_dir($tempDir)) { + @mkdir($tempDir, 0777, true); + } + $tempOut = $tempDir . '/' . uniqid('wm_') . '.pdf'; + + try { + $this->watermarkCenterToFile($fullPath, $stampFile, $tempOut); + } catch (\Throwable $e) { + // fallback convert then watermark + $converted = $tempDir . '/' . uniqid('conv_') . '.pdf'; + $this->convertWithGhostscript($fullPath, $converted); + $this->watermarkCenterToFile($converted, $stampFile, $tempOut); + $tempFiles[] = $converted; + } + + $tempFiles[] = $tempOut; + return $tempOut; + } + + private function watermarkCenterToFile(string $pdfPath, string $stampFile, string $outputPath): void + { + $pdf = new Fpdi(); + $pageCount = $pdf->setSourceFile($pdfPath); + + for ($pageNo = 1; $pageNo <= $pageCount; $pageNo++) { + $tplId = $pdf->importPage($pageNo); + $size = $pdf->getTemplateSize($tplId); + + $pdf->AddPage($size['orientation'], [$size['width'], $size['height']]); + $pdf->useTemplate($tplId); + + $stampW = $size['width'] * 0.60; + $stampH = $stampW * 0.75; + + $x = ($size['width'] - $stampW) / 2; + $y = ($size['height'] - $stampH) / 2; + + $pdf->Image($stampFile, $x, $y, $stampW, $stampH); + } + + $pdf->Output($outputPath, 'F'); + } + + private function cleanupTempFiles(array $tempFiles): void + { + foreach ($tempFiles as $file) { + if (is_string($file) && file_exists($file)) { + @unlink($file); + } + } + } + private function convertWithGhostscript(string $inputPdf, string $outputPdf): void { $gs = config('services.ghostscript.path'); @@ -530,4 +627,78 @@ class DashboardController extends Controller throw new \RuntimeException('Convert Ghostscript gagal (code=' . $code . ')'); } } + + public function recapData(){ + try { + $perPage = (int) request('per_page', 10); + $page = max(1, (int) request('page', 1)); + $keyword = strtolower(request('keyword', '')); + + $rows = FileDirectory::where('statusenabled', true)->pluck('file'); + + $grouped = []; + foreach ($rows as $path) { + $parts = array_values(array_filter(explode('/', $path))); + if(count($parts) < 4){ + continue; + } + + $unit = $parts[0]; + $folder = $parts[1]. '/' . $parts[2]; + if($keyword){ + $hit = str_contains(strtolower($unit), $keyword) || str_contains(strtolower($folder), $keyword); + if(!$hit) continue; + } + if(!isset($grouped[$unit])){ + $grouped[$unit] = []; + } + if (!isset($grouped[$unit][$folder])) { + $grouped[$unit][$folder] = 0; + } + $grouped[$unit][$folder]++; + } + $result = []; + foreach ($grouped as $unitName => $folders) { + $data = []; + foreach ($folders as $folder => $count) { + $data[] = [ + 'folder'=> $folder, + 'count' => $count, + ]; + } + + usort($data, fn($a, $b) => $b['count'] <=> $a['count']); + $result[] = [ + 'unit' => $unitName, + 'data' => $data, + ]; + } + // paginate manually + $total = count($result); + $chunks = array_chunk($result, $perPage); + $currentData = $chunks[$page-1] ?? []; + + return response()->json([ + 'status' => true, + 'data' => $currentData, + 'message' => 'Berhasil mendapatkan data', + 'pagination' => [ + 'current_page' => $page, + 'per_page' => $perPage, + 'total' => $total, + 'last_page' => max(1, ceil($total / $perPage)), + 'has_more' => $page < max(1, ceil($total / $perPage)), + ] + ]); + } catch (\Throwable $th) { + return response()->json([ + 'status' => false, + 'message' => 'Gagal! mendapatkan data' + ]); + } + } + + public function recapView(){ + return view('dashboard.recap', ['title' => 'Rekap Dokumen']); + } } diff --git a/app/Http/Controllers/LogActivityController.php b/app/Http/Controllers/LogActivityController.php new file mode 100644 index 0000000..b64c7d2 --- /dev/null +++ b/app/Http/Controllers/LogActivityController.php @@ -0,0 +1,67 @@ + 'Log Activity' + ]; + return view('logActivity.index', $data); + } + + public function datatable(){ + $perPage = (int) request('per_page', 10); + $keyword = request('keyword'); + $start = request('start_date'); + $end = request('end_date'); + + $query = LogActivity::query() + ->orderBy('entry_at','desc'); + + if($keyword){ + $query->where(function($q) use ($keyword){ + $q->where('pegawai_nama_entry', 'ILIKE', "%{$keyword}%") + ->orWhere('action_type', 'ILIKE', "%{$keyword}%") + ->orWhere('file', 'ILIKE', "%{$keyword}%") + ->orWhere('no_dokumen', 'ILIKE', "%{$keyword}%"); + }); + } + + if($start){ + $query->whereDate('entry_at','>=',$start); + } + if($end){ + $query->whereDate('entry_at','<=',$end); + } + + $paginated = $query->paginate($perPage); + $data = $paginated->getCollection()->map(function($item){ + return [ + 'pegawai_nama_entry' => $item->pegawai_nama_entry, + 'action_type' => $item->action_type, + 'file' => $item->file, + 'no_dokumen' => $item->no_dokumen ?? $item->fileDirectory->no_dokumen ?? '-', + 'entry_at' => $item->entry_at, + 'unit_name' => $item->unit_name ?? '-', + ]; + }); + + return response()->json([ + 'status' => true, + 'data' => $data, + 'pagination' => [ + 'current_page' => $paginated->currentPage(), + 'next_page' => $paginated->hasMorePages() ? $paginated->currentPage() + 1 : null, + 'has_more' => $paginated->hasMorePages(), + 'last_page' => $paginated->lastPage(), + 'per_page' => $paginated->perPage(), + 'total' => $paginated->total(), + ] + ]); + } +} diff --git a/app/Models/LogActivity.php b/app/Models/LogActivity.php index 8e50b20..b3e9eec 100644 --- a/app/Models/LogActivity.php +++ b/app/Models/LogActivity.php @@ -11,4 +11,9 @@ class LogActivity extends Model public $timestamps = false; protected $primaryKey = 'id'; protected $guarded = ['id']; + + // public function unitKerja(){ + // return $this->belongsTo(UnitKerja::class, 'id_unit_kerja', 'id'); + // } + } diff --git a/public/js/dashboard/action.js b/public/js/dashboard/action.js index 3cfe60f..6a36ec5 100644 --- a/public/js/dashboard/action.js +++ b/public/js/dashboard/action.js @@ -1,4 +1,3 @@ - let allFiles = []; document.addEventListener("DOMContentLoaded", () => { document.querySelectorAll(".file-input").forEach(input => { bindFileUpload(input); @@ -6,72 +5,40 @@ }); function bindFileUpload(input) { - const dropArea = input.closest(".file-drop-area"); - const fileNameBox = dropArea.querySelector(".file-name"); - const inputId = input?.id; - allFiles[inputId] = []; + const fileNameBox = input.closest(".border")?.querySelector(".file-name") || input.closest(".file-drop-area")?.querySelector(".file-name"); input.addEventListener("change", function () { - for (let i = 0; i < this.files.length; i++) { - allFiles[inputId].push(this.files[i]); - } - renderFileList(inputId, fileNameBox); - this.value = ""; // reset agar bisa pilih file lagi + if (!fileNameBox) return; + const file = this.files[0]; + if (file) { + fileNameBox.textContent = `✔ ${file.name}`; + fileNameBox.classList.remove("d-none"); + } else { + fileNameBox.textContent = ''; + fileNameBox.classList.add("d-none"); + } }); } - function renderFileList(inputId, container) { - const files = allFiles[inputId]; - if (!files || files.length === 0) { - container.classList.add("d-none"); - container.innerHTML = ""; - return; - } - - let list = "