From 1a15bc03f83fe7904481fa2f6a8a967533ce3308 Mon Sep 17 00:00:00 2001 From: arifal Date: Tue, 11 Feb 2025 23:40:31 +0700 Subject: [PATCH] create loading and handle from js create edit and delete roles --- app/Http/Controllers/RolesController.php | 16 +- app/View/Components/ModalConfirmation.php | 25 +++ app/View/Components/ToastNotification.php | 25 +++ database/seeders/UsersRoleMenuSeeder.php | 12 +- resources/js/roles/create.js | 55 +++++ resources/js/roles/index.js | 46 ++++- resources/js/roles/update.js | 53 +++++ resources/scss/structure/_general.scss | 193 ++++++++++-------- .../components/modal-confirmation.blade.php | 17 ++ .../components/toast-notification.blade.php | 14 ++ resources/views/data-settings/index.blade.php | 12 +- resources/views/master/users/index.blade.php | 13 +- resources/views/menus/index.blade.php | 14 +- resources/views/pbg_task/index.blade.php | 12 +- resources/views/roles/create.blade.php | 12 +- resources/views/roles/edit.blade.php | 11 +- resources/views/roles/index.blade.php | 15 +- .../views/settings/general/index.blade.php | 12 +- .../views/settings/syncronize/index.blade.php | 16 +- vite.config.js | 2 + 20 files changed, 422 insertions(+), 153 deletions(-) create mode 100644 app/View/Components/ModalConfirmation.php create mode 100644 app/View/Components/ToastNotification.php create mode 100644 resources/js/roles/create.js create mode 100644 resources/js/roles/update.js create mode 100644 resources/views/components/modal-confirmation.blade.php create mode 100644 resources/views/components/toast-notification.blade.php 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 @@ +
+ +
\ No newline at end of file diff --git a/resources/views/data-settings/index.blade.php b/resources/views/data-settings/index.blade.php index 16bfd11..202aa77 100644 --- a/resources/views/data-settings/index.blade.php +++ b/resources/views/data-settings/index.blade.php @@ -9,11 +9,13 @@ @include('layouts.partials/page-title', ['title' => 'Data Settings', 'subtitle' => 'Setting Dashboard'])
-
- Create -
-
-
+
+
+
+ Create +
+
+
diff --git a/resources/views/master/users/index.blade.php b/resources/views/master/users/index.blade.php index 664d784..ea0c486 100644 --- a/resources/views/master/users/index.blade.php +++ b/resources/views/master/users/index.blade.php @@ -9,12 +9,13 @@ @include('layouts.partials/page-title', ['title' => 'Master', 'subtitle' => 'Users'])
-
- Create -
- {{$users}} -
-
+
+
+
+ Create +
+
+
diff --git a/resources/views/menus/index.blade.php b/resources/views/menus/index.blade.php index 481189b..06294b5 100644 --- a/resources/views/menus/index.blade.php +++ b/resources/views/menus/index.blade.php @@ -9,11 +9,15 @@ @include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Menu'])
-
- Create -
-
-
+
+
+
+ Create +
+
+
+
+
diff --git a/resources/views/pbg_task/index.blade.php b/resources/views/pbg_task/index.blade.php index b40bc0e..414632f 100644 --- a/resources/views/pbg_task/index.blade.php +++ b/resources/views/pbg_task/index.blade.php @@ -9,11 +9,13 @@ @include('layouts.partials/page-title', ['title' => 'Data', 'subtitle' => 'PBG'])
-
- Create -
-
-
+
+
+
+ Create +
+
+
diff --git a/resources/views/roles/create.blade.php b/resources/views/roles/create.blade.php index 7b9627f..403f4e4 100644 --- a/resources/views/roles/create.blade.php +++ b/resources/views/roles/create.blade.php @@ -4,11 +4,12 @@ @include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Role']) +
-
+ @csrf
@@ -20,7 +21,10 @@
- +
@@ -28,3 +32,7 @@
@endsection + +@section('scripts') +@vite(['resources/js/roles/create.js']) +@endsection diff --git a/resources/views/roles/edit.blade.php b/resources/views/roles/edit.blade.php index 2c4c073..e96b140 100644 --- a/resources/views/roles/edit.blade.php +++ b/resources/views/roles/edit.blade.php @@ -4,11 +4,12 @@ @include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Role']) +
-
id)}}" method="post"> + id)}}" method="post" > @csrf @method("put")
@@ -21,7 +22,10 @@
- +
@@ -29,3 +33,6 @@
@endsection +@section('scripts') +@vite(['resources/js/roles/update.js']) +@endsection diff --git a/resources/views/roles/index.blade.php b/resources/views/roles/index.blade.php index b2bf211..a8c0d4d 100644 --- a/resources/views/roles/index.blade.php +++ b/resources/views/roles/index.blade.php @@ -8,12 +8,17 @@ @include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Role']) + + +
-
- Create -
-
-
+
+
+
+ Create +
+
+
diff --git a/resources/views/settings/general/index.blade.php b/resources/views/settings/general/index.blade.php index 93cefed..d862d46 100644 --- a/resources/views/settings/general/index.blade.php +++ b/resources/views/settings/general/index.blade.php @@ -9,11 +9,13 @@ @include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'General'])
-
- Create -
-
-
+
+
+
+ Create +
+
+
diff --git a/resources/views/settings/syncronize/index.blade.php b/resources/views/settings/syncronize/index.blade.php index af98e2f..ab83860 100644 --- a/resources/views/settings/syncronize/index.blade.php +++ b/resources/views/settings/syncronize/index.blade.php @@ -9,12 +9,16 @@ @include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Syncronize'])
-
- - -
-
-
+
+
+
+ + +
+
+
+
+
diff --git a/vite.config.js b/vite.config.js index 5d3fcab..154488a 100755 --- a/vite.config.js +++ b/vite.config.js @@ -54,6 +54,8 @@ export default defineConfig({ "resources/js/tables/common-table.js", "resources/js/menus/index.js", "resources/js/roles/index.js", + "resources/js/roles/create.js", + "resources/js/roles/update.js", "resources/roles/role_menu.js", ], refresh: true,