diff --git a/app/Http/Controllers/RolesController.php b/app/Http/Controllers/RolesController.php index ef78ba6..4db0026 100644 --- a/app/Http/Controllers/RolesController.php +++ b/app/Http/Controllers/RolesController.php @@ -35,18 +35,18 @@ class RolesController extends Controller { try{ $request->validate([ - "name" => "required", + "name" => "required|unique:roles,name", "description" => "nullable", ]); DB::beginTransaction(); Role::create($request->all()); DB::commit(); - return redirect()->route("roles.index")->with('success','Succesfully Created'); + return response()->json(['message' => 'Role created successfully'], 201); } catch(\Exception $e){ DB::rollBack(); - return redirect()->back()->with("error", $e->getMessage()); + return response()->json(['message' => $e->getMessage()], 500); } } @@ -83,10 +83,10 @@ class RolesController extends Controller DB::beginTransaction(); $role->update($validatedData); DB::commit(); - return redirect()->route('roles.index')->with('success','Successfully updated'); + return response()->json(['message' => 'Role updated successfully'], 200); }catch(\Exception $e){ DB::rollBack(); - return redirect()->back()->with("error", $e->getMessage()); + return response()->json(['message' => $e->getMessage()], 500); } } @@ -97,11 +97,13 @@ class RolesController extends Controller { try{ DB::beginTransaction(); - Role::findOrFail($id)->delete(); + $deleted = Role::findOrFail($id)->delete(); + info("deleted". $deleted); DB::commit(); + return response()->json(array('success' => true, "message" => "Successfully deleted")); }catch(\Exception $e){ DB::rollBack(); - return redirect()->back()->with("error", $e->getMessage()); + return response()->json(array('success' => false, "message" => $e->getMessage())); } } diff --git a/app/View/Components/ModalConfirmation.php b/app/View/Components/ModalConfirmation.php new file mode 100644 index 0000000..ed2f6bf --- /dev/null +++ b/app/View/Components/ModalConfirmation.php @@ -0,0 +1,25 @@ +menus()->sync([ - $dashboard->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], - $master->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], - $settings->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], - $dataSettings->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], - $data->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], + // parent + $dashboard->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false], + $master->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false], + $settings->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false], + $dataSettings->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false], + $data->id => ["allow_show" => true, "allow_create" => false, "allow_update" => false, "allow_destroy" => false], + // children $dashboard_pimpinan->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], $dashboard_pbg->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], $users->id => ["allow_show" => true, "allow_create" => true, "allow_update" => true, "allow_destroy" => true], diff --git a/resources/js/roles/create.js b/resources/js/roles/create.js new file mode 100644 index 0000000..243d0b0 --- /dev/null +++ b/resources/js/roles/create.js @@ -0,0 +1,55 @@ +document.addEventListener("DOMContentLoaded", function (e) { + const toastNotification = document.getElementById("toastNotification"); + const toast = new bootstrap.Toast(toastNotification); + document + .getElementById("btnCreateRole") + .addEventListener("click", async function () { + let submitButton = this; + let spinner = document.getElementById("spinner"); + let form = document.getElementById("formCreateRole"); + + 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: { + "X-CSRF-TOKEN": document + .querySelector('meta[name="csrf-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); + } + } catch (error) { + console.error("Request failed:", error); + document.getElementById("toast-message").innerText = + error.message; + toast.show(); + } + }); +}); diff --git a/resources/js/roles/index.js b/resources/js/roles/index.js index 617d8ba..ed5f4d2 100644 --- a/resources/js/roles/index.js +++ b/resources/js/roles/index.js @@ -67,11 +67,37 @@ class Roles { handleDelete(event) { if (event.target.classList.contains("btn-delete-role")) { event.preventDefault(); - const id = event.target.getAttribute("data-id"); - if (confirm("Are you sure you want to delete this item?")) { - fetch(`/roles/${id}`, { + const id = event.target.getAttribute("data-id"); + let modalElement = document.getElementById("modalConfirmation"); + + if (!modalElement) { + console.error("Modal element not found!"); + return; + } + + let modal = new bootstrap.Modal(modalElement); + + // Set the role ID on the confirm button inside the modal + let btnSaveConfirmation = document.getElementById( + "btnSaveConfirmation" + ); + btnSaveConfirmation.setAttribute("data-role-id", id); + + let toastElement = document.getElementById("toastNotificationApi"); + let toast = new bootstrap.Toast(toastElement); + + // Show the modal + modal.show(); + + // Prevent multiple event listeners + btnSaveConfirmation.onclick = function () { + let roleId = this.getAttribute("data-role-id"); + console.log("Deleted ID:", roleId); + + fetch(`/roles/${roleId}`, { method: "DELETE", + credentials: "include", headers: { "X-CSRF-TOKEN": document .querySelector('meta[name="csrf-token"]') @@ -81,21 +107,21 @@ class Roles { }) .then((response) => { if (response.ok) { - alert("Item deleted successfully!"); - window.location.reload(); + modal.hide(); + toast.show(); + setTimeout(() => window.location.reload(), 1500); } else { return response.json().then((error) => { - throw new Error( - error.message || "Failed to delete item." - ); + console.error("Delete failed:", error); + toast.show(); }); } }) .catch((error) => { console.error("Error deleting item:", error); - alert("Something went wrong. Please try again."); + toast.show(); }); - } + }; } } } diff --git a/resources/js/roles/update.js b/resources/js/roles/update.js new file mode 100644 index 0000000..5753448 --- /dev/null +++ b/resources/js/roles/update.js @@ -0,0 +1,53 @@ +document.addEventListener("DOMContentLoaded", function (e) { + let form = document.getElementById("formUpdateRole"); + let submitButton = document.getElementById("btnUpdateRole"); + 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) { + 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: { + "X-CSRF-TOKEN": document + .querySelector('meta[name="csrf-token"]') + .getAttribute("content"), + }, + body: formData, + }); + + if (response.ok) { + let result = await response.json(); + 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(); + } + }); +}); diff --git a/resources/scss/structure/_general.scss b/resources/scss/structure/_general.scss index 945cdcf..1a56581 100755 --- a/resources/scss/structure/_general.scss +++ b/resources/scss/structure/_general.scss @@ -3,128 +3,141 @@ // .app-wrapper { - height: 100%; - margin: 0 auto; - position: relative; + height: 100%; + margin: 0 auto; + position: relative; } // Main Content .page-content { - position: relative; - transition: all 0.3s ease-in-out; - min-height: calc(100vh - $topbar-height); - padding: calc($spacer * 1) calc($spacer * 0.5) $footer-height calc($spacer * 0.5); - margin-left: $sidebar-width; + position: relative; + transition: all 0.3s ease-in-out; + min-height: calc(100vh - $topbar-height); + padding: calc($spacer * 1) calc($spacer * 0.5) $footer-height + calc($spacer * 0.5); + margin-left: $sidebar-width; } - // Animated Star $shooting-time: 3000ms; .animated-stars { - position: relative; - width: 100%; - height: 100%; - transform: rotateZ(45deg); + position: relative; + width: 100%; + height: 100%; + transform: rotateZ(45deg); } .shooting-star { - position: absolute; - left: 50%; - top: 50%; - height: 2px; - background: linear-gradient(-45deg, rgba(95, 145, 255, 1), rgba(0, 0, 255, 0)); - border-radius: 999px; - filter: drop-shadow(0 0 6px rgba(105, 155, 255, 1)); - animation: - tail $shooting-time ease-in-out infinite, - shooting $shooting-time ease-in-out infinite; + position: absolute; + left: 50%; + top: 50%; + height: 2px; + background: linear-gradient( + -45deg, + rgba(95, 145, 255, 1), + rgba(0, 0, 255, 0) + ); + border-radius: 999px; + filter: drop-shadow(0 0 6px rgba(105, 155, 255, 1)); + animation: tail $shooting-time ease-in-out infinite, + shooting $shooting-time ease-in-out infinite; - &::before { - content: ''; - position: absolute; - top: calc(50% - 1px); - right: 0; - height: 2px; - background: linear-gradient(-45deg, rgba(0, 0, 255, 0), rgba(95, 145, 255, 1), rgba(0, 0, 255, 0)); - transform: translateX(50%) rotateZ(45deg); - border-radius: 100%; - animation: shining $shooting-time ease-in-out infinite; - } + &::before { + content: ""; + position: absolute; + top: calc(50% - 1px); + right: 0; + height: 2px; + background: linear-gradient( + -45deg, + rgba(0, 0, 255, 0), + rgba(95, 145, 255, 1), + rgba(0, 0, 255, 0) + ); + transform: translateX(50%) rotateZ(45deg); + border-radius: 100%; + animation: shining $shooting-time ease-in-out infinite; + } - &::after { - content: ''; - position: absolute; - top: calc(50% - 1px); - right: 0; - height: 2px; - background: linear-gradient(-45deg, rgba(0, 0, 255, 0), rgba(95, 145, 255, 1), rgba(0, 0, 255, 0)); - transform: translateX(50%) rotateZ(45deg); - border-radius: 100%; - animation: shining $shooting-time ease-in-out infinite; - transform: translateX(50%) rotateZ(-45deg); - } + &::after { + content: ""; + position: absolute; + top: calc(50% - 1px); + right: 0; + height: 2px; + background: linear-gradient( + -45deg, + rgba(0, 0, 255, 0), + rgba(95, 145, 255, 1), + rgba(0, 0, 255, 0) + ); + transform: translateX(50%) rotateZ(45deg); + border-radius: 100%; + animation: shining $shooting-time ease-in-out infinite; + transform: translateX(50%) rotateZ(-45deg); + } - @for $i from 1 through 20 { - &:nth-child(#{$i}) { - $delay: random(9999) + 0ms; - top: calc(50% - #{random(400) - 200px}); - left: calc(50% - #{random(300) + 0px}); - animation-delay: $delay; - // opacity: random(50) / 100 + 0.5; + @for $i from 1 through 20 { + &:nth-child(#{$i}) { + $delay: random(9999) + 0ms; + top: calc(50% - #{random(400) - 200px}); + left: calc(50% - #{random(300) + 0px}); + animation-delay: $delay; + // opacity: random(50) / 100 + 0.5; - &::before, - &::after { - animation-delay: $delay; - } - } - } + &::before, + &::after { + animation-delay: $delay; + } + } + } } @keyframes tail { - 0% { - width: 0; - } + 0% { + width: 0; + } - 30% { - width: 100px; - } + 30% { + width: 100px; + } - 100% { - width: 0; - } + 100% { + width: 0; + } } @keyframes shining { - 0% { - width: 0; - } + 0% { + width: 0; + } - 50% { - width: 30px; - } + 50% { + width: 30px; + } - 100% { - width: 0; - } + 100% { + width: 0; + } } @keyframes shooting { - 0% { - transform: translateX(0); - } + 0% { + transform: translateX(0); + } - 100% { - transform: translateX(300px); - } + 100% { + transform: translateX(300px); + } } @keyframes sky { - 0% { - transform: rotate(45deg); - } + 0% { + transform: rotate(45deg); + } - 100% { - transform: rotate(45 + 360deg); - } -} \ No newline at end of file + 100% { + transform: rotate(45 + 360deg); + } +} diff --git a/resources/views/components/modal-confirmation.blade.php b/resources/views/components/modal-confirmation.blade.php new file mode 100644 index 0000000..0a47b3f --- /dev/null +++ b/resources/views/components/modal-confirmation.blade.php @@ -0,0 +1,17 @@ +@props(['buttonText' => 'Confirm', 'confirmationMessage' => 'Are you sure?']) + +
\ No newline at end of file diff --git a/resources/views/components/toast-notification.blade.php b/resources/views/components/toast-notification.blade.php new file mode 100644 index 0000000..16d273d --- /dev/null +++ b/resources/views/components/toast-notification.blade.php @@ -0,0 +1,14 @@ +