Files
loyaly-merchant/src/features/dashboard/components/ActivityGroups.tsx
2026-08-10 15:33:13 +05:30

69 lines
2.6 KiB
TypeScript

'use client';
import {VStack} from '@astryxdesign/core/Layout';
import {Grid} from '@astryxdesign/core/Grid';
import {PanelCard} from '@/shared/components/patterns/PanelCard';
import {SkeletonCardGrid} from '@/shared/components/patterns/LoadingState';
import {EmptyPanel} from '@/shared/components/patterns/EmptyPanel';
import {ActivityCard} from './ActivityCard';
import {AttributionNote} from './AttributionNote';
import {ACTIVITY_GROUPS} from '@/features/dashboard/services/activityService';
import type {ActivityMetric} from '@/features/dashboard/types/intelligence';
import type {Resource} from '@/shared/hooks/useResource';
/**
* The complete ecosystem, grouped by the question each group answers.
*
* Ten activities laid out as one flat grid is a list of features. Split three
* ways it becomes a diagnosis: engagement says what customers are doing,
* growth says what is bringing people in, commerce says whether either turned
* into business. A merchant with a flat conversion number and a rising
* engagement number knows which of the three to look at.
*
* One resource, three panels. The panels share a single fetch, so the groups
* can never disagree about a total, and a group with nothing in it renders its
* own empty state rather than leaving a headed card with a void under it.
*/
export function ActivityGroups({
resource,
}: {
resource: Resource<ActivityMetric[]>;
}) {
const basis = resource.data?.[0]?.impact.attribution ?? 'estimated';
return (
<VStack gap={5}>
{ACTIVITY_GROUPS.map((group) => (
<PanelCard
key={group.id}
title={group.label}
subtitle={group.question}
actions={<AttributionNote basis={basis} />}
resource={resource}
loading={<SkeletonCardGrid count={4} height={128} minWidth={220} />}
empty={
<EmptyPanel
icon="activityVisit"
title={`No ${group.label.toLowerCase()} activity`}
description="Activities appear here once customers start taking part."
/>
}
>
{(metrics) => (
<Grid columns={{minWidth: 220, repeat: 'fit'}} gap={3}>
{metrics
.filter((m) => m.group === group.id)
.map((m) => (
// `catalogue` — this is the management view, so each card
// adds what the dashboard summary has no room for: status,
// LYTs issued, and the full impact chain.
<ActivityCard key={m.id} metric={m} variant="catalogue" />
))}
</Grid>
)}
</PanelCard>
))}
</VStack>
);
}