create user role and menu, create seeder for first user and create crud role, menu and user

This commit is contained in:
arifal
2025-02-11 02:35:53 +07:00
parent 6307417ae3
commit cb90f69d1e
37 changed files with 1326 additions and 151 deletions

View File

@@ -17,6 +17,15 @@ class UsersTable {
"Firstname",
"Lastname",
"Position",
"Roles",
{
name: "Action",
formatter: (cell) =>
gridjs.html(`
<div class="d-flex justify-content-end gap-x-2">
<a href="/master/users/${cell}/edit" class="btn btn-yellow me-2">Update</a>
`),
},
],
pagination: {
limit: 15,
@@ -42,15 +51,19 @@ class UsersTable {
.getAttribute("content")}`,
"Content-Type": "application/json",
},
then: (data) =>
data.data.map((item) => [
then: (data) => {
console.log(data.data);
return data.data.map((item) => [
item.id,
item.name,
item.email,
item.firstname,
item.lastname,
item.position,
]),
item.roles,
item.id,
]);
},
total: (data) => data.meta.total,
},
}).render(document.getElementById("table-users"));

109
resources/js/menus/index.js Normal file
View File

@@ -0,0 +1,109 @@
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";
class Menus {
init() {
this.initTableMenus();
}
initTableMenus() {
new Grid({
columns: [
"ID",
"Name",
"Url",
"Icon",
"ParentID",
"Sort Order",
{
name: "Action",
formatter: (cell) =>
gridjs.html(`
<div class="d-flex justify-content-end gap-x-2">
<a href="/menus/${cell}/edit" class="btn btn-yellow me-2">Update</a>
<button class="btn btn-red btn-delete btn-delete-menu" data-id="${cell}">Delete</button>
`),
},
],
pagination: {
limit: 15,
server: {
url: (prev, page) =>
`${prev}${prev.includes("?") ? "&" : "?"}page=${
page + 1
}`,
},
},
sort: true,
search: {
server: {
url: (prev, keyword) => `${prev}?search=${keyword}`,
},
},
server: {
url: `${GlobalConfig.apiHost}/api/api-menus`,
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.url,
item.icon,
item.parent_id,
item.sort_order,
item.id,
]),
total: (data) => data.total,
},
}).render(document.getElementById("table-menus"));
document.addEventListener("click", this.handleDelete);
}
handleDelete(event) {
if (event.target.classList.contains("btn-delete-menu")) {
event.preventDefault();
const id = event.target.getAttribute("data-id");
if (confirm("Are you sure you want to delete this item?")) {
fetch(`/menus/${id}`, {
method: "DELETE",
headers: {
"X-CSRF-TOKEN": document
.querySelector('meta[name="csrf-token"]')
.getAttribute("content"),
"Content-Type": "application/json",
},
})
.then((response) => {
if (response.ok) {
alert("Item deleted successfully!");
window.location.reload();
} else {
return response.json().then((error) => {
throw new Error(
error.message || "Failed to delete item."
);
});
}
})
.catch((error) => {
console.error("Error deleting item:", error);
alert("Something went wrong. Please try again.");
});
}
}
}
}
document.addEventListener("DOMContentLoaded", function (e) {
new Menus().init();
});

103
resources/js/roles/index.js Normal file
View File

@@ -0,0 +1,103 @@
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";
class Roles {
init() {
this.initTableRoles();
}
initTableRoles() {
new Grid({
columns: [
"ID",
"Name",
"Description",
{
name: "Action",
formatter: (cell) =>
gridjs.html(`
<div class="d-flex justify-content-end gap-x-2">
<a href="/roles/${cell}/edit" class="btn btn-yellow me-2">Update</a>
<button class="btn btn-red btn-delete btn-delete-role" data-id="${cell}">Delete</button>
`),
},
],
pagination: {
limit: 15,
server: {
url: (prev, page) =>
`${prev}${prev.includes("?") ? "&" : "?"}page=${
page + 1
}`,
},
},
sort: true,
search: {
server: {
url: (prev, keyword) => `${prev}?search=${keyword}`,
},
},
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,
},
}).render(document.getElementById("table-roles"));
document.addEventListener("click", this.handleDelete);
}
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}`, {
method: "DELETE",
headers: {
"X-CSRF-TOKEN": document
.querySelector('meta[name="csrf-token"]')
.getAttribute("content"),
"Content-Type": "application/json",
},
})
.then((response) => {
if (response.ok) {
alert("Item deleted successfully!");
window.location.reload();
} else {
return response.json().then((error) => {
throw new Error(
error.message || "Failed to delete item."
);
});
}
})
.catch((error) => {
console.error("Error deleting item:", error);
alert("Something went wrong. Please try again.");
});
}
}
}
}
document.addEventListener("DOMContentLoaded", function (e) {
new Roles().init();
});

View File

@@ -13,11 +13,11 @@ class="authentication-bg"
<div class="card-body p-5">
<div class="text-center">
<div class="mx-auto mb-4 text-center auth-logo">
<a href="{{ route('home') }}" class="logo-dark">
<a href="{{ route('dashboard.home') }}" class="logo-dark">
<img src="/images/dputr-kab-bandung.png" height="auto" width="100%" alt="logo dark">
</a>
<a href="{{ route('home') }}" class="logo-light">
<a href="{{ route('dashboard.home') }}" class="logo-light">
<img src="/images/dputr-kab-bandung.png" height="auto" width="100%" alt="logo light">
</a>
</div>

View File

@@ -1,114 +1,47 @@
<div class="app-sidebar">
<!-- Sidebar Logo -->
<div class="logo-box">
<a href="{{ route('home') }}" class="logo-dark">
<a href="{{ route('dashboard.home') }}" class="logo-dark">
<img src="/images/dputr-kab-bandung.png" class="logo-sm" alt="logo sm">
<img src="/images/dputr-kab-bandung.png" class="logo-lg" alt="logo dark">
</a>
<a href="{{ route('home') }}" class="logo-light">
<a href="{{ route('dashboard.home') }}" class="logo-light">
<img src="/images/dputr-kab-bandung.png" class="logo-sm" alt="logo sm">
<img src="/images/dputr-kab-bandung.png" class="logo-lg" alt="logo light">
</a>
</div>
<div class="scrollbar" data-simplebar>
<ul class="navbar-nav" id="navbar-nav">
<li class="menu-title">Menu</li>
<li class="nav-item">
<a class="nav-link menu-arrow" href="#sidebarDashboard" data-bs-toggle="collapse" role="button"
aria-expanded="false" aria-controls="sidebarDashboard">
<span class="nav-icon">
<iconify-icon icon="mingcute:home-3-line"></iconify-icon>
</span>
<span class="nav-text"> Dashboards</span>
</a>
<div class="collapse" id="sidebarDashboard">
<ul class="nav sub-navbar-nav">
<li class="sub-nav-item">
<a class="sub-nav-link" href="{{ route ('home' ) }}">Dashboard Pimpinan</a>
</li>
<li class="sub-nav-item">
<a class="sub-nav-link" href="{{ route ('dashboards.pbg' ) }}">Dashboard PBG</a>
</li>
</ul>
</div>
</li>
<li class="nav-item">
<a class="nav-link menu-arrow" href="#sidebarDataMaster" data-bs-toggle="collapse" role="button"
aria-expanded="false" aria-controls="sidebarDataMaster">
<span class="nav-icon">
<iconify-icon icon="mingcute:cylinder-line"></iconify-icon>
</span>
<span class="nav-text">Master</span>
</a>
<div class="collapse" id="sidebarDataMaster">
<ul class="nav sub-navbar-nav">
<li class="sub-nav-item">
<a class="sub-nav-link" href="{{ route ('users.index' ) }}">Users</a>
</li>
</ul>
</div>
</li>
<li class="nav-item">
<a class="nav-link menu-arrow" href="#sidebarSettings" data-bs-toggle="collapse" role="button"
aria-expanded="false" aria-controls="sidebarSettings">
<span class="nav-icon">
<iconify-icon icon="mingcute:settings-6-line"></iconify-icon>
</span>
<span class="nav-text">Settings</span>
</a>
<div class="collapse" id="sidebarSettings">
<ul class="nav sub-navbar-nav">
<li class="sub-nav-item d-none">
<a class="sub-nav-link" href="{{ route ('general.index' ) }}">General</a>
</li>
<li class="sub-nav-item">
<a class="sub-nav-link" href="{{ route ('settings.syncronize' ) }}">Syncronize</a>
</li>
</ul>
</div>
</li>
<li class="nav-item">
<a class="nav-link menu-arrow" href="#dataSettings" data-bs-toggle="collapse" role="button"
aria-expanded="false" aria-controls="dataSettings">
<span class="nav-icon">
<iconify-icon icon="mingcute:settings-1-line"></iconify-icon>
</span>
<span class="nav-text">Data Settings</span>
</a>
<div class="collapse" id="dataSettings">
<ul class="nav sub-navbar-nav">
<li class="sub-nav-item">
<a class="sub-nav-link" href="{{ route ('data-settings.index' ) }}">Setting Dashboard</a>
</li>
</ul>
</div>
</li>
<li class="nav-item">
<a class="nav-link menu-arrow" href="#data" data-bs-toggle="collapse" role="button"
aria-expanded="false" aria-controls="data">
<span class="nav-icon">
<iconify-icon icon="mingcute:task-line"></iconify-icon>
</span>
<span class="nav-text">Data</span>
</a>
<div class="collapse" id="data">
<ul class="nav sub-navbar-nav">
<li class="sub-nav-item">
<a class="sub-nav-link" href="{{ route ('pbg-task.index' ) }}">PBG</a>
</li>
</ul>
</div>
</li>
@foreach ($menus as $menu)
<li class="nav-item">
<!-- parent menu -->
<a class="nav-link menu-arrow" href="#sidebar-{{$menu->id}}" data-bs-toggle="collapse" role="button"
aria-expanded="true" aria-controls="sidebar-{{$menu->id}}">
<span class="nav-icon">
<iconify-icon icon="{{$menu->icon}}"></iconify-icon>
</span>
<span class="nav-text">{{$menu->name}}</span>
</a>
<!-- children menu foreach -->
@if ($menu->children->count() > 0)
<div class="collapse" id="sidebar-{{$menu->id}}">
<ul class="nav sub-navbar-nav">
@foreach ( $menu->children as $child)
<li class="sub-nav-item">
<a class="sub-nav-link" href="{{ $child->url ? (Route::has($child->url) ? route($child->url) : $child->url) : '#' }}">
{{ $child->name }}
</a>
</li>
@endforeach
</ul>
</div>
@endif
</li>
@endforeach
</ul>
</div>
</div>

View File

@@ -172,7 +172,7 @@
class="align-middle me-2 fs-18"></iconify-icon><span
class="align-middle">Help</span>
</a>
<a class="dropdown-item" href="auth-{{ route ('home') }}">
<a class="dropdown-item" href="auth-{{ route ('dashboard.home') }}">
<iconify-icon icon="solar:lock-keyhole-outline"
class="align-middle me-2 fs-18"></iconify-icon><span class="align-middle">Lock
screen</span>

View File

@@ -26,7 +26,7 @@
placeholder="Enter your password" required>
</div>
<div class="mb-3">
<label class="form-label" for="password_confirmation">Password</label>
<label class="form-label" for="password_confirmation">Password Confirmation</label>
<input type="password" id="password_confirmation" class="form-control" name="password_confirmation"
placeholder="Enter your password confirmation" required>
</div>
@@ -45,6 +45,15 @@
<input type="text" id="position" class="form-control" name="position"
placeholder="Enter your position" required>
</div>
<div class="mb-3">
<label class="form-label" for="role">Role</label>
<select name="role_id" id="role" class="form-control">
<option value="">Select Role</option>
@foreach ($roles as $role)
<option value="{{$role->id}}">{{$role->name}}</option>
@endforeach
</select>
</div>
<!-- username, firstname, lastname, position -->
<button type="submit" class="btn btn-success width-lg">Create</button>
</form>

View File

@@ -8,40 +8,47 @@
<div class="col-lg-6">
<div class="card">
<div class="card-body">
<form action="{{ route('master.users.update')}}">
<form action="{{ route('users.update', $user->id)}}" method="post">
@csrf
@method("put")
<div class="mb-3">
<label class="form-label" for="name">Name</label>
<input type="name" id="name" name="name"
class="form-control" placeholder="Enter your name" required>
class="form-control" placeholder="Enter your name" value="{{$user->name}}" required>
</div>
<div class="mb-3">
<label class="form-label" for="email">Email</label>
<input type="email" id="email" name="email"
class="form-control" placeholder="Enter your email" required>
</div>
<div class="mb-3">
<label class="form-label" for="password">Password</label>
<input type="text" id="password" class="form-control" name="password"
placeholder="Enter your password" required>
class="form-control" placeholder="Enter your email" value="{{$user->email}}" required>
</div>
<div class="mb-3">
<label class="form-label" for="firstname">Firstname</label>
<input type="text" id="firstname" class="form-control" name="firstname"
placeholder="Enter your firstname" required>
placeholder="Enter your firstname" value="{{$user->firstname}}" required>
</div>
<div class="mb-3">
<label class="form-label" for="lastname">Lastname</label>
<input type="text" id="lastname" class="form-control" name="lastname"
placeholder="Enter your lastname" required>
placeholder="Enter your lastname" value="{{$user->lastname}}" required>
</div>
<div class="mb-3">
<label class="form-label" for="position">Position</label>
<input type="text" id="position" class="form-control" name="position"
placeholder="Enter your position" required>
placeholder="Enter your position" value="{{$user->position}}" required>
</div>
<div class="mb-3">
<label class="form-label" for="role">Role</label>
<select name="role_id" id="role" class="form-control">
<option value="">Select Role</option>
@foreach($roles as $role)
<option value="{{ $role->id }}" {{ $user->roles->contains($role->id) ? 'selected' : '' }}>
{{ $role->name }}
</option>
@endforeach
</select>
</div>
<!-- username, firstname, lastname, position -->
<button type="submit" class="btn btn-outline-success width-lg">Update</button>
<button type="submit" class="btn btn-success width-lg">Update</button>
</form>
</div>
</div>

View File

@@ -0,0 +1,56 @@
@extends('layouts.vertical', ['subtitle' => 'Menu'])
@section('css')
@vite(['node_modules/gridjs/dist/theme/mermaid.min.css'])
@endsection
@section('content')
@include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Menu'])
<div class="row d-flex justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<form action="{{route("menus.store")}}" method="post">
@csrf
<div class="mb-3">
<label class="form-label" for="name">Name</label>
<input type="text" id="name" name="name"
class="form-control" placeholder="Enter menu name" required>
</div>
<div class="mb-3">
<label class="form-label" for="url">URL</label>
<input type="text" id="url" name="url"
class="form-control" placeholder="Enter menu url" required>
</div>
<div class="mb-3">
<label class="form-label" for="icon">Icon</label>
<input type="text" id="icon" name="icon"
class="form-control" placeholder="Enter menu icon" required>
</div>
<div class="mb-3">
<label class="form-label" for="parent_id">Parent Menu</label>
<select name="parent_id" class="form-control">
<option value="">Select parent menu</option>
@foreach($parent_menus as $menu)
<option value="{{ $menu->id }}">{{ $menu->name }}</option>
@endforeach
</select>
</div>
<div class="mb-3">
<label class="form-label" for="sort_order">Sort Order</label>
<input type="number" id="sort_order" name="sort_order"
class="form-control" placeholder="Enter sort order" required>
</div>
<button type="submit" class="btn btn-success">Create</button>
</form>
</div>
</div>
</div>
</div>
@endsection
@section('scripts')
@endsection

View File

@@ -0,0 +1,57 @@
@extends('layouts.vertical', ['subtitle' => 'Menu'])
@section('css')
@vite(['node_modules/gridjs/dist/theme/mermaid.min.css'])
@endsection
@section('content')
@include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Menu'])
<div class="row d-flex justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<form action="{{route("menus.update", $menu->id)}}" method="post">
@csrf
@method("put")
<div class="mb-3">
<label class="form-label" for="name">Name</label>
<input type="text" id="name" name="name"
class="form-control" placeholder="Enter menu name" value="{{$menu->name}}" required>
</div>
<div class="mb-3">
<label class="form-label" for="url">URL</label>
<input type="text" id="url" name="url"
class="form-control" placeholder="Enter menu url" value="{{$menu->url}}" required>
</div>
<div class="mb-3">
<label class="form-label" for="icon">Icon</label>
<input type="text" id="icon" name="icon"
class="form-control" placeholder="Enter menu icon" value="{{$menu->icon}}" required>
</div>
<div class="mb-3">
<label class="form-label" for="parent_id">Parent Menu</label>
<select name="parent_id" class="form-control">
<option value="">Select parent menu (Leave blank for parent)</option>
@foreach($parent_menus as $parent)
<option value="{{$parent->id}}"{{ $menu->parent_id == $parent->id ? 'selected' : '' }}>{{ $parent->name }}</option>
@endforeach
</select>
</div>
<div class="mb-3">
<label class="form-label" for="sort_order">Sort Order</label>
<input type="number" id="sort_order" name="sort_order"
class="form-control" placeholder="Enter sort order" value="{{$menu->sort_order}}" required>
</div>
<button type="submit" class="btn btn-success">Update</button>
</form>
</div>
</div>
</div>
</div>
@endsection
@section('scripts')
@endsection

View File

@@ -0,0 +1,24 @@
@extends('layouts.vertical', ['subtitle' => 'Menu'])
@section('css')
@vite(['node_modules/gridjs/dist/theme/mermaid.min.css'])
@endsection
@section('content')
@include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Menu'])
<div class="row">
<div class="d-flex justify-content-end pb-3">
<a href="{{ route('menus.create')}}" class="btn btn-success width-lg">Create</a>
</div>
<div>
<div id="table-menus"></div>
</div>
</div>
@endsection
@section('scripts')
@vite(['resources/js/menus/index.js'])
@endsection

View File

@@ -17,11 +17,11 @@ class="authentication-bg"
<div class="p-4">
<div class="mx-auto mb-4 text-center">
<div class="mx-auto text-center auth-logo">
<!-- <a href="{{ route('home') }}" class="logo-dark">
<!-- <a href="{{ route('dashboard.home') }}" class="logo-dark">
<img src="/images/simbg-dputr.png" height="200" width="100%" alt="dputr logo">
</a>
<a href="{{ route('home') }}" class="logo-light">
<a href="{{ route('dashboard.home') }}" class="logo-light">
<img src="/images/simbg-dputr.png" height="200" width="100%" alt="dputr light">
</a> -->
</div>
@@ -32,7 +32,7 @@ class="authentication-bg"
<p class="text-muted mt-1 mb-4">The page you're trying to reach seems to have gone <br /> missing in the digital wilderness.</p>
<div class="text-center">
<a href="{{ route('home') }}" class="btn btn-danger">Back to Home</a>
<a href="{{ route('dashboard.home') }}" class="btn btn-danger">Back to Home</a>
</div>
</div>
</div>

View File

@@ -0,0 +1,37 @@
@extends('layouts.vertical', ['subtitle' => 'Role'])
@section('css')
@vite(['node_modules/gridjs/dist/theme/mermaid.min.css'])
@endsection
@section('content')
@include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Role'])
<div class="row d-flex justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<form action="{{route("roles.store")}}" method="post">
@csrf
<div class="mb-3">
<label class="form-label" for="name">Name</label>
<input type="text" id="name" name="name"
class="form-control" placeholder="Enter role name" required>
</div>
<div class="mb-3">
<label class="form-label" for="description">Description</label>
<input type="text" id="description" name="description"
class="form-control" placeholder="Enter description">
</div>
<button type="submit" class="btn btn-success">Create</button>
</form>
</div>
</div>
</div>
</div>
@endsection
@section('scripts')
@endsection

View File

@@ -0,0 +1,38 @@
@extends('layouts.vertical', ['subtitle' => 'Role'])
@section('css')
@vite(['node_modules/gridjs/dist/theme/mermaid.min.css'])
@endsection
@section('content')
@include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Role'])
<div class="row d-flex justify-content-center">
<div class="col-md-6">
<div class="card">
<div class="card-body">
<form action="{{route("roles.update", $role->id)}}" method="post">
@csrf
@method("put")
<div class="mb-3">
<label class="form-label" for="name">Name</label>
<input type="text" id="name" name="name"
class="form-control" placeholder="Enter role name" value="{{$role->name}}" required>
</div>
<div class="mb-3">
<label class="form-label" for="description">Description</label>
<input type="text" id="description" name="description"
class="form-control" placeholder="Enter description" value="{{$role->description}}">
</div>
<button type="submit" class="btn btn-success">Update</button>
</form>
</div>
</div>
</div>
</div>
@endsection
@section('scripts')
@endsection

View File

@@ -0,0 +1,24 @@
@extends('layouts.vertical', ['subtitle' => 'Role'])
@section('css')
@vite(['node_modules/gridjs/dist/theme/mermaid.min.css'])
@endsection
@section('content')
@include('layouts.partials/page-title', ['title' => 'Settings', 'subtitle' => 'Role'])
<div class="row">
<div class="d-flex justify-content-end pb-3">
<a href="{{ route('roles.create')}}" class="btn btn-success width-lg">Create</a>
</div>
<div>
<div id="table-roles"></div>
</div>
</div>
@endsection
@section('scripts')
@vite(['resources/js/roles/index.js'])
@endsection