diff --git a/.config/psysh/psysh_history b/.config/psysh/psysh_history index f84a832..d3dc184 100644 --- a/.config/psysh/psysh_history +++ b/.config/psysh/psysh_history @@ -9,3 +9,8 @@ exit (new App\Jobs\ScrapingDataJob())->handle(); (new App\Jobs\ScrapingDataJob())->handle(); exit +exit +BigdataResume::generateResumeData(253,2025,"simbg"); +exit +BigdataResume::generateResumeData(253,2025,"simbg"); +exit diff --git a/.npm/_logs/2025-08-19T05_29_08_290Z-debug-0.log b/.npm/_logs/2025-08-19T05_29_08_290Z-debug-0.log new file mode 100644 index 0000000..d28e202 --- /dev/null +++ b/.npm/_logs/2025-08-19T05_29_08_290Z-debug-0.log @@ -0,0 +1,17 @@ +0 verbose cli /usr/bin/node /usr/bin/npm +1 info using npm@10.8.2 +2 info using node@v18.20.8 +3 silly config load:file:/usr/lib/node_modules/npm/npmrc +4 silly config load:file:/var/www/.npmrc +5 silly config load:file:/usr/etc/npmrc +6 verbose title npm run build +7 verbose argv "run" "build" +8 verbose logfile logs-max:10 dir:/var/www/.npm/_logs/2025-08-19T05_29_08_290Z- +9 verbose logfile /var/www/.npm/_logs/2025-08-19T05_29_08_290Z-debug-0.log +10 silly logfile done cleaning log files +11 verbose cwd /var/www +12 verbose os Linux 6.6.87.2-microsoft-standard-WSL2 +13 verbose node v18.20.8 +14 verbose npm v10.8.2 +15 verbose exit 0 +16 info ok diff --git a/app/Http/Controllers/Api/BigDataResumeController.php b/app/Http/Controllers/Api/BigDataResumeController.php index c932e30..79a91f6 100644 --- a/app/Http/Controllers/Api/BigDataResumeController.php +++ b/app/Http/Controllers/Api/BigDataResumeController.php @@ -42,11 +42,11 @@ class BigDataResumeController extends Controller } $data_settings = DataSetting::all(); - if($data_settings->isEmpty()){ - return response()->json(['message' => 'No data setting found']); + $target_pad = 0; + if($data_settings->where('key', 'TARGET_PAD')->first()){ + $target_pad = floatval($data_settings->where('key', 'TARGET_PAD')->first()->value ?? 0); } - $target_pad = floatval(optional($data_settings->where('key', 'TARGET_PAD')->first())->value); $realisasi_terbit_pbg_sum = $big_data_resume->issuance_realization_pbg_sum; $realisasi_terbit_pbg_count = $big_data_resume->issuance_realization_pbg_count; $menunggu_klik_dpmptsp_sum = $big_data_resume->waiting_click_dpmptsp_sum; @@ -107,7 +107,7 @@ class BigDataResumeController extends Controller $business_krk_count = $big_data_resume->business_krk_count; $non_business_rab_count = $big_data_resume->non_business_rab_count; $non_business_krk_count = $big_data_resume->non_business_krk_count; - $non_business_dlh_count = $big_data_resume->non_business_dlh_count; + $business_dlh_count = $big_data_resume->business_dlh_count; $result = [ 'target_pad' => [ @@ -167,7 +167,7 @@ class BigDataResumeController extends Controller 'business_krk_count' => $business_krk_count, 'non_business_rab_count' => $non_business_rab_count, 'non_business_krk_count' => $non_business_krk_count, - 'non_business_dlh_count' => $non_business_dlh_count + 'business_dlh_count' => $business_dlh_count ]; return response()->json($result); }catch(\Exception $e){ @@ -336,9 +336,15 @@ class BigDataResumeController extends Controller return $pdf->download('laporan-pimpinan.pdf'); } private function response_empty_resume(){ + $data_settings = DataSetting::all(); + $target_pad = 0; + if($data_settings->where('key', 'TARGET_PAD')->first()){ + $target_pad = floatval($data_settings->where('key', 'TARGET_PAD')->first()->value ?? 0); + } + $result = [ 'target_pad' => [ - 'sum' => 0, + 'sum' => $target_pad, 'percentage' => 100, ], 'tata_ruang' => [ diff --git a/app/Http/Controllers/Api/PbgTaskController.php b/app/Http/Controllers/Api/PbgTaskController.php index 2ac3132..7b0a9ce 100644 --- a/app/Http/Controllers/Api/PbgTaskController.php +++ b/app/Http/Controllers/Api/PbgTaskController.php @@ -132,39 +132,48 @@ class PbgTaskController extends Controller } $validated = $request->validate([ - 'name' => 'required|string|max:255', - 'owner_name' => 'required|string|max:255', + 'name' => 'nullable|string|max:255', + 'owner_name' => 'nullable|string|max:255', 'application_type' => ['nullable', new Enum(PbgTaskApplicationTypes::class)], - 'condition' => 'required|string|max:255', - 'registration_number' => 'required|string|max:255', - 'document_number' => 'required|string|max:255', + 'condition' => 'nullable|string|max:255', + 'registration_number' => 'nullable|string|max:255', + 'document_number' => 'nullable|string|max:255', 'status' => ['nullable', new Enum(PbgTaskStatus::class)], - 'address' => 'required|string|max:255', + 'address' => 'nullable|string|max:255', 'slf_status_name' => 'nullable|string|max:255', - 'function_type' => 'required|string|max:255', - 'consultation_type' => 'required|string|max:255', - 'due_date' => 'nullable|date|after_or_equal:today', + 'function_type' => 'nullable|string|max:255', + 'consultation_type' => 'nullable|string|max:255', + 'due_date' => 'nullable|date', + 'is_valid' => 'nullable|boolean', ]); $statusLabel = $validated['status'] !== null ? PbgTaskStatus::getLabel($validated['status']) : null; $applicationLabel = $validated['application_type'] !== null ? PbgTaskApplicationTypes::getLabel($validated['application_type']) : null; - $pbg_task->update([ - 'name' => $validated['name'], - 'owner_name' => $validated['owner_name'], - 'application_type' => $validated['application_type'], - 'application_type_name' => $applicationLabel, // Automatically set application_type_name - 'condition' => $validated['condition'], - 'registration_number' => $validated['registration_number'], - 'document_number' => $validated['document_number'], - 'status' => $validated['status'], - 'status_name' => $statusLabel, // Automatically set status_name - 'address' => $validated['address'], - 'slf_status_name' => $validated['slf_status_name'], - 'function_type' => $validated['function_type'], - 'consultation_type' => $validated['consultation_type'], - 'due_date' => $validated['due_date'], - ]); + // Prepare update data - only include fields that are actually provided + $updateData = []; + + foreach ($validated as $key => $value) { + if ($value !== null || $request->has($key)) { + $updateData[$key] = $value; + } + } + + // Handle special cases for labels + if (isset($updateData['status'])) { + $updateData['status_name'] = $statusLabel; + } + + if (isset($updateData['application_type'])) { + $updateData['application_type_name'] = $applicationLabel; + } + + // Handle is_valid specifically + if ($request->has('is_valid')) { + $updateData['is_valid'] = $validated['is_valid']; + } + + $pbg_task->update($updateData); return response()->json([ "success"=> true, "message"=> "Data berhasil diubah", diff --git a/app/Models/BigdataResume.php b/app/Models/BigdataResume.php index 880fb20..965937b 100644 --- a/app/Models/BigdataResume.php +++ b/app/Models/BigdataResume.php @@ -34,9 +34,9 @@ class BigdataResume extends Model 'process_in_technical_office_sum', 'business_rab_count', 'business_krk_count', + 'business_dlh_count', 'non_business_rab_count', 'non_business_krk_count', - 'non_business_dlh_count', 'resume_type', ]; @@ -155,6 +155,41 @@ class BigdataResume extends Model ') ->value('total_count') ?? 0; + // Non-Business DLH count - for each non-business task with data_type=5: + // if any status != 1 then return 1, if all status = 1 then return 0, then sum all + $business_dlh_count = DB::table('pbg_task') + ->where(function ($q) { + $q->where(function ($q2) { + $q2->where(function ($q3) { + $q3->whereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%fungsi usaha%']) + ->whereRaw("LOWER(TRIM(function_type)) LIKE ?", ['%sebagai tempat usaha%']); + }) + ->orWhereNull('function_type'); + }) + ->whereIn("status", PbgTaskStatus::getNonVerified()); + }) + ->whereYear('task_created_at', $year) + ->whereExists(function ($query) { + $query->select(DB::raw(1)) + ->from('pbg_task_detail_data_lists') + ->whereColumn('pbg_task_detail_data_lists.pbg_task_uuid', 'pbg_task.uuid') + ->where('pbg_task_detail_data_lists.data_type', 5); + }) + ->selectRaw(' + SUM( + CASE + WHEN EXISTS ( + SELECT 1 FROM pbg_task_detail_data_lists ptddl + WHERE ptddl.pbg_task_uuid = pbg_task.uuid + AND ptddl.data_type = 5 + AND ptddl.status != 1 + ) THEN 1 + ELSE 0 + END + ) as total_count + ') + ->value('total_count') ?? 0; + // Non-Business RAB count - for each non-business task with data_type=3: // if any status != 1 then return 1, if all status = 1 then return 0, then sum all $non_business_rab_count = DB::table('pbg_task') @@ -225,60 +260,54 @@ class BigdataResume extends Model ') ->value('total_count') ?? 0; - // Non-Business DLH count - for each non-business task with data_type=5: - // if any status != 1 then return 1, if all status = 1 then return 0, then sum all - $non_business_dlh_count = DB::table('pbg_task') - ->where(function ($q) { - $q->where(function ($q2) { - $q2->where(function ($q3) { - $q3->whereRaw("LOWER(TRIM(function_type)) NOT LIKE ?", ['%fungsi usaha%']) - ->whereRaw("LOWER(TRIM(function_type)) NOT LIKE ?", ['%sebagai tempat usaha%']); - }) - ->orWhereNull('function_type'); - }) - ->whereIn("status", PbgTaskStatus::getNonVerified()); - }) + + + // Debug: Check if there are non-verified tasks and their retribution data + $debug_non_verified = PbgTask::whereIn('status', PbgTaskStatus::getNonVerified()) ->whereYear('task_created_at', $year) - ->whereExists(function ($query) { - $query->select(DB::raw(1)) - ->from('pbg_task_detail_data_lists') - ->whereColumn('pbg_task_detail_data_lists.pbg_task_uuid', 'pbg_task.uuid') - ->where('pbg_task_detail_data_lists.data_type', 5); - }) - ->selectRaw(' - SUM( - CASE - WHEN EXISTS ( - SELECT 1 FROM pbg_task_detail_data_lists ptddl - WHERE ptddl.pbg_task_uuid = pbg_task.uuid - AND ptddl.data_type = 5 - AND ptddl.status != 1 - ) THEN 1 - ELSE 0 - END - ) as total_count - ') - ->value('total_count') ?? 0; + ->with('pbg_task_retributions') + ->get(); + + \Log::info('Non-verified tasks debug', [ + 'year' => $year, + 'non_verified_statuses' => PbgTaskStatus::getNonVerified(), + 'tasks_count' => $debug_non_verified->count(), + 'tasks_with_retribution' => $debug_non_verified->filter(fn($task) => $task->pbg_task_retributions)->count(), + 'sample_retribution_values' => $debug_non_verified->take(3)->map(fn($task) => [ + 'uuid' => $task->uuid, + 'status' => $task->status, + 'has_retribution' => !is_null($task->pbg_task_retributions), + 'retribution_value' => $task->pbg_task_retributions?->nilai_retribusi_bangunan ?? 'NULL' + ]) + ]); // Get sum values using proper aggregation to handle multiple retributions $stats = PbgTask::leftJoin('pbg_task_retributions as ptr', 'pbg_task.uuid', '=', 'ptr.pbg_task_uid') ->whereYear('pbg_task.task_created_at', $year) ->selectRaw(" - SUM(CASE WHEN pbg_task.status in (".implode(',', PbgTaskStatus::getVerified()).") THEN ptr.nilai_retribusi_bangunan ELSE 0 END) AS verified_total, - SUM(CASE WHEN pbg_task.status in (".implode(',', PbgTaskStatus::getNonVerified()).") THEN ptr.nilai_retribusi_bangunan ELSE 0 END) AS non_verified_total, + SUM(CASE WHEN pbg_task.status in (".implode(',', PbgTaskStatus::getVerified()).") THEN COALESCE(ptr.nilai_retribusi_bangunan, 0) ELSE 0 END) AS verified_total, + SUM(CASE WHEN pbg_task.status in (".implode(',', PbgTaskStatus::getNonVerified()).") THEN COALESCE(ptr.nilai_retribusi_bangunan, 0) ELSE 0 END) AS non_verified_total, SUM(CASE WHEN (LOWER(TRIM(pbg_task.function_type)) LIKE '%fungsi usaha%' OR LOWER(TRIM(pbg_task.function_type)) LIKE '%sebagai tempat usaha%') - AND pbg_task.status in (".implode(',', PbgTaskStatus::getNonVerified()).") THEN ptr.nilai_retribusi_bangunan ELSE 0 END) AS business_total, + AND pbg_task.status in (".implode(',', PbgTaskStatus::getNonVerified()).") THEN COALESCE(ptr.nilai_retribusi_bangunan, 0) ELSE 0 END) AS business_total, SUM(CASE WHEN (LOWER(TRIM(pbg_task.function_type)) NOT LIKE '%fungsi usaha%' AND LOWER(TRIM(pbg_task.function_type)) NOT LIKE '%sebagai tempat usaha%' AND pbg_task.status in (".implode(',', PbgTaskStatus::getNonVerified()).")) - OR (pbg_task.function_type IS NULL AND pbg_task.status in (".implode(',', PbgTaskStatus::getNonVerified()).")) THEN ptr.nilai_retribusi_bangunan ELSE 0 END) AS non_business_total, - SUM(CASE WHEN pbg_task.status in (".implode(',', PbgTaskStatus::getWaitingClickDpmptsp()).") THEN ptr.nilai_retribusi_bangunan ELSE 0 END) AS waiting_click_dpmptsp_total, - SUM(CASE WHEN pbg_task.status in (".implode(',', PbgTaskStatus::getIssuanceRealizationPbg()).") THEN ptr.nilai_retribusi_bangunan ELSE 0 END) AS issuance_realization_pbg_total, - SUM(CASE WHEN pbg_task.status in (".implode(',', PbgTaskStatus::getProcessInTechnicalOffice()).") THEN ptr.nilai_retribusi_bangunan ELSE 0 END) AS process_in_technical_office_total, - SUM(CASE WHEN pbg_task.status in (".implode(',', PbgTaskStatus::getPotention()).") THEN ptr.nilai_retribusi_bangunan ELSE 0 END) AS potention_total + OR (pbg_task.function_type IS NULL AND pbg_task.status in (".implode(',', PbgTaskStatus::getNonVerified()).")) THEN COALESCE(ptr.nilai_retribusi_bangunan, 0) ELSE 0 END) AS non_business_total, + SUM(CASE WHEN pbg_task.status in (".implode(',', PbgTaskStatus::getWaitingClickDpmptsp()).") THEN COALESCE(ptr.nilai_retribusi_bangunan, 0) ELSE 0 END) AS waiting_click_dpmptsp_total, + SUM(CASE WHEN pbg_task.status in (".implode(',', PbgTaskStatus::getIssuanceRealizationPbg()).") THEN COALESCE(ptr.nilai_retribusi_bangunan, 0) ELSE 0 END) AS issuance_realization_pbg_total, + SUM(CASE WHEN pbg_task.status in (".implode(',', PbgTaskStatus::getProcessInTechnicalOffice()).") THEN COALESCE(ptr.nilai_retribusi_bangunan, 0) ELSE 0 END) AS process_in_technical_office_total, + SUM(CASE WHEN pbg_task.status in (".implode(',', PbgTaskStatus::getPotention()).") THEN COALESCE(ptr.nilai_retribusi_bangunan, 0) ELSE 0 END) AS potention_total, + COUNT(CASE WHEN pbg_task.status in (".implode(',', PbgTaskStatus::getNonVerified()).") THEN 1 END) AS non_verified_tasks_count, + COUNT(CASE WHEN pbg_task.status in (".implode(',', PbgTaskStatus::getNonVerified()).") AND ptr.nilai_retribusi_bangunan IS NOT NULL THEN 1 END) AS non_verified_with_retribution_count ") ->first(); + + \Log::info('Stats calculation result', [ + 'non_verified_total' => $stats->non_verified_total ?? 'NULL', + 'non_verified_tasks_count' => $stats->non_verified_tasks_count ?? 'NULL', + 'non_verified_with_retribution_count' => $stats->non_verified_with_retribution_count ?? 'NULL' + ]); $service_google_sheet = app(ServiceGoogleSheet::class); @@ -305,9 +334,9 @@ class BigdataResume extends Model 'process_in_technical_office_sum' => $stats->process_in_technical_office_total ?? 0.00, 'business_rab_count' => $business_rab_count, 'business_krk_count' => $business_krk_count, + 'business_dlh_count' => $business_dlh_count, 'non_business_rab_count' => $non_business_rab_count, 'non_business_krk_count' => $non_business_krk_count, - 'non_business_dlh_count' => $non_business_dlh_count, 'resume_type' => $resume_type, ]); } diff --git a/app/Models/PbgTask.php b/app/Models/PbgTask.php index 59c8900..8c9eb43 100644 --- a/app/Models/PbgTask.php +++ b/app/Models/PbgTask.php @@ -27,7 +27,8 @@ class PbgTask extends Model 'consultation_type', 'due_date', 'land_certificate_phase', - 'task_created_at' + 'task_created_at', + 'is_valid' ]; public function pbg_task_retributions(){ diff --git a/app/Models/PbgTaskDetail.php b/app/Models/PbgTaskDetail.php index c3a3500..ad38e3d 100644 --- a/app/Models/PbgTaskDetail.php +++ b/app/Models/PbgTaskDetail.php @@ -143,54 +143,208 @@ class PbgTaskDetail extends Model + /** + * Helper method to clean and convert latitude/longitude values + */ + private static function cleanCoordinate($value): ?float + { + if ($value === null || $value === '' || $value === '?' || $value === '-') { + return null; + } + + // Convert to string and trim whitespace + $stringValue = trim((string) $value); + + // Check for common invalid values + if (in_array($stringValue, ['', '?', '-', 'null', 'NULL', 'N/A', '0,'], true)) { + return null; + } + + // Remove degree symbol and other non-numeric characters except minus and decimal point + $cleaned = preg_replace('/[^\d.-]/', '', $stringValue); + + // Check if cleaned value is empty or just a hyphen + if ($cleaned === '' || $cleaned === '-' || $cleaned === '.') { + return null; + } + + // Validate if it's a valid number and within reasonable coordinate bounds + if (is_numeric($cleaned)) { + $coordinate = (float) $cleaned; + + // Basic validation for reasonable coordinate ranges + // Latitude: -90 to 90, Longitude: -180 to 180 + if ($coordinate >= -180 && $coordinate <= 180) { + return $coordinate; + } + } + + return null; + } + + /** + * Helper method to clean and convert integer values + */ + private static function cleanIntegerValue($value): int + { + if ($value === null || $value === '' || $value === '?') { + return 0; + } + + // Convert to string and trim whitespace + $stringValue = trim((string) $value); + + // Check for common invalid values + if (in_array($stringValue, ['', '?', '-', 'null', 'NULL', 'N/A'], true)) { + return 0; + } + + // Remove any non-numeric characters except minus + $cleaned = preg_replace('/[^\d-]/', '', $stringValue); + + // Check if cleaned value is empty or just invalid characters + if ($cleaned === '' || $cleaned === '-') { + return 0; + } + + // Validate if it's a valid number + if (is_numeric($cleaned)) { + return (int) $cleaned; + } + + return 0; + } + + /** + * Helper method to clean and convert numeric values + */ + private static function cleanNumericValue($value, bool $nullable = false): ?float + { + if ($value === null || $value === '' || $value === '?') { + return $nullable ? null : 0; + } + + // Convert to string and trim whitespace + $stringValue = trim((string) $value); + + // Check for common invalid values + if (in_array($stringValue, ['', '?', '-', 'null', 'NULL', 'N/A'], true)) { + return $nullable ? null : 0; + } + + // Remove any non-numeric characters except minus and decimal point + $cleaned = preg_replace('/[^\d.-]/', '', $stringValue); + + // Check if cleaned value is empty or just invalid characters + if ($cleaned === '' || $cleaned === '-' || $cleaned === '.') { + return $nullable ? null : 0; + } + + // Validate if it's a valid number + if (is_numeric($cleaned)) { + return (float) $cleaned; + } + + return $nullable ? null : 0; + } + + /** + * Helper method to handle date parsing with fallback + */ + private static function parseDate($date): ?string + { + if (!$date || $date === '?' || $date === 'null') { + return null; + } + + try { + return Carbon::parse($date)->format('Y-m-d'); + } catch (\Exception $e) { + return null; + } + } + + /** + * Helper method to handle datetime parsing with fallback + */ + private static function parseDateTime($datetime): ?string + { + if (!$datetime || $datetime === '?' || $datetime === 'null') { + return null; + } + + try { + return Carbon::parse($datetime)->format('Y-m-d H:i:s'); + } catch (\Exception $e) { + return null; + } + } + /** * Create or update PbgTaskDetail from API response */ public static function createFromApiResponse(array $data, string $pbgTaskUuid): self { $detailData = [ + // Foreign key relationship - string, required 'pbg_task_uid' => $pbgTaskUuid, - 'uid' => $data['uid'] ?? null, - 'nik' => $data['nik'] ?? null, - 'type_card' => $data['type_card'] ?? null, - 'ownership' => $data['ownership'] ?? null, - 'owner_name' => $data['owner_name'] ?? null, - 'ward_id' => $data['ward_id'] ?? null, - 'ward_name' => $data['ward_name'] ?? null, - 'district_id' => $data['district_id'] ?? null, - 'district_name' => $data['district_name'] ?? null, - 'regency_id' => $data['regency_id'] ?? null, - 'regency_name' => $data['regency_name'] ?? null, - 'province_id' => $data['province_id'] ?? null, - 'province_name' => $data['province_name'] ?? null, - 'address' => $data['address'] ?? null, - 'owner_email' => $data['owner_email'] ?? null, - 'owner_phone' => $data['owner_phone'] ?? null, - 'user' => $data['user'] ?? null, - 'name' => $data['name'] ?? null, - 'email' => $data['email'] ?? null, - 'phone' => $data['phone'] ?? null, - 'user_nik' => $data['user_nik'] ?? null, - 'user_province_id' => $data['user_province_id'] ?? null, - 'user_province_name' => $data['user_province_name'] ?? null, - 'user_regency_id' => $data['user_regency_id'] ?? null, - 'user_regency_name' => $data['user_regency_name'] ?? null, - 'user_district_id' => $data['user_district_id'] ?? null, - 'user_district_name' => $data['user_district_name'] ?? null, - 'user_address' => $data['user_address'] ?? null, - 'status' => $data['status'] ?? null, - 'status_name' => $data['status_name'] ?? null, - 'slf_status' => $data['slf_status'] ?? null, - 'slf_status_name' => $data['slf_status_name'] ?? null, - 'sppst_status' => $data['sppst_status'] ?? null, - 'sppst_file' => $data['sppst_file'] ?? null, - 'sppst_status_name' => $data['sppst_status_name'] ?? null, - 'file_pbg' => $data['file_pbg'] ?? null, - 'file_pbg_date' => isset($data['file_pbg_date']) ? Carbon::parse($data['file_pbg_date'])->format('Y-m-d') : null, - 'due_date' => isset($data['due_date']) ? Carbon::parse($data['due_date'])->format('Y-m-d') : null, - 'start_date' => isset($data['start_date']) ? Carbon::parse($data['start_date'])->format('Y-m-d') : null, - 'document_number' => $data['document_number'] ?? null, - 'registration_number' => $data['registration_number'] ?? null, + + // Basic information + 'uid' => $data['uid'] ?? "N/A", // string, unique, required + 'nik' => isset($data['nik']) && $data['nik'] !== '' && $data['nik'] !== '?' ? $data['nik'] : null, // string, nullable + 'type_card' => isset($data['type_card']) && $data['type_card'] !== '' && $data['type_card'] !== '?' ? $data['type_card'] : null, // string, nullable + 'ownership' => $data['ownership'] ?? null, // string, nullable + 'owner_name' => $data['owner_name'] ?? "N/A", // string, required + + // Owner location information - all required + 'ward_id' => self::cleanIntegerValue($data['ward_id'] ?? 0), // bigInteger, required + 'ward_name' => $data['ward_name'] ?? "N/A", // string, required + 'district_id' => self::cleanIntegerValue($data['district_id'] ?? 0), // integer, required + 'district_name' => $data['district_name'] ?? "N/A", // string, required + 'regency_id' => self::cleanIntegerValue($data['regency_id'] ?? 0), // integer, required + 'regency_name' => $data['regency_name'] ?? "N/A", // string, required + 'province_id' => self::cleanIntegerValue($data['province_id'] ?? 0), // integer, required + 'province_name' => $data['province_name'] ?? "N/A", // string, required + 'address' => $data['address'] ?? "N/A", // text, required + + // Owner contact information - required + 'owner_email' => $data['owner_email'] ?? "N/A", // string, required + 'owner_phone' => $data['owner_phone'] ?? "N/A", // string, required + + // User information - all required + 'user' => self::cleanIntegerValue($data['user'] ?? 0), // integer, required + 'name' => $data['name'] ?? "N/A", // string, required + 'email' => $data['email'] ?? "N/A", // string, required + 'phone' => $data['phone'] ?? "N/A", // string, required + 'user_nik' => $data['user_nik'] ?? "N/A", // string, required + + // User location information - all required + 'user_province_id' => self::cleanIntegerValue($data['user_province_id'] ?? 0), // integer, required + 'user_province_name' => $data['user_province_name'] ?? "N/A", // string, required + 'user_regency_id' => self::cleanIntegerValue($data['user_regency_id'] ?? 0), // integer, required + 'user_regency_name' => $data['user_regency_name'] ?? "N/A", // string, required + 'user_district_id' => self::cleanIntegerValue($data['user_district_id'] ?? 0), // integer, required + 'user_district_name' => $data['user_district_name'] ?? "N/A", // string, required + 'user_address' => $data['user_address'] ?? "N/A", // text, required + + // Status information + 'status' => self::cleanIntegerValue($data['status'] ?? 0), // integer, required + 'status_name' => $data['status_name'] ?? "N/A", // string, required + 'slf_status' => isset($data['slf_status']) && is_numeric($data['slf_status']) ? (int) $data['slf_status'] : null, // integer, nullable + 'slf_status_name' => $data['slf_status_name'] ?? null, // string, nullable + 'sppst_status' => self::cleanIntegerValue($data['sppst_status'] ?? 0), // integer, required + 'sppst_file' => $data['sppst_file'] ?? null, // string, nullable + 'sppst_status_name' => $data['sppst_status_name'] ?? "N/A", // string, required + + // Files and documents + 'file_pbg' => $data['file_pbg'] ?? null, // string, nullable + 'file_pbg_date' => self::parseDate($data['file_pbg_date'] ?? null), // date, nullable + 'due_date' => self::parseDate($data['due_date'] ?? null), // date, nullable + 'start_date' => self::parseDate($data['start_date'] ?? null) ?? now()->format('Y-m-d'), // date, required + 'document_number' => $data['document_number'] ?? null, // string, nullable + 'registration_number' => $data['registration_number'] ?? "N/A", // string, required + + // Application information - all nullable 'function_type' => $data['function_type'] ?? null, 'application_type' => $data['application_type'] ?? null, 'application_type_name' => $data['application_type_name'] ?? null, @@ -198,52 +352,74 @@ class PbgTaskDetail extends Model 'condition' => $data['condition'] ?? null, 'prototype' => $data['prototype'] ?? null, 'permanency' => $data['permanency'] ?? null, - 'building_type' => $data['building_type'] ?? null, + + // Building information - all nullable + 'building_type' => isset($data['building_type']) && is_numeric($data['building_type']) ? (int) $data['building_type'] : null, // integer, nullable 'building_type_name' => $data['building_type_name'] ?? null, 'building_purpose' => $data['building_purpose'] ?? null, 'building_use' => $data['building_use'] ?? null, 'occupancy' => $data['occupancy'] ?? null, 'name_building' => $data['name_building'] ?? null, - 'total_area' => $data['total_area'] ?? null, - 'area' => $data['area'] ?? null, - 'area_type' => $data['area_type'] ?? null, - 'height' => $data['height'] ?? null, - 'floor' => $data['floor'] ?? null, - 'floor_area' => $data['floor_area'] ?? 0, - 'basement' => $data['basement'] ?? null, - 'basement_height' => $data['basement_height'] ?? 0, - 'basement_area' => $data['basement_area'] ?? 0, - 'unit' => $data['unit'] ?? null, - 'prev_retribution' => $data['prev_retribution'] ?? 0, - 'prev_pbg' => $data['prev_pbg'] ?? null, - 'prev_total_area' => $data['prev_total_area'] ?? 0, - 'koefisien_dasar_bangunan' => $data['koefisien_dasar_bangunan'] ?? 0, - 'koefisien_lantai_bangunan' => $data['koefisien_lantai_bangunan'] ?? 0, - 'koefisien_lantai_hijau' => $data['koefisien_lantai_hijau'] ?? 0, - 'koefisien_tapak_basement' => $data['koefisien_tapak_basement'] ?? 0, - 'ketinggian_bangunan' => $data['ketinggian_bangunan'] ?? 0, + + // Building dimensions and specifications + 'total_area' => self::cleanNumericValue($data['total_area'] ?? 0), // decimal(10,2), required + 'area' => self::cleanNumericValue($data['area'] ?? null, true), // decimal(10,2), nullable + 'area_type' => $data['area_type'] ?? null, // string, nullable + 'height' => self::cleanNumericValue($data['height'] ?? 0), // decimal(8,2), required + 'floor' => self::cleanIntegerValue($data['floor'] ?? 0), // integer, required + 'floor_area' => self::cleanNumericValue($data['floor_area'] ?? null, true), // decimal(10,2), nullable + 'basement' => isset($data['basement']) && $data['basement'] !== '' && $data['basement'] !== '?' ? $data['basement'] : null, // string, nullable + 'basement_height' => self::cleanNumericValue($data['basement_height'] ?? null, true), // decimal(8,2), nullable + 'basement_area' => self::cleanNumericValue($data['basement_area'] ?? 0), // decimal(10,2), required + 'unit' => isset($data['unit']) && is_numeric($data['unit']) ? (int) $data['unit'] : null, // integer, nullable + + // Previous information + 'prev_retribution' => self::cleanNumericValue($data['prev_retribution'] ?? null, true), // decimal(15,2), nullable + 'prev_pbg' => $data['prev_pbg'] ?? null, // string, nullable + 'prev_total_area' => self::cleanNumericValue($data['prev_total_area'] ?? null, true), // decimal(10,2), nullable + + // Coefficients - all nullable, decimal(8,4) + 'koefisien_dasar_bangunan' => self::cleanNumericValue($data['koefisien_dasar_bangunan'] ?? null, true), + 'koefisien_lantai_bangunan' => self::cleanNumericValue($data['koefisien_lantai_bangunan'] ?? null, true), + 'koefisien_lantai_hijau' => self::cleanNumericValue($data['koefisien_lantai_hijau'] ?? null, true), + 'koefisien_tapak_basement' => self::cleanNumericValue($data['koefisien_tapak_basement'] ?? null, true), + 'ketinggian_bangunan' => self::cleanNumericValue($data['ketinggian_bangunan'] ?? null, true), // decimal(8,2), nullable + + // Road information - all nullable 'jalan_arteri' => $data['jalan_arteri'] ?? null, 'jalan_kolektor' => $data['jalan_kolektor'] ?? null, 'jalan_bangunan' => $data['jalan_bangunan'] ?? null, - 'gsb' => $data['gsb'] ?? null, - 'kkr_number' => $data['kkr_number'] ?? null, + 'gsb' => self::cleanNumericValue($data['gsb'] ?? null, true), // decimal(8,2), nullable + 'kkr_number' => $data['kkr_number'] ?? null, // string, nullable + + // Unit data as JSON - nullable 'unit_data' => $data['unit_data'] ?? null, - 'is_mbr' => $data['is_mbr'] ?? false, - 'code' => $data['code'] ?? null, - 'building_ward_id' => $data['building_ward_id'] ?? null, - 'building_ward_name' => $data['building_ward_name'] ?? null, - 'building_district_id' => $data['building_district_id'] ?? null, - 'building_district_name' => $data['building_district_name'] ?? null, - 'building_regency_id' => $data['building_regency_id'] ?? null, - 'building_regency_name' => $data['building_regency_name'] ?? null, - 'building_province_id' => $data['building_province_id'] ?? null, - 'building_province_name' => $data['building_province_name'] ?? null, - 'building_address' => $data['building_address'] ?? null, - 'latitude' => $data['latitude'] ?? null, - 'longitude' => $data['longitude'] ?? null, + + // Additional flags + 'is_mbr' => (bool) ($data['is_mbr'] ?? false), // boolean, default false + 'code' => $data['code'] ?? "N/A", // string, required + + // Building location information - all required + 'building_ward_id' => self::cleanIntegerValue($data['building_ward_id'] ?? 0), // bigInteger, required + 'building_ward_name' => $data['building_ward_name'] ?? "N/A", // string, required + 'building_district_id' => self::cleanIntegerValue($data['building_district_id'] ?? 0), // integer, required + 'building_district_name' => $data['building_district_name'] ?? "N/A", // string, required + 'building_regency_id' => self::cleanIntegerValue($data['building_regency_id'] ?? 0), // integer, required + 'building_regency_name' => $data['building_regency_name'] ?? "N/A", // string, required + 'building_province_id' => self::cleanIntegerValue($data['building_province_id'] ?? 0), // integer, required + 'building_province_name' => $data['building_province_name'] ?? "N/A", // string, required + 'building_address' => $data['building_address'] ?? "N/A", // text, required + + // Coordinates - decimal(15,8), nullable + 'latitude' => self::cleanCoordinate($data['latitude'] ?? null), + 'longitude' => self::cleanCoordinate($data['longitude'] ?? null), + + // Additional files - nullable 'building_photo' => $data['building_photo'] ?? null, 'pbg_parent' => $data['pbg_parent'] ?? null, - 'api_created_at' => isset($data['created_at']) ? Carbon::parse($data['created_at'])->format('Y-m-d H:i:s') : null, + + // Original created_at from API - nullable + 'api_created_at' => self::parseDateTime($data['created_at'] ?? null), ]; return static::updateOrCreate( diff --git a/database/migrations/2025_08_19_112318_add_is_valid_in_pbg_task_table.php b/database/migrations/2025_08_19_112318_add_is_valid_in_pbg_task_table.php new file mode 100644 index 0000000..b9a2877 --- /dev/null +++ b/database/migrations/2025_08_19_112318_add_is_valid_in_pbg_task_table.php @@ -0,0 +1,28 @@ +boolean('is_valid')->default(true)->after('status'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('pbg_task', function (Blueprint $table) { + $table->dropColumn('is_valid'); + }); + } +}; diff --git a/database/migrations/2025_08_19_112707_change_business_dlh_count_in_bigdata_resumes_table.php b/database/migrations/2025_08_19_112707_change_business_dlh_count_in_bigdata_resumes_table.php new file mode 100644 index 0000000..37edd1d --- /dev/null +++ b/database/migrations/2025_08_19_112707_change_business_dlh_count_in_bigdata_resumes_table.php @@ -0,0 +1,28 @@ +renameColumn('non_business_dlh_count', 'business_dlh_count'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('bigdata_resumes', function (Blueprint $table) { + $table->renameColumn('business_dlh_count', 'non_business_dlh_count'); + }); + } +}; diff --git a/resources/js/dashboards/bigdata.js b/resources/js/dashboards/bigdata.js index 9d27117..a74e3df 100644 --- a/resources/js/dashboards/bigdata.js +++ b/resources/js/dashboards/bigdata.js @@ -3,6 +3,21 @@ import GlobalConfig, { addThousandSeparators } from "../global-config.js"; import InitDatePicker from "../utils/InitDatePicker.js"; class BigData { + // Helper function to safely access nested object properties with default values + safeGet(obj, path, defaultValue = 0) { + try { + return path.split(".").reduce((current, key) => { + return current && + current[key] !== null && + current[key] !== undefined + ? current[key] + : defaultValue; + }, obj); + } catch (error) { + return defaultValue; + } + } + async init() { try { new InitDatePicker( @@ -25,6 +40,14 @@ class BigData { try { this.resumeBigData = await this.getBigDataResume(filterDate); + // Ensure resumeBigData is not null/undefined + if (!this.resumeBigData) { + this.resumeBigData = {}; + console.warn( + "No data received, using empty object with default values" + ); + } + this.initChartTargetPAD(filterDate); this.initChartUsaha(); this.initChartNonUsaha(); @@ -43,6 +66,8 @@ class BigData { this.initChartNonBusinessDLH(); } catch (e) { console.error(e); + // Initialize with empty data if there's an error + this.resumeBigData = {}; } } @@ -92,14 +117,24 @@ class BigData { document .querySelectorAll(".document-total.chart-target-pad") .forEach((element) => { + const sum = this.safeGet( + this.resumeBigData, + "target_pad.sum", + 0 + ); element.innerText = `Rp.${addThousandSeparators( - this.resumeBigData.target_pad.sum.toString() + sum.toString() )}`; }); document .querySelectorAll(".small-percentage.chart-target-pad") .forEach((element) => { - element.innerText = `${this.resumeBigData.target_pad.percentage}%`; + const percentage = this.safeGet( + this.resumeBigData, + "target_pad.percentage", + 0 + ); + element.innerText = `${percentage}%`; }); } initChartTotalPotensi() { @@ -108,44 +143,68 @@ class BigData { document .querySelectorAll(".document-count.chart-total-potensi") .forEach((element) => { - // element.innerText = `${countAll}`; - element.innerText = `${this.resumeBigData.total_potensi.count}`; + const count = this.safeGet( + this.resumeBigData, + "total_potensi.count", + 0 + ); + element.innerText = `${count}`; }); document .querySelectorAll(".document-total.chart-total-potensi") .forEach((element) => { + const sum = this.safeGet( + this.resumeBigData, + "total_potensi.sum", + 0 + ); element.innerText = `Rp.${addThousandSeparators( - // this.bigTotalPotensi.toString() - this.resumeBigData.total_potensi.sum.toString() + sum.toString() )}`; }); document .querySelectorAll(".small-percentage.chart-total-potensi") .forEach((element) => { - // element.innerText = `${this.resultPercentage}%`; - element.innerText = `${this.resumeBigData.total_potensi.percentage}%`; + const percentage = this.safeGet( + this.resumeBigData, + "total_potensi.percentage", + 0 + ); + element.innerText = `${percentage}%`; }); } initChartVerificationDocuments() { document .querySelectorAll(".document-count.chart-berkas-terverifikasi") .forEach((element) => { - // element.innerText = `${this.dataVerification.count}`; - element.innerText = `${this.resumeBigData.verified_document.count}`; + const count = this.safeGet( + this.resumeBigData, + "verified_document.count", + 0 + ); + element.innerText = `${count}`; }); document .querySelectorAll(".document-total.chart-berkas-terverifikasi") .forEach((element) => { + const sum = this.safeGet( + this.resumeBigData, + "verified_document.sum", + 0 + ); element.innerText = `Rp.${addThousandSeparators( - // this.bigTotalVerification.toString() - this.resumeBigData.verified_document.sum.toString() + sum.toString() )}`; }); document .querySelectorAll(".small-percentage.chart-berkas-terverifikasi") .forEach((element) => { - // element.innerText = `${this.percetageResultVerification}%`; - element.innerText = `${this.resumeBigData.verified_document.percentage}%`; + const percentage = this.safeGet( + this.resumeBigData, + "verified_document.percentage", + 0 + ); + element.innerText = `${percentage}%`; }); } initChartNonVerificationDocuments() { @@ -154,17 +213,25 @@ class BigData { ".document-count.chart-berkas-belum-terverifikasi" ) .forEach((element) => { - // element.innerText = `${this.dataNonVerification.count}`; - element.innerText = `${this.resumeBigData.non_verified_document.count}`; + const count = this.safeGet( + this.resumeBigData, + "non_verified_document.count", + 0 + ); + element.innerText = `${count}`; }); document .querySelectorAll( ".document-total.chart-berkas-belum-terverifikasi" ) .forEach((element) => { + const sum = this.safeGet( + this.resumeBigData, + "non_verified_document.sum", + 0 + ); element.innerText = `Rp.${addThousandSeparators( - // this.bigTotalNonVerification.toString() - this.resumeBigData.non_verified_document.sum.toString() + sum.toString() )}`; }); document @@ -172,52 +239,80 @@ class BigData { ".small-percentage.chart-berkas-belum-terverifikasi" ) .forEach((element) => { - // element.innerText = `${this.percentageResultNonVerification}%`; - element.innerText = `${this.resumeBigData.non_verified_document.percentage}%`; + const percentage = this.safeGet( + this.resumeBigData, + "non_verified_document.percentage", + 0 + ); + element.innerText = `${percentage}%`; }); } initChartUsaha() { document .querySelectorAll(".document-count.chart-business") .forEach((element) => { - // element.innerText = `${this.dataBusiness.count}`; - element.innerText = `${this.resumeBigData.business_document.count}`; + const count = this.safeGet( + this.resumeBigData, + "business_document.count", + 0 + ); + element.innerText = `${count}`; }); document .querySelectorAll(".document-total.chart-business") .forEach((element) => { + const sum = this.safeGet( + this.resumeBigData, + "business_document.sum", + 0 + ); element.innerText = `Rp.${addThousandSeparators( - // this.bigTotalBusiness.toString() - this.resumeBigData.business_document.sum.toString() + sum.toString() )}`; }); document .querySelectorAll(".small-percentage.chart-business") .forEach((element) => { - // element.innerText = `${this.percentageResultBusiness}%`; - element.innerText = `${this.resumeBigData.business_document.percentage}%`; + const percentage = this.safeGet( + this.resumeBigData, + "business_document.percentage", + 0 + ); + element.innerText = `${percentage}%`; }); } initChartNonUsaha() { document .querySelectorAll(".document-count.chart-non-business") .forEach((element) => { - // element.innerText = `${this.dataNonBusiness.count}`; - element.innerText = `${this.resumeBigData.non_business_document.count}`; + const count = this.safeGet( + this.resumeBigData, + "non_business_document.count", + 0 + ); + element.innerText = `${count}`; }); document .querySelectorAll(".document-total.chart-non-business") .forEach((element) => { + const sum = this.safeGet( + this.resumeBigData, + "non_business_document.sum", + 0 + ); element.innerText = `Rp.${addThousandSeparators( - // this.bigTotalNonBusiness.toString() - this.resumeBigData.non_business_document.sum.toString() + sum.toString() )}`; }); document .querySelectorAll(".small-percentage.chart-non-business") .forEach((element) => { - // element.innerText = `${this.percentageResultNonBusiness}%`; - element.innerText = `${this.resumeBigData.non_business_document.percentage}%`; + const percentage = this.safeGet( + this.resumeBigData, + "non_business_document.percentage", + 0 + ); + element.innerText = `${percentage}%`; }); } initChartKekuranganPotensi() { @@ -229,129 +324,226 @@ class BigData { document .querySelectorAll(".document-total.chart-kekurangan-potensi") .forEach((element) => { + const sum = this.safeGet( + this.resumeBigData, + "kekurangan_potensi.sum", + 0 + ); element.innerText = `Rp.${addThousandSeparators( - // this.totalKekuranganPotensi.toString() - this.resumeBigData.kekurangan_potensi.sum.toString() + sum.toString() )}`; }); document .querySelectorAll(".small-percentage.chart-kekurangan-potensi") .forEach((element) => { - // element.innerText = `${this.percentageKekuranganPotensi}%`; - element.innerText = `${this.resumeBigData.kekurangan_potensi.percentage}%`; + const percentage = this.safeGet( + this.resumeBigData, + "kekurangan_potensi.percentage", + 0 + ); + element.innerText = `${percentage}%`; }); } initChartRealisasiTerbitPBG() { document .querySelectorAll(".document-count.chart-realisasi-tebit-pbg") .forEach((element) => { - // element.innerText = `${this.dataCountRealisasiTerbit}`; - element.innerText = `${this.resumeBigData.realisasi_terbit.count}`; + const count = this.safeGet( + this.resumeBigData, + "realisasi_terbit.count", + 0 + ); + element.innerText = `${count}`; }); document .querySelectorAll(".document-total.chart-realisasi-tebit-pbg") .forEach((element) => { + const sum = this.safeGet( + this.resumeBigData, + "realisasi_terbit.sum", + 0 + ); element.innerText = `Rp.${addThousandSeparators( - // this.dataSumRealisasiTerbit - this.resumeBigData.realisasi_terbit.sum.toString() + sum.toString() )}`; }); document .querySelectorAll(".small-percentage.chart-realisasi-tebit-pbg") .forEach((element) => { - element.innerText = `${this.resumeBigData.realisasi_terbit.percentage}%`; + const percentage = this.safeGet( + this.resumeBigData, + "realisasi_terbit.percentage", + 0 + ); + element.innerText = `${percentage}%`; }); } initChartMenungguKlikDPMPTSP() { document .querySelectorAll(".document-count.chart-menunggu-klik-dpmptsp") .forEach((element) => { - // element.innerText = `${this.dataCountMenungguKlikDPMPTSP}`; - element.innerText = `${this.resumeBigData.menunggu_klik_dpmptsp.count}`; + const count = this.safeGet( + this.resumeBigData, + "menunggu_klik_dpmptsp.count", + 0 + ); + element.innerText = `${count}`; }); document .querySelectorAll(".document-total.chart-menunggu-klik-dpmptsp") .forEach((element) => { + const sum = this.safeGet( + this.resumeBigData, + "menunggu_klik_dpmptsp.sum", + 0 + ); element.innerText = `Rp.${addThousandSeparators( - // this.dataSumMenungguKlikDPMPTSP - this.resumeBigData.menunggu_klik_dpmptsp.sum.toString() + sum.toString() )}`; }); document .querySelectorAll(".small-percentage.chart-menunggu-klik-dpmptsp") .forEach((element) => { - element.innerText = `${this.resumeBigData.menunggu_klik_dpmptsp.percentage}%`; + const percentage = this.safeGet( + this.resumeBigData, + "menunggu_klik_dpmptsp.percentage", + 0 + ); + element.innerText = `${percentage}%`; }); } initChartProsesDinasTeknis() { document .querySelectorAll(".document-count.chart-proses-dinas-teknis") .forEach((element) => { - // element.innerText = `${this.dataCountProsesDinasTeknis}`; - element.innerText = `${this.resumeBigData.proses_dinas_teknis.count}`; + const count = this.safeGet( + this.resumeBigData, + "proses_dinas_teknis.count", + 0 + ); + element.innerText = `${count}`; }); document .querySelectorAll(".document-total.chart-proses-dinas-teknis") .forEach((element) => { + const sum = this.safeGet( + this.resumeBigData, + "proses_dinas_teknis.sum", + 0 + ); element.innerText = `Rp.${addThousandSeparators( - // this.dataSumProsesDinasTeknis - this.resumeBigData.proses_dinas_teknis.sum.toString() + sum.toString() )}`; }); document .querySelectorAll(".small-percentage.chart-proses-dinas-teknis") .forEach((element) => { - element.innerText = `${this.resumeBigData.proses_dinas_teknis.percentage}%`; + const percentage = this.safeGet( + this.resumeBigData, + "proses_dinas_teknis.percentage", + 0 + ); + element.innerText = `${percentage}%`; }); } initChartPotensiTataRuang() { document .querySelectorAll(".document-count.chart-potensi-tata-ruang") .forEach((element) => { - element.innerText = `${this.resumeBigData.tata_ruang.count}`; + const count = this.safeGet( + this.resumeBigData, + "tata_ruang.count", + 0 + ); + element.innerText = `${count}`; }); document .querySelectorAll(".document-total.chart-potensi-tata-ruang") .forEach((element) => { + const sum = this.safeGet( + this.resumeBigData, + "tata_ruang.sum", + 0 + ); element.innerText = `Rp.${addThousandSeparators( - this.resumeBigData.tata_ruang.sum.toString() + sum.toString() )}`; }); document .querySelectorAll(".small-percentage.chart-potensi-tata-ruang") .forEach((element) => { - element.innerText = `${this.resumeBigData.tata_ruang.percentage}%`; + const percentage = this.safeGet( + this.resumeBigData, + "tata_ruang.percentage", + 0 + ); + element.innerText = `${percentage}%`; }); } initChartBusinessRAB() { document.querySelectorAll("#business-rab-count").forEach((element) => { - element.innerText = `${this.resumeBigData.business_rab_count}`; + const count = this.safeGet( + this.resumeBigData, + "business_rab_count", + 0 + ); + element.innerText = `${count}`; }); } initChartBusinessKRK() { document.querySelectorAll("#business-krk-count").forEach((element) => { - element.innerText = `${this.resumeBigData.business_krk_count}`; + const count = this.safeGet( + this.resumeBigData, + "business_krk_count", + 0 + ); + element.innerText = `${count}`; + }); + } + initChartBusinessDLH() { + document.querySelectorAll("#business-dlh-count").forEach((element) => { + const count = this.safeGet( + this.resumeBigData, + "business_dlh_count", + 0 + ); + element.innerText = `${count}`; }); } initChartNonBusinessRAB() { document .querySelectorAll("#non-business-rab-count") .forEach((element) => { - element.innerText = `${this.resumeBigData.non_business_rab_count}`; + const count = this.safeGet( + this.resumeBigData, + "non_business_rab_count", + 0 + ); + element.innerText = `${count}`; }); } initChartNonBusinessKRK() { document .querySelectorAll("#non-business-krk-count") .forEach((element) => { - element.innerText = `${this.resumeBigData.non_business_krk_count}`; + const count = this.safeGet( + this.resumeBigData, + "non_business_krk_count", + 0 + ); + element.innerText = `${count}`; }); } initChartNonBusinessDLH() { document .querySelectorAll("#non-business-dlh-count") .forEach((element) => { - element.innerText = `${this.resumeBigData.non_business_dlh_count}`; + const count = this.safeGet( + this.resumeBigData, + "non_business_dlh_count", + 0 + ); + element.innerText = `${count}`; }); } } diff --git a/resources/js/pbg-task/show.js b/resources/js/pbg-task/show.js index 686570c..d4bacbb 100644 --- a/resources/js/pbg-task/show.js +++ b/resources/js/pbg-task/show.js @@ -10,6 +10,7 @@ class PbgTaskAssignments { this.initTablePbgTaskAssignments(); this.handleUpdateData(); this.initDatePicker(); + this.initIsValidToggle(); } initDatePicker() { @@ -20,6 +21,30 @@ class PbgTaskAssignments { }); } + initIsValidToggle() { + const checkbox = document.getElementById("is_valid"); + const statusText = document.querySelector(".status-text"); + const statusDescription = statusText?.nextElementSibling; + + if (checkbox && statusText) { + checkbox.addEventListener("change", function () { + if (this.checked) { + statusText.textContent = "Data Valid"; + if (statusDescription) { + statusDescription.textContent = + "Data telah diverifikasi dan sesuai"; + } + } else { + statusText.textContent = "Data Tidak Valid"; + if (statusDescription) { + statusDescription.textContent = + "Data perlu diverifikasi atau diperbaiki"; + } + } + }); + } + } + initTablePbgTaskAssignments() { let tableContainer = document.getElementById( "table-pbg-task-assignments" @@ -92,6 +117,12 @@ class PbgTaskAssignments { formData.forEach((value, key) => { formObject[key] = value; }); + + // Handle checkbox properly - ensure boolean value is sent + const isValidCheckbox = document.getElementById("is_valid"); + if (isValidCheckbox) { + formObject["is_valid"] = isValidCheckbox.checked ? 1 : 0; + } fetch(form.action, { method: "PUT", // Ensure your Laravel route is set to accept PUT requests body: JSON.stringify(formObject), // Convert form data to JSON diff --git a/resources/scss/pages/pbg-task/show.scss b/resources/scss/pages/pbg-task/show.scss index 2f17e64..4f9ad86 100644 --- a/resources/scss/pages/pbg-task/show.scss +++ b/resources/scss/pages/pbg-task/show.scss @@ -1,6 +1,102 @@ // PBG Task Show Page Styles // Custom styles for data lists display (List Layout) +// Enhanced checkbox styling for is_valid field +.form-check.form-switch { + padding-left: 0; + + .form-check-input { + width: 3.25rem; + height: 1.75rem; + border-radius: 1rem; + background-color: #dc3545; + border: none; + transition: all 0.3s ease; + position: relative; + cursor: pointer; + margin-left: 0; + + // Remove default browser styling + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + + &:checked { + background-color: #28a745; + box-shadow: 0 0 0 0.2rem rgba(40, 167, 69, 0.25); + } + + &:focus { + box-shadow: 0 0 0 0.2rem rgba(93, 135, 255, 0.25); + outline: none; + } + + &:not(:checked) { + background-color: #dc3545; + } + + // The toggle circle + &::before { + content: ""; + position: absolute; + top: 0.125rem; + left: 0.125rem; + width: 1.5rem; + height: 1.5rem; + background-color: #fff; + border-radius: 50%; + transition: all 0.3s ease; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.15); + z-index: 1; + } + + // Animation for checked state + &:checked::before { + transform: translateX(1.5rem); + } + } + + .form-check-label { + margin-left: 1rem; + cursor: pointer; + padding-left: 0; + + .status-text { + font-weight: 600; + font-size: 1rem; + color: #2c3e50; + transition: color 0.3s ease; + } + + small { + font-size: 0.85rem; + margin-top: 0.25rem; + line-height: 1.3; + } + } +} + +// Status indicator styling +.status-validation-container { + background: #f8f9fa; + border: 1px solid #e9ecef; + border-radius: 8px; + padding: 1rem; + transition: all 0.3s ease; + + &.valid { + background: #d4edda; + border-color: #c3e6cb; + color: #155724; + } + + &.invalid { + background: #f8d7da; + border-color: #f5c6cb; + color: #721c24; + } +} + .data-list-section { .section-header { border-bottom: 2px solid #f8f9fa; diff --git a/resources/views/dashboards/bigdata.blade.php b/resources/views/dashboards/bigdata.blade.php index 561a6cd..c0aeffe 100644 --- a/resources/views/dashboards/bigdata.blade.php +++ b/resources/views/dashboards/bigdata.blade.php @@ -100,15 +100,15 @@
-
@@ -123,22 +123,22 @@
-
-
-
diff --git a/resources/views/errors/503.blade.php b/resources/views/errors/503.blade.php index b8076d3..f33a96f 100644 --- a/resources/views/errors/503.blade.php +++ b/resources/views/errors/503.blade.php @@ -14,13 +14,13 @@ class="authentication-bg"
-
- auth +
+ auth -

Service Unavailable!

-

Our site is currently undergoing scheduled maintenance.
Please check back later.

+

Service Unavailable!

+

Our site is currently undergoing scheduled maintenance.
Please check back later.

-
+
diff --git a/resources/views/home/index.blade.php b/resources/views/home/index.blade.php index 759eba8..b6173b7 100644 --- a/resources/views/home/index.blade.php +++ b/resources/views/home/index.blade.php @@ -4,19 +4,42 @@ @include('layouts.partials/page-title', ['title' => 'Home', 'subtitle' => 'Home']) -
-
-
-
-

Selamat Datang di SIBEDAS PBG!

-

- Sistem Informasi Berbasis Data (SIBEDAS) adalah sebuah sistem yang dirancang untuk mengelola dan mengolah data pegawai secara efektif dan efisien. - Dengan teknologi modern, SIBEDAS memungkinkan pegawai untuk mendata, melihat, dan memanipulasi informasi pegawai dengan mudah. -

-

- PBG (Pegawai Badan Kepegawaian) merupakan unit kerja yang bertanggung jawab atas administrasi kepegawaian. - Melalui SIBEDAS PBG, proses manajemen data pegawai menjadi lebih terstruktur, transparan, dan mudah diakses kapan saja. -

+
+
+
+
+
+
+
+

+ SIBEDAS PBG +

+
+
+ +
+
+
+
+ Tentang Aplikasi +
+

+ Aplikasi SIBEDAS PBG merupakan sistem pendukung yang dirancang untuk membantu pimpinan dalam melakukan pengawasan dan monitoring terhadap berkas pengajuan Persetujuan Bangunan Gedung (PBG) yang tercatat di SIMBG. +

+
+ +
+
+ Manfaat & Keunggulan +
+

+ Melalui SIBEDAS PBG, pimpinan dapat memantau secara langsung status perkembangan setiap berkas, mengidentifikasi pengajuan yang belum selesai, dan memastikan tindak lanjut penyelesaian dilakukan tepat waktu. Pengawasan yang lebih terstruktur, cepat, dan akurat ini tidak hanya meningkatkan kualitas pelayanan kepada masyarakat, tetapi juga mendukung tercapainya target Pendapatan Asli Daerah (PAD) melalui optimalisasi penyelesaian berkas PBG. +

+
+
+
+
+
diff --git a/resources/views/pages/404.blade.php b/resources/views/pages/404.blade.php index 0543649..6613dd8 100644 --- a/resources/views/pages/404.blade.php +++ b/resources/views/pages/404.blade.php @@ -26,7 +26,7 @@ class="authentication-bg" -->
- auth + dputr logo

Page Not Found !

The page you're trying to reach seems to have gone
missing in the digital wilderness.

diff --git a/resources/views/pbg_task/show.blade.php b/resources/views/pbg_task/show.blade.php index 9806258..36a18fa 100644 --- a/resources/views/pbg_task/show.blade.php +++ b/resources/views/pbg_task/show.blade.php @@ -80,10 +80,24 @@
-
+
+
+ +
+ is_valid ? 'checked' : '' }}> + +
+ + +