Load dashboard information from the backend
All checks were successful
Playwright Tests / test (pull_request) Has been skipped
Check usage of free licenses / build-static-assets (pull_request) Successful in 18s
Add copyright notice / copyright_notice (pull_request) Successful in 20s

This commit is contained in:
Manuel Bustillo 2024-12-11 08:38:50 +01:00
parent c57bdec9fc
commit 7dbfbb76b7
4 changed files with 77 additions and 44 deletions

View File

@ -1,9 +1,26 @@
/* Copyright (C) 2024 Manuel Bustillo*/
'use client'
import { GlobalSummary as Summary } from '@/app/lib/definitions';
import { getSlug } from '@/app/lib/utils';
import GlobalSummary from '@/app/ui/dashboard/global-summary';
import { useEffect, useState } from 'react';
export default function Page() {
return(
<GlobalSummary />
const [globalSummary, setGlobalSummary] = useState<Summary | undefined>(undefined);
function refreshSummary() {
fetch(`/api/${getSlug()}/summary`)
.then((response) => response.json())
.then((data) => {
setGlobalSummary(data);
})
}
useEffect(refreshSummary, []);
return (
globalSummary && <GlobalSummary summary={globalSummary} />
);
}

View File

@ -1,5 +1,6 @@
/* Copyright (C) 2024 Manuel Bustillo*/
import { AttendanceSummary } from "./group";
import { Guest } from "./guest";
export interface Entity {
@ -37,4 +38,23 @@ export type Captcha = {
export type StructuredErrors = {
[key: string]: string[] | string;
};
};
export type GlobalSummary = {
expenses: ExpenseSummary;
guests: AttendanceSummary
}
export type ExpenseSummary = {
projected: ExpensePossibleSummary;
confirmed: ExpensePossibleSummary;
status: StatusSummary;
}
export type ExpensePossibleSummary = {
total: number;
guests: number;
}
export type StatusSummary = {
paid: number;
}

View File

@ -21,7 +21,7 @@ const colorClasses = (style: Style) => {
}
}
export async function MainCard({ amount, title, subtitle, style, iconName }:
export function MainCard({ amount, title, subtitle, style, iconName }:
{
amount: string,
title: string,
@ -42,7 +42,7 @@ export async function MainCard({ amount, title, subtitle, style, iconName }:
);
}
export async function SecondaryCard({ amount, title, iconName, style }: { amount: string, title: string, iconName: keyof typeof HeroIcon, style: Style }) {
export function SecondaryCard({ amount, title, iconName, style }: { amount: string, title: string, iconName: keyof typeof HeroIcon, style: Style }) {
return (
<div className={`h-12 w-80 m-1 p-2 text-white flex flex-row items-center ${colorClasses(style)}`}>
<Icon className="m-3 h-7 w-7" name={iconName} />

View File

@ -1,45 +1,41 @@
/* Copyright (C) 2024 Manuel Bustillo*/
import { GlobalSummary } from '@/app/lib/definitions';
import { MainCard, SecondaryCard } from '../components/dashboard-cards';
export default async function GlobalSummary() {
return (
<div className="my-4">
<div className="flex flex-row w-full my-2">
<MainCard style="green" amount="65000€" title="Projected" subtitle="150 guests" iconName="ArrowTrendingUpIcon" />
<div className="flex flex-col">
<MainCard amount="10000€" title="Paid already" iconName="Square3Stack3DIcon" style='blue' />
<MainCard amount="198€" title="/ guest" iconName="UserIcon" style='blue' />
</div>
<div className="flex flex-col">
<MainCard amount="78000€" title="Max." subtitle='200 guests' iconName="ChevronDoubleUpIcon" style="orange" />
<MainCard amount="45000€" title="Min." subtitle="125 guests" iconName="ChevronDoubleDownIcon" style="green" />
</div>
</div>
<div className="flex flex-row w-full my-2">
<MainCard style="blue" amount="150" title="Invites sent" iconName="UsersIcon" />
<div className="flex flex-col">
<SecondaryCard amount="31%" title="confirmed (27 guests)" iconName="CheckIcon" style='green' />
<SecondaryCard amount="5%" title="declined (8 guests)" iconName="XMarkIcon" style='red' />
</div>
<div className="flex flex-col">
<SecondaryCard amount="17%" title="tentative (14 guests)" iconName="QuestionMarkCircleIcon" style='orange' />
<SecondaryCard amount="65%" title="awaiting (72 guests)" iconName="EllipsisHorizontalIcon" style='gray' />
</div>
</div>
<div className="flex flex-row w-full my-2">
<MainCard style="blue" amount="5" title="Table simulations" iconName="ServerStackIcon" />
<MainCard style="blue" amount="9" title="Bus simulations" iconName="TruckIcon" />
<MainCard style="blue" amount="98" title="QR codes" iconName="QrCodeIcon" />
</div>
export default function GlobalSummary({ summary }: { summary: GlobalSummary }) {
return (
<div className="my-4">
<div className="flex flex-row w-full my-2">
<div className="flex flex-col">
<MainCard amount={`${summary.expenses.projected.total}`} title="Projected" subtitle={`${summary.expenses.projected.guests} guests`} style="blue" iconName="ArrowTrendingUpIcon" />
<MainCard amount={`${Math.round(summary.expenses.projected.total / summary.expenses.projected.guests)}`} title="/ guest" iconName="UserIcon" style='blue' />
</div>
);
<div className="flex flex-col">
<MainCard amount={`${summary.expenses.confirmed.total}`} title="Min." subtitle={`${summary.expenses.confirmed.guests} guests`} iconName="ChevronDoubleDownIcon" style="green" />
<MainCard amount={`${Math.round(summary.expenses.confirmed.total / summary.expenses.confirmed.guests)}`} title="/ guest" iconName="UserIcon" style='green' />
</div>
<div className="flex flex-col">
<MainCard amount={`${summary.expenses.status.paid}`} title="Paid already" subtitle={`${Math.round(summary.expenses.status.paid / summary.expenses.projected.total * 100)}% of projected`} iconName="CheckIcon" style='blue' />
<MainCard amount={`${summary.expenses.projected.total - summary.expenses.status.paid}`} title="To pay" subtitle={`${100 - Math.round(summary.expenses.status.paid / summary.expenses.projected.total * 100)}% of projected`} iconName="BanknotesIcon" style="orange" />
</div>
</div>
<div className="flex flex-row w-full my-2">
<MainCard style="blue" amount={summary.guests.total.toString()} title="Invites sent" iconName="UsersIcon" />
<div className="flex flex-col">
<SecondaryCard amount={`${Math.round(summary.guests.confirmed / summary.guests.total * 100)}%`} title={`confirmed (${summary.guests.confirmed} guests)`} iconName="CheckIcon" style='green' />
<SecondaryCard amount={`${Math.round(summary.guests.declined / summary.guests.total * 100)}%`} title={`declined (${summary.guests.declined} guests)`} iconName="XMarkIcon" style='red' />
</div>
<div className="flex flex-col">
<SecondaryCard amount={`${Math.round(summary.guests.tentative / summary.guests.total * 100)}%`} title={`tentative (${summary.guests.tentative} guests)`} iconName="QuestionMarkCircleIcon" style='orange' />
<SecondaryCard amount={`${Math.round(summary.guests.invited / summary.guests.total * 100)}%`} title={`awaiting (${summary.guests.invited} guests)`} iconName="EllipsisHorizontalIcon" style='gray' />
</div>
</div>
</div>
);
}