Skip to content

Commit 21fb2d4

Browse files
committed
docs(overview): align implementation plan with UI standards
中文:使总览页实施计划符合现有 UI、可访问性与测试规范。
1 parent fb033aa commit 21fb2d4

1 file changed

Lines changed: 66 additions & 11 deletions

File tree

docs/superpowers/plans/2026-07-21-executive-overview.md

Lines changed: 66 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
- `packages/app/src/lib/overview-data.ts` — pure model/hardware aggregation and ranking.
4646
- `packages/app/src/lib/overview-data.test.ts` — reducer tests.
4747
- `packages/app/src/lib/overview-data.server.ts` — official DB/fixture loading for all active models.
48+
- `packages/app/src/lib/overview-data.server.test.ts` — server orchestration and active-model ordering tests.
4849
- `packages/app/src/components/overview/overview-page.tsx` — locale-aware server-rendered scorecard.
4950
- `packages/app/src/components/overview/overview-detail-link.tsx` — tracked drill-down link.
5051
- `packages/app/src/app/overview/page.tsx` — English route and metadata.
@@ -475,9 +476,27 @@ git commit -m "feat(overview): aggregate executive inference metrics" \
475476
**Files:**
476477

477478
- Create: `packages/app/src/lib/overview-data.server.ts`
479+
- Create: `packages/app/src/lib/overview-data.server.test.ts`
478480
- Create: `packages/app/cypress/fixtures/api/overview.json`
479481

480-
- [ ] **Step 1: Create the server loader**
482+
- [ ] **Step 1: Write the server-orchestration test first**
483+
484+
Mock only the DB boundary (`getCachedBenchmarks`) and assert that `getOverviewPageData()`:
485+
486+
1. Requests every `DEFAULT_MODELS` entry through its complete `DISPLAY_MODEL_TO_DB` key list.
487+
2. Returns models in the existing `DEFAULT_MODELS` product order.
488+
3. Computes the page `latestDate` from the newest hardware-result date.
489+
4. Keeps an empty model summary in the returned list.
490+
491+
Run the test before creating the loader:
492+
493+
```bash
494+
pnpm --dir packages/app exec vitest run src/lib/overview-data.server.test.ts
495+
```
496+
497+
Expected: FAIL because `overview-data.server.ts` does not exist.
498+
499+
- [ ] **Step 2: Create the server loader**
481500

482501
Implement `overview-data.server.ts`:
483502

@@ -505,7 +524,7 @@ export async function getOverviewPageData(): Promise<OverviewPageData> {
505524
}
506525
```
507526

508-
- [ ] **Step 2: Add a small precomputed page fixture**
527+
- [ ] **Step 3: Add a small precomputed page fixture**
509528

510529
Create `overview.json` using the exact `OverviewPageData` schema. Include all five current default models in product order:
511530

@@ -517,18 +536,20 @@ Create `overview.json` using the exact `OverviewPageData` schema. Include all fi
517536

518537
Use deterministic dates such as `2026-07-20` and simple tier values `[1200, 1000, 800, 600]` for a winner and `[1000, 800, 600, 400]` for a runner-up. Store `null` for an unreachable 100 tok/s/user tier in at least one non-winning hardware result.
519538

520-
- [ ] **Step 3: Typecheck the server boundary**
539+
- [ ] **Step 4: Verify the server boundary**
521540

522541
```bash
542+
pnpm --dir packages/app exec vitest run src/lib/overview-data.server.test.ts
523543
pnpm typecheck
524544
```
525545

526-
Expected: exit code 0.
546+
Expected: both commands exit 0.
527547

528-
- [ ] **Step 4: Commit server loading and fixture**
548+
- [ ] **Step 5: Commit server loading and fixture**
529549

530550
```bash
531551
git add packages/app/src/lib/overview-data.server.ts \
552+
packages/app/src/lib/overview-data.server.test.ts \
532553
packages/app/cypress/fixtures/api/overview.json
533554
git commit -m "feat(overview): load active-model scorecard data" \
534555
-m "中文:加载活跃模型总览评分数据。"
@@ -554,6 +575,7 @@ Create `overview-detail-link.tsx`:
554575

555576
import Link from 'next/link';
556577
import type { ReactNode } from 'react';
578+
import { ArrowRight } from 'lucide-react';
557579

558580
import { track } from '@/lib/analytics';
559581

@@ -569,10 +591,14 @@ export function OverviewDetailLink({
569591
return (
570592
<Link
571593
href={href}
572-
className="text-brand hover:underline font-medium"
594+
className="group inline-flex items-center gap-1 rounded-sm font-medium text-foreground underline decoration-brand/50 underline-offset-4 transition-colors hover:decoration-brand focus-visible:outline-none focus-visible:ring-[3px] focus-visible:ring-ring/50 motion-reduce:transition-none"
573595
onClick={() => track('overview_model_detail_clicked', { model })}
574596
>
575597
{children}
598+
<ArrowRight
599+
aria-hidden="true"
600+
className="size-4 transition-transform group-hover:translate-x-0.5 motion-reduce:transition-none"
601+
/>
576602
</Link>
577603
);
578604
}
@@ -594,13 +620,18 @@ Define a colocated `STRINGS` dictionary covering every visible phrase: page titl
594620
Render:
595621

596622
- one `<h1>` and short methodology line;
597-
- a single semantic `<table data-testid="overview-scorecard">` in `overflow-x-auto`;
623+
- one repository `Card` with `className="overflow-hidden p-0"`; never one card per model;
624+
- a single semantic `<table data-testid="overview-scorecard">` with a localized `sr-only` caption, `scope="col"` headers, `scope="row"` model cells, and a grouped heading over the four tier columns;
625+
- the table inside a keyboard-focusable `role="region"` scroll container with `data-testid="overview-scorecard-scroll"`, `overflow-x-auto`, a visible inset focus ring, and a deliberate `min-w-[70rem]` table width;
598626
- one row per `data.models` entry;
599627
- winner hardware and selected precision in the platform cell;
628+
- an explicit localized `Best` / `第一名` text label, so winner status never depends on color alone;
600629
- winner score and tier values formatted with `Intl.NumberFormat(locale)`;
601630
- runner-up gap as `+N% vs. runner-up` / `较第二名高 N%`;
602631
- `` for no data or unreachable values;
603632
- `` beside `clamped_low` values;
633+
- screen-reader text for the unreachable dash and clamped marker;
634+
- semantic `<time dateTime={...}>` for freshness dates;
604635
- detail link built with:
605636

606637
```ts
@@ -613,7 +644,11 @@ const query = new URLSearchParams({
613644
const detailHref = `${locale === 'zh' ? '/zh/inference' : '/inference'}?${query}`;
614645
```
615646

616-
Use a sticky first column and `tabular-nums`. Do not add selectors, charts, accordions, client-side sorting, or an AUC card.
647+
Use the repository's existing semantic tokens and table language: muted headers, thin `border-border/50` dividers, `hover:bg-muted/30`, `focus-within:bg-muted/30`, right-aligned `tabular-nums`, and the GPU-spec table's sticky first-column divider. Use an outline `Badge` for precision at most. Do not hard-code brand/vendor colors, use `font-mono` for numbers, or use `text-primary` as the sole winner treatment. Respect `motion-reduce`.
648+
649+
Add a code comment beside the server-rendered official-data table explaining that unofficial-run overlays are intentionally inapplicable: this page is a cached, cross-model official snapshot, while uploaded runs remain available in the linked interactive inference dashboard.
650+
651+
Do not add selectors, charts, accordions, client-side sorting, AUC language, or an AUC card. Call the scalar a **weighted score** in both locales.
617652

618653
- [ ] **Step 3: Create the English route**
619654

@@ -710,6 +745,11 @@ describe('Executive overview', () => {
710745
cy.get('select').should('not.exist');
711746
cy.contains('30 tok/s/user').should('exist');
712747
cy.contains('100 tok/s/user').should('exist');
748+
cy.contains('tr', 'DeepSeek V4 Pro').within(() => {
749+
cy.contains('Best').should('exist');
750+
cy.contains('1,200').should('exist');
751+
cy.contains('+20%').should('exist');
752+
});
713753
});
714754

715755
it('shows a truthful empty state and a tracked detail link', () => {
@@ -719,20 +759,35 @@ describe('Executive overview', () => {
719759
.find('a[href^="/inference?"]')
720760
.should('have.attr', 'href')
721761
.and('include', 'i_seq=8k%2F1k');
762+
cy.contains('tr', 'Qwen3.5').should('contain.text', 'Standard decode fallback');
763+
cy.get('[data-testid="overview-scorecard"]').should('contain.text', '');
764+
cy.get('[data-testid="overview-scorecard"]').should('contain.text', '');
765+
cy.get('[data-testid="overview-scorecard"] time').first().should('have.attr', 'datetime');
722766
});
723767

724-
it('keeps the scorecard usable on mobile', () => {
768+
it('keeps the sticky model column usable while the mobile scorecard scrolls', () => {
725769
cy.viewport(375, 812);
726770
cy.visit('/overview');
727-
cy.get('[data-testid="overview-scorecard"]').should('be.visible');
728-
cy.contains('DeepSeek V4 Pro').should('be.visible');
771+
cy.get('[data-testid="overview-scorecard-scroll"]').then(($region) => {
772+
expect($region[0].scrollWidth).to.be.greaterThan($region[0].clientWidth);
773+
$region[0].scrollLeft = 500;
774+
});
775+
cy.contains('th[scope="row"]', 'DeepSeek V4 Pro').then(($cell) => {
776+
const before = $cell[0].getBoundingClientRect().left;
777+
cy.get('[data-testid="overview-scorecard-scroll"]').scrollTo('right');
778+
cy.wrap($cell).then(($scrolledCell) => {
779+
expect($scrolledCell[0].getBoundingClientRect().left).to.be.closeTo(before, 2);
780+
});
781+
});
729782
});
730783

731784
it('renders the Chinese sibling and language toggle', () => {
732785
cy.visit('/zh/overview');
733786
cy.contains('h1', 'AI 推理高管总览').should('exist');
734787
cy.get('[data-testid="language-toggle"]').should('have.attr', 'href', '/overview');
735788
cy.contains('tr', 'GLM5.2').should('contain.text', '暂无 8K/1K 单轮基准测试数据');
789+
cy.contains('加权评分').should('exist');
790+
cy.contains('第一名').should('exist');
736791
});
737792
});
738793
```

0 commit comments

Comments
 (0)