added data table, formatter

revert context on __root beforeLoad
refactor project structure
refactor role badge
dynamic nav menu
This commit is contained in:
2026-01-14 09:35:46 +07:00
parent a44fa70500
commit edb4ebe11c
45 changed files with 1519 additions and 149 deletions

View File

@@ -0,0 +1,159 @@
import { m } from '@/paraglide/messages';
import { formatters } from '@/utils/formatters';
import { jsonSupport } from '@/utils/help';
import { EyeIcon } from '@phosphor-icons/react';
import { ColumnDef } from '@tanstack/react-table';
import { Badge } from '../ui/badge';
import { Button } from '../ui/button';
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '../ui/dialog';
import { Label } from '../ui/label';
import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip';
import ActionBadge, { UserActionType } from './action-badge';
export const logColumns: ColumnDef<AuditLog>[] = [
{
accessorKey: 'user.name',
header: m.logs_page_ui_table_header_username(),
meta: {
thClass: 'w-1/6',
},
},
{
accessorKey: 'tableName',
header: m.logs_page_ui_table_header_table(),
meta: {
thClass: 'w-1/6',
},
cell: ({ row }) => {
return (
<Badge variant="table" className="px-3 py-1 text-xs">
{row.original.tableName}
</Badge>
);
},
},
{
accessorKey: 'action',
header: m.logs_page_ui_table_header_action(),
meta: {
thClass: 'w-1/6',
},
cell: ({ row }) => {
return (
<ActionBadge action={row.original.action as keyof UserActionType} />
);
},
},
{
accessorKey: 'createdAt',
header: m.logs_page_ui_table_header_action(),
meta: {
thClass: 'w-2/6',
},
cell: ({ row }) => {
return formatters.dateTime(new Date(row.original.createdAt));
},
},
{
id: 'actions',
meta: {
thClass: 'w-1/6',
},
cell: ({ row }) => (
<div className="flex justify-end">
<ViewDetail data={row.original} />
</div>
),
},
];
type ViewDetailProps = {
data: AuditLog;
};
const ViewDetail = ({ data }: ViewDetailProps) => {
return (
<Dialog>
<Tooltip>
<TooltipTrigger asChild>
<DialogTrigger asChild>
<Button
type="button"
variant="ghost"
size="icon"
className="rounded-full cursor-pointer text-blue-500 hover:bg-blue-100 hover:text-blue-600"
>
<EyeIcon size={16} />
<span className="sr-only">View</span>
</Button>
</DialogTrigger>
</TooltipTrigger>
<TooltipContent
side="left"
className="bg-blue-300 [&_svg]:bg-blue-300 [&_svg]:fill-blue-300 text-white"
>
<Label>View</Label>
</TooltipContent>
</Tooltip>
<DialogContent className="max-w-100 xl:max-w-2xl">
<DialogHeader>
<DialogTitle>
{m.ui_dialog_view_title({ type: m.nav_log() })}
</DialogTitle>
</DialogHeader>
<div className="flex flex-col gap-2">
<div className="flex items-center gap-2">
<span className="font-bold">
{m.logs_page_ui_table_header_username()}:
</span>
<Label>{data.user.name}</Label>
</div>
<div className="flex items-center gap-2">
<span className="font-bold">
{m.logs_page_ui_table_header_table()}:
</span>
<Badge variant="table" className="px-3 py-1 text-xs">
{data.tableName}
</Badge>
</div>
<div className="flex items-center gap-2">
<span className="font-bold">
{m.logs_page_ui_table_header_action()}:
</span>
<ActionBadge action={data.action as keyof UserActionType} />
</div>
{data.oldValue && (
<div className="flex flex-col gap-2">
<span className="font-bold">
{m.logs_page_ui_table_header_old_value()}:
</span>
<pre className="whitespace-pre-wrap wrap-break-word">
{jsonSupport(data.oldValue)}
</pre>
</div>
)}
<div className="flex flex-col gap-2">
<span className="font-bold">
{m.logs_page_ui_table_header_new_value()}:
</span>
<pre className="whitespace-pre-wrap wrap-break-word">
{data.newValue ? jsonSupport(data.newValue) : ''}
</pre>
</div>
<div className="flex items-center gap-2">
<span className="font-bold">
{m.logs_page_ui_table_header_create_at()}:
</span>
<span>{formatters.dateTime(new Date(data.createdAt))}</span>
</div>
</div>
</DialogContent>
</Dialog>
);
};