List out house

view house detail
This commit is contained in:
2026-02-01 11:12:33 +07:00
parent ed7e5baaea
commit afa26ab50d
25 changed files with 570 additions and 103 deletions

View File

@@ -56,7 +56,7 @@ const DataTable = <TData, TValue>({
return (
<>
<div className="overflow-hidden rounded-md border">
<Table>
<Table className="bg-white">
<TableHeader>
{table.getHeaderGroups().map((headerGroup) => (
<TableRow key={headerGroup.id}>
@@ -66,7 +66,7 @@ const DataTable = <TData, TValue>({
key={header.id}
colSpan={header.colSpan}
className={cn(
'px-4',
'px-4 bg-primary text-white text-sm',
header.column.columnDef.meta?.thClass,
)}
>

View File

@@ -5,7 +5,7 @@ import { Badge } from '../ui/badge';
import { LOG_ACTION } from '@/types/enum';
import ActionBadge from './action-badge';
import ViewDetail from './view-detail-dialog';
import ViewDetailAudit from './view-detail-dialog';
export const logColumns: ColumnDef<AuditWithUser>[] = [
{
@@ -56,7 +56,7 @@ export const logColumns: ColumnDef<AuditWithUser>[] = [
},
cell: ({ row }) => (
<div className="flex justify-end">
<ViewDetail data={row.original} />
<ViewDetailAudit data={row.original} />
</div>
),
},

View File

@@ -23,7 +23,7 @@ type ViewDetailProps = {
data: AuditWithUser;
};
const ViewDetail = ({ data }: ViewDetailProps) => {
const ViewDetailAudit = ({ data }: ViewDetailProps) => {
const prevent = usePreventAutoFocus();
const { isCopied, copyToClipboard } = useCopyToClipboard();
@@ -134,4 +134,4 @@ const ViewDetail = ({ data }: ViewDetailProps) => {
);
};
export default ViewDetail;
export default ViewDetailAudit;

View File

@@ -74,6 +74,9 @@ const ProfileForm = () => {
);
} catch (error) {
console.error('update load file', error);
toast.error(JSON.stringify(error), {
richColors: true,
});
}
},
});

View File

@@ -0,0 +1,46 @@
import usePreventAutoFocus from '@/hooks/use-prevent-auto-focus';
import { m } from '@/paraglide/messages';
import { PlusIcon } from '@phosphor-icons/react';
import { useState } from 'react';
import { Button } from '../ui/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '../ui/dialog';
const CreateNewHouse = () => {
const [_open, _setOpen] = useState(false);
const prevent = usePreventAutoFocus();
return (
<Dialog open={_open} onOpenChange={_setOpen}>
<DialogTrigger asChild>
<Button type="button" variant="default">
<PlusIcon />
{m.nav_add_new()}
</Button>
</DialogTrigger>
<DialogContent
className="max-w-80 xl:max-w-xl"
{...prevent}
onPointerDownOutside={(e) => e.preventDefault()}
>
<DialogHeader>
<DialogTitle className="flex items-center gap-3 text-lg font-bold text-primary">
<PlusIcon size={16} />
{m.nav_add_new()}
</DialogTitle>
<DialogDescription className="sr-only">
{m.nav_add_new()}
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
);
};
export default CreateNewHouse;

View File

@@ -0,0 +1,47 @@
import { m } from '@/paraglide/messages';
import { formatters } from '@/utils/formatters';
import { ColumnDef } from '@tanstack/react-table';
import ViewDetailHouse from './view-detail-dialog';
export const houseColumns: ColumnDef<OrganizationWithMembers>[] = [
{
accessorKey: 'name',
header: m.houses_page_ui_table_header_name(),
meta: {
thClass: 'w-1/6',
},
},
{
accessorKey: 'members',
header: m.houses_page_ui_table_header_members(),
meta: {
thClass: 'w-1/6',
},
cell: ({ row }) => {
return row.original.members.length;
},
},
{
accessorKey: 'createdAt',
header: m.houses_page_ui_table_header_created_at(),
meta: {
thClass: 'w-1/6',
},
cell: ({ row }) => {
return formatters.dateTime(new Date(row.original.createdAt));
},
},
{
id: 'actions',
meta: {
thClass: 'w-1/6',
},
cell: ({ row }) => {
return (
<div className="flex justify-end">
<ViewDetailHouse data={row.original} />
</div>
);
},
},
];

View File

@@ -0,0 +1,120 @@
import usePreventAutoFocus from '@/hooks/use-prevent-auto-focus';
import { m } from '@/paraglide/messages';
import { formatters } from '@/utils/formatters';
import { EyeIcon } from '@phosphor-icons/react';
import { Button } from '../ui/button';
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
DialogTrigger,
} from '../ui/dialog';
import { Label } from '../ui/label';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '../ui/table';
import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/tooltip';
type ViewDetailProps = {
data: OrganizationWithMembers;
};
const ViewDetailHouse = ({ data }: ViewDetailProps) => {
const prevent = usePreventAutoFocus();
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">{m.ui_view_btn()}</span>
</Button>
</DialogTrigger>
</TooltipTrigger>
<TooltipContent
side="left"
className="bg-blue-500 [&_svg]:bg-blue-500 [&_svg]:fill-blue-500 text-white"
>
<Label>{m.ui_view_btn()}</Label>
</TooltipContent>
</Tooltip>
<DialogContent
className="max-w-100 xl:max-w-2xl"
{...prevent}
onPointerDownOutside={(e) => e.preventDefault()}
>
<DialogHeader>
<DialogTitle className="flex items-center gap-3 text-lg font-bold text-blue-600">
<EyeIcon size={20} />
{m.ui_dialog_view_title({ type: m.nav_houses() })}
</DialogTitle>
<DialogDescription className="sr-only">
{m.ui_dialog_view_title({ type: m.nav_houses() })}
</DialogDescription>
</DialogHeader>
<div className="flex flex-col gap-2">
<div className="flex items-center gap-2">
<span className="font-bold">
{m.houses_page_ui_table_header_name()}:
</span>
<Label>{data.name}</Label>
</div>
<div className="flex items-center gap-2">
<span className="font-bold">
{m.houses_page_ui_table_header_created_at()}:
</span>
<Label>{formatters.dateTime(new Date(data.createdAt))}</Label>
</div>
<div className="flex flex-col gap-2">
<span className="font-bold">
{m.houses_page_ui_table_header_members()}:
</span>
<div className="flex items-center gap-2">
<span className="font-bold">
{m.houses_page_ui_view_label_count()}:
</span>
<Label>{data.members.length}</Label>
</div>
<div className="overflow-hidden rounded-md border">
<Table className="bg-white">
<TableHeader>
<TableRow>
<TableHead className="px-2 h-7 bg-primary text-white text-xs w-1/2">
{m.houses_page_ui_view_table_header_email()}
</TableHead>
<TableHead className="px-2 h-7 bg-primary text-white text-xs w-1/2">
{m.houses_page_ui_view_table_header_role()}
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.members.map((member) => (
<TableRow>
<TableCell>{member.user.email}</TableCell>
<TableCell>{member.role}</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</div>
</div>
</DialogContent>
</Dialog>
);
};
export default ViewDetailHouse;

View File

@@ -5,6 +5,7 @@ import {
GearIcon,
HouseIcon,
UsersIcon,
WarehouseIcon,
} from '@phosphor-icons/react';
import { createLink } from '@tanstack/react-router';
import AdminShow from '../auth/AdminShow';
@@ -23,7 +24,7 @@ const SidebarMenuButtonLink = createLink(SidebarMenuButton);
const NAV_MAIN = [
{
id: '1',
title: 'Basic',
title: m.nav_label_basic(),
items: [
{
title: m.nav_home(),
@@ -43,8 +44,15 @@ const NAV_MAIN = [
},
{
id: '2',
title: 'Management',
title: m.nav_label_management(),
items: [
{
title: m.nav_houses(),
path: '/kanri/houses',
icon: WarehouseIcon,
isAuth: false,
admin: true,
},
{
title: m.nav_users(),
path: '/kanri/users',

View File

@@ -14,7 +14,7 @@ const SearchInput = ({ keywords, setKeyword, onChange }: SearchInputProps) => {
};
return (
<InputGroup className="w-70">
<InputGroup className="w-70 bg-white">
<InputGroupInput
id="keywords"
placeholder="Search...."

View File

@@ -31,7 +31,7 @@ const AddNewUserButton = () => {
onPointerDownOutside={(e) => e.preventDefault()}
>
<DialogHeader>
<DialogTitle className="flex items-center gap-3 text-lg font-bold text-red-600">
<DialogTitle className="flex items-center gap-3 text-lg font-bold text-primary">
<PlusIcon size={16} />
{m.nav_add_new()}
</DialogTitle>