FactuGES_ERP/apps/web/src/home/ui/pages/home-page.tsx
david ef2ae58046 feat(home): implement home page with KPIs, recent activity, and attention items
- Added Spanish translations for home page components.
- Created API endpoints for fetching home-related data (customers, proformas, issued invoices).
- Developed hooks for managing home page data queries.
- Implemented home page layout with KPI cards, attention panels, monthly billing summary, and recent activity sections.
- Added error handling and loading states for improved user experience.
2026-08-13 11:04:57 +02:00

113 lines
3.8 KiB
TypeScript

import { PageErrorState } from "@erp/core/components";
import { RdxPageContainer, RdxPageHeader, RdxPageStack } from "@repo/rdx-ui/components";
import { Skeleton } from "@repo/shadcn-ui/components";
import { Link, useNavigate } from "react-router-dom";
import { useAppTranslation } from "@/locales/i18n";
import { useHomePageController } from "../../controllers/use-home-page-controller";
import { HomeAttentionPanel } from "../blocks/home-attention-panel";
import { HomeKpiCard } from "../blocks/home-kpi-card";
import { HomeMonthlyBillingSummary } from "../blocks/home-monthly-billing-summary";
import { HomeProformaStatusSummary } from "../blocks/home-proforma-status-summary";
import { HomeRecentActivity } from "../blocks/home-recent-activity";
const HomeKpisSkeleton = () => {
return (
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
{Array.from({ length: 4 }).map((_, index) => (
<div className="rounded-xl border border-border/70 bg-background p-5" key={index}>
<div className="space-y-3">
<Skeleton className="h-4 w-24" />
<Skeleton className="h-9 w-16" />
<Skeleton className="h-4 w-20" />
</div>
</div>
))}
</div>
);
};
export const HomePage = () => {
const { t } = useAppTranslation();
const navigate = useNavigate();
const ctrl = useHomePageController();
if (ctrl.hasBlockingError && ctrl.pageError) {
return (
<RdxPageContainer width="full">
<div className="mx-auto w-full max-w-3xl">
<PageErrorState
description={ctrl.pageError.description}
primaryAction={{
label: t("web.actions.retry"),
onClick: ctrl.retry,
}}
secondaryAction={{
label: t("web.actions.goHome"),
onClick: () => navigate("/proformas"),
}}
title={ctrl.pageError.title}
variant="server"
/>
</div>
</RdxPageContainer>
);
}
return (
<RdxPageContainer width="full">
<RdxPageStack spacing="default">
<RdxPageHeader
description={t("web.home.header.description")}
title={t("web.home.header.title")}
/>
{ctrl.customersTotalQuery.isLoading ||
ctrl.proformasOverviewQuery.isLoading ||
ctrl.monthlyBillingQuery.isLoading ? (
<HomeKpisSkeleton />
) : (
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
{ctrl.kpis.map((kpi) => (
<HomeKpiCard item={kpi} key={kpi.id} />
))}
</div>
)}
<div className="grid gap-4 xl:grid-cols-[minmax(0,1.2fr)_minmax(0,0.8fr)]">
<HomeAttentionPanel
hasError={ctrl.proformasOverviewQuery.isError}
items={ctrl.attentionItems}
/>
<HomeProformaStatusSummary
hasError={ctrl.proformasOverviewQuery.isError}
items={ctrl.proformaStatusSummary}
/>
</div>
<div className="grid gap-4 xl:grid-cols-[minmax(0,1.2fr)_minmax(0,0.8fr)]">
<HomeRecentActivity
hasPartialError={ctrl.recentActivityQuery.hasPartialError}
items={ctrl.recentActivityQuery.items}
/>
<HomeMonthlyBillingSummary
data={ctrl.monthlyBillingQuery.data}
hasError={ctrl.monthlyBillingQuery.isError}
/>
</div>
<p className="text-xs text-muted-foreground">
{t("web.home.footer.note")}{" "}
<Link
className="underline-offset-4 hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
to="/customer-invoices"
>
{t("web.home.footer.link")}
</Link>
</p>
</RdxPageStack>
</RdxPageContainer>
);
};