Compare commits

...

42 Commits

Author SHA1 Message Date
@jamaludinarifrohman6661
fefef609ac feature: chatbot 2025-02-28 17:53:16 +07:00
arifal
7f8a2e4936 Merge branch 'feature/dashboard-pbg' into dev 2025-02-25 11:34:08 +07:00
@jamaludinarifrohman6661
38948b6633 bug-fix: map tourisms 2025-02-25 11:32:39 +07:00
arifal
aa9943ba45 hot fix vite config js resources 2025-02-25 04:18:15 +00:00
arifal
9e55ea0dbb fix change api route data settings 2025-02-25 10:41:59 +07:00
arifal
93f0ac5ef7 Merge branch 'feature/dashboard-pbg' into fix/dashboard-resume 2025-02-25 10:23:41 +07:00
@jamaludinarifrohman6661
8bae33519f feature: sebaran lokasi project pariwisata 2025-02-24 21:47:24 +07:00
@jamaludinarifrohman6661
6865e353e6 feature: first check point 2025-02-24 20:03:36 +07:00
arifal
bb4ab5c769 add filter date for dashboard 2025-02-24 19:00:09 +07:00
arifal hidayat
e743b82087 add remove disabled if error on data settings 2025-02-23 07:11:14 +07:00
arifal hidayat
c3c7b8e3ec fix js roles and add back button on card header 2025-02-23 07:08:50 +07:00
arifal hidayat
d49035ce8d fix js menus and handle api menus 2025-02-23 06:42:13 +07:00
arifal hidayat
ffd9d3514c fix business industries and data settings front end 2025-02-22 16:08:59 +07:00
arifal
675477c734 fix service sync simbg upsert integrations 2025-02-21 18:49:42 +07:00
arifal
4350c466e3 add backup db from local 2025-02-21 18:18:45 +07:00
arifal
5ab407d672 change get value dashboard pimpinan from resume bigdata 2025-02-21 18:09:32 +07:00
arifal
2aaa487746 fix get total data from api 2025-02-21 14:49:09 +07:00
arifal
080582f7ab add new menu peta in dashboard 2025-02-21 11:12:35 +07:00
arifal
91475aeead Merge branch 'dev' into feat/load-map-from-kmz 2025-02-21 10:43:06 +07:00
arifal
a7afbd69fb fix conflict spatial-plannings jamal 2025-02-21 09:07:15 +07:00
arifal
1ff5587050 merge conflix spatial-plannings 2025-02-21 08:42:08 +07:00
arifal
9c8a1a3f3f add vite js customers 2025-02-21 00:49:29 +07:00
arifal
5c4be7635b partial update crud customer data 2025-02-21 00:46:33 +07:00
@jamaludinarifrohman6661
ba315b1dee Fix: upload bulk in column date, and fix template 2025-02-20 22:54:41 +07:00
@jamaludinarifrohman6661
33b7131c33 feature: crud spatial planning and fix search in tourism and umkm 2025-02-20 16:16:06 +07:00
arifal
7a56735099 done crud spatial plannings 2025-02-20 15:35:06 +07:00
@jamaludinarifrohman6661
dd331b4a08 create message error validation on advertisementrequest and tourismsrequest 2025-02-20 10:59:32 +07:00
@jamaludinarifrohman6661
dcd93d66e2 fix: change path download template and add template pariwisata 2025-02-20 10:36:56 +07:00
arifal
5d50d6d6cc fix height card 2025-02-19 15:34:28 +07:00
arifal
39717d184c partial update create google map from kmz file 2025-02-19 14:31:12 +07:00
arifal
54146c8c08 partial update spatial plannings crud 2025-02-19 12:08:18 +07:00
arifal
e8da7193ef fix crud users 2025-02-19 11:23:15 +07:00
arifal
006c008542 done dashboard-potential 2025-02-19 07:22:06 +07:00
root
39bb8e6d5f hot fix vite and create view 2025-02-18 23:49:48 +00:00
arifal
7994a62cea change name dashboard 2025-02-19 06:24:44 +07:00
arifal
3a09e2e68a Merge branch 'bug-fix/tourisms' into feat/dashboard-kekurangan-potensi 2025-02-19 06:17:16 +07:00
arifal
116dc1c8c7 add dashboard potential 2025-02-19 06:15:35 +07:00
@jamaludinarifrohman6661
f10bc153b4 fix: tourisms 2025-02-19 01:39:27 +07:00
@jamaludinarifrohman6661
06a3bb8c8b fix:pariwisata and report pariwisata 2025-02-19 01:07:20 +07:00
arifal
6c936d4c14 add more handle if zero devided 2025-02-18 03:40:20 +07:00
arifal
d4d0a485cd fix vite resources 2025-02-18 03:32:00 +07:00
arifal
864fb26471 add local db backup 2025-02-18 02:58:11 +07:00
220 changed files with 6919 additions and 3795 deletions

View File

@@ -199,7 +199,7 @@ class AdvertisementController extends Controller
public function downloadExcelAdvertisement() public function downloadExcelAdvertisement()
{ {
$filePath = storage_path('app/public/templates/template_reklame.xlsx'); $filePath = public_path('templates/template_reklame.xlsx');
// Cek apakah file ada // Cek apakah file ada
if (!file_exists($filePath)) { if (!file_exists($filePath)) {

View File

@@ -0,0 +1,241 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\BigdataResume;
use App\Models\DataSetting;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class BigDataResumeController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
try{
$filterDate = $request->get("filterByDate");
// If filterByDate is "latest" or empty, get the most recent record
if (!$filterDate || $filterDate === "latest") {
$big_data_resume = BigdataResume::latest()->first();
} else {
// Filter by specific date
$big_data_resume = BigdataResume::whereDate('created_at', $filterDate)
->orderBy('id', 'desc')
->first();
if (!$big_data_resume) {
return $this->response_empty_resume();
}
}
$data_settings = DataSetting::all();
if($data_settings->isEmpty()){
return response()->json(['message' => 'No data setting found']);
}
$target_pad = floatval(optional($data_settings->where('key', 'TARGET_PAD')->first())->value);
$tata_ruang = floatval(optional($data_settings->where('key', 'TATA_RUANG')->first())->value);
$realisasi_terbit_pbg_sum = floatval(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);
$menuggu_klik_dpmptsp_sum = floatval(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);
$proses_dinas_teknis_sum = floatval(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);
$kekurangan_potensi = $target_pad - $big_data_resume->potention_sum;
// percentage kekurangan potensi
$kekurangan_potensi_percentage = $target_pad > 0 && $target_pad > 0
? round(($kekurangan_potensi / $target_pad) * 100, 2) : 0;
// percentage total potensi
$total_potensi_percentage = $big_data_resume->potention_sum > 0 && $target_pad > 0
? round(($big_data_resume->potention_sum / $target_pad) * 100, 2) : 0;
// percentage verified document
$verified_percentage = $big_data_resume->verified_sum > 0 && $big_data_resume->potention_sum > 0
? round(($big_data_resume->verified_sum / $big_data_resume->potention_sum) * 100, 2) : 0;
// percentage non-verified document
$non_verified_percentage = $big_data_resume->non_verified_sum > 0 && $big_data_resume->potention_sum > 0
? round(($big_data_resume->non_verified_sum / $big_data_resume->potention_sum) * 100, 2) : 0;
// percentage business document
$business_percentage = $big_data_resume->business_sum > 0 && $big_data_resume->non_verified_sum > 0
? round(($big_data_resume->business_sum / $big_data_resume->non_verified_sum) * 100, 2) : 0;
// percentage non-business document
$non_business_percentage = $big_data_resume->non_business_sum > 0 && $big_data_resume->potention_sum > 0
? round(($big_data_resume->non_business_sum / $big_data_resume->potention_sum) * 100, 2) : 0;
// percentage tata ruang
$tata_ruang_percentage = $tata_ruang > 0 && $big_data_resume->potention_sum > 0
? round(($tata_ruang / $big_data_resume->potention_sum) * 100, 2) : 0;
// percentage realisasi terbit pbg
$realisasi_terbit_percentage = $big_data_resume->verified_sum > 0 && $realisasi_terbit_pbg_sum > 0
? round(($realisasi_terbit_pbg_sum / $big_data_resume->verified_sum) * 100, 2) : 0;
// percentage menunggu klik dpmptsp
$menunggu_klik_dpmptsp_percentage = $big_data_resume->verified_sum > 0 && $menuggu_klik_dpmptsp_sum > 0
? round(($menuggu_klik_dpmptsp_sum / $big_data_resume->verified_sum) * 100, 2) : 0;
// percentage proses_dinas_teknis
$proses_dinas_teknis_percentage = $big_data_resume->verified_sum > 0 && $proses_dinas_teknis_sum > 0
? round(($proses_dinas_teknis_sum / $big_data_resume->verified_sum) * 100, 2) : 0;
$result = [
'target_pad' => [
'sum' => $target_pad,
'percentage' => 100,
],
'tata_ruang' => [
'sum' => $tata_ruang,
'percentage' => $tata_ruang_percentage,
],
'kekurangan_potensi' => [
'sum' => $kekurangan_potensi,
'percentage' => $kekurangan_potensi_percentage
],
'total_potensi' => [
'sum' => (float) $big_data_resume->potention_sum,
'count' => $big_data_resume->potention_count,
'percentage' => $total_potensi_percentage
],
'verified_document' => [
'sum' => (float) $big_data_resume->verified_sum,
'count' => $big_data_resume->verified_count,
'percentage' => $verified_percentage
],
'non_verified_document' => [
'sum' => (float) $big_data_resume->non_verified_sum,
'count' => $big_data_resume->non_verified_count,
'percentage' => $non_verified_percentage
],
'business_document' => [
'sum' => (float) $big_data_resume->business_sum,
'count' => $big_data_resume->business_count,
'percentage' => $business_percentage
],
'non_business_document' => [
'sum' => (float) $big_data_resume->non_business_sum,
'count' => $big_data_resume->non_business_count,
'percentage' => $non_business_percentage
],
'realisasi_terbit' => [
'sum' => $realisasi_terbit_pbg_sum,
'count' => $realisasi_terbit_pbg_count,
'percentage' => $realisasi_terbit_percentage
],
'menunggu_klik_dpmptsp' => [
'sum' => $menuggu_klik_dpmptsp_sum,
'count' => $menuggu_klik_dpmptsp_count,
'percentage' => $menunggu_klik_dpmptsp_percentage
],
'proses_dinas_teknis' => [
'sum' => $proses_dinas_teknis_sum,
'count' => $proses_dinas_teknis_count,
'percentage' => $proses_dinas_teknis_percentage
]
];
return response()->json($result);
}catch(\Exception $e){
return response()->json(['message' => 'Error when fetching data'], 500);
}
}
/**
* 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(){
$result = [
'target_pad' => [
'sum' => 0,
'percentage' => 100,
],
'tata_ruang' => [
'sum' => 0,
'percentage' => 0,
],
'kekurangan_potensi' => [
'sum' => 0,
'percentage' => 0
],
'total_potensi' => [
'sum' => 0,
'count' => 0,
'percentage' => 0
],
'verified_document' => [
'sum' => 0,
'count' => 0,
'percentage' => 0
],
'non_verified_document' => [
'sum' => 0,
'count' => 0,
'percentage' => 0
],
'business_document' => [
'sum' => 0,
'count' => 0,
'percentage' => 0
],
'non_business_document' => [
'sum' => 0,
'count' => 0,
'percentage' => 0
],
'realisasi_terbit' => [
'sum' => 0,
'count' => 0,
'percentage' => 0
],
'menunggu_klik_dpmptsp' => [
'sum' => 0,
'count' => 0,
'percentage' => 0
],
'proses_dinas_teknis' => [
'sum' => 0,
'count' => 0,
'percentage' => 0
]
];
return response()->json($result);
}
}

View File

@@ -0,0 +1,84 @@
<?php
namespace App\Http\Controllers\Api;
use Illuminate\Http\Request;
use App\Services\OpenAIService;
use App\Http\Controllers\Controller;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\DB;
class ChatbotController extends Controller
{
protected $openAIService;
public function __construct(OpenAIService $openAIService)
{
$this->openAIService = $openAIService;
}
public function generateText(Request $request)
{
info($request);
$request->validate([
'tab_active' => 'required|string',
'prompt' => 'required|string',
]);
$tab_active = $request->input('tab_active');
$main_content = match ($tab_active) {
"count-retribusi" => "RETRIBUTION",
"document-validation" => "DOCUMENT VALIDATION",
"data-information" => "DATA SUMMARY",
default => "UNKNOWN",
};
if ($main_content === "UNKNOWN") {
return response()->json(['response' => 'Invalid tab_active value.'], 400);
}
info($main_content);
// Klasifikasi apakah pertanyaan butuh database atau bisa dijawab langsung
$classifyResponse = $this->openAIService->generateClassifyMainContent($request->input('prompt'), $main_content);
if ($classifyResponse === "DATABASE") {
$queryResponse = $this->openAIService->generateQueryBasedMainContent($request->input('prompt'), $main_content);
if (is_array($queryResponse)) {
info('Query Response is an array: ', $queryResponse);
} else {
info('Query Response is a string: ' . $queryResponse);
}
// Validasi query dua kali sebelum eksekusi
if (
$this->openAIService->validateSyntaxQuery($queryResponse) === "VALID" &&
$this->openAIService->validateSyntaxQuery($queryResponse) === "VALID"
) {
info($queryResponse);
$queryResponse = str_replace(['```sql', '```'], '', $queryResponse);
$resultQuery = DB::select($queryResponse);
$formattedResultQuery = json_encode($resultQuery, JSON_PRETTY_PRINT);
$nlpResult = $this->openAIService->generateNLPFromQuery($request->input('prompt'), $formattedResultQuery);
$finalGeneratedText =$this->openAIService->generateFinalText($nlpResult);
return response()->json(['response' => $finalGeneratedText]);
}
return response()->json(['response' => ''], 400);
}
if ($classifyResponse === "GENERAL") {
$nlpResult = $this->openAIService->generateGeneralText($request->input('prompt'), $main_content);
$finalGeneratedText =$this->openAIService->generateFinalText($nlpResult);
return response()->json(['response' => $finalGeneratedText]);
}
return response()->json(['response' => ''], 500);
}
private function classifyContent(string $prompt) {
$classifyResponse = $this->openAIService->generateClassifyContent($prompt);
return $classifyResponse;
}
}

View File

@@ -0,0 +1,130 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Http\Requests\CustomersRequest;
use App\Http\Requests\ExcelUploadRequest;
use App\Http\Resources\CustomersResource;
use App\Imports\CustomersImport;
use App\Models\Customer;
use Illuminate\Http\Request;
use Maatwebsite\Excel\Facades\Excel;
class CustomersController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
$query = Customer::query()->orderBy('id', 'desc');
if ($request->has('search') &&!empty($request->get('search'))) {
$query = $query->where('nomor_pelanggan', 'LIKE', '%'.$request->get('search').'%')
->orWhere('nama', 'LIKE', '%'.$request->get('search').'%')
->orWhere('kota_palayanan', 'LIKE', '%'.$request->get('search').'%');
}
return CustomersResource::collection($query->paginate());
}
/**
* Store a newly created resource in storage.
*/
public function store(CustomersRequest $request)
{
try{
$customer = Customer::create($request->validated());
return response()->json(['message' => 'Successfully created', new CustomersResource($customer)]);
}catch(\Exception $e){
return response()->json([
'message' => 'Failed to create customer',
'error' => $e->getMessage()
], 500);
}
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
try{
$customer = Customer::find($id);
if($customer){
return new CustomersResource($customer);
} else {
return response()->json(['message' => 'Customer not found'], 404);
}
}catch(\Exception $e){
return response()->json([
'message' => 'Failed to retrieve customer',
'error' => $e->getMessage()
], 500);
}
}
/**
* Update the specified resource in storage.
*/
public function update(CustomersRequest $request, string $id)
{
try{
$customer = Customer::find($id);
if($customer){
$customer->update($request->validated());
return response()->json(['message' => 'Successfully updated', new CustomersResource($customer)]);
} else {
return response()->json(['message' => 'Customer not found'], 404);
}
}catch(\Exception $e) {
return response()->json([
'message' => 'Failed to update customer',
'error' => $e->getMessage()
], 500);
}
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id)
{
try{
$customer = Customer::find($id);
if($customer){
$customer->delete();
return response()->json(['message' => 'Successfully deleted']);
}else {
return response()->json(['message' => 'Customer not found'], 404);
}
}catch(\Exception $e) {
return response()->json([
'message' => 'Failed to delete customer',
'error' => $e->getMessage()
], 500);
}
}
public function upload(ExcelUploadRequest $request){
try{
if(!$request->hasFile('file')){
return response()->json([
'error' => 'No file provided'
], 400);
}
$file = $request->file('file');
Excel::import(new CustomersImport, $file);
return response()->json([
'message' => 'File uploaded successfully',
]);
}catch(\Exception $e){
\Log::info($e->getMessage());
return response()->json([
'error' => 'Failed to upload file',
'message' => $e->getMessage()
], 500);
}
}
}

View File

@@ -0,0 +1,31 @@
<?php
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use App\Models\Advertisement;
use App\Models\Customer;
use App\Models\SpatialPlanning;
use Illuminate\Http\Request;
class LackOfPotentialController extends Controller
{
public function count_lack_of_potential(){
try{
$total_reklame = Advertisement::count();
$total_pdam = Customer::count();
$total_tata_ruang = SpatialPlanning::count();
return response()->json([
'total_reklame' => $total_reklame,
'total_pdam' => $total_pdam,
'total_tata_ruang' => $total_tata_ruang
], 200);
}catch(\Exception $e){
return response()->json([
'message' => 'Error: '.$e->getMessage()
], 500);
}
}
}

View File

@@ -3,8 +3,11 @@
namespace App\Http\Controllers\Api; namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\MenuRequest;
use App\Http\Resources\MenuResource;
use App\Models\Menu; use App\Models\Menu;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
class MenusController extends Controller class MenusController extends Controller
{ {
@@ -13,7 +16,7 @@ class MenusController extends Controller
*/ */
public function index(Request $request) public function index(Request $request)
{ {
$query = Menu::query(); $query = Menu::query()->orderBy('id', 'desc');
if($request->has("search") && !empty($request->get("search"))){ if($request->has("search") && !empty($request->get("search"))){
$query = $query->where("name", "like", "%".$request->get("search")."%"); $query = $query->where("name", "like", "%".$request->get("search")."%");
@@ -25,9 +28,15 @@ class MenusController extends Controller
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
*/ */
public function store(Request $request) public function store(MenuRequest $request)
{ {
// try{
$menu = Menu::create($request->validated());
return response()->json(['message' => 'Menu created successfully', 'data' => new MenuResource($menu)]);
}catch(\Exception $e){
Log::error($e);
return response()->json(['message' => 'Error when creating menu'], 500);
}
} }
/** /**
@@ -35,15 +44,37 @@ class MenusController extends Controller
*/ */
public function show(string $id) public function show(string $id)
{ {
// try{
$menu = Menu::find($id);
if($menu){
return response()->json(['message' => 'Menu found', 'data' => new MenuResource($menu)]);
} else {
return response()->json(['message' => 'Menu not found'], 404);
}
}catch(\Exception $e){
Log::error($e);
Log::error($e->getMessage());
return response()->json(['message' => 'Error when finding menu'], 500);
}
} }
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
*/ */
public function update(Request $request, string $id) public function update(MenuRequest $request, string $id)
{ {
// try{
$menu = Menu::findOrFail($id);
if($menu){
$menu->update($request->validated());
return response()->json(['message' => 'Menu updated successfully', 'data' => new MenuResource($menu)]);
} else {
return response()->json(['message' => 'Menu not found'], 404);
}
}catch(\Exception $e){
Log::error($e);
return response()->json(['message' => 'Error when updating menu'], 500);
}
} }
/** /**
@@ -51,6 +82,28 @@ class MenusController extends Controller
*/ */
public function destroy(string $id) public function destroy(string $id)
{ {
// try{
$menu = Menu::findOrFail($id);
if($menu){
$this->deleteChildren($menu);
$menu->roles()->detach();
$menu->delete();
return response()->json(['message' => 'Menu deleted successfully']);
} else {
return response()->json(['message' => 'Menu not found'], 404);
}
}catch(\Exception $e){
Log::error($e);
return response()->json(['message' => 'Error when deleting menu'], 500);
}
}
private function deleteChildren($menu)
{
foreach ($menu->children as $child) {
$this->deleteChildren($child); // Recursively delete its children
$child->roles()->detach(); // Detach roles before deleting
$child->delete();
}
} }
} }

View File

@@ -21,9 +21,35 @@ class PbgTaskController extends Controller
public function __construct(GoogleSheetService $googleSheetService){ public function __construct(GoogleSheetService $googleSheetService){
$this->googleSheetService = $googleSheetService; $this->googleSheetService = $googleSheetService;
} }
public function index() public function index(Request $request)
{ {
// info($request);
$isLastUpdated = filter_var($request->query('isLastUpdated', false), FILTER_VALIDATE_BOOLEAN);
$query = PbgTask::query();
if ($isLastUpdated) {
$query->orderBy('updated_at', 'desc');
} else {
$query->where('status', 20);
}
// Ambil maksimal 10 data
$pbg_task = $query->limit(10)->get();
$totalData = $pbg_task->count();
// Tambahkan nomor urut
$data = $pbg_task->map(function ($item, $index) {
return array_merge($item->toArray(), ['no' => $index + 1]);
});
return response()->json([
'data' => $data,
'meta' => [
'total' => $totalData
]
]);
} }
/** /**

View File

@@ -3,6 +3,7 @@
namespace App\Http\Controllers\Api; namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Http\Requests\RoleRequest;
use App\Models\Role; use App\Models\Role;
use Illuminate\Http\Request; use Illuminate\Http\Request;
@@ -13,7 +14,7 @@ class RolesController extends Controller
*/ */
public function index(Request $request) public function index(Request $request)
{ {
$query = Role::query(); $query = Role::query()->orderBy('id', 'desc');
if($request->has('search') && !empty($request->get('search'))){ if($request->has('search') && !empty($request->get('search'))){
$query = $query->where('name', 'like', '%'. $request->get('search') . '%'); $query = $query->where('name', 'like', '%'. $request->get('search') . '%');
@@ -25,9 +26,14 @@ class RolesController extends Controller
/** /**
* Store a newly created resource in storage. * Store a newly created resource in storage.
*/ */
public function store(Request $request) public function store(RoleRequest $request)
{ {
// try{
$role = Role::create($request->validated());
return response()->json(['message' => 'Successfully created', 'data' => $role]);
}catch(\Exception $e){
return response()->json(['message' => 'Error when creating role', 'error' => $e->getMessage()], 500);
}
} }
/** /**
@@ -35,15 +41,34 @@ class RolesController extends Controller
*/ */
public function show(string $id) public function show(string $id)
{ {
// try{
$role = Role::find($id);
if($role){
return response()->json(['data' => $role]);
} else {
return response()->json(['message' => 'Role not found'], 404);
}
}catch(\Exception $e){
return response()->json(['message' => 'Error when getting role', 'error' => $e->getMessage()], 500);
}
} }
/** /**
* Update the specified resource in storage. * Update the specified resource in storage.
*/ */
public function update(Request $request, string $id) public function update(RoleRequest $request, string $id)
{ {
// try{
$role = Role::find($id);
if($role){
$role->update($request->validated());
return response()->json(['message' => 'Successfully updated', 'data' => $role]);
} else {
return response()->json(['message' => 'Role not found'], 404);
}
}catch(\Exception $e){
return response()->json(['message' => 'Error when updating role', 'error' => $e->getMessage()], 500);
}
} }
/** /**
@@ -51,6 +76,16 @@ class RolesController extends Controller
*/ */
public function destroy(string $id) public function destroy(string $id)
{ {
// try{
$role = Role::find($id);
if($role){
$role->delete();
return response()->json(['message' => 'Successfully deleted']);
} else {
return response()->json(['message' => 'Role not found'], 404);
}
}catch(\Exception $e){
return response()->json(['message' => 'Error when deleting role', 'error' => $e->getMessage()], 500);
}
} }
} }

View File

@@ -0,0 +1,146 @@
<?php
namespace App\Http\Controllers\Api;
use App\Models\SpatialPlanning;
use Illuminate\Http\Request;
use App\Http\Requests\SpatialPlanningRequest;
use Illuminate\Http\Response;
use App\Http\Controllers\Controller;
use App\Http\Resources\SpatialPlanningResource;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Validator;
use Maatwebsite\Excel\Facades\Excel;
use App\Imports\SpatialPlanningImport;
use Illuminate\Support\Facades\Storage;
class SpatialPlanningController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(Request $request)
{
info($request);
$perPage = $request->input('per_page', 15);
$search = $request->input('search', '');
$query = SpatialPlanning::query();
if ($search) {
$query->where(function ($q) use ($search) {
$q->where('name', 'like', "%$search%")
->orWhere('kbli', 'like', "%$search%")
->orWhere('activities', 'like', "%$search%")
->orWhere('area', 'like', "%$search%")
->orWhere('location', 'like', "%$search%")
->orWhere('number', 'like', "%$search%");
});
}
$spatialPlannings = $query->paginate($perPage);
// Menambhakan nomor urut (No)
$start = ($spatialPlannings->currentPage()-1) * $perPage + 1;
// Tambahkan nomor urut ke dalam data
$data = $spatialPlannings->map(function ($item, $index) use ($start) {
return array_merge($item->toArray(), ['no' => $start + $index]);
});
info($data);
return response()->json([
'data' => $data,
'meta' => [
'total' => $spatialPlannings->total(),
'per_page' => $spatialPlannings->perPage(),
'current_page' => $spatialPlannings->currentPage(),
'last_page'=>$spatialPlannings->lastPage(),
]
]);
}
/**
* Store a newly created resource in storage.
*/
public function store(SpatialPlanningRequest $request): SpatialPlanning
{
$data = $request->validated();
return SpatialPlanning::create($data);
}
/**
* import spatial planning from excel
*/
public function importFromFile(Request $request)
{
info($request);
//validasi file
$validator = Validator::make($request->all(), [
'file' => 'required|mimes:xlsx, xls|max:10240'
]);
if ($validator->fails()) {
return response()->json([
'message'=>'File vaildation failed.',
"errors"=>$validator->errors()
], 400);
}
try {
$file = $request->file('file');
Excel::import(new SpatialPlanningImport, $file);
return response()->json([
'message'=>'File uploaded and imported successfully!'
], 200);
} catch (\Exception $e) {
return response()->json([
'message'=>'Error during file import.',
'error'=>$e->getMessage()
], 500);
}
}
/**
* Display the specified resource.
*/
public function show(SpatialPlanning $spatialPlanning): SpatialPlanning
{
return $spatialPlanning;
}
/**
* Update the specified resource in storage.
*/
public function update(SpatialPlanningRequest $request, SpatialPlanning $spatialPlanning): SpatialPlanning
{
info($request);
$data = $request->validated();
info($data);
$spatialPlanning->update($data);
return $spatialPlanning;
}
public function destroy(SpatialPlanning $spatialPlanning): Response
{
$spatialPlanning->delete();
return response()->noContent();
}
public function downloadExcelSpatialPlanning()
{
$filePath = public_path('templates/template_spatial_planning.xlsx');
info(sprintf("File Path: %s | Exists: %s", $filePath, file_exists($filePath) ? 'Yes' : 'No'));
// Cek apakah file ada
if (!file_exists($filePath)) {
return response()-> json(['message' => 'File tidak ditemukan!'], Response::HTTP_NOT_FOUND);
}
// Return file to download
return response()->download($filePath);
}
}

View File

@@ -21,32 +21,41 @@ class TourismController extends Controller
*/ */
public function index(Request $request) public function index(Request $request)
{ {
info($request);
$perPage = $request->input('per_page', 15); $perPage = $request->input('per_page', 15);
$search = $request->input('search', ''); $search = $request->input('search', '');
$query = Tourism::query(); $query = Tourism::query();
if ($search) {
$query->where(function ($q) use ($search) {
$q->where('business_name', 'like', "%$search%")
->orWhere('project_name', 'like', "%$search%")
->orWhere('business_address', 'like', "%$search%");
});
}
$tourisms = $query->paginate($perPage); $tourisms = $query->paginate($perPage);
$tourisms->getCollection()->transform(function ($tourisms) { $tourisms->getCollection()->transform(function ($tourisms) {
$village = DB::table('villages')->where('village_code', $tourisms->village_code)->first(); $village = DB::table('villages')->where('village_code', $tourisms->village_code)->first();
$tourisms->village_name = $village ? $village->village_name : null; $tourisms->village_name = $village ? $village->village_name : null;
$district = DB::table('districts')->where('district_code', $tourisms->district_code)->first();
$tourisms->district_name = $district ? $district->district_name : null;
$business_type = DB::table('business_type')->where('id', $tourisms->business_type_id)->first(); $district = DB::table('districts')->where('district_code', $tourisms->district_code)->first();
$tourisms->business_type = $business_type ? $business_type->business_type : null; $tourisms->district_name = $village ? $village->village_name : null;
return $tourisms; return $tourisms;
}); });
$start = ($tourisms->currentPage()-1) * $perPage + 1;
$data = $tourisms->map(function ($item, $index) use ($start) {
return array_merge($item->toArray(), ['no' => $start + $index]);
});
return response()->json([ return response()->json([
'data' => TourismResource::collection($tourisms), 'data' => $data,
'meta' => [ 'meta' => [
'total' => $tourisms->total(), 'total' => $tourisms->total(),
'per_page' => $tourisms->perPage(), 'per_page' => $tourisms->perPage(),
'current_page' => $tourisms->currentPage(), 'current_page' => $tourisms->currentPage(),
'last_page' => $tourisms->lastPage(), 'last_page'=>$tourisms->lastPage(),
] ]
]); ]);
} }
@@ -62,11 +71,12 @@ class TourismController extends Controller
$data['district_code'] = $district_code; $data['district_code'] = $district_code;
$data['village_code'] = $village_code; $data['village_code'] = $village_code;
return Tourism::create($data); return Tourism::create($data);
} }
/** /**
* Import advertisements from Excel or CSV * Import advertisements from Excel
*/ */
public function importFromFile(Request $request) public function importFromFile(Request $request)
{ {
@@ -77,25 +87,37 @@ class TourismController extends Controller
if ($validator->fails()) { if ($validator->fails()) {
return response()->json([ return response()->json([
'message' => 'File validation failed.', 'message'=>'File validation failed.',
'errors' => $validator->errors() 'errors'=>$validator->errors()
], 400); ], 400);
} }
try { try {
$file = $request->file('file'); $file = $request->file('file');
Excel::import(new TourismImport, $file); Excel::import(new TourismImport, $file);
return response()->json([ return response()->json([
'message' => 'File uploaded and imported successfully!' 'message'=>'File uploaded and imported successfully!'
], 200); ], 200);
} catch (\Exception $e) { } catch (\Exception $e) {
return response()->json([ return response()->json([
'message' => 'Error during file import.', 'message'=>'Error during file import.',
'error' => $e->getMessage() 'error'=>$e->getMessage()
], 500); ], 500);
} }
} }
public function getAllLocation()
{
$locations = Tourism::whereNotNull('longitude')
->whereNotNull('latitude')
->select('project_name', 'longitude', 'latitude')
->get();
return response()->json([
'data' => $locations
]);
}
/** /**
* Display the specified resource. * Display the specified resource.
*/ */
@@ -109,7 +131,17 @@ class TourismController extends Controller
*/ */
public function update(TourismRequest $request, Tourism $tourism): Tourism public function update(TourismRequest $request, Tourism $tourism): Tourism
{ {
$tourism->update($request->validated()); $data = $request->validated();
// Cari district_code berdasarkan district_name
$district_code = DB::table('districts')->where('district_name', $data['district_name'])->value('district_code');
// Cari village_code berdasarkan village_name
$village_code = DB::table('villages')->where('village_name', $data['village_name'])->where('district_code', $district_code)->value('village_code');
// Tambahkan village_code dan district_code ke data
$data['village_code'] = $village_code;
$data['district_code'] = $district_code;
$tourism->update($data);
return $tourism; return $tourism;
} }
@@ -120,4 +152,18 @@ class TourismController extends Controller
return response()->noContent(); return response()->noContent();
} }
public function downloadExcelTourism()
{
$filePath = public_path('templates/template_pariwisata.xlsx');
info(sprintf("File Path: %s | Exists: %s", $filePath, file_exists($filePath) ? 'Yes' : 'No'));
// Cek apakah file ada
if (!file_exists($filePath)) {
return response()-> json(['message' => 'File tidak ditemukan!'], Response::HTTP_NOT_FOUND);
}
// Return file to download
return response()->download($filePath);
}
} }

View File

@@ -27,6 +27,17 @@ class UmkmController extends Controller
$query = Umkm::query(); $query = Umkm::query();
if ($search) {
$query->where(function ($q) use ($search) {
$q->where('business_name', 'like', "%$search%")
->orWhere('business_address', 'like', "%$search%")
->orWhere('business_desc', 'like', "%$search%")
->orWhere('business_id_number', 'like', "%$search%")
->orWhere('owner_id', 'like', "%$search%")
->orWhere('owner_name', 'like', "%$search%");
});
}
$umkm = $query->paginate($perPage); $umkm = $query->paginate($perPage);
$umkm->getCollection()->transform(function ($umkm) { $umkm->getCollection()->transform(function ($umkm) {
@@ -47,8 +58,14 @@ class UmkmController extends Controller
return $umkm; return $umkm;
}); });
$start = ($umkm->currentPage()-1) * $perPage + 1;
$data = $umkm->map(function ($item, $index) use ($start) {
return array_merge($item->toArray(), ['no' => $start + $index]);
});
return response()->json([ return response()->json([
'data' => UmkmResource::collection($umkm), 'data' => $data,
'meta' => [ 'meta' => [
'total' => $umkm->total(), 'total' => $umkm->total(),
'per_page' => $umkm->perPage(), 'per_page' => $umkm->perPage(),
@@ -69,9 +86,9 @@ class UmkmController extends Controller
// Cari kode berdasarkan nama // Cari kode berdasarkan nama
$district_code = DB::table('districts')->where('district_name', $data['district_name'])->value('district_code'); $district_code = DB::table('districts')->where('district_name', $data['district_name'])->value('district_code');
$village_code = DB::table('villages')->where('village_name', $data['village_name'])->where('district_code', $district_code)->value('village_code'); $village_code = DB::table('villages')->where('village_name', $data['village_name'])->where('district_code', $district_code)->value('village_code');
$business_scale_id = DB::table('business_scale')->where('id', $data['business_scale'])->value('id'); $business_scale_id = DB::table('business_scale')->where('id', $data['business_scale_id'])->value('id');
$permit_status_id = DB::table('permit_status')->where('id', $data['permit_status'])->value('id'); $permit_status_id = DB::table('permit_status')->where('id', $data['permit_status_id'])->value('id');
$business_form_id = DB::table('business_form')->where('id', $data['business_form'])->value('id'); $business_form_id = DB::table('business_form')->where('id', $data['business_form_id'])->value('id');
info($business_scale_id); info($business_scale_id);
@@ -176,7 +193,7 @@ class UmkmController extends Controller
public function downloadExcelUmkm() public function downloadExcelUmkm()
{ {
$filePath = storage_path('app/public/templates/template_umkm.xlsx'); $filePath = public_path('templates/template_umkm.xlsx');
// Cek apakah file ada // Cek apakah file ada
if (!file_exists($filePath)) { if (!file_exists($filePath)) {

View File

@@ -38,20 +38,20 @@ class UsersController extends Controller
return response()->json(['message' => 'logged out successfully']); return response()->json(['message' => 'logged out successfully']);
} }
public function store(UsersRequest $request){ public function store(UsersRequest $request){
$validate_data = $request->validated(); $validate_data = $request->validated();
DB::beginTransaction(); DB::beginTransaction();
try{ try{
$user = User::create([ $user = User::create([
'name' => $validate_data->name, 'name' => $validate_data['name'],
'email' => $validate_data->email, 'email' => $validate_data['email'],
'password' => Hash::make($validate_data->password), 'password' => Hash::make($validate_data['password']),
'firstname' => $validate_data->firstname, 'firstname' => $validate_data['firstname'],
'lastname' => $validate_data->lastname, 'lastname' => $validate_data['lastname'],
'position' => $validate_data->position 'position' => $validate_data['position'],
]); ]);
$user->roles()->attach($request->role_id); $user->roles()->attach((int) $validate_data['role_id']);
DB::commit(); DB::commit();
return response()->json(['message' => 'Successfully created'],201); return response()->json(['message' => 'Successfully created'],201);
@@ -60,4 +60,27 @@ class UsersController extends Controller
return response()->json(['message' => $e->getMessage()],500); return response()->json(['message' => $e->getMessage()],500);
}; };
} }
public function update(UsersRequest $request, $id){
try{
$validate_data = $request->validated();
$user = User::findOrFail($id);
DB::beginTransaction();
$user->update([
'name' => $validate_data['name'],
'email' => $validate_data['email'],
'firstname' => $validate_data['firstname'],
'lastname' => $validate_data['lastname'],
'position' => $validate_data['position']
]);
$user->roles()->sync($request->role_id);
DB::commit();
return response()->json(['message' => 'Successfully updated'], 200);
}catch(\Exception $e){
DB::rollBack();
return response()->json(['message' => $e->getMessage()],500);
}
}
} }

View File

@@ -0,0 +1,17 @@
<?php
namespace App\Http\Controllers\Chatbot;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class ChatbotController extends Controller
{
/**
* Displya a listing of the resource
*/
public function index()
{
return view('chatbot.index');
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace App\Http\Controllers;
use App\Models\Customer;
use Illuminate\Http\Request;
class CustomersController extends Controller
{
public function index()
{
return view('customers.index');
}
public function create()
{
return view('customers.create');
}
public function edit(string $id)
{
$data = Customer::findOrFail($id);
return view('customers.edit', compact('data'));
}
public function upload(){
return view('customers.upload');
}
}

View File

@@ -15,7 +15,8 @@ class BigDataController extends Controller
return view('dashboards.bigdata', compact('latest_created')); return view('dashboards.bigdata', compact('latest_created'));
} }
public function pbg(){ public function pbg()
return view('index'); {
return view('dashboards.pbg');
} }
} }

View File

@@ -0,0 +1,13 @@
<?php
namespace App\Http\Controllers\Dashboards;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
class LackOfPotentialController extends Controller
{
public function lack_of_potential(){
return view('dashboards.lack_of_potential');
}
}

View File

@@ -0,0 +1,125 @@
<?php
namespace App\Http\Controllers\Data;
use App\Http\Controllers\Controller;
use App\Models\SpatialPlanning;
use Illuminate\Http\Request;
class SpatialPlanningController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index()
{
return view('data.spatialPlannings.index');
}
/**
* show the form for creating a new resource.
*/
public function bulkCreate()
{
return view('data.spatialPlannings.form-upload');
}
/**
* Show the form for creating a new resource.
*/
public function create()
{
$title = 'Rencana Tata Ruang';
$subtitle = "Create Data";
// Mengambil data untuk dropdown
$dropdownOptions = [];
$fields = $this->getFields();
$fieldTypes = $this->getFieldTypes();
$apiUrl = url('/api/spatial-plannings');
return view('data.spatialPlannings.form', compact('title', 'subtitle', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions'));
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
//
}
/**
* Display the specified resource.
*/
public function show(string $id)
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id)
{
$title = 'Rencana Tata Ruang';
$subtitle = 'Update Data';
$modelInstance = SpatialPlanning::find($id);
// Pastikan model ditemukan
if (!$modelInstance) {
return redirect()->route('spatialPlanning.index') ->with('error', 'Rencana tata ruang tidak ditemukan');
}
$dropdownOptions = [];
$fields = $this->getFields();
$fieldTypes = $this->getFieldTypes();
$apiUrl = url('/api/spatial-plannings');
return view('data.spatialPlannings.form', compact('title', 'subtitle', 'modelInstance', 'fields', 'fieldTypes', 'apiUrl', 'dropdownOptions'));
}
/**
* 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 getFields()
{
return [
"name"=> "Nama",
"kbli"=> "KBLI",
"activities"=> "Kegiatan",
"area"=> "Luas (m2)",
"location"=> "Lokasi",
"number"=> "Nomor",
"date"=> "Tanggal",
];
}
private function getFieldTypes()
{
return [
"name"=> "text",
"kbli"=> "text",
"activities"=> "text",
"area"=> "text",
"location"=> "text",
"number"=> "text",
"date"=> "date",
];
}
}

View File

@@ -36,9 +36,7 @@ class TourismController extends Controller
// Mengambil data untuk dropdown // Mengambil data untuk dropdown
$dropdownOptions = [ $dropdownOptions = [
'village_name' => DB::table('villages')->orderBy('village_name')->pluck('village_name', 'village_code'), 'village_name' => DB::table('villages')->orderBy('village_name')->pluck('village_name', 'village_code'),
'district_name' => DB::table('districts')->orderBy('district_name')->pluck('district_name', 'district_code'), 'district_name' => DB::table('districts')->orderBy('district_name')->pluck('district_name', 'district_code')
'business_type_id' => DB::table('business_type')->orderBy('business_type')->pluck('business_type', 'id'),
'business_scale_id' => DB::table('business_scale')->orderBy('business_scale')->pluck('business_scale', 'id'),
]; ];
$fields = $this->getFields(); $fields = $this->getFields();
@@ -55,7 +53,7 @@ class TourismController extends Controller
public function edit($id) public function edit($id)
{ {
$title = 'Pariwisata'; $title = 'Pariwisata';
$subtitle = 'Create Data'; $subtitle = 'Update Data';
$modelInstance = Tourism::find($id); $modelInstance = Tourism::find($id);
// Pastikan model ditemukan // Pastikan model ditemukan
@@ -70,13 +68,9 @@ class TourismController extends Controller
$district = DB::table('districts')->where('district_code', $modelInstance->district_code)->first(); $district = DB::table('districts')->where('district_code', $modelInstance->district_code)->first();
$modelInstance->district_name = $district ? $district->district_name : null; $modelInstance->district_name = $district ? $district->district_name : null;
$business_type = DB::table('business_type')->where('id', $modelInstance->business_type_id)->first();
$modelInstance->business_scale_id = $business_type ? $business_type->id : null;
$dropdownOptions = [ $dropdownOptions = [
'village_name' => DB::table('villages')->orderBy('village_name')->pluck('village_name', 'village_code'), 'village_name' => DB::table('villages')->orderBy('village_name')->pluck('village_name', 'village_code'),
'district_name' => DB::table('districts')->orderBy('district_name')->pluck('district_name', 'district_code'), 'district_name' => DB::table('districts')->orderBy('district_name')->pluck('district_name', 'district_code')
'business_type_id' => DB::table('business_type')->orderBy('business_type')->pluck('business_type', 'id'),
]; ];
$fields = $this->getFields(); $fields = $this->getFields();
@@ -90,46 +84,62 @@ class TourismController extends Controller
private function getFields() private function getFields()
{ {
return [ return [
"business_name" => "Nama Usaha", "project_id" => "ID Proyek",
"business_form" => "Bentuk Usaha", "project_type_id" => "Jenis Proyek",
"project_name" => "Nama Project", "nib" => "NIB",
"business_name" => "Nama Perusahaan",
"oss_publication_date" => "Tanggal Terbit OSS",
"investment_status_description" => "Uraian Status Penanaman Modal",
"business_form" => "Uraian Jenis Perusahaan",
"project_risk" => "Risiko Proyek",
"project_name" => "Nama Proyek",
"business_scale" => "Uraian Skala Usaha",
"business_address" => "Alamat Usaha", "business_address" => "Alamat Usaha",
"district_name" => "Kecamatan", "district_name" => "Kecamatan",
"village_name" => "Desa", "village_name" => "Desa",
"land_area" => "Luas Tanah", "longitude" => "Longitude",
"latitude" => "Latitude",
"project_submission_date" => "Tanggal Pengajuan Project",
"kbli" => "KBLI",
"kbli_title" => "Judul KBLI",
"supervisory_sector" => "Sektor Pembina",
"user_name" => "Nama User",
"email" => "Email",
"contact" => "Kontak",
"land_area_in_m2" => "Luas Tanah (m2)",
"investment_amount" => "Jumlah Investasi", "investment_amount" => "Jumlah Investasi",
"number_of_employee" => "TKI", "tki" => "TKI",
"business_type_id" => "Jenis Usaha",
"project_id" => "Priject ID",
"nib" => "NIB",
"jenis_proyek" => "Jenis Proyek",
"status_penanaman_modal" => "Status Penanaman Modal",
"uraian_resiko_proyek" => "Uraian Resiko Proyek",
"business_scale_id" => "Skala Bisnis/Usaha",
"terbit_oss" => "Terbit OSS",
]; ];
} }
private function getFieldTypes() private function getFieldTypes()
{ {
return [ return [
"project_id" => "text",
"project_type_id" => "text",
"nib" => "text",
"business_name" => "text", "business_name" => "text",
"oss_publication_date" => "date",
"investment_status_description" => "text",
"business_form" => "text", "business_form" => "text",
"project_risk" => "text",
"project_name" => "text", "project_name" => "text",
"business_address" => "textarea", "business_scale" => "text",
"business_address" => "text",
"district_name" => "combobox", "district_name" => "combobox",
"village_name" => "combobox", "village_name" => "combobox",
"land_area" => "text", "longitude" => "text",
"latitude" => "text",
"project_submission_date" => "date",
"kbli" => "text",
"kbli_title" => "text",
"supervisory_sector" => "text",
"user_name" => "text",
"email" => "text",
"contact" => "text",
"land_area_in_m2" => "text",
"investment_amount" => "text", "investment_amount" => "text",
"number_of_employee" => "text", "tki" => "text",
"business_type_id" => "select",
"project_id" => "text",
"nib" => "text",
"jenis_proyek" => "text",
"status_penanaman_modal" => "text",
"uraian_resiko_proyek" => "text",
"business_scale_id" => "select",
"terbit_oss" => "date"
]; ];
} }
} }

View File

@@ -0,0 +1,12 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
class GoogleApisController extends Controller
{
public function index(){
return view('maps.google-api');
}
}

View File

@@ -3,7 +3,7 @@
namespace App\Http\Controllers\Report; namespace App\Http\Controllers\Report;
use App\Http\Controllers\Controller; use App\Http\Controllers\Controller;
use App\Models\BusinessTypeCount; use App\Models\TourismBasedKBLI;
use Illuminate\Http\Request; use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
@@ -14,8 +14,8 @@ class ReportTourismController extends Controller
*/ */
public function index() public function index()
{ {
$businessTypeCounts = BusinessTypeCount::all(); $tourismBasedKBLI = TourismBasedKBLI::all();
info($businessTypeCounts); info($tourismBasedKBLI);
return view('report.tourisms.index', compact('businessTypeCounts')); return view('report.tourisms.index', compact('tourismBasedKBLI'));
} }
} }

View File

@@ -40,4 +40,40 @@ class AdvertisementRequest extends FormRequest
'contact' => 'required|string', 'contact' => 'required|string',
]; ];
} }
/**
* pesan error validasi
*/
public function messages(): array
{
return [
'no.required' => 'Nomor harus diisi.',
'business_name.required' => 'Nama usaha harus diisi.',
'business_name.string' => 'Nama usaha harus berupa teks.',
'npwpd.required' => 'NPWPD harus diisi.',
'npwpd.string' => 'NPWPD harus berupa teks.',
'advertisement_type.required' => 'Jenis reklame harus diisi.',
'advertisement_type.string' => 'Jenis reklame harus berupa teks.',
'advertisement_content.required' => 'Isi reklame harus diisi.',
'advertisement_content.string' => 'Isi reklame harus berupa teks.',
'business_address.required' => 'Alamat usaha harus diisi.',
'business_address.string' => 'Alamat usaha harus berupa teks.',
'advertisement_location.required' => 'Lokasi reklame harus diisi.',
'advertisement_location.string' => 'Lokasi reklame harus berupa teks.',
'village_name.required' => 'Nama desa harus diisi.',
'district_name.required' => 'Nama kecamatan harus diisi.',
'length.required' => 'Panjang harus diisi.',
'width.required' => 'Lebar harus diisi.',
'viewing_angle.required' => 'Sudut pandang harus diisi.',
'viewing_angle.string' => 'Sudut pandang harus berupa teks.',
'face.required' => 'Jumlah sisi harus diisi.',
'face.string' => 'Jumlah sisi harus berupa teks.',
'area.required' => 'Luas harus diisi.',
'area.string' => 'Luas harus berupa teks.',
'angle.required' => 'Sudut harus diisi.',
'angle.string' => 'Sudut harus berupa teks.',
'contact.required' => 'Kontak harus diisi.',
'contact.string' => 'Kontak harus berupa teks.',
];
}
} }

View File

@@ -0,0 +1,33 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CustomersRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'nomor_pelanggan' => ['required', 'string'],
'kota_pelayanan' => ['required', 'string'],
'nama' => ['required', 'string'],
'alamat' => ['required', 'string'],
'latitude' => ['required', 'numeric', 'between:-90,90'],
'longitude' => ['required', 'numeric', 'between:-180,180'],
];
}
}

View File

@@ -21,7 +21,7 @@ class DataSettingRequest extends FormRequest
*/ */
public function rules(): array public function rules(): array
{ {
$id = $this->route('data_setting'); $id = $this->route('data_setting_id');
return [ return [
"key" => "required|unique:data_settings,key," . $id, "key" => "required|unique:data_settings,key," . $id,
"value" => "required", "value" => "required",

View File

@@ -0,0 +1,28 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class ExcelUploadRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
"file" => "required|file|mimes:xlsx,xls|max:102400"
];
}
}

View File

@@ -3,6 +3,7 @@
namespace App\Http\Requests; namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest; use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class MenuRequest extends FormRequest class MenuRequest extends FormRequest
{ {
@@ -21,12 +22,14 @@ class MenuRequest extends FormRequest
*/ */
public function rules(): array public function rules(): array
{ {
$menuId = $this->route('menu_id'); // Get the menu ID if updating
return [ return [
'name' => ['required','string','max:255'], 'name' => ['required', 'string', 'max:255', Rule::unique('menus', 'name')->ignore($menuId)],
'url' => ['nullable','string','max:255'], 'url' => ['nullable', 'string', 'max:255'],
'icon' => ['nullable','string','max:255'], 'icon' => ['nullable', 'string', 'max:255'],
'parent_id' => ['nullable','exists:menus,id'], 'parent_id' => ['nullable', 'exists:menus,id'],
'sort_order' => ['required','integer'], 'sort_order' => ['required', 'integer'],
]; ];
} }
} }

View File

@@ -21,7 +21,7 @@ class RoleRequest extends FormRequest
*/ */
public function rules(): array public function rules(): array
{ {
$roleId = $this->route('role'); $roleId = $this->route('role_id');
return [ return [
'name' => 'required|string|max:255|unique:roles,name,' . ($roleId ?? 'NULL') . ',id', 'name' => 'required|string|max:255|unique:roles,name,' . ($roleId ?? 'NULL') . ',id',
'description' => 'nullable|string', 'description' => 'nullable|string',

View File

@@ -0,0 +1,52 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class SpatialPlanningRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => 'string',
'kbli' => 'string',
'activities' => 'string',
'area' => 'string',
'location' => 'string',
'number' => 'string',
'date' => 'date_format:Y-m-d',
];
}
/**
* Get the validation messages for the defined validation rules.
*
* @return array<string, string>
*/
public function messages(): array
{
return [
'name.string' => 'Kolom Nama harus berupa teks.',
'kbli.string' => 'Kolom KBLI harus berupa teks.',
'activities.string' => 'Kolom Kegiatan harus berupa teks.',
'area.string' => 'Kolom Area harus berupa teks.',
'location.string' => 'Kolom Lokasi harus berupa teks.',
'number.string' => 'Kolom Nomor harus berupa teks.',
'date.date_format' => 'Format tanggal tidak valid, gunakan format Y-m-d H:i:s.',
];
}
}

View File

@@ -0,0 +1,35 @@
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Validation\Rule;
class SpatialPlanningsRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*/
public function authorize(): bool
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
*/
public function rules(): array
{
return [
'name' => ['required','string','max:255'],
'kbli' => ['required','string','max:255'],
'kegiatan' => ['required','string'],
'luas' => ['required','numeric','regex:/^\d{1,16}(\.\d{1,2})?$/'],
'lokasi' => ['required','string'],
'nomor' => ['required','string','max:255',Rule::unique('spatial_plannings')->ignore($this->id)],
'sp_date' => ['required','date'],
];
}
}

View File

@@ -22,23 +22,112 @@ class TourismRequest extends FormRequest
public function rules(): array public function rules(): array
{ {
return [ return [
'jenis_proyek' => 'required|string',
'nib' => 'string',
'business_name' => 'required|string',
'status_penanaman_modal' => 'string',
'business_form' => 'string',
'uraian_resiko_proyek' => 'string',
'project_name' => 'required|string',
'project_id' => 'required|string', 'project_id' => 'required|string',
'project_type_id' => 'required|string',
'nib' => 'required|string',
'business_name' => 'required|string',
'oss_publication_date' => 'required',
'investment_status_description' => 'required|string',
'business_form' => 'required|string',
'project_risk' => 'required|string',
'project_name' => 'required|string',
'business_scale' => 'required|string',
'business_address' => 'required|string', 'business_address' => 'required|string',
'district_name' => 'required|string', 'district_name' => 'required',
'village_name' => 'required|string', 'village_name' => 'required',
'land_area' => 'required|string', 'longitude' => 'required|string',
'latitude' => 'required|string',
'project_submission_date' => 'required',
'kbli' => 'required|string',
'kbli_title' => 'required|string',
'supervisory_sector' => 'required|string',
'user_name' => 'required|string',
'email' => 'required|string',
'contact' => 'required|string',
'land_area_in_m2' => 'required|string',
'investment_amount' => 'required|string', 'investment_amount' => 'required|string',
'number_of_employee' => 'required|string', 'tki' => 'required|string',
'business_type_id' => 'required|string',
'terbit_oss' => 'required|date',
'business_scale_id' => 'required',
]; ];
} }
/**
* Get the validation messages for the defined validation rules.
*
* @return array<string, string>
*/
public function messages(): array
{
return [
'project_id.required' => 'ID proyek harus diisi.',
'project_id.string' => 'ID proyek harus berupa teks.',
'project_type_id.required' => 'ID tipe proyek harus diisi.',
'project_type_id.string' => 'ID tipe proyek harus berupa teks.',
'nib.required' => 'NIB harus diisi.',
'nib.string' => 'NIB harus berupa teks.',
'business_name.required' => 'Nama usaha harus diisi.',
'business_name.string' => 'Nama usaha harus berupa teks.',
'oss_publication_date.required' => 'Tanggal publikasi OSS harus diisi.',
'investment_status_description.required' => 'Deskripsi status investasi harus diisi.',
'investment_status_description.string' => 'Deskripsi status investasi harus berupa teks.',
'business_form.required' => 'Bentuk usaha harus diisi.',
'business_form.string' => 'Bentuk usaha harus berupa teks.',
'project_risk.required' => 'Risiko proyek harus diisi.',
'project_risk.string' => 'Risiko proyek harus berupa teks.',
'project_name.required' => 'Nama proyek harus diisi.',
'project_name.string' => 'Nama proyek harus berupa teks.',
'business_scale.required' => 'Skala usaha harus diisi.',
'business_scale.string' => 'Skala usaha harus berupa teks.',
'business_address.required' => 'Alamat usaha harus diisi.',
'business_address.string' => 'Alamat usaha harus berupa teks.',
'district_name.required' => 'Nama kecamatan harus diisi.',
'village_name.required' => 'Nama desa harus diisi.',
'longitude.required' => 'Garis bujur harus diisi.',
'longitude.string' => 'Garis bujur harus berupa teks.',
'latitude.required' => 'Garis lintang harus diisi.',
'latitude.string' => 'Garis lintang harus berupa teks.',
'project_submission_date.required' => 'Tanggal pengajuan proyek harus diisi.',
'kbli.required' => 'Kode KBLI harus diisi.',
'kbli.string' => 'Kode KBLI harus berupa teks.',
'kbli_title.required' => 'Judul KBLI harus diisi.',
'kbli_title.string' => 'Judul KBLI harus berupa teks.',
'supervisory_sector.required' => 'Sektor pengawasan harus diisi.',
'supervisory_sector.string' => 'Sektor pengawasan harus berupa teks.',
'user_name.required' => 'Nama pengguna harus diisi.',
'user_name.string' => 'Nama pengguna harus berupa teks.',
'email.required' => 'Email harus diisi.',
'email.string' => 'Email harus berupa teks.',
'contact.required' => 'Kontak harus diisi.',
'contact.string' => 'Kontak harus berupa teks.',
'land_area_in_m2.required' => 'Luas lahan dalam m² harus diisi.',
'land_area_in_m2.string' => 'Luas lahan dalam m² harus berupa teks.',
'investment_amount.required' => 'Jumlah investasi harus diisi.',
'investment_amount.string' => 'Jumlah investasi harus berupa teks.',
'tki.required' => 'Jumlah tenaga kerja Indonesia harus diisi.',
'tki.string' => 'Jumlah tenaga kerja Indonesia harus berupa teks.',
];
}
} }

View File

@@ -65,7 +65,7 @@ class UmkmRequest extends FormRequest
'business_id_number.string' => 'Nomor ID usaha harus berupa teks.', 'business_id_number.string' => 'Nomor ID usaha harus berupa teks.',
'business_scale.required' => 'Skala usaha wajib diisi.', 'business_scale_id.required' => 'Skala usaha wajib diisi.',
'owner_id.required' => 'ID pemilik wajib diisi.', 'owner_id.required' => 'ID pemilik wajib diisi.',
'owner_id.string' => 'ID pemilik harus berupa teks.', 'owner_id.string' => 'ID pemilik harus berupa teks.',
@@ -96,7 +96,7 @@ class UmkmRequest extends FormRequest
'number_of_employee.required' => 'Jumlah karyawan wajib diisi.', 'number_of_employee.required' => 'Jumlah karyawan wajib diisi.',
'permit_status.required' => 'Status izin wajib diisi.', 'permit_status_id.required' => 'Status izin wajib diisi.',
'land_area.required' => 'Luas lahan wajib diisi.', 'land_area.required' => 'Luas lahan wajib diisi.',
'land_area.integer' => 'Luas lahan harus berupa angka bulat.', 'land_area.integer' => 'Luas lahan harus berupa angka bulat.',

View File

@@ -22,7 +22,7 @@ class UsersRequest extends FormRequest
*/ */
public function rules(): array public function rules(): array
{ {
$userId = $this->route('user'); // Get user ID from route (used in update) $userId = $this->route('users'); // Get user ID from route (used in update)
return [ return [
'name' => ['required', 'string', 'max:255'], 'name' => ['required', 'string', 'max:255'],
@@ -31,7 +31,7 @@ class UsersRequest extends FormRequest
'string', 'string',
'email', 'email',
'max:255', 'max:255',
Rule::unique('users')->ignore($userId), // Ignore the user's own email when updating Rule::unique('users')->ignore($userId)
], ],
'password' => [$this->isMethod('post') ? 'required' : 'nullable', 'confirmed', 'max:255'], 'password' => [$this->isMethod('post') ? 'required' : 'nullable', 'confirmed', 'max:255'],
'firstname' => ['required', 'string', 'max:255'], 'firstname' => ['required', 'string', 'max:255'],

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class CustomersResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return parent::toArray($request);
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class MenuResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return parent::toArray($request);
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class SpatialPlanningResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return parent::toArray($request);
}
}

View File

@@ -0,0 +1,19 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class SpatialPlanningsResource extends JsonResource
{
/**
* Transform the resource into an array.
*
* @return array<string, mixed>
*/
public function toArray(Request $request): array
{
return parent::toArray($request);
}
}

View File

@@ -0,0 +1,52 @@
<?php
namespace App\Imports;
use App\Models\Customer;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\ToCollection;
use Maatwebsite\Excel\Concerns\WithLimit;
use Maatwebsite\Excel\Concerns\WithChunkReading;
use Maatwebsite\Excel\Concerns\WithHeadingRow;
use Maatwebsite\Excel\Concerns\WithMultipleSheets;
use Illuminate\Contracts\Queue\ShouldQueue;
use Maatwebsite\Excel\Concerns\WithBatchInserts;
class CustomersImport implements ToCollection, WithMultipleSheets
{
/**
* @param Collection $collection
*/
public function collection(Collection $collection)
{
$batchData = [];
foreach ($collection->skip(1) as $row) {
if (!isset($row[0]) || empty($row[0])) {
continue;
}
$latitude = filter_var($row[4], FILTER_VALIDATE_FLOAT) ? bcadd($row[4], '0', 18) : null;
$longitude = filter_var($row[5], FILTER_VALIDATE_FLOAT) ? bcadd($row[5], '0', 18) : null;
$batchData[] = [
'nomor_pelanggan' => $row[0],
'kota_pelayanan' => $row[1],
'nama' => $row[2],
'alamat' => $row[3],
'latitude' => $latitude,
'longitude' => $longitude,
];
}
if (!empty($batchData)) {
Customer::upsert($batchData, ['nomor_pelanggan'], ['kota_pelayanan', 'nama', 'alamat', 'latitude', 'longitude']);
}
}
public function sheets(): array {
return [
0 => $this
];
}
}

View File

@@ -0,0 +1,63 @@
<?php
namespace App\Imports;
use App\Models\SpatialPlanning;
use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\ToCollection;
use Illuminate\Support\Facades\DB;
use DateTime;
use Carbon\Carbon;
class SpatialPlanningImport implements ToCollection
{
protected static $processed = false;
/**
* Process each row in the file
*/
public function collection(Collection $rows)
{
if (self::$processed) {
return;
}
self::$processed = true;
if ($rows->isEmpty())
{
return;
}
//cari header secara otomatis
$header = $rows->first();
$headerIndex = collect($header)->search(fn($value) => !empty($value));
// Pastikan header ditemukan
if ($headerIndex === false) {
return;
}
foreach ($rows->skip(1) as $row) {
$dateValue = trim($row[7]);
info($dateValue);
$parsedDate = Carbon::createFromFormat('Y-m-d', $dateValue)->format('Y-m-d');
info($parsedDate);
$dataToInsert[] = [
'name'=>$row[1],
'kbli'=>$row[2],
'activities'=>$row[3],
'area'=>$row[4],
'location'=>$row[5],
'number'=>$row[6],
'date'=>$parsedDate,
];
}
if(!empty($dataToInsert)) {
SpatialPlanning::insert($dataToInsert);
} else {
return;
}
}
}

View File

@@ -7,6 +7,7 @@ use Illuminate\Support\Collection;
use Maatwebsite\Excel\Concerns\ToCollection; use Maatwebsite\Excel\Concerns\ToCollection;
use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\DB;
use DateTime; use DateTime;
use Carbon\Carbon;
class TourismImport implements ToCollection class TourismImport implements ToCollection
{ {
@@ -41,13 +42,11 @@ class TourismImport implements ToCollection
if ($headerIndex === false) { if ($headerIndex === false) {
return; return;
} }
info($rows);
foreach ($rows->skip(1) as $row) { foreach ($rows->skip(1) as $row) {
// Normalisasi nama kecamatan dan desa // Normalisasi nama kecamatan dan desa
$districtName = strtolower(trim(str_replace('Kecamatan', '', $row[13]))); $districtName = strtolower(trim(str_replace('Kecamatan', '', $row[12])));
$villageName = strtolower(trim($row[14])); $villageName = strtolower(trim($row[13]));
// Cari distric_code dari table districts // Cari distric_code dari table districts
$districtCode = $districts[$districtName] ?? null; $districtCode = $districts[$districtName] ?? null;
@@ -66,24 +65,42 @@ class TourismImport implements ToCollection
// ambill village code yang village_name sama dengan $villageName // ambill village code yang village_name sama dengan $villageName
$villageCode = $listTrueVillage[$villageName]['village_code'] ?? '000000'; $villageCode = $listTrueVillage[$villageName]['village_code'] ?? '000000';
$excelSerialDate = $row[16];
if (is_numeric($excelSerialDate)) {
$projectSubmissionDate = Carbon::createFromFormat('Y-m-d', '1899-12-30')
->addDays($excelSerialDate)
->format('Y-m-d H:i:s');
} else {
$projectSubmissionDate = Carbon::createFromFormat('m/d/Y', $excelSerialDate)
->format('Y-m-d H:i:s');
}
$dataToInsert[] = [ $dataToInsert[] = [
'project_id' => $row[1], 'project_id' => $row[1],
'jenis_proyek' => $row[2], 'project_type_id' => $row[2],
'nib' => $row[3], 'nib' => $row[3],
'business_name' => $row[4], 'business_name' => $row[4],
'terbit_oss' => DateTime::createFromFormat('d/m/Y', $row[5])->format('Y-m-d'), 'oss_publication_date' => DateTime::createFromFormat('d/m/Y', $row[5]),
'status_penanaman_modal' => $row[6], 'investment_status_description' => $row[6],
'business_form' => $row[7], 'business_form' => $row[7],
'uraian_resiko_proyek' => $row[8], 'project_risk' => $row[8],
'project_name' => $row[9], 'project_name' => $row[9],
'business_type_id' => $row[10], 'business_scale' => $row[10],
'business_scale_id' => (int) $row[11],
'business_address' => $row[12], 'business_address' => $row[12],
'district_code' => $districtCode, 'district_code' => $districtCode,
'village_code' => $villageCode, 'village_code' => $villageCode,
'land_area' => $row[15], 'longitude' => $row[14],
'investment_amount' => (string) $row[16], 'latitude' => (string) $row[15],
'number_of_employee' => (string) $row[17], 'project_submission_date' => $projectSubmissionDate,
'kbli'=> $row[17],
'kbli_title'=>$row[18],
'supervisory_sector'=>$row[19],
'user_name'=>$row[20],
'email'=>$row[21],
'contact'=>$row[22],
'land_area_in_m2'=>$row[23],
'investment_amount'=>$row[24],
'tki'=>$row[25]
]; ];
} }

View File

@@ -1,14 +0,0 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class BusinessTypeCount extends Model
{
protected $table = 'business_type_counts';
protected $primaryKey = null;
public $timestamps = false;
protected $fillable = ['business_type', 'count'];
}

20
app/Models/Customer.php Normal file
View File

@@ -0,0 +1,20 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Customer extends Model
{
protected $table = 'customers';
protected $fillable = [
'nomor_pelanggan',
'kota_pelayanan',
'nama',
'alamat',
'latitude',
'longitude',
];
}

View File

@@ -0,0 +1,37 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
/**
* Class SpatialPlanning
*
* @property $id
* @property $created_at
* @property $updated_at
* @property $name
* @property $kbli
* @property $activities
* @property $area
* @property $location
* @property $number
* @property $date
*
* @package App
* @mixin \Illuminate\Database\Eloquent\Builder
*/
class SpatialPlanning extends Model
{
protected $perPage = 20;
/**
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = ['name', 'kbli', 'activities', 'area', 'location', 'number', 'date'];
}

View File

@@ -8,25 +8,33 @@ use Illuminate\Database\Eloquent\Model;
* Class Tourism * Class Tourism
* *
* @property $id * @property $id
* @property $project_id
* @property $jenis_proyek
* @property $nib
* @property $created_at * @property $created_at
* @property $updated_at * @property $updated_at
* @property $project_id
* @property $project_type_id
* @property $nib
* @property $business_name * @property $business_name
* @property $terbit_oss * @property $oss_publication_date
* @property $status_penanaman_modal * @property $investment_status_description
* @property $business_form * @property $business_form
* @property $uraian_resiko_proyek * @property $project_risk
* @property $project_name * @property $project_name
* @property $business_scale_id * @property $business_scale
* @property $business_address * @property $business_address
* @property $district_code * @property $district_code
* @property $village_code * @property $village_code
* @property $land_area * @property $longitude
* @property $latitude
* @property $project_submission_date
* @property $kbli
* @property $kbli_title
* @property $supervisory_sector
* @property $user_name
* @property $email
* @property $contact
* @property $land_area_in_m2
* @property $investment_amount * @property $investment_amount
* @property $number_of_employee * @property $tki
* @property $business_type_id
* *
* @package App * @package App
* @mixin \Illuminate\Database\Eloquent\Builder * @mixin \Illuminate\Database\Eloquent\Builder
@@ -41,7 +49,7 @@ class Tourism extends Model
* *
* @var array<int, string> * @var array<int, string>
*/ */
protected $fillable = ['project_id', 'jenis_proyek', 'nib', 'business_name', 'terbit_oss', 'status_penanaman_modal', 'business_form', 'uraian_resiko_proyek', 'project_name', 'business_scale_id', 'business_address', 'district_code', 'village_code', 'land_area', 'investment_amount', 'number_of_employee', 'business_type_id']; protected $fillable = ['project_id', 'project_type_id', 'nib', 'business_name', 'oss_publication_date', 'investment_status_description', 'business_form', 'project_risk', 'project_name', 'business_scale', 'business_address', 'district_code', 'village_code', 'longitude', 'latitude', 'project_submission_date', 'kbli', 'kbli_title', 'supervisory_sector', 'user_name', 'email', 'contact', 'land_area_in_m2', 'investment_amount', 'tki'];
} }

View File

@@ -0,0 +1,14 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class TourismBasedKBLI extends Model
{
protected $table = 'v_tourisms_based_kbli';
protected $primaryKey = null;
public $timestamps = false;
protected $fillable = ['kbli_title', 'total_records'];
}

View File

@@ -0,0 +1,157 @@
<?php
namespace App\Services;
use OpenAI;
use Illuminate\Support\Facades\Storage;
class OpenAIService
{
protected $client;
public function __construct()
{
$this->client = OpenAI::client(env('OPENAI_API_KEY'));
}
public function generateGeneralText($prompt, $mainContent)
{
$response = $this->client->chat()->create([
'model' => 'gpt-4o-mini',
'messages' => [
[
'role' => 'system',
'content' => "You are an expert assistant. Your task is to generate a concise response based on the provided prompt and main content.
Guidelines:
- Summarize the key points in exactly 5 bullet points.
- Ensure the response is clear and relevant to the prompt.
- Use simple and professional language."
],
['role' => 'user', 'content' => "Prompt: $prompt \nMain Content: $mainContent"],
],
]);
return trim($response['choices'][0]['message']['content'] ?? 'No response');
}
public function generateClassifyMainContent($prompt, $mainContent)
{
$response = $this->client->chat()->create([
'model' => 'gpt-4o-mini',
'messages' => [
[
'role' => 'system',
'content' => "You are an expert assistant in classifying questions based on whether their answers must be retrieved from a database or can be explained generally.
Your task is to return one of the following two labels:
- \"DATABASE\" → If the question requires specific data that can only be obtained from a database.
- \"GENERAL\" → If the question can be answered without accessing a database.
Consider the following context: \"$mainContent\"
Respond with only one of the labels: \"DATABASE\" or \"GENERAL\"."
],
['role' => 'user', 'content' => $prompt],
],
]);
return trim($response['choices'][0]['message']['content'] ?? 'No response');
}
public function generateQueryBasedMainContent($prompt, $mainContent)
{
// Load file JSON
$jsonPath = public_path('templates/contentTemplatePrompt.json'); // Sesuaikan path
$jsonData = json_decode(file_get_contents($jsonPath), true);
// Periksa apakah kategori ada dalam JSON
if (!isset($jsonData[$mainContent])) {
return "Template prompt tidak ditemukan.";
}
// Ambil template berdasarkan kategori
$promptTemplate = $jsonData[$mainContent]['prompt'];
$response = $this->client->chat()->create([
'model' => 'gpt-4o-mini',
'messages' => [
['role' => 'system', 'content' => $promptTemplate],
['role' => 'user', 'content' => $prompt],
],
]);
return trim($response['choices'][0]['message']['content'] ?? 'No response');
}
public function validateSyntaxQuery($queryResponse)
{
$response = $this->client->chat()->create([
'model' => 'gpt-4o-mini',
'messages' => [
[
'role' => 'system',
'content' => "You are a MariaDB SQL expert. Your task is to validate the syntax of an SQL query to ensure it follows proper MariaDB syntax rules.
Guidelines:
- Check for any syntax errors, missing keywords, or incorrect clause usage.
- Ensure the query is well-structured and adheres to best practices.
- Verify that all SQL keywords are used correctly and in the right order.
- If the query is valid, respond with: \"VALID\".
- If the query has issues, respond with: \"INVALID\".
Always respond with either \"VALID\" or \"INVALID\"."
],
['role' => 'user', 'content' => $queryResponse],
],
]);
return trim($response['choices'][0]['message']['content'] ?? 'No response');
}
public function generateNLPFromQuery($inputUser, $resultQuery) {
$response = $this->client->chat()->create([
'model' => 'gpt-4o-mini',
'messages' => [
[
'role' => 'system',
'content' => "You are an expert assistant. Your task is to analyze the database query results and transform them into a human-readable answer based on the user's question.
Guidelines:
- Understand the user's question and extract the key intent.
- Summarize or format the query results to directly answer the user's question.
- Ensure the response is clear, concise, and relevant.
- If the query result is empty or does not match the question, provide a polite response indicating that no data is available.
Always provide a well-structured response that makes sense based on the input question."
],
['role' => 'user', 'content' => "User's question: $inputUser \nDatabase result: $resultQuery"],
],
]);
return trim($response['choices'][0]['message']['content'] ?? 'No response');
}
public function generateFinalText($nlpResult) {
$response = $this->client->chat()->create([
'model' => 'gpt-4o-mini',
'messages' => [
[
'role' => 'system',
'content' => "You are an expert text formatter. Your task is to take the given NLP result and format it into a structured, human-readable text suitable for rendering inside an HTML <div>.
Guidelines:
- Preserve the meaning and clarity of the content.
- Use proper line breaks for readability.
- If the text contains lists, convert them into bullet points.
- Emphasize important keywords using <strong> tags if necessary.
- Ensure the response remains clean and concise without extra explanations."
],
['role' => 'user', 'content' => "Here is the NLP result that needs formatting:\n\n$nlpResult"],
],
]);
return trim($response['choices'][0]['message']['content'] ?? 'No response');
}
}

View File

@@ -61,32 +61,37 @@ class ServiceSIMBG
} }
} }
public function syncIndexIntegration($uuid, $token) public function syncIndexIntegration($uuids, $token)
{ {
try{ try{
$url = "/api/pbg/v1/detail/" . $uuid . "/retribution/indeks-terintegrasi/"; if(empty($uuids)){
$headers = [
'Authorization' => "Bearer " . $token,
];
$res = $this->service_client->get($url, $headers);
if (empty($res->original['success']) || !$res->original['success']) {
// Log error
Log::error("API response indicates failure", ['url' => $url, 'uuid' => $uuid]);
return false; return false;
} }
$integrations = [];
foreach($uuids as $uuid){
$url = "/api/pbg/v1/detail/" . $uuid . "/retribution/indeks-terintegrasi/";
$data = $res->original['data']['data'] ?? null; $headers = [
if (!$data) { 'Authorization' => "Bearer " . $token,
Log::error("No valid data returned from API", ['url' => $url, 'uuid' => $uuid]); ];
return false;
} $res = $this->service_client->get($url, $headers);
$resultData = PbgTaskIndexIntegrations::updateOrCreate( if (empty($res->original['success']) || !$res->original['success']) {
['pbg_task_uid' => $uuid], // Log error
[ Log::error("API response indicates failure", ['url' => $url, 'uuid' => $uuid]);
return false;
}
$data = $res->original['data']['data'] ?? null;
if (!$data) {
Log::error("No valid data returned from API", ['url' => $url, 'uuid' => $uuid]);
return false;
}
$integrations[] = [
'pbg_task_uid' => $uuid,
'indeks_fungsi_bangunan' => $data['indeks_fungsi_bangunan'] ?? null, 'indeks_fungsi_bangunan' => $data['indeks_fungsi_bangunan'] ?? null,
'indeks_parameter_kompleksitas' => $data['indeks_parameter_kompleksitas'] ?? null, 'indeks_parameter_kompleksitas' => $data['indeks_parameter_kompleksitas'] ?? null,
'indeks_parameter_permanensi' => $data['indeks_parameter_permanensi'] ?? null, 'indeks_parameter_permanensi' => $data['indeks_parameter_permanensi'] ?? null,
@@ -94,8 +99,24 @@ class ServiceSIMBG
'faktor_kepemilikan' => $data['faktor_kepemilikan'] ?? null, 'faktor_kepemilikan' => $data['faktor_kepemilikan'] ?? null,
'indeks_terintegrasi' => $data['indeks_terintegrasi'] ?? null, 'indeks_terintegrasi' => $data['indeks_terintegrasi'] ?? null,
'total' => $data['total'] ?? null, 'total' => $data['total'] ?? null,
] ];
); }
PbgTaskIndexIntegrations::upsert($integrations, ['pbg_task_uid'], ['indeks_fungsi_bangunan',
'indeks_parameter_kompleksitas', 'indeks_parameter_permanensi', 'indeks_parameter_ketinggian', 'faktor_kepemilikan', 'indeks_terintegrasi', 'total']);
// $resultData = PbgTaskIndexIntegrations::updateOrCreate(
// ['pbg_task_uid' => $uuid],
// [
// 'indeks_fungsi_bangunan' => $data['indeks_fungsi_bangunan'] ?? null,
// 'indeks_parameter_kompleksitas' => $data['indeks_parameter_kompleksitas'] ?? null,
// 'indeks_parameter_permanensi' => $data['indeks_parameter_permanensi'] ?? null,
// 'indeks_parameter_ketinggian' => $data['indeks_parameter_ketinggian'] ?? null,
// 'faktor_kepemilikan' => $data['faktor_kepemilikan'] ?? null,
// 'indeks_terintegrasi' => $data['indeks_terintegrasi'] ?? null,
// 'total' => $data['total'] ?? null,
// ]
// );
return true; return true;
}catch (Exception $e){ }catch (Exception $e){
@@ -190,7 +211,7 @@ class ServiceSIMBG
'created_at' => now(), 'created_at' => now(),
]; ];
$this->syncIndexIntegration($item['uid'], $token); // $this->syncIndexIntegration($item['uid'], $token);
$this->syncTaskDetailSubmit($item['uid'], $token); $this->syncTaskDetailSubmit($item['uid'], $token);
@@ -215,8 +236,11 @@ class ServiceSIMBG
'slf_status', 'slf_status_name', 'function_type', 'consultation_type', 'due_date', 'slf_status', 'slf_status_name', 'function_type', 'consultation_type', 'due_date',
'land_certificate_phase', 'task_created_at', 'updated_at' 'land_certificate_phase', 'task_created_at', 'updated_at'
]); ]);
$uuids = array_column($tasksCollective, 'uuid');
$this->syncIndexIntegration($uuids, $token);
} }
$importDatasource->update([ $importDatasource->update([
'status' => ImportDatasourceStatus::Success->value, 'status' => ImportDatasourceStatus::Success->value,
'message' => "Successfully processed: $savedCount, Failed: $failedCount" 'message' => "Successfully processed: $savedCount, Failed: $failedCount"
@@ -311,5 +335,4 @@ class ServiceSIMBG
} }
} }
} }

View File

@@ -0,0 +1,26 @@
<?php
namespace App\View\Components;
use Closure;
use Illuminate\Contracts\View\View;
use Illuminate\View\Component;
class CustomCircle extends Component
{
/**
* Create a new component instance.
*/
public function __construct()
{
//
}
/**
* Get the view / contents that represent the component.
*/
public function render(): View|Closure|string
{
return view('components.custom-circle');
}
}

View File

@@ -15,11 +15,11 @@
"laravel/framework": "^11.31", "laravel/framework": "^11.31",
"laravel/sanctum": "^4.0", "laravel/sanctum": "^4.0",
"laravel/tinker": "^2.9", "laravel/tinker": "^2.9",
"maatwebsite/excel": "^3.1" "maatwebsite/excel": "^3.1",
"openai-php/client": "^0.10.3"
}, },
"require-dev": { "require-dev": {
"fakerphp/faker": "^1.23", "fakerphp/faker": "^1.23",
"ibex/crud-generator": "^2.1",
"laravel/pail": "^1.1", "laravel/pail": "^1.1",
"laravel/pint": "^1.13", "laravel/pint": "^1.13",
"laravel/sail": "^1.26", "laravel/sail": "^1.26",

300
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "52617d098d62b15c6ce8538cc8aea775", "content-hash": "41bb51871a746904ab745e4095db8b46",
"packages": [ "packages": [
{ {
"name": "brick/math", "name": "brick/math",
@@ -3296,6 +3296,97 @@
], ],
"time": "2024-11-21T10:39:51+00:00" "time": "2024-11-21T10:39:51+00:00"
}, },
{
"name": "openai-php/client",
"version": "v0.10.3",
"source": {
"type": "git",
"url": "https://github.com/openai-php/client.git",
"reference": "4a565d145e0fb3ea1baba8fffe39d86c56b6dc2c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/openai-php/client/zipball/4a565d145e0fb3ea1baba8fffe39d86c56b6dc2c",
"reference": "4a565d145e0fb3ea1baba8fffe39d86c56b6dc2c",
"shasum": ""
},
"require": {
"php": "^8.1.0",
"php-http/discovery": "^1.20.0",
"php-http/multipart-stream-builder": "^1.4.2",
"psr/http-client": "^1.0.3",
"psr/http-client-implementation": "^1.0.1",
"psr/http-factory-implementation": "*",
"psr/http-message": "^1.1.0|^2.0.0"
},
"require-dev": {
"guzzlehttp/guzzle": "^7.9.2",
"guzzlehttp/psr7": "^2.7.0",
"laravel/pint": "^1.18.1",
"mockery/mockery": "^1.6.12",
"nunomaduro/collision": "^7.11.0|^8.5.0",
"pestphp/pest": "^2.36.0|^3.5.0",
"pestphp/pest-plugin-arch": "^2.7|^3.0",
"pestphp/pest-plugin-type-coverage": "^2.8.7|^3.1.0",
"phpstan/phpstan": "^1.12.7",
"symfony/var-dumper": "^6.4.11|^7.1.5"
},
"type": "library",
"autoload": {
"files": [
"src/OpenAI.php"
],
"psr-4": {
"OpenAI\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nuno Maduro",
"email": "enunomaduro@gmail.com"
},
{
"name": "Sandro Gehri"
}
],
"description": "OpenAI PHP is a supercharged PHP API client that allows you to interact with the Open AI API",
"keywords": [
"GPT-3",
"api",
"client",
"codex",
"dall-e",
"language",
"natural",
"openai",
"php",
"processing",
"sdk"
],
"support": {
"issues": "https://github.com/openai-php/client/issues",
"source": "https://github.com/openai-php/client/tree/v0.10.3"
},
"funding": [
{
"url": "https://www.paypal.com/paypalme/enunomaduro",
"type": "custom"
},
{
"url": "https://github.com/gehrisandro",
"type": "github"
},
{
"url": "https://github.com/nunomaduro",
"type": "github"
}
],
"time": "2024-11-12T20:51:16+00:00"
},
{ {
"name": "paragonie/constant_time_encoding", "name": "paragonie/constant_time_encoding",
"version": "v3.0.0", "version": "v3.0.0",
@@ -3413,6 +3504,141 @@
}, },
"time": "2020-10-15T08:29:30+00:00" "time": "2020-10-15T08:29:30+00:00"
}, },
{
"name": "php-http/discovery",
"version": "1.20.0",
"source": {
"type": "git",
"url": "https://github.com/php-http/discovery.git",
"reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-http/discovery/zipball/82fe4c73ef3363caed49ff8dd1539ba06044910d",
"reference": "82fe4c73ef3363caed49ff8dd1539ba06044910d",
"shasum": ""
},
"require": {
"composer-plugin-api": "^1.0|^2.0",
"php": "^7.1 || ^8.0"
},
"conflict": {
"nyholm/psr7": "<1.0",
"zendframework/zend-diactoros": "*"
},
"provide": {
"php-http/async-client-implementation": "*",
"php-http/client-implementation": "*",
"psr/http-client-implementation": "*",
"psr/http-factory-implementation": "*",
"psr/http-message-implementation": "*"
},
"require-dev": {
"composer/composer": "^1.0.2|^2.0",
"graham-campbell/phpspec-skip-example-extension": "^5.0",
"php-http/httplug": "^1.0 || ^2.0",
"php-http/message-factory": "^1.0",
"phpspec/phpspec": "^5.1 || ^6.1 || ^7.3",
"sebastian/comparator": "^3.0.5 || ^4.0.8",
"symfony/phpunit-bridge": "^6.4.4 || ^7.0.1"
},
"type": "composer-plugin",
"extra": {
"class": "Http\\Discovery\\Composer\\Plugin",
"plugin-optional": true
},
"autoload": {
"psr-4": {
"Http\\Discovery\\": "src/"
},
"exclude-from-classmap": [
"src/Composer/Plugin.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Márk Sági-Kazár",
"email": "mark.sagikazar@gmail.com"
}
],
"description": "Finds and installs PSR-7, PSR-17, PSR-18 and HTTPlug implementations",
"homepage": "http://php-http.org",
"keywords": [
"adapter",
"client",
"discovery",
"factory",
"http",
"message",
"psr17",
"psr7"
],
"support": {
"issues": "https://github.com/php-http/discovery/issues",
"source": "https://github.com/php-http/discovery/tree/1.20.0"
},
"time": "2024-10-02T11:20:13+00:00"
},
{
"name": "php-http/multipart-stream-builder",
"version": "1.4.2",
"source": {
"type": "git",
"url": "https://github.com/php-http/multipart-stream-builder.git",
"reference": "10086e6de6f53489cca5ecc45b6f468604d3460e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-http/multipart-stream-builder/zipball/10086e6de6f53489cca5ecc45b6f468604d3460e",
"reference": "10086e6de6f53489cca5ecc45b6f468604d3460e",
"shasum": ""
},
"require": {
"php": "^7.1 || ^8.0",
"php-http/discovery": "^1.15",
"psr/http-factory-implementation": "^1.0"
},
"require-dev": {
"nyholm/psr7": "^1.0",
"php-http/message": "^1.5",
"php-http/message-factory": "^1.0.2",
"phpunit/phpunit": "^7.5.15 || ^8.5 || ^9.3"
},
"type": "library",
"autoload": {
"psr-4": {
"Http\\Message\\MultipartStream\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Tobias Nyholm",
"email": "tobias.nyholm@gmail.com"
}
],
"description": "A builder class that help you create a multipart stream",
"homepage": "http://php-http.org",
"keywords": [
"factory",
"http",
"message",
"multipart stream",
"stream"
],
"support": {
"issues": "https://github.com/php-http/multipart-stream-builder/issues",
"source": "https://github.com/php-http/multipart-stream-builder/tree/1.4.2"
},
"time": "2024-09-04T13:22:54+00:00"
},
{ {
"name": "phpoffice/phpspreadsheet", "name": "phpoffice/phpspreadsheet",
"version": "1.29.10", "version": "1.29.10",
@@ -7155,74 +7381,6 @@
}, },
"time": "2020-07-09T08:09:16+00:00" "time": "2020-07-09T08:09:16+00:00"
}, },
{
"name": "ibex/crud-generator",
"version": "v2.1.2",
"source": {
"type": "git",
"url": "https://github.com/awais-vteams/laravel-crud-generator.git",
"reference": "3906f4a702c91bbe3a84d940c3021d1511834320"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/awais-vteams/laravel-crud-generator/zipball/3906f4a702c91bbe3a84d940c3021d1511834320",
"reference": "3906f4a702c91bbe3a84d940c3021d1511834320",
"shasum": ""
},
"require": {
"laravel/framework": "^10.30|^11.0",
"php": "^8.2"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"Ibex\\CrudGenerator\\CrudServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"Ibex\\CrudGenerator\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "M Awais",
"email": "asargodha@gmail.com"
}
],
"description": "Laravel CRUD Generator",
"keywords": [
"alpine js",
"bootstrap css",
"crud",
"crud generator",
"laravel",
"laravel crud generator",
"laravel package",
"tailwind css"
],
"support": {
"issues": "https://github.com/awais-vteams/laravel-crud-generator/issues",
"source": "https://github.com/awais-vteams/laravel-crud-generator/tree/v2.1.2"
},
"funding": [
{
"url": "https://github.com/awais-vteams",
"type": "github"
},
{
"url": "https://ko-fi.com/mawais",
"type": "ko_fi"
}
],
"time": "2024-12-09T06:01:54+00:00"
},
{ {
"name": "laravel/pail", "name": "laravel/pail",
"version": "v1.2.2", "version": "v1.2.2",
@@ -9316,12 +9474,12 @@
], ],
"aliases": [], "aliases": [],
"minimum-stability": "stable", "minimum-stability": "stable",
"stability-flags": [], "stability-flags": {},
"prefer-stable": true, "prefer-stable": true,
"prefer-lowest": false, "prefer-lowest": false,
"platform": { "platform": {
"php": "^8.2" "php": "^8.2"
}, },
"platform-dev": [], "platform-dev": {},
"plugin-api-version": "2.6.0" "plugin-api-version": "2.6.0"
} }

View File

@@ -0,0 +1,54 @@
<?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::dropIfExists('tourisms');
Schema::create('tourisms', function (Blueprint $table) {
$table->id();
$table->timestamp('created_at')->default(DB::raw('CURRENT_TIMESTAMP'));
$table->timestamp('updated_at')->default(DB::raw('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'));
$table->string('project_id');
$table->string('project_type_id');
$table->string('nib');
$table->string('business_name');
$table->datetime('oss_publication_date');
$table->string('investment_status_description');
$table->string('business_form');
$table->string('project_risk');
$table->string('project_name');
$table->string('business_scale');
$table->string('business_address');
$table->integer('district_code');
$table->integer('village_code');
$table->string('longitude');
$table->string('latitude');
$table->datetime('project_submission_date');
$table->string('kbli');
$table->string('kbli_title');
$table->string('supervisory_sector');
$table->string('user_name');
$table->string('email');
$table->string('contact');
$table->string('land_area_in_m2');
$table->string('investment_amount');
$table->string('tki');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('tourisms');
}
};

View File

@@ -0,0 +1,28 @@
<?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('tourisms', function (Blueprint $table) {
$table->string('village_code')->change();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('tourisms', function (Blueprint $table) {
$table->integer('village_code')->change();
});
}
};

View File

@@ -0,0 +1,35 @@
<?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::create('spatial_planning', function (Blueprint $table) {
$table->id();
$table->timestamp('created_at')->default(DB::raw('CURRENT_TIMESTAMP'));
$table->timestamp('updated_at')->default(DB::raw('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP'));
$table->string('name')->nullable();
$table->string('kbli')->nullable();
$table->string('activities')->nullable();
$table->string('area')->nullable();
$table->string('location')->nullable();
$table->string('number')->nullable();
$table->dateTime('date')->nullable();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('spatial_planning');
}
};

View File

@@ -0,0 +1,24 @@
<?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::rename('spatial_planning', 'spatial_plannings');
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::rename('spatial_plannings', 'spatial_planning');
}
};

View File

@@ -0,0 +1,33 @@
<?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::create('customers', function (Blueprint $table) {
$table->id();
$table->string('nomor_pelanggan')->unique();
$table->string('kota_pelayanan');
$table->string('nama');
$table->text('alamat');
$table->decimal('latitude', 22,18);
$table->decimal('longitude', 22,18);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('customers');
}
};

View File

@@ -0,0 +1,28 @@
<?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('pbg_task_google_sheet', function (Blueprint $table) {
$table->string('formatted_registration_number')->nullable()->after('no_registrasi');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('pbg_task_google_sheet', function (Blueprint $table) {
$table->dropColumn('formatted_registration_number');
});
}
};

View File

@@ -109,6 +109,20 @@ class UsersRoleMenuSeeder extends Seeder
"parent_id" => $dashboard->id, "parent_id" => $dashboard->id,
"sort_order" => 2, "sort_order" => 2,
], ],
[
"name" => "Dashboard Potensi",
"url" => "dashboard.lack_of_potential",
"icon" => null,
"parent_id" => $dashboard->id,
"sort_order" => 3,
],
[
"name" => "PETA",
"url" => "dashboard.maps",
"icon" => null,
"parent_id" => $dashboard->id,
"sort_order" => 4,
],
[ [
"name" => "Users", "name" => "Users",
"url" => "users.index", "url" => "users.index",
@@ -179,6 +193,20 @@ class UsersRoleMenuSeeder extends Seeder
"parent_id" => $data->id, "parent_id" => $data->id,
"sort_order" => 5, "sort_order" => 5,
], ],
[
"name" => "Tata Ruang",
"url" => "spatial-plannings.index",
"icon" => null,
"parent_id" => $data->id,
"sort_order" => 6,
],
[
"name" => "PDAM",
"url" => "customers",
"icon" => null,
"parent_id" => $data->id,
"sort_order" => 7,
],
[ [
"name" => "Lap Pariwisata", "name" => "Lap Pariwisata",
"url" => "tourisms.index", "url" => "tourisms.index",
@@ -205,6 +233,10 @@ class UsersRoleMenuSeeder extends Seeder
$pariwisata = Menu::where('name', 'Pariwisata')->first(); $pariwisata = Menu::where('name', 'Pariwisata')->first();
$laporan_pariwisata = Menu::where('name', 'Lap Pariwisata')->first(); $laporan_pariwisata = Menu::where('name', 'Lap Pariwisata')->first();
$umkm = Menu::where('name', 'UMKM')->first(); $umkm = Menu::where('name', 'UMKM')->first();
$lack_of_potentials = Menu::where('name', 'Dashboard Potensi')->first();
$spatial_plannings = Menu::where('name', 'Tata Ruang')->first();
$pdam = Menu::where('name', 'PDAM')->first();
$peta = Menu::where('name', 'PETA')->first();
// Superadmin gets all menus // Superadmin gets all menus
$superadmin->menus()->sync([ $superadmin->menus()->sync([
@@ -229,6 +261,10 @@ class UsersRoleMenuSeeder extends Seeder
$pariwisata->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], $pariwisata->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$laporan_pariwisata->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], $laporan_pariwisata->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$umkm->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], $umkm->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$lack_of_potentials->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true],
$spatial_plannings->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],
]); ]);
// Admin gets limited menus // Admin gets limited menus

2
package-lock.json generated
View File

@@ -1,5 +1,5 @@
{ {
"name": "sibedas-pbg-web", "name": "sibedas-pbg",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {

View File

@@ -1,506 +0,0 @@
{
"__commonjsHelpers-C4iS2aBk.js": {
"file": "assets/_commonjsHelpers-C4iS2aBk.js",
"name": "_commonjsHelpers"
},
"_apexcharts.common-7mov3gaG.js": {
"file": "assets/apexcharts.common-7mov3gaG.js",
"name": "apexcharts.common",
"imports": [
"__commonjsHelpers-C4iS2aBk.js"
]
},
"_dropzone-B5tMhgFp.js": {
"file": "assets/dropzone-B5tMhgFp.js",
"name": "dropzone"
},
"_global-config-9uDKFQ8j.js": {
"file": "assets/global-config-9uDKFQ8j.js",
"name": "global-config"
},
"_gridjs.umd-BiCNXlqL.js": {
"file": "assets/gridjs.umd-BiCNXlqL.js",
"name": "gridjs.umd",
"imports": [
"__commonjsHelpers-C4iS2aBk.js"
]
},
"_table-generator-DPFVsCqk.js": {
"file": "assets/table-generator-DPFVsCqk.js",
"name": "table-generator",
"imports": [
"_gridjs.umd-BiCNXlqL.js"
]
},
"_world-BH8KG5u4.js": {
"file": "assets/world-BH8KG5u4.js",
"name": "world",
"imports": [
"__commonjsHelpers-C4iS2aBk.js"
]
},
"node_modules/flatpickr/dist/flatpickr.min.css": {
"file": "assets/flatpickr-CksuuEqD.css",
"src": "node_modules/flatpickr/dist/flatpickr.min.css",
"isEntry": true
},
"node_modules/flatpickr/dist/themes/dark.css": {
"file": "assets/dark-CLxH30By.css",
"src": "node_modules/flatpickr/dist/themes/dark.css",
"isEntry": true
},
"node_modules/gridjs/dist/theme/mermaid.css": {
"file": "assets/mermaid-B5wPN5RC.css",
"src": "node_modules/gridjs/dist/theme/mermaid.css",
"isEntry": true
},
"node_modules/gridjs/dist/theme/mermaid.min.css": {
"file": "assets/mermaid-1KsrsKla.css",
"src": "node_modules/gridjs/dist/theme/mermaid.min.css",
"isEntry": true
},
"node_modules/quill/dist/quill.bubble.css": {
"file": "assets/quill-BzaoboQ1.css",
"src": "node_modules/quill/dist/quill.bubble.css",
"isEntry": true
},
"node_modules/quill/dist/quill.snow.css": {
"file": "assets/quill-D-Ncpkvi.css",
"src": "node_modules/quill/dist/quill.snow.css",
"isEntry": true
},
"public/images/bg-dashboard.jpg": {
"file": "assets/bg-dashboard-CUwt8_jP.jpg",
"src": "public/images/bg-dashboard.jpg"
},
"resources/fonts/boxicons.eot": {
"file": "assets/boxicons-0t2gX1vj.eot",
"src": "resources/fonts/boxicons.eot"
},
"resources/fonts/boxicons.svg": {
"file": "assets/boxicons-KSR1BgPC.svg",
"src": "resources/fonts/boxicons.svg"
},
"resources/fonts/boxicons.ttf": {
"file": "assets/boxicons-BEZXjQG5.ttf",
"src": "resources/fonts/boxicons.ttf"
},
"resources/fonts/boxicons.woff": {
"file": "assets/boxicons-CEgI8ccS.woff",
"src": "resources/fonts/boxicons.woff"
},
"resources/fonts/boxicons.woff2": {
"file": "assets/boxicons-C7pETWQJ.woff2",
"src": "resources/fonts/boxicons.woff2"
},
"resources/js/app.js": {
"file": "assets/app-Wz_4hh3O.js",
"name": "app",
"src": "resources/js/app.js",
"isEntry": true,
"imports": [
"__commonjsHelpers-C4iS2aBk.js"
]
},
"resources/js/business-industries/create.js": {
"file": "assets/create-CiRhiWZV.js",
"name": "create",
"src": "resources/js/business-industries/create.js",
"isEntry": true,
"imports": [
"_dropzone-B5tMhgFp.js",
"_global-config-9uDKFQ8j.js"
]
},
"resources/js/business-industries/index.js": {
"file": "assets/index-BIXijOG8.js",
"name": "index",
"src": "resources/js/business-industries/index.js",
"isEntry": true,
"imports": [
"_gridjs.umd-BiCNXlqL.js",
"_global-config-9uDKFQ8j.js",
"__commonjsHelpers-C4iS2aBk.js"
]
},
"resources/js/business-industries/update.js": {
"file": "assets/update-C8VOvIbF.js",
"name": "update",
"src": "resources/js/business-industries/update.js",
"isEntry": true
},
"resources/js/config.js": {
"file": "assets/config-DqV4EBmE.js",
"name": "config",
"src": "resources/js/config.js",
"isEntry": true
},
"resources/js/dashboards/bigdata.js": {
"file": "assets/bigdata-Cyb9tZIY.js",
"name": "bigdata",
"src": "resources/js/dashboards/bigdata.js",
"isEntry": true,
"imports": [
"_global-config-9uDKFQ8j.js"
],
"css": [
"assets/flatpickr-CksuuEqD.css"
]
},
"resources/js/data-settings/create.js": {
"file": "assets/create-C1IbeTHP.js",
"name": "create",
"src": "resources/js/data-settings/create.js",
"isEntry": true
},
"resources/js/data-settings/index.js": {
"file": "assets/index-C4raIbBK.js",
"name": "index",
"src": "resources/js/data-settings/index.js",
"isEntry": true,
"imports": [
"_gridjs.umd-BiCNXlqL.js",
"_global-config-9uDKFQ8j.js",
"__commonjsHelpers-C4iS2aBk.js"
]
},
"resources/js/data-settings/update.js": {
"file": "assets/update-nNw3P4hj.js",
"name": "update",
"src": "resources/js/data-settings/update.js",
"isEntry": true
},
"resources/js/data/advertisements/data-advertisements.js": {
"file": "assets/data-advertisements-ckr1EtLP.js",
"name": "data-advertisements",
"src": "resources/js/data/advertisements/data-advertisements.js",
"isEntry": true,
"imports": [
"_gridjs.umd-BiCNXlqL.js",
"_global-config-9uDKFQ8j.js",
"_table-generator-DPFVsCqk.js",
"__commonjsHelpers-C4iS2aBk.js"
]
},
"resources/js/data/advertisements/form-create-update.js": {
"file": "assets/form-create-update-CyN97GsU.js",
"name": "form-create-update",
"src": "resources/js/data/advertisements/form-create-update.js",
"isEntry": true,
"imports": [
"_global-config-9uDKFQ8j.js"
]
},
"resources/js/data/advertisements/form-upload.js": {
"file": "assets/form-upload-tA9zPESO.js",
"name": "form-upload",
"src": "resources/js/data/advertisements/form-upload.js",
"isEntry": true,
"imports": [
"_dropzone-B5tMhgFp.js",
"_global-config-9uDKFQ8j.js"
]
},
"resources/js/data/tourisms/data-tourisms.js": {
"file": "assets/data-tourisms-Db9aMSJn.js",
"name": "data-tourisms",
"src": "resources/js/data/tourisms/data-tourisms.js",
"isEntry": true,
"imports": [
"_gridjs.umd-BiCNXlqL.js",
"_global-config-9uDKFQ8j.js",
"_table-generator-DPFVsCqk.js",
"__commonjsHelpers-C4iS2aBk.js"
]
},
"resources/js/data/tourisms/form-create-update.js": {
"file": "assets/form-create-update-SviVezNW.js",
"name": "form-create-update",
"src": "resources/js/data/tourisms/form-create-update.js",
"isEntry": true,
"imports": [
"_global-config-9uDKFQ8j.js"
]
},
"resources/js/data/tourisms/form-upload.js": {
"file": "assets/form-upload-BtbLOLpA.js",
"name": "form-upload",
"src": "resources/js/data/tourisms/form-upload.js",
"isEntry": true,
"imports": [
"_dropzone-B5tMhgFp.js",
"_global-config-9uDKFQ8j.js"
]
},
"resources/js/data/umkm/data-umkm.js": {
"file": "assets/data-umkm-CLB33rMj.js",
"name": "data-umkm",
"src": "resources/js/data/umkm/data-umkm.js",
"isEntry": true,
"imports": [
"_gridjs.umd-BiCNXlqL.js",
"_global-config-9uDKFQ8j.js",
"_table-generator-DPFVsCqk.js",
"__commonjsHelpers-C4iS2aBk.js"
]
},
"resources/js/data/umkm/form-create-update.js": {
"file": "assets/form-create-update-BrB0xTMN.js",
"name": "form-create-update",
"src": "resources/js/data/umkm/form-create-update.js",
"isEntry": true,
"imports": [
"_global-config-9uDKFQ8j.js"
]
},
"resources/js/data/umkm/form-upload.js": {
"file": "assets/form-upload-BDyAWo4b.js",
"name": "form-upload",
"src": "resources/js/data/umkm/form-upload.js",
"isEntry": true,
"imports": [
"_dropzone-B5tMhgFp.js",
"_global-config-9uDKFQ8j.js"
]
},
"resources/js/master/users/create.js": {
"file": "assets/create-BfiYISiN.js",
"name": "create",
"src": "resources/js/master/users/create.js",
"isEntry": true
},
"resources/js/master/users/update.js": {
"file": "assets/update-DhoG4v8r.js",
"name": "update",
"src": "resources/js/master/users/update.js",
"isEntry": true
},
"resources/js/master/users/users.js": {
"file": "assets/users-jfWUOWyP.js",
"name": "users",
"src": "resources/js/master/users/users.js",
"isEntry": true,
"imports": [
"_gridjs.umd-BiCNXlqL.js",
"_global-config-9uDKFQ8j.js",
"__commonjsHelpers-C4iS2aBk.js"
]
},
"resources/js/menus/create.js": {
"file": "assets/create-ChUgh-yc.js",
"name": "create",
"src": "resources/js/menus/create.js",
"isEntry": true
},
"resources/js/menus/index.js": {
"file": "assets/index-C4xA1kYa.js",
"name": "index",
"src": "resources/js/menus/index.js",
"isEntry": true,
"imports": [
"_gridjs.umd-BiCNXlqL.js",
"_global-config-9uDKFQ8j.js",
"__commonjsHelpers-C4iS2aBk.js"
]
},
"resources/js/menus/update.js": {
"file": "assets/update-8JQOGES4.js",
"name": "update",
"src": "resources/js/menus/update.js",
"isEntry": true
},
"resources/js/pages/chart.js": {
"file": "assets/chart-DQBoD9wk.js",
"name": "chart",
"src": "resources/js/pages/chart.js",
"isEntry": true,
"imports": [
"_apexcharts.common-7mov3gaG.js",
"__commonjsHelpers-C4iS2aBk.js"
]
},
"resources/js/pages/dashboard.js": {
"file": "assets/dashboard-nkb3Omy9.js",
"name": "dashboard",
"src": "resources/js/pages/dashboard.js",
"isEntry": true,
"imports": [
"_apexcharts.common-7mov3gaG.js",
"_world-BH8KG5u4.js",
"__commonjsHelpers-C4iS2aBk.js"
]
},
"resources/js/pages/form-fileupload.js": {
"file": "assets/form-fileupload-DGbdX8GT.js",
"name": "form-fileupload",
"src": "resources/js/pages/form-fileupload.js",
"isEntry": true,
"imports": [
"_dropzone-B5tMhgFp.js"
]
},
"resources/js/pages/form-flatepicker.js": {
"file": "assets/form-flatepicker-ChSlk6xC.js",
"name": "form-flatepicker",
"src": "resources/js/pages/form-flatepicker.js",
"isEntry": true,
"imports": [
"__commonjsHelpers-C4iS2aBk.js"
]
},
"resources/js/pages/form-quilljs.js": {
"file": "assets/form-quilljs-pSObT4Ti.js",
"name": "form-quilljs",
"src": "resources/js/pages/form-quilljs.js",
"isEntry": true,
"imports": [
"__commonjsHelpers-C4iS2aBk.js"
]
},
"resources/js/pages/maps-canada.js": {
"file": "assets/maps-canada-Btz07hSk.js",
"name": "maps-canada",
"src": "resources/js/pages/maps-canada.js",
"isEntry": true
},
"resources/js/pages/maps-google.js": {
"file": "assets/maps-google-KamR_rNw.js",
"name": "maps-google",
"src": "resources/js/pages/maps-google.js",
"isEntry": true,
"imports": [
"__commonjsHelpers-C4iS2aBk.js"
]
},
"resources/js/pages/maps-iraq.js": {
"file": "assets/maps-iraq-Blhf9V_8.js",
"name": "maps-iraq",
"src": "resources/js/pages/maps-iraq.js",
"isEntry": true
},
"resources/js/pages/maps-russia.js": {
"file": "assets/maps-russia-C3XucoMP.js",
"name": "maps-russia",
"src": "resources/js/pages/maps-russia.js",
"isEntry": true
},
"resources/js/pages/maps-spain.js": {
"file": "assets/maps-spain-CdBIHB66.js",
"name": "maps-spain",
"src": "resources/js/pages/maps-spain.js",
"isEntry": true
},
"resources/js/pages/maps-vector.js": {
"file": "assets/maps-vector-C2WpvMU7.js",
"name": "maps-vector",
"src": "resources/js/pages/maps-vector.js",
"isEntry": true,
"imports": [
"_world-BH8KG5u4.js",
"__commonjsHelpers-C4iS2aBk.js"
]
},
"resources/js/pages/table-gridjs.js": {
"file": "assets/table-gridjs-BQh80cFh.js",
"name": "table-gridjs",
"src": "resources/js/pages/table-gridjs.js",
"isEntry": true,
"imports": [
"_gridjs.umd-BiCNXlqL.js",
"__commonjsHelpers-C4iS2aBk.js"
]
},
"resources/js/pbg-task/index.js": {
"file": "assets/index-CqcfkvSL.js",
"name": "index",
"src": "resources/js/pbg-task/index.js",
"isEntry": true,
"imports": [
"_gridjs.umd-BiCNXlqL.js",
"_global-config-9uDKFQ8j.js",
"__commonjsHelpers-C4iS2aBk.js"
]
},
"resources/js/roles/create.js": {
"file": "assets/create-Dd-lHOwF.js",
"name": "create",
"src": "resources/js/roles/create.js",
"isEntry": true
},
"resources/js/roles/index.js": {
"file": "assets/index-C_kI_q7O.js",
"name": "index",
"src": "resources/js/roles/index.js",
"isEntry": true,
"imports": [
"_gridjs.umd-BiCNXlqL.js",
"_global-config-9uDKFQ8j.js",
"__commonjsHelpers-C4iS2aBk.js"
]
},
"resources/js/roles/role_menu.js": {
"file": "assets/role_menu-BuFAi1wM.js",
"name": "role_menu",
"src": "resources/js/roles/role_menu.js",
"isEntry": true
},
"resources/js/roles/update.js": {
"file": "assets/update-CapXnAP8.js",
"name": "update",
"src": "resources/js/roles/update.js",
"isEntry": true
},
"resources/js/settings/general/general-settings.js": {
"file": "assets/general-settings-BoJeYQk1.js",
"name": "general-settings",
"src": "resources/js/settings/general/general-settings.js",
"isEntry": true,
"imports": [
"_gridjs.umd-BiCNXlqL.js",
"_global-config-9uDKFQ8j.js",
"__commonjsHelpers-C4iS2aBk.js"
]
},
"resources/js/settings/syncronize/syncronize.js": {
"file": "assets/syncronize-DjtcngRb.js",
"name": "syncronize",
"src": "resources/js/settings/syncronize/syncronize.js",
"isEntry": true,
"imports": [
"_gridjs.umd-BiCNXlqL.js",
"_global-config-9uDKFQ8j.js",
"__commonjsHelpers-C4iS2aBk.js"
]
},
"resources/js/tables/common-table.js": {
"file": "assets/common-table-KF_NVAIE.js",
"name": "common-table",
"src": "resources/js/tables/common-table.js",
"isEntry": true,
"imports": [
"_gridjs.umd-BiCNXlqL.js",
"_global-config-9uDKFQ8j.js",
"__commonjsHelpers-C4iS2aBk.js"
]
},
"resources/scss/components/_circle.scss": {
"file": "assets/_circle-DgPqpfJw.css",
"src": "resources/scss/components/_circle.scss",
"isEntry": true
},
"resources/scss/dashboards/_bigdata.scss": {
"file": "assets/_bigdata-Syu0AGbZ.css",
"src": "resources/scss/dashboards/_bigdata.scss",
"isEntry": true
},
"resources/scss/icons.scss": {
"file": "assets/icons-CHxf0fE3.css",
"src": "resources/scss/icons.scss",
"isEntry": true
},
"resources/scss/style.scss": {
"file": "assets/style-C8C4w8xF.css",
"src": "resources/scss/style.scss",
"isEntry": true
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

View File

@@ -0,0 +1,11 @@
{
"RETRIBUTION": {
"prompt": "You are a MariaDB SQL expert. Your task is to generate an efficient and optimized SQL query based on user input.\n\n The query should retrieve data from the view table `v_pbg_task_with_retributions`, specifically selecting the following columns:\n\n - nilai_retribusi_bangunan\n - land_certificate_phase\n - due_date\n - consultation_type\n - function_type\n - slf_status_name\n - slf_status\n - status_name\n - status\n - address\n - document_number\n - registration_number\n - application_type_name\n - application_type\n - owner_name\n - name\n\n Ensure the query is well-structured, uses best indexing practices, and avoids performance bottlenecks.\n\n Consider the following context: \"$mainContent\".\n\n Always return only the SQL query without any additional explanation."
},
"DOCUMENT VALIDATION": {
"prompt": "You are a MariaDB SQL expert. Your task is to generate an efficient and optimized SQL query based on user input.\n\n The query should retrieve data from the view table `pbg_task`, specifically selecting the following columns:\n\n - name\n - owner_name\n - application_type\n - application_type_name\n - registration_number\n - document_number\n - address\n - status_name\n - slf_status\n - slf_status_name\n - function_type\n - consultation_type\n - function_type\n - consultation_type\n - due_date\n - land_certificate_phase\n\n Ensure the query is well-structured, uses best indexing practices, and avoids performance bottlenecks.\n\n Consider the following context: \"$mainContent\".\n\n Always return only the SQL query without any additional explanation."
},
"DATA SUMMARY": {
"prompt": "You are a MariaDB SQL expert. Your task is to generate an efficient and optimized SQL query based on user input.\n\n The query should retrieve data from the view table `bigdata_resumes`, specifically selecting the following columns:\n\n - potention_count\n - potention_sum\n - non_verified_count\n - non_verified_sum\n - verified_sum\n - verified_count\n - business_count\n - business_sum\n - non_business_count\n - non_business_sum\n - spatial_count\n - spatial_sum\n - updated_at\n\n Ensure the query is well-structured, uses best indexing practices, and avoids performance bottlenecks.\n\n Consider the following context: \"$mainContent\".\n\n Always return only the SQL query without any additional explanation."
}
}

Binary file not shown.

Binary file not shown.

View File

@@ -2,6 +2,30 @@ import bootstrap from "bootstrap/dist/js/bootstrap";
window.bootstrap = bootstrap; window.bootstrap = bootstrap;
import "iconify-icon"; import "iconify-icon";
import "simplebar/dist/simplebar"; import "simplebar/dist/simplebar";
// import flatpickr from "flatpickr";
// import "flatpickr/dist/flatpickr.min.css";
// class InitDatePicker {
// constructor(selector = ".datepicker") {
// this.selector = selector;
// }
// init() {
// const elements = document.querySelectorAll(this.selector);
// if (elements.length === 0) return; // Skip if no elements found
// const today = new Date();
// const minYear = today.getFullYear() - 5;
// elements.forEach((element) => {
// flatpickr(element, {
// enableTime: false,
// dateFormat: "Y-m-d",
// minDate: `${minYear}-01-01`,
// maxDate: today,
// });
// });
// }
// }
class Components { class Components {
initBootstrapComponents() { initBootstrapComponents() {
@@ -107,6 +131,7 @@ class FormValidation {
} }
document.addEventListener("DOMContentLoaded", function (e) { document.addEventListener("DOMContentLoaded", function (e) {
new Components().init(), new FormValidation().init(); new Components().init(), new FormValidation().init();
// new InitDatePicker().init();
}); });
class ThemeLayout { class ThemeLayout {
constructor() { constructor() {

View File

@@ -2,58 +2,36 @@ import { Grid } from "gridjs/dist/gridjs.umd.js";
import gridjs from "gridjs/dist/gridjs.umd.js"; import gridjs from "gridjs/dist/gridjs.umd.js";
import "gridjs/dist/gridjs.umd.js"; import "gridjs/dist/gridjs.umd.js";
import GlobalConfig from "../global-config.js"; import GlobalConfig from "../global-config.js";
import Swal from "sweetalert2";
class BusinessIndustries { class BusinessIndustries {
constructor() { constructor() {
this.table = null; // Store Grid.js instance this.toastMessage = document.getElementById("toast-message");
this.toastElement = document.getElementById("toastNotification");
this.toast = new bootstrap.Toast(this.toastElement);
this.table = null;
// Initialize functions
this.initTableBusinessIndustries();
this.initEvents();
} }
init() { initEvents() {
this.getFetchApiData(); document.body.addEventListener("click", async (event) => {
const deleteButton = event.target.closest(
".btn-delete-business-industry"
);
if (deleteButton) {
event.preventDefault();
await this.handleDelete(deleteButton);
}
});
} }
getFetchApiData() { initTableBusinessIndustries() {
let tableContainer = document.getElementById( let tableContainer = document.getElementById(
"table-business-industries" "table-business-industries"
); );
// Create a new Grid.js instance only if it doesn't exist
if (this.table) {
// If table exists, update its data instead of recreating
this.table
.updateConfig({
server: {
url: `${GlobalConfig.apiHost}/api/api-business-industries`,
credentials: "include",
headers: {
Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`,
"Content-Type": "application/json",
},
then: (data) =>
data.data.map((item) => [
item.id,
item.nama_kecamatan,
item.nama_kelurahan,
item.nop,
item.nama_wajib_pajak,
item.alamat_wajib_pajak,
item.alamat_objek_pajak,
item.luas_bumi,
item.luas_bangunan,
item.njop_bumi,
item.njop_bangunan,
item.ketetapan,
item.tahun_pajak,
item.created_at,
item.id,
]),
total: (data) => data.total,
},
})
.forceRender();
return;
}
this.table = new Grid({ this.table = new Grid({
columns: [ columns: [
{ name: "ID", width: "80px", hidden: false }, { name: "ID", width: "80px", hidden: false },
@@ -71,27 +49,20 @@ class BusinessIndustries {
{ name: "Tahun Pajak", width: "120px" }, { name: "Tahun Pajak", width: "120px" },
{ name: "Created", width: "180px" }, { name: "Created", width: "180px" },
{ {
name: "Actions", name: "Action",
width: "120px", formatter: (cell) =>
formatter: function (cell) { gridjs.html(`
return gridjs.html(` <div class="d-flex justify-content-center gap-2">
<div class="d-flex justify-content-center gap-2"> <a href="/data/business-industries/${cell}/edit" class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center">
<a href="/data/business-industries/${cell}/edit" class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center"> <i class='bx bx-edit'></i>
<i class='bx bx-edit'></i> </a>
</a> <button data-id="${cell}" class="btn btn-sm btn-red btn-delete-business-industry d-inline-flex align-items-center justify-content-center">
<button class="btn btn-sm btn-red d-inline-flex align-items-center justify-content-center btn-delete-business-industries" data-id="${cell}"> <i class='bx bxs-trash' ></i>
<i class='bx bxs-trash'></i> </button>
</button> </div>
</div> `),
`);
},
}, },
], ],
search: {
server: {
url: (prev, keyword) => `${prev}?search=${keyword}`,
},
},
pagination: { pagination: {
limit: 15, limit: 15,
server: { server: {
@@ -102,6 +73,11 @@ class BusinessIndustries {
}, },
}, },
sort: true, sort: true,
search: {
server: {
url: (prev, keyword) => `${prev}?search=${keyword}`,
},
},
server: { server: {
url: `${GlobalConfig.apiHost}/api/api-business-industries`, url: `${GlobalConfig.apiHost}/api/api-business-industries`,
headers: { headers: {
@@ -131,98 +107,26 @@ class BusinessIndustries {
total: (data) => data.total, total: (data) => data.total,
}, },
}).render(tableContainer); }).render(tableContainer);
document.addEventListener("click", this.handleDelete.bind(this));
} }
handleDelete(event) { async handleDelete(deleteButton) {
if (event.target.classList.contains("btn-delete-business-industries")) { const id = deleteButton.getAttribute("data-id");
event.preventDefault();
const id = event.target.getAttribute("data-id");
let modalElement = document.getElementById("modalConfirmation");
let toastMessage = document.getElementById("toast-message");
if (!modalElement) { const result = await Swal.fire({
console.error("Modal element not found!"); title: "Are you sure?",
return; text: "You won't be able to revert this!",
} icon: "warning",
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "Yes, delete it!",
});
let modal = new bootstrap.Modal(modalElement); if (result.isConfirmed) {
let btnSaveConfirmation = document.getElementById( try {
"btnSaveConfirmation" let response = await fetch(
); `${GlobalConfig.apiHost}/api/api-business-industries/${id}`,
let toastElement = document.getElementById("toastNotification"); {
let toast = new bootstrap.Toast(toastElement); method: "DELETE",
// Remove previous event listeners to avoid multiple bindings
btnSaveConfirmation.replaceWith(
btnSaveConfirmation.cloneNode(true)
);
btnSaveConfirmation = document.getElementById(
"btnSaveConfirmation"
);
// Set the role ID on the confirm button inside the modal
btnSaveConfirmation.setAttribute("data-business-industries-id", id);
// Show the modal
modal.show();
btnSaveConfirmation.addEventListener("click", async () => {
let deletedId = btnSaveConfirmation.getAttribute(
"data-business-industries-id"
);
try {
let response = await fetch(
`${GlobalConfig.apiHost}/api/api-business-industries/${deletedId}`,
{
method: "DELETE",
credentials: "include",
headers: {
"X-CSRF-TOKEN": document
.querySelector('meta[name="csrf-token"]')
.getAttribute("content"),
Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`,
"Content-Type": "application/json",
},
}
);
if (response.ok) {
let result = await response.json();
toastMessage.innerText =
result.message || "Deleted successfully!";
toast.show();
// Hide modal
modal.hide();
// Refresh Grid.js table
this.refreshDataSettings();
} else {
let error = await response.json();
console.error("Delete failed:", error);
toastMessage.innerText =
error.message || "Delete failed!";
toast.show();
}
} catch (error) {
console.error("Error deleting item:", error);
toastMessage.innerText = "An error occurred!";
toast.show();
}
});
}
}
refreshDataSettings() {
if (this.table) {
this.table
.updateConfig({
server: {
url: `${GlobalConfig.apiHost}/api/api-business-industries`,
credentials: "include", credentials: "include",
headers: { headers: {
Authorization: `Bearer ${document Authorization: `Bearer ${document
@@ -230,31 +134,34 @@ class BusinessIndustries {
.getAttribute("content")}`, .getAttribute("content")}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
then: (data) => }
data.data.map((item) => [ );
item.id,
item.nama_kecamatan, if (response.ok) {
item.nama_kelurahan, let result = await response.json();
item.nop, this.toastMessage.innerText =
item.nama_wajib_pajak, result.message || "Deleted successfully!";
item.alamat_wajib_pajak, this.toast.show();
item.alamat_objek_pajak,
item.luas_bumi, // Refresh Grid.js table
item.luas_bangunan, if (typeof this.table !== "undefined") {
item.njop_bumi, this.table.updateConfig({}).forceRender();
item.njop_bangunan, }
item.ketetapan, } else {
item.tahun_pajak, let error = await response.json();
item.created_at, console.error("Delete failed:", error);
item.id, // ID for Actions column this.toastMessage.innerText =
]), error.message || "Delete failed!";
total: (data) => data.total, this.toast.show();
}, }
}) } catch (error) {
.forceRender(); console.error("Error deleting item:", error);
this.toastMessage.innerText = "An error occurred!";
this.toast.show();
}
} }
} }
} }
document.addEventListener("DOMContentLoaded", function (e) { document.addEventListener("DOMContentLoaded", function (e) {
new BusinessIndustries().init(); new BusinessIndustries();
}); });

View File

@@ -0,0 +1,176 @@
import GlobalConfig from "../global-config.js";
document.addEventListener("DOMContentLoaded", function () {
document.querySelectorAll(".nav-link").forEach(tab => {
tab.addEventListener("click", function () {
setTimeout(() => {
const tab_active = getActiveTabId();
console.log("Active Tab ID:", tab_active);
}, 100); // Timeout untuk memastikan class `active` sudah diperbarui
});
});
const textarea = document.getElementById("user-message");
const sendButton = document.getElementById("send");
const conversationArea = document.querySelector(".row.flex-grow");
// Fungsi untuk mengirim pesan
async function sendMessage() {
const userText = textarea.value.trim();
if (userText !== "") {
// Kosongkan textarea setelah mengirim
textarea.value = "";
// Ambil tab aktif saat ini
const currentTab = getActiveTabId();
// Tambahkan pesan user ke UI
addMessage(userText, "user");
// Tambahkan pesan bot sementara dengan "Loading..."
const botMessageElement = addMessage('<div class="loader w-auto"></div>', "bot");
// Panggil API untuk mendapatkan response dari bot
const botResponse = await getBotResponse(currentTab, userText);
// Perbarui pesan bot dengan respons yang sebenarnya
botMessageElement.innerHTML = botResponse;;
}
}
// Event listener untuk klik tombol
sendButton.addEventListener("click", sendMessage);
// Event listener untuk menekan Enter di textarea
textarea.addEventListener("keydown", function (event) {
if (event.key === "Enter" && !event.shiftKey) {
event.preventDefault(); // Mencegah newline di textarea
sendMessage(); // Panggil fungsi kirim pesan
}
});
function addMessage(text, sender) {
const messageRow = document.createElement("div");
messageRow.classList.add("row", "flex-grow", "overflow-auto");
const messageCol = document.createElement("div");
messageCol.classList.add("w-auto", "d-inline-block"); // Menyesuaikan lebar konten
if (sender === "user") {
messageCol.classList.add("ms-auto", "max-w-50"); // Rata kanan, max 50% (setara col-6)
} else {
messageCol.classList.add("max-w-75"); // Max 75% (setara col-9)
// Tambahkan avatar hanya untuk bot
const avatarSpan = document.createElement("span");
avatarSpan.classList.add("d-flex", "align-items-center", "mb-1");
const avatarImg = document.createElement("img");
avatarImg.classList.add("rounded-circle");
avatarImg.width = 32;
avatarImg.src = "/images/iconchatbot.jpeg";
avatarImg.alt = "bot-avatar";
avatarSpan.appendChild(avatarImg);
messageCol.appendChild(avatarSpan);
}
const messageDiv = document.createElement("div");
messageDiv.classList.add("p-2", "rounded", "mb-2");
if (sender === "user") {
messageDiv.classList.add("user-response", "bg-primary", "text-white");
} else {
messageDiv.classList.add("bot-response", "bg-light");
}
// Menyisipkan konten HTML langsung (bisa berupa teks atau loader)
messageDiv.innerHTML = text;
messageCol.appendChild(messageDiv);
messageRow.appendChild(messageCol);
// Tambahkan ke area percakapan
conversationArea.appendChild(messageRow);
// Scroll otomatis ke bawah
conversationArea.scrollTop = conversationArea.scrollHeight;
return messageDiv; // Mengembalikan elemen agar bisa diperbarui nanti
}
// function addMessage(text, sender) {
// const messageRow = document.createElement("div");
// messageRow.classList.add("row", "flex-grow", "overflow-auto");
// const messageCol = document.createElement("div");
// messageCol.classList.add("col-9", "w-auto");
// if (sender === "user") {
// messageCol.classList.add("ms-auto"); // Geser ke kanan untuk user
// }
// const messageDiv = document.createElement("div");
// messageDiv.classList.add("p-2", "rounded", "mb-2");
// if (sender === "user") {
// messageDiv.classList.add("user-response", "bg-primary", "text-white");
// } else {
// messageDiv.classList.add("bot-response", "bg-light");
// // Tambahkan avatar hanya untuk bot
// const avatarSpan = document.createElement("span");
// avatarSpan.classList.add("d-flex", "align-items-center", "mb-1");
// const avatarImg = document.createElement("img");
// avatarImg.classList.add("rounded-circle");
// avatarImg.width = 32;
// avatarImg.src = "/images/iconchatbot.jpeg";
// avatarImg.alt = "bot-avatar";
// avatarSpan.appendChild(avatarImg);
// messageCol.appendChild(avatarSpan);
// }
// // Menyisipkan konten HTML langsung (bisa berupa teks atau loader)
// messageDiv.innerHTML = text;
// messageCol.appendChild(messageDiv);
// messageRow.appendChild(messageCol);
// // Tambahkan ke area percakapan
// conversationArea.appendChild(messageRow);
// // Scroll otomatis ke bawah
// conversationArea.scrollTop = conversationArea.scrollHeight;
// return messageDiv; // Mengembalikan elemen agar bisa diperbarui nanti
// }
// Fungsi untuk memanggil API
async function getBotResponse(tab_active, userText) {
try {
const url = `${GlobalConfig.apiHost}/api/generate-text`;
const response = await fetch(url, {
method: "POST",
body: JSON.stringify({tab_active:tab_active, prompt: userText }),
headers: {
Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`,
"Content-Type": "application/json",
},
});
const data = await response.json();
return data.response || "Maaf, saya tidak mengerti.";
} catch (error) {
console.error("Error fetching bot response:", error);
return "Terjadi kesalahan, coba lagi nanti.";
}
}
});
function getActiveTabId() {
const activeTab = document.querySelector(".nav-link.active");
return activeTab ? activeTab.id : null;
}

View File

@@ -0,0 +1,65 @@
class CreateCustomer {
constructor() {
this.initCreateCustomer();
}
initCreateCustomer() {
const toastNotification = document.getElementById("toastNotification");
const toast = new bootstrap.Toast(toastNotification);
document
.getElementById("btnCreateCustomer")
.addEventListener("click", async function () {
let submitButton = this;
let spinner = document.getElementById("spinner");
let form = document.getElementById("formCreateCustomer");
if (!form) {
console.error("Form element not found!");
return;
}
// Get form data
let formData = new FormData(form);
// Disable button and show spinner
submitButton.disabled = true;
spinner.classList.remove("d-none");
try {
let response = await fetch(form.action, {
method: "POST",
headers: {
Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`,
},
body: formData,
});
if (response.ok) {
let result = await response.json();
document.getElementById("toast-message").innerText =
result.message;
toast.show();
setTimeout(() => {
window.location.href = "/data/customers";
}, 2000);
} else {
let error = await response.json();
document.getElementById("toast-message").innerText =
error.message;
toast.show();
console.error("Error:", error);
}
} catch (error) {
console.error("Request failed:", error);
document.getElementById("toast-message").innerText =
error.message;
toast.show();
}
});
}
}
document.addEventListener("DOMContentLoaded", function (e) {
new CreateCustomer();
});

View File

@@ -0,0 +1,65 @@
class UpdateCustomer {
constructor() {
this.initUpdateCustomer();
}
initUpdateCustomer() {
const toastNotification = document.getElementById("toastNotification");
const toast = new bootstrap.Toast(toastNotification);
document
.getElementById("btnUpdateCustomer")
.addEventListener("click", async function () {
let submitButton = this;
let spinner = document.getElementById("spinner");
let form = document.getElementById("formUpdateCustomer");
if (!form) {
console.error("Form element not found!");
return;
}
// Get form data
let formData = new FormData(form);
// Disable button and show spinner
submitButton.disabled = true;
spinner.classList.remove("d-none");
try {
let response = await fetch(form.action, {
method: "POST",
headers: {
Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`,
},
body: formData,
});
if (response.ok) {
let result = await response.json();
document.getElementById("toast-message").innerText =
result.message;
toast.show();
setTimeout(() => {
window.location.href = "/data/customers";
}, 2000);
} else {
let error = await response.json();
document.getElementById("toast-message").innerText =
error.message;
toast.show();
console.error("Error:", error);
}
} catch (error) {
console.error("Request failed:", error);
document.getElementById("toast-message").innerText =
error.message;
toast.show();
}
});
}
}
document.addEventListener("DOMContentLoaded", function (e) {
new UpdateCustomer();
});

View File

@@ -0,0 +1,151 @@
import { Grid } from "gridjs/dist/gridjs.umd.js";
import gridjs from "gridjs/dist/gridjs.umd.js";
import "gridjs/dist/gridjs.umd.js";
import GlobalConfig from "../global-config";
import Swal from "sweetalert2";
class Customers {
constructor() {
this.toastMessage = document.getElementById("toast-message");
this.toastElement = document.getElementById("toastNotification");
this.toast = new bootstrap.Toast(this.toastElement);
this.table = null;
// Initialize functions
this.initTableCustomers();
this.initEvents();
}
initEvents() {
document.body.addEventListener("click", async (event) => {
const deleteButton = event.target.closest(".btn-delete-customers");
if (deleteButton) {
event.preventDefault();
await this.handleDelete(deleteButton);
}
});
}
initTableCustomers() {
let tableContainer = document.getElementById("table-customers");
// Create a new Grid.js instance only if it doesn't exist
this.table = new Grid({
columns: [
"ID",
"Nomor Pelanggan",
"Nama",
"Kota Pelayanan",
"Alamat",
"Latitude",
"Longitude",
{
name: "Action",
formatter: (cell) =>
gridjs.html(`
<div class="d-flex justify-content-center gap-2">
<a href="/data/customers/${cell}/edit" class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center">
<i class='bx bx-edit'></i>
</a>
<button data-id="${cell}" class="btn btn-sm btn-red btn-delete-customers d-inline-flex align-items-center justify-content-center">
<i class='bx bxs-trash' ></i>
</button>
</div>
`),
},
],
pagination: {
limit: 15,
server: {
url: (prev, page) =>
`${prev}${prev.includes("?") ? "&" : "?"}page=${
page + 1
}`,
},
},
sort: true,
search: {
server: {
url: (prev, keyword) => `${prev}?search=${keyword}`,
},
},
server: {
url: `${GlobalConfig.apiHost}/api/customers`,
headers: {
Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`,
"Content-Type": "application/json",
},
then: (data) =>
data.data.map((item) => [
item.id,
item.nomor_pelanggan,
item.nama,
item.kota_pelayanan,
item.alamat,
item.latitude,
item.longitude,
item.id,
]),
total: (data) => data.meta.total,
},
}).render(tableContainer);
}
async handleDelete(deleteButton) {
const id = deleteButton.getAttribute("data-id");
const result = await Swal.fire({
title: "Are you sure?",
text: "You won't be able to revert this!",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "Yes, delete it!",
});
if (result.isConfirmed) {
try {
let response = await fetch(
`${GlobalConfig.apiHost}/api/customers/${id}`,
{
method: "DELETE",
credentials: "include",
headers: {
Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`,
"Content-Type": "application/json",
},
}
);
if (response.ok) {
let result = await response.json();
this.toastMessage.innerText =
result.message || "Deleted successfully!";
this.toast.show();
// Refresh Grid.js table
if (typeof this.table !== "undefined") {
this.table.updateConfig({}).forceRender();
}
} else {
let error = await response.json();
console.error("Delete failed:", error);
this.toastMessage.innerText =
error.message || "Delete failed!";
this.toast.show();
}
} catch (error) {
console.error("Error deleting item:", error);
this.toastMessage.innerText = "An error occurred!";
this.toast.show();
}
}
}
}
document.addEventListener("DOMContentLoaded", function (e) {
new Customers();
});

View File

@@ -0,0 +1,78 @@
import { Dropzone } from "dropzone";
Dropzone.autoDiscover = false;
class UploadCustomers {
constructor() {
this.spatialDropzone = null;
this.formElement = document.getElementById("formUploadCustomers");
this.uploadButton = document.getElementById("submit-upload");
this.spinner = document.getElementById("spinner");
if (!this.formElement) {
console.error("Element formUploadCustomers tidak ditemukan!");
}
}
init() {
this.initDropzone();
this.setupUploadButton();
}
initDropzone() {
const toastNotification = document.getElementById("toastNotification");
const toast = new bootstrap.Toast(toastNotification);
var previewTemplate,
dropzonePreviewNode = document.querySelector(
"#dropzone-preview-list"
);
(dropzonePreviewNode.id = ""),
dropzonePreviewNode &&
((previewTemplate = dropzonePreviewNode.parentNode.innerHTML),
dropzonePreviewNode.parentNode.removeChild(dropzonePreviewNode),
(this.spatialDropzone = new Dropzone(".dropzone", {
url: this.formElement.action,
method: "post",
acceptedFiles: ".xls,.xlsx",
previewTemplate: previewTemplate,
previewsContainer: "#dropzone-preview",
autoProcessQueue: false,
headers: {
Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`,
},
init: function () {
this.on("success", function (file, response) {
document.getElementById("toast-message").innerText =
response.message;
toast.show();
setTimeout(() => {
window.location.href = "/data/customers";
}, 2000);
});
this.on("error", function (file, errorMessage) {
document.getElementById("toast-message").innerText =
errorMessage.message;
toast.show();
this.uploadButton.disabled = false;
this.spinner.classList.add("d-none");
});
},
})));
}
setupUploadButton() {
this.uploadButton.addEventListener("click", (e) => {
if (this.spatialDropzone.files.length > 0) {
this.spatialDropzone.processQueue();
this.uploadButton.disabled = true;
this.spinner.classList.remove("d-none");
} else {
return;
}
});
}
}
document.addEventListener("DOMContentLoaded", function (e) {
new UploadCustomers().init();
});

View File

@@ -1,165 +1,141 @@
import Big from "big.js"; import Big from "big.js";
import GlobalConfig, { addThousandSeparators } from "../global-config.js"; import GlobalConfig, { addThousandSeparators } from "../global-config.js";
import flatpickr from "flatpickr"; import InitDatePicker from "../utils/InitDatePicker.js";
import "flatpickr/dist/flatpickr.min.css";
class BigData { class BigData {
async init() { async init() {
try { try {
this.filterYear = new Date().getFullYear(); // Set initial year new InitDatePicker(
"#datepicker-dashboard-bigdata",
let yearSelect = document.querySelector("#yearPicker"); this.handleChangeDate.bind(this)
let filterButton = document.querySelector("#btnFilterYear"); ).init();
if (!yearSelect || !filterButton) {
console.error(
"Element #yearPicker or #btnFilterYear not found."
);
return;
}
// Set default value for input
yearSelect.value = this.filterYear;
yearSelect.addEventListener("change", () => {
this.updateYear(yearSelect.value);
});
// Handle button click
filterButton.addEventListener("click", () => {
this.updateYear(yearSelect.value);
});
console.log("init filter this year", this.filterYear);
// Load initial data // Load initial data
await this.updateData(this.filterYear); this.updateData("latest");
} catch (error) { } catch (error) {
console.error("Error initializing data:", error); console.error("Error initializing data:", error);
} }
} }
updateYear(value) {
let inputYear = parseInt(value, 10); handleChangeDate(filterDate) {
if (!isNaN(inputYear)) { if (!filterDate) return;
this.filterYear = inputYear; this.updateData(filterDate);
this.updateData(this.filterYear);
} else {
console.error("Invalid year input");
}
} }
async updateData(year) { async updateData(filterDate) {
try { try {
this.totalTargetPAD = await this.getDataSettings("TARGET_PAD"); console.log("Filtering data for date:", filterDate);
this.resultDataTotal = await this.getDataTotalPotensi(year); this.resumeBigData = await this.getBigDataResume(filterDate);
this.dataVerification = await this.getDataVerfication(year); // this.totalTargetPAD = await this.getDataSettings("TARGET_PAD");
this.dataNonVerification = await this.getDataNonVerfication(year); // this.resultDataTotal = await this.getDataTotalPotensi(year);
this.dataBusiness = await this.getDataBusiness(year); // this.dataVerification = await this.getDataVerfication(year);
this.dataNonBusiness = await this.getDataNonBusiness(year); // this.dataNonVerification = await this.getDataNonVerfication(year);
this.dataTataRuang = await this.getDataSettings("TATA_RUANG"); // this.dataBusiness = await this.getDataBusiness(year);
this.dataSumRealisasiTerbit = await this.getDataSettings( // this.dataNonBusiness = await this.getDataNonBusiness(year);
"REALISASI_TERBIT_PBG_SUM" // this.dataTataRuang = await this.getDataSettings("TATA_RUANG");
); // this.dataSumRealisasiTerbit = await this.getDataSettings(
this.dataCountRealisasiTerbit = await this.getDataSettings( // "REALISASI_TERBIT_PBG_SUM"
"REALISASI_TERBIT_PBG_COUNT" // );
); // this.dataCountRealisasiTerbit = await this.getDataSettings(
this.dataSumMenungguKlikDPMPTSP = await this.getDataSettings( // "REALISASI_TERBIT_PBG_COUNT"
"MENUNGGU_KLIK_DPMPTSP_SUM" // );
); // this.dataSumMenungguKlikDPMPTSP = await this.getDataSettings(
this.dataCountMenungguKlikDPMPTSP = await this.getDataSettings( // "MENUNGGU_KLIK_DPMPTSP_SUM"
"MENUNGGU_KLIK_DPMPTSP_COUNT" // );
); // this.dataCountMenungguKlikDPMPTSP = await this.getDataSettings(
this.dataSumProsesDinasTeknis = await this.getDataSettings( // "MENUNGGU_KLIK_DPMPTSP_COUNT"
"PROSES_DINAS_TEKNIS_SUM" // );
); // this.dataSumProsesDinasTeknis = await this.getDataSettings(
this.dataCountProsesDinasTeknis = await this.getDataSettings( // "PROSES_DINAS_TEKNIS_SUM"
"PROSES_DINAS_TEKNIS_COUNT" // );
); // this.dataCountProsesDinasTeknis = await this.getDataSettings(
// "PROSES_DINAS_TEKNIS_COUNT"
// );
// total potensi // // total potensi
this.bigTargetPAD = new Big(this.totalTargetPAD ?? 0); // this.bigTargetPAD = new Big(this.totalTargetPAD ?? 0);
this.bigTotalPotensi = new Big(this.resultDataTotal.totalData ?? 0); // this.bigTotalPotensi = new Big(this.resultDataTotal.totalData ?? 0);
this.resultPercentage = 0; // this.resultPercentage = 0;
if (this.bigTotalPotensi > 0 && this.bigTargetPAD > 0) { // if (this.bigTotalPotensi > 0 && this.bigTargetPAD > 0) {
this.resultPercentage = this.bigTotalPotensi // this.resultPercentage = this.bigTotalPotensi
.div(this.bigTargetPAD) // .div(this.bigTargetPAD)
.times(100) // .times(100)
.toFixed(2); // .toFixed(2);
if (this.resultPercentage > 100) { // if (this.resultPercentage > 100) {
this.resultPercentage = 100; // this.resultPercentage = 100;
} // }
} // }
// tata ruang // // tata ruang
this.bigTotalTataRuang = new Big(this.dataTataRuang); // this.bigTotalTataRuang = new Big(this.dataTataRuang);
this.percentageResultTataRuang = // this.percentageResultTataRuang =
this.bigTotalTataRuang <= 0 || this.bigTotalPotensi <= 0 // this.bigTotalTataRuang <= 0 || this.bigTotalPotensi <= 0
? 0 // ? 0
: this.bigTotalTataRuang // : this.bigTotalTataRuang
.div(this.bigTotalPotensi) // .div(this.bigTotalPotensi)
.times(100) // .times(100)
.toFixed(2); // .toFixed(2);
// kekurangan potensi // // kekurangan potensi
this.totalKekuranganPotensi = new Big( // this.totalKekuranganPotensi = new Big(
this.bigTargetPAD - this.bigTotalPotensi // this.bigTargetPAD - this.bigTotalPotensi
); // );
this.percentageKekuranganPotensi = // this.percentageKekuranganPotensi =
this.totalKekuranganPotensi <= 0 || this.bigTargetPAD <= 0 // this.totalKekuranganPotensi <= 0 || this.bigTargetPAD <= 0
? 0 // ? 0
: this.totalKekuranganPotensi // : this.totalKekuranganPotensi
.div(this.bigTargetPAD) // .div(this.bigTargetPAD)
.times(100) // .times(100)
.toFixed(2); // .toFixed(2);
// non-verification documents // // non-verification documents
this.bigTotalNonVerification = new Big( // this.bigTotalNonVerification = new Big(
this.dataNonVerification.total // this.dataNonVerification.total
); // );
this.percentageResultNonVerification = // this.percentageResultNonVerification =
this.bigTotalNonVerification <= 0 || this.bigTotalPotensi // this.bigTotalNonVerification <= 0 || this.bigTotalPotensi <= 0
? 0 // ? 0
: this.bigTotalNonVerification // : this.bigTotalNonVerification
.div(this.bigTotalPotensi) // .div(this.bigTotalPotensi)
.times(100) // .times(100)
.toFixed(2); // .toFixed(2);
// verification documents // // verification documents
this.bigTotalVerification = new Big(this.dataVerification.total); // this.bigTotalVerification = new Big(this.dataVerification.total);
this.percetageResultVerification = // this.percetageResultVerification =
this.bigTotalVerification <= 0 || this.bigTotalPotensi <= 0 // this.bigTotalVerification <= 0 || this.bigTotalPotensi <= 0
? 0 // ? 0
: this.bigTotalVerification // : this.bigTotalVerification
.div(this.bigTargetPAD) // .div(this.bigTargetPAD)
.times(100) // .times(100)
.toFixed(2); // .toFixed(2);
// business documents // // business documents
this.bigTotalBusiness = new Big(this.dataBusiness.total); // this.bigTotalBusiness = new Big(this.dataBusiness.total);
this.percentageResultBusiness = // this.percentageResultBusiness =
this.bigTotalNonVerification <= 0 // this.bigTotalNonVerification <= 0 || this.bigTotalBusiness <= 0
? 0 // ? 0
: this.bigTotalBusiness // : this.bigTotalBusiness
.div(this.bigTotalNonVerification) // .div(this.bigTotalNonVerification)
.times(100) // .times(100)
.toFixed(2); // .toFixed(2);
// non-business documents // // non-business documents
this.bigTotalNonBusiness = new Big(this.dataNonBusiness.total); // this.bigTotalNonBusiness = new Big(this.dataNonBusiness.total);
this.percentageResultNonBusiness = // this.percentageResultNonBusiness =
this.bigTotalNonBusiness <= 0 || // this.bigTotalNonBusiness <= 0 ||
this.bigTotalNonVerification <= 0 // this.bigTotalNonVerification <= 0
? 0 // ? 0
: this.bigTotalNonBusiness // : this.bigTotalNonBusiness
.div(this.bigTotalNonVerification) // .div(this.bigTotalNonVerification)
.times(100) // .times(100)
.toFixed(2); // .toFixed(2);
if (!this.bigTargetPAD) { // if (!this.bigTargetPAD) {
console.error("Failed to load chart data"); // console.error("Failed to load chart data");
return; // return;
} // }
this.initChartTargetPAD(); this.initChartTargetPAD();
this.initChartUsaha(); this.initChartUsaha();
@@ -177,6 +153,33 @@ class BigData {
} }
} }
async getBigDataResume(filterByDate) {
try {
const response = await fetch(
`${GlobalConfig.apiHost}/api/bigdata-resume?filterByDate=${filterByDate}`,
{
credentials: "include",
headers: {
Authorization: `Bearer ${
document.querySelector("meta[name='api-token']")
.content
}`,
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
console.error("Network response was not ok", response);
}
const data = await response.json();
return data;
} catch (error) {
console.error("Error fetching chart data:", error);
return null;
}
}
async getDataTotalPotensi(year) { async getDataTotalPotensi(year) {
try { try {
const response = await fetch( const response = await fetch(
@@ -370,53 +373,60 @@ class BigData {
.querySelectorAll(".document-total.chart-target-pad") .querySelectorAll(".document-total.chart-target-pad")
.forEach((element) => { .forEach((element) => {
element.innerText = `Rp.${addThousandSeparators( element.innerText = `Rp.${addThousandSeparators(
this.bigTargetPAD.toString() // this.bigTargetPAD.toString()
this.resumeBigData.target_pad.sum.toString()
)}`; )}`;
}); });
document document
.querySelectorAll(".small-percentage.chart-target-pad") .querySelectorAll(".small-percentage.chart-target-pad")
.forEach((element) => { .forEach((element) => {
element.innerText = `${100}%`; element.innerText = `${this.resumeBigData.target_pad.percentage}%`;
}); });
} }
initChartTotalPotensi() { initChartTotalPotensi() {
const countAll = this.resultDataTotal.countData ?? 0; // const countAll = this.resultDataTotal.countData ?? 0;
document document
.querySelectorAll(".document-count.chart-total-potensi") .querySelectorAll(".document-count.chart-total-potensi")
.forEach((element) => { .forEach((element) => {
element.innerText = `${countAll}`; // element.innerText = `${countAll}`;
element.innerText = `${this.resumeBigData.total_potensi.count}`;
}); });
document document
.querySelectorAll(".document-total.chart-total-potensi") .querySelectorAll(".document-total.chart-total-potensi")
.forEach((element) => { .forEach((element) => {
element.innerText = `Rp.${addThousandSeparators( element.innerText = `Rp.${addThousandSeparators(
this.bigTotalPotensi.toString() // this.bigTotalPotensi.toString()
this.resumeBigData.total_potensi.sum.toString()
)}`; )}`;
}); });
document document
.querySelectorAll(".small-percentage.chart-total-potensi") .querySelectorAll(".small-percentage.chart-total-potensi")
.forEach((element) => { .forEach((element) => {
element.innerText = `${this.resultPercentage}%`; // element.innerText = `${this.resultPercentage}%`;
element.innerText = `${this.resumeBigData.total_potensi.percentage}%`;
}); });
} }
initChartVerificationDocuments() { initChartVerificationDocuments() {
document document
.querySelectorAll(".document-count.chart-berkas-terverifikasi") .querySelectorAll(".document-count.chart-berkas-terverifikasi")
.forEach((element) => { .forEach((element) => {
element.innerText = `${this.dataVerification.count}`; // element.innerText = `${this.dataVerification.count}`;
element.innerText = `${this.resumeBigData.verified_document.count}`;
}); });
document document
.querySelectorAll(".document-total.chart-berkas-terverifikasi") .querySelectorAll(".document-total.chart-berkas-terverifikasi")
.forEach((element) => { .forEach((element) => {
element.innerText = `Rp.${addThousandSeparators( element.innerText = `Rp.${addThousandSeparators(
this.bigTotalVerification.toString() // this.bigTotalVerification.toString()
this.resumeBigData.verified_document.sum.toString()
)}`; )}`;
}); });
document document
.querySelectorAll(".small-percentage.chart-berkas-terverifikasi") .querySelectorAll(".small-percentage.chart-berkas-terverifikasi")
.forEach((element) => { .forEach((element) => {
element.innerText = `${this.percetageResultVerification}%`; // element.innerText = `${this.percetageResultVerification}%`;
element.innerText = `${this.resumeBigData.verified_document.percentage}%`;
}); });
} }
initChartNonVerificationDocuments() { initChartNonVerificationDocuments() {
@@ -425,7 +435,8 @@ class BigData {
".document-count.chart-berkas-belum-terverifikasi" ".document-count.chart-berkas-belum-terverifikasi"
) )
.forEach((element) => { .forEach((element) => {
element.innerText = `${this.dataNonVerification.count}`; // element.innerText = `${this.dataNonVerification.count}`;
element.innerText = `${this.resumeBigData.non_verified_document.count}`;
}); });
document document
.querySelectorAll( .querySelectorAll(
@@ -433,7 +444,8 @@ class BigData {
) )
.forEach((element) => { .forEach((element) => {
element.innerText = `Rp.${addThousandSeparators( element.innerText = `Rp.${addThousandSeparators(
this.bigTotalNonVerification.toString() // this.bigTotalNonVerification.toString()
this.resumeBigData.non_verified_document.sum.toString()
)}`; )}`;
}); });
document document
@@ -441,45 +453,52 @@ class BigData {
".small-percentage.chart-berkas-belum-terverifikasi" ".small-percentage.chart-berkas-belum-terverifikasi"
) )
.forEach((element) => { .forEach((element) => {
element.innerText = `${this.percentageResultNonVerification}%`; // element.innerText = `${this.percentageResultNonVerification}%`;
element.innerText = `${this.resumeBigData.non_verified_document.percentage}%`;
}); });
} }
initChartUsaha() { initChartUsaha() {
document document
.querySelectorAll(".document-count.chart-business") .querySelectorAll(".document-count.chart-business")
.forEach((element) => { .forEach((element) => {
element.innerText = `${this.dataBusiness.count}`; // element.innerText = `${this.dataBusiness.count}`;
element.innerText = `${this.resumeBigData.business_document.count}`;
}); });
document document
.querySelectorAll(".document-total.chart-business") .querySelectorAll(".document-total.chart-business")
.forEach((element) => { .forEach((element) => {
element.innerText = `Rp.${addThousandSeparators( element.innerText = `Rp.${addThousandSeparators(
this.bigTotalBusiness.toString() // this.bigTotalBusiness.toString()
this.resumeBigData.business_document.sum.toString()
)}`; )}`;
}); });
document document
.querySelectorAll(".small-percentage.chart-business") .querySelectorAll(".small-percentage.chart-business")
.forEach((element) => { .forEach((element) => {
element.innerText = `${this.percentageResultBusiness}%`; // element.innerText = `${this.percentageResultBusiness}%`;
element.innerText = `${this.resumeBigData.business_document.percentage}%`;
}); });
} }
initChartNonUsaha() { initChartNonUsaha() {
document document
.querySelectorAll(".document-count.chart-non-business") .querySelectorAll(".document-count.chart-non-business")
.forEach((element) => { .forEach((element) => {
element.innerText = `${this.dataNonBusiness.count}`; // element.innerText = `${this.dataNonBusiness.count}`;
element.innerText = `${this.resumeBigData.non_business_document.count}`;
}); });
document document
.querySelectorAll(".document-total.chart-non-business") .querySelectorAll(".document-total.chart-non-business")
.forEach((element) => { .forEach((element) => {
element.innerText = `Rp.${addThousandSeparators( element.innerText = `Rp.${addThousandSeparators(
this.bigTotalNonBusiness.toString() // this.bigTotalNonBusiness.toString()
this.resumeBigData.non_business_document.sum.toString()
)}`; )}`;
}); });
document document
.querySelectorAll(".small-percentage.chart-non-business") .querySelectorAll(".small-percentage.chart-non-business")
.forEach((element) => { .forEach((element) => {
element.innerText = `${this.percentageResultNonBusiness}%`; // element.innerText = `${this.percentageResultNonBusiness}%`;
element.innerText = `${this.resumeBigData.non_business_document.percentage}%`;
}); });
} }
initChartKekuranganPotensi() { initChartKekuranganPotensi() {
@@ -492,70 +511,78 @@ class BigData {
.querySelectorAll(".document-total.chart-kekurangan-potensi") .querySelectorAll(".document-total.chart-kekurangan-potensi")
.forEach((element) => { .forEach((element) => {
element.innerText = `Rp.${addThousandSeparators( element.innerText = `Rp.${addThousandSeparators(
this.totalKekuranganPotensi.toString() // this.totalKekuranganPotensi.toString()
this.resumeBigData.kekurangan_potensi.sum.toString()
)}`; )}`;
}); });
document document
.querySelectorAll(".small-percentage.chart-kekurangan-potensi") .querySelectorAll(".small-percentage.chart-kekurangan-potensi")
.forEach((element) => { .forEach((element) => {
element.innerText = `${this.percentageKekuranganPotensi}%`; // element.innerText = `${this.percentageKekuranganPotensi}%`;
element.innerText = `${this.resumeBigData.kekurangan_potensi.percentage}%`;
}); });
} }
initChartRealisasiTerbitPBG() { initChartRealisasiTerbitPBG() {
document document
.querySelectorAll(".document-count.chart-realisasi-tebit-pbg") .querySelectorAll(".document-count.chart-realisasi-tebit-pbg")
.forEach((element) => { .forEach((element) => {
element.innerText = `${this.dataCountRealisasiTerbit}`; // element.innerText = `${this.dataCountRealisasiTerbit}`;
element.innerText = `${this.resumeBigData.realisasi_terbit.count}`;
}); });
document document
.querySelectorAll(".document-total.chart-realisasi-tebit-pbg") .querySelectorAll(".document-total.chart-realisasi-tebit-pbg")
.forEach((element) => { .forEach((element) => {
element.innerText = `Rp.${addThousandSeparators( element.innerText = `Rp.${addThousandSeparators(
this.dataSumRealisasiTerbit // this.dataSumRealisasiTerbit
this.resumeBigData.realisasi_terbit.sum.toString()
)}`; )}`;
}); });
document document
.querySelectorAll(".small-percentage.chart-realisasi-tebit-pbg") .querySelectorAll(".small-percentage.chart-realisasi-tebit-pbg")
.forEach((element) => { .forEach((element) => {
element.innerText = `0.00%`; element.innerText = `${this.resumeBigData.realisasi_terbit.percentage}%`;
}); });
} }
initChartMenungguKlikDPMPTSP() { initChartMenungguKlikDPMPTSP() {
document document
.querySelectorAll(".document-count.chart-menunggu-klik-dpmptsp") .querySelectorAll(".document-count.chart-menunggu-klik-dpmptsp")
.forEach((element) => { .forEach((element) => {
element.innerText = `${this.dataCountMenungguKlikDPMPTSP}`; // element.innerText = `${this.dataCountMenungguKlikDPMPTSP}`;
element.innerText = `${this.resumeBigData.menunggu_klik_dpmptsp.count}`;
}); });
document document
.querySelectorAll(".document-total.chart-menunggu-klik-dpmptsp") .querySelectorAll(".document-total.chart-menunggu-klik-dpmptsp")
.forEach((element) => { .forEach((element) => {
element.innerText = `Rp.${addThousandSeparators( element.innerText = `Rp.${addThousandSeparators(
this.dataSumMenungguKlikDPMPTSP // this.dataSumMenungguKlikDPMPTSP
this.resumeBigData.menunggu_klik_dpmptsp.sum.toString()
)}`; )}`;
}); });
document document
.querySelectorAll(".small-percentage.chart-menunggu-klik-dpmptsp") .querySelectorAll(".small-percentage.chart-menunggu-klik-dpmptsp")
.forEach((element) => { .forEach((element) => {
element.innerText = `0.00%`; element.innerText = `${this.resumeBigData.menunggu_klik_dpmptsp.percentage}%`;
}); });
} }
initChartProsesDinasTeknis() { initChartProsesDinasTeknis() {
document document
.querySelectorAll(".document-count.chart-proses-dinas-teknis") .querySelectorAll(".document-count.chart-proses-dinas-teknis")
.forEach((element) => { .forEach((element) => {
element.innerText = `${this.dataCountProsesDinasTeknis}`; // element.innerText = `${this.dataCountProsesDinasTeknis}`;
element.innerText = `${this.resumeBigData.proses_dinas_teknis.count}`;
}); });
document document
.querySelectorAll(".document-total.chart-proses-dinas-teknis") .querySelectorAll(".document-total.chart-proses-dinas-teknis")
.forEach((element) => { .forEach((element) => {
element.innerText = `Rp.${addThousandSeparators( element.innerText = `Rp.${addThousandSeparators(
this.dataSumProsesDinasTeknis // this.dataSumProsesDinasTeknis
this.resumeBigData.proses_dinas_teknis.sum.toString()
)}`; )}`;
}); });
document document
.querySelectorAll(".small-percentage.chart-proses-dinas-teknis") .querySelectorAll(".small-percentage.chart-proses-dinas-teknis")
.forEach((element) => { .forEach((element) => {
element.innerText = `0.00%`; element.innerText = `${this.resumeBigData.proses_dinas_teknis.percentage}%`;
}); });
} }
initChartPotensiTataRuang() { initChartPotensiTataRuang() {
@@ -568,13 +595,15 @@ class BigData {
.querySelectorAll(".document-total.chart-potensi-tata-ruang") .querySelectorAll(".document-total.chart-potensi-tata-ruang")
.forEach((element) => { .forEach((element) => {
element.innerText = `Rp.${addThousandSeparators( element.innerText = `Rp.${addThousandSeparators(
this.bigTotalTataRuang.toString() // this.bigTotalTataRuang.toString()
this.resumeBigData.tata_ruang.sum.toString()
)}`; )}`;
}); });
document document
.querySelectorAll(".small-percentage.chart-potensi-tata-ruang") .querySelectorAll(".small-percentage.chart-potensi-tata-ruang")
.forEach((element) => { .forEach((element) => {
element.innerText = `${this.percentageResultTataRuang}%`; // element.innerText = `${this.percentageResultTataRuang}%`;
element.innerText = `${this.resumeBigData.tata_ruang.percentage}%`;
}); });
} }
} }

View File

@@ -0,0 +1,172 @@
import Big from "big.js";
import GlobalConfig, { addThousandSeparators } from "../global-config.js";
import InitDatePicker from "../utils/InitDatePicker.js";
class LackOfPotential {
async init() {
new InitDatePicker(
"#datepicker-lack-of-potential",
this.handleChangedDate.bind(this)
).init();
this.bigTotalLackPotential = 0;
this.totalPotensi = await this.getDataTotalPotensi("latest");
this.totalTargetPAD = await this.getDataSettings("TARGET_PAD");
this.allCountData = await this.getValueDashboard();
this.reklameCount = this.allCountData.total_reklame ?? 0;
this.pdamCount = this.allCountData.total_pdam ?? 0;
this.tataRuangCount = this.allCountData.total_tata_ruang ?? 0;
this.bigTargetPAD = new Big(this.totalTargetPAD ?? 0);
this.bigTotalPotensi = new Big(this.totalPotensi.total ?? 0);
this.bigTotalLackPotential = this.bigTargetPAD.minus(
this.bigTotalPotensi
);
this.initChartKekuranganPotensi();
this.initDataValueDashboard();
}
async handleChangedDate(filterDate) {
const totalPotensi = await this.getDataTotalPotensi(filterDate);
this.bigTotalPotensi = new Big(totalPotensi.total ?? 0);
this.bigTotalLackPotential = this.bigTargetPAD.minus(
this.bigTotalPotensi
);
this.initChartKekuranganPotensi();
}
async getDataTotalPotensi(filterDate) {
try {
const response = await fetch(
`${GlobalConfig.apiHost}/api/bigdata-resume?filterByDate=${filterDate}`,
{
credentials: "include",
headers: {
Authorization: `Bearer ${
document.querySelector("meta[name='api-token']")
.content
}`,
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
console.error("Network response was not ok", response);
}
const data = await response.json();
return {
total: data.total_potensi.sum,
};
} catch (error) {
console.error("Error fetching chart data:", error);
return null;
}
}
async getDataSettings(string_key) {
try {
const response = await fetch(
`${GlobalConfig.apiHost}/api/data-settings?search=${string_key}`,
{
credentials: "include",
headers: {
Authorization: `Bearer ${
document.querySelector("meta[name='api-token']")
.content
}`,
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
console.error("Network response was not ok", response);
}
const data = await response.json();
return data.data[0].value;
} catch (error) {
console.error("Error fetching chart data:", error);
return 0;
}
}
async getValueDashboard() {
try {
const response = await fetch(
`${GlobalConfig.apiHost}/api/dashboard-potential-count`,
{
credentials: "include",
headers: {
Authorization: `Bearer ${
document.querySelector("meta[name='api-token']")
.content
}`,
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
console.error("Network response was not ok", response);
}
const data = await response.json();
return data;
} catch (error) {
console.error("Error fetching chart data:", error);
return 0;
}
}
initChartKekuranganPotensi() {
document
.querySelectorAll(".document-count.chart-lack-of-potential")
.forEach((element) => {
element.innerText = ``;
});
document
.querySelectorAll(".document-total.chart-lack-of-potential")
.forEach((element) => {
element.innerText = `Rp.${addThousandSeparators(
this.bigTotalLackPotential.toString()
)}`;
});
document
.querySelectorAll(".small-percentage.chart-lack-of-potential")
.forEach((element) => {
element.innerText = ``;
});
}
initDataValueDashboard() {
document.getElementById("reklame-count").innerText = this.reklameCount;
document.getElementById("pdam-count").innerText = this.pdamCount;
document.getElementById("pbb-bangunan-count").innerText =
this.tataRuangCount;
}
}
document.addEventListener("DOMContentLoaded", async function (e) {
await new LackOfPotential().init();
});
function resizeDashboard() {
let targetElement = document.getElementById("lack-of-potential-wrapper");
let dashboardElement = document.getElementById(
"lack-of-potential-fixed-container"
);
let targetWidth = targetElement.offsetWidth;
let dashboardWidth = 1400;
let scaleFactor = (targetWidth / dashboardWidth).toFixed(2);
// Prevent scaling beyond 1 (100%) to avoid overflow
scaleFactor = Math.min(scaleFactor, 1);
dashboardElement.style.transformOrigin = "left top";
dashboardElement.style.transition = "transform 0.2s ease-in-out";
dashboardElement.style.transform = `scale(${scaleFactor})`;
// Ensure horizontal scrolling is allowed if necessary
document.body.style.overflowX = "auto";
}
window.addEventListener("load", resizeDashboard);
window.addEventListener("resize", resizeDashboard);

View File

@@ -0,0 +1,543 @@
import Big from "big.js";
import GlobalConfig, { addThousandSeparators } from "../global-config.js";
import ApexCharts from "apexcharts";
import "gridjs/dist/gridjs.umd.js";
import GeneralTable from "../table-generator.js";
var chart;
document.addEventListener("DOMContentLoaded", async function () {
await initChart();
const yearPicker = document.getElementById("yearPicker");
async function updateDataByYear(selectedYear) {
// Target PAD Element
const targetPadElement = document.getElementById("target-pad");
if (!targetPadElement) return;
const targetPadValue = await getDataSettings("TARGET_PAD");
targetPadElement.textContent = formatCurrency(targetPadValue);
// Total Potensi Berkas
const totalPotensiBerkas = document.getElementById(
"total-potensi-berkas"
);
if (!totalPotensiBerkas) return;
const totalPotensiBerkasValue = await getDataTotalPotensi(selectedYear);
totalPotensiBerkas.textContent = formatCurrency(
totalPotensiBerkasValue.totalData
);
// Total Berkas Terverifikasi
const totalBerkasTerverifikasi = document.getElementById(
"total-berkas-terverifikasi"
);
if (!totalBerkasTerverifikasi) return;
const totalBerkasTerverifikasiValue = await getDataVerification(
selectedYear
);
totalBerkasTerverifikasi.textContent = formatCurrency(
totalBerkasTerverifikasiValue.totalData
);
// Total Kekurangan potensi
const totalKekuranganPotensi = document.getElementById(
"total-kekurangan-potensi"
);
if (!totalKekuranganPotensi) return;
const totalKekuranganPotensiValue =
new Big(targetPadValue) -
new Big(totalPotensiBerkasValue.totalData);
totalKekuranganPotensi.textContent = formatCurrency(
totalKekuranganPotensiValue
);
// Total Potensi PBG dari tata ruang
const totalPotensiPBGTataRuang = document.getElementById(
"total-potensi-pbd-tata-ruang"
);
if (!totalPotensiPBGTataRuang) return;
const totalPotensiPBGTataRuangValue = await getDataSettings(
"TATA_RUANG"
);
totalPotensiPBGTataRuang.textContent = formatCurrency(
totalPotensiPBGTataRuangValue
);
// Total Berkas Belum terverifikasi
const totalBerkasBelumTerverifikasi = document.getElementById(
"total-berkas-belum-terverifikasi"
);
if (!totalBerkasBelumTerverifikasi) return;
const totalBerkasBelumTerverifikasiValue = await getDataNonVerification(
selectedYear
);
const totalBerkasBelumTerverifikasiCount =
totalBerkasBelumTerverifikasiValue.countData;
totalBerkasBelumTerverifikasi.textContent = formatCurrency(
totalBerkasBelumTerverifikasiValue.totalData
);
// Total Berkas Usaha
const totalBerkasUsahaValue = await getDataBusiness(selectedYear);
const totalBerkasUsahaCount = totalBerkasUsahaValue.countData;
const totalBerkasUsahaTotalData = totalBerkasUsahaValue.totalData;
// Total Berkas Non Usaha
const totalBerkasNonUsahaValue = await getDataNonBusiness(selectedYear);
const totalBerkasNonUsahaCount = totalBerkasNonUsahaValue.countData;
const totalBerkasNonUsahaTotalData = totalBerkasNonUsahaValue.totalData;
// Pie Chart Section
let persenUsaha =
totalBerkasBelumTerverifikasiCount > 0
? (
(totalBerkasUsahaCount /
totalBerkasBelumTerverifikasiCount) *
100
).toFixed(2)
: "0";
let persenNonUsaha =
totalBerkasBelumTerverifikasiCount > 0
? (
(totalBerkasNonUsahaCount /
totalBerkasBelumTerverifikasiCount) *
100
).toFixed(2)
: "0";
const dataSeriesPieChart = [
Number(persenUsaha),
Number(persenNonUsaha),
];
const labelsPieChart = ["Berkas Usaha", "Berkas Non Usaha"];
document.querySelector("td[data-category='non-usaha']").textContent =
formatCurrency(totalBerkasNonUsahaTotalData).toLocaleString();
document.querySelector(
"td[data-category='non-usaha-percentage']"
).textContent = persenNonUsaha + "%";
document.querySelector("td[data-category='usaha']").textContent =
formatCurrency(totalBerkasUsahaTotalData).toLocaleString();
document.querySelector(
"td[data-category='usaha-percentage']"
).textContent = persenUsaha + "%";
updatePieChart(dataSeriesPieChart, labelsPieChart);
// Load all Tourism location
const allLocation = await getAllLocation();
console.log(allLocation);
// Filter hanya data yang memiliki angka valid
let validLocations = allLocation.dataLocation.filter((loc) => {
return (
!isNaN(parseFloat(loc.longitude)) &&
!isNaN(parseFloat(loc.latitude))
);
});
// Ubah string ke float
validLocations = validLocations.map((loc) => ({
name: loc.project_name,
longitude: parseFloat(loc.longitude),
latitude: parseFloat(loc.latitude),
}));
console.log(validLocations.name);
// Inisialisasi peta
var map = L.map("map").setView([-7.023, 107.5275], 10);
L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", {
attribution: "&copy; OpenStreetMap contributors",
}).addTo(map);
// Tambahkan marker ke peta
validLocations.forEach(function (loc) {
L.marker([loc.latitude, loc.longitude])
.addTo(map)
.bindPopup(`<b>${loc.name}</b>`) // Popup saat diklik
.bindTooltip(loc.name, { permanent: false, direction: "top" }); // Tooltip saat di-hover
});
// Realisasi terbit PBG
const totalRealisasiTerbitPBG = document.getElementById(
"realisasi-terbit-pbg"
);
if (!totalRealisasiTerbitPBG) return;
const totalRealisasiTerbitPBGValue = await getDataSettings(
"REALISASI_TERBIT_PBG_SUM"
);
totalRealisasiTerbitPBG.textContent = formatCurrency(
totalRealisasiTerbitPBGValue
);
// Menunggu Klik DPMPTSP
const totalMenungguKlikDpmptsp = document.getElementById(
"waiting-click-dpmptsp"
);
if (!totalMenungguKlikDpmptsp) return;
const totalMenungguKlikDpmptspValue = await getDataSettings(
"MENUNGGU_KLIK_DPMPTSP_SUM"
);
totalMenungguKlikDpmptsp.textContent = formatCurrency(
totalMenungguKlikDpmptspValue
);
// Proses Dinas Teknis
const totalProsesDinasTeknis = document.getElementById(
"processing-technical-services"
);
if (!totalProsesDinasTeknis) return;
const totalProsesDinasTeknisValue = await getDataSettings(
"PROSES_DINAS_TEKNIS_SUM"
);
totalProsesDinasTeknis.textContent = formatCurrency(
totalProsesDinasTeknisValue
);
// Load Tabel Baru di Update
const tableLastUpdated = new GeneralTable(
"pbg-filter-by-updated-at",
`${GlobalConfig.apiHost}/api/api-pbg-task?isLastUpdated=true`,
`${GlobalConfig.apiHost}`,
pbgTaskColumns
);
tableLastUpdated.processData = function (data) {
console.log("Response Data:", data);
return data.data.map((item) => {
return [
item.no,
item.name,
item.registration_number,
item.document_number,
item.address,
];
});
};
tableLastUpdated.init();
// Load Tabel Status SK Terbit
const tableSKPBGTerbit = new GeneralTable(
"pbg-filter-by-status",
`${GlobalConfig.apiHost}/api/api-pbg-task?isLastUpdated=false`,
`${GlobalConfig.apiHost}`,
pbgTaskColumns
);
tableSKPBGTerbit.processData = function (data) {
console.log("Response Data:", data);
return data.data.map((item) => {
return [
item.no,
item.name,
item.registration_number,
item.document_number,
item.address,
];
});
};
tableSKPBGTerbit.init();
document.querySelector(
"#pbg-filter-by-updated-at .gridjs-search"
).hidden = true;
document.querySelector(
"#pbg-filter-by-updated-at .gridjs-footer"
).hidden = true;
document.querySelector(
"#pbg-filter-by-status .gridjs-search"
).hidden = true;
document.querySelector(
"#pbg-filter-by-status .gridjs-footer"
).hidden = true;
}
await updateDataByYear(yearPicker.value);
yearPicker.addEventListener("change", async function () {
console.log("event change dropdown");
await updateDataByYear(yearPicker.value);
});
});
async function getDataSettings(string_key) {
try {
const response = await fetch(
`${GlobalConfig.apiHost}/api/data-settings?search=${string_key}`,
{
credentials: "include",
headers: {
Authorization: `Bearer ${
document.querySelector("meta[name='api-token']").content
}`,
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
console.error("Network response was not ok", response);
}
const data = await response.json();
return data.data?.[0]?.value ?? 0; // Pastikan tidak error jika data kosong
} catch (error) {
console.error("Error fetching data:", error);
return 0;
}
}
async function getDataTotalPotensi(year) {
try {
const response = await fetch(
`${GlobalConfig.apiHost}/api/all-task-documents?year=${year}`,
{
credentials: "include",
headers: {
Authorization: `Bearer ${
document.querySelector("meta[name='api-token']").content
}`,
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
console.error("Network response was not ok", response);
}
const data = await response.json();
return {
totalData: data.data.total,
};
} catch (error) {
console.error("Error fetching chart data:", error);
return null;
}
}
async function getDataVerification(year) {
try {
const response = await fetch(
`${GlobalConfig.apiHost}/api/verification-documents?year=${year}`,
{
credentials: "include",
headers: {
Authorization: `Bearer ${
document.querySelector("meta[name='api-token']").content
}`,
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
console.error("Network response was not ok", response);
}
const data = await response.json();
return {
totalData: data.data.total,
};
} catch (error) {
console.error("Error fetching chart data:", error);
return 0;
}
}
async function getDataNonVerification(year) {
try {
const response = await fetch(
`${GlobalConfig.apiHost}/api/non-verification-documents?year=${year}`,
{
credentials: "include",
headers: {
Authorization: `Bearer ${
document.querySelector("meta[name='api-token']").content
}`,
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
console.error("Network response was not ok", response);
}
const data = await response.json();
return {
countData: data.data.count,
totalData: data.data.total,
};
} catch (error) {
console.error("Error fetching chart data:", error);
return 0;
}
}
async function getDataBusiness(year) {
try {
const response = await fetch(
`${GlobalConfig.apiHost}/api/business-documents?year=${year}`,
{
credentials: "include",
headers: {
Authorization: `Bearer ${
document.querySelector("meta[name='api-token']").content
}`,
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
console.error("Network response was not ok", response);
}
const data = await response.json();
return {
countData: data.data.count,
totalData: data.data.total,
};
} catch (error) {
console.error("Error fetching chart data:", error);
return 0;
}
}
async function getDataNonBusiness(year) {
try {
const response = await fetch(
`${GlobalConfig.apiHost}/api/non-business-documents?year=${year}`,
{
credentials: "include",
headers: {
Authorization: `Bearer ${
document.querySelector("meta[name='api-token']").content
}`,
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
console.error("Network response was not ok", response);
}
const data = await response.json();
return {
countData: data.data.count,
totalData: data.data.total,
};
} catch (error) {
console.error("Error fetching chart data:", error);
return 0;
}
}
async function getAllLocation() {
try {
const response = await fetch(
`${GlobalConfig.apiHost}/api/get-all-location`,
{
credentials: "include",
headers: {
Authorization: `Bearer ${
document.querySelector("meta[name='api-token']").content
}`,
"Content-Type": "application/json",
},
}
);
if (!response.ok) {
console.error("Network response was not ok", response);
}
const data = await response.json();
return {
dataLocation: data.data,
};
} catch (error) {
console.error("Error fetching chart data:", error);
return 0;
}
}
async function initChart() {
var options = {
chart: {
height: 180,
type: "donut",
},
series: [0, 0], // Inisialisasi dengan nilai awal
labels: ["Berkas Usaha", "Berkas Non Usaha"],
legend: {
show: false,
},
stroke: {
width: 0,
},
plotOptions: {
pie: {
donut: {
size: "60%",
},
},
},
colors: ["#7e67fe", "#17c553"],
dataLabels: {
enabled: false,
},
responsive: [
{
breakpoint: 480,
options: {
chart: {
width: 200,
},
},
},
],
fill: {
type: "gradient",
},
};
chart = new ApexCharts(document.querySelector("#conversions"), options);
chart.render();
}
async function updatePieChart(dataSeries, labels) {
if (!Array.isArray(dataSeries) || dataSeries.length === 0) {
console.error("Data series tidak valid:", dataSeries);
return;
}
// Perbarui data series chart
chart.updateSeries(dataSeries);
// Perbarui label jika diperlukan
if (Array.isArray(labels) && labels.length === dataSeries.length) {
chart.updateOptions({
labels: labels,
});
}
}
// Fungsi untuk format angka ke Rupiah
function formatCurrency(number) {
return new Intl.NumberFormat("id-ID", {
style: "currency",
currency: "IDR",
minimumFractionDigits: 0,
}).format(number);
}
const pbgTaskColumns = [
"No",
"Name",
"Nomor Registrasi",
"Nomor Dokumen",
"Alamat",
];

View File

@@ -24,10 +24,9 @@ document.addEventListener("DOMContentLoaded", function (e) {
method: "POST", method: "POST",
credentials: "include", credentials: "include",
headers: { headers: {
"X-CSRF-TOKEN": document Authorization: `Bearer ${document
.querySelector('meta[name="csrf-token"]') .querySelector('meta[name="api-token"]')
.getAttribute("content"), .getAttribute("content")}`,
"Content-Type": "application/json",
}, },
body: formData, body: formData,
}); });
@@ -35,7 +34,7 @@ document.addEventListener("DOMContentLoaded", function (e) {
if (response.ok) { if (response.ok) {
let result = await response.json(); let result = await response.json();
document.getElementById("toast-message").innerText = document.getElementById("toast-message").innerText =
result.message; result.data.message;
toast.show(); toast.show();
setTimeout(() => { setTimeout(() => {
window.location.href = "/data-settings"; window.location.href = "/data-settings";
@@ -46,6 +45,8 @@ document.addEventListener("DOMContentLoaded", function (e) {
error.message; error.message;
toast.show(); toast.show();
console.error("Error:", error); console.error("Error:", error);
submitButton.disabled = false;
spinner.classList.add("d-none");
} }
} catch (error) { } catch (error) {
console.error("Request failed:", error); console.error("Request failed:", error);

View File

@@ -2,46 +2,34 @@ import { Grid } from "gridjs/dist/gridjs.umd.js";
import gridjs from "gridjs/dist/gridjs.umd.js"; import gridjs from "gridjs/dist/gridjs.umd.js";
import "gridjs/dist/gridjs.umd.js"; import "gridjs/dist/gridjs.umd.js";
import GlobalConfig from "../global-config.js"; import GlobalConfig from "../global-config.js";
import Swal from "sweetalert2";
class DataSettings { class DataSettings {
constructor() { constructor() {
this.table = null; // Store Grid.js instance this.toastMessage = document.getElementById("toast-message");
this.toastElement = document.getElementById("toastNotification");
this.toast = new bootstrap.Toast(this.toastElement);
this.table = null;
// Initialize functions
this.initTableDataSettings();
this.initEvents();
} }
init() { initEvents() {
this.getFetchApiData(); document.body.addEventListener("click", async (event) => {
const deleteButton = event.target.closest(
".btn-delete-data-settings"
);
if (deleteButton) {
event.preventDefault();
await this.handleDelete(deleteButton);
}
});
} }
getFetchApiData() { initTableDataSettings() {
let tableContainer = document.getElementById("table-data-settings"); let tableContainer = document.getElementById("table-data-settings");
// Create a new Grid.js instance only if it doesn't exist
if (this.table) {
// If table exists, update its data instead of recreating
this.table
.updateConfig({
server: {
url: `${GlobalConfig.apiHost}/api/api-data-settings`,
credentials: "include",
headers: {
Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`,
"Content-Type": "application/json",
},
then: (data) =>
data.data.map((item) => [
item.id,
item.key,
item.value,
item.created_at,
item.id,
]),
total: (data) => data.meta.total,
},
})
.forceRender();
return;
}
this.table = new Grid({ this.table = new Grid({
columns: [ columns: [
"ID", "ID",
@@ -65,11 +53,6 @@ class DataSettings {
}, },
}, },
], ],
search: {
server: {
url: (prev, keyword) => `${prev}?search=${keyword}`,
},
},
pagination: { pagination: {
limit: 15, limit: 15,
server: { server: {
@@ -80,8 +63,13 @@ class DataSettings {
}, },
}, },
sort: true, sort: true,
search: {
server: {
url: (prev, keyword) => `${prev}?search=${keyword}`,
},
},
server: { server: {
url: `${GlobalConfig.apiHost}/api/api-data-settings`, url: `${GlobalConfig.apiHost}/api/data-settings`,
headers: { headers: {
Authorization: `Bearer ${document Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]') .querySelector('meta[name="api-token"]')
@@ -99,94 +87,26 @@ class DataSettings {
total: (data) => data.meta.total, total: (data) => data.meta.total,
}, },
}).render(tableContainer); }).render(tableContainer);
document.addEventListener("click", this.handleDelete.bind(this));
} }
handleDelete(event) { async handleDelete(deleteButton) {
if (event.target.classList.contains("btn-delete-data-settings")) { const id = deleteButton.getAttribute("data-id");
event.preventDefault();
const id = event.target.getAttribute("data-id");
let modalElement = document.getElementById("modalConfirmation");
let toastMessage = document.getElementById("toast-message");
if (!modalElement) { const result = await Swal.fire({
console.error("Modal element not found!"); title: "Are you sure?",
return; text: "You won't be able to revert this!",
} icon: "warning",
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "Yes, delete it!",
});
let modal = new bootstrap.Modal(modalElement); if (result.isConfirmed) {
let btnSaveConfirmation = document.getElementById( try {
"btnSaveConfirmation" let response = await fetch(
); `${GlobalConfig.apiHost}/api/data-settings/${id}`,
let toastElement = document.getElementById("toastNotification"); {
let toast = new bootstrap.Toast(toastElement); method: "DELETE",
// Remove previous event listeners to avoid multiple bindings
btnSaveConfirmation.replaceWith(
btnSaveConfirmation.cloneNode(true)
);
btnSaveConfirmation = document.getElementById(
"btnSaveConfirmation"
);
// Set the role ID on the confirm button inside the modal
btnSaveConfirmation.setAttribute("data-settings-id", id);
// Show the modal
modal.show();
btnSaveConfirmation.addEventListener("click", async () => {
let dataSettingId =
btnSaveConfirmation.getAttribute("data-settings-id");
try {
let response = await fetch(
`/data-settings/${dataSettingId}`,
{
method: "DELETE",
credentials: "include",
headers: {
"X-CSRF-TOKEN": document
.querySelector('meta[name="csrf-token"]')
.getAttribute("content"),
"Content-Type": "application/json",
},
}
);
if (response.ok) {
let result = await response.json();
toastMessage.innerText =
result.message || "Deleted successfully!";
toast.show();
// Hide modal
modal.hide();
// Refresh Grid.js table
this.refreshDataSettings();
} else {
let error = await response.json();
console.error("Delete failed:", error);
toastMessage.innerText =
error.message || "Delete failed!";
toast.show();
}
} catch (error) {
console.error("Error deleting item:", error);
toastMessage.innerText = "An error occurred!";
toast.show();
}
});
}
}
refreshDataSettings() {
if (this.table) {
this.table
.updateConfig({
server: {
url: `${GlobalConfig.apiHost}/api/api-data-settings`,
credentials: "include", credentials: "include",
headers: { headers: {
Authorization: `Bearer ${document Authorization: `Bearer ${document
@@ -194,21 +114,34 @@ class DataSettings {
.getAttribute("content")}`, .getAttribute("content")}`,
"Content-Type": "application/json", "Content-Type": "application/json",
}, },
then: (data) => }
data.data.map((item) => [ );
item.id,
item.key, if (response.ok) {
item.value, let result = await response.json();
item.created_at, this.toastMessage.innerText =
item.id, result.message || "Deleted successfully!";
]), this.toast.show();
total: (data) => data.meta.total,
}, // Refresh Grid.js table
}) if (typeof this.table !== "undefined") {
.forceRender(); this.table.updateConfig({}).forceRender();
}
} else {
let error = await response.json();
console.error("Delete failed:", error);
this.toastMessage.innerText =
error.message || "Delete failed!";
this.toast.show();
}
} catch (error) {
console.error("Error deleting item:", error);
this.toastMessage.innerText = "An error occurred!";
this.toast.show();
}
} }
} }
} }
document.addEventListener("DOMContentLoaded", function (e) { document.addEventListener("DOMContentLoaded", function (e) {
new DataSettings().init(); new DataSettings();
}); });

View File

@@ -24,16 +24,16 @@ document.addEventListener("DOMContentLoaded", function (e) {
let response = await fetch(form.action, { let response = await fetch(form.action, {
method: "POST", method: "POST",
headers: { headers: {
"X-CSRF-TOKEN": document Authorization: `Bearer ${document
.querySelector('meta[name="csrf-token"]') .querySelector('meta[name="api-token"]')
.getAttribute("content"), .getAttribute("content")}`,
}, },
body: formData, body: formData,
}); });
if (response.ok) { if (response.ok) {
let result = await response.json(); let result = await response.json();
toastMessage.innerText = result.message; toastMessage.innerText = result.data.message;
toast.show(); toast.show();
setTimeout(() => { setTimeout(() => {
window.location.href = "/data-settings"; window.location.href = "/data-settings";
@@ -43,6 +43,8 @@ document.addEventListener("DOMContentLoaded", function (e) {
toastMessage.innerText = error.message; toastMessage.innerText = error.message;
toast.show(); toast.show();
console.error("Error:", error); console.error("Error:", error);
submitButton.disabled = false;
spinner.classList.add("d-none");
} }
} catch (error) { } catch (error) {
console.error("Request failed:", error); console.error("Request failed:", error);

View File

@@ -28,12 +28,7 @@ document.addEventListener("DOMContentLoaded", function () {
data[key] = value; data[key] = value;
}); });
// Log semua data dalam FormData
for (let pair of formData.entries()) {
console.log(pair[0] + ": " + pair[1]);
}
const url = form.getAttribute("action"); const url = form.getAttribute("action");
console.log("Ini adalah url dari form action", url);
const method = isEdit ? "PUT" : "POST"; const method = isEdit ? "PUT" : "POST";
@@ -49,7 +44,6 @@ document.addEventListener("DOMContentLoaded", function () {
}) })
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(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) {
@@ -74,11 +68,11 @@ document.addEventListener("DOMContentLoaded", function () {
toast.classList.add('show'); // Show the toast 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); }, 2000);
setTimeout(() => { setTimeout(() => {
window.location.href = '/data/advertisements'; window.location.href = '/data/advertisements';
}, 3000); }, 1000);
} else { } else {
if (authLogo) { if (authLogo) {
// Hapus ikon yang sudah ada jika ada // Hapus ikon yang sudah ada jika ada
@@ -96,21 +90,21 @@ document.addEventListener("DOMContentLoaded", function () {
// Tambahkan ikon ke dalam auth-logo // Tambahkan ikon ke dalam auth-logo
authLogo.appendChild(icon); authLogo.appendChild(icon);
} }
// Set error message for the toast
toastBody.textContent = "Error: " + (responseData.message || "Something went wrong");
toast.classList.add('show'); // Show the toast
// 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
toastBody.textContent = "Failed: " + (data.message || "Something went wrong");
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 => { .catch(errors => {
console.error("Error:", error);
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');
@@ -127,14 +121,14 @@ document.addEventListener("DOMContentLoaded", function () {
// Tambahkan ikon ke dalam auth-logo // Tambahkan ikon ke dalam auth-logo
authLogo.appendChild(icon); 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 // Set error message for the toast
toastBody.textContent = "An error occurred while processing your request."; toastBody.textContent = "An error occurred while processing your request.";
toast.classList.add('show'); // Show the toast toast.classList.add('show'); // Show the toast
// Enable button and reset its text on error
modalButton.disabled = false;
modalButton.innerHTML = isEdit ? "Update" : "Create";
setTimeout(() => { setTimeout(() => {
toast.classList.remove('show'); // Hide the toast after 3 seconds toast.classList.remove('show'); // Hide the toast after 3 seconds
}, 3000); }, 3000);
@@ -144,7 +138,6 @@ document.addEventListener("DOMContentLoaded", function () {
// Fungsi fetchOptions untuk autocomplete server-side // Fungsi fetchOptions untuk autocomplete server-side
window.fetchOptions = function (field) { window.fetchOptions = function (field) {
let inputValue = document.getElementById(field).value; let inputValue = document.getElementById(field).value;
console.log("Query Value:", inputValue); // Debugging log
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

View File

@@ -0,0 +1,63 @@
import { Grid } from "gridjs/dist/gridjs.umd.js";
import gridjs from "gridjs/dist/gridjs.umd.js";
import "gridjs/dist/gridjs.umd.js";
import GlobalConfig from "../../global-config.js";
import GeneralTable from "../../table-generator.js";
const dataSpatialPlanningColumns = [
"No",
"Nama",
"KBLI",
"Kegiatan",
"Luas",
"Lokasi",
"Nomor",
"Tanggal",
{
name: "Actions",
widht: "120px",
formatter: function (cell, row) {
const id = row.cells[8].data;
const model = "data/spatial-plannings";
return gridjs.html(`
<div class="d-flex justify-items-end gap-10">
<button class="btn btn-warning me-2 btn-edit"
data-id="${id}"
data-model="${model}">
<i class='bx bx-edit'></i></button>
<button class="btn btn-red btn-delete"
data-id="${id}">
<i class='bx bxs-trash'></i></button>
</div>
`);
},
},
];
document.addEventListener("DOMContentLoaded", () => {
const table = new GeneralTable(
"spatial-planning-data-table",
`${GlobalConfig.apiHost}/api/spatial-plannings`,
`${GlobalConfig.apiHost}`,
dataSpatialPlanningColumns
);
table.processData = function (data) {
return data.data.map((item) => {
return [
item.no,
item.name,
item.kbli,
item.activities,
item.area,
item.location,
item.number,
item.date,
item.id,
];
});
};
table.init();
});

View File

@@ -0,0 +1,185 @@
import GlobalConfig from "../../global-config";
document.addEventListener("DOMContentLoaded", function () {
const saveButton = document.querySelector(".modal-footer .btn-primary");
const modalButton = document.querySelector(".btn-modal");
const form = document.querySelector("form#create-update-form");
var authLogo = document.querySelector(".auth-logo");
if (!saveButton || !form) return;
saveButton.addEventListener("click", function () {
// Disable tombol dan tampilkan spinner
modalButton.disabled = true;
modalButton.innerHTML = `
<span class="spinner-border spinner-border-sm me-1" role="status" aria-hidden="true"></span>
Loading...
`;
const isEdit = saveButton.classList.contains("btn-edit");
const formData = new FormData(form);
const toast = document.getElementById("toastEditUpdate");
const toastBody = toast.querySelector(".toast-body");
const toastHeader = toast.querySelector(".toast-header small");
const data = {};
// Mengonversi FormData ke dalam JSON
formData.forEach((value, key) => {
data[key] = value;
});
const url = form.getAttribute("action");
const method = isEdit ? "PUT" : "POST";
fetch(url, {
method: method,
body: JSON.stringify(data),
headers: {
Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`,
"Content-Type": "application/json",
},
})
.then((response) => response.json())
.then((data) => {
if (!data.errors) {
// Remove existing icon (if any) before adding the new one
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-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
}, 3000);
setTimeout(() => {
window.location.href = "/data/spatial-plannings";
}, 3000);
} 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);
}
// Set error message for the toast
toastBody.textContent =
"Error: " + (data.message || "Something went wrong");
toast.classList.add("show"); // Show the toast
// Enable button and reset its text on error
modalButton.disabled = false;
modalButton.innerHTML = isEdit ? "Update" : "Create";
setTimeout(() => {
toast.classList.remove("show"); // Hide the toast after 3 seconds
}, 3000);
}
})
.catch((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);
}
// Set error message for the toast
toastBody.textContent =
"An error occurred while processing your request.";
toast.classList.add("show"); // Show the toast
// Enable button and reset its text on error
modalButton.disabled = false;
modalButton.innerHTML = isEdit ? "Update" : "Create";
setTimeout(() => {
toast.classList.remove("show"); // Hide the toast after 3 seconds
}, 3000);
});
});
// Fungsi fetchOptions untuk autocomplete server-side
window.fetchOptions = function (field) {
let inputValue = document.getElementById(field).value;
if (inputValue.length < 2) return;
let districtValue = document.getElementById("district_name").value; // Ambil kecamatan terpilih
let url = `${
GlobalConfig.apiHost
}/api/combobox/search-options?query=${encodeURIComponent(
inputValue
)}&field=${field}`;
// Jika field desa, tambahkan kecamatan sebagai filter
if (field === "village_name") {
url += `&district=${encodeURIComponent(districtValue)}`;
}
fetch(url, {
method: "GET",
headers: {
Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`,
"Content-Type": "application/json",
},
})
.then((response) => response.json())
.then((data) => {
let dataList = document.getElementById(field + "Options");
dataList.innerHTML = "";
data.forEach((item) => {
let option = document.createElement("option");
option.value = item.name;
option.dataset.code = item.code;
dataList.appendChild(option);
});
})
.catch((error) => console.error("Error fetching options:", error));
};
document.querySelector(".btn-back").addEventListener("click", function () {
window.history.back();
});
});

View File

@@ -0,0 +1,150 @@
import { Dropzone } from "dropzone";
import GlobalConfig from "../../global-config.js";
Dropzone.autoDiscover = false;
var previewTemplate,
dropzone,
dropzonePreviewNode = document.querySelector("#dropzone-preview-list");
console.log(previewTemplate);
console.log(dropzone);
console.log(dropzonePreviewNode);
(dropzonePreviewNode.id = ""),
dropzonePreviewNode &&
((previewTemplate = dropzonePreviewNode.parentNode.innerHTML),
dropzonePreviewNode.parentNode.removeChild(dropzonePreviewNode),
(dropzone = new Dropzone(".dropzone", {
url: `${GlobalConfig.apiHost}/api/spatial-plannings/import`,
// url: "https://httpbin.org/post",
method: "post",
acceptedFiles: ".xls,.xlsx", // Use acceptedFiles for better validation
previewTemplate: previewTemplate,
previewsContainer: "#dropzone-preview",
autoProcessQueue: false, // Disable auto post
headers: {
Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`
},
init: function() {
// Listen for the success event
this.on("success", function(file, response) {
console.log("File successfully uploaded:", file);
console.log("API Response:", response);
// Show success toast
showToast('bxs-check-square', 'green', response.message);
document.getElementById("submit-upload").innerHTML = "Upload Files";
// Tunggu sebentar lalu reload halaman
setTimeout(() => {
window.location.href = "/data/spatial-plannings";
}, 2000);
});
// Listen for the error event
this.on("error", function(file, errorMessage) {
console.error("Error uploading file:", file);
console.error("Error message:", errorMessage);
// Handle the error response
// Show error toast
showToast('bxs-error-alt', 'red', errorMessage.message);
document.getElementById("submit-upload").innerHTML = "Upload Files";
});
}
})));
// Add event listener to control the submission manually
document.querySelector("#submit-upload").addEventListener("click", function() {
console.log("Ini adalah value dropzone", dropzone.files[0]);
const formData = new FormData()
console.log("Dropzonefiles",dropzone.files);
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
if (dropzone.files.length > 0) {
formData.append('file', dropzone.files[0])
console.log("ini adalah form data on submit", ...formData);
dropzone.processQueue(); // Ini akan manual memicu upload
} else {
// Show error toast when no file is selected
showToast('bxs-error-alt', 'red', "Please add a file first.");
document.getElementById("submit-upload").innerHTML = "Upload Files";
}
});
// Optional: Listen for the 'addedfile' event to log or control file add behavior
dropzone.on("addedfile", function (file) {
console.log("File ditambahkan:", file);
console.log("Nama File:", file.name);
console.log("Tipe File:", file.type);
console.log("Ukuran File:", (file.size / 1024).toFixed(2) + " KB");
});
dropzone.on("complete", function(file) {
dropzone.removeFile(file);
});
// Add event listener to download file template
document.getElementById('downloadtempspatialPlannings').addEventListener('click', function() {
var url = `${GlobalConfig.apiHost}/api/download-template-spatialPlannings`;
fetch(url, {
method: 'GET',
headers: {
Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`
},
})
.then(response => {
if (response.ok) {
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);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = 'template_rencana_tata_ruang.xlsx';
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
})
.catch((error) => {
console.error("Gagal mendownload file:", error);
showToast('bxs-error-alt', 'red', "Template file is not already exist.");
})
})
// Function to show toast
function showToast(iconClass, iconColor, message) {
const toastElement = document.getElementById('toastUploadSpatialPlannings');
const toastBody = toastElement.querySelector('.toast-body');
const toastHeader = toastElement.querySelector('.toast-header');
// Remove existing icon (if any) before adding the new one
const existingIcon = toastHeader.querySelector('.bx');
if (existingIcon) {
toastHeader.querySelector('.auth-logo').removeChild(existingIcon); // Remove the existing icon
}
// Add the new icon to the toast header
const icon = document.createElement('i');
icon.classList.add('bx', iconClass);
icon.style.fontSize = '25px';
icon.style.color = iconColor;
toastHeader.querySelector('.auth-logo').appendChild(icon);
// Set the toast message
toastBody.textContent = message;
// Show the toast
const toast = new bootstrap.Toast(toastElement); // Inisialisasi Bootstrap Toast
toast.show();
}

View File

@@ -5,37 +5,39 @@ import GlobalConfig from "../../global-config.js";
import GeneralTable from "../../table-generator.js"; import GeneralTable from "../../table-generator.js";
const dataTourismsColumns = [ const dataTourismsColumns = [
"Proyek ID", "No",
"Jenis Proyek",
"NIB",
"Nama Perusahaan", "Nama Perusahaan",
"Terbit OSS",
"Status Penanaman Modal",
"Bentuk Bisnis",
"Uraian Resiko Proyek",
"Nama Proyek", "Nama Proyek",
"Alamat Usaha", "Alamat Usaha",
"Kecamatan", "Kecamatan",
"Desa", "Desa",
"Luas Tanah", "Luas Tanah (m2)",
"Jumlah Investasi", "Jumlah Investasi",
"TKI", "TKI",
"Tipe Usaha", "Longitude",
"Latitude",
{ {
name: "Actions", name: "Actions",
widht: "120px", widht: "120px",
formatter: function (cell, row) { formatter: function (cell, row) {
const id = row.cells[16].data; const id = row.cells[11].data;
const long = row.cells[9].data;
const lat = row.cells[10].data;
const model = "data/tourisms"; const model = "data/tourisms";
return gridjs.html(` return gridjs.html(`
<div class="d-flex justify-items-end gap-10"> <div class="d-flex justify-items-end gap-10">
<button class="btn btn-warning me-2 btn-edit" <button class="btn btn-warning me-2 btn-edit"
data-id="${id}" data-id="${id}"
data-model="${model}"> data-model="${model}">
<i class='bx bx-edit' ></i></button> <i class='bx bx-edit'></i></button>
<button class="btn btn-info me-2 btn-view"
data-long="${long}" data-lat="${lat}">
<i class='bx bx-map'></i></button>
<button class="btn btn-red btn-delete" <button class="btn btn-red btn-delete"
data-id="${id}"> data-id="${id}">
<i class='bx bxs-trash' ></i></button> <i class='bx bxs-trash'></i></button>
</div> </div>
`); `);
}, },
@@ -53,26 +55,43 @@ document.addEventListener("DOMContentLoaded", () => {
table.processData = function (data) { table.processData = function (data) {
return data.data.map((item) => { return data.data.map((item) => {
return [ return [
item.project_id, item.no,
item.jenis_proyek,
item.nib,
item.business_name, item.business_name,
item.terbit_oss,
item.status_penanaman_modal,
item.business_form,
item.uraian_resiko_proyek,
item.project_name, item.project_name,
item.business_address, item.business_address,
item.district_name, item.district_name,
item.village_name, item.village_name,
item.land_area, item.land_area_in_m2,
item.investment_amount, item.investment_amount,
item.number_of_employee, item.tki,
item.business_type, item.longitude,
item.latitude,
item.id, item.id,
]; ];
}); });
}; };
table.init(); table.init();
// Event listener untuk tombol "View" yang memunculkan modal
document.addEventListener("click", function (e) {
if (e.target && e.target.classList.contains("btn-view")) {
const long = e.target.getAttribute("data-long");
const lat = e.target.getAttribute("data-lat");
// Menyiapkan URL iframe dengan koordinat yang didapatkan
const iframeSrc = `https://www.google.com/maps?q=${lat},${long}&hl=es;z=14&output=embed`;
// Menemukan modal dan iframe di dalam modal
const modal = document.querySelector(".modalGMaps");
const iframe = modal.querySelector("iframe");
// Set src iframe untuk menampilkan peta dengan koordinat yang relevan
iframe.src = iframeSrc;
// Menampilkan modal
var modalInstance = new bootstrap.Modal(modal);
modalInstance.show();
}
});
}); });

View File

@@ -28,12 +28,7 @@ document.addEventListener("DOMContentLoaded", function () {
data[key] = value; data[key] = value;
}); });
// Log semua data dalam FormData
for (let pair of formData.entries()) {
console.log(pair[0] + ": " + pair[1]);
}
const url = form.getAttribute("action"); const url = form.getAttribute("action");
console.log("Ini adalah url dari form action", url);
const method = isEdit ? "PUT" : "POST"; const method = isEdit ? "PUT" : "POST";
@@ -49,7 +44,6 @@ document.addEventListener("DOMContentLoaded", function () {
}) })
.then((response) => response.json()) .then((response) => response.json())
.then((data) => { .then((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) {
@@ -113,7 +107,6 @@ document.addEventListener("DOMContentLoaded", function () {
} }
}) })
.catch((error) => { .catch((error) => {
console.error("Error:", error);
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");
@@ -148,7 +141,6 @@ document.addEventListener("DOMContentLoaded", function () {
// Fungsi fetchOptions untuk autocomplete server-side // Fungsi fetchOptions untuk autocomplete server-side
window.fetchOptions = function (field) { window.fetchOptions = function (field) {
let inputValue = document.getElementById(field).value; let inputValue = document.getElementById(field).value;
console.log("Query Value:", inputValue); // Debugging log
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

View File

@@ -88,7 +88,7 @@ dropzone.on("complete", function(file) {
// Add event listener to download file template // Add event listener to download file template
document.getElementById('downloadtemptourisms').addEventListener('click', function() { document.getElementById('downloadtemptourisms').addEventListener('click', function() {
var url = `${GlobalConfig.apiHost}/api/download-template-umkm`; var url = `${GlobalConfig.apiHost}/api/download-template-tourism`;
fetch(url, { fetch(url, {
method: 'GET', method: 'GET',
headers: { headers: {
@@ -105,11 +105,12 @@ document.getElementById('downloadtemptourisms').addEventListener('click', functi
} }
}) })
.then((blob) => { .then((blob) => {
console.log(blob);
const url = window.URL.createObjectURL(blob); const url = window.URL.createObjectURL(blob);
const a = document.createElement('a'); const a = document.createElement('a');
a.style.display = 'none'; a.style.display = 'none';
a.href = url; a.href = url;
a.download = 'template_tourisms.xlsx'; a.download = 'template_pariwisata.xlsx';
document.body.appendChild(a); document.body.appendChild(a);
a.click(); a.click();
window.URL.revokeObjectURL(url); window.URL.revokeObjectURL(url);
@@ -122,7 +123,7 @@ document.getElementById('downloadtemptourisms').addEventListener('click', functi
// 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('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');

View File

@@ -4,6 +4,7 @@ import GlobalConfig from "../../global-config.js";
import GeneralTable from "../../table-generator.js"; import GeneralTable from "../../table-generator.js";
const dataUMKMColumns = [ const dataUMKMColumns = [
"No",
"Nama Usaha", "Nama Usaha",
"Alamat Usaha", "Alamat Usaha",
"Deskripsi Usaha", "Deskripsi Usaha",
@@ -26,7 +27,7 @@ const dataUMKMColumns = [
name: "Actions", name: "Actions",
widht: "120px", widht: "120px",
formatter: function(cell, row) { formatter: function(cell, row) {
const id = row.cells[18].data; const id = row.cells[19].data;
const model = "data/umkm"; const model = "data/umkm";
return gridjs.html(` return gridjs.html(`
<div class="d-flex justify-items-end gap-10"> <div class="d-flex justify-items-end gap-10">
@@ -54,6 +55,7 @@ document.addEventListener("DOMContentLoaded", () => {
table.processData = function (data) { table.processData = function (data) {
return data.data.map((item) => { return data.data.map((item) => {
return [ return [
item.no,
item.business_name, item.business_name,
item.business_address, item.business_address,
item.business_desc, item.business_desc,

View File

@@ -28,12 +28,7 @@ document.addEventListener("DOMContentLoaded", function () {
data[key] = value; data[key] = value;
}); });
// Log semua data dalam FormData
for (let pair of formData.entries()) {
console.log(pair[0] + ": " + pair[1]);
}
const url = form.getAttribute("action"); const url = form.getAttribute("action");
console.log("Ini adalah url dari form action", url);
const method = isEdit ? "PUT" : "POST"; const method = isEdit ? "PUT" : "POST";
@@ -74,11 +69,11 @@ document.addEventListener("DOMContentLoaded", function () {
toast.classList.add('show'); // Show the toast 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); }, 2000);
setTimeout(() => { setTimeout(() => {
window.location.href = '/data/umkm'; window.location.href = '/data/umkm';
}, 3000); }, 1000);
} else { } else {
if (authLogo) { if (authLogo) {
// Hapus ikon yang sudah ada jika ada // Hapus ikon yang sudah ada jika ada
@@ -96,14 +91,16 @@ document.addEventListener("DOMContentLoaded", function () {
// Tambahkan ikon ke dalam auth-logo // Tambahkan ikon ke dalam auth-logo
authLogo.appendChild(icon); authLogo.appendChild(icon);
} }
// Set error message for the toast
toastBody.textContent = "Error: " + (data.message || "Something went wrong");
toast.classList.add('show'); // Show the toast
// 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
toastBody.textContent = "Error: " + (data.message || "Something went wrong");
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);
@@ -127,14 +124,15 @@ document.addEventListener("DOMContentLoaded", function () {
// Tambahkan ikon ke dalam auth-logo // Tambahkan ikon ke dalam auth-logo
authLogo.appendChild(icon); authLogo.appendChild(icon);
} }
// Set error message for the toast
toastBody.textContent = "An error occurred while processing your request.";
toast.classList.add('show'); // Show the toast
// 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
toastBody.textContent = "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);
@@ -144,7 +142,6 @@ document.addEventListener("DOMContentLoaded", function () {
// Fungsi fetchOptions untuk autocomplete server-side // Fungsi fetchOptions untuk autocomplete server-side
window.fetchOptions = function (field) { window.fetchOptions = function (field) {
let inputValue = document.getElementById(field).value; let inputValue = document.getElementById(field).value;
console.log("Query Value:", inputValue); // Debugging log
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

View File

@@ -19,15 +19,24 @@ document.addEventListener("DOMContentLoaded", function (e) {
submitButton.disabled = true; submitButton.disabled = true;
spinner.classList.remove("d-none"); spinner.classList.remove("d-none");
let jsonData = {};
formData.forEach((value, key) => {
jsonData[key] = value;
});
console.log(jsonData);
try { try {
let response = await fetch(form.action, { let response = await fetch(form.action, {
method: "POST", method: "POST",
headers: { headers: {
"X-CSRF-TOKEN": document "Content-Type": "application/json",
.querySelector('meta[name="csrf-token"]') Accept: "application/json",
.getAttribute("content"), Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`,
}, },
body: formData, body: JSON.stringify(jsonData),
}); });
if (response.ok) { if (response.ok) {

View File

@@ -1,55 +1,67 @@
document.addEventListener("DOMContentLoaded", function (e) { class CreateMenu {
const toastNotification = document.getElementById("toastNotification"); constructor() {
const toast = new bootstrap.Toast(toastNotification); this.initCreateMenu();
document }
.getElementById("btnCreateMenus")
.addEventListener("click", async function () {
let submitButton = this;
let spinner = document.getElementById("spinner");
let form = document.getElementById("formCreateMenus");
if (!form) { initCreateMenu() {
console.error("Form element not found!"); const toastNotification = document.getElementById("toastNotification");
return; const toast = new bootstrap.Toast(toastNotification);
} document
// Get form data .getElementById("btnCreateMenus")
let formData = new FormData(form); .addEventListener("click", async function () {
let submitButton = this;
let spinner = document.getElementById("spinner");
let form = document.getElementById("formCreateMenus");
// Disable button and show spinner if (!form) {
submitButton.disabled = true; console.error("Form element not found!");
spinner.classList.remove("d-none"); return;
}
// Get form data
let formData = new FormData(form);
try { // Disable button and show spinner
let response = await fetch(form.action, { submitButton.disabled = true;
method: "POST", spinner.classList.remove("d-none");
headers: {
"X-CSRF-TOKEN": document
.querySelector('meta[name="csrf-token"]')
.getAttribute("content"),
},
body: formData,
});
if (response.ok) { try {
let result = await response.json(); let response = await fetch(form.action, {
document.getElementById("toast-message").innerText = method: "POST",
result.message; headers: {
toast.show(); Authorization: `Bearer ${document
setTimeout(() => { .querySelector('meta[name="api-token"]')
window.location.href = "/menus"; .getAttribute("content")}`,
}, 2000); },
} else { body: formData,
let error = await response.json(); });
if (response.ok) {
let result = await response.json();
document.getElementById("toast-message").innerText =
result.message;
toast.show();
setTimeout(() => {
window.location.href = "/menus";
}, 2000);
} else {
let error = await response.json();
document.getElementById("toast-message").innerText =
error.message;
toast.show();
console.error("Error:", error);
submitButton.disabled = false;
spinner.classList.add("d-none");
}
} catch (error) {
console.error("Request failed:", error);
document.getElementById("toast-message").innerText = document.getElementById("toast-message").innerText =
error.message; error.message;
toast.show(); toast.show();
console.error("Error:", error);
} }
} catch (error) { });
console.error("Request failed:", error); }
document.getElementById("toast-message").innerText = }
error.message;
toast.show(); document.addEventListener("DOMContentLoaded", function (e) {
} new CreateMenu();
});
}); });

View File

@@ -2,13 +2,27 @@ import { Grid } from "gridjs/dist/gridjs.umd.js";
import gridjs from "gridjs/dist/gridjs.umd.js"; import gridjs from "gridjs/dist/gridjs.umd.js";
import "gridjs/dist/gridjs.umd.js"; import "gridjs/dist/gridjs.umd.js";
import GlobalConfig from "../global-config"; import GlobalConfig from "../global-config";
import Swal from "sweetalert2";
class Menus { class Menus {
constructor() { constructor() {
this.toastMessage = document.getElementById("toast-message");
this.toastElement = document.getElementById("toastNotification");
this.toast = new bootstrap.Toast(this.toastElement);
this.table = null; this.table = null;
}
init() { // Initialize functions
this.initTableMenus(); this.initTableMenus();
this.initEvents();
}
initEvents() {
document.body.addEventListener("click", async (event) => {
const deleteButton = event.target.closest(".btn-delete-menu");
if (deleteButton) {
event.preventDefault();
await this.handleDelete(deleteButton);
}
});
} }
initTableMenus() { initTableMenus() {
@@ -19,7 +33,7 @@ class Menus {
this.table this.table
.updateConfig({ .updateConfig({
server: { server: {
url: `${GlobalConfig.apiHost}/api/api-menus`, url: `${GlobalConfig.apiHost}/api/menus`,
credentials: "include", credentials: "include",
headers: { headers: {
Authorization: `Bearer ${document Authorization: `Bearer ${document
@@ -60,7 +74,8 @@ class Menus {
<a href="/menus/${cell}/edit" class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center"> <a href="/menus/${cell}/edit" class="btn btn-yellow btn-sm d-inline-flex align-items-center justify-content-center">
<i class='bx bx-edit'></i> <i class='bx bx-edit'></i>
</a> </a>
<button data-id="${cell}" class="btn btn-red btn-sm btn-delete-menu d-inline-flex align-items-center justify-content-center"> <button data-id="${cell}"
class="btn btn-red btn-sm btn-delete-menu d-inline-flex align-items-center justify-content-center">
<i class='bx bxs-trash' ></i> <i class='bx bxs-trash' ></i>
</button> </button>
</div> </div>
@@ -83,7 +98,7 @@ class Menus {
}, },
}, },
server: { server: {
url: `${GlobalConfig.apiHost}/api/api-menus`, url: `${GlobalConfig.apiHost}/api/menus`,
credentials: "include", credentials: "include",
headers: { headers: {
Authorization: `Bearer ${document Authorization: `Bearer ${document
@@ -104,118 +119,63 @@ class Menus {
total: (data) => data.total, total: (data) => data.total,
}, },
}).render(tableContainer); }).render(tableContainer);
document.addEventListener("click", this.handleDelete.bind(this));
} }
handleDelete(event) { async handleDelete(button) {
if (event.target.classList.contains("btn-delete-menu")) { const id = button.getAttribute("data-id");
event.preventDefault();
const id = event.target.getAttribute("data-id");
let modalElement = document.getElementById("modalConfirmation");
let toastMessage = document.getElementById("toast-message");
if (!modalElement) { const result = await Swal.fire({
console.error("Modal element not found!"); title: "Are you sure?",
return; text: "You won't be able to revert this!",
} icon: "warning",
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "Yes, delete it!",
});
let modal = new bootstrap.Modal(modalElement); if (result.isConfirmed) {
let btnSaveConfirmation = document.getElementById( try {
"btnSaveConfirmation" let response = await fetch(
); `${GlobalConfig.apiHost}/api/menus/${id}`,
let toastElement = document.getElementById("toastNotification"); {
let toast = new bootstrap.Toast(toastElement);
// Remove previous event listeners to avoid multiple bindings
btnSaveConfirmation.replaceWith(
btnSaveConfirmation.cloneNode(true)
);
btnSaveConfirmation = document.getElementById(
"btnSaveConfirmation"
);
// Set the role ID on the confirm button inside the modal
btnSaveConfirmation.setAttribute("data-menu-id", id);
// Show the modal
modal.show();
btnSaveConfirmation.addEventListener("click", async () => {
let menuId = btnSaveConfirmation.getAttribute("data-menu-id");
try {
let response = await fetch(`/menus/${menuId}`, {
method: "DELETE", method: "DELETE",
credentials: "include", credentials: "include",
headers: {
"X-CSRF-TOKEN": document
.querySelector('meta[name="csrf-token"]')
.getAttribute("content"),
"Content-Type": "application/json",
},
});
if (response.ok) {
let result = await response.json();
toastMessage.innerText =
result.message || "Deleted successfully!";
toast.show();
// Hide modal
modal.hide();
// Refresh Grid.js table
this.refreshTableMenus();
} else {
let error = await response.json();
console.error("Delete failed:", error);
toastMessage.innerText =
error.message || "Delete failed!";
toast.show();
}
} catch (error) {
console.error("Error deleting item:", error);
toastMessage.innerText = "An error occurred!";
toast.show();
}
});
}
}
refreshTableMenus() {
if (this.table) {
this.table
.updateConfig({
server: {
url: `${GlobalConfig.apiHost}/api/api-menus`,
credentials: "include",
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: (data) => }
data.data.map((item) => [ );
item.id,
item.name, if (response.ok) {
item.url, let result = await response.json();
item.icon, this.toastMessage.innerText =
item.parent_id, result.message || "Deleted successfully!";
item.sort_order, this.toast.show();
item.id,
]), // Refresh Grid.js table
total: (data) => data.total, if (typeof this.table !== "undefined") {
}, this.table.updateConfig({}).forceRender();
}) }
.forceRender(); } else {
} else { let error = await response.json();
this.initTableMenus(); // If no table exists, reinitialize it console.error("Delete failed:", error);
this.toastMessage.innerText =
error.message || "Delete failed!";
this.toast.show();
}
} catch (error) {
console.error("Error deleting item:", error);
this.toastMessage.innerText = "An error occurred!";
this.toast.show();
}
} }
} }
} }
document.addEventListener("DOMContentLoaded", function (e) { document.addEventListener("DOMContentLoaded", function (e) {
new Menus().init(); new Menus();
}); });

View File

@@ -1,53 +1,67 @@
document.addEventListener("DOMContentLoaded", function (e) { class UpdateMenu {
let form = document.getElementById("formUpdateMenus"); constructor() {
let submitButton = document.getElementById("btnUpdateMenus"); this.initUpdateMenu();
let spinner = document.getElementById("spinner"); }
let toastMessage = document.getElementById("toast-message");
let toast = new bootstrap.Toast(
document.getElementById("toastNotification")
);
submitButton.addEventListener("click", async function () {
let submitButton = this;
if (!form) { initUpdateMenu() {
console.error("Form element not found!"); const toastNotification = document.getElementById("toastNotification");
return; const toast = new bootstrap.Toast(toastNotification);
} document
// Get form data .getElementById("btnUpdateMenus")
let formData = new FormData(form); .addEventListener("click", async function () {
let submitButton = this;
let spinner = document.getElementById("spinner");
let form = document.getElementById("formUpdateMenus");
// Disable button and show spinner if (!form) {
submitButton.disabled = true; console.error("Form element not found!");
spinner.classList.remove("d-none"); return;
}
// Get form data
let formData = new FormData(form);
try { // Disable button and show spinner
let response = await fetch(form.action, { submitButton.disabled = true;
method: "POST", spinner.classList.remove("d-none");
headers: {
"X-CSRF-TOKEN": document try {
.querySelector('meta[name="csrf-token"]') let response = await fetch(form.action, {
.getAttribute("content"), method: "POST",
}, headers: {
body: formData, Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`,
},
body: formData,
});
if (response.ok) {
let result = await response.json();
document.getElementById("toast-message").innerText =
result.message;
toast.show();
setTimeout(() => {
window.location.href = "/menus";
}, 2000);
} else {
let error = await response.json();
document.getElementById("toast-message").innerText =
error.message;
toast.show();
console.error("Error:", error);
submitButton.disabled = false;
spinner.classList.add("d-none");
}
} catch (error) {
console.error("Request failed:", error);
document.getElementById("toast-message").innerText =
error.message;
toast.show();
}
}); });
}
}
if (response.ok) { document.addEventListener("DOMContentLoaded", function (e) {
let result = await response.json(); new UpdateMenu();
toastMessage.innerText = result.message;
toast.show();
setTimeout(() => {
window.location.href = "/menus";
}, 2000);
} else {
let error = await response.json();
toastMessage.innerText = error.message;
toast.show();
console.error("Error:", error);
}
} catch (error) {
console.error("Request failed:", error);
toastMessage.innerText = error.message;
toast.show();
}
});
}); });

View File

@@ -2,14 +2,14 @@ import gridjs from "gridjs/dist/gridjs.umd.js";
import "gridjs/dist/gridjs.umd.js"; import "gridjs/dist/gridjs.umd.js";
// Mengambil data dari input dengan id="business_type_counts" // Mengambil data dari input dengan id="business_type_counts"
const businessTypeCountsElement = document.getElementById("business_type_counts"); const businessTypeCountsElement = document.getElementById("tourism_based_KBLI");
console.log(businessTypeCountsElement); console.log(businessTypeCountsElement);
const businessTypeCounts = JSON.parse(businessTypeCountsElement.value); // Cek apakah data sudah terbawa dengan benar const businessTypeCounts = JSON.parse(businessTypeCountsElement.value); // Cek apakah data sudah terbawa dengan benar
// Membuat Grid.js instance // Membuat Grid.js instance
new gridjs.Grid({ new gridjs.Grid({
columns: ["Jenis Bisnis Pariwisata", "Jumlah Total"], // Nama kolom columns: ["Jenis Bisnis Pariwisata", "Jumlah Total"], // Nama kolom
data: businessTypeCounts.map(item => [item.business_type, item.count]), // Mengubah data untuk Grid.js data: businessTypeCounts.map(item => [item.kbli_title, item.total_records]), // Mengubah data untuk Grid.js
search: true, // Menambahkan fitur pencarian search: true, // Menambahkan fitur pencarian
pagination: true, // Menambahkan fitur pagination pagination: true, // Menambahkan fitur pagination
sort: true, // Menambahkan fitur sorting sort: true, // Menambahkan fitur sorting

View File

@@ -1,55 +1,67 @@
document.addEventListener("DOMContentLoaded", function (e) { class CreateRoles {
const toastNotification = document.getElementById("toastNotification"); constructor() {
const toast = new bootstrap.Toast(toastNotification); this.initCreateRole();
document }
.getElementById("btnCreateRole")
.addEventListener("click", async function () {
let submitButton = this;
let spinner = document.getElementById("spinner");
let form = document.getElementById("formCreateRole");
if (!form) { initCreateRole() {
console.error("Form element not found!"); const toastNotification = document.getElementById("toastNotification");
return; const toast = new bootstrap.Toast(toastNotification);
} document
// Get form data .getElementById("btnCreateRole")
let formData = new FormData(form); .addEventListener("click", async function () {
let submitButton = this;
let spinner = document.getElementById("spinner");
let form = document.getElementById("formCreateRole");
// Disable button and show spinner if (!form) {
submitButton.disabled = true; console.error("Form element not found!");
spinner.classList.remove("d-none"); return;
}
// Get form data
let formData = new FormData(form);
try { // Disable button and show spinner
let response = await fetch(form.action, { submitButton.disabled = true;
method: "POST", spinner.classList.remove("d-none");
headers: {
"X-CSRF-TOKEN": document
.querySelector('meta[name="csrf-token"]')
.getAttribute("content"),
},
body: formData,
});
if (response.ok) { try {
let result = await response.json(); let response = await fetch(form.action, {
document.getElementById("toast-message").innerText = method: "POST",
result.message; headers: {
toast.show(); Authorization: `Bearer ${document
setTimeout(() => { .querySelector('meta[name="api-token"]')
window.location.href = "/roles"; .getAttribute("content")}`,
}, 2000); },
} else { body: formData,
let error = await response.json(); });
if (response.ok) {
let result = await response.json();
document.getElementById("toast-message").innerText =
result.message;
toast.show();
setTimeout(() => {
window.location.href = "/roles";
}, 2000);
} else {
let error = await response.json();
document.getElementById("toast-message").innerText =
error.message;
toast.show();
console.error("Error:", error);
submitButton.disabled = false;
spinner.classList.add("d-none");
}
} catch (error) {
console.error("Request failed:", error);
document.getElementById("toast-message").innerText = document.getElementById("toast-message").innerText =
error.message; error.message;
toast.show(); toast.show();
console.error("Error:", error);
} }
} catch (error) { });
console.error("Request failed:", error); }
document.getElementById("toast-message").innerText = }
error.message;
toast.show(); document.addEventListener("DOMContentLoaded", function (e) {
} new CreateRoles();
});
}); });

View File

@@ -2,46 +2,31 @@ import { Grid } from "gridjs/dist/gridjs.umd.js";
import gridjs from "gridjs/dist/gridjs.umd.js"; import gridjs from "gridjs/dist/gridjs.umd.js";
import "gridjs/dist/gridjs.umd.js"; import "gridjs/dist/gridjs.umd.js";
import GlobalConfig from "../global-config"; import GlobalConfig from "../global-config";
import Swal from "sweetalert2";
class Roles { class Roles {
constructor() { constructor() {
this.table = null; // Store Grid.js instance this.toastMessage = document.getElementById("toast-message");
} this.toastElement = document.getElementById("toastNotification");
this.toast = new bootstrap.Toast(this.toastElement);
this.table = null;
init() { // Initialize functions
this.initTableRoles(); this.initTableRoles();
this.initEvents();
}
initEvents() {
document.body.addEventListener("click", async (event) => {
const deleteButton = event.target.closest(".btn-delete-role");
if (deleteButton) {
event.preventDefault();
await this.handleDelete(deleteButton);
}
});
} }
initTableRoles() { initTableRoles() {
let tableContainer = document.getElementById("table-roles"); let tableContainer = document.getElementById("table-roles");
// If table instance already exists, update it instead of re-creating
if (this.table) {
this.table
.updateConfig({
server: {
url: `${GlobalConfig.apiHost}/api/api-roles`,
credentials: "include",
headers: {
Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`,
"Content-Type": "application/json",
},
then: (data) =>
data.data.map((item) => [
item.id,
item.name,
item.description,
item.id,
]),
total: (data) => data.total,
},
})
.forceRender();
return;
}
// Create a new Grid.js instance only if it doesn't exist // Create a new Grid.js instance only if it doesn't exist
this.table = new gridjs.Grid({ this.table = new gridjs.Grid({
columns: [ columns: [
@@ -82,7 +67,7 @@ class Roles {
}, },
}, },
server: { server: {
url: `${GlobalConfig.apiHost}/api/api-roles`, url: `${GlobalConfig.apiHost}/api/roles`,
credentials: "include", credentials: "include",
headers: { headers: {
Authorization: `Bearer ${document Authorization: `Bearer ${document
@@ -100,116 +85,63 @@ class Roles {
total: (data) => data.total, total: (data) => data.total,
}, },
}).render(tableContainer); }).render(tableContainer);
document.addEventListener("click", this.handleDelete.bind(this));
} }
handleDelete(event) { async handleDelete(deleteButton) {
if (event.target.classList.contains("btn-delete-role")) { const id = deleteButton.getAttribute("data-id");
event.preventDefault();
const id = event.target.getAttribute("data-id"); const result = await Swal.fire({
let modalElement = document.getElementById("modalConfirmation"); title: "Are you sure?",
let toastMessage = document.getElementById("toast-message"); text: "You won't be able to revert this!",
icon: "warning",
showCancelButton: true,
confirmButtonColor: "#3085d6",
cancelButtonColor: "#d33",
confirmButtonText: "Yes, delete it!",
});
if (!modalElement) { if (result.isConfirmed) {
console.error("Modal element not found!"); try {
return; let response = await fetch(
} `${GlobalConfig.apiHost}/api/roles/${id}`,
{
let modal = new bootstrap.Modal(modalElement);
let btnSaveConfirmation = document.getElementById(
"btnSaveConfirmation"
);
let toastElement = document.getElementById("toastNotification");
let toast = new bootstrap.Toast(toastElement);
// Remove previous event listeners to avoid multiple bindings
btnSaveConfirmation.replaceWith(
btnSaveConfirmation.cloneNode(true)
);
btnSaveConfirmation = document.getElementById(
"btnSaveConfirmation"
);
// Set the role ID on the confirm button inside the modal
btnSaveConfirmation.setAttribute("data-role-id", id);
// Show the modal
modal.show();
btnSaveConfirmation.addEventListener("click", async () => {
let roleId = btnSaveConfirmation.getAttribute("data-role-id");
try {
let response = await fetch(`/roles/${roleId}`, {
method: "DELETE", method: "DELETE",
credentials: "include", credentials: "include",
headers: {
"X-CSRF-TOKEN": document
.querySelector('meta[name="csrf-token"]')
.getAttribute("content"),
"Content-Type": "application/json",
},
});
if (response.ok) {
let result = await response.json();
toastMessage.innerText =
result.message || "Deleted successfully!";
toast.show();
// Hide modal
modal.hide();
// Refresh Grid.js table
this.refreshRolesTable();
} else {
let error = await response.json();
console.error("Delete failed:", error);
toastMessage.innerText =
error.message || "Delete failed!";
toast.show();
}
} catch (error) {
console.error("Error deleting item:", error);
toastMessage.innerText = "An error occurred!";
toast.show();
}
});
}
}
refreshRolesTable() {
if (this.table) {
this.table
.updateConfig({
server: {
url: `${GlobalConfig.apiHost}/api/api-roles`,
credentials: "include",
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: (data) => }
data.data.map((item) => [ );
item.id,
item.name, if (response.ok) {
item.description, let result = await response.json();
item.id, this.toastMessage.innerText =
]), result.message || "Deleted successfully!";
total: (data) => data.total, this.toast.show();
},
}) // Refresh Grid.js table
.forceRender(); if (typeof this.table !== "undefined") {
} else { this.table.updateConfig({}).forceRender();
this.initTableRoles(); // If the table is null, reinitialize }
} else {
let error = await response.json();
console.error("Delete failed:", error);
this.toastMessage.innerText =
error.message || "Delete failed!";
this.toast.show();
}
} catch (error) {
console.error("Error deleting item:", error);
this.toastMessage.innerText = "An error occurred!";
this.toast.show();
}
} }
} }
} }
document.addEventListener("DOMContentLoaded", function (e) { document.addEventListener("DOMContentLoaded", function (e) {
new Roles().init(); new Roles();
}); });

View File

@@ -1,53 +1,67 @@
document.addEventListener("DOMContentLoaded", function (e) { class UpdateRoles {
let form = document.getElementById("formUpdateRole"); constructor() {
let submitButton = document.getElementById("btnUpdateRole"); this.initUpdateRole();
let spinner = document.getElementById("spinner"); }
let toastMessage = document.getElementById("toast-message");
let toast = new bootstrap.Toast(
document.getElementById("toastNotification")
);
submitButton.addEventListener("click", async function () {
let submitButton = this;
if (!form) { initUpdateRole() {
console.error("Form element not found!"); const toastNotification = document.getElementById("toastNotification");
return; const toast = new bootstrap.Toast(toastNotification);
} document
// Get form data .getElementById("btnUpdateRole")
let formData = new FormData(form); .addEventListener("click", async function () {
let submitButton = this;
let spinner = document.getElementById("spinner");
let form = document.getElementById("formUpdateRole");
// Disable button and show spinner if (!form) {
submitButton.disabled = true; console.error("Form element not found!");
spinner.classList.remove("d-none"); return;
}
// Get form data
let formData = new FormData(form);
try { // Disable button and show spinner
let response = await fetch(form.action, { submitButton.disabled = true;
method: "POST", spinner.classList.remove("d-none");
headers: {
"X-CSRF-TOKEN": document try {
.querySelector('meta[name="csrf-token"]') let response = await fetch(form.action, {
.getAttribute("content"), method: "POST",
}, headers: {
body: formData, Authorization: `Bearer ${document
.querySelector('meta[name="api-token"]')
.getAttribute("content")}`,
},
body: formData,
});
if (response.ok) {
let result = await response.json();
document.getElementById("toast-message").innerText =
result.message;
toast.show();
setTimeout(() => {
window.location.href = "/roles";
}, 2000);
} else {
let error = await response.json();
document.getElementById("toast-message").innerText =
error.message;
toast.show();
console.error("Error:", error);
submitButton.disabled = false;
spinner.classList.add("d-none");
}
} catch (error) {
console.error("Request failed:", error);
document.getElementById("toast-message").innerText =
error.message;
toast.show();
}
}); });
}
}
if (response.ok) { document.addEventListener("DOMContentLoaded", function (e) {
let result = await response.json(); new UpdateRoles();
toastMessage.innerText = result.message;
toast.show();
setTimeout(() => {
window.location.href = "/roles";
}, 2000);
} else {
let error = await response.json();
toastMessage.innerText = error.message;
toast.show();
console.error("Error:", error);
}
} catch (error) {
console.error("Request failed:", error);
toastMessage.innerText = error.message;
toast.show();
}
});
}); });

View File

@@ -0,0 +1,34 @@
import flatpickr from "flatpickr";
import "flatpickr/dist/flatpickr.min.css";
class InitDatePicker {
constructor(selector = ".datepicker", onChangeCallback = null) {
this.selector = selector;
this.onChangeCallback = onChangeCallback;
}
init() {
const today = new Date();
document.querySelectorAll(this.selector).forEach((element) => {
flatpickr(element, {
enableTime: false,
dateFormat: "Y-m-d",
maxDate: today,
onChange: (selectedDates, dateStr) => {
if (this.onChangeCallback) {
this.onChangeCallback(dateStr); // Call callback with selected date
}
},
onReady: (selectedDates, dateStr, instance) => {
// Call the callback with the default date when initialized
if (this.onChangeCallback && dateStr) {
this.onChangeCallback(dateStr);
}
},
});
});
}
}
export default InitDatePicker;

View File

@@ -1,15 +0,0 @@
<?php
namespace [% namespace %];
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Routing\Controller as BaseController;
class Controller extends BaseController
{
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
[% response_methods %]
}

View File

@@ -1 +0,0 @@
return new [% api_resource_class %]($[% model_name_singular_variable %], [% model_was_deleted %]);

View File

@@ -1,4 +0,0 @@
return $this->successResponse(
[% model_was_deleted %],
$this->transform($[% model_name_singular_variable %])
);

View File

@@ -1 +0,0 @@
return new [% api_resource_collection_class %]($[% model_name_plural_variable %], [% models_were_retrieved %]);

View File

@@ -1,26 +0,0 @@
$[% data_variable %] = $[% model_name_plural_variable %]->transform(function ($[% model_name_singular_variable %]) {
return $this->transform($[% model_name_singular_variable %]);
});
return $this->successResponse(
[% models_were_retrieved %],
$[% data_variable %],
[
'links' => [
'first' => $[% model_name_plural_variable %]->url(1),
'last' => $[% model_name_plural_variable %]->url($[% model_name_plural_variable %]->lastPage()),
'prev' => $[% model_name_plural_variable %]->previousPageUrl(),
'next' => $[% model_name_plural_variable %]->nextPageUrl(),
],
'meta' =>
[
'current_page' => $[% model_name_plural_variable %]->currentPage(),
'from' => $[% model_name_plural_variable %]->firstItem(),
'last_page' => $[% model_name_plural_variable %]->lastPage(),
'path' => $[% model_name_plural_variable %]->resolveCurrentPath(),
'per_page' => $[% model_name_plural_variable %]->perPage(),
'to' => $[% model_name_plural_variable %]->lastItem(),
'total' => $[% model_name_plural_variable %]->total(),
],
]
);

View File

@@ -1 +0,0 @@
return new [% api_resource_class %]($[% model_name_singular_variable %], [% model_was_retrieved %]);

View File

@@ -1,4 +0,0 @@
return $this->successResponse(
[% model_was_retrieved %],
$this->transform($[% model_name_singular_variable %])
);

View File

@@ -1 +0,0 @@
return new [% api_resource_class %]($[% model_name_singular_variable %], [% model_was_added %]);

Some files were not shown because too many files have changed in this diff Show More