fix business industries and data settings front end

This commit is contained in:
arifal hidayat
2025-02-22 16:08:59 +07:00
parent 675477c734
commit ffd9d3514c
11 changed files with 171 additions and 327 deletions

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",

2
package-lock.json generated
View File

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

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

@@ -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";

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";

View File

@@ -16,7 +16,7 @@
<div class="card w-100"> <div class="card w-100">
<div class="card-body"> <div class="card-body">
<div class="d-flex flex-wrap justify-content-end align-items-center mb-2"> <div class="d-flex flex-wrap justify-content-end align-items-center mb-2">
<a href="{{ route('business-industries.create')}}" class="btn btn-success btn-sm d-block d-sm-inline w-auto">Create</a> <a href="{{ route('business-industries.create')}}" class="btn btn-success btn-sm d-block d-sm-inline w-auto">Upload</a>
</div> </div>
<div id="table-business-industries"></div> <div id="table-business-industries"></div>
</div> </div>

View File

@@ -9,7 +9,7 @@
<div class="col-lg-6"> <div class="col-lg-6">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<form id="formDataSettings" action="{{ route('data-settings.store') }}" method="POST"> <form id="formDataSettings" action="{{ route('api.data-settings.store') }}" method="POST">
@csrf @csrf
<div class="mb-3"> <div class="mb-3">
<label for="key" class="form-label">Key</label> <label for="key" class="form-label">Key</label>

View File

@@ -9,7 +9,7 @@
<div class="col-lg-6"> <div class="col-lg-6">
<div class="card"> <div class="card">
<div class="card-body"> <div class="card-body">
<form id="formUpdateDataSettings" action="{{ route('data-settings.update', $data->id) }}" method="POST"> <form id="formUpdateDataSettings" action="{{ route('api.data-settings.update', $data->id) }}" method="POST">
@csrf @csrf
@method('PUT') @method('PUT')
<div class="mb-3"> <div class="mb-3">

View File

@@ -9,7 +9,6 @@
@include('layouts.partials/page-title', ['title' => 'Data Settings', 'subtitle' => 'Setting Dashboard']) @include('layouts.partials/page-title', ['title' => 'Data Settings', 'subtitle' => 'Setting Dashboard'])
<x-toast-notification /> <x-toast-notification />
<x-modal-confirmation buttonText="Delete" confirmationMessage="Are you sure you want to delete this?" />
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">

View File

@@ -81,7 +81,13 @@ Route::group(['middleware' => 'auth:sanctum'], function (){
Route::get('/download-template-spatialPlannings', [SpatialPlanningController::class, 'downloadExcelSpatialPlanning']); Route::get('/download-template-spatialPlannings', [SpatialPlanningController::class, 'downloadExcelSpatialPlanning']);
// data-settings // data-settings
Route::apiResource('/api-data-settings', DataSettingController::class); // Route::apiResource('/api-data-settings', DataSettingController::class);
Route::controller(DataSettingController::class)->group(function (){
Route::get('/data-settings', 'index')->name('api.data-settings');
Route::post('/data-settings', 'store')->name('api.data-settings.store');
Route::put('/data-settings/{data_setting_id}', 'update')->name('api.data-settings.update');
Route::delete('/data-settings/{data_setting_id}', 'destroy')->name('api.data-settings.destroy');
});
Route::apiResource('/api-pbg-task', PbgTaskController::class); Route::apiResource('/api-pbg-task', PbgTaskController::class);