Load dashboard information from the backend
This commit is contained in:
		
							parent
							
								
									c57bdec9fc
								
							
						
					
					
						commit
						7dbfbb76b7
					
				@ -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} />
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
@ -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;
 | 
			
		||||
}
 | 
			
		||||
@ -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} />
 | 
			
		||||
 | 
			
		||||
@ -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>
 | 
			
		||||
  );
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user