Merge remote-tracking branch 'origin/dev' into feature/chatbot-sidebar

This commit is contained in:
2025-03-06 11:20:45 +07:00
51 changed files with 1229 additions and 689 deletions

View File

@@ -2,6 +2,7 @@
namespace App\Console\Commands; namespace App\Console\Commands;
use App\Jobs\SyncronizeSIMBG;
use App\Services\ServiceSIMBG; use App\Services\ServiceSIMBG;
use Illuminate\Console\Command; use Illuminate\Console\Command;
use \Illuminate\Support\Facades\Log; use \Illuminate\Support\Facades\Log;
@@ -28,13 +29,12 @@ class ExecuteScraping extends Command
private $service_simbg; private $service_simbg;
public function __construct(ServiceSIMBG $service_simbg){ public function __construct(){
$this->service_simbg = $service_simbg;
parent::__construct(); parent::__construct();
} }
public function handle() public function handle()
{ {
SyncronizeSIMBG::dispatch();
Log::info("running scheduler daily scraping"); Log::info("running scheduler daily scraping");
$this->service_simbg->syncTaskList();
} }
} }

View File

@@ -39,13 +39,17 @@ class BigDataResumeController extends Controller
return response()->json(['message' => 'No data setting found']); return response()->json(['message' => 'No data setting found']);
} }
function cleanNumber($value) {
return floatval(str_replace('.', '', $value));
}
$target_pad = floatval(optional($data_settings->where('key', 'TARGET_PAD')->first())->value); $target_pad = floatval(optional($data_settings->where('key', 'TARGET_PAD')->first())->value);
$realisasi_terbit_pbg_sum = floatval(optional($data_settings->where('key', 'REALISASI_TERBIT_PBG_SUM')->first())->value); $realisasi_terbit_pbg_sum = cleanNumber(optional($data_settings->where('key', 'REALISASI_TERBIT_PBG_SUM')->first())->value);
$realisasi_terbit_pbg_count = floatval(optional($data_settings->where('key', 'REALISASI_TERBIT_PBG_COUNT')->first())->value); $realisasi_terbit_pbg_count = cleanNumber(optional($data_settings->where('key', 'REALISASI_TERBIT_PBG_COUNT')->first())->value);
$menuggu_klik_dpmptsp_sum = floatval(optional($data_settings->where('key', 'MENUNGGU_KLIK_DPMPTSP_SUM')->first())->value); $menunggu_klik_dpmptsp_sum = cleanNumber(optional($data_settings->where('key', 'MENUNGGU_KLIK_DPMPTSP_SUM')->first())->value);
$menuggu_klik_dpmptsp_count = floatval(optional($data_settings->where('key', 'MENUNGGU_KLIK_DPMPTSP_COUNT')->first())->value); $menunggu_klik_dpmptsp_count = cleanNumber(optional($data_settings->where('key', 'MENUNGGU_KLIK_DPMPTSP_COUNT')->first())->value);
$proses_dinas_teknis_sum = floatval(optional($data_settings->where('key', 'PROSES_DINAS_TEKNIS_SUM')->first())->value); $proses_dinas_teknis_sum = cleanNumber(optional($data_settings->where('key', 'PROSES_DINAS_TEKNIS_SUM')->first())->value);
$proses_dinas_teknis_count = floatval(optional($data_settings->where('key', 'PROSES_DINAS_TEKNIS_COUNT')->first())->value); $proses_dinas_teknis_count = cleanNumber(optional($data_settings->where('key', 'PROSES_DINAS_TEKNIS_COUNT')->first())->value);
$tata_ruang = $big_data_resume->spatial_sum; $tata_ruang = $big_data_resume->spatial_sum;
$kekurangan_potensi = $target_pad - $big_data_resume->potention_sum; $kekurangan_potensi = $target_pad - $big_data_resume->potention_sum;
@@ -83,8 +87,8 @@ class BigDataResumeController extends Controller
? round(($realisasi_terbit_pbg_sum / $big_data_resume->verified_sum) * 100, 2) : 0; ? round(($realisasi_terbit_pbg_sum / $big_data_resume->verified_sum) * 100, 2) : 0;
// percentage menunggu klik dpmptsp // percentage menunggu klik dpmptsp
$menunggu_klik_dpmptsp_percentage = $big_data_resume->verified_sum > 0 && $menuggu_klik_dpmptsp_sum > 0 $menunggu_klik_dpmptsp_percentage = $big_data_resume->verified_sum > 0 && $menunggu_klik_dpmptsp_sum > 0
? round(($menuggu_klik_dpmptsp_sum / $big_data_resume->verified_sum) * 100, 2) : 0; ? round(($menunggu_klik_dpmptsp_sum / $big_data_resume->verified_sum) * 100, 2) : 0;
// percentage proses_dinas_teknis // percentage proses_dinas_teknis
$proses_dinas_teknis_percentage = $big_data_resume->verified_sum > 0 && $proses_dinas_teknis_sum > 0 $proses_dinas_teknis_percentage = $big_data_resume->verified_sum > 0 && $proses_dinas_teknis_sum > 0
@@ -135,8 +139,8 @@ class BigDataResumeController extends Controller
'percentage' => $realisasi_terbit_percentage 'percentage' => $realisasi_terbit_percentage
], ],
'menunggu_klik_dpmptsp' => [ 'menunggu_klik_dpmptsp' => [
'sum' => $menuggu_klik_dpmptsp_sum, 'sum' => $menunggu_klik_dpmptsp_sum,
'count' => $menuggu_klik_dpmptsp_count, 'count' => $menunggu_klik_dpmptsp_count,
'percentage' => $menunggu_klik_dpmptsp_percentage 'percentage' => $menunggu_klik_dpmptsp_percentage
], ],
'proses_dinas_teknis' => [ 'proses_dinas_teknis' => [
@@ -151,6 +155,8 @@ class BigDataResumeController extends Controller
} }
} }
public function bigdata_report(Request $request){ public function bigdata_report(Request $request){
try{ try{
$query = BigdataResume::query()->orderBy('id', 'desc'); $query = BigdataResume::query()->orderBy('id', 'desc');
@@ -159,7 +165,7 @@ class BigDataResumeController extends Controller
$query->where('name', 'LIKE', '%'.$request->input('search').'%'); $query->where('name', 'LIKE', '%'.$request->input('search').'%');
} }
$query = $query->paginate(15); $query = $query->paginate(config('app.paginate_per_page', 50));
return BigdataResumeResource::collection($query)->response()->getData(true); return BigdataResumeResource::collection($query)->response()->getData(true);
}catch(\Exception $e){ }catch(\Exception $e){
Log::error($e->getMessage()); Log::error($e->getMessage());
@@ -167,38 +173,6 @@ class BigDataResumeController extends Controller
} }
} }
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id)
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
//
}
private function response_empty_resume(){ private function response_empty_resume(){
$result = [ $result = [
'target_pad' => [ 'target_pad' => [

View File

@@ -31,7 +31,7 @@ class BusinessOrIndustriesController extends Controller
}); });
} }
return response()->json($query->paginate()); return response()->json($query->paginate(config('app.paginate_per_page', 50)));
} }
/** /**

View File

@@ -24,7 +24,7 @@ class CustomersController extends Controller
->orWhere('nama', 'LIKE', '%'.$request->get('search').'%') ->orWhere('nama', 'LIKE', '%'.$request->get('search').'%')
->orWhere('kota_pelayanan', 'LIKE', '%'.$request->get('search').'%'); ->orWhere('kota_pelayanan', 'LIKE', '%'.$request->get('search').'%');
} }
return CustomersResource::collection($query->paginate()); return CustomersResource::collection($query->paginate(config('app.paginate_per_page', 50)));
} }
/** /**

View File

@@ -24,7 +24,7 @@ class ImportDatasourceController extends Controller
$search = $request->get("search"); $search = $request->get("search");
$query->where('status', 'like', "%".$search."%"); $query->where('status', 'like', "%".$search."%");
} }
return ImportDatasourceResource::collection($query->paginate()); return ImportDatasourceResource::collection($query->paginate(config('app.paginate_per_page', 50)));
} }
public function checkImportDatasource(){ public function checkImportDatasource(){

View File

@@ -7,6 +7,7 @@ use App\Models\Advertisement;
use App\Models\Customer; use App\Models\Customer;
use App\Models\SpatialPlanning; use App\Models\SpatialPlanning;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use App\Models\TourismBasedKBLI;
class LackOfPotentialController extends Controller class LackOfPotentialController extends Controller
{ {
@@ -16,11 +17,13 @@ class LackOfPotentialController extends Controller
$total_reklame = Advertisement::count(); $total_reklame = Advertisement::count();
$total_pdam = Customer::count(); $total_pdam = Customer::count();
$total_tata_ruang = SpatialPlanning::count(); $total_tata_ruang = SpatialPlanning::count();
$data_report_tourism = TourismBasedKBLI::all();
return response()->json([ return response()->json([
'total_reklame' => $total_reklame, 'total_reklame' => $total_reklame,
'total_pdam' => $total_pdam, 'total_pdam' => $total_pdam,
'total_tata_ruang' => $total_tata_ruang 'total_tata_ruang' => $total_tata_ruang,
'data_report' => $data_report_tourism,
], 200); ], 200);
}catch(\Exception $e){ }catch(\Exception $e){
return response()->json([ return response()->json([

View File

@@ -22,7 +22,7 @@ class MenusController extends Controller
$query = $query->where("name", "like", "%".$request->get("search")."%"); $query = $query->where("name", "like", "%".$request->get("search")."%");
} }
return response()->json($query->paginate()); return response()->json($query->paginate(config('app.paginate_per_page', 50)));
} }
/** /**

View File

@@ -17,7 +17,8 @@ class RequestAssignmentController extends Controller
$query = PbgTask::query()->orderBy('id', 'desc'); $query = PbgTask::query()->orderBy('id', 'desc');
if($request->has('search') && !empty($request->get("search"))){ if($request->has('search') && !empty($request->get("search"))){
$query->where('name', 'LIKE', '%'.$request->get('search').'%') $query->where('name', 'LIKE', '%'.$request->get('search').'%')
->orWhere('registration_number', 'LIKE', '%'.$request->get('search').'%'); ->orWhere('registration_number', 'LIKE', '%'.$request->get('search').'%')
->orWhere('document_number', 'LIKE', '%'.$request->get('search').'%');
} }
return RequestAssignmentResouce::collection($query->paginate()); return RequestAssignmentResouce::collection($query->paginate());
} }

View File

@@ -20,7 +20,7 @@ class RolesController extends Controller
$query = $query->where('name', 'like', '%'. $request->get('search') . '%'); $query = $query->where('name', 'like', '%'. $request->get('search') . '%');
} }
return response()->json($query->paginate()); return response()->json($query->paginate(config('app.paginate_per_page', 50)));
} }
/** /**

View File

@@ -4,6 +4,7 @@ namespace App\Http\Controllers\Api;
use App\Enums\ImportDatasourceStatus; use App\Enums\ImportDatasourceStatus;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Jobs\SyncronizeSIMBG;
use App\Models\ImportDatasource; use App\Models\ImportDatasource;
use App\Traits\GlobalApiResponse; use App\Traits\GlobalApiResponse;
use Illuminate\Support\Facades\Artisan; use Illuminate\Support\Facades\Artisan;
@@ -23,8 +24,8 @@ class ScrapingController extends Controller
} }
// run service artisan command // run service artisan command
Artisan::call("app:execute-scraping"); SyncronizeSIMBG::dispatch();
return $this->resSuccess("Success execute scraping service please wait"); return $this->resSuccess(["message" => "Success execute scraping service on background, check status for more"]);
} }
/** /**

View File

@@ -31,7 +31,7 @@ class UsersController extends Controller
if($request->has('search') && !empty($request->get("search"))){ if($request->has('search') && !empty($request->get("search"))){
$query->where('name', 'LIKE', '%'.$request->get('search').'%'); $query->where('name', 'LIKE', '%'.$request->get('search').'%');
} }
return UserResource::collection($query->paginate()); return UserResource::collection($query->paginate(config('app.paginate_per_page', 50)));
} }
public function logout(Request $request){ public function logout(Request $request){
$request->user()->tokens()->delete(); $request->user()->tokens()->delete();

View File

@@ -62,7 +62,7 @@ class AdvertisementController extends Controller
// Pastikan model ditemukan // Pastikan model ditemukan
if (!$modelInstance) { if (!$modelInstance) {
info("AdvertisementController@edit: Model tidak ditemukan."); info("AdvertisementController@edit: Model tidak ditemukan.");
return redirect()->route('advertisements.index')->with('error', 'Advertisement not found'); return redirect()->route('web.advertisements.index')->with('error', 'Advertisement not found');
} }
// Mengambil dan memetakan village_name dan district_name // Mengambil dan memetakan village_name dan district_name

View File

@@ -58,7 +58,7 @@ class TourismController extends Controller
$modelInstance = Tourism::find($id); $modelInstance = Tourism::find($id);
// Pastikan model ditemukan // Pastikan model ditemukan
if (!$modelInstance) { if (!$modelInstance) {
return redirect()->route('tourisms.index') ->with('error', 'Pariwisata tidak ditemukan'); return redirect()->route('web-tourisms.index') ->with('error', 'Pariwisata tidak ditemukan');
} }
// Mengambil dan memetakan village_name dan district_name // Mengambil dan memetakan village_name dan district_name

View File

@@ -60,7 +60,7 @@ class UmkmController extends Controller
$modelInstance = Umkm::find($id); $modelInstance = Umkm::find($id);
// Pastikan model ditemukan // Pastikan model ditemukan
if (!$modelInstance) { if (!$modelInstance) {
return redirect()->route('umkm.index')->with('error', 'Umkm not found'); return redirect()->route('web-umkm.index')->with('error', 'Umkm not found');
} }
// Mengambil dan memetakan village_name dan district_name // Mengambil dan memetakan village_name dan district_name

View File

@@ -17,12 +17,12 @@ class SyncronizeController extends Controller
} }
public function syncPbgTask(){ public function syncPbgTask(){
$res = $this->service_simbg->syncTaskList(); $res = $this->service_simbg->syncTaskPBG();
return $res; return $res;
} }
public function syncronizeTask(Request $request){ public function syncronizeTask(Request $request){
$res = $this->service_simbg->syncTaskList(); $res = $this->service_simbg->syncTaskPBG();
return redirect()->back()->with('success', 'Processing completed successfully'); return redirect()->back()->with('success', 'Processing completed successfully');
} }

View File

@@ -34,7 +34,16 @@ class BigdataResumeResource extends JsonResource
'spatial_count' => (int) $this->spatial_count, 'spatial_count' => (int) $this->spatial_count,
'spatial_sum' => number_format((float) $this->spatial_sum, 2, ',', '.'), 'spatial_sum' => number_format((float) $this->spatial_sum, 2, ',', '.'),
'issuance_realization_pbg_count' => (int) $this->issuance_realization_pbg_count,
'issuance_realization_pbg_sum' => number_format((float) $this->issuance_realization_pbg_sum, 2, ',', '.'),
'waiting_click_dpmptsp_count' => (int) $this->waiting_click_dpmptsp_count,
'waiting_click_dpmptsp_sum' => number_format((float) $this->waiting_click_dpmptsp_sum, 2, ',', '.'),
'process_in_technical_office_count' => (int) $this->process_in_technical_office_count,
'process_in_technical_office_sum' => number_format((float) $this->process_in_technical_office_sum, 2, ',', '.'),
'year' => $this->year, 'year' => $this->year,
'created_at' => $this->created_at->toDateTimeString(), 'created_at' => $this->created_at->toDateTimeString(),
]; ];

View File

@@ -0,0 +1,27 @@
<?php
namespace App\Jobs;
use App\Services\GoogleSheetService;
use App\Services\ServiceSIMBG;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
class SyncronizeSIMBG implements ShouldQueue
{
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
public function __construct()
{
}
public function handle(): void
{
$serviceSIMBG = app(ServiceSIMBG::class);
$serviceSIMBG->syncTaskPBG();
}
}

View File

@@ -23,7 +23,13 @@ class BigdataResume extends Model
'non_business_sum', 'non_business_sum',
'spatial_count', 'spatial_count',
'spatial_sum', 'spatial_sum',
'year' 'year',
'waiting_click_dpmptsp_count',
'waiting_click_dpmptsp_sum',
'issuance_realization_pbg_count',
'issuance_realization_pbg_sum',
'process_in_technical_office_count',
'process_in_technical_office_sum',
]; ];
public function importDatasource() public function importDatasource()
@@ -31,7 +37,7 @@ class BigdataResume extends Model
return $this->belongsTo(ImportDatasource::class, 'import_datasource_id'); return $this->belongsTo(ImportDatasource::class, 'import_datasource_id');
} }
public static function generateResumeData($import_datasource_id, $year){ public static function generateResumeData($import_datasource_id, $year, $data_setting){
$stats = PbgTask::with(['googleSheet', 'pbg_task_retributions']) $stats = PbgTask::with(['googleSheet', 'pbg_task_retributions'])
->leftJoin('pbg_task_retributions as ptr', 'pbg_task.uuid', '=', 'ptr.pbg_task_uid') ->leftJoin('pbg_task_retributions as ptr', 'pbg_task.uuid', '=', 'ptr.pbg_task_uid')
->leftJoin('pbg_task_google_sheet as ptgs', 'pbg_task.registration_number', '=', 'ptgs.no_registrasi') ->leftJoin('pbg_task_google_sheet as ptgs', 'pbg_task.registration_number', '=', 'ptgs.no_registrasi')
@@ -82,9 +88,15 @@ class BigdataResume extends Model
$potention_total = $query_potention->total_retribution ?? 0; $potention_total = $query_potention->total_retribution ?? 0;
$query_spatial_plannings = once(function () use ($year) { $query_spatial_plannings = once(function () use ($year) {
$query = PbgTask::join('spatial_plannings as sp', 'pbg_task.document_number', '=', 'sp.number') $query = PbgTask::leftJoin('spatial_plannings as sp', 'pbg_task.document_number', '=', 'sp.number')
->join('pbg_task_retributions as ptr', 'ptr.pbg_task_uid', '=', 'pbg_task.uuid') ->leftJoin('pbg_task_retributions as ptr', 'ptr.pbg_task_uid', '=', 'pbg_task.uuid')
->selectRaw('COUNT(DISTINCT pbg_task.id) as task_count, SUM(ptr.nilai_retribusi_bangunan) as total_retribution'); ->selectRaw('
CASE
WHEN COUNT(DISTINCT sp.id) > 0 THEN COUNT(DISTINCT sp.id)
ELSE (SELECT COUNT(*) FROM spatial_plannings)
END as task_count,
SUM(CASE WHEN sp.id IS NOT NULL AND ptr.id IS NOT NULL THEN ptr.nilai_retribusi_bangunan ELSE 0 END) as total_retribution
');
if ($year !== 'all') { if ($year !== 'all') {
$query->whereYear('pbg_task.task_created_at', (int) $year); $query->whereYear('pbg_task.task_created_at', (int) $year);
@@ -94,7 +106,7 @@ class BigdataResume extends Model
}); });
$spatial_planning_count = $query_spatial_plannings->task_count ?? 0; $spatial_planning_count = $query_spatial_plannings->task_count ?? 0;
$spatial_planning_total = $query_spatial_plannings->total_retribution ?? 0; $spatial_planning_total = $query_spatial_plannings->total_retribution;
$potention_count -= $spatial_planning_count; $potention_count -= $spatial_planning_count;
$potention_total -= $spatial_planning_total; $potention_total -= $spatial_planning_total;
@@ -113,7 +125,13 @@ class BigdataResume extends Model
'business_sum' => $business_total ?? 0.00, 'business_sum' => $business_total ?? 0.00,
'non_business_count' => $non_business_count ?? 0, 'non_business_count' => $non_business_count ?? 0,
'non_business_sum' => $non_business_total ?? 0.00, 'non_business_sum' => $non_business_total ?? 0.00,
'year' => $year 'year' => $year,
'waiting_click_dpmptsp_count' => $data_setting['MENUNGGU_KLIK_DPMPTSP_COUNT'] ?? 0,
'waiting_click_dpmptsp_sum' => $data_setting['MENUNGGU_KLIK_DPMPTSP_SUM'] ?? 0.00,
'issuance_realization_pbg_count' => $data_setting['REALISASI_TERBIT_PBG_COUNT'] ?? 0,
'issuance_realization_pbg_sum' => $data_setting['REALISASI_TERBIT_PBG_SUM'] ?? 0.00,
'process_in_technical_office_count' => $data_setting['PROSES_DINAS_TEKNIS_COUNT'] ?? 0,
'process_in_technical_office_sum' => $data_setting['PROSES_DINAS_TEKNIS_SUM'] ??0.00,
]); ]);
} }
} }

View File

@@ -9,6 +9,8 @@ use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\View; use Illuminate\Support\Facades\View;
use Illuminate\Support\ServiceProvider; use Illuminate\Support\ServiceProvider;
use Carbon\Carbon; use Carbon\Carbon;
use App\Services\ServiceSIMBG;
use App\Services\GoogleSheetService;
class AppServiceProvider extends ServiceProvider class AppServiceProvider extends ServiceProvider
{ {
@@ -17,7 +19,9 @@ class AppServiceProvider extends ServiceProvider
*/ */
public function register(): void public function register(): void
{ {
// $this->app->singleton(ServiceSIMBG::class, function ($app) {
return new ServiceSIMBG($app->make(GoogleSheetService::class));
});
} }
/** /**

View File

@@ -13,6 +13,7 @@ class GoogleSheetService
protected $client; protected $client;
protected $service; protected $service;
protected $spreadsheetID; protected $spreadsheetID;
protected $service_sheets;
public function __construct() public function __construct()
{ {
$this->client = new Google_Client(); $this->client = new Google_Client();

View File

@@ -15,6 +15,9 @@ use App\Traits\GlobalApiResponse;
use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Log;
use Carbon\Carbon; use Carbon\Carbon;
use App\Services\ServiceClient; use App\Services\ServiceClient;
use App\Services\GoogleSheetService;
use App\Models\DataSetting;
use App\Models\PbgTaskGoogleSheet;
class ServiceSIMBG class ServiceSIMBG
{ {
@@ -24,10 +27,11 @@ class ServiceSIMBG
private $simbg_host; private $simbg_host;
private $fetch_per_page; private $fetch_per_page;
private $service_client; private $service_client;
private $googleSheetService;
/** /**
* Create a new class instance. * Create a new class instance.
*/ */
public function __construct() public function __construct(GoogleSheetService $googleSheetService)
{ {
$settings = GlobalSetting::whereIn('key', [ $settings = GlobalSetting::whereIn('key', [
'SIMBG_EMAIL', 'SIMBG_PASSWORD', 'SIMBG_HOST', 'FETCH_PER_PAGE' 'SIMBG_EMAIL', 'SIMBG_PASSWORD', 'SIMBG_HOST', 'FETCH_PER_PAGE'
@@ -39,6 +43,7 @@ class ServiceSIMBG
$this->fetch_per_page = trim((string) ($settings['FETCH_PER_PAGE'] ?? "")); $this->fetch_per_page = trim((string) ($settings['FETCH_PER_PAGE'] ?? ""));
$this->service_client = new ServiceClient($this->simbg_host); $this->service_client = new ServiceClient($this->simbg_host);
$this->googleSheetService = $googleSheetService;
} }
public function getToken(){ public function getToken(){
@@ -112,12 +117,173 @@ class ServiceSIMBG
} }
} }
public function syncTaskList() public function syncTaskPBG()
{ {
try { try {
$importDatasource = ImportDatasource::create([ $importDatasource = ImportDatasource::create([
'status' => ImportDatasourceStatus::Processing->value, 'status' => ImportDatasourceStatus::Processing->value,
]); ]);
// sync google sheet first
$totalRowCount = $this->googleSheetService->getLastRowByColumn("C");
$sheetData = $this->googleSheetService->getSheetDataCollection($totalRowCount);
$sheet_big_data = $this->googleSheetService->get_data_by_sheet();
$data_setting_result = []; // Initialize result storage
$found_section = null; // Track which section is found
foreach ($sheet_big_data as $row) {
// Check for section headers
if (in_array("•PROSES PENERBITAN:", $row)) {
$found_section = "MENUNGGU_KLIK_DPMPTSP";
} elseif (in_array("•BERKAS AKTUAL TERVERIFIKASI DINAS TEKNIS 2024:", $row)) {
$found_section = "REALISASI_TERBIT_PBG";
} elseif (in_array("•TERPROSES DI DPUTR: belum selesai rekomtek'", $row)) {
$found_section = "PROSES_DINAS_TEKNIS";
}
// If a section is found and we reach "Grand Total", save the corresponding values
if ($found_section && isset($row[0]) && trim($row[0]) === "Grand Total") {
if ($found_section === "MENUNGGU_KLIK_DPMPTSP") {
$data_setting_result["MENUNGGU_KLIK_DPMPTSP_COUNT"] = $row[2] ?? null;
$data_setting_result["MENUNGGU_KLIK_DPMPTSP_SUM"] = $row[3] ?? null;
} elseif ($found_section === "REALISASI_TERBIT_PBG") {
$data_setting_result["REALISASI_TERBIT_PBG_COUNT"] = $row[2] ?? null;
$data_setting_result["REALISASI_TERBIT_PBG_SUM"] = $row[4] ?? null;
} elseif ($found_section === "PROSES_DINAS_TEKNIS") {
$data_setting_result["PROSES_DINAS_TEKNIS_COUNT"] = $row[2] ?? null;
$data_setting_result["PROSES_DINAS_TEKNIS_SUM"] = $row[3] ?? null;
}
// Reset section tracking after capturing "Grand Total"
$found_section = null;
}
}
foreach ($data_setting_result as $key => $value) {
DataSetting::updateOrInsert(
["key" => $key], // Find by key
["value" => $value] // Update or insert value
);
}
$mapToUpsert = [];
$count = 0;
foreach($sheetData as $data){
$mapToUpsert[] =
[
'no_registrasi' => $data['no__registrasi'] ?? null,
'jenis_konsultasi' => $data['jenis_konsultasi'] ?? null,
'fungsi_bg' => $data['fungsi_bg'] ?? null,
'tgl_permohonan' => $this->convertToDate($data['tgl_permohonan']),
'status_verifikasi' => $data['status_verifikasi'] ?? null,
'status_permohonan' => $this->convertToDate($data['status_permohonan']),
'alamat_pemilik' => $data['alamat_pemilik'] ?? null,
'no_hp' => $data['no__hp'] ?? null,
'email' => $data['e_mail'] ?? null,
'tanggal_catatan' => $this->convertToDate($data['tanggal_catatan']),
'catatan_kekurangan_dokumen' => $data['catatan_kekurangan_dokumen'] ?? null,
'gambar' => $data['gambar'] ?? null,
'krk_kkpr' => $data['krk_kkpr'] ?? null,
'no_krk' => $data['no__krk'] ?? null,
'lh' => $data['lh'] ?? null,
'ska' => $data['ska'] ?? null,
'keterangan' => $data['keterangan'] ?? null,
'helpdesk' => $data['helpdesk'] ?? null,
'pj' => $data['pj'] ?? null,
'kepemilikan' => $data['kepemilikan'] ?? null,
'potensi_taru' => $data['potensi_taru'] ?? null,
'validasi_dinas' => $data['validasi_dinas'] ?? null,
'kategori_retribusi' => $data['kategori_retribusi'] ?? null,
'no_urut_ba_tpt' => $data['no__urut_ba_tpt__2024_0001_'] ?? null,
'tanggal_ba_tpt' => $this->convertToDate($data['tanggal_ba_tpt']),
'no_urut_ba_tpa' => $data['no__urut_ba_tpa'] ?? null,
'tanggal_ba_tpa' => $this->convertToDate($data['tanggal_ba_tpa']),
'no_urut_skrd' => $data['no__urut_skrd__2024_0001_'] ?? null,
'tanggal_skrd' => $this->convertToDate($data['tanggal_skrd']),
'ptsp' => $data['ptsp'] ?? null,
'selesai_terbit' => $data['selesai_terbit'] ?? null,
'tanggal_pembayaran' => $this->convertToDate($data['tanggal_pembayaran__yyyy_mm_dd_']),
'format_sts' => $data['format_sts'] ?? null,
'tahun_terbit' => (int) $data['tahun_terbit'] ?? null,
'tahun_berjalan' => (int) $data['tahun_berjalan'] ?? null,
'kelurahan' => $data['kelurahan'] ?? null,
'kecamatan' => $data['kecamatan'] ?? null,
'lb' => $this->convertToDecimal($data['lb']) ?? null,
'tb' => $this->convertToDecimal($data['tb']) ?? null,
'jlb' => (int) $data['jlb'] ?? null,
'unit' => (int) $data['unit'] ?? null,
'usulan_retribusi' => (int) $data['usulan_retribusi'] ?? null,
'nilai_retribusi_keseluruhan_simbg' => $this->convertToDecimal($data['nilai_retribusi_keseluruhan__simbg_']) ?? null,
'nilai_retribusi_keseluruhan_pad' => $this->convertToDecimal($data['nilai_retribusi_keseluruhan__pad_']) ?? null,
'denda' => $this->convertToDecimal($data['denda']) ?? null,
'latitude' => $data['latitude'] ?? null,
'longitude' => $data['longitude'] ?? null,
'nik_nib' => $data['nik_nib'] ?? null,
'dok_tanah' => $data['dok__tanah'] ?? null,
'temuan' => $data['temuan'] ?? null,
];
}
$batchSize = 1000;
$chunks = array_chunk($mapToUpsert, $batchSize);
foreach($chunks as $chunk){
PbgTaskGoogleSheet::upsert($chunk, ["no_registrasi"],[
'jenis_konsultasi',
'nama_pemilik',
'lokasi_bg',
'fungsi_bg',
'nama_bangunan',
'tgl_permohonan',
'status_verifikasi',
'status_permohonan',
'alamat_pemilik',
'no_hp',
'email',
'tanggal_catatan',
'catatan_kekurangan_dokumen',
'gambar',
'krk_kkpr',
'no_krk',
'lh',
'ska',
'keterangan',
'helpdesk',
'pj',
'kepemilikan',
'potensi_taru',
'validasi_dinas',
'kategori_retribusi',
'no_urut_ba_tpt',
'tanggal_ba_tpt',
'no_urut_ba_tpa',
'tanggal_ba_tpa',
'no_urut_skrd',
'tanggal_skrd',
'ptsp',
'selesai_terbit',
'tanggal_pembayaran',
'format_sts',
'tahun_terbit',
'tahun_berjalan',
'kelurahan',
'kecamatan',
'lb',
'tb',
'jlb',
'unit',
'usulan_retribusi',
'nilai_retribusi_keseluruhan_simbg',
'nilai_retribusi_keseluruhan_pad',
'denda',
'latitude',
'longitude',
'nik_nib',
'dok_tanah',
'temuan',
]);
}
$initResToken = $this->getToken(); $initResToken = $this->getToken();
if (empty($initResToken->original['data']['token']['access'])) { if (empty($initResToken->original['data']['token']['access'])) {
@@ -216,8 +382,8 @@ class ServiceSIMBG
} }
} }
BigdataResume::generateResumeData($importDatasource->id, "all"); BigdataResume::generateResumeData($importDatasource->id, "all", $data_setting_result);
BigdataResume::generateResumeData($importDatasource->id, now()->year); BigdataResume::generateResumeData($importDatasource->id, now()->year, $data_setting_result);
// Final update after processing all pages // Final update after processing all pages
$importDatasource->update([ $importDatasource->update([
@@ -323,5 +489,59 @@ class ServiceSIMBG
throw $e; throw $e;
} }
} }
protected function convertToDecimal(?string $value): ?float
{
if (empty($value)) {
return null; // Return null if the input is empty
}
// Remove all non-numeric characters except comma and dot
$value = preg_replace('/[^0-9,\.]/', '', $value);
// If the number contains both dot (.) and comma (,)
if (strpos($value, '.') !== false && strpos($value, ',') !== false) {
$value = str_replace('.', '', $value); // Remove thousands separator
$value = str_replace(',', '.', $value); // Convert decimal separator to dot
}
// If only a dot is present (assumed as thousands separator)
elseif (strpos($value, '.') !== false) {
$value = str_replace('.', '', $value); // Remove all dots (treat as thousands separators)
}
// If only a comma is present (assumed as decimal separator)
elseif (strpos($value, ',') !== false) {
$value = str_replace(',', '.', $value); // Convert comma to dot (decimal separator)
}
// Ensure the value is numeric before returning
return is_numeric($value) ? (float) number_format((float) $value, 2, '.', '') : null;
}
protected function convertToInteger($value) {
// Check if the value is an empty string, and return null if true
if (trim($value) === "") {
return null;
}
// Otherwise, cast to integer
return (int) $value;
}
protected function convertToDate($dateString)
{
try {
// Check if the string is empty
if (empty($dateString)) {
return null;
}
// Try to parse the date string
$date = Carbon::parse($dateString);
// Return the Carbon instance
return $date->format('Y-m-d');
} catch (Exception $e) {
// Return null if an error occurs during parsing
return null;
}
}
} }

View File

@@ -123,5 +123,6 @@ return [
'store' => env('APP_MAINTENANCE_STORE', 'database'), 'store' => env('APP_MAINTENANCE_STORE', 'database'),
], ],
'api_url' => env('API_URL', 'http://localhost:8000') 'api_url' => env('API_URL', 'http://localhost:8000'),
'paginate_per_page' => 50
]; ];

View File

@@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('bigdata_resumes', function (Blueprint $table) {
$table->integer('waiting_click_dpmptsp_count')->default(0);
$table->decimal('waiting_click_dpmptsp_sum', 20,2)->default(0);
$table->integer('issuance_realization_pbg_count')->default(0);
$table->decimal('issuance_realization_pbg_sum', 20,2)->default(0);
$table->integer('process_in_technical_office_count')->default(0);
$table->decimal('process_in_technical_office_sum', 20,2)->default(0);
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('bigdata_resumes', function (Blueprint $table) {
$table->dropColumn('waiting_click_dpmptsp_count');
$table->dropColumn('waiting_click_dpmptsp_sum');
$table->dropColumn('issuance_realization_pbg_count');
$table->dropColumn('issuance_realization_pbg_sum');
$table->dropColumn('process_in_technical_office_count');
$table->dropColumn('process_in_technical_office_sum');
});
}
};

View File

@@ -74,6 +74,13 @@ class UsersRoleMenuSeeder extends Seeder
"icon" => "mingcute:report-line", "icon" => "mingcute:report-line",
"parent_id" => null, "parent_id" => null,
"sort_order" => 6, "sort_order" => 6,
],
[
"name" => "Neng Bedas",
"url" => "/chat",
"icon" => "mingcute:wechat-line",
"parent_id" => null,
"sort_order" => 7,
] ]
]; ];
@@ -92,6 +99,7 @@ class UsersRoleMenuSeeder extends Seeder
$dataSettings = Menu::where('name', 'Data Settings')->first(); $dataSettings = Menu::where('name', 'Data Settings')->first();
$data = Menu::where('name', 'Data')->first(); $data = Menu::where('name', 'Data')->first();
$laporan = Menu::where('name', 'Laporan')->first(); $laporan = Menu::where('name', 'Laporan')->first();
$chat_bedas = Menu::where('name', 'Neng Bedas')->first();
// create children menu // create children menu
$children_menus = [ $children_menus = [
@@ -167,7 +175,7 @@ class UsersRoleMenuSeeder extends Seeder
], ],
[ [
"name" => "Reklame", "name" => "Reklame",
"url" => "advertisements.index", "url" => "web.advertisements.index",
"icon" => null, "icon" => null,
"parent_id" => $data->id, "parent_id" => $data->id,
"sort_order" => 2, "sort_order" => 2,
@@ -181,21 +189,21 @@ class UsersRoleMenuSeeder extends Seeder
], ],
[ [
"name" => "UMKM", "name" => "UMKM",
"url" => "umkm.index", "url" => "web-umkm.index",
"icon" => null, "icon" => null,
"parent_id" => $data->id, "parent_id" => $data->id,
"sort_order" => 4, "sort_order" => 4,
], ],
[ [
"name" => "Pariwisata", "name" => "Pariwisata",
"url" => "tourisms.index", "url" => "web-tourisms.index",
"icon" => null, "icon" => null,
"parent_id" => $data->id, "parent_id" => $data->id,
"sort_order" => 5, "sort_order" => 5,
], ],
[ [
"name" => "Tata Ruang", "name" => "Tata Ruang",
"url" => "spatial-plannings.index", "url" => "web-spatial-plannings.index",
"icon" => null, "icon" => null,
"parent_id" => $data->id, "parent_id" => $data->id,
"sort_order" => 6, "sort_order" => 6,
@@ -209,7 +217,7 @@ class UsersRoleMenuSeeder extends Seeder
], ],
[ [
"name" => "Lap Pariwisata", "name" => "Lap Pariwisata",
"url" => "tourisms.index", "url" => "tourisms-report.index",
"icon" => null, "icon" => null,
"parent_id" => $laporan->id, "parent_id" => $laporan->id,
"sort_order" => 1, "sort_order" => 1,
@@ -221,6 +229,13 @@ class UsersRoleMenuSeeder extends Seeder
"parent_id" => $laporan->id, "parent_id" => $laporan->id,
"sort_order" => 2, "sort_order" => 2,
], ],
[
"name" => "Chat",
"url" => "main-chatbot.index",
"icon" => null,
"parent_id" => $chat_bedas->id,
"sort_order" => 1,
],
]; ];
foreach ($children_menus as $child_menu) { foreach ($children_menus as $child_menu) {
@@ -245,6 +260,7 @@ class UsersRoleMenuSeeder extends Seeder
$pdam = Menu::where('name', 'PDAM')->first(); $pdam = Menu::where('name', 'PDAM')->first();
$peta = Menu::where('name', 'PETA')->first(); $peta = Menu::where('name', 'PETA')->first();
$bigdata_resume = Menu::where('name', 'Lap Pimpinan')->first(); $bigdata_resume = Menu::where('name', 'Lap Pimpinan')->first();
$chatbot = Menu::where('name', 'Chat')->first();
// Superadmin gets all menus // Superadmin gets all menus
$superadmin->menus()->sync([ $superadmin->menus()->sync([
@@ -255,6 +271,7 @@ class UsersRoleMenuSeeder extends Seeder
$dataSettings->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false], $dataSettings->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false],
$data->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false], $data->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false],
$laporan->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false], $laporan->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false],
$chat_bedas->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false],
// children // children
$dashboard_pimpinan->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], $dashboard_pimpinan->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$dashboard_pbg->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], $dashboard_pbg->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
@@ -274,6 +291,7 @@ class UsersRoleMenuSeeder extends Seeder
$pdam->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], $pdam->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$peta->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], $peta->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$bigdata_resume->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], $bigdata_resume->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$chatbot->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
]); ]);
// Admin gets limited menus // Admin gets limited menus

View File

@@ -0,0 +1,21 @@
CREATE VIEW v_advertisements AS
SELECT
a.no,
a.business_name,
a.npwpd,
a.advertisement_type,
a.advertisement_content,
a.business_address,
a.advertisement_location,
v.village_name AS village_name,
d.district_name AS district_name,
a.length,
a.width,
a.viewing_angle,
a.face,
a.area,
a.angle,
a.contact
FROM advertisements a
JOIN villages v ON a.village_code = v.village_code
JOIN districts d ON a.district_code = d.district_code;

View File

@@ -0,0 +1,8 @@
CREATE VIEW v_tourisms_based_kbli AS
SELECT kbli_title, total_records
FROM (
SELECT kbli, kbli_title, COUNT(*) AS total_records
FROM tourisms
GROUP BY kbli, kbli_title
) AS subquery
ORDER BY total_records DESC;

View File

@@ -0,0 +1,29 @@
CREATE VIEW v_tourisms AS
SELECT
t.project_id,
t.project_type_id,
t.nib,
t.business_name,
t.oss_publication_date,
t.investment_status_description,
t.business_form,
t.project_risk,
t.project_name,
t.business_scale,
t.business_address,
v.village_name as village_name,
d.district_name as district_name,
t.longitude,
t.latitude,
t.project_submission_date,
t.kbli_title,
t.supervisory_sector,
t.user_name,
t.email,
t.contact,
t.land_area_in_m2,
t.investment_amount,
t.tki
FROM tourisms t
JOIN villages v on t.village_code = v.village_code
JOIN districts d on t.district_code = d.district_code;

View File

@@ -0,0 +1,28 @@
CREATE VIEW v_umkms AS
SELECT
u.business_address,
u.business_contact,
u.business_desc,
bf.business_form,
u.business_id_number,
u.business_name,
bs.business_scale,
u.business_type,
u.created_at,
d.district_name,
u.land_area,
u.number_of_employee,
u.owner_address,
u.owner_contact,
u.owner_id,
u.owner_name,
ps.permit_status,
u.revenue,
u.updated_at,
v.village_name
FROM umkms u
JOIN business_form bf on u.business_form_id = bf.id
JOIN permit_status ps on u.permit_status_id = ps.id
JOIn business_scale bs on u.business_scale_id = bs.id
JOIN villages v on u.village_code = v.village_code
JOIN districts d on u.district_code = v.district_code;

34
deploy.sh Executable file
View File

@@ -0,0 +1,34 @@
GIT_BRANCH="dev"
PHP_VERSION="php8.3"
echo "🚀 Starting deployment..."
php artisan down
echo "📥 Pulling latest changes from Git..."
git fetch origin $GIT_BRANCH
git reset --hard origin/$GIT_BRANCH
git pull origin $GIT_BRANCH
echo "⚡ Installing NPM dependencies and building assets..."
npm ci --no-audit --no-fund
npm run build
echo "📦 Installing composer dependencies..."
COMPOSER_ALLOW_SUPERUSER=1 composer install --no-interaction --optimize-autoloader
echo "🗄️ Running migrations..."
php artisan migrate --force
echo "⚡ Optimizing application..."
php artisan optimize:clear
echo "🔄 Restarting PHP service..."
systemctl restart $PHP_VERSION-fpm
echo "🔁 Restarting Supervisor queue workers..."
supervisorctl stop all
supervisorctl reload
supervisorctl start all
php artisan up
echo "🚀 Deployment completed successfully!"

View File

@@ -30,29 +30,34 @@ class BigdataResume {
initTableDataSettings() { initTableDataSettings() {
let tableContainer = document.getElementById("table-bigdata-resumes"); let tableContainer = document.getElementById("table-bigdata-resumes");
// Create a new Grid.js instance only if it doesn't exist
this.table = new Grid({ this.table = new Grid({
columns: [ columns: [
{ name: "ID" }, { name: "ID" },
{ name: "Potention Count" }, { name: "Jumlah Potensi" },
{ name: "Potention Sum" }, { name: "Total Potensi" },
{ name: "Non Verified Count" }, { name: "Jumlah Berkas Belum Terverifikasi" },
{ name: "Non Verified Sum" }, { name: "Total Berkas Belum Terverifikasi" },
{ name: "Verified Count" }, { name: "Jumlah Berkas Terverifikasi" },
{ name: "Verified Sum" }, { name: "Total Berkas Terverifikasi" },
{ name: "Business Count" }, { name: "Jumlah Usaha" },
{ name: "Business Sum" }, { name: "Total Usaha" },
{ name: "Non Business Count" }, { name: "Jumlah Non Usaha" },
{ name: "Non Business Sum" }, { name: "Total Non Usaha" },
{ name: "Spatial Sum" }, { name: "Jumlah Tata Ruang" },
{ name: "Spatial Count" }, { name: "Total Tata Ruang" },
{ name: "Jumlah Menunggu Klik DPMPTSP" },
{ name: "Total Menunggu Klik DPMPTSP" },
{ name: "Jumlah Realisasi Terbit PBG" },
{ name: "Total Realisasi Terbit PBG" },
{ name: "Jumlah Proses Dinas Teknis" },
{ name: "Total Proses Dinas Teknis" },
{ {
name: "Created", name: "Created",
attributes: { style: "width: 200px; white-space: nowrap;" }, // Set width dynamically attributes: { style: "width: 200px; white-space: nowrap;" }, // Set width dynamically
}, },
], ],
pagination: { pagination: {
limit: 15, limit: 50,
server: { server: {
url: (prev, page) => url: (prev, page) =>
`${prev}${prev.includes("?") ? "&" : "?"}page=${ `${prev}${prev.includes("?") ? "&" : "?"}page=${
@@ -74,8 +79,8 @@ class BigdataResume {
.getAttribute("content")}`, .getAttribute("content")}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
then: (data) => then: (data) => {
data.data.map((item) => [ return data.data.map((item) => [
item.id, item.id,
item.potention_count, item.potention_count,
addThousandSeparators(item.potention_sum), addThousandSeparators(item.potention_sum),
@@ -89,8 +94,19 @@ class BigdataResume {
addThousandSeparators(item.non_business_sum), addThousandSeparators(item.non_business_sum),
item.spatial_count, item.spatial_count,
addThousandSeparators(item.spatial_sum), addThousandSeparators(item.spatial_sum),
item.waiting_click_dpmptsp_count,
addThousandSeparators(item.waiting_click_dpmptsp_sum),
item.issuance_realization_pbg_count,
addThousandSeparators(
item.issuance_realization_pbg_sum
),
item.process_in_technical_office_count,
addThousandSeparators(
item.process_in_technical_office_sum
),
moment(item.created_at).format("YYYY-MM-DD H:mm:ss"), moment(item.created_at).format("YYYY-MM-DD H:mm:ss"),
]), ]);
},
total: (data) => data.total, total: (data) => data.total,
}, },
}).render(tableContainer); }).render(tableContainer);

View File

@@ -64,7 +64,7 @@ class BusinessIndustries {
}, },
], ],
pagination: { pagination: {
limit: 15, limit: 50,
server: { server: {
url: (prev, page) => url: (prev, page) =>
`${prev}${prev.includes("?") ? "&" : "?"}page=${ `${prev}${prev.includes("?") ? "&" : "?"}page=${

View File

@@ -53,7 +53,7 @@ class Customers {
}, },
], ],
pagination: { pagination: {
limit: 15, limit: 50,
server: { server: {
url: (prev, page) => url: (prev, page) =>
`${prev}${prev.includes("?") ? "&" : "?"}page=${ `${prev}${prev.includes("?") ? "&" : "?"}page=${

View File

@@ -16,6 +16,19 @@ class LackOfPotential {
this.pdamCount = this.allCountData.total_pdam ?? 0; this.pdamCount = this.allCountData.total_pdam ?? 0;
this.tataRuangCount = this.allCountData.total_tata_ruang ?? 0; this.tataRuangCount = this.allCountData.total_tata_ruang ?? 0;
let dataReportTourism = this.allCountData.data_report;
this.totalVilla = dataReportTourism
.filter((item) => item.kbli_title.toLowerCase() === "vila")
.reduce((sum, item) => sum + item.total_records, 0);
this.totalRestoran = dataReportTourism
.filter((item) => item.kbli_title.toLowerCase() === "restoran")
.reduce((sum, item) => sum + item.total_records, 0);
this.totalPariwisata = dataReportTourism.reduce(
(sum, item) => sum + item.total_records,
0
);
this.bigTargetPAD = new Big(this.totalTargetPAD ?? 0); this.bigTargetPAD = new Big(this.totalTargetPAD ?? 0);
this.bigTotalPotensi = new Big(this.totalPotensi.total ?? 0); this.bigTotalPotensi = new Big(this.totalPotensi.total ?? 0);
this.bigTotalLackPotential = this.bigTargetPAD.minus( this.bigTotalLackPotential = this.bigTargetPAD.minus(
@@ -140,6 +153,15 @@ class LackOfPotential {
document.getElementById("pdam-count").innerText = this.pdamCount; document.getElementById("pdam-count").innerText = this.pdamCount;
document.getElementById("pbb-bangunan-count").innerText = document.getElementById("pbb-bangunan-count").innerText =
this.tataRuangCount; this.tataRuangCount;
document.getElementById("tata-ruang-count").innerText =
this.tataRuangCount;
document.getElementById("tata-ruang-usaha-count").innerText =
this.tataRuangCount;
document.getElementById("restoran-count").innerText =
this.totalRestoran;
document.getElementById("villa-count").innerText = this.totalVilla;
document.getElementById("pariwisata-count").innerText =
this.totalPariwisata;
} }
} }
document.addEventListener("DOMContentLoaded", async function (e) { document.addEventListener("DOMContentLoaded", async function (e) {

View File

@@ -3,7 +3,7 @@ import GlobalConfig from "../../global-config";
document.addEventListener("DOMContentLoaded", function () { document.addEventListener("DOMContentLoaded", function () {
const saveButton = document.querySelector(".modal-footer .btn-primary"); const saveButton = document.querySelector(".modal-footer .btn-primary");
const modalButton = document.querySelector(".btn-modal"); const modalButton = document.querySelector(".btn-modal");
const form = document.querySelector("form#create-update-form"); const form = document.querySelector("form#create-update-form");
var authLogo = document.querySelector(".auth-logo"); var authLogo = document.querySelector(".auth-logo");
if (!saveButton || !form) return; if (!saveButton || !form) return;
@@ -16,10 +16,10 @@ document.addEventListener("DOMContentLoaded", function () {
Loading... Loading...
`; `;
const isEdit = saveButton.classList.contains("btn-edit"); const isEdit = saveButton.classList.contains("btn-edit");
const formData = new FormData(form) const formData = new FormData(form);
const toast = document.getElementById('toastEditUpdate'); const toast = document.getElementById("toastEditUpdate");
const toastBody = toast.querySelector('.toast-body'); const toastBody = toast.querySelector(".toast-body");
const toastHeader = toast.querySelector('.toast-header small'); const toastHeader = toast.querySelector(".toast-header small");
const data = {}; const data = {};
@@ -27,9 +27,9 @@ document.addEventListener("DOMContentLoaded", function () {
formData.forEach((value, key) => { formData.forEach((value, key) => {
data[key] = value; data[key] = value;
}); });
const url = form.getAttribute("action"); const url = form.getAttribute("action");
const method = isEdit ? "PUT" : "POST"; const method = isEdit ? "PUT" : "POST";
fetch(url, { fetch(url, {
@@ -40,99 +40,103 @@ document.addEventListener("DOMContentLoaded", function () {
.querySelector('meta[name="api-token"]') .querySelector('meta[name="api-token"]')
.getAttribute("content")}`, .getAttribute("content")}`,
"Content-Type": "application/json", "Content-Type": "application/json",
} },
}) })
.then(response => response.json()) .then((response) => response.json())
.then(data => { .then((data) => {
if (!data.errors) { if (!data.errors) {
// Remove existing icon (if any) before adding the new one // Remove existing icon (if any) before adding the new one
if (authLogo) { if (authLogo) {
// Hapus ikon yang sudah ada jika ada // Hapus ikon yang sudah ada jika ada
const existingIcon = authLogo.querySelector('.bx'); const existingIcon = authLogo.querySelector(".bx");
if (existingIcon) { if (existingIcon) {
authLogo.removeChild(existingIcon); authLogo.removeChild(existingIcon);
} }
// Buat ikon baru
const icon = document.createElement('i');
icon.classList.add('bx', 'bxs-check-square');
icon.style.fontSize = '25px';
icon.style.color = 'green'; // Pastikan 'green' dalam bentuk string
// Tambahkan ikon ke dalam auth-logo
authLogo.appendChild(icon);
}
// Set success message for the toast
toastBody.textContent = isEdit ? "Data updated successfully!" : "Data created successfully!";
toast.classList.add('show'); // Show the toast
setTimeout(() => {
toast.classList.remove('show'); // Hide the toast after 3 seconds
}, 2000);
setTimeout(() => {
window.location.href = '/data/advertisements';
}, 1000);
} else {
if (authLogo) {
// Hapus ikon yang sudah ada jika ada
const existingIcon = authLogo.querySelector('.bx');
if (existingIcon) {
authLogo.removeChild(existingIcon);
}
// Buat ikon baru
const icon = document.createElement('i');
icon.classList.add('bx', 'bxs-error-alt');
icon.style.fontSize = '25px';
icon.style.color = 'red'; // Pastikan 'green' dalam bentuk string
// Tambahkan ikon ke dalam auth-logo
authLogo.appendChild(icon);
}
// Buat ikon baru
const icon = document.createElement("i");
icon.classList.add("bx", "bxs-check-square");
icon.style.fontSize = "25px";
icon.style.color = "green"; // Pastikan 'green' dalam bentuk string
// Tambahkan ikon ke dalam auth-logo
authLogo.appendChild(icon);
}
// Set success message for the toast
toastBody.textContent = isEdit
? "Data updated successfully!"
: "Data created successfully!";
toast.classList.add("show"); // Show the toast
setTimeout(() => {
toast.classList.remove("show"); // Hide the toast after 3 seconds
}, 2000);
setTimeout(() => {
window.location.href = "/data/web-advertisements";
}, 1000);
} else {
if (authLogo) {
// Hapus ikon yang sudah ada jika ada
const existingIcon = authLogo.querySelector(".bx");
if (existingIcon) {
authLogo.removeChild(existingIcon);
}
// Buat ikon baru
const icon = document.createElement("i");
icon.classList.add("bx", "bxs-error-alt");
icon.style.fontSize = "25px";
icon.style.color = "red"; // Pastikan 'green' dalam bentuk string
// Tambahkan ikon ke dalam auth-logo
authLogo.appendChild(icon);
}
// Enable button and reset its text on error
modalButton.disabled = false;
modalButton.innerHTML = isEdit ? "Update" : "Create";
// Set error message for the toast
toastBody.textContent =
"Failed: " + (data.message || "Something went wrong");
toast.classList.add("show"); // Show the toast
setTimeout(() => {
toast.classList.remove("show"); // Hide the toast after 3 seconds
}, 3000);
}
})
.catch((errors) => {
if (authLogo) {
// Hapus ikon yang sudah ada jika ada
const existingIcon = authLogo.querySelector(".bx");
if (existingIcon) {
authLogo.removeChild(existingIcon);
}
// Buat ikon baru
const icon = document.createElement("i");
icon.classList.add("bx", "bxs-error-alt");
icon.style.fontSize = "25px";
icon.style.color = "red"; // Pastikan 'green' dalam bentuk string
// Tambahkan ikon ke dalam auth-logo
authLogo.appendChild(icon);
}
// Enable button and reset its text on error // Enable button and reset its text on error
modalButton.disabled = false; modalButton.disabled = false;
modalButton.innerHTML = isEdit ? "Update" : "Create"; modalButton.innerHTML = isEdit ? "Update" : "Create";
// Set error message for the toast // Set error message for the toast
toastBody.textContent = "Failed: " + (data.message || "Something went wrong"); toastBody.textContent =
toast.classList.add('show'); // Show the toast "An error occurred while processing your request.";
toast.classList.add("show"); // Show the toast
setTimeout(() => { setTimeout(() => {
toast.classList.remove('show'); // Hide the toast after 3 seconds toast.classList.remove("show"); // Hide the toast after 3 seconds
}, 3000); }, 3000);
} });
})
.catch(errors => {
if (authLogo) {
// Hapus ikon yang sudah ada jika ada
const existingIcon = authLogo.querySelector('.bx');
if (existingIcon) {
authLogo.removeChild(existingIcon);
}
// Buat ikon baru
const icon = document.createElement('i');
icon.classList.add('bx', 'bxs-error-alt');
icon.style.fontSize = '25px';
icon.style.color = 'red'; // Pastikan 'green' dalam bentuk string
// Tambahkan ikon ke dalam auth-logo
authLogo.appendChild(icon);
}
// Enable button and reset its text on error
modalButton.disabled = false;
modalButton.innerHTML = isEdit ? "Update" : "Create";
// Set error message for the toast
toastBody.textContent = "An error occurred while processing your request.";
toast.classList.add('show'); // Show the toast
setTimeout(() => {
toast.classList.remove('show'); // Hide the toast after 3 seconds
}, 3000);
});
}); });
// Fungsi fetchOptions untuk autocomplete server-side // Fungsi fetchOptions untuk autocomplete server-side
@@ -140,39 +144,43 @@ document.addEventListener("DOMContentLoaded", function () {
let inputValue = document.getElementById(field).value; let inputValue = document.getElementById(field).value;
if (inputValue.length < 2) return; if (inputValue.length < 2) return;
let districtValue = document.getElementById("district_name").value; // Ambil kecamatan terpilih let districtValue = document.getElementById("district_name").value; // Ambil kecamatan terpilih
let url = `${GlobalConfig.apiHost}/api/combobox/search-options?query=${encodeURIComponent(inputValue)}&field=${field}`; let url = `${
GlobalConfig.apiHost
}/api/combobox/search-options?query=${encodeURIComponent(
inputValue
)}&field=${field}`;
// Jika field desa, tambahkan kecamatan sebagai filter // Jika field desa, tambahkan kecamatan sebagai filter
if (field === "village_name") { if (field === "village_name") {
url += `&district=${encodeURIComponent(districtValue)}`; url += `&district=${encodeURIComponent(districtValue)}`;
} }
fetch(url, { fetch(url, {
method: 'GET', method: "GET",
headers: { headers: {
Authorization: `Bearer ${document Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]') .querySelector('meta[name="api-token"]')
.getAttribute("content")}`, .getAttribute("content")}`,
"Content-Type": "application/json", "Content-Type": "application/json",
} },
}) })
.then(response => response.json()) .then((response) => response.json())
.then(data => { .then((data) => {
let dataList = document.getElementById(field + "Options"); let dataList = document.getElementById(field + "Options");
dataList.innerHTML = ""; dataList.innerHTML = "";
data.forEach(item => { data.forEach((item) => {
let option = document.createElement("option"); let option = document.createElement("option");
option.value = item.name; option.value = item.name;
option.dataset.code = item.code; option.dataset.code = item.code;
dataList.appendChild(option); dataList.appendChild(option);
}); });
}) })
.catch(error => console.error("Error fetching options:", error)); .catch((error) => console.error("Error fetching options:", error));
}; };
document.querySelector('.btn-back').addEventListener('click', function() { document.querySelector(".btn-back").addEventListener("click", function () {
window.history.back(); window.history.back();
}); });
}); });

View File

@@ -18,58 +18,61 @@ console.log(dropzonePreviewNode);
url: `${GlobalConfig.apiHost}/api/advertisements/import`, url: `${GlobalConfig.apiHost}/api/advertisements/import`,
// url: "https://httpbin.org/post", // url: "https://httpbin.org/post",
method: "post", method: "post",
acceptedFiles: ".xls,.xlsx", // Use acceptedFiles for better validation acceptedFiles: ".xls,.xlsx", // Use acceptedFiles for better validation
previewTemplate: previewTemplate, previewTemplate: previewTemplate,
previewsContainer: "#dropzone-preview", previewsContainer: "#dropzone-preview",
autoProcessQueue: false, // Disable auto post autoProcessQueue: false, // Disable auto post
headers: { headers: {
Authorization: `Bearer ${document Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]') .querySelector('meta[name="api-token"]')
.getAttribute("content")}` .getAttribute("content")}`,
}, },
init: function() { init: function () {
// Listen for the success event // Listen for the success event
this.on("success", function(file, response) { this.on("success", function (file, response) {
console.log("File successfully uploaded:", file); console.log("File successfully uploaded:", file);
console.log("API Response:", response); console.log("API Response:", response);
// Show success toast // Show success toast
showToast('bxs-check-square', 'green', response.message); showToast("bxs-check-square", "green", response.message);
document.getElementById("submit-upload").innerHTML = "Upload Files"; document.getElementById("submit-upload").innerHTML =
"Upload Files";
// Tunggu sebentar lalu reload halaman // Tunggu sebentar lalu reload halaman
setTimeout(() => { setTimeout(() => {
window.location.href = "/data/advertisements"; window.location.href = "/data/web-advertisements";
}, 2000); }, 2000);
}); });
// Listen for the error event // Listen for the error event
this.on("error", function(file, errorMessage) { this.on("error", function (file, errorMessage) {
console.error("Error uploading file:", file); console.error("Error uploading file:", file);
console.error("Error message:", errorMessage); console.error("Error message:", errorMessage);
// Handle the error response // Handle the error response
// Show error toast // Show error toast
showToast('bxs-error-alt', 'red', errorMessage.message); showToast("bxs-error-alt", "red", errorMessage.message);
document.getElementById("submit-upload").innerHTML = "Upload Files"; document.getElementById("submit-upload").innerHTML =
"Upload Files";
}); });
} },
}))); })));
// Add event listener to control the submission manually // Add event listener to control the submission manually
document.querySelector("#submit-upload").addEventListener("click", function() { document.querySelector("#submit-upload").addEventListener("click", function () {
console.log("Ini adalah value dropzone", dropzone.files[0]); console.log("Ini adalah value dropzone", dropzone.files[0]);
const formData = new FormData() const formData = new FormData();
console.log("Dropzonefiles",dropzone.files); console.log("Dropzonefiles", dropzone.files);
this.innerHTML = '<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>Loading...'; this.innerHTML =
'<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>Loading...';
// Pastikan ada file dalam queue sebelum memprosesnya // Pastikan ada file dalam queue sebelum memprosesnya
if (dropzone.files.length > 0) { if (dropzone.files.length > 0) {
formData.append('file', dropzone.files[0]) formData.append("file", dropzone.files[0]);
console.log("ini adalah form data on submit", ...formData); console.log("ini adalah form data on submit", ...formData);
dropzone.processQueue(); // Ini akan manual memicu upload dropzone.processQueue(); // Ini akan manual memicu upload
} else { } else {
// Show error toast when no file is selected // Show error toast when no file is selected
showToast('bxs-error-alt', 'red', "Please add a file first."); showToast("bxs-error-alt", "red", "Please add a file first.");
document.getElementById("submit-upload").innerHTML = "Upload Files"; document.getElementById("submit-upload").innerHTML = "Upload Files";
} }
}); });
@@ -82,62 +85,68 @@ dropzone.on("addedfile", function (file) {
console.log("Ukuran File:", (file.size / 1024).toFixed(2) + " KB"); console.log("Ukuran File:", (file.size / 1024).toFixed(2) + " KB");
}); });
dropzone.on("complete", function(file) { dropzone.on("complete", function (file) {
dropzone.removeFile(file); dropzone.removeFile(file);
}); });
// Add event listener to donwload file template // Add event listener to donwload file template
document.getElementById('downloadtempadvertisement').addEventListener('click', function() { document
var url = `${GlobalConfig.apiHost}/api/download-template-advertisement`; .getElementById("downloadtempadvertisement")
fetch(url, { .addEventListener("click", function () {
method: 'GET', var url = `${GlobalConfig.apiHost}/api/download-template-advertisement`;
headers: { fetch(url, {
Authorization: `Bearer ${document method: "GET",
.querySelector('meta[name="api-token"]') headers: {
.getAttribute("content")}` Authorization: `Bearer ${document
}, .querySelector('meta[name="api-token"]')
}) .getAttribute("content")}`,
.then(response => { },
if (response.ok) { })
return response.blob(); .then((response) => {
} else { if (response.ok) {
return response.json(); return response.blob();
} } else {
}) return response.json();
.then((blob) => { }
const url = window.URL.createObjectURL(blob); })
const a = document.createElement('a'); .then((blob) => {
a.style.display = 'none'; const url = window.URL.createObjectURL(blob);
a.href = url; const a = document.createElement("a");
a.download = 'template_reklame.xlsx'; a.style.display = "none";
document.body.appendChild(a); a.href = url;
a.click(); a.download = "template_reklame.xlsx";
window.URL.revokeObjectURL(url); document.body.appendChild(a);
}) a.click();
.catch((error) => { window.URL.revokeObjectURL(url);
console.error("Gagal mendownload file:", error); })
showToast('bxs-error-alt', 'red', "Template file is not already exist."); .catch((error) => {
}) console.error("Gagal mendownload file:", error);
}) showToast(
"bxs-error-alt",
"red",
"Template file is not already exist."
);
});
});
// Function to show toast // Function to show toast
function showToast(iconClass, iconColor, message) { function showToast(iconClass, iconColor, message) {
const toastElement = document.getElementById('toastUploadAdvertisement'); const toastElement = document.getElementById("toastUploadAdvertisement");
const toastBody = toastElement.querySelector('.toast-body'); const toastBody = toastElement.querySelector(".toast-body");
const toastHeader = toastElement.querySelector('.toast-header'); const toastHeader = toastElement.querySelector(".toast-header");
// Remove existing icon (if any) before adding the new one // Remove existing icon (if any) before adding the new one
const existingIcon = toastHeader.querySelector('.bx'); const existingIcon = toastHeader.querySelector(".bx");
if (existingIcon) { if (existingIcon) {
toastHeader.querySelector('.auth-logo').removeChild(existingIcon); // Remove the existing icon toastHeader.querySelector(".auth-logo").removeChild(existingIcon); // Remove the existing icon
} }
// Add the new icon to the toast header // Add the new icon to the toast header
const icon = document.createElement('i'); const icon = document.createElement("i");
icon.classList.add('bx', iconClass); icon.classList.add("bx", iconClass);
icon.style.fontSize = '25px'; icon.style.fontSize = "25px";
icon.style.color = iconColor; icon.style.color = iconColor;
toastHeader.querySelector('.auth-logo').appendChild(icon); toastHeader.querySelector(".auth-logo").appendChild(icon);
// Set the toast message // Set the toast message
toastBody.textContent = message; toastBody.textContent = message;
@@ -146,4 +155,3 @@ function showToast(iconClass, iconColor, message) {
const toast = new bootstrap.Toast(toastElement); // Inisialisasi Bootstrap Toast const toast = new bootstrap.Toast(toastElement); // Inisialisasi Bootstrap Toast
toast.show(); toast.show();
} }

View File

@@ -73,7 +73,7 @@ document.addEventListener("DOMContentLoaded", function () {
}, 3000); }, 3000);
setTimeout(() => { setTimeout(() => {
window.location.href = "/data/spatial-plannings"; window.location.href = "/data/web-spatial-plannings";
}, 3000); }, 3000);
} else { } else {
if (authLogo) { if (authLogo) {

View File

@@ -18,58 +18,61 @@ console.log(dropzonePreviewNode);
url: `${GlobalConfig.apiHost}/api/spatial-plannings/import`, url: `${GlobalConfig.apiHost}/api/spatial-plannings/import`,
// url: "https://httpbin.org/post", // url: "https://httpbin.org/post",
method: "post", method: "post",
acceptedFiles: ".xls,.xlsx", // Use acceptedFiles for better validation acceptedFiles: ".xls,.xlsx", // Use acceptedFiles for better validation
previewTemplate: previewTemplate, previewTemplate: previewTemplate,
previewsContainer: "#dropzone-preview", previewsContainer: "#dropzone-preview",
autoProcessQueue: false, // Disable auto post autoProcessQueue: false, // Disable auto post
headers: { headers: {
Authorization: `Bearer ${document Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]') .querySelector('meta[name="api-token"]')
.getAttribute("content")}` .getAttribute("content")}`,
}, },
init: function() { init: function () {
// Listen for the success event // Listen for the success event
this.on("success", function(file, response) { this.on("success", function (file, response) {
console.log("File successfully uploaded:", file); console.log("File successfully uploaded:", file);
console.log("API Response:", response); console.log("API Response:", response);
// Show success toast // Show success toast
showToast('bxs-check-square', 'green', response.message); showToast("bxs-check-square", "green", response.message);
document.getElementById("submit-upload").innerHTML = "Upload Files"; document.getElementById("submit-upload").innerHTML =
"Upload Files";
// Tunggu sebentar lalu reload halaman // Tunggu sebentar lalu reload halaman
setTimeout(() => { setTimeout(() => {
window.location.href = "/data/spatial-plannings"; window.location.href = "/data/web-spatial-plannings";
}, 2000); }, 2000);
}); });
// Listen for the error event // Listen for the error event
this.on("error", function(file, errorMessage) { this.on("error", function (file, errorMessage) {
console.error("Error uploading file:", file); console.error("Error uploading file:", file);
console.error("Error message:", errorMessage); console.error("Error message:", errorMessage);
// Handle the error response // Handle the error response
// Show error toast // Show error toast
showToast('bxs-error-alt', 'red', errorMessage.message); showToast("bxs-error-alt", "red", errorMessage.message);
document.getElementById("submit-upload").innerHTML = "Upload Files"; document.getElementById("submit-upload").innerHTML =
"Upload Files";
}); });
} },
}))); })));
// Add event listener to control the submission manually // Add event listener to control the submission manually
document.querySelector("#submit-upload").addEventListener("click", function() { document.querySelector("#submit-upload").addEventListener("click", function () {
console.log("Ini adalah value dropzone", dropzone.files[0]); console.log("Ini adalah value dropzone", dropzone.files[0]);
const formData = new FormData() const formData = new FormData();
console.log("Dropzonefiles",dropzone.files); console.log("Dropzonefiles", dropzone.files);
this.innerHTML = '<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>Loading...'; this.innerHTML =
'<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>Loading...';
// Pastikan ada file dalam queue sebelum memprosesnya // Pastikan ada file dalam queue sebelum memprosesnya
if (dropzone.files.length > 0) { if (dropzone.files.length > 0) {
formData.append('file', dropzone.files[0]) formData.append("file", dropzone.files[0]);
console.log("ini adalah form data on submit", ...formData); console.log("ini adalah form data on submit", ...formData);
dropzone.processQueue(); // Ini akan manual memicu upload dropzone.processQueue(); // Ini akan manual memicu upload
} else { } else {
// Show error toast when no file is selected // Show error toast when no file is selected
showToast('bxs-error-alt', 'red', "Please add a file first."); showToast("bxs-error-alt", "red", "Please add a file first.");
document.getElementById("submit-upload").innerHTML = "Upload Files"; document.getElementById("submit-upload").innerHTML = "Upload Files";
} }
}); });
@@ -82,63 +85,69 @@ dropzone.on("addedfile", function (file) {
console.log("Ukuran File:", (file.size / 1024).toFixed(2) + " KB"); console.log("Ukuran File:", (file.size / 1024).toFixed(2) + " KB");
}); });
dropzone.on("complete", function(file) { dropzone.on("complete", function (file) {
dropzone.removeFile(file); dropzone.removeFile(file);
}); });
// Add event listener to download file template // Add event listener to download file template
document.getElementById('downloadtempspatialPlannings').addEventListener('click', function() { document
var url = `${GlobalConfig.apiHost}/api/download-template-spatialPlannings`; .getElementById("downloadtempspatialPlannings")
fetch(url, { .addEventListener("click", function () {
method: 'GET', var url = `${GlobalConfig.apiHost}/api/download-template-spatialPlannings`;
headers: { fetch(url, {
Authorization: `Bearer ${document method: "GET",
.querySelector('meta[name="api-token"]') headers: {
.getAttribute("content")}` Authorization: `Bearer ${document
}, .querySelector('meta[name="api-token"]')
}) .getAttribute("content")}`,
.then(response => { },
if (response.ok) { })
return response.blob(); // Jika respons OK, konversi menjadi blob .then((response) => {
} else { if (response.ok) {
return response.json(); // Jika respons gagal, konversi menjadi JSON untuk menangani pesan error return response.blob(); // Jika respons OK, konversi menjadi blob
} } else {
}) return response.json(); // Jika respons gagal, konversi menjadi JSON untuk menangani pesan error
.then((blob) => { }
console.log(blob); })
const url = window.URL.createObjectURL(blob); .then((blob) => {
const a = document.createElement('a'); console.log(blob);
a.style.display = 'none'; const url = window.URL.createObjectURL(blob);
a.href = url; const a = document.createElement("a");
a.download = 'template_rencana_tata_ruang.xlsx'; a.style.display = "none";
document.body.appendChild(a); a.href = url;
a.click(); a.download = "template_rencana_tata_ruang.xlsx";
window.URL.revokeObjectURL(url); document.body.appendChild(a);
}) a.click();
.catch((error) => { window.URL.revokeObjectURL(url);
console.error("Gagal mendownload file:", error); })
showToast('bxs-error-alt', 'red', "Template file is not already exist."); .catch((error) => {
}) console.error("Gagal mendownload file:", error);
}) showToast(
"bxs-error-alt",
"red",
"Template file is not already exist."
);
});
});
// Function to show toast // Function to show toast
function showToast(iconClass, iconColor, message) { function showToast(iconClass, iconColor, message) {
const toastElement = document.getElementById('toastUploadSpatialPlannings'); const toastElement = document.getElementById("toastUploadSpatialPlannings");
const toastBody = toastElement.querySelector('.toast-body'); const toastBody = toastElement.querySelector(".toast-body");
const toastHeader = toastElement.querySelector('.toast-header'); const toastHeader = toastElement.querySelector(".toast-header");
// Remove existing icon (if any) before adding the new one // Remove existing icon (if any) before adding the new one
const existingIcon = toastHeader.querySelector('.bx'); const existingIcon = toastHeader.querySelector(".bx");
if (existingIcon) { if (existingIcon) {
toastHeader.querySelector('.auth-logo').removeChild(existingIcon); // Remove the existing icon toastHeader.querySelector(".auth-logo").removeChild(existingIcon); // Remove the existing icon
} }
// Add the new icon to the toast header // Add the new icon to the toast header
const icon = document.createElement('i'); const icon = document.createElement("i");
icon.classList.add('bx', iconClass); icon.classList.add("bx", iconClass);
icon.style.fontSize = '25px'; icon.style.fontSize = "25px";
icon.style.color = iconColor; icon.style.color = iconColor;
toastHeader.querySelector('.auth-logo').appendChild(icon); toastHeader.querySelector(".auth-logo").appendChild(icon);
// Set the toast message // Set the toast message
toastBody.textContent = message; toastBody.textContent = message;
@@ -147,4 +156,3 @@ function showToast(iconClass, iconColor, message) {
const toast = new bootstrap.Toast(toastElement); // Inisialisasi Bootstrap Toast const toast = new bootstrap.Toast(toastElement); // Inisialisasi Bootstrap Toast
toast.show(); toast.show();
} }

View File

@@ -73,7 +73,7 @@ document.addEventListener("DOMContentLoaded", function () {
}, 3000); }, 3000);
setTimeout(() => { setTimeout(() => {
window.location.href = "/data/tourisms"; window.location.href = "/data/web-tourisms";
}, 3000); }, 3000);
} else { } else {
if (authLogo) { if (authLogo) {

View File

@@ -18,58 +18,61 @@ console.log(dropzonePreviewNode);
url: `${GlobalConfig.apiHost}/api/tourisms/import`, url: `${GlobalConfig.apiHost}/api/tourisms/import`,
// url: "https://httpbin.org/post", // url: "https://httpbin.org/post",
method: "post", method: "post",
acceptedFiles: ".xls,.xlsx", // Use acceptedFiles for better validation acceptedFiles: ".xls,.xlsx", // Use acceptedFiles for better validation
previewTemplate: previewTemplate, previewTemplate: previewTemplate,
previewsContainer: "#dropzone-preview", previewsContainer: "#dropzone-preview",
autoProcessQueue: false, // Disable auto post autoProcessQueue: false, // Disable auto post
headers: { headers: {
Authorization: `Bearer ${document Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]') .querySelector('meta[name="api-token"]')
.getAttribute("content")}` .getAttribute("content")}`,
}, },
init: function() { init: function () {
// Listen for the success event // Listen for the success event
this.on("success", function(file, response) { this.on("success", function (file, response) {
console.log("File successfully uploaded:", file); console.log("File successfully uploaded:", file);
console.log("API Response:", response); console.log("API Response:", response);
// Show success toast // Show success toast
showToast('bxs-check-square', 'green', response.message); showToast("bxs-check-square", "green", response.message);
document.getElementById("submit-upload").innerHTML = "Upload Files"; document.getElementById("submit-upload").innerHTML =
"Upload Files";
// Tunggu sebentar lalu reload halaman // Tunggu sebentar lalu reload halaman
setTimeout(() => { setTimeout(() => {
window.location.href = "/data/tourisms"; window.location.href = "/data/web-tourisms";
}, 2000); }, 2000);
}); });
// Listen for the error event // Listen for the error event
this.on("error", function(file, errorMessage) { this.on("error", function (file, errorMessage) {
console.error("Error uploading file:", file); console.error("Error uploading file:", file);
console.error("Error message:", errorMessage); console.error("Error message:", errorMessage);
// Handle the error response // Handle the error response
// Show error toast // Show error toast
showToast('bxs-error-alt', 'red', errorMessage.message); showToast("bxs-error-alt", "red", errorMessage.message);
document.getElementById("submit-upload").innerHTML = "Upload Files"; document.getElementById("submit-upload").innerHTML =
"Upload Files";
}); });
} },
}))); })));
// Add event listener to control the submission manually // Add event listener to control the submission manually
document.querySelector("#submit-upload").addEventListener("click", function() { document.querySelector("#submit-upload").addEventListener("click", function () {
console.log("Ini adalah value dropzone", dropzone.files[0]); console.log("Ini adalah value dropzone", dropzone.files[0]);
const formData = new FormData() const formData = new FormData();
console.log("Dropzonefiles",dropzone.files); console.log("Dropzonefiles", dropzone.files);
this.innerHTML = '<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>Loading...'; this.innerHTML =
'<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>Loading...';
// Pastikan ada file dalam queue sebelum memprosesnya // Pastikan ada file dalam queue sebelum memprosesnya
if (dropzone.files.length > 0) { if (dropzone.files.length > 0) {
formData.append('file', dropzone.files[0]) formData.append("file", dropzone.files[0]);
console.log("ini adalah form data on submit", ...formData); console.log("ini adalah form data on submit", ...formData);
dropzone.processQueue(); // Ini akan manual memicu upload dropzone.processQueue(); // Ini akan manual memicu upload
} else { } else {
// Show error toast when no file is selected // Show error toast when no file is selected
showToast('bxs-error-alt', 'red', "Please add a file first."); showToast("bxs-error-alt", "red", "Please add a file first.");
document.getElementById("submit-upload").innerHTML = "Upload Files"; document.getElementById("submit-upload").innerHTML = "Upload Files";
} }
}); });
@@ -82,63 +85,69 @@ dropzone.on("addedfile", function (file) {
console.log("Ukuran File:", (file.size / 1024).toFixed(2) + " KB"); console.log("Ukuran File:", (file.size / 1024).toFixed(2) + " KB");
}); });
dropzone.on("complete", function(file) { dropzone.on("complete", function (file) {
dropzone.removeFile(file); dropzone.removeFile(file);
}); });
// Add event listener to download file template // Add event listener to download file template
document.getElementById('downloadtemptourisms').addEventListener('click', function() { document
var url = `${GlobalConfig.apiHost}/api/download-template-tourism`; .getElementById("downloadtemptourisms")
fetch(url, { .addEventListener("click", function () {
method: 'GET', var url = `${GlobalConfig.apiHost}/api/download-template-tourism`;
headers: { fetch(url, {
Authorization: `Bearer ${document method: "GET",
.querySelector('meta[name="api-token"]') headers: {
.getAttribute("content")}` Authorization: `Bearer ${document
}, .querySelector('meta[name="api-token"]')
}) .getAttribute("content")}`,
.then(response => { },
if (response.ok) { })
return response.blob(); // Jika respons OK, konversi menjadi blob .then((response) => {
} else { if (response.ok) {
return response.json(); // Jika respons gagal, konversi menjadi JSON untuk menangani pesan error return response.blob(); // Jika respons OK, konversi menjadi blob
} } else {
}) return response.json(); // Jika respons gagal, konversi menjadi JSON untuk menangani pesan error
.then((blob) => { }
console.log(blob); })
const url = window.URL.createObjectURL(blob); .then((blob) => {
const a = document.createElement('a'); console.log(blob);
a.style.display = 'none'; const url = window.URL.createObjectURL(blob);
a.href = url; const a = document.createElement("a");
a.download = 'template_pariwisata.xlsx'; a.style.display = "none";
document.body.appendChild(a); a.href = url;
a.click(); a.download = "template_pariwisata.xlsx";
window.URL.revokeObjectURL(url); document.body.appendChild(a);
}) a.click();
.catch((error) => { window.URL.revokeObjectURL(url);
console.error("Gagal mendownload file:", error); })
showToast('bxs-error-alt', 'red', "Template file is not already exist."); .catch((error) => {
}) console.error("Gagal mendownload file:", error);
}) showToast(
"bxs-error-alt",
"red",
"Template file is not already exist."
);
});
});
// Function to show toast // Function to show toast
function showToast(iconClass, iconColor, message) { function showToast(iconClass, iconColor, message) {
const toastElement = document.getElementById('toastUploadTourisms'); const toastElement = document.getElementById("toastUploadTourisms");
const toastBody = toastElement.querySelector('.toast-body'); const toastBody = toastElement.querySelector(".toast-body");
const toastHeader = toastElement.querySelector('.toast-header'); const toastHeader = toastElement.querySelector(".toast-header");
// Remove existing icon (if any) before adding the new one // Remove existing icon (if any) before adding the new one
const existingIcon = toastHeader.querySelector('.bx'); const existingIcon = toastHeader.querySelector(".bx");
if (existingIcon) { if (existingIcon) {
toastHeader.querySelector('.auth-logo').removeChild(existingIcon); // Remove the existing icon toastHeader.querySelector(".auth-logo").removeChild(existingIcon); // Remove the existing icon
} }
// Add the new icon to the toast header // Add the new icon to the toast header
const icon = document.createElement('i'); const icon = document.createElement("i");
icon.classList.add('bx', iconClass); icon.classList.add("bx", iconClass);
icon.style.fontSize = '25px'; icon.style.fontSize = "25px";
icon.style.color = iconColor; icon.style.color = iconColor;
toastHeader.querySelector('.auth-logo').appendChild(icon); toastHeader.querySelector(".auth-logo").appendChild(icon);
// Set the toast message // Set the toast message
toastBody.textContent = message; toastBody.textContent = message;
@@ -147,4 +156,3 @@ function showToast(iconClass, iconColor, message) {
const toast = new bootstrap.Toast(toastElement); // Inisialisasi Bootstrap Toast const toast = new bootstrap.Toast(toastElement); // Inisialisasi Bootstrap Toast
toast.show(); toast.show();
} }

View File

@@ -16,10 +16,10 @@ document.addEventListener("DOMContentLoaded", function () {
Loading... Loading...
`; `;
const isEdit = saveButton.classList.contains("btn-edit"); const isEdit = saveButton.classList.contains("btn-edit");
const formData = new FormData(form) const formData = new FormData(form);
const toast = document.getElementById('toastEditUpdate'); const toast = document.getElementById("toastEditUpdate");
const toastBody = toast.querySelector('.toast-body'); const toastBody = toast.querySelector(".toast-body");
const toastHeader = toast.querySelector('.toast-header small'); const toastHeader = toast.querySelector(".toast-header small");
const data = {}; const data = {};
@@ -40,53 +40,88 @@ document.addEventListener("DOMContentLoaded", function () {
.querySelector('meta[name="api-token"]') .querySelector('meta[name="api-token"]')
.getAttribute("content")}`, .getAttribute("content")}`,
"Content-Type": "application/json", "Content-Type": "application/json",
} },
}) })
.then(response => response.json()) .then((response) => response.json())
.then(data => { .then((data) => {
console.log("Response data:", data); console.log("Response data:", data);
if (!data.errors) { if (!data.errors) {
// Remove existing icon (if any) before adding the new one // Remove existing icon (if any) before adding the new one
if (authLogo) { if (authLogo) {
// Hapus ikon yang sudah ada jika ada // Hapus ikon yang sudah ada jika ada
const existingIcon = authLogo.querySelector('.bx'); const existingIcon = authLogo.querySelector(".bx");
if (existingIcon) { if (existingIcon) {
authLogo.removeChild(existingIcon); authLogo.removeChild(existingIcon);
}
// Buat ikon baru
const icon = document.createElement("i");
icon.classList.add("bx", "bxs-check-square");
icon.style.fontSize = "25px";
icon.style.color = "green"; // Pastikan 'green' dalam bentuk string
// Tambahkan ikon ke dalam auth-logo
authLogo.appendChild(icon);
} }
// Buat ikon baru // Set success message for the toast
const icon = document.createElement('i'); toastBody.textContent = isEdit
icon.classList.add('bx', 'bxs-check-square'); ? "Data updated successfully!"
icon.style.fontSize = '25px'; : "Data created successfully!";
icon.style.color = 'green'; // Pastikan 'green' dalam bentuk string toast.classList.add("show"); // Show the toast
setTimeout(() => {
toast.classList.remove("show"); // Hide the toast after 3 seconds
}, 2000);
// Tambahkan ikon ke dalam auth-logo setTimeout(() => {
authLogo.appendChild(icon); window.location.href = "/data/web-umkm";
}, 1000);
} else {
if (authLogo) {
// Hapus ikon yang sudah ada jika ada
const existingIcon = authLogo.querySelector(".bx");
if (existingIcon) {
authLogo.removeChild(existingIcon);
}
// Buat ikon baru
const icon = document.createElement("i");
icon.classList.add("bx", "bxs-error-alt");
icon.style.fontSize = "25px";
icon.style.color = "red"; // Pastikan 'green' dalam bentuk string
// Tambahkan ikon ke dalam auth-logo
authLogo.appendChild(icon);
}
// Enable button and reset its text on error
modalButton.disabled = false;
modalButton.innerHTML = isEdit ? "Update" : "Create";
// Set error message for the toast
toastBody.textContent =
"Error: " + (data.message || "Something went wrong");
toast.classList.add("show"); // Show the toast
setTimeout(() => {
toast.classList.remove("show"); // Hide the toast after 3 seconds
}, 3000);
} }
})
// Set success message for the toast .catch((error) => {
toastBody.textContent = isEdit ? "Data updated successfully!" : "Data created successfully!"; console.error("Error:", error);
toast.classList.add('show'); // Show the toast
setTimeout(() => {
toast.classList.remove('show'); // Hide the toast after 3 seconds
}, 2000);
setTimeout(() => {
window.location.href = '/data/umkm';
}, 1000);
} else {
if (authLogo) { if (authLogo) {
// Hapus ikon yang sudah ada jika ada // Hapus ikon yang sudah ada jika ada
const existingIcon = authLogo.querySelector('.bx'); const existingIcon = authLogo.querySelector(".bx");
if (existingIcon) { if (existingIcon) {
authLogo.removeChild(existingIcon); authLogo.removeChild(existingIcon);
} }
// Buat ikon baru // Buat ikon baru
const icon = document.createElement('i'); const icon = document.createElement("i");
icon.classList.add('bx', 'bxs-error-alt'); icon.classList.add("bx", "bxs-error-alt");
icon.style.fontSize = '25px'; icon.style.fontSize = "25px";
icon.style.color = 'red'; // Pastikan 'green' dalam bentuk string icon.style.color = "red"; // Pastikan 'green' dalam bentuk string
// Tambahkan ikon ke dalam auth-logo // Tambahkan ikon ke dalam auth-logo
authLogo.appendChild(icon); authLogo.appendChild(icon);
@@ -96,47 +131,15 @@ document.addEventListener("DOMContentLoaded", function () {
modalButton.disabled = false; modalButton.disabled = false;
modalButton.innerHTML = isEdit ? "Update" : "Create"; modalButton.innerHTML = isEdit ? "Update" : "Create";
// Set error message for the toast // Set error message for the toast
toastBody.textContent = "Error: " + (data.message || "Something went wrong"); toastBody.textContent =
toast.classList.add('show'); // Show the toast "An error occurred while processing your request.";
toast.classList.add("show"); // Show the toast
setTimeout(() => { setTimeout(() => {
toast.classList.remove('show'); // Hide the toast after 3 seconds toast.classList.remove("show"); // Hide the toast after 3 seconds
}, 3000); }, 3000);
} });
})
.catch(error => {
console.error("Error:", error);
if (authLogo) {
// Hapus ikon yang sudah ada jika ada
const existingIcon = authLogo.querySelector('.bx');
if (existingIcon) {
authLogo.removeChild(existingIcon);
}
// Buat ikon baru
const icon = document.createElement('i');
icon.classList.add('bx', 'bxs-error-alt');
icon.style.fontSize = '25px';
icon.style.color = 'red'; // Pastikan 'green' dalam bentuk string
// Tambahkan ikon ke dalam auth-logo
authLogo.appendChild(icon);
}
// Enable button and reset its text on error
modalButton.disabled = false;
modalButton.innerHTML = isEdit ? "Update" : "Create";
// Set error message for the toast
toastBody.textContent = "An error occurred while processing your request.";
toast.classList.add('show'); // Show the toast
setTimeout(() => {
toast.classList.remove('show'); // Hide the toast after 3 seconds
}, 3000);
});
}); });
// Fungsi fetchOptions untuk autocomplete server-side // Fungsi fetchOptions untuk autocomplete server-side
@@ -145,7 +148,11 @@ document.addEventListener("DOMContentLoaded", function () {
if (inputValue.length < 2) return; if (inputValue.length < 2) return;
let districtValue = document.getElementById("district_name").value; // Ambil kecamatan terpilih let districtValue = document.getElementById("district_name").value; // Ambil kecamatan terpilih
let url = `${GlobalConfig.apiHost}/api/combobox/search-options?query=${encodeURIComponent(inputValue)}&field=${field}`; let url = `${
GlobalConfig.apiHost
}/api/combobox/search-options?query=${encodeURIComponent(
inputValue
)}&field=${field}`;
// Jika field desa, tambahkan kecamatan sebagai filter // Jika field desa, tambahkan kecamatan sebagai filter
if (field === "village_name") { if (field === "village_name") {
@@ -153,30 +160,30 @@ document.addEventListener("DOMContentLoaded", function () {
} }
fetch(url, { fetch(url, {
method: 'GET', method: "GET",
headers: { headers: {
Authorization: `Bearer ${document Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]') .querySelector('meta[name="api-token"]')
.getAttribute("content")}`, .getAttribute("content")}`,
"Content-Type": "application/json", "Content-Type": "application/json",
} },
}) })
.then(response => response.json()) .then((response) => response.json())
.then(data => { .then((data) => {
let dataList = document.getElementById(field + "Options"); let dataList = document.getElementById(field + "Options");
dataList.innerHTML = ""; dataList.innerHTML = "";
data.forEach(item => { data.forEach((item) => {
let option = document.createElement("option"); let option = document.createElement("option");
option.value = item.name; option.value = item.name;
option.dataset.code = item.code; option.dataset.code = item.code;
dataList.appendChild(option); dataList.appendChild(option);
}); });
}) })
.catch(error => console.error("Error fetching options:", error)); .catch((error) => console.error("Error fetching options:", error));
}; };
document.querySelector('.btn-back').addEventListener('click', function() { document.querySelector(".btn-back").addEventListener("click", function () {
window.history.back(); window.history.back();
}); });
}); });

View File

@@ -18,58 +18,61 @@ console.log(dropzonePreviewNode);
url: `${GlobalConfig.apiHost}/api/umkm/import`, url: `${GlobalConfig.apiHost}/api/umkm/import`,
// url: "https://httpbin.org/post", // url: "https://httpbin.org/post",
method: "post", method: "post",
acceptedFiles: ".xls,.xlsx", // Use acceptedFiles for better validation acceptedFiles: ".xls,.xlsx", // Use acceptedFiles for better validation
previewTemplate: previewTemplate, previewTemplate: previewTemplate,
previewsContainer: "#dropzone-preview", previewsContainer: "#dropzone-preview",
autoProcessQueue: false, // Disable auto post autoProcessQueue: false, // Disable auto post
headers: { headers: {
Authorization: `Bearer ${document Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]') .querySelector('meta[name="api-token"]')
.getAttribute("content")}` .getAttribute("content")}`,
}, },
init: function() { init: function () {
// Listen for the success event // Listen for the success event
this.on("success", function(file, response) { this.on("success", function (file, response) {
console.log("File successfully uploaded:", file); console.log("File successfully uploaded:", file);
console.log("API Response:", response); console.log("API Response:", response);
// Show success toast // Show success toast
showToast('bxs-check-square', 'green', response.message); showToast("bxs-check-square", "green", response.message);
document.getElementById("submit-upload").innerHTML = "Upload Files"; document.getElementById("submit-upload").innerHTML =
"Upload Files";
// Tunggu sebentar lalu reload halaman // Tunggu sebentar lalu reload halaman
setTimeout(() => { setTimeout(() => {
window.location.href = "/data/umkm"; window.location.href = "/data/web-umkm";
}, 2000); }, 2000);
}); });
// Listen for the error event // Listen for the error event
this.on("error", function(file, errorMessage) { this.on("error", function (file, errorMessage) {
console.error("Error uploading file:", file); console.error("Error uploading file:", file);
console.error("Error message:", errorMessage); console.error("Error message:", errorMessage);
// Handle the error response // Handle the error response
// Show error toast // Show error toast
showToast('bxs-error-alt', 'red', errorMessage.message); showToast("bxs-error-alt", "red", errorMessage.message);
document.getElementById("submit-upload").innerHTML = "Upload Files"; document.getElementById("submit-upload").innerHTML =
"Upload Files";
}); });
} },
}))); })));
// Add event listener to control the submission manually // Add event listener to control the submission manually
document.querySelector("#submit-upload").addEventListener("click", function() { document.querySelector("#submit-upload").addEventListener("click", function () {
console.log("Ini adalah value dropzone", dropzone.files[0]); console.log("Ini adalah value dropzone", dropzone.files[0]);
const formData = new FormData() const formData = new FormData();
console.log("Dropzonefiles",dropzone.files); console.log("Dropzonefiles", dropzone.files);
this.innerHTML = '<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>Loading...'; this.innerHTML =
'<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>Loading...';
// Pastikan ada file dalam queue sebelum memprosesnya // Pastikan ada file dalam queue sebelum memprosesnya
if (dropzone.files.length > 0) { if (dropzone.files.length > 0) {
formData.append('file', dropzone.files[0]) formData.append("file", dropzone.files[0]);
console.log("ini adalah form data on submit", ...formData); console.log("ini adalah form data on submit", ...formData);
dropzone.processQueue(); // Ini akan manual memicu upload dropzone.processQueue(); // Ini akan manual memicu upload
} else { } else {
// Show error toast when no file is selected // Show error toast when no file is selected
showToast('bxs-error-alt', 'red', "Please add a file first."); showToast("bxs-error-alt", "red", "Please add a file first.");
document.getElementById("submit-upload").innerHTML = "Upload Files"; document.getElementById("submit-upload").innerHTML = "Upload Files";
} }
}); });
@@ -82,62 +85,68 @@ dropzone.on("addedfile", function (file) {
console.log("Ukuran File:", (file.size / 1024).toFixed(2) + " KB"); console.log("Ukuran File:", (file.size / 1024).toFixed(2) + " KB");
}); });
dropzone.on("complete", function(file) { dropzone.on("complete", function (file) {
dropzone.removeFile(file); dropzone.removeFile(file);
}); });
// Add event listener to download file template // Add event listener to download file template
document.getElementById('downloadtempumkm').addEventListener('click', function() { document
var url = `${GlobalConfig.apiHost}/api/download-template-umkm`; .getElementById("downloadtempumkm")
fetch(url, { .addEventListener("click", function () {
method: 'GET', var url = `${GlobalConfig.apiHost}/api/download-template-umkm`;
headers: { fetch(url, {
Authorization: `Bearer ${document method: "GET",
.querySelector('meta[name="api-token"]') headers: {
.getAttribute("content")}` Authorization: `Bearer ${document
}, .querySelector('meta[name="api-token"]')
}) .getAttribute("content")}`,
.then(response => { },
if (response.ok) { })
return response.blob(); // Jika respons OK, konversi menjadi blob .then((response) => {
} else { if (response.ok) {
return response.json(); // Jika respons gagal, konversi menjadi JSON untuk menangani pesan error return response.blob(); // Jika respons OK, konversi menjadi blob
} } else {
}) return response.json(); // Jika respons gagal, konversi menjadi JSON untuk menangani pesan error
.then((blob) => { }
const url = window.URL.createObjectURL(blob); })
const a = document.createElement('a'); .then((blob) => {
a.style.display = 'none'; const url = window.URL.createObjectURL(blob);
a.href = url; const a = document.createElement("a");
a.download = 'template_umkm.xlsx'; a.style.display = "none";
document.body.appendChild(a); a.href = url;
a.click(); a.download = "template_umkm.xlsx";
window.URL.revokeObjectURL(url); document.body.appendChild(a);
}) a.click();
.catch((error) => { window.URL.revokeObjectURL(url);
console.error("Gagal mendownload file:", error); })
showToast('bxs-error-alt', 'red', "Template file is not already exist."); .catch((error) => {
}) console.error("Gagal mendownload file:", error);
}) showToast(
"bxs-error-alt",
"red",
"Template file is not already exist."
);
});
});
// Function to show toast // Function to show toast
function showToast(iconClass, iconColor, message) { function showToast(iconClass, iconColor, message) {
const toastElement = document.getElementById('toastUploadUmkm'); const toastElement = document.getElementById("toastUploadUmkm");
const toastBody = toastElement.querySelector('.toast-body'); const toastBody = toastElement.querySelector(".toast-body");
const toastHeader = toastElement.querySelector('.toast-header'); const toastHeader = toastElement.querySelector(".toast-header");
// Remove existing icon (if any) before adding the new one // Remove existing icon (if any) before adding the new one
const existingIcon = toastHeader.querySelector('.bx'); const existingIcon = toastHeader.querySelector(".bx");
if (existingIcon) { if (existingIcon) {
toastHeader.querySelector('.auth-logo').removeChild(existingIcon); // Remove the existing icon toastHeader.querySelector(".auth-logo").removeChild(existingIcon); // Remove the existing icon
} }
// Add the new icon to the toast header // Add the new icon to the toast header
const icon = document.createElement('i'); const icon = document.createElement("i");
icon.classList.add('bx', iconClass); icon.classList.add("bx", iconClass);
icon.style.fontSize = '25px'; icon.style.fontSize = "25px";
icon.style.color = iconColor; icon.style.color = iconColor;
toastHeader.querySelector('.auth-logo').appendChild(icon); toastHeader.querySelector(".auth-logo").appendChild(icon);
// Set the toast message // Set the toast message
toastBody.textContent = message; toastBody.textContent = message;
@@ -146,4 +155,3 @@ function showToast(iconClass, iconColor, message) {
const toast = new bootstrap.Toast(toastElement); // Inisialisasi Bootstrap Toast const toast = new bootstrap.Toast(toastElement); // Inisialisasi Bootstrap Toast
toast.show(); toast.show();
} }

View File

@@ -31,7 +31,7 @@ class UsersTable {
}, },
], ],
pagination: { pagination: {
limit: 15, limit: 50,
server: { server: {
url: (prev, page) => url: (prev, page) =>
`${prev}${prev.includes("?") ? "&" : "?"}page=${ `${prev}${prev.includes("?") ? "&" : "?"}page=${

View File

@@ -83,7 +83,7 @@ class Menus {
}, },
], ],
pagination: { pagination: {
limit: 15, limit: 50,
server: { server: {
url: (prev, page) => url: (prev, page) =>
`${prev}${prev.includes("?") ? "&" : "?"}page=${ `${prev}${prev.includes("?") ? "&" : "?"}page=${

View File

@@ -52,7 +52,7 @@ class Roles {
}, },
], ],
pagination: { pagination: {
limit: 15, limit: 50,
server: { server: {
url: (prev, page) => url: (prev, page) =>
`${prev}${prev.includes("?") ? "&" : "?"}page=${ `${prev}${prev.includes("?") ? "&" : "?"}page=${

View File

@@ -4,13 +4,21 @@ import "gridjs/dist/gridjs.umd.js";
import GlobalConfig from "../../global-config.js"; import GlobalConfig from "../../global-config.js";
class SyncronizeTask { class SyncronizeTask {
constructor() {
this.toastElement = document.getElementById("toastNotification");
this.toastMessage = document.getElementById("toast-message");
this.toast = new bootstrap.Toast(this.toastElement);
this.table = null;
}
init() { init() {
this.initTableImportDatasources(); this.initTableImportDatasources();
this.handleSubmitSync(); this.handleSubmitSync();
this.handleSubmitSnycGoogleSheet();
} }
initTableImportDatasources() { initTableImportDatasources() {
new Grid({ let tableContainer = document.getElementById(
"table-import-datasources"
);
this.table = new gridjs.Grid({
columns: ["ID", "Message", "Response", "Status", "Created"], columns: ["ID", "Message", "Response", "Status", "Created"],
search: { search: {
server: { server: {
@@ -18,7 +26,7 @@ class SyncronizeTask {
}, },
}, },
pagination: { pagination: {
limit: 15, limit: 50,
server: { server: {
url: (prev, page) => url: (prev, page) =>
`${prev}${prev.includes("?") ? "&" : "?"}page=${ `${prev}${prev.includes("?") ? "&" : "?"}page=${
@@ -45,20 +53,24 @@ class SyncronizeTask {
]), ]),
total: (data) => data.meta.total, total: (data) => data.meta.total,
}, },
}).render(document.getElementById("table-import-datasources")); }).render(tableContainer);
} }
handleSubmitSync() { handleSubmitSync() {
const button = document.getElementById("btn-sync-submit"); const button = document.getElementById("btn-sync-submit");
const spinner = document.getElementById("spinner");
const apiToken = document
.querySelector('meta[name="api-token"]')
.getAttribute("content");
// Show the spinner while checking
spinner.classList.remove("d-none");
// Check if the button should be enabled or disabled based on the status
fetch( fetch(
`${GlobalConfig.apiHost}/api/import-datasource/check-datasource`, `${GlobalConfig.apiHost}/api/import-datasource/check-datasource`,
{ {
method: "GET", method: "GET",
headers: { headers: {
Authorization: `Bearer ${document Authorization: `Bearer ${apiToken}`,
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
} }
@@ -70,49 +82,21 @@ class SyncronizeTask {
return response.json(); return response.json();
}) })
.then((data) => { .then((data) => {
console.log("data check button sync", data.can_execute);
button.disabled = !data.can_execute; button.disabled = !data.can_execute;
// If the button is enabled, add click event to trigger sync if (!data.can_execute) {
if (!button.disabled) { // Keep spinner visible if cannot execute
button.addEventListener("click", function (e) { spinner.classList.remove("d-none");
button.disabled = true; // Disable button to prevent multiple clicks } else {
button.textContent = "Syncing..."; // Change button text to show syncing // Hide spinner when execution is allowed
spinner.classList.add("d-none");
// Trigger the scraping API call // Remove previous event listener before adding a new one
fetch(`${GlobalConfig.apiHost}/api/scraping`, { button.removeEventListener("click", this.handleSyncClick);
method: "GET", button.addEventListener(
headers: { "click",
Authorization: `Bearer ${document this.handleSyncClick.bind(this)
.querySelector('meta[name="api-token"]') );
.getAttribute("content")}`,
"Content-Type": "application/json",
},
})
.then((response) => {
if (!response.ok) {
throw new Error(
"Network response was not ok"
);
}
return response.json();
})
.then((data) => {
console.log("data sync button", data);
alert("Synchronization executed successfully");
window.location.reload();
})
.catch((err) => {
console.error("Fetch error:", err);
alert(
"An error occurred during synchronization"
);
})
.finally(() => {
button.disabled = false; // Re-enable the button after the request is complete
button.textContent = "Sync Data"; // Reset button text
});
});
} }
}) })
.catch((err) => { .catch((err) => {
@@ -120,42 +104,53 @@ class SyncronizeTask {
alert("An error occurred while checking the datasource"); alert("An error occurred while checking the datasource");
}); });
} }
handleSubmitSnycGoogleSheet() {
const button = document.getElementById("btn-sync-submit-google-sheet");
button.addEventListener("click", function (e) {
button.disabled = true; // Disable button to prevent multiple clicks
button.textContent = "Syncing..."; // Change button text to show syncing
// Trigger the scraping API call handleSyncClick() {
fetch(`${GlobalConfig.apiHost}/api/sync-pbg-task-google-sheet`, { const button = document.getElementById("btn-sync-submit");
method: "GET", const spinner = document.getElementById("spinner");
headers: { const apiToken = document
Authorization: `Bearer ${document .querySelector('meta[name="api-token"]')
.querySelector('meta[name="api-token"]') .getAttribute("content");
.getAttribute("content")}`,
"Content-Type": "application/json", button.disabled = true; // Prevent multiple clicks
}, spinner.classList.remove("d-none"); // Show spinner during sync
fetch(`${GlobalConfig.apiHost}/api/scraping`, {
method: "GET",
headers: {
Authorization: `Bearer ${apiToken}`,
"Content-Type": "application/json",
},
})
.then(async (response) => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
let data;
try {
data = await response.json();
} catch (jsonError) {
throw new Error("Failed to parse JSON response");
}
return data;
}) })
.then((response) => { .then((data) => {
if (!response.ok) { this.toastMessage.innerText =
throw new Error("Network response was not ok"); data.data.message || "Synchronize successfully!";
} this.toast.show();
return response.json();
}) // Update the table if it exists
.then((data) => { if (this.table) {
console.log("data sync button", data); this.table.updateConfig({}).forceRender();
alert("Synchronization executed successfully"); }
window.location.reload(); })
}) .catch((err) => {
.catch((err) => { console.error("Fetch error:", err);
console.error("Fetch error:", err); alert("An error occurred during synchronization" + err.message);
alert("An error occurred during synchronization"); button.disabled = false;
}) });
.finally(() => {
button.disabled = false; // Re-enable the button after the request is complete
button.textContent = "Sync Google Sheet"; // Reset button text
});
});
} }
} }
document.addEventListener("DOMContentLoaded", function (e) { document.addEventListener("DOMContentLoaded", function (e) {

View File

@@ -21,7 +21,7 @@
<div class="wrapper"> <div class="wrapper">
<div id="lack-of-potential-fixed-container" class="" style="width:1400px;height:770px;position:relative;margin:auto;z-index:1;"> <div id="lack-of-potential-fixed-container" class="" style="width:1400px;height:770px;position:relative;margin:auto;z-index:1;">
<div style="position: absolute; top: 200px; left: 50px;"> <div style="position: absolute; top: 200px; left: 50px;">
<x-custom-circle title="Restoran" size="small" style="background-color: #0e4753;" /> <x-custom-circle title="Restoran" size="small" style="background-color: #0e4753;" visible_data="true" data_id="restoran-count" data_count="0" />
<div class="square dia-top-left-bottom-right" style="top:30px;left:50px;width:150px;height:120px;"></div> <div class="square dia-top-left-bottom-right" style="top:30px;left:50px;width:150px;height:120px;"></div>
<x-custom-circle title="PBB Bangunan" visible_data="true" data_id="pbb-bangunan-count" data_count="0" size="small" style="background-color: #0e4753;" /> <x-custom-circle title="PBB Bangunan" visible_data="true" data_id="pbb-bangunan-count" data_count="0" size="small" style="background-color: #0e4753;" />
<div class="square" style="width:150px;height:2px;background-color:black;left:50px;top:150px;"></div> <div class="square" style="width:150px;height:2px;background-color:black;left:50px;top:150px;"></div>
@@ -52,11 +52,11 @@
<div class="square dia-top-right-bottom-left" style="top:-110px;left:90px;width:150px;height:170px;"></div> <div class="square dia-top-right-bottom-left" style="top:-110px;left:90px;width:150px;height:170px;"></div>
<div class="square dia-top-left-bottom-right" style="top:-110px;left:230px;width:150px;height:170px;"></div> <div class="square dia-top-left-bottom-right" style="top:-110px;left:230px;width:150px;height:170px;"></div>
<div class="square dia-top-left-bottom-right" style="top:-110px;left:260px;width:200px;height:180px;"></div> <div class="square dia-top-left-bottom-right" style="top:-110px;left:260px;width:200px;height:180px;"></div>
<x-custom-circle title="Villa" size="small" style="float:left;background-color: #234f6c;" /> <x-custom-circle title="Villa" size="small" style="float:left;background-color: #234f6c;" visible_data="true" data_id="villa-count" data_count="0" />
<x-custom-circle title="Pabrik" size="small" style="float:left;background-color: #234f6c;" /> <x-custom-circle title="Pabrik" size="small" style="float:left;background-color: #234f6c;" />
<x-custom-circle title="Jalan Protocol" size="small" style="float:left;background-color: #234f6c;" /> <x-custom-circle title="Jalan Protocol" size="small" style="float:left;background-color: #234f6c;" />
<x-custom-circle title="Ruko" size="small" style="float:left;background-color: #234f6c;" /> <x-custom-circle title="Ruko" size="small" style="float:left;background-color: #234f6c;" />
<x-custom-circle title="Pariwisata" size="small" style="float:left;background-color: #234f6c; margin-right: 20px;" /> <x-custom-circle title="Pariwisata" size="small" style="float:left;background-color: #234f6c; margin-right: 20px;" visible_data="true" data_id="pariwisata-count" data_count="0" />
<div class="square" style="width:150px;height:2px;background-color:black;left:350px;top:50px;"></div> <div class="square" style="width:150px;height:2px;background-color:black;left:350px;top:50px;"></div>
<x-custom-circle title="DISBUDPAR" size="small" style="background-color: #3a968b;" /> <x-custom-circle title="DISBUDPAR" size="small" style="background-color: #3a968b;" />
</div> </div>
@@ -74,23 +74,20 @@
'visible_small_circle' => false, 'visible_small_circle' => false,
'style' => 'margin-left:180px;top:-20px;' 'style' => 'margin-left:180px;top:-20px;'
]) ])
@endcomponent @endcomponent
<x-custom-circle title="Tata Ruang" size="large" style="background-color: #da6635;float:left;margin-left:250px;" visible_data="true" data_id="tata-ruang-count" data_count="0" />
<x-custom-circle title="Tata Ruang" size="large" style="background-color: #da6635;float:left;margin-left:250px;" /> </div>
</div>
<div style="position: absolute; top: 310px; left: 1150px;"> <div style="position: absolute; top: 310px; left: 1150px;">
<div class="square dia-top-left-bottom-right" style="top:90px;left:-100px;width:100px;height:100px;"></div> <div class="square dia-top-left-bottom-right" style="top:90px;left:-100px;width:100px;height:100px;"></div>
<div class="square dia-top-right-bottom-left" style="top:-110px;left:-100px;width:100px;height:100px;"></div> <div class="square dia-top-right-bottom-left" style="top:-110px;left:-100px;width:100px;height:100px;"></div>
<x-custom-circle title="Peta" visible_data_type="true" data_type="1:5000" size="small" style="background-color: #224f6d;float:left;" /> <x-custom-circle title="Peta" visible_data_type="true" data_type="1:5000" size="small" style="background-color: #224f6d;float:left;" />
<x-custom-circle title="Tapak Bangunan" size="small" style="background-color: #2390af;float:left;margin-left:20px;" /> <x-custom-circle title="Tapak Bangunan" size="small" style="background-color: #2390af;float:left;margin-left:20px;" />
</div>
</div>
<x-custom-circle title="BPN" size="small" style="background-color: #2390af;position:absolute;left:1270px;top:440px;" /> <x-custom-circle title="BPN" size="small" style="background-color: #2390af;position:absolute;left:1270px;top:440px;" />
<div style="position: absolute; top: 470px; left: 430px;"> <div style="position: absolute; top: 470px; left: 430px;">
<div class="square dia-top-right-bottom-left" style="top:-80px;left:20px;width:150px;height:120px;"></div> <div class="square dia-top-right-bottom-left" style="top:-80px;left:20px;width:150px;height:120px;"></div>
<div class="square dia-top-right-bottom-left" style="top:-50px;left:100px;width:100px;height:100px;"></div> <div class="square dia-top-right-bottom-left" style="top:-50px;left:100px;width:100px;height:100px;"></div>
<div class="square dia-top-left-bottom-right" style="top:-50px;left:180px;width:100px;height:100px;"></div> <div class="square dia-top-left-bottom-right" style="top:-50px;left:180px;width:100px;height:100px;"></div>
@@ -105,7 +102,7 @@
<div style="position: absolute; top: 50px; left: 1100px;"> <div style="position: absolute; top: 50px; left: 1100px;">
<x-custom-circle title="Non Usaha" size="large" style="background-color: #3a968b;margin-top:20px;" /> <x-custom-circle title="Non Usaha" size="large" style="background-color: #3a968b;margin-top:20px;" />
<x-custom-circle title="USAHA" size="large" style="background-color: #627c8b;margin-top:260px;" /> <x-custom-circle title="USAHA" size="large" style="background-color: #627c8b;margin-top:260px;" visible_data="true" data_id="tata-ruang-usaha-count" data_count="0" />
</div> </div>
</div> </div>
</div> </div>

View File

@@ -3,32 +3,57 @@
<style> <style>
/* .floating-icon { /* .floating-icon {
position: fixed;
right: 30px;
bottom: 70px;
background: white;
padding: 10px;
border-radius: 50%;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
cursor: pointer;
z-index: 1000;
} */
.floating-icon {
position: fixed; position: fixed;
right: 40px; right: 40px;
bottom: 100px; bottom: 100px;
width: 70px; /* Sesuaikan ukuran */ width: 70px;
height: 70px; /* Sesuaikan ukuran */ height: 70px;
background: white; background: white;
border-radius: 50%; border-radius: 50%;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
cursor: pointer; cursor: pointer;
z-index: 1000; z-index: 1000;
background-image: url('/images/iconchatbot.jpeg'); /* Path ke gambar */ background-image: url('/images/iconchatbot.jpeg');
background-size: cover; /* Agar gambar menyesuaikan */ background-size: cover;
background-position: center; /* Memusatkan gambar */ background-position: center;
} */
.floating-icon {
position: fixed;
right: 20px;
bottom: 20px;
width: 60px;
height: 60px;
background: white;
border-radius: 50%;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
cursor: pointer;
z-index: 1000;
background-image: url('/images/iconchatbot.jpeg');
background-size: contain;
background-repeat: no-repeat;
background-position: center;
transition: transform 0.3s ease-in-out, box-shadow 0.3s ease-in-out;
} }
.floating-icon:hover {
transform: scale(1.1);
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3);
}
@keyframes bounce {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-10px);
}
}
.floating-icon.animate {
animation: bounce 1s infinite;
}
</style> </style>
<head> <head>

View File

@@ -7,14 +7,16 @@
@section('content') @section('content')
@include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Syncronize']) @include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Syncronize'])
<x-toast-notification />
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="card w-100"> <div class="card w-100">
<div class="card-body"> <div class="card-body">
<div class="d-flex flex-wrap justify-content-end gap-2"> <div class="d-flex flex-wrap justify-content-end gap-2">
<button type="button" class="btn btn-success btn-sm d-block d-sm-inline w-auto" id="btn-sync-submit-google-sheet">Sync Google Sheet</button> <button type="button" class="btn btn-success btn-sm d-block d-sm-inline w-auto" id="btn-sync-submit">
<button type="button" class="btn btn-success btn-sm d-block d-sm-inline w-auto" id="btn-sync-submit">Sync SIMBG</button> <span id="spinner" class="spinner-border spinner-border-sm me-1 d-none" role="status" aria-hidden="true"></span>
Sync SIMBG
</button>
</div> </div>
<div> <div>
<div id="table-import-datasources"></div> <div id="table-import-datasources"></div>

View File

@@ -8,4 +8,4 @@ Artisan::command('inspire', function () {
$this->comment(Inspiring::quote()); $this->comment(Inspiring::quote());
})->purpose('Display an inspiring quote')->hourly(); })->purpose('Display an inspiring quote')->hourly();
Schedule::command("app:execute-scraping")->daily(); Schedule::command("app:execute-scraping")->dailyAt("00:00");

View File

@@ -77,30 +77,30 @@ Route::group(['middleware' => 'auth'], function(){
// data // data
Route::group(['prefix' => '/data'], function(){ Route::group(['prefix' => '/data'], function(){
// Resource route, kecuali create karena dibuat terpisah // Resource route, kecuali create karena dibuat terpisah
Route::resource('/advertisements', AdvertisementController::class)->except(['create', 'show']); Route::resource('/web-advertisements', AdvertisementController::class)->except(['create', 'show']);
// Rute khusus untuk create dan bulk-create // Rute khusus untuk create dan bulk-create
Route::get('/advertisements/create', [AdvertisementController::class, 'create'])->name('advertisements.create'); Route::get('/advertisements/create', [AdvertisementController::class, 'create'])->name('advertisements.create');
Route::get('/advertisements/bulk-create', [AdvertisementController::class, 'bulkCreate'])->name('advertisements.bulk-create'); Route::get('/advertisements/bulk-create', [AdvertisementController::class, 'bulkCreate'])->name('advertisements.bulk-create');
// Resource route, kecuali create karena dibuat terpisah // Resource route, kecuali create karena dibuat terpisah
Route::resource('/umkm', UmkmController::class)->except(['create', 'show']); Route::resource('/web-umkm', UmkmController::class)->except(['create', 'show']);
// Rute khusus untuk create dan bulk-create // Rute khusus untuk create dan bulk-create
Route::get('/umkm/create', [UmkmController::class, 'create'])->name('umkm.create'); Route::get('/umkm/create', [UmkmController::class, 'create'])->name('umkm.create');
Route::get('/umkm/bulk-create', [UmkmController::class, 'bulkCreate'])->name('umkm.bulk-create'); Route::get('/umkm/bulk-create', [UmkmController::class, 'bulkCreate'])->name('umkm.bulk-create');
// Resource route, kecuali create karena dibuat terpisah // Resource route, kecuali create karena dibuat terpisah
Route::resource('/tourisms', TourismController::class)->except(['create', 'show']); Route::resource('/web-tourisms', TourismController::class)->except(['create', 'show']);
// Rute khusus untuk create dan bulk-create // Rute khusus untuk create dan bulk-create
Route::get('/tourisms/create', [TourismController::class, 'create'])->name('tourisms.create'); Route::get('/tourisms/create', [TourismController::class, 'create'])->name('tourisms.create');
Route::get('/tourisms/bulk-create', [TourismController::class, 'bulkCreate'])->name('tourisms.bulk-create'); Route::get('/tourisms/bulk-create', [TourismController::class, 'bulkCreate'])->name('tourisms.bulk-create');
// Resource route, kecuali create karena dibuat terpisah // Resource route, kecuali create karena dibuat terpisah
Route::resource('/spatial-plannings', SpatialPlanningController::class)->except(['create', 'show']); Route::resource('/web-spatial-plannings', SpatialPlanningController::class)->except(['create', 'show']);
// Rute khusus untuk create dan bulk-create // Rute khusus untuk create dan bulk-create
Route::get('/spatial-plannings/create', [SpatialPlanningController::class, 'create'])->name('tourisms.create'); Route::get('/spatial-plannings/create', [SpatialPlanningController::class, 'create'])->name('spatial-plannings.create');
Route::get('/spatial-plannings/bulk-create', [SpatialPlanningController::class, 'bulkCreate'])->name('tourisms.bulk-create'); Route::get('/spatial-plannings/bulk-create', [SpatialPlanningController::class, 'bulkCreate'])->name('spatial-plannings.bulk-create');
Route::resource('/business-industries',BusinessOrIndustriesController::class); Route::resource('/business-industries',BusinessOrIndustriesController::class);

View File

@@ -100,6 +100,7 @@ export default defineConfig({
// laporan pimpinan // laporan pimpinan
"resources/js/bigdata-resumes/index.js", "resources/js/bigdata-resumes/index.js",
"resources/js/chatbot/index.js", "resources/js/chatbot/index.js",
"resources/js/chatbot-pimpinan/index.js",
], ],
refresh: true, refresh: true,
}), }),