diff --git a/app/Http/Controllers/Api/TaxationsController.php b/app/Http/Controllers/Api/TaxationsController.php index ef9a193..38950db 100644 --- a/app/Http/Controllers/Api/TaxationsController.php +++ b/app/Http/Controllers/Api/TaxationsController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Api; use App\Exports\TaxationsExport; use App\Http\Controllers\Controller; use App\Http\Requests\ExcelUploadRequest; +use App\Http\Requests\TaxationsRequest; use App\Http\Resources\TaxationsResource; use App\Imports\TaxationsImport; use Illuminate\Http\Request; @@ -60,4 +61,38 @@ class TaxationsController extends Controller { return Excel::download(new TaxationsExport, 'pajak_per_kecamatan.xlsx'); } + + public function delete(Request $request) + { + try{ + $tax = Tax::find($request->id); + $tax->delete(); + return response()->json(['message' => 'Data deleted successfully'], 200); + }catch(\Exception $e){ + Log::info($e->getMessage()); + return response()->json([ + 'error' => 'Failed to delete data', + 'message' => $e->getMessage() + ], 500); + } + } + + public function update(TaxationsRequest $request, string $id) + { + try{ + $tax = Tax::find($id); + if($tax){ + $tax->update($request->validated()); + return response()->json(['message' => 'Successfully updated', new TaxationsResource($tax)]); + } else { + return response()->json(['message' => 'Tax not found'], 404); + } + }catch(\Exception $e){ + Log::info($e->getMessage()); + return response()->json([ + 'error' => 'Failed to update tax', + 'message' => $e->getMessage() + ], 500); + } + } } diff --git a/app/Http/Controllers/TaxationController.php b/app/Http/Controllers/TaxationController.php index dfce41c..aef2925 100644 --- a/app/Http/Controllers/TaxationController.php +++ b/app/Http/Controllers/TaxationController.php @@ -2,6 +2,7 @@ namespace App\Http\Controllers; +use App\Models\Tax; use Illuminate\Http\Request; class TaxationController extends Controller @@ -52,9 +53,11 @@ class TaxationController extends Controller /** * Show the form for editing the specified resource. */ - public function edit(string $id) + public function edit(Request $request, string $id) { - // + $menuId = $request->query('menu_id') ?? $request->input('menu_id'); + $data = Tax::find($id); + return view('taxation.edit', compact('menuId', 'data')); } /** diff --git a/app/Http/Requests/TaxationsRequest.php b/app/Http/Requests/TaxationsRequest.php new file mode 100644 index 0000000..9276e0e --- /dev/null +++ b/app/Http/Requests/TaxationsRequest.php @@ -0,0 +1,38 @@ +|string> + */ + public function rules(): array + { + return [ + 'tax_no' => ['required', 'string', Rule::unique('taxs')->ignore($this->id)], + 'tax_code' => ['required', 'string'], + 'wp_name' => ['required', 'string'], + 'business_name' => ['required', 'string'], + 'address' => ['required', 'string'], + 'start_validity' => ['required', 'date_format:Y-m-d'], + 'end_validity' => ['required', 'date_format:Y-m-d'], + 'tax_value' => ['required', 'numeric', 'regex:/^\d{1,16}(\.\d{1,2})?$/'], + 'subdistrict' => ['required', 'string'], + 'village' => ['required', 'string'], + ]; + } +} diff --git a/build.zip b/build.zip index 9701ce1..c23f58c 100644 Binary files a/build.zip and b/build.zip differ diff --git a/resources/js/taxation/edit.js b/resources/js/taxation/edit.js new file mode 100644 index 0000000..42153ed --- /dev/null +++ b/resources/js/taxation/edit.js @@ -0,0 +1,66 @@ +class UpdateTax { + constructor() { + this.initUpdateCustomer(); + } + + initUpdateCustomer() { + const toastNotification = document.getElementById("toastNotification"); + const toast = new bootstrap.Toast(toastNotification); + let menuId = document.getElementById("menuId").value; + document + .getElementById("btnUpdateTax") + .addEventListener("click", async function () { + let submitButton = this; + let spinner = document.getElementById("spinner"); + let form = document.getElementById("formUpdateTax"); + + 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 = `/tax?menu_id=${menuId}`; + }, 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 UpdateTax(); +}); diff --git a/resources/js/taxation/index.js b/resources/js/taxation/index.js index 4a50836..fc61391 100644 --- a/resources/js/taxation/index.js +++ b/resources/js/taxation/index.js @@ -1,7 +1,9 @@ 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 { addThousandSeparators } from "../global-config"; +import Swal from "sweetalert2"; class Taxation { constructor() { @@ -11,7 +13,7 @@ class Taxation { this.table = null; this.initTableTaxation(); - // this.initEvents(); + this.initEvents(); this.handleExportToExcel(); } @@ -66,11 +68,64 @@ class Taxation { } initEvents() { - document.body.addEventListener("click", async (event) => { + document.body.addEventListener("click", (event) => { const deleteButton = event.target.closest(".btn-delete-taxation"); if (deleteButton) { event.preventDefault(); - await this.handleDelete(deleteButton); + this.handleDelete(deleteButton); + } + }); + } + + handleDelete(deleteButton) { + const id = deleteButton.getAttribute("data-id"); + + 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!", + }).then((result) => { + if (result.isConfirmed) { + fetch(`${GlobalConfig.apiHost}/api/taxs/${id}`, { + method: "DELETE", + credentials: "include", + headers: { + Authorization: `Bearer ${document + .querySelector('meta[name="api-token"]') + .getAttribute("content")}`, + "Content-Type": "application/json", + }, + }) + .then((response) => { + if (response.ok) { + return response.json(); + } else { + return response.json().then((error) => { + throw new Error( + error.message || "Delete failed!" + ); + }); + } + }) + .then((result) => { + this.toastMessage.innerText = + result.message || "Data deleted successfully!"; + this.toast.show(); + + if (typeof this.table !== "undefined") { + this.table.updateConfig({}).forceRender(); + } + }) + .catch((error) => { + console.error("Error deleting item:", error); + this.toastMessage.innerText = + error.message || "An error occurred!"; + this.toast.show(); + }); } }); } @@ -86,6 +141,10 @@ class Taxation { // Clear previous table content tableContainer.innerHTML = ""; + let canUpdate = tableContainer.getAttribute("data-updater") === "1"; + let canDelete = tableContainer.getAttribute("data-destroyer") === "1"; + let menuId = tableContainer.getAttribute("data-menuId"); + this.table = new Grid({ columns: [ "ID", @@ -99,6 +158,33 @@ class Taxation { { name: "Tax Value" }, { name: "Subdistrict" }, { name: "Village" }, + { + name: "Action", + formatter: (cell) => { + let buttons = ""; + if (canUpdate) { + buttons += ` + + + + `; + } + if (canDelete) { + buttons += ` + + `; + } + if (!canUpdate && !canDelete) { + buttons = `No Privilege`; + } + + return gridjs.html( + `
${buttons}
` + ); + }, + }, ], pagination: { limit: 50, @@ -148,6 +234,7 @@ class Taxation { addThousandSeparators(item.tax_value), item.subdistrict, item.village, + item.id, ]; }); }, diff --git a/resources/views/taxation/edit.blade.php b/resources/views/taxation/edit.blade.php new file mode 100644 index 0000000..5f281db --- /dev/null +++ b/resources/views/taxation/edit.blade.php @@ -0,0 +1,34 @@ +@extends('layouts.vertical', ['subtitle' => 'Data']) + +@section('content') + +@include('layouts.partials/page-title', ['title' => 'Data', 'subtitle' => 'PDAM']) + + + +
+
+
+
+ Back +
+
+
+ @csrf + @method('put') + @include('taxation.form') + +
+
+
+
+
+ +@endsection + +@section('scripts') +@vite(['resources/js/taxation/edit.js']) +@endsection diff --git a/resources/views/taxation/form.blade.php b/resources/views/taxation/form.blade.php new file mode 100644 index 0000000..80e2092 --- /dev/null +++ b/resources/views/taxation/form.blade.php @@ -0,0 +1,40 @@ +
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
\ No newline at end of file diff --git a/resources/views/taxation/index.blade.php b/resources/views/taxation/index.blade.php index 599ba6c..c38cb33 100644 --- a/resources/views/taxation/index.blade.php +++ b/resources/views/taxation/index.blade.php @@ -21,10 +21,14 @@
- Upload + @if ($creator) + Upload + @endif
-
+
diff --git a/routes/api.php b/routes/api.php index 430c21e..243a644 100644 --- a/routes/api.php +++ b/routes/api.php @@ -198,6 +198,8 @@ Route::group(['middleware' => 'auth:sanctum'], function (){ Route::get('/taxs', 'index')->name('api.taxs'); Route::post('/taxs/upload', 'upload')->name('api.taxs.upload'); Route::get('/taxs/export', 'export')->name('api.taxs.export'); + Route::delete('/taxs/{id}', 'delete')->name('api.taxs.delete'); + Route::put('/taxs/{id}', 'update')->name('api.taxs.update'); }); // TODO: Implement new retribution calculation API endpoints using the new schema diff --git a/routes/web.php b/routes/web.php index 8b9e8fa..0bc2d67 100644 --- a/routes/web.php +++ b/routes/web.php @@ -192,5 +192,6 @@ Route::group(['middleware' => 'auth'], function(){ Route::group(['prefix' => '/tax'], function (){ Route::get('/', [TaxationController::class, 'index'])->name('taxation'); Route::get('/upload', [TaxationController::class, 'upload'])->name('taxation.upload'); + Route::get('/{id}/edit', [TaxationController::class, 'edit'])->name('taxation.edit'); }); }); \ No newline at end of file diff --git a/vite.config.js b/vite.config.js index b12f43b..4f566ef 100644 --- a/vite.config.js +++ b/vite.config.js @@ -151,6 +151,7 @@ export default defineConfig({ // taxation "resources/js/taxation/index.js", "resources/js/taxation/upload.js", + "resources/js/taxation/edit.js", ], refresh: true, }),