active() ->orderBy('TanggalMulaiPeriode', 'desc') ->get() ->map(function ($item, $index) { return [ 'no' => $index + 1, 'no_registrasi' => $item->NoRegistrasi, 'nama_perusahaan' => $item->Pemrakarsa, 'nama_kegiatan' => $item->NamaKegiatan, 'jenis_dokumen' => $item->JenisDokumen, 'periode' => $this->formatBusinessDayPeriod($item->TanggalMulaiPeriode, 10), 'status' => 'Menunggu Tanggapan', 'detail_url' => route('pengumuman.detail', $item->KegiatanID) ]; }); return view('pengumuman.index', compact('pengumuman')); } /** * Generate business day period (10 working days excluding weekends and holidays) */ private function formatBusinessDayPeriod($startDate, $workingDays = 10) { $start = Carbon::parse($startDate); $current = $start->copy(); $daysAdded = 0; // Indonesian public holidays 2025 (tanggal merah) $holidays = [ '2025-01-01', // Tahun Baru '2025-01-29', // Tahun Baru Imlek '2025-03-29', // Hari Raya Nyepi '2025-03-30', // Wafat Isa Almasih '2025-04-09', // Isra Miraj '2025-05-01', // Hari Buruh '2025-05-12', // Hari Raya Waisak '2025-05-29', // Kenaikan Isa Almasih '2025-06-01', // Hari Lahir Pancasila '2025-06-06', // Idul Fitri 1446 H '2025-06-07', // Idul Fitri 1446 H '2025-08-17', // Hari Kemerdekaan RI '2025-08-31', // Idul Adha 1446 H '2025-09-21', // Tahun Baru Islam 1447 H '2025-11-30', // Maulid Nabi Muhammad SAW '2025-12-25', // Hari Raya Natal ]; while ($daysAdded < $workingDays) { $current->addDay(); // Skip weekends (Saturday = 6, Sunday = 0) if ($current->dayOfWeek == Carbon::SATURDAY || $current->dayOfWeek == Carbon::SUNDAY) { continue; } // Skip public holidays if (in_array($current->format('Y-m-d'), $holidays)) { continue; } $daysAdded++; } return $start->format('d M') . ' - ' . $current->format('d M Y'); } public function detail($id) { $kegiatan = InformasiKegiatan::with('saranTanggapanApproved')->findOrFail($id); return view('pengumuman.detail', compact('kegiatan')); } public function create() { $kabupaten = Kabupaten::orderBy('NamaKabupaten', 'asc')->get(); return view('pengumuman.create', compact('kabupaten')); } /** * Generate unique 12-digit registration number with enhanced security * Format: Mixed alphanumeric with timestamp hash to prevent easy guessing * Example: ENV4B7X9K2M */ private function generateRegistrationNumber() { do { // Create a unique seed from current time, microtime, and random data $seed = now()->timestamp . microtime(true) . random_int(100000, 999999); // Generate hash and extract alphanumeric characters $hash = hash('sha256', $seed); // Create character pool (excluding similar looking characters) $chars = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ'; // Start with prefix $prefix = 'REG'; // Generate 9 additional characters from hash $remaining = ''; $hashPos = 0; while (strlen($remaining) < 9 && $hashPos < strlen($hash)) { $hexChar = strtoupper($hash[$hashPos]); // Convert hex to index in our character pool $index = hexdec($hexChar) % strlen($chars); $remaining .= $chars[$index]; $hashPos++; } // If we need more characters, use additional random generation while (strlen($remaining) < 9) { $remaining .= $chars[random_int(0, strlen($chars) - 1)]; } $registrationNumber = $prefix . $remaining; // Check if this number already exists $exists = InformasiKegiatan::where('NoRegistrasi', $registrationNumber)->exists(); } while ($exists); // Keep generating until we get a unique number return $registrationNumber; } public function store(Request $request) { $validated = $request->validate([ 'jenis_dokumen' => 'required|string|max:50', 'nama_kegiatan' => 'required|string|max:255', 'pemrakarsa' => 'required|string|max:100', 'bidang_usaha' => 'required|string|max:100', 'deskripsi_kegiatan' => 'required|string', 'dampak_potensial' => 'required|string', 'kabupaten' => 'required|string|max:100', 'kecamatan' => 'required|string|max:100', 'kelurahan' => 'required|string|max:100', 'lokasi_kegiatan' => 'nullable|string|max:100', 'deskripsi_lokasi' => 'required|string', 'latitude' => 'nullable|numeric|between:-90,90', 'longitude' => 'nullable|numeric|between:-180,180', 'tanggal_mulai' => 'required|date|after_or_equal:today', 'skala_besaran' => 'nullable|string|max:50', 'kewenangan' => 'required|string|max:50', 'status' => 'required|in:aktif,nonaktif' ], [ 'jenis_dokumen.required' => 'Jenis dokumen wajib dipilih', 'nama_kegiatan.required' => 'Nama kegiatan wajib diisi', 'pemrakarsa.required' => 'Pemrakarsa wajib diisi', 'bidang_usaha.required' => 'Bidang usaha wajib diisi', 'deskripsi_kegiatan.required' => 'Deskripsi kegiatan wajib diisi', 'dampak_potensial.required' => 'Dampak potensial wajib diisi', 'kabupaten.required' => 'Kabupaten wajib dipilih', 'kecamatan.required' => 'Kecamatan wajib dipilih', 'kelurahan.required' => 'Kelurahan wajib dipilih', 'deskripsi_lokasi.required' => 'Deskripsi lokasi wajib diisi', 'tanggal_mulai.required' => 'Tanggal mulai periode wajib diisi', 'tanggal_mulai.after_or_equal' => 'Tanggal mulai tidak boleh kurang dari hari ini', 'kewenangan.required' => 'Kewenangan wajib dipilih', 'status.required' => 'Status wajib dipilih', 'latitude.between' => 'Latitude harus antara -90 sampai 90', 'longitude.between' => 'Longitude harus antara -180 sampai 180' ]); try { // Generate unique registration number $noRegistrasi = $this->generateRegistrationNumber(); // Calculate end date (10 business days from start date) $startDate = Carbon::parse($validated['tanggal_mulai']); $endDate = $this->calculateBusinessDayEnd($startDate, 10); // Get location names from database $kabupaten = Kabupaten::find($validated['kabupaten']); $kecamatan = Kecamatan::find($validated['kecamatan']); $kelurahan = Kelurahan::find($validated['kelurahan']); $kombinasiLokasi = $kabupaten->NamaKabupaten . ', ' . $kecamatan->NamaKecamatan . ', ' . $kelurahan->NamaKelurahan; InformasiKegiatan::create([ 'NoRegistrasi' => $noRegistrasi, 'JenisDokumen' => $validated['jenis_dokumen'], 'NamaKegiatan' => $validated['nama_kegiatan'], 'Pemrakarsa' => $validated['pemrakarsa'], 'BidangUsaha' => $validated['bidang_usaha'], 'DeskripsiKegiatan' => $validated['deskripsi_kegiatan'], 'DampakPotensial' => $validated['dampak_potensial'], 'ProvinsiKota' => $kombinasiLokasi, 'LokasiKegiatan' => $validated['lokasi_kegiatan'], 'DeskripsiLokasi' => $validated['deskripsi_lokasi'], 'Latitude' => $validated['latitude'], 'Longitude' => $validated['longitude'], 'TanggalMulaiPeriode' => $startDate, 'TanggalSelesaiPeriode' => $endDate, 'SkalaBesaran' => $validated['skala_besaran'], 'Kewenangan' => $validated['kewenangan'], 'Status' => $validated['status'] ]); return redirect()->route('pengumuman.index') ->with('success', 'Pengumuman kegiatan berhasil ditambahkan dengan nomor registrasi: ' . $noRegistrasi); } catch (\Exception $e) { return back() ->withInput() ->with('error', 'Terjadi kesalahan saat menyimpan data: ' . $e->getMessage()); } } public function calculatePeriod(Request $request) { $startDate = Carbon::parse($request->start_date); $endDate = $this->calculateBusinessDayEnd($startDate, 10); $periode = $startDate->format('d M') . ' - ' . $endDate->format('d M Y'); return response()->json([ 'success' => true, 'periode' => $periode, 'start_date' => $startDate->format('Y-m-d'), 'end_date' => $endDate->format('Y-m-d') ]); } /** * Calculate business day end date (excludes weekends and holidays) */ private function calculateBusinessDayEnd($startDate, $workingDays = 10) { $current = $startDate->copy(); $daysAdded = 0; // Indonesian public holidays 2025 (tanggal merah) $holidays = [ '2025-01-01', '2025-01-29', '2025-03-29', '2025-03-30', '2025-04-09', '2025-05-01', '2025-05-12', '2025-05-29', '2025-06-01', '2025-06-06', '2025-06-07', '2025-08-17', '2025-08-31', '2025-09-21', '2025-11-30', '2025-12-25' ]; while ($daysAdded < $workingDays) { $current->addDay(); // Skip weekends and holidays if ($current->dayOfWeek == Carbon::SATURDAY || $current->dayOfWeek == Carbon::SUNDAY || in_array($current->format('Y-m-d'), $holidays)) { continue; } $daysAdded++; } return $current; } /** * Get kecamatan by kabupaten ID */ public function getKecamatan(Request $request) { $kabupatenId = $request->kabupaten_id; $kecamatan = Kecamatan::where('KabupatenId', $kabupatenId) ->orderBy('NamaKecamatan', 'asc') ->get(['KecamatanId', 'NamaKecamatan']); return response()->json($kecamatan); } /** * Get kelurahan by kecamatan ID */ public function getKelurahan(Request $request) { $kecamatanId = $request->kecamatan_id; $kelurahan = Kelurahan::where('KecamatanId', $kecamatanId) ->orderBy('NamaKelurahan', 'asc') ->get(['KelurahanId', 'NamaKelurahan']); return response()->json($kelurahan); } }