diff --git a/app/Http/Controllers/API/LayananController.php b/app/Http/Controllers/API/LayananController.php new file mode 100644 index 0000000..1ded2cc --- /dev/null +++ b/app/Http/Controllers/API/LayananController.php @@ -0,0 +1,232 @@ +currentPeriod() + ->orderBy('TanggalMulaiPeriode', 'desc') + ->get(); + + $groupedData = []; + + foreach ($activities as $activity) { + $docType = strtolower(str_replace(['-', ' '], ['', ''], $activity->JenisDokumen)); + + if (!isset($groupedData[$docType])) { + $groupedData[$docType] = []; + } + + $groupedData[$docType][] = [ + 'id' => $activity->KegiatanID, + 'title' => $activity->NamaKegiatan, + 'description' => $activity->DampakPotensial, + 'link' => '#', + 'periode' => $activity->periode, + 'no_registrasi' => $activity->NoRegistrasi, + 'jenis_dokumen' => $activity->JenisDokumen, + 'bidang_usaha' => $activity->BidangUsaha, + 'skala_besaran' => $activity->SkalaBesaran, + 'lokasi_kegiatan' => $activity->LokasiKegiatan, + 'kewenangan' => $activity->Kewenangan, + 'pemrakarsa' => $activity->Pemrakarsa, + 'provinsi_kota' => $activity->ProvinsiKota, + 'deskripsi_kegiatan' => $activity->DeskripsiKegiatan, + 'dampak_potensial' => $activity->DampakPotensial, + 'deskripsi_lokasi' => $activity->DeskripsiLokasi, + 'latitude' => $activity->Latitude, + 'longitude' => $activity->Longitude + ]; + } + + // Add audit data (static for now) + $groupedData['audit'] = [ + [ + 'no' => 1, + 'company' => 'PT PETROKIMIA GRESIK', + 'title' => 'PENGUMUMAN AUDIT LINGKUNGAN HIDUP WAJIB BERKALA INDUSTRI PETROKIMIA', + 'description' => 'PT Petrokimia Gresik merupakan Kegiatan pada sektor Industri Petrokimia : Industri Pupuk dan Bahan Kimia (Amonia, Pupuk, Asam Sulfat, Asam Fosfat, dan Asam Klorida)...', + 'link' => '#' + ] + ]; + + $response = [ + 'header' => [ + 'title' => 'Daftar Pengumuman & Informasi', + 'subtitle' => 'Daftar Pengumuman dan Informasi terkait kegiatan yang mengajukan izin lingkungan' + ], + 'tabs' => [ + ['id' => 'amdal', 'label' => 'AMDAL'], + ['id' => 'addendum', 'label' => 'Addendum'], + ['id' => 'uklupl', 'label' => 'UKL - UPL'], + ['id' => 'audit', 'label' => 'AUDIT'] + ], + 'contents' => $groupedData + ]; + + return response()->json($response); + } catch (\Exception $e) { + return response()->json([ + 'error' => 'Failed to fetch layanan data', + 'message' => $e->getMessage() + ], 500); + } + } + + /** + * Get specific activity information + */ + public function getActivityInfo($id): JsonResponse + { + try { + $activity = InformasiKegiatan::findOrFail($id); + + $data = [ + 'no_registrasi' => $activity->NoRegistrasi, + 'jenis_dokumen' => $activity->JenisDokumen, + 'nama_kegiatan' => $activity->NamaKegiatan, + 'bidang_usaha' => $activity->BidangUsaha, + 'skala_besaran' => $activity->SkalaBesaran, + 'lokasi_kegiatan' => $activity->LokasiKegiatan, + 'kewenangan' => $activity->Kewenangan, + 'pemrakarsa' => $activity->Pemrakarsa, + 'provinsi_kota' => $activity->ProvinsiKota, + 'deskripsi_kegiatan' => $activity->DeskripsiKegiatan, + 'dampak_potensial' => $activity->DampakPotensial, + 'deskripsi_lokasi' => $activity->DeskripsiLokasi, + 'latitude' => $activity->Latitude, + 'longitude' => $activity->Longitude, + 'periode' => $activity->periode, + 'is_active' => $activity->isActiveFeedbackPeriod() + ]; + + return response()->json($data); + } catch (\Exception $e) { + return response()->json([ + 'error' => 'Activity not found', + 'message' => $e->getMessage() + ], 404); + } + } + + /** + * Submit feedback for an activity + */ + public function submitFeedback(Request $request): JsonResponse + { + try { + $validator = Validator::make($request->all(), [ + 'kegiatan_id' => 'required|exists:InformasiKegiatan,KegiatanID', + 'nama' => 'required|string|max:100', + 'peran' => 'required|in:masyarakat,lsm,akademisi,pemerintah', + 'nik' => 'required|string|size:16', + 'no_telepon' => 'required|string|max:20', + 'email' => 'required|email|max:100', + 'gender' => 'required|in:laki-laki,perempuan', + 'kondisi_lingkungan' => 'required|string', + 'nilai_lokal' => 'required|string', + 'kekhawatiran' => 'required|string', + 'harapan' => 'required|string', + 'tingkat_kekhawatiran' => 'required|integer|min:1|max:5', + 'foto_selfie' => 'nullable|image|mimes:jpg,jpeg,png|max:1024' + ]); + + if ($validator->fails()) { + return response()->json([ + 'error' => 'Validation failed', + 'errors' => $validator->errors() + ], 422); + } + + // Check if activity is still accepting feedback + $activity = InformasiKegiatan::findOrFail($request->kegiatan_id); + if (!$activity->isActiveFeedbackPeriod()) { + return response()->json([ + 'error' => 'Periode pengajuan tanggapan untuk kegiatan ini sudah berakhir' + ], 400); + } + + // Handle photo upload + $photoPath = null; + if ($request->hasFile('foto_selfie')) { + $photo = $request->file('foto_selfie'); + $fileName = time() . '_' . uniqid() . '.' . $photo->getClientOriginalExtension(); + $photoPath = $photo->storeAs('feedback-photos', $fileName, 'public'); + } + + // Create feedback record + $feedback = SaranTanggapan::create([ + 'KegiatanID' => $request->kegiatan_id, + 'Nama' => $request->nama, + 'Peran' => $request->peran, + 'NIK' => $request->nik, + 'NoTelepon' => $request->no_telepon, + 'Email' => $request->email, + 'Gender' => $request->gender, + 'KondisiLingkungan' => $request->kondisi_lingkungan, + 'NilaiLokal' => $request->nilai_lokal, + 'Kekhawatiran' => $request->kekhawatiran, + 'Harapan' => $request->harapan, + 'TingkatKekhawatiran' => $request->tingkat_kekhawatiran, + 'FotoSelfie' => $photoPath, + 'TanggalDiajukan' => now() + ]); + + return response()->json([ + 'message' => 'Tanggapan berhasil dikirim dan akan diproses oleh admin', + 'tanggapan_id' => $feedback->TanggapanID + ], 201); + + } catch (\Exception $e) { + return response()->json([ + 'error' => 'Failed to submit feedback', + 'message' => $e->getMessage() + ], 500); + } + } + + /** + * Get feedback statistics for an activity + */ + public function getFeedbackStats($id): JsonResponse + { + try { + $activity = InformasiKegiatan::findOrFail($id); + + $stats = [ + 'total_feedback' => $activity->saranTanggapan()->count(), + 'approved_feedback' => $activity->saranTanggapanApproved()->count(), + 'pending_feedback' => $activity->saranTanggapan()->pending()->count(), + 'by_role' => [ + 'masyarakat' => $activity->saranTanggapanApproved()->byRole('masyarakat')->count(), + 'lsm' => $activity->saranTanggapanApproved()->byRole('lsm')->count(), + 'akademisi' => $activity->saranTanggapanApproved()->byRole('akademisi')->count(), + 'pemerintah' => $activity->saranTanggapanApproved()->byRole('pemerintah')->count(), + ], + 'average_rating' => $activity->saranTanggapanApproved()->avg('TingkatKekhawatiran') + ]; + + return response()->json($stats); + } catch (\Exception $e) { + return response()->json([ + 'error' => 'Activity not found', + 'message' => $e->getMessage() + ], 404); + } + } +} diff --git a/app/Http/Controllers/JadwalSidangController.php b/app/Http/Controllers/JadwalSidangController.php index b947437..7460df2 100644 --- a/app/Http/Controllers/JadwalSidangController.php +++ b/app/Http/Controllers/JadwalSidangController.php @@ -4,59 +4,85 @@ namespace App\Http\Controllers; use Illuminate\Http\Request; use Illuminate\Support\Facades\File; +use App\Models\JadwalSidang as JadwalSidangModel; +use App\Models\Perusahaan; +use App\Models\DokumenType; +use Carbon\Carbon; class JadwalSidangController extends Controller { public function index(Request $request) { - $jadwal = json_decode(File::get(public_path('assets/json/backend/jadwal.json')), true); + // Ambil jadwal dari DB dengan relasi + $query = JadwalSidangModel::with(['perusahaan', 'dokumenType'])->orderBy('TanggalMulai', 'desc'); - // Filter by document type if provided in request - $documentType = $request->input('document_type'); - if ($documentType) { - $jadwal = array_values(array_filter($jadwal, function($item) use ($documentType) { - return isset($item['documentType']) && $item['documentType'] === $documentType; - })); + if ($request->filled('document_type')) { + $kode = $request->input('document_type'); + $query->whereHas('dokumenType', function ($q) use ($kode) { + $q->where('Kode', $kode); + }); } - return view('penjadwalan.jadwal_sidang', compact('jadwal')); + $jadwalDb = $query->get(); + + // Map ke struktur frontend lama agar script disable time bekerja + $jadwal = $jadwalDb->map(function ($j) { + return [ + 'id' => $j->JadwalSidangID, + 'title' => $j->NamaKegiatan, + 'start' => optional($j->TanggalMulai)->toIso8601String(), + 'end' => optional($j->TanggalSelesai)->toIso8601String(), + 'documentType' => optional($j->dokumenType)->Kode, + 'perusahaan' => optional($j->perusahaan)->NamaPerusahaan, + 'description' => $j->Deskripsi, + ]; + })->values()->all(); + + // Data master untuk modal + $perusahaan = Perusahaan::orderBy('NamaPerusahaan')->get(); + $dokumenTypes = DokumenType::orderBy('Nama')->get(); + + return view('penjadwalan.jadwal_sidang', compact('jadwal', 'perusahaan', 'dokumenTypes')); } public function create(Request $request) { - // Handle form submission for creating new schedule if ($request->isMethod('post')) { - $jadwal = json_decode(File::get(public_path('assets/json/backend/jadwal.json')), true); + // Validasi minimal + $request->validate([ + 'title' => 'required|string|max:255', + 'perusahaan_id' => 'required|exists:Perusahaan,PerusahaanID', + 'start_date' => 'required|string', + 'end_date' => 'required|string', + 'document_type' => 'required|string', + ]); - // Get the highest ID and increment by 1 - $maxId = 0; - foreach ($jadwal as $item) { - if (isset($item['id']) && $item['id'] > $maxId) { - $maxId = $item['id']; - } - } + // Cari / buat dokumen type jika belum ada + $kode = $request->input('document_type'); + $dokumenType = DokumenType::firstOrCreate( + ['Kode' => $kode], + ['Nama' => strtoupper(str_replace('_', ' ', $kode))] + ); - // Create new jadwal item - $newJadwal = [ - 'id' => $maxId + 1, - 'title' => $request->input('title'), - 'start' => $request->input('start_date'), - 'end' => $request->input('end_date'), - 'allDay' => $request->input('all_day', false), - 'documentType' => $request->input('document_type'), - 'description' => $request->input('description', '') - ]; + // Parse tanggal dari format d/m/Y H:i + $start = Carbon::createFromFormat('d/m/Y H:i', $request->input('start_date')); + $end = Carbon::createFromFormat('d/m/Y H:i', $request->input('end_date')); - // Add to existing jadwal - $jadwal[] = $newJadwal; - - // Save back to JSON file - File::put(public_path('assets/json/backend/jadwal.json'), json_encode($jadwal, JSON_PRETTY_PRINT)); + // Simpan ke DB + JadwalSidangModel::create([ + 'NamaKegiatan' => $request->input('title'), + 'PerusahaanID' => $request->input('perusahaan_id'), + 'DokumenTypeID' => $dokumenType->DokumenTypeID, + 'Deskripsi' => $request->input('description', ''), + 'TanggalMulai' => $start, + 'TanggalSelesai' => $end, + ]); return redirect()->route('jadwal.index')->with('success', 'Jadwal berhasil ditambahkan'); } - return view('penjadwalan.create_jadwal'); + // GET fallback (tidak digunakan karena pakai modal) + return redirect()->route('jadwal.index'); } } diff --git a/app/Http/Controllers/LayananController.php b/app/Http/Controllers/LayananController.php new file mode 100644 index 0000000..d23a145 --- /dev/null +++ b/app/Http/Controllers/LayananController.php @@ -0,0 +1,227 @@ + 'Layanan Perizinan Lingkungan', + 'subtitle' => 'Informasi terkini mengenai berbagai layanan perizinan dan penilaian dampak lingkungan' + ]; + + // Tabs data + $tabs = [ + ['id' => 'amdal', 'label' => 'AMDAL'], + ['id' => 'uklupl', 'label' => 'UKL-UPL'], + ['id' => 'addendum', 'label' => 'Addendum'], + ['id' => 'audit', 'label' => 'AUDIT'] + ]; + + // Contents data from database + $contents = [ + 'amdal' => $this->getKegiatanByJenis('AMDAL'), + 'uklupl' => $this->getKegiatanByJenis('UKL-UPL'), + 'addendum' => $this->getKegiatanByJenis('Addendum'), + 'audit' => $this->getKegiatanByJenis('AUDIT', true) // Special format for audit + ]; + + return response()->json([ + 'header' => $header, + 'tabs' => $tabs, + 'contents' => $contents + ]); + } + + /** + * Get kegiatan data by document type + */ + private function getKegiatanByJenis($jenis, $isAudit = false) + { + $kegiatan = InformasiKegiatan::where('JenisDokumen', $jenis) + ->where('Status', 'aktif') + ->whereDate('TanggalSelesaiPeriode', '>=', Carbon::now()) + ->orderBy('TanggalMulaiPeriode', 'desc') + ->take(10) + ->get(); + + if ($isAudit) { + // Format untuk audit table + return $kegiatan->map(function ($item, $index) { + return [ + 'no' => $index + 1, + 'company' => $item->Pemrakarsa, + 'title' => $item->NamaKegiatan, + 'description' => $item->DeskripsiKegiatan, + 'link' => route('layanan.detail', $item->KegiatanID) + ]; + })->toArray(); + } else { + // Format untuk standard items + return $kegiatan->map(function ($item) { + return [ + 'id' => $item->KegiatanID, + 'title' => $item->NamaKegiatan, + 'description' => $item->DeskripsiKegiatan, + 'periode' => $this->formatPeriode($item->TanggalMulaiPeriode, $item->TanggalSelesaiPeriode), + 'link' => route('layanan.detail', $item->KegiatanID), + 'status' => $item->Status, + 'pemrakarsa' => $item->Pemrakarsa, + 'jenis_dokumen' => $item->JenisDokumen, + 'no_registrasi' => $item->NoRegistrasi + ]; + })->toArray(); + } + } + + /** + * Format periode display + */ + private function formatPeriode($tanggalMulai, $tanggalSelesai) + { + $mulai = Carbon::parse($tanggalMulai); + $selesai = Carbon::parse($tanggalSelesai); + + return $mulai->format('d M') . ' - ' . $selesai->format('d M Y'); + } + + /** + * Get detail kegiatan for feedback form + */ + public function getDetailKegiatan($id) + { + $kegiatan = InformasiKegiatan::findOrFail($id); + + return response()->json([ + 'id' => $kegiatan->KegiatanID, + 'no_registrasi' => $kegiatan->NoRegistrasi, + 'jenis_dokumen' => $kegiatan->JenisDokumen, + 'nama_kegiatan' => $kegiatan->NamaKegiatan, + 'bidang_usaha' => $kegiatan->BidangUsaha, + 'skala_besaran' => $kegiatan->SkalaBesaran, + 'lokasi_kegiatan' => $kegiatan->LokasiKegiatan, + 'kewenangan' => $kegiatan->Kewenangan, + 'pemrakarsa' => $kegiatan->Pemrakarsa, + 'provinsi_kota' => $kegiatan->ProvinsiKota, + 'deskripsi_kegiatan' => $kegiatan->DeskripsiKegiatan, + 'dampak_potensial' => $kegiatan->DampakPotensial, + 'deskripsi_lokasi' => $kegiatan->DeskripsiLokasi, + 'latitude' => $kegiatan->Latitude, + 'longitude' => $kegiatan->Longitude, + 'periode' => $this->formatPeriode($kegiatan->TanggalMulaiPeriode, $kegiatan->TanggalSelesaiPeriode) + ]); + } + + /** + * Get wilayah data (Kabupaten, Kecamatan, Kelurahan) + */ + public function getWilayahData() + { + $kabupaten = Kabupaten::orderBy('NamaKabupaten')->get(); + $kecamatan = Kecamatan::orderBy('NamaKecamatan')->get(); + $kelurahan = Kelurahan::orderBy('NamaKelurahan')->get(); + + return response()->json([ + 'kabkota' => $kabupaten->map(function($item) { + return [ + 'value' => $item->KabupatenId, + 'label' => $item->NamaKabupaten + ]; + }), + 'kecamatan' => $kecamatan->map(function($item) { + return [ + 'value' => $item->KecamatanId, + 'label' => $item->NamaKecamatan, + 'kabupaten_id' => $item->KabupatenId + ]; + }), + 'kelurahan' => $kelurahan->map(function($item) { + return [ + 'value' => $item->KelurahanId, + 'label' => $item->NamaKelurahan, + 'kecamatan_id' => $item->KecamatanId + ]; + }) + ]); + } + + /** + * Get filtered kegiatan data + */ + public function getFilteredKegiatan(Request $request) + { + $query = InformasiKegiatan::where('Status', 'aktif') + ->whereDate('TanggalSelesaiPeriode', '>=', Carbon::now()); + + // Filter by search term + if ($request->search) { + $query->where(function($q) use ($request) { + $q->where('NamaKegiatan', 'ILIKE', '%' . $request->search . '%') + ->orWhere('DeskripsiKegiatan', 'ILIKE', '%' . $request->search . '%') + ->orWhere('Pemrakarsa', 'ILIKE', '%' . $request->search . '%'); + }); + } + + // Filter by document type + if ($request->jenis_dokumen) { + $allowedTypes = ['AMDAL', 'UKL-UPL', 'Addendum', 'AUDIT']; + if (in_array($request->jenis_dokumen, $allowedTypes)) { + $query->where('JenisDokumen', $request->jenis_dokumen); + } + } + + // Filter by location (you can extend this based on your needs) + if ($request->kabkota) { + $query->where('ProvinsiKota', 'ILIKE', '%' . $request->kabkota . '%'); + } + + $kegiatan = $query->orderBy('TanggalMulaiPeriode', 'desc')->get(); + + // Group by document type + $groupedKegiatan = $kegiatan->groupBy('JenisDokumen'); + + $contents = []; + foreach ($groupedKegiatan as $jenis => $items) { + $key = strtolower($jenis); + if ($jenis === 'AUDIT') { + $contents[$key] = $items->map(function ($item, $index) { + return [ + 'no' => $index + 1, + 'company' => $item->Pemrakarsa, + 'title' => $item->NamaKegiatan, + 'description' => $item->DeskripsiKegiatan, + 'link' => route('layanan.detail', $item->KegiatanID) + ]; + })->toArray(); + } else { + $contents[$key] = $items->map(function ($item) { + return [ + 'id' => $item->KegiatanID, + 'title' => $item->NamaKegiatan, + 'description' => $item->DeskripsiKegiatan, + 'periode' => $this->formatPeriode($item->TanggalMulaiPeriode, $item->TanggalSelesaiPeriode), + 'link' => route('layanan.detail', $item->KegiatanID), + 'status' => $item->Status, + 'pemrakarsa' => $item->Pemrakarsa, + 'jenis_dokumen' => $item->JenisDokumen, + 'no_registrasi' => $item->NoRegistrasi + ]; + })->toArray(); + } + } + + return response()->json($contents); + } +} diff --git a/app/Http/Controllers/LoginController.php b/app/Http/Controllers/LoginController.php index cec82c1..1e56164 100644 --- a/app/Http/Controllers/LoginController.php +++ b/app/Http/Controllers/LoginController.php @@ -10,4 +10,10 @@ class LoginController extends Controller { return view('auth/signin'); } + + public function register() + { + return view('auth/signup'); + } + } diff --git a/app/Http/Controllers/PengumumanController.php b/app/Http/Controllers/PengumumanController.php new file mode 100644 index 0000000..cafb68f --- /dev/null +++ b/app/Http/Controllers/PengumumanController.php @@ -0,0 +1,301 @@ +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); + } +} diff --git a/app/Http/Controllers/PerusahaanController.php b/app/Http/Controllers/PerusahaanController.php new file mode 100644 index 0000000..86db16a --- /dev/null +++ b/app/Http/Controllers/PerusahaanController.php @@ -0,0 +1,17 @@ + $request->query('user'), + 'daftarId' => $request->query('daftar'), + ]); + } +} diff --git a/app/Http/Controllers/SignupController.php b/app/Http/Controllers/SignupController.php new file mode 100644 index 0000000..a1e66d8 --- /dev/null +++ b/app/Http/Controllers/SignupController.php @@ -0,0 +1,65 @@ +validate([ + 'company_name' => ['required', 'string', 'max:255'], + 'email' => ['required', 'email', 'max:255', 'unique:users,email'], + 'password' => ['required', 'string', 'min:8', 'confirmed'], + ]); + + // Create user and signup record + DB::beginTransaction(); + try { + $user = User::create([ + 'name' => $data['company_name'], + 'email' => $data['email'], + 'username' => $data['email'], + 'password' => Hash::make($data['password']), + ]); + + // Ensure 'perusahaan' role exists, then assign it + if (!\Spatie\Permission\Models\Role::where('name', 'perusahaan')->exists()) { + \Spatie\Permission\Models\Role::create(['name' => 'perusahaan']); + } + $user->assignRole('perusahaan'); + + // Create Daftar record (Status Active by default) + $daftar = Daftar::create([ + 'NamaPerusahaan' => $data['company_name'], + 'Email' => $data['email'], + 'Password' => Hash::make($data['password']), + 'Status' => 'Active', + ]); + + // Ensure a Perusahaan exists and attach user via pivot + $perusahaan = Perusahaan::firstOrCreate( + ['NamaPerusahaan' => $data['company_name']], + ['Alamat' => null] + ); + + // Attach user to perusahaan via pivot (avoid duplicates) + $user->perusahaans()->syncWithoutDetaching([$perusahaan->PerusahaanID]); + + DB::commit(); + } catch (\Throwable $e) { + DB::rollBack(); + report($e); + return back()->withErrors(['error' => 'Registration failed.']); + } + + // Redirect to login page; user already has 'perusahaan' role and is attached to perusahaan + return redirect()->route('login.index')->with('status', 'Akun berhasil dibuat dan aktif. Silakan masuk.'); + } +} diff --git a/app/Models/Daftar.php b/app/Models/Daftar.php new file mode 100644 index 0000000..9c356b3 --- /dev/null +++ b/app/Models/Daftar.php @@ -0,0 +1,34 @@ + 'datetime', + 'updated_at' => 'datetime', + ]; +} diff --git a/app/Models/DokumenType.php b/app/Models/DokumenType.php new file mode 100644 index 0000000..6562eab --- /dev/null +++ b/app/Models/DokumenType.php @@ -0,0 +1,22 @@ +hasMany(JadwalSidang::class, 'DokumenTypeID', 'DokumenTypeID'); + } +} + diff --git a/app/Models/FastestPermohonan.php b/app/Models/FastestPermohonan.php index b41661c..9ede079 100644 --- a/app/Models/FastestPermohonan.php +++ b/app/Models/FastestPermohonan.php @@ -41,7 +41,7 @@ class FastestPermohonan extends Model return self::where('Kategori_ID', $kategori) ->whereDate('SyncDate', $latestSyncDate) - ->orderBy('FastestPermohonanID') // Keep API order by using insertion order + ->orderBy('FastestPermohonanID') ->limit(5) ->get(); } diff --git a/app/Models/InformasiKegiatan.php b/app/Models/InformasiKegiatan.php new file mode 100644 index 0000000..5d26b2e --- /dev/null +++ b/app/Models/InformasiKegiatan.php @@ -0,0 +1,103 @@ + 'date', + 'TanggalSelesaiPeriode' => 'date', + 'Latitude' => 'decimal:8', + 'Longitude' => 'decimal:8', + ]; + + /** + * Get all feedback for this activity + */ + public function saranTanggapan() + { + return $this->hasMany(SaranTanggapan::class, 'KegiatanID'); + } + + /** + * Get approved feedback only + */ + public function saranTanggapanApproved() + { + return $this->hasMany(SaranTanggapan::class, 'KegiatanID')->where('Status', 'approved'); + } + + /** + * Scope for active activities + */ + public function scopeActive($query) + { + return $query->where('Status', 'aktif'); + } + + /** + * Scope for specific document type + */ + public function scopeByDocumentType($query, $type) + { + return $query->where('JenisDokumen', $type); + } + + /** + * Scope for current period + */ + public function scopeCurrentPeriod($query) + { + $today = now()->toDateString(); + return $query->where('TanggalMulaiPeriode', '<=', $today) + ->where('TanggalSelesaiPeriode', '>=', $today); + } + + /** + * Get formatted period string + */ + public function getPeriodeAttribute() + { + return $this->TanggalMulaiPeriode->format('d M') . ' - ' . + $this->TanggalSelesaiPeriode->format('d M Y'); + } + + /** + * Check if activity is currently active for feedback + */ + public function isActiveFeedbackPeriod() + { + $today = now()->toDateString(); + return $this->Status === 'aktif' && + $this->TanggalMulaiPeriode <= $today && + $this->TanggalSelesaiPeriode >= $today; + } +} diff --git a/app/Models/JadwalSidang.php b/app/Models/JadwalSidang.php new file mode 100644 index 0000000..64bdfb8 --- /dev/null +++ b/app/Models/JadwalSidang.php @@ -0,0 +1,35 @@ + 'datetime', + 'TanggalSelesai' => 'datetime', + ]; + + public function dokumenType() + { + return $this->belongsTo(DokumenType::class, 'DokumenTypeID', 'DokumenTypeID'); + } + + public function perusahaan() + { + return $this->belongsTo(Perusahaan::class, 'PerusahaanID', 'PerusahaanID'); + } +} diff --git a/app/Models/Kabupaten.php b/app/Models/Kabupaten.php new file mode 100644 index 0000000..fe53e52 --- /dev/null +++ b/app/Models/Kabupaten.php @@ -0,0 +1,20 @@ +hasMany(Kecamatan::class, 'KabupatenId', 'KabupatenId'); + } +} diff --git a/app/Models/Kecamatan.php b/app/Models/Kecamatan.php new file mode 100644 index 0000000..798cbf6 --- /dev/null +++ b/app/Models/Kecamatan.php @@ -0,0 +1,29 @@ +belongsTo(Kabupaten::class, 'KabupatenId', 'KabupatenId'); + } + + public function kelurahan() + { + return $this->hasMany(Kelurahan::class, 'KecamatanId', 'KecamatanId'); + } +} diff --git a/app/Models/Kelurahan.php b/app/Models/Kelurahan.php new file mode 100644 index 0000000..500a2ff --- /dev/null +++ b/app/Models/Kelurahan.php @@ -0,0 +1,19 @@ +belongsTo(Kecamatan::class, 'KecamatanId', 'KecamatanId'); + } +} diff --git a/app/Models/Perusahaan.php b/app/Models/Perusahaan.php new file mode 100644 index 0000000..211e8e0 --- /dev/null +++ b/app/Models/Perusahaan.php @@ -0,0 +1,26 @@ +hasMany(JadwalSidang::class, 'PerusahaanID', 'PerusahaanID'); + } + + public function users() + { + return $this->belongsToMany(User::class, 'Perusahaan_User', 'PerusahaanID', 'UserID'); + } +} diff --git a/app/Models/SaranTanggapan.php b/app/Models/SaranTanggapan.php new file mode 100644 index 0000000..9c68a48 --- /dev/null +++ b/app/Models/SaranTanggapan.php @@ -0,0 +1,139 @@ + 'datetime', + 'TanggalDiproses' => 'datetime', + 'TingkatKekhawatiran' => 'integer' + ]; + + /** + * Get the activity this feedback belongs to + */ + public function informasiKegiatan() + { + return $this->belongsTo(InformasiKegiatan::class, 'KegiatanID'); + } + + /** + * Scope for pending feedback + */ + public function scopePending($query) + { + return $query->where('Status', 'pending'); + } + + /** + * Scope for approved feedback + */ + public function scopeApproved($query) + { + return $query->where('Status', 'approved'); + } + + /** + * Scope for rejected feedback + */ + public function scopeRejected($query) + { + return $query->where('Status', 'rejected'); + } + + /** + * Scope by role + */ + public function scopeByRole($query, $role) + { + return $query->where('Peran', $role); + } + + /** + * Get the rating level description + */ + public function getRatingDescriptionAttribute() + { + $descriptions = [ + 1 => 'Sangat Khawatir', + 2 => 'Khawatir', + 3 => 'Netral', + 4 => 'Berharap', + 5 => 'Sangat Berharap' + ]; + + return $descriptions[$this->TingkatKekhawatiran] ?? 'Tidak Diketahui'; + } + + /** + * Get formatted submission date + */ + public function getFormattedSubmissionDateAttribute() + { + return $this->TanggalDiajukan->format('d M Y H:i'); + } + + /** + * Get photo URL + */ + public function getPhotoUrlAttribute() + { + if ($this->FotoSelfie) { + return asset('storage/feedback-photos/' . $this->FotoSelfie); + } + return null; + } + + /** + * Approve this feedback + */ + public function approve($adminNote = null) + { + $this->update([ + 'Status' => 'approved', + 'CatatanAdmin' => $adminNote, + 'TanggalDiproses' => now() + ]); + } + + /** + * Reject this feedback + */ + public function reject($adminNote = null) + { + $this->update([ + 'Status' => 'rejected', + 'CatatanAdmin' => $adminNote, + 'TanggalDiproses' => now() + ]); + } +} diff --git a/app/Models/Signup.php b/app/Models/Signup.php new file mode 100644 index 0000000..21af8ea --- /dev/null +++ b/app/Models/Signup.php @@ -0,0 +1,34 @@ + 'datetime', + 'updated_at' => 'datetime', + ]; +} diff --git a/app/Models/User.php b/app/Models/User.php index c05eea9..d735ffd 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -64,4 +64,9 @@ class User extends Authenticatable 'password' => 'hashed', ]; } + + public function perusahaans() + { + return $this->belongsToMany(Perusahaan::class, 'Perusahaan_User', 'UserID', 'PerusahaanID'); + } } diff --git a/database/migrations/2025_01_17_100000_create_informasi_kegiatan_table.php b/database/migrations/2025_01_17_100000_create_informasi_kegiatan_table.php new file mode 100644 index 0000000..a7b91d0 --- /dev/null +++ b/database/migrations/2025_01_17_100000_create_informasi_kegiatan_table.php @@ -0,0 +1,47 @@ +id('KegiatanID'); + $table->string('NoRegistrasi', 50)->unique(); + $table->string('JenisDokumen', 50); + $table->string('NamaKegiatan'); + $table->string('BidangUsaha', 100); + $table->string('SkalaBesaran', 50); + $table->string('LokasiKegiatan', 100)->nullable(); + $table->string('Kewenangan', 50); + $table->string('Pemrakarsa', 100); + $table->string('ProvinsiKota', 100); + $table->text('DeskripsiKegiatan'); + $table->text('DampakPotensial'); + $table->string('DeskripsiLokasi'); + $table->decimal('Latitude', 10, 8)->nullable(); + $table->decimal('Longitude', 11, 8)->nullable(); + $table->date('TanggalMulaiPeriode'); + $table->date('TanggalSelesaiPeriode'); + $table->enum('Status', ['aktif', 'nonaktif', 'selesai'])->default('aktif'); + $table->timestamps(); + + $table->index(['JenisDokumen', 'Status']); + $table->index(['TanggalMulaiPeriode', 'TanggalSelesaiPeriode']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('InformasiKegiatan'); + } +}; diff --git a/database/migrations/2025_01_17_101000_create_saran_tanggapan_table.php b/database/migrations/2025_01_17_101000_create_saran_tanggapan_table.php new file mode 100644 index 0000000..8afb400 --- /dev/null +++ b/database/migrations/2025_01_17_101000_create_saran_tanggapan_table.php @@ -0,0 +1,49 @@ +id('TanggapanID'); + $table->unsignedBigInteger('KegiatanID'); + $table->string('Nama', 100); + $table->enum('Peran', ['masyarakat', 'lsm', 'akademisi', 'pemerintah']); + $table->string('NIK', 16); + $table->string('NoTelepon', 20); + $table->string('Email', 100); + $table->enum('Gender', ['laki-laki', 'perempuan']); + $table->text('KondisiLingkungan'); + $table->text('NilaiLokal'); + $table->text('Kekhawatiran'); + $table->text('Harapan'); + $table->tinyInteger('TingkatKekhawatiran')->comment('1-5 scale: 1=sangat khawatir, 5=sangat berharap'); + $table->string('FotoSelfie', 255)->nullable(); + $table->enum('Status', ['pending', 'approved', 'rejected'])->default('pending'); + $table->text('CatatanAdmin')->nullable(); + $table->timestamp('TanggalDiajukan')->useCurrent(); + $table->timestamp('TanggalDiproses')->nullable(); + $table->timestamps(); + + $table->foreign('KegiatanID')->references('KegiatanID')->on('InformasiKegiatan')->onDelete('cascade'); + $table->index(['KegiatanID', 'Status']); + $table->index(['Peran', 'Status']); + $table->index('TanggalDiajukan'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('SaranTanggapan'); + } +}; diff --git a/database/migrations/2025_09_12_000000_create_dokumen_type_table.php b/database/migrations/2025_09_12_000000_create_dokumen_type_table.php new file mode 100644 index 0000000..b595ac6 --- /dev/null +++ b/database/migrations/2025_09_12_000000_create_dokumen_type_table.php @@ -0,0 +1,32 @@ +id('DokumenTypeID'); + $table->string('Kode', 50)->unique(); + $table->string('Nama', 100); + $table->timestamps(); + + $table->index(['Kode'], 'IX_DokumenType_Kode'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('DokumenType'); + } +}; + diff --git a/database/migrations/2025_09_12_000001_create_jadwal_sidang_table.php b/database/migrations/2025_09_12_000001_create_jadwal_sidang_table.php new file mode 100644 index 0000000..b487bee --- /dev/null +++ b/database/migrations/2025_09_12_000001_create_jadwal_sidang_table.php @@ -0,0 +1,45 @@ +id('JadwalSidangID'); + $table->string('NamaKegiatan'); + // FK ke master DokumenType + $table->unsignedBigInteger('DokumenTypeID'); + $table->text('Deskripsi')->nullable(); + $table->dateTime('TanggalMulai'); + $table->dateTime('TanggalSelesai'); + $table->timestamps(); + + $table->index(['DokumenTypeID'], 'IX_Jadwal_DokumenTypeID'); + $table->index(['TanggalMulai'], 'IX_Jadwal_TanggalMulai'); + $table->index(['TanggalMulai', 'TanggalSelesai'], 'IX_Jadwal_TanggalRange'); + + $table->foreign('DokumenTypeID') + ->references('DokumenTypeID') + ->on('DokumenType') + ->cascadeOnUpdate() + ->restrictOnDelete(); + }); + + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('JadwalSidang'); + } +}; diff --git a/database/migrations/2025_09_12_000002_create_perusahaan_table.php b/database/migrations/2025_09_12_000002_create_perusahaan_table.php new file mode 100644 index 0000000..b0ba7a5 --- /dev/null +++ b/database/migrations/2025_09_12_000002_create_perusahaan_table.php @@ -0,0 +1,29 @@ +id('PerusahaanID'); + $table->string('NamaPerusahaan', 255); + $table->text('Alamat')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('Perusahaan'); + } +}; diff --git a/database/migrations/2025_09_12_000003_alter_jadwal_sidang_add_perusahaan_fk.php b/database/migrations/2025_09_12_000003_alter_jadwal_sidang_add_perusahaan_fk.php new file mode 100644 index 0000000..4ce0404 --- /dev/null +++ b/database/migrations/2025_09_12_000003_alter_jadwal_sidang_add_perusahaan_fk.php @@ -0,0 +1,37 @@ +unsignedBigInteger('PerusahaanID')->after('JadwalSidangID'); + $table->index(['PerusahaanID'], 'IX_Jadwal_PerusahaanID'); + $table->foreign('PerusahaanID') + ->references('PerusahaanID') + ->on('Perusahaan') + ->cascadeOnUpdate() + ->restrictOnDelete(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('JadwalSidang', function (Blueprint $table) { + $table->dropForeign(['PerusahaanID']); + $table->dropIndex('IX_Jadwal_PerusahaanID'); + $table->dropColumn('PerusahaanID'); + }); + } +}; + diff --git a/database/migrations/2025_09_17_000001_create_signups_table.php b/database/migrations/2025_09_17_000001_create_signups_table.php new file mode 100644 index 0000000..67a99c9 --- /dev/null +++ b/database/migrations/2025_09_17_000001_create_signups_table.php @@ -0,0 +1,31 @@ +uuid('DaftarID')->primary(); + $table->string('NamaPerusahaan'); + $table->string('Email')->unique(); + $table->string('Password'); + $table->string('Status')->default('Active'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('signups'); + } +}; diff --git a/database/migrations/2025_09_17_000003_create_perusahaan_user_table.php b/database/migrations/2025_09_17_000003_create_perusahaan_user_table.php new file mode 100644 index 0000000..c967438 --- /dev/null +++ b/database/migrations/2025_09_17_000003_create_perusahaan_user_table.php @@ -0,0 +1,36 @@ +id(), so it's bigint + $table->unsignedBigInteger('PerusahaanID'); + // users.id is UUID in this project + $table->uuid('UserID'); + $table->timestamps(); + + $table->primary(['PerusahaanID', 'UserID']); + + // Foreign key constraints + $table->foreign('PerusahaanID')->references('PerusahaanID')->on('Perusahaan')->onDelete('cascade'); + $table->foreign('UserID')->references('id')->on('users')->onDelete('cascade'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('Perusahaan_User'); + } +}; diff --git a/database/migrations/2025_09_17_045610_kabupaten.php b/database/migrations/2025_09_17_045610_kabupaten.php new file mode 100644 index 0000000..48f5afd --- /dev/null +++ b/database/migrations/2025_09_17_045610_kabupaten.php @@ -0,0 +1,28 @@ +id('KabupatenId'); + $table->string('NamaKabupaten'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('Kabupaten'); + } +}; diff --git a/database/migrations/2025_09_17_045625_kecamatan.php b/database/migrations/2025_09_17_045625_kecamatan.php new file mode 100644 index 0000000..3ef994f --- /dev/null +++ b/database/migrations/2025_09_17_045625_kecamatan.php @@ -0,0 +1,30 @@ +id('KecamatanId'); + $table->unsignedBigInteger('KabupatenId'); + $table->foreign('KabupatenId')->references('KabupatenId')->on('Kabupaten')->onDelete('cascade'); + $table->string('NamaKecamatan'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('Kecamatan'); + } +}; diff --git a/database/migrations/2025_09_17_045651_kelurahan.php b/database/migrations/2025_09_17_045651_kelurahan.php new file mode 100644 index 0000000..80fa63a --- /dev/null +++ b/database/migrations/2025_09_17_045651_kelurahan.php @@ -0,0 +1,30 @@ +id('KelurahanId'); + $table->unsignedBigInteger('KecamatanId'); + $table->foreign('KecamatanId')->references('KecamatanId')->on('Kecamatan')->onDelete('cascade'); + $table->string('NamaKelurahan'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('Kelurahan'); + } +}; diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 0ac5283..2884292 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -2,10 +2,10 @@ namespace Database\Seeders; -use App\Models\User; use Database\Seeders\UserSeeder; use Database\Seeders\RolesAndPermissionsSeeder; -// use Illuminate\Database\Console\Seeds\WithoutModelEvents; +use Database\Seeders\PerusahaanSeeder; +use Database\Seeders\InformasiKegiatanSeeder; use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder @@ -19,6 +19,11 @@ class DatabaseSeeder extends Seeder $this->call([ RolesAndPermissionsSeeder::class, UserSeeder::class, + PerusahaanSeeder::class, + InformasiKegiatanSeeder::class, + KabupatenTableSeeder::class, + KecamatanTableSeeder::class, + KelurahanTableSeeder::class, ]); } } diff --git a/database/seeders/InformasiKegiatanSeeder.php b/database/seeders/InformasiKegiatanSeeder.php new file mode 100644 index 0000000..07465d5 --- /dev/null +++ b/database/seeders/InformasiKegiatanSeeder.php @@ -0,0 +1,313 @@ + 'REGC7EGE9465', + 'JenisDokumen' => 'AMDAL', + 'NamaKegiatan' => 'Kegiatan Penggalian Kuarsa/Pasir Kuarsa, LUKITO HARTONO LAWY', + 'BidangUsaha' => 'Pertambangan', + 'SkalaBesaran' => '50000.00 ton/tahun', + 'LokasiKegiatan' => 'Desa Marga Sungsang', + 'Kewenangan' => 'Pusat', + 'Pemrakarsa' => 'PT. KARUNIA SAGEA MINERAL', + 'ProvinsiKota' => 'SUMATERA SELATAN', + 'DeskripsiKegiatan' => 'Kegiatan penggalian kuarsa dan pasir kuarsa untuk keperluan industri kaca dan konstruksi dengan metode tambang terbuka', + 'DampakPotensial' => 'Penurunan Kualitas Udara, Peningkatan Kebisingan, Peningkatan Air Larian, Peningkatan Timbulan Sampah, Terjadinya Gangguan Lalu Lintas', + 'DeskripsiLokasi' => 'Desa Marga Sungsang, Kecamatan Banyuasin II, Kabupaten Banyuasin, Sumatra Selatan', + 'Latitude' => -2.1234567, + 'Longitude' => 104.7654321, + 'TanggalMulaiPeriode' => Carbon::now()->subDays(5), + 'TanggalSelesaiPeriode' => Carbon::now()->addDays(10), + 'Status' => 'aktif' + ], + [ + 'NoRegistrasi' => 'REG976H3AG89', + 'JenisDokumen' => 'AMDAL', + 'NamaKegiatan' => 'Pembangunan Waterfront Malalayang, TARCISIUS ASWIN JULIZAR', + 'BidangUsaha' => 'Perhubungan Laut', + 'SkalaBesaran' => '5000.00 m2', + 'LokasiKegiatan' => 'Pantai Malalayang', + 'Kewenangan' => 'Daerah', + 'Pemrakarsa' => 'PT. BENUA LAUT LEPAS', + 'ProvinsiKota' => 'SULAWESI UTARA', + 'DeskripsiKegiatan' => 'Pembangunan Terminal Alih Muat Barang (Ship To Ship Transfer) dan Fasilitas Penunjangnya oleh PT. Benua Laut Lepas', + 'DampakPotensial' => 'Penurunan kualitas air laut, Peningkatan Sedimentasi, Perubahan Garis Pantai, Peningkatan Kesempatan Kerja dan Berusaha', + 'DeskripsiLokasi' => 'Pantai Malalayang, Kecamatan Malalayang, Kota Manado, Sulawesi Utara', + 'Latitude' => 1.4897167, + 'Longitude' => 124.8421083, + 'TanggalMulaiPeriode' => Carbon::now()->subDays(2), + 'TanggalSelesaiPeriode' => Carbon::now()->addDays(12), + 'Status' => 'aktif' + ], + [ + 'NoRegistrasi' => 'REGAMD001235', + 'JenisDokumen' => 'AMDAL', + 'NamaKegiatan' => 'Pembangunan PLTU Batubara Cilacap Unit 3 & 4', + 'BidangUsaha' => 'Energi dan Sumber Daya Mineral', + 'SkalaBesaran' => '2000.00 MW', + 'LokasiKegiatan' => 'Pesisir Cilacap', + 'Kewenangan' => 'Pusat', + 'Pemrakarsa' => 'PT. PLN (PERSERO)', + 'ProvinsiKota' => 'JAWA TENGAH', + 'DeskripsiKegiatan' => 'Pembangunan pembangkit listrik tenaga uap dengan bahan bakar batubara kapasitas 2x1000 MW lengkap dengan fasilitas penunjang', + 'DampakPotensial' => 'Emisi gas buang, Limbah abu batubara, Peningkatan suhu air laut, Dampak visual, Peningkatan lapangan kerja', + 'DeskripsiLokasi' => 'Pesisir Cilacap, Kecamatan Cilacap Selatan, Kabupaten Cilacap, Jawa Tengah', + 'Latitude' => -7.7326, + 'Longitude' => 109.0158, + 'TanggalMulaiPeriode' => Carbon::now()->subDays(10), + 'TanggalSelesaiPeriode' => Carbon::now()->addDays(5), + 'Status' => 'aktif' + ], + [ + 'NoRegistrasi' => 'REGAMD004578', + 'JenisDokumen' => 'AMDAL', + 'NamaKegiatan' => 'Pembangunan Jalan Tol Trans Sumatera Segmen Kayu Agung - Palembang', + 'BidangUsaha' => 'Perhubungan Darat', + 'SkalaBesaran' => '75.50 km', + 'LokasiKegiatan' => 'Kabupaten Ogan Komering Ilir - Kota Palembang', + 'Kewenangan' => 'Pusat', + 'Pemrakarsa' => 'PT. HUTAMA KARYA (PERSERO)', + 'ProvinsiKota' => 'SUMATERA SELATAN', + 'DeskripsiKegiatan' => 'Pembangunan jalan tol 4 lajur sebagai bagian dari jaringan jalan tol Trans Sumatera untuk meningkatkan konektivitas antar wilayah', + 'DampakPotensial' => 'Pembebasan lahan, Gangguan hidrologi, Kebisingan konstruksi, Peningkatan aksesibilitas, Pembukaan lapangan kerja', + 'DeskripsiLokasi' => 'Melintasi Kabupaten Ogan Komering Ilir hingga Kota Palembang, Sumatera Selatan', + 'Latitude' => -3.0044, + 'Longitude' => 104.6178, + 'TanggalMulaiPeriode' => Carbon::now()->subDays(15), + 'TanggalSelesaiPeriode' => Carbon::now()->addDays(0), + 'Status' => 'berakhir' + ], + [ + 'NoRegistrasi' => 'REGAMD007892', + 'JenisDokumen' => 'AMDAL', + 'NamaKegiatan' => 'Pengembangan Bandara Internasional Syamsudin Noor Fase II', + 'BidangUsaha' => 'Perhubungan Udara', + 'SkalaBesaran' => '15000.00 m2', + 'LokasiKegiatan' => 'Banjarbaru', + 'Kewenangan' => 'Pusat', + 'Pemrakarsa' => 'PT. ANGKASA PURA II (PERSERO)', + 'ProvinsiKota' => 'KALIMANTAN SELATAN', + 'DeskripsiKegiatan' => 'Perluasan terminal penumpang, penambahan apron pesawat, dan peningkatan fasilitas bandara untuk mendukung peningkatan kapasitas penumpang', + 'DampakPotensial' => 'Kebisingan pesawat, Peningkatan lalu lintas, Perubahan tata guna lahan, Peningkatan perekonomian daerah', + 'DeskripsiLokasi' => 'Banjarbaru, Kalimantan Selatan', + 'Latitude' => -3.4424, + 'Longitude' => 114.7625, + 'TanggalMulaiPeriode' => Carbon::now()->addDays(3), + 'TanggalSelesaiPeriode' => Carbon::now()->addDays(18), + 'Status' => 'aktif' + ], + + // Addendum Documents (5 records) + [ + 'NoRegistrasi' => 'REG7HA9279E8', + 'JenisDokumen' => 'Addendum', + 'NamaKegiatan' => 'Addendum AMDAL Pembangunan Waterfront Malalayang', + 'BidangUsaha' => 'Perhubungan Laut', + 'SkalaBesaran' => '7500.00 m2', + 'LokasiKegiatan' => 'Pantai Malalayang', + 'Kewenangan' => 'Daerah', + 'Pemrakarsa' => 'PT. BENUA LAUT LEPAS', + 'ProvinsiKota' => 'SULAWESI UTARA', + 'DeskripsiKegiatan' => 'Perubahan dan penambahan fasilitas pendukung pada proyek pembangunan waterfront dengan penambahan area parkir dan fasilitas komersial', + 'DampakPotensial' => 'Peningkatan lalu lintas, Perubahan tata guna lahan, Peningkatan aktivitas ekonomi lokal', + 'DeskripsiLokasi' => 'Pantai Malalayang, Kecamatan Malalayang, Kota Manado, Sulawesi Utara', + 'Latitude' => 1.4897167, + 'Longitude' => 124.8421083, + 'TanggalMulaiPeriode' => Carbon::now()->subDays(1), + 'TanggalSelesaiPeriode' => Carbon::now()->addDays(13), + 'Status' => 'aktif' + ], + [ + 'NoRegistrasi' => 'REGADD002456', + 'JenisDokumen' => 'Addendum', + 'NamaKegiatan' => 'Addendum AMDAL PLTU Batubara Cilacap - Perubahan Teknologi', + 'BidangUsaha' => 'Energi dan Sumber Daya Mineral', + 'SkalaBesaran' => '2000.00 MW', + 'LokasiKegiatan' => 'Pesisir Cilacap', + 'Kewenangan' => 'Pusat', + 'Pemrakarsa' => 'PT. PLN (PERSERO)', + 'ProvinsiKota' => 'JAWA TENGAH', + 'DeskripsiKegiatan' => 'Perubahan teknologi pembakaran batubara dengan teknologi yang lebih ramah lingkungan dan efisien untuk mengurangi emisi', + 'DampakPotensial' => 'Pengurangan emisi SO2 dan NOx, Peningkatan efisiensi bahan bakar, Pengurangan abu terbang', + 'DeskripsiLokasi' => 'Pesisir Cilacap, Kecamatan Cilacap Selatan, Kabupaten Cilacap, Jawa Tengah', + 'Latitude' => -7.7326, + 'Longitude' => 109.0158, + 'TanggalMulaiPeriode' => Carbon::now()->subDays(8), + 'TanggalSelesaiPeriode' => Carbon::now()->addDays(7), + 'Status' => 'aktif' + ], + [ + 'NoRegistrasi' => 'REGADD003789', + 'JenisDokumen' => 'Addendum', + 'NamaKegiatan' => 'Addendum AMDAL Tol Trans Sumatera - Penambahan Rest Area', + 'BidangUsaha' => 'Perhubungan Darat', + 'SkalaBesaran' => '85.50 km', + 'LokasiKegiatan' => 'Kabupaten Ogan Komering Ilir - Kota Palembang', + 'Kewenangan' => 'Pusat', + 'Pemrakarsa' => 'PT. HUTAMA KARYA (PERSERO)', + 'ProvinsiKota' => 'SUMATERA SELATAN', + 'DeskripsiKegiatan' => 'Penambahan 3 rest area di sepanjang ruas tol dengan fasilitas SPBU, food court, dan area istirahat untuk meningkatkan keselamatan perjalanan', + 'DampakPotensial' => 'Peningkatan keselamatan jalan, Pembukaan lapangan kerja baru, Peningkatan konsumsi air dan listrik', + 'DeskripsiLokasi' => 'Melintasi Kabupaten Ogan Komering Ilir hingga Kota Palembang, Sumatera Selatan', + 'Latitude' => -3.0044, + 'Longitude' => 104.6178, + 'TanggalMulaiPeriode' => Carbon::now()->subDays(3), + 'TanggalSelesaiPeriode' => Carbon::now()->addDays(12), + 'Status' => 'aktif' + ], + [ + 'NoRegistrasi' => 'REGADD005123', + 'JenisDokumen' => 'Addendum', + 'NamaKegiatan' => 'Addendum AMDAL Bandara Syamsudin Noor - Perpanjangan Runway', + 'BidangUsaha' => 'Perhubungan Udara', + 'SkalaBesaran' => '3200.00 m', + 'LokasiKegiatan' => 'Banjarbaru', + 'Kewenangan' => 'Pusat', + 'Pemrakarsa' => 'PT. ANGKASA PURA II (PERSERO)', + 'ProvinsiKota' => 'KALIMANTAN SELATAN', + 'DeskripsiKegiatan' => 'Perpanjangan runway dari 2500m menjadi 3200m untuk mengakomodasi pesawat wide body dan meningkatkan kapasitas operasional bandara', + 'DampakPotensial' => 'Peningkatan kebisingan pesawat, Pembebasan lahan tambahan, Peningkatan frekuensi penerbangan', + 'DeskripsiLokasi' => 'Banjarbaru, Kalimantan Selatan', + 'Latitude' => -3.4424, + 'Longitude' => 114.7625, + 'TanggalMulaiPeriode' => Carbon::now()->addDays(5), + 'TanggalSelesaiPeriode' => Carbon::now()->addDays(20), + 'Status' => 'aktif' + ], + [ + 'NoRegistrasi' => 'REGADD006789', + 'JenisDokumen' => 'Addendum', + 'NamaKegiatan' => 'Addendum AMDAL Tambang Kuarsa - Perluasan Area Tambang', + 'BidangUsaha' => 'Pertambangan', + 'SkalaBesaran' => '75000.00 ton/tahun', + 'LokasiKegiatan' => 'Desa Marga Sungsang', + 'Kewenangan' => 'Pusat', + 'Pemrakarsa' => 'PT. KARUNIA SAGEA MINERAL', + 'ProvinsiKota' => 'SUMATERA SELATAN', + 'DeskripsiKegiatan' => 'Perluasan area penambangan kuarsa dari 100 ha menjadi 150 ha untuk memenuhi peningkatan demand pasar domestik dan ekspor', + 'DampakPotensial' => 'Peningkatan kebisingan, Bertambahnya debu dan partikel, Perluasan dampak visual, Peningkatan produksi', + 'DeskripsiLokasi' => 'Desa Marga Sungsang, Kecamatan Banyuasin II, Kabupaten Banyuasin, Sumatra Selatan', + 'Latitude' => -2.1234567, + 'Longitude' => 104.7654321, + 'TanggalMulaiPeriode' => Carbon::now()->addDays(2), + 'TanggalSelesaiPeriode' => Carbon::now()->addDays(17), + 'Status' => 'aktif' + ], + + // UKL-UPL Documents (5 records) + [ + 'NoRegistrasi' => 'REGDF3D52A98', + 'JenisDokumen' => 'UKL-UPL', + 'NamaKegiatan' => 'Pembangunan Pabrik Pengolahan Kelapa Sawit', + 'BidangUsaha' => 'Industri Pangan', + 'SkalaBesaran' => '1000.00 ton/hari', + 'LokasiKegiatan' => 'Desa Sumber Jaya', + 'Kewenangan' => 'Provinsi', + 'Pemrakarsa' => 'PT. AGRO MANDIRI SEJAHTERA', + 'ProvinsiKota' => 'RIAU', + 'DeskripsiKegiatan' => 'Pembangunan dan operasional pabrik pengolahan kelapa sawit menjadi crude palm oil (CPO) dan produk turunannya', + 'DampakPotensial' => 'Pencemaran air limbah, Bau dari proses produksi, Peningkatan lalu lintas kendaraan berat', + 'DeskripsiLokasi' => 'Desa Sumber Jaya, Kecamatan Kampar, Kabupaten Kampar, Riau', + 'Latitude' => 0.2895833, + 'Longitude' => 101.3828889, + 'TanggalMulaiPeriode' => Carbon::now()->addDays(1), + 'TanggalSelesaiPeriode' => Carbon::now()->addDays(15), + 'Status' => 'aktif' + ], + [ + 'NoRegistrasi' => 'REGUKL001234', + 'JenisDokumen' => 'UKL-UPL', + 'NamaKegiatan' => 'Pembangunan Pabrik Tekstil dan Garmen Modern', + 'BidangUsaha' => 'Industri Tekstil', + 'SkalaBesaran' => '500.00 ton/bulan', + 'LokasiKegiatan' => 'Kawasan Industri Karawang', + 'Kewenangan' => 'Provinsi', + 'Pemrakarsa' => 'PT. TEKSTIL NUSANTARA MAKMUR', + 'ProvinsiKota' => 'JAWA BARAT', + 'DeskripsiKegiatan' => 'Pembangunan pabrik tekstil terintegrasi dengan fasilitas dyeing, printing, dan finishing untuk produksi kain dan garmen', + 'DampakPotensial' => 'Limbah cair pewarna, Konsumsi air tinggi, Kebisingan mesin, Peningkatan lapangan kerja', + 'DeskripsiLokasi' => 'Kawasan Industri Karawang, Kabupaten Karawang, Jawa Barat', + 'Latitude' => -6.3019, + 'Longitude' => 107.3372, + 'TanggalMulaiPeriode' => Carbon::now()->subDays(4), + 'TanggalSelesaiPeriode' => Carbon::now()->addDays(11), + 'Status' => 'aktif' + ], + [ + 'NoRegistrasi' => 'REGUKL002567', + 'JenisDokumen' => 'UKL-UPL', + 'NamaKegiatan' => 'Pembangunan Cold Storage dan Distribution Center', + 'BidangUsaha' => 'Logistik dan Pergudangan', + 'SkalaBesaran' => '10000.00 m3', + 'LokasiKegiatan' => 'Kawasan Industri Medan', + 'Kewenangan' => 'Provinsi', + 'Pemrakarsa' => 'PT. LOGISTIK SUMATERA UTARA', + 'ProvinsiKota' => 'SUMATERA UTARA', + 'DeskripsiKegiatan' => 'Pembangunan fasilitas penyimpanan dingin dan pusat distribusi untuk produk pertanian dan perikanan dengan sistem otomatis', + 'DampakPotensial' => 'Konsumsi listrik tinggi, Refrigeran ramah lingkungan, Peningkatan lalu lintas truk, Peningkatan nilai tambah produk', + 'DeskripsiLokasi' => 'Kawasan Industri Medan, Kota Medan, Sumatera Utara', + 'Latitude' => 3.6951, + 'Longitude' => 98.7579, + 'TanggalMulaiPeriode' => Carbon::now()->subDays(6), + 'TanggalSelesaiPeriode' => Carbon::now()->addDays(9), + 'Status' => 'aktif' + ], + [ + 'NoRegistrasi' => 'REGUKL003890', + 'JenisDokumen' => 'UKL-UPL', + 'NamaKegiatan' => 'Pembangunan Pabrik Pengolahan Limbah Medis', + 'BidangUsaha' => 'Jasa Lingkungan', + 'SkalaBesaran' => '50.00 ton/hari', + 'LokasiKegiatan' => 'Kawasan Industri Makassar', + 'Kewenangan' => 'Provinsi', + 'Pemrakarsa' => 'PT. ECO MEDICAL WASTE SOLUTION', + 'ProvinsiKota' => 'SULAWESI SELATAN', + 'DeskripsiKegiatan' => 'Pembangunan fasilitas pengolahan limbah medis B3 dengan teknologi incinerator dan autoclave untuk wilayah Sulawesi Selatan', + 'DampakPotensial' => 'Emisi hasil pembakaran, Pengelolaan abu insinerator, Peningkatan keselamatan kesehatan masyarakat', + 'DeskripsiLokasi' => 'Kawasan Industri Makassar, Kota Makassar, Sulawesi Selatan', + 'Latitude' => -5.1477, + 'Longitude' => 119.4327, + 'TanggalMulaiPeriode' => Carbon::now()->addDays(4), + 'TanggalSelesaiPeriode' => Carbon::now()->addDays(19), + 'Status' => 'aktif' + ], + [ + 'NoRegistrasi' => 'REGUKL004567', + 'JenisDokumen' => 'UKL-UPL', + 'NamaKegiatan' => 'Pembangunan Pabrik Pupuk Organik dari Limbah Ternak', + 'BidangUsaha' => 'Industri Pupuk', + 'SkalaBesaran' => '200.00 ton/hari', + 'LokasiKegiatan' => 'Desa Sidomulyo', + 'Kewenangan' => 'Provinsi', + 'Pemrakarsa' => 'PT. PUPUK ORGANIK NUSANTARA', + 'ProvinsiKota' => 'JAWA TIMUR', + 'DeskripsiKegiatan' => 'Pembangunan pabrik pengolahan limbah ternak menjadi pupuk organik granul dengan teknologi composting dan granulasi modern', + 'DampakPotensial' => 'Bau selama proses composting, Lalat dan serangga, Peningkatan nilai tambah limbah ternak, Produksi pupuk ramah lingkungan', + 'DeskripsiLokasi' => 'Desa Sidomulyo, Kecamatan Tempeh, Kabupaten Lumajang, Jawa Timur', + 'Latitude' => -8.1215, + 'Longitude' => 113.2234, + 'TanggalMulaiPeriode' => Carbon::now()->addDays(6), + 'TanggalSelesaiPeriode' => Carbon::now()->addDays(21), + 'Status' => 'aktif' + ] + ]; + + foreach ($kegiatan as $data) { + InformasiKegiatan::create($data); + } + } +} diff --git a/database/seeders/KabupatenTableSeeder.php b/database/seeders/KabupatenTableSeeder.php new file mode 100644 index 0000000..397e4f1 --- /dev/null +++ b/database/seeders/KabupatenTableSeeder.php @@ -0,0 +1,38 @@ +json() as $kabupaten) { + // Convert the id format and map the data + $cleanId = str_replace('.', '', $kabupaten['id']); + + Kabupaten::create([ + 'KabupatenId' => $cleanId, + 'NamaKabupaten' => $kabupaten['data'] + ]); + + // Log data kabupaten yang sedang di-seed + Log::info('Seeding kabupaten: ', [ + 'KabupatenId' => $cleanId, + 'NamaKabupaten' => $kabupaten['data'] + ]); + } + } +} diff --git a/database/seeders/KecamatanTableSeeder.php b/database/seeders/KecamatanTableSeeder.php new file mode 100644 index 0000000..548f80d --- /dev/null +++ b/database/seeders/KecamatanTableSeeder.php @@ -0,0 +1,45 @@ +KabupatenId, 0, 2) . '.' . + substr($kabupaten->KabupatenId, 2, 2); + + $response = Http::get("https://api-wilayah.dinaslhdki.id/api/kecamatan/search?kab={$kabId}"); + + foreach ($response->json() as $kecamatan) { + $cleanId = str_replace('.', '', $kecamatan['id']); + + Kecamatan::create([ + 'KecamatanId' => $cleanId, + 'KabupatenId' => $kabupaten->KabupatenId, + 'NamaKecamatan' => $kecamatan['data'] + ]); + + // Log the seeding process + Log::info('Seeding kecamatan: ', [ + 'KecamatanId' => $cleanId, + 'KabupatenId' => $kabupaten->KabupatenId, + 'NamaKecamatan' => $kecamatan['data'] + ]); + } + } + } +} diff --git a/database/seeders/KelurahanTableSeeder.php b/database/seeders/KelurahanTableSeeder.php new file mode 100644 index 0000000..213b8bc --- /dev/null +++ b/database/seeders/KelurahanTableSeeder.php @@ -0,0 +1,43 @@ +KecamatanId, 0, 2) . '.' . + substr($kecamatan->KecamatanId, 2, 2) . '.' . + substr($kecamatan->KecamatanId, 4, 2); + + $response = Http::get("https://api-wilayah.dinaslhdki.id/api/kelurahan/search?kec={$kecId}"); + + foreach ($response->json() as $kelurahan) { + $cleanId = str_replace('.', '', $kelurahan['id']); + $cleanName = str_replace("\n", ' ', $kelurahan['data']); + + Kelurahan::create([ + 'KelurahanId' => $cleanId, + 'KecamatanId' => $kecamatan->KecamatanId, + 'NamaKelurahan' => $cleanName + ]); + + Log::info('Seeding kelurahan: ', [ + 'KelurahanId' => $cleanId, + 'KecamatanId' => $kecamatan->KecamatanId, + 'NamaKelurahan' => $cleanName + ]); + } + } + } +} diff --git a/database/seeders/PerusahaanSeeder.php b/database/seeders/PerusahaanSeeder.php new file mode 100644 index 0000000..7dd84b0 --- /dev/null +++ b/database/seeders/PerusahaanSeeder.php @@ -0,0 +1,45 @@ + 'PT. Permata Hijau', + 'Alamat' => 'Jl. Merdeka No. 1, Jakarta Pusat', + ], + [ + 'NamaPerusahaan' => 'PT. BERKAH SELAMANYA ENERGI (R4)', + 'Alamat' => 'Jl. Sudirman No. 88, Jakarta Selatan', + ], + [ + 'NamaPerusahaan' => 'PT. FORTUNA MUDA LAGA (FORMULA BAN)', + 'Alamat' => 'Jl. Gatot Subroto No. 12, Jakarta Barat', + ], + [ + 'NamaPerusahaan' => 'PT. MANDIRI KREASI KOLABORASI', + 'Alamat' => 'Jl. Ahmad Yani No. 45, Jakarta Timur', + ], + [ + 'NamaPerusahaan' => 'PT. UNILAB PERDANA', + 'Alamat' => 'Kawasan Industri Cikarang, Bekasi', + ], + ]; + + foreach ($data as $row) { + Perusahaan::updateOrCreate( + ['NamaPerusahaan' => $row['NamaPerusahaan']], + ['Alamat' => $row['Alamat']] + ); + } + } +} diff --git a/database/seeders/RolesAndPermissionsSeeder.php b/database/seeders/RolesAndPermissionsSeeder.php index 7bdc857..2523730 100644 --- a/database/seeders/RolesAndPermissionsSeeder.php +++ b/database/seeders/RolesAndPermissionsSeeder.php @@ -21,6 +21,7 @@ class RolesAndPermissionsSeeder extends Seeder 'dashboard.view.uji_emisi', // Menus + 'pengumuman.access', 'penjadwalan.access', 'persetujuan_teknis.access', 'rincian_teknis.access', @@ -42,6 +43,24 @@ class RolesAndPermissionsSeeder extends Seeder $kadis = Role::firstOrCreate(['name' => 'Kadis']); $ppkl = Role::firstOrCreate(['name' => 'PPKL']); $dlh = Role::firstOrCreate(['name' => 'DLH']); + $perusahaan = Role::firstOrCreate(['name' => 'Perusahaan']); + + // Kadis permissions (semua dashboard tabs + modules, tanpa pengaturan/konten/master data) + $perusahaanPerms = [ + 'dashboard.view.pertek', + 'dashboard.view.rintek', + 'dashboard.view.amdal', + 'dashboard.view.izin_angkut', + 'dashboard.view.uji_emisi', + 'pengumuman.access', + 'penjadwalan.access', + 'persetujuan_teknis.access', + 'rincian_teknis.access', + 'persetujuan_lingkungan.access', + 'izin_angkut_olah.access', + 'izin_tempat_uji_emisi.access', + ]; + $perusahaan->syncPermissions($perusahaanPerms); // Kadis permissions (semua dashboard tabs + modules, tanpa pengaturan/konten/master data) $kadisPerms = [ diff --git a/database/seeders/SaranTanggapanSeeder.php b/database/seeders/SaranTanggapanSeeder.php new file mode 100644 index 0000000..d5578cc --- /dev/null +++ b/database/seeders/SaranTanggapanSeeder.php @@ -0,0 +1,121 @@ +take(3); + + $sampleTanggapan = [ + [ + 'KegiatanID' => $kegiatanIds[0], + 'Nama' => 'Budi Santoso', + 'Peran' => 'masyarakat', + 'NIK' => '3171234567890123', + 'Email' => 'budi.santoso@email.com', + 'NoTelepon' => '081234567890', + 'Gender' => 'laki-laki', + 'KondisiLingkungan' => 'Lingkungan sekitar masih cukup bersih dengan kualitas udara yang baik. Terdapat sungai kecil di dekat area.', + 'NilaiLokal' => 'Area ini adalah tempat bermain anak-anak dan berkumpul warga setiap sore. Memiliki nilai sosial yang tinggi bagi masyarakat.', + 'Kekhawatiran' => 'Khawatir akan dampak terhadap lalu lintas di sekitar area dan kemungkinan polusi suara yang mengganggu.', + 'Harapan' => 'Berharap kegiatan ini dapat memberikan manfaat ekonomi bagi masyarakat sekitar dan tidak merusak lingkungan.', + 'TingkatKekhawatiran' => 3, + 'Status' => 'approved', + 'TanggalDiajukan' => now()->subDays(2), + 'TanggalDiproses' => now()->subDays(1), + 'created_at' => now()->subDays(2), + 'updated_at' => now()->subDays(1), + ], + [ + 'KegiatanID' => $kegiatanIds[0], + 'Nama' => 'Siti Rahayu', + 'Peran' => 'masyarakat', + 'NIK' => '3171234567890124', + 'Email' => 'siti.rahayu@email.com', + 'NoTelepon' => '081234567891', + 'Gender' => 'perempuan', + 'KondisiLingkungan' => 'Kualitas air sungai di sekitar masih bagus dan digunakan warga untuk kebutuhan sehari-hari.', + 'NilaiLokal' => 'Sungai ini merupakan sumber air bersih bagi warga sekitar dan tempat mencuci pakaian.', + 'Kekhawatiran' => 'Sangat khawatir limbah dari kegiatan ini akan mencemari sungai yang menjadi sumber air bagi warga.', + 'Harapan' => 'Berharap ada sistem pengolahan limbah yang baik sehingga tidak mencemari lingkungan.', + 'TingkatKekhawatiran' => 2, + 'Status' => 'approved', + 'TanggalDiajukan' => now()->subDays(1), + 'TanggalDiproses' => now()->subHours(12), + 'created_at' => now()->subDays(1), + 'updated_at' => now()->subHours(12), + ], + [ + 'KegiatanID' => $kegiatanIds[1], + 'Nama' => 'Ahmad Wijaya', + 'Peran' => 'lsm', + 'NIK' => '3171234567890125', + 'Email' => 'ahmad.wijaya@email.com', + 'NoTelepon' => '081234567892', + 'Gender' => 'laki-laki', + 'KondisiLingkungan' => 'Area ini masih alami dengan banyak pohon dan burung. Merupakan habitat beberapa spesies lokal.', + 'NilaiLokal' => 'Tempat ini memiliki nilai konservasi tinggi sebagai habitat satwa lokal dan paru-paru kota.', + 'Kekhawatiran' => 'Khawatir akan hilangnya habitat satwa dan berkurangnya ruang terbuka hijau.', + 'Harapan' => 'Berharap ada kompensasi berupa penanaman pohon di lokasi lain dan program konservasi.', + 'TingkatKekhawatiran' => 2, + 'Status' => 'approved', + 'TanggalDiajukan' => now()->subHours(12), + 'TanggalDiproses' => now()->subHours(6), + 'created_at' => now()->subHours(12), + 'updated_at' => now()->subHours(6), + ], + [ + 'KegiatanID' => $kegiatanIds[1], + 'Nama' => 'Dr. Maria Kusuma', + 'Peran' => 'akademisi', + 'NIK' => '3171234567890126', + 'Email' => 'maria.kusuma@email.com', + 'NoTelepon' => '081234567893', + 'Gender' => 'perempuan', + 'KondisiLingkungan' => 'Dari segi geologis, area ini stabil. Namun perlu perhatian khusus pada drainase.', + 'NilaiLokal' => 'Secara akademis, area ini menarik untuk penelitian tentang urban development.', + 'Kekhawatiran' => 'Khawatir tentang dampak jangka panjang terhadap sistem drainase dan kemungkinan banjir.', + 'Harapan' => 'Berharap ada studi dampak lingkungan yang komprehensif dan sistem mitigasi yang tepat.', + 'TingkatKekhawatiran' => 3, + 'Status' => 'approved', + 'TanggalDiajukan' => now()->subHours(6), + 'TanggalDiproses' => now()->subHours(3), + 'created_at' => now()->subHours(6), + 'updated_at' => now()->subHours(3), + ], + [ + 'KegiatanID' => $kegiatanIds[2], + 'Nama' => 'Rudi Hartono', + 'Peran' => 'masyarakat', + 'NIK' => '3171234567890127', + 'Email' => 'rudi.hartono@email.com', + 'NoTelepon' => '081234567894', + 'Gender' => 'laki-laki', + 'KondisiLingkungan' => 'Lingkungan cukup padat penduduk dengan aktivitas ekonomi yang tinggi.', + 'NilaiLokal' => 'Area ini adalah pusat ekonomi lokal dengan banyak warung dan toko kecil.', + 'Kekhawatiran' => 'Khawatir akan gangguan terhadap aktivitas ekonomi masyarakat selama pembangunan.', + 'Harapan' => 'Berharap ada kompensasi ekonomi dan lapangan kerja bagi masyarakat yang terdampak.', + 'TingkatKekhawatiran' => 4, + 'Status' => 'approved', + 'TanggalDiajukan' => now()->subHours(3), + 'TanggalDiproses' => now()->subHours(1), + 'created_at' => now()->subHours(3), + 'updated_at' => now()->subHours(1), + ], + ]; + + foreach ($sampleTanggapan as $tanggapan) { + SaranTanggapan::create($tanggapan); + } + } +} diff --git a/database/seeders/UserSeeder.php b/database/seeders/UserSeeder.php index e776cd9..7664825 100644 --- a/database/seeders/UserSeeder.php +++ b/database/seeders/UserSeeder.php @@ -33,6 +33,17 @@ class UserSeeder extends Seeder ); $kadis->syncRoles(['Kadis']); + // Perusahaan user + $perusahaan = User::updateOrCreate( + ['email' => 'perusahaan@dinaslhdki.id'], + [ + 'name' => 'Perusahaan', + 'username' => 'perusahaan', + 'password' => Hash::make('Perling2025$'), + ] + ); + $perusahaan->syncRoles(['Perusahaan']); + // PPKL user (note: domain as provided) $ppkl = User::updateOrCreate( ['email' => 'ppkl@dinaslhkdki.id'], diff --git a/docs/registration-number-format.md b/docs/registration-number-format.md new file mode 100644 index 0000000..6998584 --- /dev/null +++ b/docs/registration-number-format.md @@ -0,0 +1,53 @@ +# Registration Number Format + +## Secure Registration Number Generation + +The system automatically generates a unique 12-character registration number for each announcement with enhanced security to prevent easy guessing. + +### Format Structure: + +``` +ENV + XXXXXXXXX +``` + +Where: + +- **ENV** = Environmental document prefix (3 characters) +- **XXXXXXXXX** = 9 secure random alphanumeric characters (excluding similar-looking characters) + +### Character Pool: + +- Uses: `23456789ABCDEFGHJKLMNPQRSTUVWXYZ` +- Excludes: `0, 1, I, O` (to prevent confusion) +- No lowercase letters (for consistency) + +### Examples: + +- **ENV4B7X9K2MQ** - Secure random generation +- **ENVH3K8P5W7R** - Another secure random generation +- **ENV9F2N6Y4MP** - Each number is unpredictable + +### Security Features: + +1. **Cryptographic Hash** - Uses SHA-256 for randomness +2. **Timestamp Seed** - Includes microtime for uniqueness +3. **Unpredictable** - No date/time patterns visible +4. **No Sequential** - Cannot guess next number +5. **Character Filtering** - Excludes confusing characters + +### Technical Implementation: + +- Generated using cryptographic hash functions +- Seeds include timestamp + microtime + random integers +- Character pool designed to avoid visual confusion +- Multiple entropy sources for maximum security +- Still maintains 12-character length requirement + +### Usage: + +1. Number is automatically generated when form is submitted +2. Field is completely hidden from users (no manual interaction) +3. System validates uniqueness before saving to database +4. Success message shows the final registration number +5. Each generation is completely unpredictable and secure +6. No user interface for regeneration (fully automated) diff --git a/public/assets/images/auth/auth_img.png b/public/assets/images/auth/auth_img.png new file mode 100644 index 0000000..34ef962 Binary files /dev/null and b/public/assets/images/auth/auth_img.png differ diff --git a/public/assets/js/backend/jadwal/datepickr.js b/public/assets/js/backend/jadwal/datepickr.js index 0909a93..5105cd0 100644 --- a/public/assets/js/backend/jadwal/datepickr.js +++ b/public/assets/js/backend/jadwal/datepickr.js @@ -1,9 +1,17 @@ -function getDatePicker(receiveID) { - flatpickr(receiveID, { - enableTime: true, - dateFormat: "d/m/Y H:i", - }); +function getDatePicker(selector) { + try { + flatpickr(selector, { + enableTime: true, + dateFormat: "d/m/Y H:i", + time_24hr: true, + minDate: "today", + disable: Array.isArray(window.disabledJadwalRanges) ? window.disabledJadwalRanges : [], + }); + } catch (e) { + // no-op + } } + getDatePicker("#startDate"); getDatePicker("#endDate"); diff --git a/public/assets/js/frontend/home/layanan.js b/public/assets/js/frontend/home/layanan.js index b361b42..b277ed7 100644 --- a/public/assets/js/frontend/home/layanan.js +++ b/public/assets/js/frontend/home/layanan.js @@ -1,8 +1,10 @@ -// Fetch Wilayah JSON -fetch("/assets/json/home/wilayah.json") +// Fetch Wilayah dari API Database +fetch("/api/layanan/wilayah") .then((response) => response.json()) .then((data) => populateFilters(data)) - .catch((error) => console.error("Gagal load wilayah.json:", error)); + .catch((error) => + console.error("Gagal load wilayah dari database:", error) + ); function populateFilters(data) { populateSelect("kabkota", data.kabkota); @@ -14,31 +16,45 @@ function populateSelect(id, options) { const select = document.getElementById(id); if (!select) return; - select.innerHTML = options - .map( - (opt) => ` + // Add default option + let defaultOption = + '"; + + select.innerHTML = + defaultOption + + options + .map( + (opt) => ` ` - ) - .join(""); + ) + .join(""); } -// Fetch Layanan JSON +// Fetch Layanan dari API Database document.addEventListener("DOMContentLoaded", function () { - fetch("/assets/json/home/layanan.json") + fetch("/api/layanan/data") .then((response) => response.json()) .then((data) => { initializeLayanan(data); setupUIHandlers(); }) - .catch((error) => console.error("Gagal load layanan.json:", error)); + .catch((error) => + console.error("Gagal load layanan dari database:", error) + ); }); function initializeLayanan(data) { renderHeader(data.header); renderTabs(data.tabs); renderContents(data.contents); - changeTab("pertek"); + changeTab("amdal"); } // Data Header @@ -62,7 +78,7 @@ function renderTabs(tabs) { (tab) => ` ` @@ -86,7 +102,9 @@ function renderContents(contents) { } else { return `
`; } @@ -94,7 +112,7 @@ function renderContents(contents) { .join(""); } -function renderStandardItem(item) { +function renderStandardItem(item, index) { return `${item.description}
- Baca selengkapnya ... +${item.description}
+Peta Lokasi Kegiatan
+ ${ + data.latitude && data.longitude + ? `${data.latitude}, ${data.longitude}
` + : "" + } +|
-
-
-
-
-
- S.L
- |
- Join Date | -Name | -Department | -Designation | -Status | -Action | -|
|---|---|---|---|---|---|---|---|
|
-
-
-
-
-
- 01
- |
- 25 Jan 2024 | -
-
-
-
-
- Kathryn Murphy
-
- |
- osgoodwy@gmail.com | -HR | -Manager | -- Active - | -
-
-
-
-
-
- |
-
|
-
-
-
-
-
- 02
- |
- 25 Jan 2024 | -
-
-
-
-
- Annette Black
-
- |
- redaniel@gmail.com | -Design | -UI UX Designer | -- Inactive - | -
-
-
-
-
-
- |
-
|
-
-
-
-
-
- 03
- |
- 10 Feb 2024 | -
-
-
-
-
- Ronald Richards
-
- |
- seannand@mail.ru | -Design | -UI UX Designer | -- Active - | -
-
-
-
-
-
- |
-
|
-
-
-
-
-
- 04
- |
- 10 Feb 2024 | -
-
-
-
-
- Eleanor Pena
-
- |
- miyokoto@mail.ru | -Design | -UI UX Designer | -- Active - | -
-
-
-
-
-
- |
-
|
-
-
-
-
-
- 05
- |
- 15 March 2024 | -
-
-
-
-
- Leslie Alexander
-
- |
- icadahli@gmail.com | -Design | -UI UX Designer | -- Inactive - | -
-
-
-
-
-
- |
-
|
-
-
-
-
-
- 06
- |
- 15 March 2024 | -
-
-
-
-
- Albert Flores
-
- |
- warn@mail.ru | -Design | -UI UX Designer | -- Active - | -
-
-
-
-
-
- |
-
|
-
-
-
-
-
- 07
- |
- 27 April 2024 | -
-
-
-
-
- Jacob Jones
-
- |
- zitka@mail.ru | -Development | -Frontend developer | -- Active - | -
-
-
-
-
-
- |
-
|
-
-
-
-
-
- 08
- |
- 25 Jan 2024 | -
-
-
-
-
- Jerome Bell
-
- |
- igerrin@gmail.com | -Development | -Frontend developer | -- Inactive - | -
-
-
-
-
-
- |
-
|
-
-
-
-
-
- 09
- |
- 30 April 2024 | -
-
-
-
-
- Marvin McKinney
-
- |
- maka@yandex.ru | -Development | -Frontend developer | -- Active - | -
-
-
-
-
-
- |
-
|
-
-
-
-
-
- 10
- |
- 30 April 2024 | -
-
-
-
-
- Cameron Williamson
-
- |
- danten@mail.ru | -Development | -Frontend developer | -- Active - | -
-
-
-
-
-
- |
-
+