From 33b7131c336b84373e22cb79585877910bfdee6c Mon Sep 17 00:00:00 2001 From: "@jamaludinarifrohman6661" Date: Thu, 20 Feb 2025 16:16:06 +0700 Subject: [PATCH] feature: crud spatial planning and fix search in tourism and umkm --- .../Api/SpatialPlanningController.php | 145 ++++++++++++++ .../Controllers/Api/TourismController.php | 15 +- app/Http/Controllers/Api/UmkmController.php | 19 +- .../Data/SpatialPlanningController.php | 125 ++++++++++++ .../Controllers/Data/TourismController.php | 2 +- app/Http/Requests/SpatialPlanningRequest.php | 52 +++++ .../Resources/SpatialPlanningResource.php | 19 ++ app/Imports/SpatialPlanningImport.php | 68 +++++++ app/Imports/TourismImport.php | 9 - app/Models/SpatialPlanning.php | 37 ++++ ...0_050004_create_spatial_planning_table.php | 35 ++++ ...0_073855_rename_spatial_planning_table.php | 24 +++ .../templates/template_spatial_planning.xlsx | Bin 0 -> 8770 bytes .../spatialPlannings/data-spatialPlannings.js | 63 ++++++ .../spatialPlannings/form-create-update.js | 185 ++++++++++++++++++ .../js/data/spatialPlannings/form-upload.js | 150 ++++++++++++++ resources/js/data/tourisms/data-tourisms.js | 2 + resources/js/data/tourisms/form-upload.js | 2 +- resources/js/data/umkm/data-umkm.js | 2 + .../spatialPlannings/form-upload.blade.php | 91 +++++++++ .../data/spatialPlannings/form.blade.php | 130 ++++++++++++ .../data/spatialPlannings/index.blade.php | 38 ++++ .../views/data/tourisms/form-upload.blade.php | 2 +- routes/api.php | 4 + routes/web.php | 9 + 25 files changed, 1214 insertions(+), 14 deletions(-) create mode 100644 app/Http/Controllers/Api/SpatialPlanningController.php create mode 100644 app/Http/Controllers/Data/SpatialPlanningController.php create mode 100644 app/Http/Requests/SpatialPlanningRequest.php create mode 100644 app/Http/Resources/SpatialPlanningResource.php create mode 100644 app/Imports/SpatialPlanningImport.php create mode 100644 app/Models/SpatialPlanning.php create mode 100644 database/migrations/2025_02_20_050004_create_spatial_planning_table.php create mode 100644 database/migrations/2025_02_20_073855_rename_spatial_planning_table.php create mode 100644 public/templates/template_spatial_planning.xlsx create mode 100644 resources/js/data/spatialPlannings/data-spatialPlannings.js create mode 100644 resources/js/data/spatialPlannings/form-create-update.js create mode 100644 resources/js/data/spatialPlannings/form-upload.js create mode 100644 resources/views/data/spatialPlannings/form-upload.blade.php create mode 100644 resources/views/data/spatialPlannings/form.blade.php create mode 100644 resources/views/data/spatialPlannings/index.blade.php diff --git a/app/Http/Controllers/Api/SpatialPlanningController.php b/app/Http/Controllers/Api/SpatialPlanningController.php new file mode 100644 index 0000000..ccb9aea --- /dev/null +++ b/app/Http/Controllers/Api/SpatialPlanningController.php @@ -0,0 +1,145 @@ +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) + { + //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); + } +} diff --git a/app/Http/Controllers/Api/TourismController.php b/app/Http/Controllers/Api/TourismController.php index 704e4e9..896ff79 100644 --- a/app/Http/Controllers/Api/TourismController.php +++ b/app/Http/Controllers/Api/TourismController.php @@ -25,6 +25,13 @@ class TourismController extends Controller $search = $request->input('search', ''); $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->getCollection()->transform(function ($tourisms) { @@ -36,8 +43,14 @@ class TourismController extends Controller 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([ - 'data' => TourismResource::collection($tourisms), + 'data' => $data, 'meta' => [ 'total' => $tourisms->total(), 'per_page' => $tourisms->perPage(), diff --git a/app/Http/Controllers/Api/UmkmController.php b/app/Http/Controllers/Api/UmkmController.php index 3be6947..b35fe01 100644 --- a/app/Http/Controllers/Api/UmkmController.php +++ b/app/Http/Controllers/Api/UmkmController.php @@ -27,6 +27,17 @@ class UmkmController extends Controller $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->getCollection()->transform(function ($umkm) { @@ -47,8 +58,14 @@ class UmkmController extends Controller 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([ - 'data' => UmkmResource::collection($umkm), + 'data' => $data, 'meta' => [ 'total' => $umkm->total(), 'per_page' => $umkm->perPage(), diff --git a/app/Http/Controllers/Data/SpatialPlanningController.php b/app/Http/Controllers/Data/SpatialPlanningController.php new file mode 100644 index 0000000..cfbb1f2 --- /dev/null +++ b/app/Http/Controllers/Data/SpatialPlanningController.php @@ -0,0 +1,125 @@ +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", + ]; + } +} diff --git a/app/Http/Controllers/Data/TourismController.php b/app/Http/Controllers/Data/TourismController.php index 5facbb5..87d4379 100644 --- a/app/Http/Controllers/Data/TourismController.php +++ b/app/Http/Controllers/Data/TourismController.php @@ -53,7 +53,7 @@ class TourismController extends Controller public function edit($id) { $title = 'Pariwisata'; - $subtitle = 'Create Data'; + $subtitle = 'Update Data'; $modelInstance = Tourism::find($id); // Pastikan model ditemukan diff --git a/app/Http/Requests/SpatialPlanningRequest.php b/app/Http/Requests/SpatialPlanningRequest.php new file mode 100644 index 0000000..853d646 --- /dev/null +++ b/app/Http/Requests/SpatialPlanningRequest.php @@ -0,0 +1,52 @@ +|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 + */ + 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.', + ]; + } +} diff --git a/app/Http/Resources/SpatialPlanningResource.php b/app/Http/Resources/SpatialPlanningResource.php new file mode 100644 index 0000000..86b438b --- /dev/null +++ b/app/Http/Resources/SpatialPlanningResource.php @@ -0,0 +1,19 @@ + + */ + public function toArray(Request $request): array + { + return parent::toArray($request); + } +} diff --git a/app/Imports/SpatialPlanningImport.php b/app/Imports/SpatialPlanningImport.php new file mode 100644 index 0000000..ce9fe15 --- /dev/null +++ b/app/Imports/SpatialPlanningImport.php @@ -0,0 +1,68 @@ +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 = $row[7]; + + try { + // Coba parsing tanggal secara otomatis + $parsedDate = Carbon::parse($dateValue)->format('Y-m-d H:i:s'); + } catch (\Exception $e) { + // Jika gagal parsing, atur nilai default atau null + $parsedDate = null; + } + + $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; + } + } +} \ No newline at end of file diff --git a/app/Imports/TourismImport.php b/app/Imports/TourismImport.php index 122f761..b8967eb 100644 --- a/app/Imports/TourismImport.php +++ b/app/Imports/TourismImport.php @@ -65,13 +65,6 @@ class TourismImport implements ToCollection // ambill village code yang village_name sama dengan $villageName $villageCode = $listTrueVillage[$villageName]['village_code'] ?? '000000'; - - // if (empty($row[16])) { - // info("Data kosong"); - // } else { - // info("Baris ke- | Nilai: " . $row[16]); - // } - $excelSerialDate = $row[16]; if (is_numeric($excelSerialDate)) { $projectSubmissionDate = Carbon::createFromFormat('Y-m-d', '1899-12-30') @@ -82,8 +75,6 @@ class TourismImport implements ToCollection ->format('Y-m-d H:i:s'); } - info("Tanggal dikonversi: " . $projectSubmissionDate); - $dataToInsert[] = [ 'project_id' => $row[1], 'project_type_id' => $row[2], diff --git a/app/Models/SpatialPlanning.php b/app/Models/SpatialPlanning.php new file mode 100644 index 0000000..2db73e2 --- /dev/null +++ b/app/Models/SpatialPlanning.php @@ -0,0 +1,37 @@ + + */ + protected $fillable = ['name', 'kbli', 'activities', 'area', 'location', 'number', 'date']; + + +} diff --git a/database/migrations/2025_02_20_050004_create_spatial_planning_table.php b/database/migrations/2025_02_20_050004_create_spatial_planning_table.php new file mode 100644 index 0000000..e7ca0fc --- /dev/null +++ b/database/migrations/2025_02_20_050004_create_spatial_planning_table.php @@ -0,0 +1,35 @@ +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'); + } +}; diff --git a/database/migrations/2025_02_20_073855_rename_spatial_planning_table.php b/database/migrations/2025_02_20_073855_rename_spatial_planning_table.php new file mode 100644 index 0000000..b940552 --- /dev/null +++ b/database/migrations/2025_02_20_073855_rename_spatial_planning_table.php @@ -0,0 +1,24 @@ +yD)^!s+LAuc(!6CT21b26LcWETJy9N&s+yVrGySoRs;1(Q$TX;J&bHAC% z%=ZiKtzN61UcKs^T2;^9NA_t2X=oS>02}}T000mJjE+B8=s^JhaWDV?IsoCBwy?dO zv#Fi4zKXkpDOi{Ot*s4FF3dB^Y``vdBT)2O;z4+ePU>rw00P@l zKtCF_;+u}1guW7E?MzF{r_AtspimB`6-Oo7_*YNnvr&sGJKMT|u)by`q)7jP4pluo z4u;ZqB#Gx&ELXh(1!4_lDkG&vr@IvC zHZWxa%Z!2XvlYgWxK@#|eR0+_?Or-w9_^GdkrxVgP3)K! z`gGcv0{+ASlk)%t>&P0cPt11X-aY)7c{0PNT|~?G48J*#*oO_{NACUP%HY-!gD z`ZGo3xj3i;e2NAJ8#p`UkBsrX>1a97IQ3H?*X~aT+Up*7ngFUbZw2LE^R2z zkse$n6`MK}sRWMGtK%Y~7vP6r@xAxg>XX%6F}f*-nh{by`V{iHo+EodVLaV;F1c_I znLmt6=5X>o<^b5pe4*THz=rtp7FSi-g3I!=QKl0&iMzguO~<)NdOP}~7oA+jfD#ES z+70u#ct3gerLR^E>)G%J_Z|+!?$UwN!GK7@;uT1S`X@>J-z27-L*7Uege15C1gN(* z48QZl&ECn{$ll)iXZHFJXP_V{4C3X#`zTYAmF{5#w!Zig%IKEviV0e9W*|9KJwQbm ztf5+>AZGEoSjD4m($$!iW`MR2aX%UDcfDpq*?>mB?4&7w>jZ~wI4kng+?$s z`a}>Q4vUUg$QpsLZyX8ikjx;IabT2`A2R5G8q$-2oHdrip28Iz_J#zPCJ> z$-0kOcjnz8ZR3|p6O^#=d4)I=g${@l`clP%w{zb&tDdYo3*8S7KR2!K4w34J!YYCQ|(CZ;@#62FNH3x-W=>3=-AEO@ zvU@RgwQM35elC+DgNw}N{Y<a)joKLV zoU``6gAGe#&k9gs0t{Vu&rbc~EHH3r%_yUah(gn?4ZDa+ghh6)(N-$DDaP8X4_%bX zS2G%fH$&7TC$XO8p&*4Z&hBYTyCj+K9We>Q>Bjyd{|PIrMASu9`+i;WrbSof}hLV;;9| zFIg=baMKV$eKsyD%D}Q&qaNKcK^EeOUeh%;8q10!a@?W97vPg*ORC$EC&St|AIW*y z!9Brmllb6>l=VGLufr5Vkv(gSOWvlAN5dn&Fz=&@&zfmh=KO)jcm%QE#B)9KPMV~F z>!bU9vb*Hu?C3A&WpOSMHtjPcWBOc$vr_vkr_DPCeI43TH^b5*GJK-r9UMV=6bTM| zvabgZnKMV?EqNQ|)XunDH7aI@FP5HcjceHP^aIL=(lpy^qXtMff0}f;;i@yop3}3AkGQ`!BcirNz`xd6xppG{aZyUWj-HgvCOaR! z$t*CmucO3dcz@yewqF2Gu#wWb3y~+sY-;U$*W9AtOnm0&K&DZ?U2Yx&@)~6hiMsD< zrBW}`O9BqWat1D;CRE6;b=*d1h{FZd>P>7`L4nCz;*r(*z#A>NHpIHhaoM$V@Re|v z?7Znjx5tce-sTIEurMF3?gG-e>;m_ z>-T;{BwPg_R>W4a4%%#tfAJDO%T%K^_u2Sm%gW>R*v;&Axo)=!s30<|sC6L=g>51H zRDR^h9--}DlR{*Gokjwrri#J++0gvPaAylsTT_PL&P+e|;81fs5|0C;4f~EC3GDV@ zZDzx82IpyGzH~@Ie2~9YTx%LzmG%y2u$hTiA>KGgAuU<&dF!RZA{^Q`@<;1BSxe=C z_AjqT=Se?p+ojnN(P?%w=iOdh?6q>NCa_{NEJuF?2Pw;GOuCk0Q59Q@;%a%nql-TWIpE$<(%>2v8o>o0k}@ULqZABhQ#)D4-w2{7wn_>3dBwh&an zgyxVgH@u1QDsVY9**Zx|AqFI=^IDK8wUzyS9LP0fGQYW%b*0E59f2NCjCDDsuyR7! zus>8aeC-9n)xgxX4Y1hRLn5A2oiJZ)Jxx0L+61pFF~gz$89cfjrO%>t^oKMe`@S@$pJr%I2E+V>XY= z692iwxj9ZZuUnSW=ZOo~v!{WA2zsoL@9W-?^0>8Me*U~3$mW$dx#x}yT_FG>A6&<6 z+JL`w;99~o>G~Ffx$?bLFfSR8K_L#g^e|pnMuUWXTPy1LvoipIm@g661B%4S%m$e_ zhEZ}!gjY)!g2+~o$fJb81!_1d`=|OpcwGv31L_zjJ*4$If-2Y~-=Gi{m{2SphZ@Yx z5HVzVKYd?7-`%e6e~p@%$$PW6wceco&u>;26~jaMe*cxe*Zo44zVG3~*;YDsY}@C} z#+awePZAJKEjt&RmO{Ip*>eIVi@)IFyn}Xen!4Kr?*QA zda%H>kWbjh^}b;XdIcL6E)k}gS8b6ZCZvt+65`zD7%+msWFF@-HbwKBSY`W-o-rn5eh|+>~f>C-w?2@0^Ly^1qS;Z zR!Qa&t`~$H(4AF_RLH`-nC0mzTcY0IzM=mHV_3jUbRUw|PsXLZC?DSRBsX1M;;c2z zLsi+P#Z#2;g?`LWxyHvTUvB)?eD=jW&!bP#>hY6aJ4fsAZpC7-LSP7XsY|VEEC>Da z>!3;R%Ivo(r87LC-&qL+@)p&KTeq!fI3m}WS1DgMSVpK2zBb(;CCPn67&QpjSH+v4 z8kd!CWHH89`%$848-7N$Wc9=tdp*y&RH;_#{raZ~C#ZySd6iZw!aeyC+} zPHx7|T~grc@ikdZrwp3Qi}1cGQ0}Q4+4RU*u;_4CF4ZW5aBbAa5T<}FCZ+~I*XMn! z=C+g%Fdc=@6?u~#hfrXXCz3k3<|OQ$C}vmySm?t@lqAdXIvjHHWAxIs>R~m4Led-w zLCGN?+)ZAd%e;j1D3?-%kAm_|Mz`!3thl2bAZsd;q8fjSCZ#VuTz1WZ%b`=1ptRa$ z#KVVm=1jq3hsI-3=jZ+Ar0>ia9u6pnX}5Dj-zIYDgv%AeF@ZQ;D8rY^Gk#FxS>&>c zIF`-|09Uk}=;D&&S~WrRckBU$k3F@t6tB+4`+)D!N(wmLT6j;j57J7k0!-o-nx}AZda8OQkCEjErLFXQdUqlrJ6gC=hATR z+igQ1(Py!6Ixh#qD{C4&xy}K9%f@5R_VL4JfWW!P=T7@lT*J$r12vy-Zrs+U z{u<>psvD*9gm-g@t`6`$dGNa(VCshFQoM1h)U#GZ!x;0%szig-RG6Js=`~)g}p7@6+rV?gz8ggRkQ$OpNC;Z=Ce~JTWVd z+W)nXXijw@+l2-I>W}~c!jB%El+UFpng zUqQnz<_Itwbg;wJh%AWQ<|;Z{xy}2Yyi25Q3g@vGg*%j%$vlo3KWoG`b86j0;sdHG zQ&kZ)MnQ{+(7&Lb)jU04X2yprp4Grel@zQpkl$bPxI5xV&^p0sTn6TX8h|cXHZ|pa ziYmK!U5?&VG$d=%RG9s^bylxtl1Lhgqe1413^DMaeifLp?$q5~&UKB}RW(C;_h2?% zMX}43yXV01{)#yv>J}q_OUi}g%3WQ$_%YXLQkm&ThE83w;rN{`rXfYvh>j^|k~*(J zmuRO(OzsyR@E|9BJ#2^IKp#JyFJMhT)cvWa^V!A*NgJyEkDX7^56%2_O6^F?s9x=; zzAdPM#e91MwjNzare0eMwuD^mZCA_UvSd?|RHE2Ge8UsV2fYDQBg;2}0>FZaG`aoQ zZ%{08WN=3G?^PtA7l8Rm2qWQBsm5aJ5xBKz;GrwIuPo2f38M*$I+8(R$3aht~-1B^M#no=@|i5)5BucCK)tp!<2u2P+YxT#P+2O&2T>uq>DZ$1TF&1*1(yK<53S&y}19ThJ7rGAe{j)HLD%(Mo z+UQ>yw%1qgfE(o(+$b{7lw%5u-jl~o(=NYiMAqqir#SVPq3JYm-+_o*-$}@4kh*95xoW=t$ zUXtBqJGh9u#^ty+i0TVHd{uvoWi%CoB#|y2$EEjyQ1zC0G!AE8lQAX3_`>>uCm_l# zsu^n4HJr3Ha4hMAcGlj5iJs?(bjD0FvurOhB0v9E9Ou_duZ7eRfg+U$X^C`T+f27Q z?^j&`HuMtV^)jok-k1s2`$#`;+6J^vc!(3`*U?vHYqv9jNpMmd{H{6JSY2;KwMDDf z#?u#2^Lj_Cuu(P_7!UQ|vEmn0i>6-fR`(ilMR=Y)Fr3uhV%p2zQ-pk%xZ6zYj8tLk zWM8xA(ZqU-2fr#u?ssHNcnNn_${OCit(p<^2t^Rm-KmO8Kb2qq^1_^BkNNU*(G!X% znz-Z!!3CTb_iQ4%nCP(Ta2x|{yup1iQEqa0@qMWA9pb4Qw(xB5O5isOLne9ZtF_xX7N;g?ql9ynYQB}D*lSv2*lbF>n zrNtYUj4PQHe%C1I@|^9FBRcuoTrrzeIc=D^GY8wCM~Yfm?->2zes0Gu_6HU-AW-VV+SgR8_4~9P8Q{@iMA9imf)o&1x%S$n;HBg;iQfHM$XmbjgJ=|R<~^8DqSSPV7}}agiB|elxM5yw zUZ3s*zb_}xUa7gs!0~IEmu$4b%!VdMtCF`y!Q-du0Ffh@WQpQ+@vEK)lFnF=%4t?4 zR4lNgU8KRsF3(n{vyCRPDhmJkxo~D()Cm~c@U?;8rQy4_^7n-b0E9zPbm@XB0L%kP z=Q^37g5a1P@7eSgBEe{jBWt)h@wbT>%j)&ya1fsbY455QCVN>LvmGTPdm>{1y2s|e zWi@V}*`@Qb?1sU`JGvfZm_9hP=oSZj8U8r9i#QhOKKAIrXr%V5nIs!oBu%;`USbPO zMmhB!cD)9`@zcRG*vdtC8rNDtKU+rPCbh<$I3>R=My#;tyecm;rlNW!0kxQJZ%U5Q zS5=#k2ubxbm*P}c%%vQXHcGG0CmT6OQR#d&Mx4E4t(hFVx@mZuVXdV8tL1Cqi1sldcJ z;lB`pbZ*uVG=m_bor%4%f|I=in8DcI$@GtL)Bm(O2*sn~73I5$FoIWQA5f7`=)Uxs z#@9(t9}bdJq)*(4t=U=ACIS|ZR;8CdsqF^FI*dJX?uK|dD!5!A&Z>`g0dPI9G zL79^;%EeI6M`~ZzoGGf~M7mz0nB%}wG2^8=$@EJmA)g3+_1J?pE1f+%^=At2r8|RQ zkMXq28#cJLghz#^@Z}TjBH7%0FtOS`39P*GO=m#%|B8)sN|XnTMiw0#l6HOiteMM9 zz9%a)$w`jKZ0_90d^g+rWZV|Xty8QV;fr3aEjAeR7A}#i?S^0*`(Mkd6+x_p2nh5* z5MToT3Oz#yhyNiD^2+`=GU5g8ml%|k(TkembsDq%ZwyAEVN zk%g7LJDiKHo0zurT#VlGe0wJ%sY}SMe-Ss)s}R1OivbIRXN`T8)Uf*4a6|o^4Bcwe z*iQ-{s<-yXji9ZQOxz$87Cz`R8l9q+WO!Zh*jm7LhZH*4JZz~YrP4AZINc(P8-a{r z_gn?#WS6M4DE-Rc+aMSoO6*Z!UkEpO>T9_ujsk0%+UAYp<9wEgcn^ zdf5BKSFcjzBQr;BG%NEK8oF~EiJgB2q#g{hLnqWTI*69| z@6-DK?AL$Rf0^AYNdMiz-{*?|G5oovKuGbIDdVq(f1kDd)vyIplKlTCFTeWvRe=A~ z(+T7c3BSnkzZ(B4u>NT*^Wrz-f5@%BdiYfx{L{k(q}PJn{#UK=R|mhgW`8 + + + + + `); + }, + }, +]; + +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(); +}); diff --git a/resources/js/data/spatialPlannings/form-create-update.js b/resources/js/data/spatialPlannings/form-create-update.js new file mode 100644 index 0000000..f65687e --- /dev/null +++ b/resources/js/data/spatialPlannings/form-create-update.js @@ -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 = ` + + 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(); + }); +}); diff --git a/resources/js/data/spatialPlannings/form-upload.js b/resources/js/data/spatialPlannings/form-upload.js new file mode 100644 index 0000000..4383f54 --- /dev/null +++ b/resources/js/data/spatialPlannings/form-upload.js @@ -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 = '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(); +} + diff --git a/resources/js/data/tourisms/data-tourisms.js b/resources/js/data/tourisms/data-tourisms.js index 0b54a3a..b3473b8 100644 --- a/resources/js/data/tourisms/data-tourisms.js +++ b/resources/js/data/tourisms/data-tourisms.js @@ -5,6 +5,7 @@ import GlobalConfig from "../../global-config.js"; import GeneralTable from "../../table-generator.js"; const dataTourismsColumns = [ + "No", "Nama Perusahaan", "Nama Proyek", "Alamat Usaha", @@ -54,6 +55,7 @@ document.addEventListener("DOMContentLoaded", () => { table.processData = function (data) { return data.data.map((item) => { return [ + item.no, item.business_name, item.project_name, item.business_address, diff --git a/resources/js/data/tourisms/form-upload.js b/resources/js/data/tourisms/form-upload.js index 4b6972d..aab1366 100644 --- a/resources/js/data/tourisms/form-upload.js +++ b/resources/js/data/tourisms/form-upload.js @@ -123,7 +123,7 @@ document.getElementById('downloadtemptourisms').addEventListener('click', functi // Function to show toast function showToast(iconClass, iconColor, message) { - const toastElement = document.getElementById('toastUploadUmkm'); + const toastElement = document.getElementById('toastUploadTourisms'); const toastBody = toastElement.querySelector('.toast-body'); const toastHeader = toastElement.querySelector('.toast-header'); diff --git a/resources/js/data/umkm/data-umkm.js b/resources/js/data/umkm/data-umkm.js index 47bf872..c0d9e5a 100644 --- a/resources/js/data/umkm/data-umkm.js +++ b/resources/js/data/umkm/data-umkm.js @@ -4,6 +4,7 @@ import GlobalConfig from "../../global-config.js"; import GeneralTable from "../../table-generator.js"; const dataUMKMColumns = [ + "No", "Nama Usaha", "Alamat Usaha", "Deskripsi Usaha", @@ -54,6 +55,7 @@ document.addEventListener("DOMContentLoaded", () => { table.processData = function (data) { return data.data.map((item) => { return [ + item.no, item.business_name, item.business_address, item.business_desc, diff --git a/resources/views/data/spatialPlannings/form-upload.blade.php b/resources/views/data/spatialPlannings/form-upload.blade.php new file mode 100644 index 0000000..4f86296 --- /dev/null +++ b/resources/views/data/spatialPlannings/form-upload.blade.php @@ -0,0 +1,91 @@ +@extends('layouts.vertical', ['subtitle' => 'File Upload']) + +@section('content') + +@include('layouts.partials/page-title', ['title' => 'Form', 'subtitle' => 'File Uploads']) + +
+
+
+
+
+
Upload Data Pariwisata
+ +
+
+ +
+ +
+ +
+
+
+ +
+
+
+ +

Drop files here or click to upload.

+

+ Please upload a file with the extension .xls or .xlsx with a maximum size of 10 MB. +
+

+
+
+ +
    +
  • + +
    +
    +
    +
    + +
    +
    +
    +
    +
      +
    +

    + +
    +
    +
    + +
    +
    +
    +
  • +
+ +
+
+ +
+
+
+
+
+ +
+ +
+ +@endsection + +@section('scripts') +@vite(['resources/js/data/spatialPlannings/form-upload.js']) +@endsection \ No newline at end of file diff --git a/resources/views/data/spatialPlannings/form.blade.php b/resources/views/data/spatialPlannings/form.blade.php new file mode 100644 index 0000000..969bcbe --- /dev/null +++ b/resources/views/data/spatialPlannings/form.blade.php @@ -0,0 +1,130 @@ +@extends('layouts.vertical', ['subtitle' => $subtitle]) + +@section('content') + +@include('layouts.partials/page-title', ['title' => $title, 'subtitle' => $subtitle]) + +
+ @if (session('error')) +
+ {{ session('error') }} +
+ @endif + + @if ($errors->any()) +
+
    + @foreach ($errors->all() as $error) +
  • {{ $error }}
  • + @endforeach +
+
+ @endif + +
+
+
+ +
+
+
+ @csrf + @if(isset($modelInstance)) + @method('PUT') + @endif + +
+ @foreach($fields as $field => $label) +
+ + @php + $fieldType = $fieldTypes[$field] ?? 'text'; // Default text jika tidak ditemukan tipe + @endphp + + @if($fieldType == 'textarea') + + @elseif($fieldType == 'select' && isset($dropdownOptions[$field])) + + @elseif($fieldType == 'combobox' && isset($dropdownOptions[$field])) + + + @elseif($fieldType == 'date') + + @else + + @endif +
+ @endforeach +
+ +
+ +
+
+
+
+
+
+ + + + + +
+ +
+ +@endsection + +@section('scripts') +@vite(['resources/js/data/spatialPlannings/form-create-update.js']) +@endsection \ No newline at end of file diff --git a/resources/views/data/spatialPlannings/index.blade.php b/resources/views/data/spatialPlannings/index.blade.php new file mode 100644 index 0000000..da2e4b0 --- /dev/null +++ b/resources/views/data/spatialPlannings/index.blade.php @@ -0,0 +1,38 @@ +@extends('layouts.vertical', ['subtitle' => 'Rencana Tata Ruang']) + +@section('css') +@vite(['node_modules/gridjs/dist/theme/mermaid.min.css']) +@endsection + +@section('content') +@include('layouts.partials/page-title', ['title' => 'Data', 'subtitle' => 'Rencana Tata Ruang']) + +
+
+
Daftar Rencana Tata Ruang
+
+
+
+
+ + +
+
+
+
+
+
+
+ +{{--
--}} + +@endsection + +@section('scripts') +@vite(['resources/js/data/spatialPlannings/data-spatialPlannings.js']) +@endsection \ No newline at end of file diff --git a/resources/views/data/tourisms/form-upload.blade.php b/resources/views/data/tourisms/form-upload.blade.php index 9f77c02..c8e4acf 100644 --- a/resources/views/data/tourisms/form-upload.blade.php +++ b/resources/views/data/tourisms/form-upload.blade.php @@ -71,7 +71,7 @@
-