diff --git a/app/lib/guest.tsx b/app/lib/guest.tsx index 0baddfe..215f318 100644 --- a/app/lib/guest.tsx +++ b/app/lib/guest.tsx @@ -13,20 +13,22 @@ export class Guest implements Entity { groupId?: string; color?: string; status?: GuestStatus; + children?: Guest[]; - constructor(id?: string, name?: string, group_name?: string, groupId?: string, color?: string, status?: GuestStatus) { + constructor(id?: string, name?: string, group_name?: string, groupId?: string, color?: string, status?: GuestStatus, children?: Guest[]) { this.id = id; this.name = name; this.group_name = group_name; this.groupId = groupId; this.color = color; this.status = status; + this.children = children; } } export class GuestSerializer implements Serializable { fromJson(data: any): Guest { - return new Guest(data.id, data.name, data.group_name, data.group_id, data.color, data.status); + return new Guest(data.id, data.name, data.group_name, data.group_id, data.color, data.status, data.children); } toJson(guest: Guest): string { diff --git a/app/ui/groups/table.tsx b/app/ui/groups/table.tsx index d17fdf9..41af307 100644 --- a/app/ui/groups/table.tsx +++ b/app/ui/groups/table.tsx @@ -4,7 +4,7 @@ import { Group, GroupSerializer } from '@/app/lib/group'; import TableOfContents from '../components/table-of-contents'; -import { PencilIcon, TrashIcon } from '@heroicons/react/24/outline'; +import { MapPinIcon, PencilIcon, TrashIcon } from '@heroicons/react/24/outline'; import { AbstractApi } from '@/app/api/abstract-api'; import { TreeTable } from 'primereact/treetable'; import { Column } from 'primereact/column'; @@ -19,56 +19,85 @@ export default function GroupsTable({ groups, onUpdate, onEdit }: { const api = new AbstractApi(); const serializer = new GroupSerializer(); - const nodes:TreeNode[] = []; + const actions = (group: Group) => ( +
+ { api.destroy(serializer, group, onUpdate) }} /> + onEdit(group)} /> +
+ ); + + const index = groups.reduce((acc, group) => { + if (group.id) { + acc.set(group.id, group) + } + + return acc; + }, new Map()); + + groups.forEach(group => { + if (group.parentId) { + const parent = index.get(group.parentId); + if (parent) { + if (!parent.children) { + parent.children = []; + } + parent.children.push(group); + } + } + }); + + const renderTree = (group: Group): TreeNode => { + const childrenAttendance = (group.children || []).reduce((acc, child) => { + acc.confirmed += child.attendance?.confirmed || 0; + acc.tentative += child.attendance?.tentative || 0; + acc.invited += child.attendance?.invited || 0; + acc.declined += child.attendance?.declined || 0; + acc.considered += child.attendance?.considered || 0; + acc.total += child.attendance?.total || 0; + return acc; + }, { confirmed: 0, tentative: 0, invited: 0, declined: 0, considered: 0, total: 0 }); + + return { + id: group.id, + key: group.id, + label: group.name, + data: { + name: group.name, + color:
, + confirmed: childrenAttendance.confirmed + (group.attendance?.confirmed || 0), + tentative: childrenAttendance.tentative + (group.attendance?.tentative || 0), + pending: childrenAttendance.invited + (group.attendance?.invited || 0), + declined: childrenAttendance.declined + (group.attendance?.declined || 0), + considered: childrenAttendance.considered + (group.attendance?.considered || 0), + total: childrenAttendance.total + (group.attendance?.total || 0), + actions: actions(group), + }, + children: group.children?.map(renderTree), + } + } + + const nodes: TreeNode[] = groups + .filter(group => !group.parentId) + .map(renderTree) const headers = ['Name', 'Color', 'Confirmed', 'Tentative', 'Pending', 'Declined', 'Considered', 'Total', 'Actions']; + const rowClassName = () => { + return { 'border-b odd:bg-white even:bg-gray-50 hover:bg-gray-100': true }; + } return ( <> - - { headers.map((header, index) => )} - + + + + + + + + + + - - ( - - - {group.name} - - -
- - - {group.attendance?.confirmed} - - - {group.attendance?.tentative} - - - {group.attendance?.invited} - - - {group.attendance?.declined} - - - {group.attendance?.considered} - - - {group.attendance?.total} - - -
- { api.destroy(serializer, group, onUpdate) }} /> - onEdit(group)} /> -
- - - )} - /> ) }