dress/resources/js/pages/admin/master/user/index.tsx

274 lines
12 KiB
TypeScript

import { Head, router, Link } from '@inertiajs/react';
import type { User } from '@/types';
import { ColumnDef } from '@tanstack/react-table';
import { Card, CardContent } from '@/components/ui/card';
import { Button } from '@/components/ui/button';
import { Trash2, Pencil, Plus, KeyRound } from 'lucide-react';
import { DataTable } from '@/components/data-table';
import { DataTableColumnHeader } from '@/components/data-table-column-header';
import { useState } from 'react';
import { toast } from 'sonner';
import { TooltipContent, TooltipTrigger } from '@radix-ui/react-tooltip';
import { Tooltip } from '@/components/ui/tooltip';
import userRoutes from '@/routes/user';
import { UserInfo } from '@/components/user-info';
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogDescription,
AlertDialogFooter,
AlertDialogHeader,
AlertDialogMedia,
AlertDialogTitle,
} from "@/components/ui/alert-dialog"
export default function UserIndex({ users, defaultPassword }: { users: User[], defaultPassword: string }) {
const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false);
const [isBulkDeleteDialogOpen, setIsBulkDeleteDialogOpen] = useState(false);
const [userToDelete, setUserToDelete] = useState<User | null>(null);
const [isResetPasswordOpen, setIsResetPasswordOpen] = useState(false);
const [userToReset, setUserToReset] = useState<User | null>(null);
const [rowsToDelete, setRowsToDelete] = useState<any[]>([]);
const [rowSelection, setRowSelection] = useState({});
const onResetPassword = (user: User) => {
setUserToReset(user);
setIsResetPasswordOpen(true);
};
const confirmResetPassword = () => {
if (userToReset) {
router.patch(userRoutes.resetPassword(userToReset.id).url, {}, {
onSuccess: (response: any) => {
toast.success(response.props.flash.success);
setIsResetPasswordOpen(false);
setUserToReset(null);
},
});
}
};
const onDelete = (user: User) => {
setUserToDelete(user);
setIsDeleteDialogOpen(true);
};
const confirmDelete = () => {
if (userToDelete) {
router.delete(userRoutes.destroy(userToDelete.id).url, {
onSuccess: (response: any) => {
toast.success(response.props.flash.success);
setIsDeleteDialogOpen(false);
setUserToDelete(null);
setRowSelection({});
},
});
}
};
const confirmBulkDelete = () => {
router.post(userRoutes.bulkDestroy().url, {
ids: rowsToDelete.map((row: any) => row.id),
_method: 'DELETE'
}, {
onSuccess: (response: any) => {
toast.success(response.props.flash.success);
setIsBulkDeleteDialogOpen(false);
setRowsToDelete([]);
setRowSelection({});
},
});
};
const columns: ColumnDef<User>[] = [
{
accessorKey: "name",
header: ({ column }) => {
return (
<DataTableColumnHeader column={column} title="User" />
)
},
meta: { title: "User" },
cell: ({ row }) => {
const user = row.original;
return (
<div className="flex items-center gap-3">
<UserInfo user={user} showEmail={true} />
</div>
);
}
},
{
accessorKey: "username",
header: ({ column }) => {
return (
<DataTableColumnHeader column={column} title="Nama Pengguna" />
)
},
meta: { title: "Nama Pengguna" },
},
{
accessorFn: (row) => row.profile?.phone_number,
id: "phone_number",
header: ({ column }) => {
return (
<DataTableColumnHeader column={column} title="Nomor Telepon" />
)
},
meta: { title: "Nomor Telepon" },
},
{
id: "actions",
header: "Aksi",
cell: ({ row }) => {
const user = row.original;
return (
<div className="flex items-center gap-2">
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon" className='text-blue-600 hover:text-blue-700 hover:bg-blue-50 dark:hover:bg-blue-950/20' onClick={() => onResetPassword(user)}>
<KeyRound className="size-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Reset Kata Sandi</p>
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Link href={userRoutes.edit(user.id).url}>
<Button variant="ghost" size="icon" className='text-yellow-600 hover:text-yellow-700 hover:bg-yellow-50 dark:hover:bg-yellow-950/20'>
<Pencil className="size-4" />
</Button>
</Link>
</TooltipTrigger>
<TooltipContent>
<p>Ubah</p>
</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger asChild>
<Button variant="ghost" size="icon" className='text-red-600 hover:text-red-700 hover:bg-red-50 dark:hover:bg-red-950/20' onClick={() => onDelete(user)}>
<Trash2 className="size-4" />
</Button>
</TooltipTrigger>
<TooltipContent>
<p>Hapus</p>
</TooltipContent>
</Tooltip>
</div>
);
},
meta: { title: "Aksi" },
},
];
return (
<div className="flex flex-col gap-6 p-6">
<Head title="Pegawai" />
<div className="flex items-center justify-between">
<div>
<h1 className="text-3xl font-bold tracking-tight">Pegawai</h1>
</div>
<Link href={userRoutes.create().url}>
<Button>
Tambah
</Button>
</Link>
</div>
<Card className="overflow-hidden border-none shadow-lg bg-card/50 backdrop-blur-sm p-5">
<CardContent className="p-0">
<DataTable
columns={columns}
data={users}
rowSelection={rowSelection}
onRowSelectionChange={setRowSelection}
bulkActions={[
{
label: 'Hapus Terpilih',
onClick: (rows) => {
setRowsToDelete(rows);
setIsBulkDeleteDialogOpen(true);
},
icon: Trash2,
variant: 'destructive'
},
]}
/>
</CardContent>
</Card>
<AlertDialog open={isDeleteDialogOpen} onOpenChange={setIsDeleteDialogOpen}>
<AlertDialogContent size="default">
<AlertDialogHeader>
<AlertDialogMedia className="bg-destructive/10 text-destructive dark:bg-destructive/20 dark:text-destructive">
<Trash2 className="size-5" />
</AlertDialogMedia>
<AlertDialogTitle>Hapus pegawai?</AlertDialogTitle>
<AlertDialogDescription>
Tindakan ini tidak dapat dibatalkan. Pegawai <strong>{userToDelete?.name}</strong> akan dihapus secara permanen.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel variant="outline">Batal</AlertDialogCancel>
<AlertDialogAction onClick={confirmDelete} variant="destructive">Hapus</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<AlertDialog open={isResetPasswordOpen} onOpenChange={setIsResetPasswordOpen}>
<AlertDialogContent size="default">
<AlertDialogHeader>
<AlertDialogMedia className="bg-blue-100 text-blue-600 dark:bg-blue-900/20 dark:text-blue-500">
<KeyRound className="size-5" />
</AlertDialogMedia>
<AlertDialogTitle>Reset password?</AlertDialogTitle>
<AlertDialogDescription>
Tindakan ini akan mereset password <strong>{userToReset?.name}</strong> menjadi <strong>{defaultPassword}</strong>. Pengguna dapat mengubahnya kembali nanti.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel variant="outline">Batal</AlertDialogCancel>
<AlertDialogAction onClick={confirmResetPassword} className="bg-blue-600 hover:bg-blue-700 text-white focus:ring-blue-600">Reset</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
<AlertDialog open={isBulkDeleteDialogOpen} onOpenChange={setIsBulkDeleteDialogOpen}>
<AlertDialogContent size="default">
<AlertDialogHeader>
<AlertDialogMedia className="bg-destructive/10 text-destructive dark:bg-destructive/20 dark:text-destructive">
<Trash2 className="size-5" />
</AlertDialogMedia>
<AlertDialogTitle>Hapus {rowsToDelete.length} pegawai?</AlertDialogTitle>
<AlertDialogDescription>
Tindakan ini tidak dapat dibatalkan. <strong>{rowsToDelete.length}</strong> pegawai yang terpilih akan dihapus secara permanen.
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel variant="outline">Batal</AlertDialogCancel>
<AlertDialogAction
onClick={confirmBulkDelete}
variant="destructive"
>
Hapus
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</div>
);
}
UserIndex.layout = {
breadcrumbs: [
{
title: 'Master',
},
],
};