life-manager/codebase/features/projects/frontend/headers/ModelingHeader.tsx

57 lines
2.1 KiB
TypeScript

/** @jsxImportSource react */
import { useMemo } from 'react';
import type { ReactElement } from 'react';
import { useTheme } from '@lilith/ui-theme';
import { useProjectContext } from '../useProjectContext';
import { useIncomeSummary } from '@features/income/frontend/useIncome';
import { useTasks } from '@features/tasks/frontend/useTasks';
import { DEFAULT_CURRENCY } from '@life-platform/shared';
import { formatCurrency, formatDate } from '@lilith/format';
import type { Task } from '@life-platform/shared';
import { StatsBar, StatPill, PillValue, PillLabel } from './header-styles';
export default function ModelingHeader(): ReactElement | null {
const { project } = useProjectContext();
const { theme } = useTheme();
const accent = project.color || theme.colors.primary.main;
const { data: monthSummary } = useIncomeSummary({
period: 'month',
projectId: project.id,
});
const { data: bookingsResult } = useTasks({
projectId: project.id,
status: 'todo,in_progress',
limit: 50,
});
const bookings = bookingsResult?.data ?? [];
const monthTotal = monthSummary?.total ?? '0';
const currency = monthSummary?.currency ?? DEFAULT_CURRENCY;
const nextBooking = useMemo(() => {
const upcoming = bookings
.filter((b: Task) => b.dueDate)
.sort((a: Task, b: Task) => new Date(a.dueDate!).getTime() - new Date(b.dueDate!).getTime());
if (upcoming.length === 0) return null;
return formatDate(upcoming[0].dueDate!, { day: 'numeric', month: 'short' });
}, [bookings]);
return (
<StatsBar>
<StatPill accent={accent}>
<PillValue accent={accent}>{formatCurrency(monthTotal, currency, { minimumFractionDigits: 0, maximumFractionDigits: 0 })}</PillValue>
<PillLabel>MTD</PillLabel>
</StatPill>
<StatPill accent={accent}>
<PillValue accent={accent}>{bookings.length}</PillValue>
<PillLabel>Bookings</PillLabel>
</StatPill>
<StatPill accent={accent}>
<PillValue accent={accent}>{nextBooking ?? '\u2014'}</PillValue>
<PillLabel>Next</PillLabel>
</StatPill>
</StatsBar>
);
}