diff --git a/app/Http/Controllers/Api/PbgTaskController.php b/app/Http/Controllers/Api/PbgTaskController.php index 8ba3ab8..9c34a1b 100644 --- a/app/Http/Controllers/Api/PbgTaskController.php +++ b/app/Http/Controllers/Api/PbgTaskController.php @@ -21,9 +21,35 @@ class PbgTaskController extends Controller public function __construct(GoogleSheetService $googleSheetService){ $this->googleSheetService = $googleSheetService; } - public function index() + public function index(Request $request) { - // + info($request); + + $isLastUpdated = filter_var($request->query('isLastUpdated', false), FILTER_VALIDATE_BOOLEAN); + + $query = PbgTask::query(); + + if ($isLastUpdated) { + $query->orderBy('updated_at', 'desc'); + } else { + $query->where('status', 20); + } + + // Ambil maksimal 10 data + $pbg_task = $query->limit(10)->get(); + $totalData = $pbg_task->count(); + + // Tambahkan nomor urut + $data = $pbg_task->map(function ($item, $index) { + return array_merge($item->toArray(), ['no' => $index + 1]); + }); + + return response()->json([ + 'data' => $data, + 'meta' => [ + 'total' => $totalData + ] + ]); } /** diff --git a/app/Http/Controllers/Api/TourismController.php b/app/Http/Controllers/Api/TourismController.php index 896ff79..3a20e69 100644 --- a/app/Http/Controllers/Api/TourismController.php +++ b/app/Http/Controllers/Api/TourismController.php @@ -106,6 +106,18 @@ class TourismController extends Controller } } + public function getAllLocation() + { + $locations = Tourism::whereNotNull('longitude') + ->whereNotNull('latitude') + ->select('project_name', 'longitude', 'latitude') + ->get(); + + return response()->json([ + 'data' => $locations + ]); + } + /** * Display the specified resource. */ diff --git a/app/Http/Controllers/Dashboards/BigDataController.php b/app/Http/Controllers/Dashboards/BigDataController.php index 470ef54..c425489 100644 --- a/app/Http/Controllers/Dashboards/BigDataController.php +++ b/app/Http/Controllers/Dashboards/BigDataController.php @@ -15,7 +15,8 @@ class BigDataController extends Controller return view('dashboards.bigdata', compact('latest_created')); } - public function pbg(){ - return view('index'); + public function pbg() + { + return view('dashboards.pbg'); } } diff --git a/resources/js/dashboards/pbg.js b/resources/js/dashboards/pbg.js new file mode 100644 index 0000000..5967118 --- /dev/null +++ b/resources/js/dashboards/pbg.js @@ -0,0 +1,470 @@ +import Big from "big.js"; +import GlobalConfig, { addThousandSeparators } from "../global-config.js"; +import ApexCharts from "apexcharts"; +import "gridjs/dist/gridjs.umd.js"; +import GeneralTable from "../table-generator.js"; + +var chart; +document.addEventListener("DOMContentLoaded", async function () { + await initChart(); + const yearPicker = document.getElementById("yearPicker"); + + async function updateDataByYear(selectedYear) { + // Target PAD Element + const targetPadElement = document.getElementById("target-pad"); + if (!targetPadElement) return; + const targetPadValue = await getDataSettings("TARGET_PAD"); + targetPadElement.textContent = formatCurrency(targetPadValue); + + // Total Potensi Berkas + const totalPotensiBerkas = document.getElementById("total-potensi-berkas"); + if (!totalPotensiBerkas) return; + const totalPotensiBerkasValue = await getDataTotalPotensi(selectedYear); + totalPotensiBerkas.textContent = formatCurrency(totalPotensiBerkasValue.totalData); + + // Total Berkas Terverifikasi + const totalBerkasTerverifikasi = document.getElementById("total-berkas-terverifikasi"); + if (!totalBerkasTerverifikasi) return; + const totalBerkasTerverifikasiValue = await getDataVerification(selectedYear); + totalBerkasTerverifikasi.textContent = formatCurrency(totalBerkasTerverifikasiValue.totalData); + + // Total Kekurangan potensi + const totalKekuranganPotensi = document.getElementById("total-kekurangan-potensi"); + if (!totalKekuranganPotensi) return; + const totalKekuranganPotensiValue = new Big(targetPadValue) - new Big(totalPotensiBerkasValue.totalData); + totalKekuranganPotensi.textContent = formatCurrency(totalKekuranganPotensiValue) + + // Total Potensi PBG dari tata ruang + const totalPotensiPBGTataRuang = document.getElementById("total-potensi-pbd-tata-ruang"); + if (!totalPotensiPBGTataRuang) return; + const totalPotensiPBGTataRuangValue = await getDataSettings("TATA_RUANG"); + totalPotensiPBGTataRuang.textContent = formatCurrency(totalPotensiPBGTataRuangValue); + + // Total Berkas Belum terverifikasi + const totalBerkasBelumTerverifikasi = document.getElementById("total-berkas-belum-terverifikasi"); + if (!totalBerkasBelumTerverifikasi) return; + const totalBerkasBelumTerverifikasiValue = await getDataNonVerification(selectedYear); + const totalBerkasBelumTerverifikasiCount = totalBerkasBelumTerverifikasiValue.countData; + totalBerkasBelumTerverifikasi.textContent = formatCurrency(totalBerkasBelumTerverifikasiValue.totalData); + + // Total Berkas Usaha + const totalBerkasUsahaValue = await getDataBusiness(selectedYear); + const totalBerkasUsahaCount = totalBerkasUsahaValue.countData; + const totalBerkasUsahaTotalData = totalBerkasUsahaValue.totalData; + + // Total Berkas Non Usaha + const totalBerkasNonUsahaValue = await getDataNonBusiness(selectedYear); + const totalBerkasNonUsahaCount = totalBerkasNonUsahaValue.countData; + const totalBerkasNonUsahaTotalData = totalBerkasNonUsahaValue.totalData; + + // Pie Chart Section + let persenUsaha = totalBerkasBelumTerverifikasiCount > 0 + ? ((totalBerkasUsahaCount / totalBerkasBelumTerverifikasiCount) * 100).toFixed(2) + : "0"; + + let persenNonUsaha = totalBerkasBelumTerverifikasiCount > 0 + ? ((totalBerkasNonUsahaCount / totalBerkasBelumTerverifikasiCount) * 100).toFixed(2) + : "0"; + + const dataSeriesPieChart = [Number(persenUsaha), Number(persenNonUsaha)] + const labelsPieChart = ["Berkas Usaha", "Berkas Non Usaha"]; + document.querySelector("td[data-category='non-usaha']").textContent = formatCurrency(totalBerkasNonUsahaTotalData).toLocaleString(); + document.querySelector("td[data-category='non-usaha-percentage']").textContent = persenNonUsaha + "%"; + + document.querySelector("td[data-category='usaha']").textContent = formatCurrency(totalBerkasUsahaTotalData).toLocaleString(); + document.querySelector("td[data-category='usaha-percentage']").textContent = persenUsaha + "%"; + + updatePieChart(dataSeriesPieChart, labelsPieChart); + + // Load all Tourism location + const allLocation = await getAllLocation(); + console.log(allLocation); + + // Filter hanya data yang memiliki angka valid + let validLocations = allLocation.dataLocation.filter(loc => { + return !isNaN(parseFloat(loc.longitude)) && !isNaN(parseFloat(loc.latitude)); + }); + + // Ubah string ke float + validLocations = validLocations.map(loc => ({ + name: loc.project_name, + longitude: parseFloat(loc.longitude), + latitude: parseFloat(loc.latitude) + })); + + console.log(validLocations.name) + + // Inisialisasi peta + var map = L.map('map').setView([-7.0230, 107.5275], 10); + L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { + attribution: '© OpenStreetMap contributors' + }).addTo(map); + + // Tambahkan marker ke peta + validLocations.forEach(function(loc) { + L.marker([loc.latitude, loc.longitude]) + .addTo(map) + .bindPopup(`${loc.name}`) // Popup saat diklik + .bindTooltip(loc.name, { permanent: false, direction: "top" }); // Tooltip saat di-hover + }); + + // Realisasi terbit PBG + const totalRealisasiTerbitPBG = document.getElementById("realisasi-terbit-pbg"); + if (!totalRealisasiTerbitPBG) return; + const totalRealisasiTerbitPBGValue = await getDataSettings("REALISASI_TERBIT_PBG_SUM"); + totalRealisasiTerbitPBG.textContent = formatCurrency(totalRealisasiTerbitPBGValue); + + // Menunggu Klik DPMPTSP + const totalMenungguKlikDpmptsp = document.getElementById("waiting-click-dpmptsp"); + if (!totalMenungguKlikDpmptsp) return; + const totalMenungguKlikDpmptspValue = await getDataSettings("MENUNGGU_KLIK_DPMPTSP_SUM"); + totalMenungguKlikDpmptsp.textContent = formatCurrency(totalMenungguKlikDpmptspValue); + + // Proses Dinas Teknis + const totalProsesDinasTeknis = document.getElementById("processing-technical-services"); + if (!totalProsesDinasTeknis) return; + const totalProsesDinasTeknisValue = await getDataSettings("PROSES_DINAS_TEKNIS_SUM"); + totalProsesDinasTeknis.textContent = formatCurrency(totalProsesDinasTeknisValue); + + // Load Tabel Baru di Update + const tableLastUpdated = new GeneralTable( + "pbg-filter-by-updated-at", + `${GlobalConfig.apiHost}/api/api-pbg-task?isLastUpdated=true`, + `${GlobalConfig.apiHost}`, + pbgTaskColumns + ); + + tableLastUpdated.processData = function (data) { + console.log("Response Data:", data); + return data.data.map((item) => { + return [ + item.no, + item.name, + item.registration_number, + item.document_number, + item.address, + ]; + }); + }; + + tableLastUpdated.init(); + + // Load Tabel Status SK Terbit + const tableSKPBGTerbit = new GeneralTable( + "pbg-filter-by-status", + `${GlobalConfig.apiHost}/api/api-pbg-task?isLastUpdated=false`, + `${GlobalConfig.apiHost}`, + pbgTaskColumns + ); + + tableSKPBGTerbit.processData = function (data) { + console.log("Response Data:", data); + return data.data.map((item) => { + return [ + item.no, + item.name, + item.registration_number, + item.document_number, + item.address, + ]; + }); + }; + + tableSKPBGTerbit.init(); + + document.querySelector("#pbg-filter-by-updated-at .gridjs-search").hidden = true; + document.querySelector("#pbg-filter-by-updated-at .gridjs-footer").hidden = true; + document.querySelector("#pbg-filter-by-status .gridjs-search").hidden = true; + document.querySelector("#pbg-filter-by-status .gridjs-footer").hidden = true; + } + + await updateDataByYear(yearPicker.value); + + yearPicker.addEventListener("change", async function () { + console.log("event change dropdown") + await updateDataByYear(yearPicker.value); + }); + +}); + +async function getDataSettings(string_key) { + try { + const response = await fetch( + `${GlobalConfig.apiHost}/api/api-data-settings?search=${string_key}`, + { + credentials: "include", + headers: { + Authorization: `Bearer ${ + document.querySelector("meta[name='api-token']").content + }`, + "Content-Type": "application/json", + }, + } + ); + + if (!response.ok) { + console.error("Network response was not ok", response); + } + + const data = await response.json(); + return data.data?.[0]?.value ?? 0; // Pastikan tidak error jika data kosong + } catch (error) { + console.error("Error fetching data:", error); + return 0; + } +} + +async function getDataTotalPotensi(year) { + try { + const response = await fetch( + `${GlobalConfig.apiHost}/api/all-task-documents?year=${year}`, + { + credentials: "include", + headers: { + Authorization: `Bearer ${ + document.querySelector("meta[name='api-token']").content + }`, + "Content-Type": "application/json" + }, + } + ); + + if (!response.ok) { + console.error("Network response was not ok", response); + } + + const data = await response.json(); + return { + totalData: data.data.total, + }; + } catch (error) { + console.error("Error fetching chart data:", error); + return null; + } +} + +async function getDataVerification(year) { + try { + const response = await fetch( + `${GlobalConfig.apiHost}/api/verification-documents?year=${year}`, + { + credentials: "include", + headers: { + Authorization: `Bearer ${ + document.querySelector("meta[name='api-token']") + .content + }`, + "Content-Type": "application/json", + }, + } + ); + + if (!response.ok) { + console.error("Network response was not ok", response); + } + + const data = await response.json() + return { + totalData: data.data.total, + }; + } catch (error) { + console.error("Error fetching chart data:", error); + return 0 + } +} + +async function getDataNonVerification(year) { + try { + const response = await fetch ( + `${GlobalConfig.apiHost}/api/non-verification-documents?year=${year}`, + { + credentials: "include", + headers: { + Authorization: `Bearer ${ + document.querySelector("meta[name='api-token']") + .content + }`, + "Content-Type": "application/json", + }, + } + ); + + if (!response.ok) { + console.error("Network response was not ok", response); + } + + const data = await response.json(); + return { + countData: data.data.count, + totalData: data.data.total, + } + } catch (error) { + console.error("Error fetching chart data:", error); + return 0; + } +} + +async function getDataBusiness(year) { + try { + const response = await fetch( + `${GlobalConfig.apiHost}/api/business-documents?year=${year}`, + { + credentials: "include", + headers: { + Authorization: `Bearer ${ + document.querySelector("meta[name='api-token']") + .content + }`, + "Content-Type": "application/json", + }, + } + ); + + if (!response.ok) { + console.error("Network response was not ok", response); + } + + const data = await response.json(); + return { + countData: data.data.count, + totalData: data.data.total, + }; + } catch (error) { + console.error("Error fetching chart data:", error); + return 0; + } +} + +async function getDataNonBusiness(year) { + try { + const response = await fetch( + `${GlobalConfig.apiHost}/api/non-business-documents?year=${year}`, + { + credentials: "include", + headers: { + Authorization: `Bearer ${ + document.querySelector("meta[name='api-token']") + .content + }`, + "Content-Type": "application/json", + }, + } + ); + + if (!response.ok) { + console.error("Network response was not ok", response); + } + const data = await response.json(); + return { + countData: data.data.count, + totalData: data.data.total + } + } catch (error) { + console.error("Error fetching chart data:", error); + return 0; + } +} + +async function getAllLocation() { + try { + const response = await fetch( + `${GlobalConfig.apiHost}/api/get-all-location`, + { + credentials: "include", + headers: { + Authorization: `Bearer ${ + document.querySelector("meta[name='api-token']") + .content + }`, + "Content-Type": "application/json", + }, + } + ); + if (!response.ok) { + console.error("Network response was not ok", response); + } + const data = await response.json(); + return { + dataLocation: data.data, + } + } catch (error) { + console.error("Error fetching chart data:", error); + return 0; + } +} + +async function initChart() { + var options = { + chart: { + height: 180, + type: 'donut', + }, + series: [0, 0], // Inisialisasi dengan nilai awal + labels: ["Berkas Usaha", "Berkas Non Usaha"], + legend: { + show: false + }, + stroke: { + width: 0 + }, + plotOptions: { + pie: { + donut: { + size: '60%', + } + } + }, + colors: ["#7e67fe", "#17c553"], + dataLabels: { + enabled: false + }, + responsive: [{ + breakpoint: 480, + options: { + chart: { + width: 200 + } + } + }], + fill: { + type: 'gradient' + } + }; + + chart = new ApexCharts(document.querySelector("#conversions"), options); + chart.render(); +} + +async function updatePieChart(dataSeries, labels) { + if (!Array.isArray(dataSeries) || dataSeries.length === 0) { + console.error("Data series tidak valid:", dataSeries); + return; + } + + // Perbarui data series chart + chart.updateSeries(dataSeries); + + // Perbarui label jika diperlukan + if (Array.isArray(labels) && labels.length === dataSeries.length) { + chart.updateOptions({ + labels: labels + }); + } +} + +// Fungsi untuk format angka ke Rupiah +function formatCurrency(number) { + return new Intl.NumberFormat("id-ID", { + style: "currency", + currency: "IDR", + minimumFractionDigits: 0 + }).format(number); +} + +const pbgTaskColumns = [ + "No", + "Name", + "Nomor Registrasi", + "Nomor Dokumen", + "Alamat", +] \ No newline at end of file diff --git a/resources/views/dashboards/pbg.blade.php b/resources/views/dashboards/pbg.blade.php new file mode 100644 index 0000000..5baf94c --- /dev/null +++ b/resources/views/dashboards/pbg.blade.php @@ -0,0 +1,324 @@ +@extends('layouts.vertical', ['subtitle' => 'Dashboard']) + +@section('css') + +@vite(['node_modules/gridjs/dist/theme/mermaid.min.css']) +@endsection +@section('content') + +@include('layouts.partials/page-title', ['title' => 'Dasboard', 'subtitle' => 'Dashboard PBG']) + + +
Kekurangan Potensi
+Target PAD
+Total Potensi Berkas
+Perkiraan Potensi PBG dari Tata Ruang
+Total Berkas Belum Terverifikasi
+Total Berkas Terverifikasi
+| + KETEGORI + | +NILAI | +PERSENTASE | +
|---|---|---|
| NON USAHA | +- | +- | +
| USAHA | +- | +- | +
Realisasi Terbit PBG
+Menunggu Klik DPMPTSP
+Berproses Di Dinas Teknis
+