diff --git a/app/Http/Controllers/Api/SpatialPlanningsController.php b/app/Http/Controllers/Api/SpatialPlanningsController.php index 3c75aee..9513398 100644 --- a/app/Http/Controllers/Api/SpatialPlanningsController.php +++ b/app/Http/Controllers/Api/SpatialPlanningsController.php @@ -3,8 +3,13 @@ namespace App\Http\Controllers\Api; use App\Http\Controllers\Controller; +use App\Http\Requests\ExcelUploadRequest; +use App\Http\Requests\SpatialPlanningsRequest; +use App\Http\Resources\SpatialPlanningsResource; +use App\Imports\SpatialPlanningImport; use App\Models\SpatialPlanning; use Illuminate\Http\Request; +use Maatwebsite\Excel\Facades\Excel; class SpatialPlanningsController extends Controller { @@ -18,13 +23,20 @@ class SpatialPlanningsController extends Controller $query = $query->where("name", "LIKE", "%{$request->get("search")}%") ->orWhere("nomor", "LIKE", "%{$request->get("search")}%"); } - $query = $query->paginate(); - return response()->json($query); + return SpatialPlanningsResource::collection($query->paginate()); } - public function store(Request $request) + public function store(SpatialPlanningsRequest $request) { - // + try{ + $validated = $request->validated(); + $data = SpatialPlanning::create($validated); + return response()->json(['message' => 'Successfully created', new SpatialPlanningsResource($data)]); + }catch(\Exception $e){ + return response()->json([ + 'message' => $e->getMessage() + ], 500); + } } /** @@ -38,9 +50,18 @@ class SpatialPlanningsController extends Controller /** * Update the specified resource in storage. */ - public function update(Request $request, string $id) + public function update(SpatialPlanningsRequest $request, string $id) { - // + try{ + $validated = $request->validated(); + $data = SpatialPlanning::find($id); + $data->update($validated); + return response()->json(['message' => 'Successfully updated', new SpatialPlanningsResource($data)]); + }catch(\Exception $e){ + return response()->json([ + 'message' => $e->getMessage() + ], 500); + } } /** @@ -51,7 +72,27 @@ class SpatialPlanningsController extends Controller try{ SpatialPlanning::destroy($id); return response()->json([ - 'message' => 'Data berhasil dihapus' + 'message' => 'Successfully deleted' + ], 200); + }catch(\Exception $e){ + return response()->json([ + 'message' => $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 SpatialPlanningImport, $file); + return response()->json([ + 'message' => 'Successfully imported' ], 200); }catch(\Exception $e){ return response()->json([ diff --git a/app/Http/Controllers/SpatialPlanningsController.php b/app/Http/Controllers/SpatialPlanningsController.php index 6d6f137..acdfc0b 100644 --- a/app/Http/Controllers/SpatialPlanningsController.php +++ b/app/Http/Controllers/SpatialPlanningsController.php @@ -2,63 +2,27 @@ namespace App\Http\Controllers; +use App\Models\SpatialPlanning; use Illuminate\Http\Request; class SpatialPlanningsController extends Controller { - /** - * Display a listing of the resource. - */ public function index() { return view('spatial-plannings.index'); } - - /** - * Show the form for creating a new resource. - */ public function create() { return view('spatial-plannings.create'); } - /** - * 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) { - return view('spatial-plannings.update'); + $data = SpatialPlanning::findOrFail($id); + return view('spatial-plannings.update', compact('data')); } - /** - * Update the specified resource in storage. - */ - public function update(Request $request, string $id) - { - // - } - - /** - * Remove the specified resource from storage. - */ - public function destroy(string $id) - { - // + public function upload (){ + return view('spatial-plannings.upload'); } } diff --git a/app/Http/Requests/ExcelUploadRequest.php b/app/Http/Requests/ExcelUploadRequest.php new file mode 100644 index 0000000..ad492ca --- /dev/null +++ b/app/Http/Requests/ExcelUploadRequest.php @@ -0,0 +1,28 @@ +|string> + */ + public function rules(): array + { + return [ + "file" => "required|file|mimes:xlsx,xls|max:102400" + ]; + } +} diff --git a/app/Http/Requests/SpatialPlanningsRequest.php b/app/Http/Requests/SpatialPlanningsRequest.php index b1af782..73971b4 100644 --- a/app/Http/Requests/SpatialPlanningsRequest.php +++ b/app/Http/Requests/SpatialPlanningsRequest.php @@ -29,7 +29,7 @@ class SpatialPlanningsRequest extends FormRequest '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)], - 'tanggal' => ['required','date'], + 'sp_date' => ['required','date'], ]; } } diff --git a/app/Http/Resources/SpatialPlanningsResource.php b/app/Http/Resources/SpatialPlanningsResource.php new file mode 100644 index 0000000..40a29d9 --- /dev/null +++ b/app/Http/Resources/SpatialPlanningsResource.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..1f5c548 --- /dev/null +++ b/app/Imports/SpatialPlanningImport.php @@ -0,0 +1,77 @@ + "January", "Februari" => "February", "Maret" => "March", + "April" => "April", "Mei" => "May", "Juni" => "June", + "Juli" => "July", "Agustus" => "August", "September" => "September", + "Oktober" => "October", "November" => "November", "Desember" => "December" + ]; + + $collection->skip(2)->each(function ($row) use ($months) { + if (empty(array_filter($row->toArray()))) { + return; + } + + if (!isset($row[6]) || empty($row[6])) { + return; + } + + if(!SpatialPlanning::where("nomor", $row[6])->exists()){ + $clean_nomor = str_replace('\\','',$row[6]); + $date_string = isset($row[7]) ? trim($row[7]) : null; + $clean_sp_date = null; + if ($date_string) { + if(is_numeric($date_string)) { + $clean_sp_date = Carbon::createFromFormat('Y-m-d', '1900-01-01')->addDays($date_string - 2)->format('Y-m-d'); + }else{ + foreach ($months as $id => $en) { + $date_string = str_replace($id, $en, $date_string); + } + + $formats = ['j F Y', 'd F Y', 'j-M-Y', 'd-M-Y']; + + foreach ($formats as $format) { + $date = DateTime::createFromFormat($format, $date_string); + if ($date) { + $clean_sp_date = $date->format('Y-m-d'); + break; + } + } + } + } + SpatialPlanning::create([ + 'name' => $row[1], + 'kbli' => $row[2], + 'kegiatan' => $row[3], + 'luas' => $row[4], + 'lokasi' => $row[5], + 'nomor' => $clean_nomor, + 'sp_date' => $clean_sp_date, + ]); + } + }); + } + + public function sheets(): array { + return [ + 0 => $this + ]; + } +} diff --git a/database/seeders/UsersRoleMenuSeeder.php b/database/seeders/UsersRoleMenuSeeder.php index 84b7625..d8a4c9f 100644 --- a/database/seeders/UsersRoleMenuSeeder.php +++ b/database/seeders/UsersRoleMenuSeeder.php @@ -186,6 +186,13 @@ class UsersRoleMenuSeeder extends Seeder "parent_id" => $data->id, "sort_order" => 5, ], + [ + "name" => "Tata Ruang", + "url" => "spatial-plannings", + "icon" => null, + "parent_id" => $data->id, + "sort_order" => 6, + ], [ "name" => "Lap Pariwisata", "url" => "tourisms.index", @@ -213,6 +220,7 @@ class UsersRoleMenuSeeder extends Seeder $laporan_pariwisata = Menu::where('name', 'Lap Pariwisata')->first(); $umkm = Menu::where('name', 'UMKM')->first(); $lack_of_potentials = Menu::where('name', 'Dashboard Potensi')->first(); + $spatial_plannings = Menu::where('name', 'Tata Ruang')->first(); // Superadmin gets all menus $superadmin->menus()->sync([ @@ -238,6 +246,7 @@ class UsersRoleMenuSeeder extends Seeder $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], $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], ]); // Admin gets limited menus diff --git a/resources/js/spatial-plannings/create.js b/resources/js/spatial-plannings/create.js new file mode 100644 index 0000000..e8ed4f5 --- /dev/null +++ b/resources/js/spatial-plannings/create.js @@ -0,0 +1,69 @@ +import flatpickr from "flatpickr"; + +class CreateSpatialPlannings { + constructor() { + this.initCreateSpatial(); + } + + initCreateSpatial() { + const toastNotification = document.getElementById("toastNotification"); + const toast = new bootstrap.Toast(toastNotification); + document + .getElementById("btnCreateSpatialPlannings") + .addEventListener("click", async function () { + let submitButton = this; + let spinner = document.getElementById("spinner"); + let form = document.getElementById( + "formCreateSpatialPlannings" + ); + + 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/spatial-plannings"; + }, 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 CreateSpatialPlannings(); +}); diff --git a/resources/js/spatial-plannings/index.js b/resources/js/spatial-plannings/index.js new file mode 100644 index 0000000..51da198 --- /dev/null +++ b/resources/js/spatial-plannings/index.js @@ -0,0 +1,156 @@ +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 SpatialPlannings { + 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.initTableSpatialPlannings(); + this.initEvents(); + } + initEvents() { + document.body.addEventListener("click", async (event) => { + const deleteButton = event.target.closest( + ".btn-delete-spatial-plannings" + ); + if (deleteButton) { + event.preventDefault(); + await this.handleDelete(deleteButton); + } + }); + } + + initTableSpatialPlannings() { + let tableContainer = document.getElementById("table-spatial-plannings"); + // Create a new Grid.js instance only if it doesn't exist + this.table = new Grid({ + columns: [ + "ID", + "Name", + "KBLI", + "Kegiatan", + "Luas", + "Lokasi", + "Nomor", + "Date", + { + name: "Action", + formatter: (cell) => + gridjs.html(` +
+ Please upload a file with the extension .xls or .xlsx with a maximum size of 10 MB.
+
+ For .xls and .xlsx files, ensure that the data is contained within a single sheet with the following columns:
+ No, Nama, KBLI, Kegiatan, Luas, Lokasi, Nomor, Tanggal.
+