Compare commits
1 Commits
main
...
groups-tre
Author | SHA1 | Date | |
---|---|---|---|
c9dab33e85 |
@ -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<Guest> {
|
||||
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 {
|
||||
|
@ -4,8 +4,11 @@
|
||||
|
||||
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';
|
||||
import { TreeNode } from 'primereact/treenode';
|
||||
|
||||
export default function GroupsTable({ groups, onUpdate, onEdit }: {
|
||||
groups: Group[],
|
||||
@ -16,45 +19,85 @@ export default function GroupsTable({ groups, onUpdate, onEdit }: {
|
||||
const api = new AbstractApi<Group>();
|
||||
const serializer = new GroupSerializer();
|
||||
|
||||
const actions = (group: Group) => (
|
||||
<div className="flex flex-row items-center">
|
||||
<TrashIcon className='size-6 cursor-pointer' onClick={() => { api.destroy(serializer, group, onUpdate) }} />
|
||||
<PencilIcon className='size-6 cursor-pointer' onClick={() => onEdit(group)} />
|
||||
</div>
|
||||
);
|
||||
|
||||
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: <div className="w-8 h-8 rounded-full" style={{ backgroundColor: group.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 (
|
||||
<TableOfContents
|
||||
headers={['Name', 'Color', 'Confirmed', 'Tentative', 'Pending', 'Declined', 'Considered', 'Total', 'Actions']}
|
||||
caption='Groups'
|
||||
elements={groups}
|
||||
rowRender={(group) => (
|
||||
<tr key={group.id} className="bg-white border-b odd:bg-white odd:dark:bg-gray-900 even:bg-gray-50 even:dark:bg-gray-800">
|
||||
<td scope="row" className="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
||||
{group.name}
|
||||
</td>
|
||||
<td className="px-6">
|
||||
<div className="w-8 h-8 rounded-full" style={{ backgroundColor: group.color }}></div>
|
||||
</td>
|
||||
<td className="px-6 text-lg">
|
||||
{group.attendance?.confirmed}
|
||||
</td>
|
||||
<td className="px-6 text-sm">
|
||||
{group.attendance?.tentative}
|
||||
</td>
|
||||
<td className="px-6 text-sm">
|
||||
{group.attendance?.invited}
|
||||
</td>
|
||||
<td className="px-6 text-sm">
|
||||
{group.attendance?.declined}
|
||||
</td>
|
||||
<td className="px-6 text-sm">
|
||||
{group.attendance?.considered}
|
||||
</td>
|
||||
<td className="px-6 text-sm">
|
||||
{group.attendance?.total}
|
||||
</td>
|
||||
<td>
|
||||
<div className="flex flex-row items-center">
|
||||
<TrashIcon className='size-6 cursor-pointer' onClick={() => { api.destroy(serializer, group, onUpdate) }} />
|
||||
<PencilIcon className='size-6 cursor-pointer' onClick={() => onEdit(group)} />
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
)}
|
||||
/>
|
||||
<>
|
||||
<TreeTable value={nodes} rowClassName={rowClassName} className='py-4'>
|
||||
<Column expander field="name" header="Name" className='w-2/5' />
|
||||
<Column field="color" header="Color" bodyClassName="text-sm" />
|
||||
<Column field="confirmed" header="Confirmed" bodyClassName="text-sm" />
|
||||
<Column field="tentative" header="Tentative" bodyClassName="text-sm" />
|
||||
<Column field="pending" header="Pending" bodyClassName="text-sm" />
|
||||
<Column field="declined" header="Declined" bodyClassName="text-sm" />
|
||||
<Column field="considered" header="Considered" bodyClassName="text-sm" />
|
||||
<Column field="total" header="Total" bodyClassName="text-sm" />
|
||||
<Column field="actions" header="Actions" />
|
||||
</TreeTable>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user