Add user role support and UI

This commit is contained in:
kizadl 2026-04-27 22:24:16 +07:00
parent e38215b1b4
commit fa3902fcea
6 changed files with 55 additions and 6 deletions

View File

@ -41,6 +41,7 @@ public function store(Request $request)
$validatedData = $request->validate([ $validatedData = $request->validate([
'name' => 'required|string|max:255', 'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users', 'email' => 'required|string|email|max:255|unique:users',
'role' => 'required|in:user,admin',
'password' => 'required|string|min:8|confirmed', 'password' => 'required|string|min:8|confirmed',
]); ]);
@ -49,6 +50,7 @@ public function store(Request $request)
$user = User::create([ $user = User::create([
'name' => $validatedData['name'], 'name' => $validatedData['name'],
'email' => $validatedData['email'], 'email' => $validatedData['email'],
'role' => $validatedData['role'],
'password' => Hash::make($validatedData['password']), 'password' => Hash::make($validatedData['password']),
]); ]);
@ -90,6 +92,7 @@ public function update(Request $request, string $id)
$validatedData = $request->validate([ $validatedData = $request->validate([
'name' => 'required|string|max:255', 'name' => 'required|string|max:255',
'email' => 'required|string|email|max:255|unique:users,email,' . $id, 'email' => 'required|string|email|max:255|unique:users,email,' . $id,
'role' => 'required|in:user,admin',
'password' => 'nullable|string|min:8|confirmed', 'password' => 'nullable|string|min:8|confirmed',
]); ]);
$password = $validatedData['password']; $password = $validatedData['password'];
@ -102,6 +105,7 @@ public function update(Request $request, string $id)
$update = [ $update = [
'name' => $validatedData['name'], 'name' => $validatedData['name'],
'email' => $validatedData['email'], 'email' => $validatedData['email'],
'role' => $validatedData['role'],
]; ];
if ($password) { if ($password) {
$update['password'] = $password; $update['password'] = $password;

View File

@ -20,7 +20,7 @@ public function run(): void
'name' => 'Admin', 'name' => 'Admin',
'email' => 'admin@example.com', 'email' => 'admin@example.com',
'password' => Hash::make('password'), 'password' => Hash::make('password'),
'role' => 'super_user', 'role' => 'admin',
]); ]);
} }
} }

View File

@ -1,5 +1,5 @@
<template> <template>
<section id="features" class="py-24 px-4"> <section id="keunggulan" class="py-24 px-4">
<div class="max-w-6xl mx-auto"> <div class="max-w-6xl mx-auto">
<div class="text-center mb-16"> <div class="text-center mb-16">
<h2 class="text-3xl sm:text-4xl font-bold text-white mb-4 font-display">Keunggulan Sistem Kami</h2> <h2 class="text-3xl sm:text-4xl font-bold text-white mb-4 font-display">Keunggulan Sistem Kami</h2>

View File

@ -30,6 +30,22 @@
</p> </p>
</div> </div>
<div class="flex flex-col gap-2">
<Label for="role">Role</Label>
<Select v-model="form.role">
<SelectTrigger class="w-full">
<SelectValue placeholder="Pilih Role" />
</SelectTrigger>
<SelectContent>
<SelectItem value="user">User</SelectItem>
<SelectItem value="admin">Admin</SelectItem>
</SelectContent>
</Select>
<p v-if="form.errors.role" class="mt-1 text-sm text-red-500">
{{ form.errors.role }}
</p>
</div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<Label for="password">Password</Label> <Label for="password">Password</Label>
<div class="relative"> <div class="relative">
@ -89,8 +105,7 @@ import { Button } from '@/components/ui/button';
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { MultipleSelect } from '@/components/ui/multiple-select'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { SelectSearch } from '@/components/ui/select-search';
import { useForm } from '@inertiajs/vue3'; import { useForm } from '@inertiajs/vue3';
import { Eye, EyeOff, UserPlus } from 'lucide-vue-next'; import { Eye, EyeOff, UserPlus } from 'lucide-vue-next';
import { computed, ref } from 'vue'; import { computed, ref } from 'vue';
@ -102,6 +117,7 @@ const showPasswordConfirmation = ref(false);
const form = useForm({ const form = useForm({
name: '', name: '',
email: '', email: '',
role: '',
password: '', password: '',
password_confirmation: '', password_confirmation: '',
}); });

View File

@ -30,6 +30,22 @@
</p> </p>
</div> </div>
<div class="flex flex-col gap-2">
<Label for="role">Role</Label>
<Select v-model="form.role">
<SelectTrigger class="w-full">
<SelectValue placeholder="Pilih Role" />
</SelectTrigger>
<SelectContent>
<SelectItem value="user">User</SelectItem>
<SelectItem value="admin">Admin</SelectItem>
</SelectContent>
</Select>
<p v-if="form.errors.role" class="mt-1 text-sm text-red-500">
{{ form.errors.role }}
</p>
</div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
<Label for="password">Password</Label> <Label for="password">Password</Label>
<div class="relative"> <div class="relative">
@ -89,8 +105,7 @@ import { Button } from '@/components/ui/button';
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog'; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger } from '@/components/ui/dialog';
import { Input } from '@/components/ui/input'; import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label'; import { Label } from '@/components/ui/label';
import { MultipleSelect } from '@/components/ui/multiple-select'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { SelectSearch } from '@/components/ui/select-search';
import { useForm } from '@inertiajs/vue3'; import { useForm } from '@inertiajs/vue3';
import { Edit, Eye, EyeOff, UserPlus } from 'lucide-vue-next'; import { Edit, Eye, EyeOff, UserPlus } from 'lucide-vue-next';
import { computed, ref,watch } from 'vue'; import { computed, ref,watch } from 'vue';
@ -105,12 +120,14 @@ const props = defineProps<{
id: number; id: number;
name: string; name: string;
email: string; email: string;
role: string;
}; };
}>(); }>();
const form = useForm({ const form = useForm({
name: props.user.name || '', name: props.user.name || '',
email: props.user.email || '', email: props.user.email || '',
role: props.user.role || '',
password: '', password: '',
password_confirmation: '', password_confirmation: '',
}); });
@ -119,6 +136,7 @@ watch(open, (value) => {
if (value) { if (value) {
form.name = props.user.name || ''; form.name = props.user.name || '';
form.email = props.user.email || ''; form.email = props.user.email || '';
form.role = props.user.role || '';
form.password = ''; form.password = '';
form.password_confirmation = ''; form.password_confirmation = '';
form.clearErrors(); form.clearErrors();

View File

@ -112,6 +112,12 @@ const changePage = (page: number) => {
{{ sortOrder === 'asc' ? '↑' : '↓' }} {{ sortOrder === 'asc' ? '↑' : '↓' }}
</span> </span>
</TableHead> </TableHead>
<TableHead class="cursor-pointer" @click="sortBy('role')">
Role
<span v-if="sortField === 'role'" class="ml-1">
{{ sortOrder === 'asc' ? '↑' : '↓' }}
</span>
</TableHead>
<TableHead class="text-center">Aksi</TableHead> <TableHead class="text-center">Aksi</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
@ -125,6 +131,11 @@ const changePage = (page: number) => {
<div class="font-medium">{{ user.name }}</div> <div class="font-medium">{{ user.name }}</div>
</TableCell> </TableCell>
<TableCell>{{ user.email }}</TableCell> <TableCell>{{ user.email }}</TableCell>
<TableCell>
<div class="font-medium">
{{ user.role === 'admin' ? 'Admin' : 'User' }}
</div>
</TableCell>
<TableCell> <TableCell>
<div class="flex items-center justify-center gap-2"> <div class="flex items-center justify-center gap-2">
<UserEdit :user="user" /> <UserEdit :user="user" />