Files
please-pay-me/web/src/pages/OverviewPage.tsx
T
2026-09-21 04:06:43 +03:00

113 lines
3.5 KiB
TypeScript

import { Link } from "react-router-dom";
import { formatDate, formatMoney } from "../api";
import { useCabinet } from "../cabinet/CabinetContext";
import { Banner, PageHeader } from "../components/ui";
import {
AppListSection,
AppListTile,
AppProgress,
AppSkeleton,
AppText,
} from "../ui";
function SkeletonOverview() {
return (
<section aria-busy="true" aria-label="Загрузка">
<AppSkeleton width="8rem" />
<div style={{ height: 16 }} />
<AppSkeleton width="16rem" height={34} />
<div style={{ height: 20 }} />
<AppListSection header="Сводка">
{Array.from({ length: 4 }).map((_, i) => (
<AppListTile key={i} title={<AppSkeleton width="40%" />} value={<AppSkeleton width="4rem" />} />
))}
</AppListSection>
</section>
);
}
export function OverviewPage() {
const { status, loading, noBudget } = useCabinet();
if (loading && !status && !noBudget) {
return <SkeletonOverview />;
}
if (noBudget) {
return (
<>
<PageHeader
title="Задай бюджет"
description="Укажи сумму и дату зарплаты — посчитаем дневной лимит."
/>
<Link to="/budgets" className="app-btn app-btn--filled app-btn--large app-btn--expanded">
Перейти к бюджетам
</Link>
</>
);
}
if (!status) {
return (
<PageHeader
title="Обзор"
description="Не удалось загрузить статус. Обнови страницу или проверь API."
/>
);
}
return (
<>
<PageHeader
eyebrow={`${status.budget.name} · ${formatDate(status.budget.start_date)}${formatDate(status.budget.end_date)} · ещё ${status.days_left} дн.${status.budget.is_active ? "" : " · выкл"}`}
title="Обзор"
description={
<>
Лимит на сегодня {formatMoney(status.daily_limit)}
{status.spent_today > 0
? ` · израсходовано ${formatMoney(status.spent_today)}`
: ""}
</>
}
/>
<AppText variant="footnote">Остаток</AppText>
<AppText variant="largeTitle" as="p">
{formatMoney(status.remaining)}
</AppText>
<div className="progress">
<AppProgress
spent={status.total_spent}
total={status.budget.total_amount}
label="Использование бюджета"
/>
</div>
<AppListSection header="Сводка">
<AppListTile title="Бюджет" value={formatMoney(status.budget.total_amount)} />
<AppListTile title="Потрачено" value={formatMoney(status.total_spent)} />
<AppListTile title="Сегодня" value={formatMoney(status.spent_today)} />
<AppListTile
title="Ещё сегодня"
value={
<span className={status.remaining_today < 0 ? "is-warn" : undefined}>
{formatMoney(status.remaining_today)}
</span>
}
/>
</AppListSection>
{(status.is_over_budget || status.is_over_daily || status.is_expired) && (
<Banner>
{status.is_expired
? "Период закончился — задай новый бюджет."
: status.is_over_budget
? "Бюджет превышен."
: "Сегодняшний лимит превышен — завтра пересчитается."}
</Banner>
)}
</>
);
}