fix js roles and add back button on card header

This commit is contained in:
arifal hidayat
2025-02-23 07:08:50 +07:00
parent d49035ce8d
commit c3c7b8e3ec
14 changed files with 252 additions and 233 deletions

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

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

@@ -1,9 +1,9 @@
class UpdateCustomer { class UpdateCustomer {
constructor() { constructor() {
this.initUpdateSpatial(); this.initUpdateCustomer();
} }
initUpdateSpatial() { initUpdateCustomer() {
const toastNotification = document.getElementById("toastNotification"); const toastNotification = document.getElementById("toastNotification");
const toast = new bootstrap.Toast(toastNotification); const toast = new bootstrap.Toast(toastNotification);
document document

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

@@ -8,6 +8,9 @@
<div class="row d-flex justify-content-center"> <div class="row d-flex justify-content-center">
<div class="col-lg-6"> <div class="col-lg-6">
<div class="card"> <div class="card">
<div class="card-header d-flex justify-content-end">
<a href="{{ route('data-settings.index') }}" class="btn btn-sm btn-secondary">Back</a>
</div>
<div class="card-body"> <div class="card-body">
<form id="formDataSettings" action="{{ route('api.data-settings.store') }}" method="POST"> <form id="formDataSettings" action="{{ route('api.data-settings.store') }}" method="POST">
@csrf @csrf

View File

@@ -8,6 +8,9 @@
<div class="row d-flex justify-content-center"> <div class="row d-flex justify-content-center">
<div class="col-lg-6"> <div class="col-lg-6">
<div class="card"> <div class="card">
<div class="card-header d-flex justify-content-end">
<a href="{{ route('data-settings.index') }}" class="btn btn-sm btn-secondary">Back</a>
</div>
<div class="card-body"> <div class="card-body">
<form id="formUpdateDataSettings" action="{{ route('api.data-settings.update', $data->id) }}" method="POST"> <form id="formUpdateDataSettings" action="{{ route('api.data-settings.update', $data->id) }}" method="POST">
@csrf @csrf

View File

@@ -12,6 +12,9 @@
<div class="row d-flex justify-content-center"> <div class="row d-flex justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<div class="card"> <div class="card">
<div class="card-header d-flex justify-content-end">
<a href="{{ route('menus.index') }}" class="btn btn-sm btn-secondary">Back</a>
</div>
<div class="card-body"> <div class="card-body">
<form id="formCreateMenus" action="{{route("api.menus.store")}}" method="post"> <form id="formCreateMenus" action="{{route("api.menus.store")}}" method="post">
@csrf @csrf

View File

@@ -12,6 +12,9 @@
<div class="row d-flex justify-content-center"> <div class="row d-flex justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<div class="card"> <div class="card">
<div class="card-header d-flex justify-content-end">
<a href="{{ route('menus.index') }}" class="btn btn-sm btn-secondary">Back</a>
</div>
<div class="card-body"> <div class="card-body">
<form id="formUpdateMenus" action="{{route("api.menus.update", $menu->id)}}" method="post"> <form id="formUpdateMenus" action="{{route("api.menus.update", $menu->id)}}" method="post">
@csrf @csrf

View File

@@ -8,8 +8,11 @@
<div class="row d-flex justify-content-center"> <div class="row d-flex justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<div class="card"> <div class="card">
<div class="card-header d-flex justify-content-end">
<a href="{{ route('roles.index') }}" class="btn btn-sm btn-secondary">Back</a>
</div>
<div class="card-body"> <div class="card-body">
<form action="{{route("roles.store")}}" method="post" id="formCreateRole" data-redirect="{{route("roles.index")}}"> <form action="{{route("api.roles.store")}}" method="post" id="formCreateRole" data-redirect="{{route("roles.index")}}">
@csrf @csrf
<div class="mb-3"> <div class="mb-3">
<label class="form-label" for="name">Name</label> <label class="form-label" for="name">Name</label>

View File

@@ -8,8 +8,11 @@
<div class="row d-flex justify-content-center"> <div class="row d-flex justify-content-center">
<div class="col-md-6"> <div class="col-md-6">
<div class="card"> <div class="card">
<div class="card-header d-flex justify-content-end">
<a href="{{ route('roles.index') }}" class="btn btn-sm btn-secondary">Back</a>
</div>
<div class="card-body"> <div class="card-body">
<form id="formUpdateRole" action="{{route("roles.update", $role->id)}}" method="post" > <form id="formUpdateRole" action="{{route("api.roles.update", $role->id)}}" method="post" >
@csrf @csrf
@method("put") @method("put")
<div class="mb-3"> <div class="mb-3">

View File

@@ -7,6 +7,9 @@
<div class="row d-flex justify-content-center"> <div class="row d-flex justify-content-center">
<div class="col-md-12"> <div class="col-md-12">
<div class="card"> <div class="card">
<div class="card-header d-flex justify-content-end">
<a href="{{ route('roles.index') }}" class="btn btn-sm btn-secondary">Back</a>
</div>
<div class="card-body"> <div class="card-body">
<h5>Manage Permissions for Role: {{ $role->name }}</h5> <h5>Manage Permissions for Role: {{ $role->name }}</h5>
<form action="{{route("role-menu.permission.update", $role->id)}}" method="post"> <form action="{{route("role-menu.permission.update", $role->id)}}" method="post">

View File

@@ -108,7 +108,12 @@ Route::group(['middleware' => 'auth:sanctum'], function (){
}); });
// roles api // roles api
Route::apiResource('api-roles', RolesController::class); Route::controller(RolesController::class)->group(function (){
Route::get('/roles', 'index')->name('api.roles');
Route::post('/roles', 'store')->name('api.roles.store');
Route::put('/roles/{role_id}', 'update')->name('api.roles.update');
Route::delete('/roles/{role_id}', 'destroy')->name('api.roles.destroy');
});
//business industries api //business industries api
Route::apiResource('api-business-industries', BusinessOrIndustriesController::class); Route::apiResource('api-business-industries', BusinessOrIndustriesController::class);