user and role refact

dev
Aji Kamaludin 1 year ago
parent dc4a5eea4b
commit 7ce58c1250
No known key found for this signature in database
GPG Key ID: 19058F67F0083AD3

@ -16,7 +16,7 @@ class RoleController extends Controller
{
$request->user()->allow('view-role', true);
$query = Role::query();
$query = Role::query()->with(['users']);
if ($request->q) {
$query->where('name', 'like', "%{$request->q}%");

@ -24,6 +24,11 @@ class UserController extends Controller
]);
}
public function create()
{
return inertia('User/Form');
}
public function store(Request $request): RedirectResponse
{
$request->validate([
@ -31,25 +36,44 @@ class UserController extends Controller
'email' => 'required|email|unique:users,email',
'password' => 'required|string|max:255',
'role_id' => 'required|ulid|exists:roles,id',
'username' => 'required|alpha_dash|unique:users,username',
'phone_wa' => 'required|string',
'photo' => 'required|image',
]);
$file = $request->file('photo');
$file->store('uploads', 'public');
User::create([
'name' => $request->name,
'email' => $request->email,
'password' => bcrypt($request->password),
'role_id' => $request->role_id,
'username' => $request->username,
'phone_wa' => $request->phone_wa,
'photo' => $file->hashName('uploads'),
]);
return redirect()->route('user.index')
->with('message', ['type' => 'success', 'message' => 'Item has beed saved']);
}
public function edit(User $user)
{
return inertia('User/Form', [
'user' => $user->load(['role'])
]);
}
public function update(Request $request, User $user): RedirectResponse
{
$request->validate([
'name' => 'required|string|max:255',
'email' => 'required|email|unique:users,email,'.$user->id,
'password' => 'nullable|string|max:255',
'username' => 'required|alpha_dash|unique:users,username,' . $user->id,
'phone_wa' => 'required|string',
'photo' => 'nullable|image',
]);
if ($user->role != null) {
@ -58,10 +82,18 @@ class UserController extends Controller
]);
}
if ($request->hasFile('photo')) {
$file = $request->file('photo');
$file->store('uploads', 'public');
$user->fill(['photo' => $file->hashName('uploads')]);
}
$user->fill([
'email' => $request->email,
'name' => $request->name,
'role_id' => $request->role_id,
'username' => $request->username,
'phone_wa' => $request->phone_wa,
]);
if ($request->password != '') {

@ -26,4 +26,9 @@ class Role extends Model
'permission_id',
);
}
public function users()
{
return $this->hasMany(User::class);
}
}

@ -4,6 +4,7 @@ namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Concerns\HasUlids;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
@ -25,6 +26,9 @@ class User extends Authenticatable
'password',
'role_id',
'reset_token',
'phone_wa',
'photo',
'username',
];
/**
@ -46,6 +50,10 @@ class User extends Authenticatable
'email_verified_at' => 'datetime',
];
protected $appends = [
'photo_url'
];
public function role()
{
return $this->belongsTo(Role::class);
@ -71,4 +79,11 @@ class User extends Authenticatable
return false;
}
public function photoUrl(): Attribute
{
return Attribute::make(get: function() {
return asset($this->photo);
});
}
}

@ -21,8 +21,16 @@ return new class extends Migration
$table->string('password');
$table->string('reset_token')->nullable();
$table->ulid('role_id')->nullable();
$table->string('phone_wa')->nullable();
$table->string('photo')->nullable();
$table->string('username')->nullable();
$table->rememberToken();
$table->timestamps();
$table->ulid('created_by')->nullable();
$table->ulid('updated_by')->nullable();
$table->ulid('deleted_by')->nullable();
});
}

@ -106,13 +106,19 @@ class PermissionSeeder extends Seeder
'name' => 'Super Administrator',
'email' => 'root@admin.com',
'password' => bcrypt('password'),
'photo' => 'sample/logo-jago.png',
'username' => 'root',
'phone_wa' => '81325307692',
]);
$admin = User::create([
User::create([
'name' => 'Administator',
'email' => 'admin@admin.com',
'password' => bcrypt('password'),
'role_id' => $role->id,
'photo' => 'sample/logo-jago.png',
'username' => 'admin',
'phone_wa' => '81325307692',
]);
$setting = [];

@ -115,6 +115,11 @@ export default function Authenticated({
type="button"
className="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 bg-white hover:text-gray-700 focus:outline-none transition ease-in-out duration-150 dark:bg-gray-700 dark:hover:text-gray-50 dark:text-gray-200 gap-2"
>
<img
src={auth.user.photo_url}
alt="user profile image"
className="h-10 rounded-full border border-gray-100"
/>
{auth.user.name}
<HiChevronDown />
</button>
@ -124,7 +129,7 @@ export default function Authenticated({
<Dropdown.Content>
<a
href={route('home.index')}
target='_blank'
target="_blank"
className="block w-full px-4 py-2 text-left text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none transition duration-150 ease-in-out dark:text-white hover:dark:bg-gray-500"
>
Visit Site

@ -154,7 +154,7 @@ export default [
show: true,
icon: HiUsers,
route: route('user.index'),
active: 'user.index',
active: 'user.*',
permission: 'view-user',
},
],

@ -16,7 +16,7 @@ export default function Form(props) {
holder_name: '',
account_number: '',
logo: null,
logo_url: ''
logo_url: '',
})
const handleOnChange = (event) => {
@ -44,13 +44,17 @@ export default function Form(props) {
bank_name: account.bank_name,
holder_name: account.holder_name,
account_number: account.account_number,
logo_url: account.logo_url
logo_url: account.logo_url,
})
}
}, [account])
return (
<AuthenticatedLayout page={'Bank Akun'} action={'Form'}>
<AuthenticatedLayout
page={'Bank Akun'}
action={'Form'}
parent={route('account.index')}
>
<Head title="Bank Akun" />
<div>

@ -1,108 +0,0 @@
import React, { useEffect } from 'react'
import Modal from '@/Components/Modal'
import { useForm } from '@inertiajs/react'
import Button from '@/Components/Button'
import FormInput from '@/Components/FormInput'
import RoleSelectionInput from '../Role/SelectionInput'
import { isEmpty } from 'lodash'
export default function FormModal(props) {
const { modalState } = props
const { data, setData, post, put, processing, errors, reset, clearErrors } =
useForm({
name: '',
bank_name: '',
holder_name: '',
account_number: '',
})
const handleOnChange = (event) => {
setData(
event.target.name,
event.target.type === 'checkbox'
? event.target.checked
? 1
: 0
: event.target.value
)
}
const handleReset = () => {
modalState.setData(null)
reset()
clearErrors()
}
const handleClose = () => {
handleReset()
modalState.toggle()
}
const handleSubmit = () => {
const account = modalState.data
if (account !== null) {
put(route('account.update', account), {
onSuccess: () => handleClose(),
})
return
}
post(route('account.store'), {
onSuccess: () => handleClose(),
})
}
useEffect(() => {
const account = modalState.data
if (isEmpty(account) === false) {
setData({
name: account.name,
bank_name: account.bank_name,
holder_name: account.holder_name,
account_number: account.account_number,
})
return
}
}, [modalState])
return (
<Modal isOpen={modalState.isOpen} toggle={handleClose} title={'Info'}>
<FormInput
name="name"
value={data.name}
onChange={handleOnChange}
label="Nama"
error={errors.name}
/>
<FormInput
name="bank_name"
value={data.bank_name}
onChange={handleOnChange}
label="Nama Bank"
error={errors.bank_name}
/>
<FormInput
name="holder_name"
value={data.holder_name}
onChange={handleOnChange}
label="Atas Nama Rekening"
error={errors.holder_name}
/>
<FormInput
name="account_number"
value={data.account_number}
onChange={handleOnChange}
label="Nomor Rekening"
error={errors.account_number}
/>
<div className="flex items-center">
<Button onClick={handleSubmit} processing={processing}>
Simpan
</Button>
<Button onClick={handleClose} type="secondary">
Batal
</Button>
</div>
</Modal>
)
}

@ -7,7 +7,6 @@ import { useModalState } from '@/hooks'
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
import Pagination from '@/Components/Pagination'
import ModalConfirm from '@/Components/ModalConfirm'
import FormModal from './FormModal'
import { hasPermission } from '@/utils'
export default function Account(props) {
@ -17,12 +16,6 @@ export default function Account(props) {
} = props
const confirmModal = useModalState()
const formModal = useModalState()
const toggleFormModal = (account = null) => {
formModal.setData(account)
formModal.toggle()
}
const handleDeleteClick = (account) => {
confirmModal.setData(account)
@ -107,7 +100,11 @@ export default function Account(props) {
{account.account_number}
</td>
<td className="py-4 px-6">
<img src={account.logo_url} alt='bank logo alt' className='h-5'/>
<img
src={account.logo_url}
alt="bank logo alt"
className="h-5"
/>
</td>
<td className="py-4 px-6 flex justify-end">
<Dropdown
@ -119,7 +116,13 @@ export default function Account(props) {
>
{canUpdate && (
<Dropdown.Item>
<Link href={route('account.edit', account)} className="flex space-x-1 items-center">
<Link
href={route(
'account.edit',
account
)}
className="flex space-x-1 items-center"
>
<HiPencil />
<div>
Ubah
@ -158,7 +161,6 @@ export default function Account(props) {
</div>
</div>
<ModalConfirm modalState={confirmModal} onConfirm={onDelete} />
<FormModal modalState={formModal} />
</AuthenticatedLayout>
)
}

@ -46,7 +46,11 @@ export default function Form(props) {
}, [info])
return (
<AuthenticatedLayout page={'Info'} action={'Form'}>
<AuthenticatedLayout
page={'Info'}
action={'Form'}
parent={route('info.index')}
>
<Head title="Info" />
<div>

@ -84,6 +84,12 @@ export default function Product(props) {
>
Nama
</th>
<th
scope="col"
className="py-3 px-6"
>
Admin
</th>
<th
scope="col"
className="py-3 px-6"
@ -102,6 +108,30 @@ export default function Product(props) {
>
{role.name}
</td>
<td className="py-4 px-6">
<div className="flex flex-row gap-1">
{role.users.map(
(user) => (
<div
key={
user.id
}
className="px-2 py-1 bg-blue-600 text-white border rounded-full border-b-blue-900"
onClick={() =>
router.visit(
route(
'user.edit',
user
)
)
}
>
{user.name}
</div>
)
)}
</div>
</td>
<td className="py-4 px-6 flex justify-end">
<Dropdown
label={'Opsi'}

@ -0,0 +1,156 @@
import React, { useEffect } from 'react'
import { Head, useForm } from '@inertiajs/react'
import { isEmpty } from 'lodash'
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
import FormInput from '@/Components/FormInput'
import FormFile from '@/Components/FormFile'
import FormInputWith from '@/Components/FormInputWith'
import Button from '@/Components/Button'
import RoleSelectionInput from '../Role/SelectionInput'
export default function Form(props) {
const { user } = props
const { data, setData, post, processing, errors } = useForm({
name: '',
email: '',
password: '',
role_id: null,
role: '',
username: '',
phone_wa: '',
photo: null,
photo_url: '',
})
const handleOnChange = (event) => {
setData(
event.target.name,
event.target.type === 'checkbox'
? event.target.checked
? 1
: 0
: event.target.value
)
}
const handleSubmit = () => {
if (isEmpty(user) === false) {
post(route('user.update', user), {
onSuccess: () => handleClose(),
})
return
}
post(route('user.store'), {
onSuccess: () => handleClose(),
})
}
useEffect(() => {
if (isEmpty(user) === false) {
setData({
name: user.name,
email: user.email,
role_id: user.role_id,
role: user.role,
username: user.username,
phone_wa: user.phone_wa,
photo: null,
photo_url: user.photo_url,
})
}
}, [user])
return (
<AuthenticatedLayout
page={'Admin'}
action={'Form'}
parent={route('user.index')}
>
<Head title="Admin" />
<div>
<div className="mx-auto sm:px-6 lg:px-8">
<div className="overflow-hidden p-4 shadow-sm sm:rounded-lg bg-white dark:bg-gray-800 flex flex-col min-h-screen">
<div className="text-xl font-bold mb-4">Admin</div>
<FormInput
name="name"
value={data.name}
onChange={handleOnChange}
label="Nama"
error={errors.name}
/>
<FormInputWith
type="number"
leftItem={<div className="text-sm">+62</div>}
name="phone_wa"
value={data.phone_wa}
onChange={handleOnChange}
error={errors.phone_wa}
formClassName={'pl-10'}
label="Whatsapp"
/>
<FormInput
name="email"
value={data.email}
onChange={handleOnChange}
label="Email"
error={errors.email}
/>
<FormInput
name="username"
value={data.username}
onChange={handleOnChange}
label="Username"
error={errors.username}
/>
<FormInput
type="password"
name="password"
value={data.password}
onChange={handleOnChange}
label="Password"
error={errors.password}
/>
{data.role !== null && (
<>
<RoleSelectionInput
label="Rule"
itemSelected={data.role_id}
onItemSelected={(id) =>
setData('role_id', id)
}
error={errors.role_id}
/>
</>
)}
<FormFile
label={'Photo'}
onChange={(e) =>
setData('photo', e.target.files[0])
}
error={errors.photo}
preview={
isEmpty(data.photo_url) === false && (
<img
src={data.photo_url}
className="mb-1 h-24 w-24 object-cover rounded-full"
alt="preview"
/>
)
}
/>
<div className="mt-8">
<Button
onClick={handleSubmit}
processing={processing}
>
Simpan
</Button>
</div>
</div>
</div>
</div>
</AuthenticatedLayout>
)
}

@ -1,115 +0,0 @@
import React, { useEffect } from "react";
import Modal from "@/Components/Modal";
import { useForm } from "@inertiajs/react";
import Button from "@/Components/Button";
import FormInput from "@/Components/FormInput";
import RoleSelectionInput from "../Role/SelectionInput";
import { isEmpty } from "lodash";
export default function FormModal(props) {
const { modalState } = props
const { data, setData, post, put, processing, errors, reset, clearErrors } = useForm({
name: '',
email: '',
password: '',
role_id: null,
role: '',
})
const handleOnChange = (event) => {
setData(event.target.name, event.target.type === 'checkbox' ? (event.target.checked ? 1 : 0) : event.target.value);
}
const handleReset = () => {
modalState.setData(null)
reset()
clearErrors()
}
const handleClose = () => {
handleReset()
modalState.toggle()
}
const handleSubmit = () => {
const user = modalState.data
if(user !== null) {
put(route('user.update', user), {
onSuccess: () => handleClose(),
})
return
}
post(route('user.store'), {
onSuccess: () => handleClose()
})
}
useEffect(() => {
const user = modalState.data
if (isEmpty(user) === false) {
setData({
name: user.name,
email: user.email,
role_id: user.role_id,
role: user.role
})
return
}
}, [modalState])
return (
<Modal
isOpen={modalState.isOpen}
toggle={handleClose}
title={"User"}
>
<FormInput
name="name"
value={data.name}
onChange={handleOnChange}
label="name"
error={errors.name}
/>
<FormInput
name="email"
value={data.email}
onChange={handleOnChange}
label="email"
error={errors.email}
/>
<FormInput
type="password"
name="password"
value={data.password}
onChange={handleOnChange}
label="Password"
error={errors.password}
/>
{data.role !== null && (
<>
<RoleSelectionInput
label="Role"
itemSelected={data.role_id}
onItemSelected={(id) => setData('role_id', id)}
error={errors.role_id}
/>
</>
)}
<div className="flex items-center">
<Button
onClick={handleSubmit}
processing={processing}
>
Simpan
</Button>
<Button
onClick={handleClose}
type="secondary"
>
Batal
</Button>
</div>
</Modal>
)
}

@ -1,7 +1,6 @@
import React, { useEffect, useState } from 'react'
import { router } from '@inertiajs/react'
import { usePrevious } from 'react-use'
import { Head } from '@inertiajs/react'
import { Head, Link, router } from '@inertiajs/react'
import { Button, Dropdown } from 'flowbite-react'
import { HiPencil, HiTrash } from 'react-icons/hi'
import { useModalState } from '@/hooks'
@ -9,7 +8,6 @@ import { useModalState } from '@/hooks'
import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout'
import Pagination from '@/Components/Pagination'
import ModalConfirm from '@/Components/ModalConfirm'
import FormModal from './FormModal'
import SearchInput from '@/Components/SearchInput'
import { hasPermission } from '@/utils'
@ -23,12 +21,6 @@ export default function User(props) {
const preValue = usePrevious(search)
const confirmModal = useModalState()
const formModal = useModalState()
const toggleFormModal = (user = null) => {
formModal.setData(user)
formModal.toggle()
}
const handleDeleteClick = (user) => {
confirmModal.setData(user)
@ -68,12 +60,9 @@ export default function User(props) {
<div className="p-6 overflow-hidden shadow-sm sm:rounded-lg bg-gray-200 dark:bg-gray-800 space-y-4">
<div className="flex justify-between">
{canCreate && (
<Button
size="sm"
onClick={() => toggleFormModal()}
>
Tambah
</Button>
<Link href={route('user.create')}>
<Button size="sm">Tambah</Button>
</Link>
)}
<div className="flex items-center">
<SearchInput
@ -87,17 +76,41 @@ export default function User(props) {
<table className="w-full text-sm text-left text-gray-500 dark:text-gray-400 mb-4">
<thead className="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
<tr>
<th
scope="col"
className="py-3 px-6 w-24"
>
Photo
</th>
<th
scope="col"
className="py-3 px-6"
>
Nama
</th>
<th
scope="col"
className="py-3 px-6"
>
Email
</th>
<th
scope="col"
className="py-3 px-6"
>
Name
Username
</th>
<th
scope="col"
className="py-3 px-6"
>
Role
Rule
</th>
<th
scope="col"
className="py-3 px-6"
>
Whatsapp
</th>
<th
scope="col"
@ -113,15 +126,37 @@ export default function User(props) {
>
<td
scope="row"
className="py-4 px-6 font-medium text-gray-900 whitespace-nowrap dark:text-white"
className="px-2 py-1 font-medium text-gray-900 whitespace-nowrap dark:text-white"
>
<img
src={user.photo_url}
alt="photo profile"
className="w-20 h-20 rounded-full"
/>
</td>
<td className="py-4 px-6">
{user.name}
</td>
<td className="py-4 px-6">
{user.email}
</td>
<td className="py-4 px-6">
{user.username}
</td>
<td className="py-4 px-6">
{user.role === null
? 'System'
: user.role?.name}
</td>
<td className="py-4 px-6">
<a
href={`https://wa.me/+62${user.phone_wa}`}
target="_blank"
className="underline"
>
+62{user.phone_wa}
</a>
</td>
<td className="py-4 px-6 flex justify-end">
<Dropdown
label={'Opsi'}
@ -133,9 +168,12 @@ export default function User(props) {
{canUpdate && (
<Dropdown.Item
onClick={() =>
toggleFormModal(
router.visit(
route(
'user.edit',
user
)
)
}
>
<div className="flex space-x-1 items-center">
@ -177,7 +215,6 @@ export default function User(props) {
</div>
</div>
<ModalConfirm modalState={confirmModal} onConfirm={onDelete} />
<FormModal modalState={formModal} />
</AuthenticatedLayout>
)
}

@ -48,8 +48,10 @@ Route::middleware(['http_secure_aware', 'inertia.admin'])
// User
Route::get('/users', [UserController::class, 'index'])->name('user.index');
Route::get('/users/create', [UserController::class, 'create'])->name('user.create');
Route::post('/users', [UserController::class, 'store'])->name('user.store');
Route::put('/users/{user}', [UserController::class, 'update'])->name('user.update');
Route::get('/users/{user}', [UserController::class, 'edit'])->name('user.edit');
Route::post('/users/{user}', [UserController::class, 'update'])->name('user.update');
Route::delete('/users/{user}', [UserController::class, 'destroy'])->name('user.destroy');
// Role

Loading…
Cancel
Save