Display a tab with information about groups #98
| @ -1,24 +1,34 @@ | ||||
| /* Copyright (C) 2024 Manuel Bustillo*/ | ||||
| 
 | ||||
| import { lusitana } from '@/app/ui/fonts'; | ||||
| import AffinityGroupsTree from '@/app/ui/guests/affinity-groups-tree'; | ||||
| import GuestsTable from '@/app/ui/guests/table'; | ||||
| import React, { Suspense } from 'react'; | ||||
| import SkeletonTable from '@/app/ui/guests/skeleton-row'; | ||||
| 
 | ||||
| 
 | ||||
| import { TabView, TabPanel } from 'primereact/tabview'; | ||||
| import GroupsTable from '@/app/ui/groups/table'; | ||||
| 
 | ||||
| export default function Page() { | ||||
|   return ( | ||||
|     <div className="w-full"> | ||||
|       <AffinityGroupsTree /> | ||||
| 
 | ||||
|       <h1 className={`${lusitana.className} text-2xl py-4`}>Guests</h1> | ||||
|       <div className="flex w-full items-center justify-between"> | ||||
|         <Suspense fallback={<SkeletonTable />}> | ||||
|           <GuestsTable /> | ||||
|         </Suspense> | ||||
|       </div> | ||||
|       <TabView> | ||||
|         <TabPanel header="Guests" leftIcon="pi pi-users mx-2"> | ||||
|           <div className="flex w-full items-center justify-between"> | ||||
|             <Suspense fallback={<SkeletonTable />}> | ||||
|               <GuestsTable /> | ||||
|             </Suspense> | ||||
|           </div> | ||||
|         </ TabPanel> | ||||
|         <TabPanel header="Groups" leftIcon="pi pi-sitemap mx-2"> | ||||
|           <div className="flex w-full items-center justify-between"> | ||||
|             <Suspense fallback={<SkeletonTable />}> | ||||
|               <GroupsTable /> | ||||
|             </Suspense> | ||||
|           </div> | ||||
|         </ TabPanel> | ||||
|       </ TabView> | ||||
|     </div> | ||||
| 
 | ||||
|   ); | ||||
| } | ||||
| @ -47,8 +47,19 @@ export type Group = { | ||||
|   guest_count: number; | ||||
|   icon: string; | ||||
|   children: Group[]; | ||||
|   color: string; | ||||
|   attendance?: AttendanceSummary | ||||
| }; | ||||
| 
 | ||||
| export type AttendanceSummary = { | ||||
|   considered: number; | ||||
|   invited: number; | ||||
|   confirmed: number; | ||||
|   declined: number; | ||||
|   tentative: number; | ||||
|   total: number; | ||||
| } | ||||
| 
 | ||||
| export type Invoice = { | ||||
|   id: string; | ||||
|   customer_id: string; | ||||
|  | ||||
							
								
								
									
										75
									
								
								app/ui/groups/table.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										75
									
								
								app/ui/groups/table.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,75 @@ | ||||
| /* Copyright (C) 2024 Manuel Bustillo*/ | ||||
| 
 | ||||
| 'use client'; | ||||
| 
 | ||||
| import TableOfContents from '../components/table-of-contents'; | ||||
| import React, { useState } from 'react'; | ||||
| import { Group } from '@/app/lib/definitions'; | ||||
| 
 | ||||
| export default function GroupsTable() { | ||||
|   const [groups, setGroups] = useState<Array<Group>>([]); | ||||
|   const [groupsLoaded, setGroupsLoaded] = useState(false); | ||||
| 
 | ||||
|   function loadGroups() { | ||||
|     fetch("/api/groups") | ||||
|       .then((response) => response.json()) | ||||
|       .then((data) => { | ||||
|         setGroups(data.map((record: any) => { | ||||
|           return ({ | ||||
|             id: record.id, | ||||
|             name: record.name, | ||||
|             color: record.color, | ||||
|             attendance: { | ||||
|               considered: record.considered, | ||||
|               invited: record.invited, | ||||
|               confirmed: record.confirmed, | ||||
|               tentative: record.tentative, | ||||
|               declined: record.declined, | ||||
|               total: record.total, | ||||
|             } | ||||
|           }); | ||||
|         })); | ||||
|         setGroupsLoaded(true); | ||||
|       }, (error) => { | ||||
|         return []; | ||||
|       }); | ||||
|   } | ||||
| 
 | ||||
|   !groupsLoaded && loadGroups(); | ||||
| 
 | ||||
|   return ( | ||||
|     <TableOfContents | ||||
|       headers={['Name', 'Color', 'Confirmed', 'Tentative', 'Pending', 'Declined', 'Considered', 'Total']} | ||||
|       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"> | ||||
|             {group.attendance?.confirmed} | ||||
|           </td> | ||||
|           <td className="px-6"> | ||||
|             {group.attendance?.tentative} | ||||
|           </td> | ||||
|           <td className="px-6"> | ||||
|             {group.attendance?.invited} | ||||
|           </td> | ||||
|           <td className="px-6"> | ||||
|             {group.attendance?.declined} | ||||
|           </td> | ||||
|           <td className="px-6"> | ||||
|             {group.attendance?.considered} | ||||
|           </td> | ||||
|           <td className="px-6"> | ||||
|             {group.attendance?.total} | ||||
|           </td> | ||||
|         </tr> | ||||
|       )} | ||||
|     /> | ||||
|   ) | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user