feat: implement employee management module
This commit is contained in:
parent
d4fdf35a3c
commit
b62df688b7
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Employee;
|
||||
use App\Models\User;
|
||||
use App\Http\Requests\StoreEmployeeRequest;
|
||||
use App\Http\Requests\UpdateEmployeeRequest;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Inertia\Inertia;
|
||||
|
||||
class EmployeeController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$employees = Employee::with('user')
|
||||
->latest()
|
||||
->paginate(10);
|
||||
|
||||
return Inertia::render('admin/employees/index', [
|
||||
'employees' => $employees
|
||||
]);
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
return Inertia::render('admin/employees/create');
|
||||
}
|
||||
|
||||
public function store(StoreEmployeeRequest $request)
|
||||
{
|
||||
DB::transaction(function () use ($request) {
|
||||
$user = User::create([
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
'password' => Hash::make($request->password),
|
||||
'role' => 'employee',
|
||||
]);
|
||||
|
||||
$user->employee()->create([
|
||||
'nip' => $request->nip,
|
||||
'position' => $request->position,
|
||||
'status' => $request->status,
|
||||
'join_date' => $request->join_date,
|
||||
]);
|
||||
});
|
||||
|
||||
return redirect()->route('admin.employees.index')
|
||||
->with('success', 'Karyawan berhasil ditambahkan.');
|
||||
}
|
||||
|
||||
public function edit(Employee $employee)
|
||||
{
|
||||
$employee->load('user');
|
||||
return Inertia::render('admin/employees/edit', [
|
||||
'employee' => $employee
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(UpdateEmployeeRequest $request, Employee $employee)
|
||||
{
|
||||
DB::transaction(function () use ($request, $employee) {
|
||||
// Update User Data
|
||||
$userData = [
|
||||
'name' => $request->name,
|
||||
'email' => $request->email,
|
||||
];
|
||||
|
||||
if ($request->filled('password')) {
|
||||
$userData['password'] = Hash::make($request->password);
|
||||
}
|
||||
|
||||
$employee->user->update($userData);
|
||||
|
||||
// Update Employee Data
|
||||
$employee->update([
|
||||
'nip' => $request->nip,
|
||||
'position' => $request->position,
|
||||
'status' => $request->status,
|
||||
'join_date' => $request->join_date,
|
||||
]);
|
||||
});
|
||||
|
||||
return redirect()->route('admin.employees.index')
|
||||
->with('success', 'Data karyawan diperbarui.');
|
||||
}
|
||||
|
||||
public function destroy(Employee $employee)
|
||||
{
|
||||
$employee->user->delete();
|
||||
return back()->with('success', 'Karyawan dihapus.');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class StoreEmployeeRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'required|email|unique:users,email',
|
||||
'password' => 'required|string|min:8',
|
||||
'nip' => 'required|string|unique:employees,nip',
|
||||
'position' => 'required|string',
|
||||
'status' => 'required|in:PKWT,PKWTT',
|
||||
'join_date' => 'required|date',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class UpdateEmployeeRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
$employeeId = $this->route('employee')->id;
|
||||
$userId = $this->route('employee')->user_id;
|
||||
|
||||
return [
|
||||
'name' => 'required|string|max:255',
|
||||
'email' => 'required|email|unique:users,email,' . $userId,
|
||||
'nip' => 'required|string|unique:employees,nip,' . $employeeId,
|
||||
'position' => 'required|string',
|
||||
'status' => 'required|in:PKWT,PKWTT',
|
||||
'join_date' => 'required|date',
|
||||
// Password opsional saat update
|
||||
'password' => 'nullable|string|min:8',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class Employee extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'user_id',
|
||||
'nip',
|
||||
'position',
|
||||
'status',
|
||||
'phone',
|
||||
'address',
|
||||
'join_date'
|
||||
];
|
||||
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@ class User extends Authenticatable
|
|||
'name',
|
||||
'email',
|
||||
'password',
|
||||
'role',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -36,6 +37,16 @@ class User extends Authenticatable
|
|||
'remember_token',
|
||||
];
|
||||
|
||||
public function employee()
|
||||
{
|
||||
return $this->hasOne(Employee::class);
|
||||
}
|
||||
|
||||
public function hasRole($role)
|
||||
{
|
||||
return $this->role === $role;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ public function up(): void
|
|||
$table->string('email')->unique();
|
||||
$table->timestamp('email_verified_at')->nullable();
|
||||
$table->string('password');
|
||||
$table->enum('role', ['admin', 'employee'])->default('employee');
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -0,0 +1,34 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('employees', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->foreignId('user_id')->constrained()->onDelete('cascade');
|
||||
$table->string('nip')->unique();
|
||||
$table->string('position');
|
||||
$table->enum('status', ['PKWT', 'PKWTT']);
|
||||
$table->string('phone')->nullable();
|
||||
$table->text('address')->nullable();
|
||||
$table->date('join_date');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('employees');
|
||||
}
|
||||
};
|
||||
|
|
@ -3,8 +3,8 @@
|
|||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\User;
|
||||
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
|
||||
class DatabaseSeeder extends Seeder
|
||||
{
|
||||
|
|
@ -13,11 +13,12 @@ class DatabaseSeeder extends Seeder
|
|||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// User::factory(10)->create();
|
||||
|
||||
User::factory()->create([
|
||||
'name' => 'Test User',
|
||||
'email' => 'test@example.com',
|
||||
User::create([
|
||||
'name' => 'Super Admin',
|
||||
'email' => 'admin@zhanhui.com',
|
||||
'password' => Hash::make('password123'),
|
||||
'role' => 'admin',
|
||||
]);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,7 +8,7 @@ export default function AppLogo() {
|
|||
</div>
|
||||
<div className="ml-1 grid flex-1 text-left text-sm">
|
||||
<span className="mb-0.5 truncate leading-tight font-semibold">
|
||||
Laravel Starter Kit
|
||||
SDM App
|
||||
</span>
|
||||
</div>
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { Link } from '@inertiajs/react';
|
||||
import { BookOpen, Folder, LayoutGrid } from 'lucide-react';
|
||||
import { BookOpen, Folder, LayoutGrid, Users } from 'lucide-react';
|
||||
import { NavFooter } from '@/components/nav-footer';
|
||||
import { NavMain } from '@/components/nav-main';
|
||||
import { NavUser } from '@/components/nav-user';
|
||||
|
|
@ -22,20 +22,25 @@ const mainNavItems: NavItem[] = [
|
|||
href: dashboard(),
|
||||
icon: LayoutGrid,
|
||||
},
|
||||
{
|
||||
title: 'Manajemen Karyawan',
|
||||
href: '/admin/employees',
|
||||
icon: Users,
|
||||
},
|
||||
];
|
||||
|
||||
const footerNavItems: NavItem[] = [
|
||||
{
|
||||
title: 'Repository',
|
||||
href: 'https://github.com/laravel/react-starter-kit',
|
||||
icon: Folder,
|
||||
},
|
||||
{
|
||||
title: 'Documentation',
|
||||
href: 'https://laravel.com/docs/starter-kits#react',
|
||||
icon: BookOpen,
|
||||
},
|
||||
];
|
||||
// const footerNavItems: NavItem[] = [
|
||||
// {
|
||||
// title: 'Repository',
|
||||
// href: 'https://github.com/laravel/react-starter-kit',
|
||||
// icon: Folder,
|
||||
// },
|
||||
// {
|
||||
// title: 'Documentation',
|
||||
// href: 'https://laravel.com/docs/starter-kits#react',
|
||||
// icon: BookOpen,
|
||||
// },
|
||||
// ];
|
||||
|
||||
export function AppSidebar() {
|
||||
return (
|
||||
|
|
@ -57,7 +62,7 @@ export function AppSidebar() {
|
|||
</SidebarContent>
|
||||
|
||||
<SidebarFooter>
|
||||
<NavFooter items={footerNavItems} className="mt-auto" />
|
||||
{/* <NavFooter items={footerNavItems} className="mt-auto" /> */}
|
||||
<NavUser />
|
||||
</SidebarFooter>
|
||||
</Sidebar>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,100 @@
|
|||
import { Head, useForm, Link } from '@inertiajs/react';
|
||||
import React from 'react';
|
||||
import type { FormEvent } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import AppLayout from '@/layouts/app-layout';
|
||||
|
||||
export default function Create() {
|
||||
const { data, setData, post, processing, errors } = useForm({
|
||||
name: '',
|
||||
email: '',
|
||||
password: '',
|
||||
nip: '',
|
||||
position: '',
|
||||
status: 'PKWT',
|
||||
join_date: '',
|
||||
});
|
||||
|
||||
const submit = (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
post('/admin/employees');
|
||||
};
|
||||
|
||||
return (
|
||||
<AppLayout>
|
||||
<Head title="Tambah Karyawan" />
|
||||
<div className="p-8 max-w-2xl mx-auto">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Tambah Karyawan</CardTitle>
|
||||
<CardDescription>Masukkan data detail karyawan baru.</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form onSubmit={submit} className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label>Nama Lengkap</Label>
|
||||
<Input value={data.name} onChange={e => setData('name', e.target.value)} />
|
||||
{errors.name && <p className="text-xs text-red-500">{errors.name}</p>}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>Email</Label>
|
||||
<Input type="email" value={data.email} onChange={e => setData('email', e.target.value)} />
|
||||
{errors.email && <p className="text-xs text-red-500">{errors.email}</p>}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label>Password Default</Label>
|
||||
<Input type="password" value={data.password} onChange={e => setData('password', e.target.value)} />
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label>NIP</Label>
|
||||
<Input value={data.nip} onChange={e => setData('nip', e.target.value)} />
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>Tanggal Masuk</Label>
|
||||
<Input type="date" value={data.join_date} onChange={e => setData('join_date', e.target.value)} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label>Jabatan</Label>
|
||||
<Select onValueChange={(val) => setData('position', val)}>
|
||||
<SelectTrigger><SelectValue placeholder="Pilih" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Staff">Staff</SelectItem>
|
||||
<SelectItem value="Supervisor">Supervisor</SelectItem>
|
||||
<SelectItem value="Manager">Manager</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>Status</Label>
|
||||
<Select onValueChange={(val) => setData('status', val)}>
|
||||
<SelectTrigger><SelectValue placeholder="Pilih" /></SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="PKWT">PKWT</SelectItem>
|
||||
<SelectItem value="PKWTT">PKWTT</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-2 pt-4">
|
||||
<Button variant="outline" asChild><Link href="/admin/employees">Batal</Link></Button>
|
||||
<Button type="submit" disabled={processing}>Simpan</Button>
|
||||
</div>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</AppLayout>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,163 @@
|
|||
import { Head, useForm, Link } from '@inertiajs/react';
|
||||
import { Separator } from '@radix-ui/react-separator';
|
||||
import React from 'react';
|
||||
import type { FormEvent } from 'react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Label } from '@/components/ui/label';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
|
||||
import AppLayout from '@/layouts/app-layout';
|
||||
|
||||
// Definisi tipe data props untuk TypeScript
|
||||
interface EmployeeProps {
|
||||
employee: {
|
||||
id: number;
|
||||
nip: string;
|
||||
position: string;
|
||||
status: string;
|
||||
join_date: string;
|
||||
user: {
|
||||
name: string;
|
||||
email: string;
|
||||
} | null;
|
||||
};
|
||||
}
|
||||
|
||||
export default function Edit({ employee }: EmployeeProps) {
|
||||
// Inisialisasi form dengan data dari database
|
||||
const { data, setData, put, processing, errors } = useForm({
|
||||
name: employee.user?.name || '',
|
||||
email: employee.user?.email || '',
|
||||
password: '', // Kosongkan untuk keamanan
|
||||
nip: employee.nip || '',
|
||||
position: employee.position || '',
|
||||
status: employee.status || 'PKWT',
|
||||
join_date: employee.join_date || '',
|
||||
});
|
||||
|
||||
const submit = (e: FormEvent) => {
|
||||
e.preventDefault();
|
||||
put(`/admin/employees/${employee.id}`);
|
||||
};
|
||||
|
||||
return (
|
||||
<AppLayout>
|
||||
<Head title={`Edit Karyawan - ${employee.user?.name}`} />
|
||||
<div className="p-8 max-w-2xl mx-auto">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Edit Karyawan</CardTitle>
|
||||
<CardDescription>
|
||||
Perbarui informasi data diri dan kepegawaian karyawan.
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form onSubmit={submit} className="space-y-4">
|
||||
{/* Data Profil */}
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="name">Nama Lengkap</Label>
|
||||
<Input
|
||||
id="name"
|
||||
value={data.name}
|
||||
onChange={e => setData('name', e.target.value)}
|
||||
/>
|
||||
{errors.name && <p className="text-xs text-red-500">{errors.name}</p>}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="email">Email</Label>
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
value={data.email}
|
||||
onChange={e => setData('email', e.target.value)}
|
||||
/>
|
||||
{errors.email && <p className="text-xs text-red-500">{errors.email}</p>}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">Password Baru (Kosongkan jika tidak diganti)</Label>
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
value={data.password}
|
||||
onChange={e => setData('password', e.target.value)}
|
||||
/>
|
||||
{errors.password && <p className="text-xs text-red-500">{errors.password}</p>}
|
||||
</div>
|
||||
|
||||
<Separator className="my-4" />
|
||||
|
||||
{/* Data Kepegawaian */}
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="nip">NIP</Label>
|
||||
<Input
|
||||
id="nip"
|
||||
value={data.nip}
|
||||
onChange={e => setData('nip', e.target.value)}
|
||||
/>
|
||||
{errors.nip && <p className="text-xs text-red-500">{errors.nip}</p>}
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="join_date">Tanggal Masuk</Label>
|
||||
<Input
|
||||
id="join_date"
|
||||
type="date"
|
||||
value={data.join_date}
|
||||
onChange={e => setData('join_date', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div className="space-y-2">
|
||||
<Label>Jabatan</Label>
|
||||
<Select
|
||||
value={data.position}
|
||||
onValueChange={(val) => setData('position', val)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Pilih Jabatan" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="Staff">Staff</SelectItem>
|
||||
<SelectItem value="Supervisor">Supervisor</SelectItem>
|
||||
<SelectItem value="Manager">Manager</SelectItem>
|
||||
<SelectItem value="HRD">HRD</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label>Status Kerja</Label>
|
||||
<Select
|
||||
value={data.status}
|
||||
onValueChange={(val) => setData('status', val)}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Pilih Status" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="PKWT">PKWT</SelectItem>
|
||||
<SelectItem value="PKWTT">PKWTT</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-2 pt-4">
|
||||
<Button variant="outline" asChild>
|
||||
<Link href="/admin/employees">Batal</Link>
|
||||
</Button>
|
||||
<Button type="submit" disabled={processing}>
|
||||
Simpan Perubahan
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</AppLayout>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
import { Head, Link, usePage } from '@inertiajs/react';
|
||||
import React from 'react';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
||||
import { Separator } from '@/components/ui/separator';
|
||||
import AppLayout from '@/layouts/app-layout';
|
||||
|
||||
interface Employee {
|
||||
id: number;
|
||||
nip: string;
|
||||
position: string;
|
||||
status: string;
|
||||
user: {
|
||||
name: string;
|
||||
email: string;
|
||||
} | null;
|
||||
}
|
||||
|
||||
interface PageProps {
|
||||
employees: {
|
||||
data: Employee[];
|
||||
};
|
||||
[key: string]: unknown;
|
||||
}
|
||||
|
||||
interface SharedData {
|
||||
flash: {
|
||||
success?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export default function Index({ employees }: PageProps) {
|
||||
const { flash } = usePage().props as unknown as SharedData;
|
||||
|
||||
return (
|
||||
<AppLayout>
|
||||
<Head title="Manajemen Karyawan" />
|
||||
<div className="p-8">
|
||||
{flash?.success && (
|
||||
<div className="mb-4 p-4 bg-green-50 text-green-700 border rounded-md">
|
||||
{flash.success}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between">
|
||||
<CardTitle>Daftar Karyawan</CardTitle>
|
||||
<Button asChild>
|
||||
<Link href="/admin/employees/create">Tambah Karyawan</Link>
|
||||
</Button>
|
||||
</CardHeader>
|
||||
<Separator />
|
||||
<CardContent className="pt-6">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="text-left border-b">
|
||||
<th className="pb-4 font-medium">Karyawan</th>
|
||||
<th className="pb-4 font-medium">NIP & Jabatan</th>
|
||||
<th className="pb-4 font-medium">Status</th>
|
||||
<th className="pb-4 text-right font-medium">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{employees.data.map((employee) => (
|
||||
<tr key={employee.id} className="border-b">
|
||||
<td className="py-4">
|
||||
<div className="font-bold">{employee.user?.name}</div>
|
||||
<div className="text-xs text-muted-foreground">{employee.user?.email}</div>
|
||||
</td>
|
||||
<td className="py-4">
|
||||
<div>{employee.nip}</div>
|
||||
<Badge variant="outline">{employee.position}</Badge>
|
||||
</td>
|
||||
<td className="py-4">
|
||||
<Badge variant={employee.status === 'PKWTT' ? 'default' : 'secondary'}>
|
||||
{employee.status}
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="py-4 text-right">
|
||||
<Button variant="ghost" asChild>
|
||||
<Link href={`/admin/employees/${employee.id}/edit`}>Edit</Link>
|
||||
</Button>
|
||||
<Button variant="ghost" className="text-red-600" asChild>
|
||||
<Link href={`/admin/employees/${employee.id}`} method="delete" as="button">Hapus</Link>
|
||||
</Button>
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</AppLayout>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
<?php
|
||||
|
||||
use App\Http\Controllers\Admin\EmployeeController;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Inertia\Inertia;
|
||||
use Laravel\Fortify\Features;
|
||||
|
|
@ -14,4 +15,7 @@
|
|||
return Inertia::render('dashboard');
|
||||
})->middleware(['auth', 'verified'])->name('dashboard');
|
||||
|
||||
Route::middleware(['auth', 'verified'])->prefix('admin')->name('admin.')->group(function () {
|
||||
Route::resource('employees', EmployeeController::class);
|
||||
});
|
||||
require __DIR__.'/settings.php';
|
||||
|
|
|
|||
Loading…
Reference in New Issue