Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1288076
feat(orders): filter the admin list by paid day with dates=paid
kris1027 Oct 19, 2026
d26b265
feat(dashboard): sales dashboard on /admin with period picker, chart …
kris1027 Oct 19, 2026
080ed70
test(dashboard): exact sums, a seeded year within budget, and e2e wit…
kris1027 Oct 19, 2026
0edf547
docs(dashboard): spec 0015 in progress, build milestones ticked in scope
kris1027 Oct 19, 2026
14b4aa5
docs(dashboard): verify steps for spec 0015
kris1027 Oct 19, 2026
131ea65
fix(dashboard): native select in the period picker so it works withou…
kris1027 Oct 19, 2026
099c7ea
docs(dashboard): verify results for spec 0015
kris1027 Oct 19, 2026
50f9614
docs(specs): spec 0016 native controls in GET forms
kris1027 Oct 19, 2026
5b70a56
feat(ui): add NativeSelect with its states on the style guide
kris1027 Oct 19, 2026
590bb0c
refactor(forms): native controls in every GET form, each with a no Ja…
kris1027 Oct 19, 2026
5b1685c
chore(lint): ban the Base UI Select outside the style guide
kris1027 Oct 19, 2026
148bbad
docs(scope): native controls in GET forms built
kris1027 Oct 19, 2026
80bc667
test(forms): lint ban, NativeSelect states and preset clearing dates
kris1027 Oct 19, 2026
9978f80
fix(dashboard): log the SQLSTATE as pgCode so the logger does not red…
kris1027 Oct 19, 2026
b8c39c7
docs(dashboard): verify results for the remaining spec 0015 steps
kris1027 Oct 19, 2026
2a20f33
docs(scope): admin sales dashboard verified
kris1027 Oct 19, 2026
8cc97e3
test(dashboard): calendar edges, orders link, slow log and safe sums
kris1027 Oct 19, 2026
dc797c0
fix(dashboard): a preset picked without JavaScript replaces a custom …
kris1027 Oct 19, 2026
b16e298
refactor(dashboard): pick the change arrow from a trend field, not th…
kris1027 Oct 19, 2026
2a249ae
refactor(dashboard): build buckets and bucket sums without mutation
kris1027 Oct 19, 2026
c09acf6
refactor(dashboard): share the group key and window filters across th…
kris1027 Oct 19, 2026
9b840db
refactor(dashboard): name today's day number and the refunded groups …
kris1027 Oct 19, 2026
b690fa7
refactor(dates): addDays for the day after an orders filter's to
kris1027 Oct 19, 2026
a2b7caf
refactor(forms): one NativeSelectField for the labelled selects in GE…
kris1027 Oct 19, 2026
56ad1e3
refactor(dashboard): a StoreDays type for the first and last day pair
kris1027 Oct 19, 2026
ee45b61
refactor(dashboard): the chart takes the page's money format instead …
kris1027 Oct 19, 2026
37b240c
docs(specs): match specs 0015 and 0016 to what was built
kris1027 Oct 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 21 additions & 5 deletions app/admin/(panel)/page.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,33 @@
import type { Metadata } from "next";
import { connection } from "next/server";

import { Welcome } from "@/features/admin-auth/components/welcome";
import { adminMetadata, requireAdmin } from "@/features/admin-auth/require-admin";
import { DashboardPage } from "@/features/dashboard/components/dashboard-page";
import { dayText, parseSalesWindow } from "@/features/dashboard/period";
import { storeToday } from "@/lib/dates";
import { env } from "@/lib/env";

// Reads the session or search params at the top level; allowed to block until converted
// to Suspense (spec 0005, Caching model).
// to Suspense (spec 0005, Caching model). The figures stream in behind their own Suspense.
export const instant = false;

export function generateMetadata(): Promise<Metadata> {
return adminMetadata({ title: "Welcome" });
return adminMetadata({ title: "Dashboard" });
}

export default async function AdminHomePage() {
// spec 0015: the sales dashboard is the panel's landing page.
export default async function AdminDashboardPage({ searchParams }: PageProps<"/admin">) {
const admin = await requireAdmin();
return <Welcome admin={admin} />;
// "Today" is read per request, never baked into a prerender.
await connection();
const today = storeToday(new Date(), env.STORE_TIMEZONE);
const window = parseSalesWindow(await searchParams, today, env.STORE_TIMEZONE);
return (
<DashboardPage
adminName={admin.name}
window={window}
today={dayText(today)}
money={{ currency: env.STORE_CURRENCY, locale: env.STORE_LOCALE }}
/>
);
}
29 changes: 26 additions & 3 deletions docs/scope/scope.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ _These are recommendations to keep your build orderly, not requirements. Skip an
| 12 | Customer accounts | Slice 6 | done |
| 13 | Categories, search & filters | Slice 7 | done |
| 14 | Discounts & promo codes | Slice 8 | done |
| 15 | Admin sales dashboard | Slice 9 | planned |
| 15 | Admin sales dashboard | Slice 9 | done |
| 23 | Native controls in GET forms | Slice 9 follow up | done |
| 16 | Legal pages & cookie consent | Launch | planned |
| 17 | SEO for storefront pages | Launch | planned |
| 18 | Analytics & error tracking | Launch | planned |
Expand Down Expand Up @@ -281,10 +282,32 @@ Spec: [0014](../specs/0014-discounts-promo-codes/index.md) · code in [src/featu

## Slice 9: Insights

### 15. Admin sales dashboard
### 15. Admin sales dashboard · done
The admin landing page: revenue, order count, average order value, and best sellers over a chosen period.
**Done when:** numbers match the orders in the database for the chosen period, refunds are subtracted, and the page loads fast with a year of orders.
- [ ] Build it: `/develop admin sales dashboard`
Spec: [0015](../specs/0015-admin-sales-dashboard/index.md) · code in [src/features/dashboard/](../../src/features/dashboard/)
- [x] Design it (spec): `/architect admin sales dashboard`
- [x] Build it: `/develop admin sales dashboard`
- [x] Thin thread: store day helper, default period, live totals, the four cards on `/admin`, the Dashboard nav item, skeletons and failure logging (AC-1, AC-2, AC-5 to AC-8, AC-14)
- [x] Period picker and comparison: presets, custom range with clamp and fallback, previous window, change text, currency notice (AC-2 to AC-4, AC-7, AC-10)
- [x] Best sellers and chart: the top 10 table, shadcn Chart with day, week and month buckets, the table toggle (AC-8, AC-9, AC-11, AC-12)
- [x] Orders link and safety net: `dates=paid` on the orders list, the seeded year speed and exactness test, e2e with axe and keyboard (AC-13, AC-15, AC-16)
- [x] Verify it: `/check verify admin sales dashboard`
- [x] Test it: `/test admin sales dashboard`

## Slice 9 follow ups (from spec 0016)

### 23. Native controls in GET forms · done
Every filter, period picker and search box uses native controls (`NativeSelect`, native checkbox and radio) and a submit button, so it works before the app's JavaScript loads. The admin orders, discounts and products filters move off the Base UI Select and Checkbox, and the hand rolled selects move to `NativeSelect`. from spec 0016
**Done when:** no GET form uses a Base UI control, ESLint bans `@/components/ui/select` outside the style guide, and every GET form has a passing e2e test with the app's scripts blocked.
spec [0016](../specs/0016-native-controls-get-forms.md) · code in [src/components/ui/native-select.tsx](../../src/components/ui/native-select.tsx)
- [x] Design it (spec): `/architect native controls in GET forms`
- [x] Build it: `/develop native controls in GET forms`
- [x] `NativeSelect` from the shadcn registry, with its states on `/style-guide`
- [x] Move the orders, discounts, products, dashboard, sort and checkout country controls to native ones, with the e2e steps that open them updated
- [x] `blockAppScripts` helper and one no JavaScript e2e test per GET form, then the ESLint ban
- [x] Verify it: `/check verify native controls in GET forms`
- [x] Test it: `/test native controls in GET forms`

## Launch readiness

Expand Down
Loading
Loading