first commit

This commit is contained in:
2026-08-05 15:26:50 +05:30
parent c000e5dc95
commit 2c8c394795
126 changed files with 10824 additions and 302 deletions

View File

@@ -0,0 +1,83 @@
'use client';
import {VStack} from '@astryxdesign/core/Layout';
import {Grid} from '@astryxdesign/core/Grid';
import {PageHeader} from '@/components/primitives/PageHeader';
import {ChartCard} from '@/components/charts/ChartCard';
import {BarChartView} from '@/components/charts/BarChartView';
import {StaffKpis} from '@/features/staff/StaffKpis';
import {StaffGrid} from '@/features/staff/StaffGrid';
import {AttendanceChart} from '@/features/staff/AttendanceChart';
import {Leaderboard} from '@/features/staff/Leaderboard';
import {useResource} from '@/lib/api/useResource';
import {endpoints} from '@/lib/api/client';
import {useWorkspace} from '@/components/shell/WorkspaceProvider';
import {storeName} from '@/lib/mock/stores';
import {formatCompact} from '@/lib/format';
/**
* The team.
*
* Ordered attendance-first: the question that brings a merchant here in the
* morning is "who is in today", and only then "who is performing".
*/
export default function StaffPage() {
const {storeId, range} = useWorkspace();
const scope = {range, storeId};
const summary = useResource(endpoints.staffSummary(scope), {
// A summary object is never "empty" the way a list is — zero present is
// a real answer, not an absence of data.
isEmpty: () => false,
});
const staff = useResource(endpoints.staff(scope));
const attendance = useResource(endpoints.staffAttendance(scope));
const scopeLabel = storeId === 'all' ? 'all stores' : storeName(storeId);
return (
<VStack gap={5}>
<PageHeader
title="Staff"
description={`Attendance, sales contribution and performance across ${scopeLabel}.`}
/>
<StaffKpis resource={summary} />
<Grid columns={{minWidth: 360, max: 2, repeat: 'fit'}} gap={4}>
<AttendanceChart resource={attendance} />
<ChartCard
title="Sales by team member"
subtitle="Top 8 by transactions in the selected period"
resource={staff}
>
{(rows) => (
<BarChartView
data={[...rows]
.sort((a, b) => b.salesCount - a.salesCount)
.slice(0, 8)
.map((m) => ({
// First name only — full names overflow the axis at this
// width and the leaderboard below carries the detail.
name: m.name.split(' ')[0],
sales: m.salesCount,
rewards: m.rewardsIssued,
}))}
xKey="name"
yFormat={formatCompact}
series={[
{key: 'sales', label: 'Sales'},
{key: 'rewards', label: 'Rewards issued'},
]}
/>
)}
</ChartCard>
</Grid>
<Leaderboard resource={staff} />
<StaffGrid resource={staff} />
</VStack>
);
}