Skip to content

Commit 1b93b2a

Browse files
committed
feat(overview): add bilingual executive scorecard
中文:新增双语高管推理总览评分页。
1 parent 0b502ee commit 1b93b2a

8 files changed

Lines changed: 524 additions & 0 deletions

File tree

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
describe('Executive overview', () => {
2+
it('shows every active model without selectors', () => {
3+
cy.visit('/overview');
4+
cy.get('[data-testid="overview-scorecard"]').should('be.visible');
5+
for (const model of ['DeepSeek V4 Pro', 'Kimi', 'MiniMax M3', 'GLM5.2', 'Qwen3.5']) {
6+
cy.get('[data-testid="overview-scorecard"]').should('contain.text', model);
7+
}
8+
cy.get('select').should('not.exist');
9+
cy.contains('30 tok/s/user').should('exist');
10+
cy.contains('100 tok/s/user').should('exist');
11+
cy.contains('tr', 'DeepSeek V4 Pro').within(() => {
12+
cy.contains('Best').should('exist');
13+
cy.contains('1,200').should('exist');
14+
cy.contains('+25%').should('exist');
15+
});
16+
});
17+
18+
it('shows a truthful empty state and a tracked detail link', () => {
19+
cy.visit('/overview');
20+
cy.contains('tr', 'GLM5.2').should('contain.text', 'No 8K/1K single-turn data');
21+
cy.contains('tr', 'Qwen3.5')
22+
.find('a[href^="/inference?"]')
23+
.should('have.attr', 'href')
24+
.and('include', 'i_seq=8k%2F1k');
25+
cy.contains('tr', 'Qwen3.5').should('contain.text', 'Standard decode fallback');
26+
cy.get('[data-testid="overview-scorecard"]').should('contain.text', '—');
27+
cy.get('[data-testid="overview-scorecard"]').should('contain.text', '†');
28+
cy.get('[data-testid="overview-scorecard"] time').first().should('have.attr', 'datetime');
29+
});
30+
31+
it('keeps the sticky model column usable while the mobile scorecard scrolls', () => {
32+
cy.viewport(375, 812);
33+
cy.visit('/overview');
34+
cy.get('[data-testid="overview-scorecard-scroll"]').then(($region) => {
35+
expect($region[0].scrollWidth).to.be.greaterThan($region[0].clientWidth);
36+
$region[0].scrollLeft = 500;
37+
});
38+
cy.contains('th[scope="row"]', 'DeepSeek V4 Pro').then(($cell) => {
39+
const before = $cell[0].getBoundingClientRect().left;
40+
cy.get('[data-testid="overview-scorecard-scroll"]').scrollTo('right');
41+
cy.wrap($cell).then(($scrolledCell) => {
42+
expect($scrolledCell[0].getBoundingClientRect().left).to.be.closeTo(before, 2);
43+
});
44+
});
45+
});
46+
47+
it('renders the Chinese sibling and language toggle', () => {
48+
cy.visit('/zh/overview');
49+
cy.contains('h1', 'AI 推理高管总览').should('exist');
50+
cy.get('[data-testid="language-toggle"]').should('have.attr', 'href', '/overview');
51+
cy.contains('tr', 'GLM5.2').should('contain.text', '暂无 8K/1K 单轮基准测试数据');
52+
cy.contains('加权评分').should('exist');
53+
cy.contains('第一名').should('exist');
54+
});
55+
});
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
import type { Metadata } from 'next';
2+
3+
import { SITE_NAME, SITE_URL } from '@semianalysisai/inferencex-constants';
4+
5+
import { ExecutiveOverviewPage } from '@/components/overview/overview-page';
6+
import { enAlternates } from '@/lib/i18n';
7+
import { getOverviewPageData } from '@/lib/overview-data.server';
8+
9+
export const dynamic = 'force-dynamic';
10+
11+
const DESCRIPTION =
12+
'Compare the best official AI inference serving platforms at a fixed single-turn 8K input / 1K output workload, with weighted scores and operating-point throughput.';
13+
14+
export const metadata: Metadata = {
15+
title: 'AI Inference Executive Overview',
16+
description: DESCRIPTION,
17+
alternates: enAlternates('/overview'),
18+
openGraph: {
19+
title: `AI Inference Executive Overview | ${SITE_NAME}`,
20+
description: DESCRIPTION,
21+
url: `${SITE_URL}/overview`,
22+
type: 'website',
23+
},
24+
twitter: {
25+
card: 'summary_large_image',
26+
title: `AI Inference Executive Overview | ${SITE_NAME}`,
27+
description: DESCRIPTION,
28+
},
29+
};
30+
31+
export default async function OverviewPage() {
32+
const data = await getOverviewPageData();
33+
return (
34+
<main className="relative">
35+
<div className="container mx-auto px-4 lg:px-8 pb-8">
36+
<ExecutiveOverviewPage data={data} locale="en" />
37+
</div>
38+
</main>
39+
);
40+
}

packages/app/src/app/sitemap.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
5858

5959
return [
6060
...localizedPair('/', { lastModified: now, changeFrequency: 'daily', priority: 1 }),
61+
...localizedPair('/overview', {
62+
lastModified: now,
63+
changeFrequency: 'daily',
64+
priority: 0.9,
65+
}),
6166
...TABS.flatMap((tab) =>
6267
localizedPair(`/${tab}`, {
6368
lastModified: now,
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import type { Metadata } from 'next';
2+
3+
import { SITE_NAME, SITE_URL } from '@semianalysisai/inferencex-constants';
4+
5+
import { ExecutiveOverviewPage } from '@/components/overview/overview-page';
6+
import { ZH_OG_LOCALE, zhAlternates } from '@/lib/i18n';
7+
import { getOverviewPageData } from '@/lib/overview-data.server';
8+
9+
export const dynamic = 'force-dynamic';
10+
11+
const DESCRIPTION =
12+
'以固定单轮 8K 输入 / 1K 输出负载,对比表现最佳的官方 AI 推理服务平台,并查看加权评分与各档位吞吐量。';
13+
14+
export const metadata: Metadata = {
15+
title: 'AI 推理高管总览',
16+
description: DESCRIPTION,
17+
alternates: zhAlternates('/overview'),
18+
openGraph: {
19+
title: `AI 推理高管总览 | ${SITE_NAME}`,
20+
description: DESCRIPTION,
21+
url: `${SITE_URL}/zh/overview`,
22+
type: 'website',
23+
locale: ZH_OG_LOCALE,
24+
},
25+
twitter: {
26+
card: 'summary_large_image',
27+
title: `AI 推理高管总览 | ${SITE_NAME}`,
28+
description: DESCRIPTION,
29+
},
30+
};
31+
32+
export default async function ZhOverviewPage() {
33+
const data = await getOverviewPageData();
34+
return (
35+
<main className="relative">
36+
<div className="container mx-auto px-4 lg:px-8 pb-8">
37+
<ExecutiveOverviewPage data={data} locale="zh" />
38+
</div>
39+
</main>
40+
);
41+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use client';
2+
3+
import { ArrowRight } from 'lucide-react';
4+
import Link from 'next/link';
5+
import type { ReactNode } from 'react';
6+
7+
import { track } from '@/lib/analytics';
8+
9+
export function OverviewDetailLink({
10+
href,
11+
model,
12+
children,
13+
}: {
14+
href: string;
15+
model: string;
16+
children: ReactNode;
17+
}) {
18+
return (
19+
<Link
20+
href={href}
21+
className="group inline-flex items-center gap-1 whitespace-nowrap 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"
22+
onClick={() => track('overview_model_detail_clicked', { model })}
23+
>
24+
{children}
25+
<ArrowRight
26+
aria-hidden="true"
27+
className="size-4 transition-transform group-hover:translate-x-0.5 motion-reduce:transition-none"
28+
/>
29+
</Link>
30+
);
31+
}

0 commit comments

Comments
 (0)