mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-24 08:02:14 +03:00
Beginner UX: lead batch page with stable outcome header (#11227)
Validated on the combined batch board (batch-page static test green) + this branch: i18n suites 16/16 with Vietnamese translations of the batch header/step strings added here. Stable outcome header with the 3-step flow; the collapsible concept card stays as advanced detail. Thank you @ignamiranda!
This commit is contained in:
@@ -246,7 +246,42 @@ export default function BatchPage() {
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-6">
|
||||
{/* Concept card (F3) */}
|
||||
{/* Stable outcome-oriented header (replaces the collapsible card as primary orientation) */}
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="flex flex-col gap-1">
|
||||
<h1 className="text-2xl font-semibold text-[var(--color-text-main)]">{t("batchConceptTitle")}</h1>
|
||||
<p className="text-sm text-[var(--color-text-muted)]">{t("batchHeaderSubtitle")}</p>
|
||||
</div>
|
||||
|
||||
{/* Three-step strip */}
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
<div className="flex items-center gap-1.5 rounded-lg bg-[var(--color-surface)] border border-[var(--color-border)] px-3 py-2">
|
||||
<span className="font-medium text-sm text-[var(--color-text-main)]">{t("batchStep1")}</span>
|
||||
<span className="text-xs text-[var(--color-text-muted)] hidden sm:inline">{t("batchStep1Desc")}</span>
|
||||
</div>
|
||||
<span className="material-symbols-outlined text-[var(--color-text-muted)]">chevron_right</span>
|
||||
<div className="flex items-center gap-1.5 rounded-lg bg-[var(--color-surface)] border border-[var(--color-border)] px-3 py-2">
|
||||
<span className="font-medium text-sm text-[var(--color-text-main)]">{t("batchStep2")}</span>
|
||||
<span className="text-xs text-[var(--color-text-muted)] hidden sm:inline">{t("batchStep2Desc")}</span>
|
||||
</div>
|
||||
<span className="material-symbols-outlined text-[var(--color-text-muted)]">chevron_right</span>
|
||||
<div className="flex items-center gap-1.5 rounded-lg bg-[var(--color-surface)] border border-[var(--color-border)] px-3 py-2">
|
||||
<span className="font-medium text-sm text-[var(--color-text-main)]">{t("batchStep3")}</span>
|
||||
<span className="text-xs text-[var(--color-text-muted)] hidden sm:inline">{t("batchStep3Desc")}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Primary CTA */}
|
||||
<button
|
||||
onClick={() => setShowWizard(true)}
|
||||
className="flex items-center gap-2 px-4 py-2 text-sm font-medium rounded-lg bg-[var(--color-accent)] text-white hover:opacity-90 transition-all duration-200 w-fit"
|
||||
>
|
||||
<span className="material-symbols-outlined text-[16px]">add</span>
|
||||
{t("batchListNewButton")}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Deeper explanation (optional, collapsible) */}
|
||||
<BatchConceptCard />
|
||||
|
||||
{/* "Batch created" success banner (A-6) — auto-dismiss 5s */}
|
||||
|
||||
@@ -805,6 +805,13 @@
|
||||
"batchConceptBenefit50pct": "50% discount on input + output tokens",
|
||||
"batchConceptAsync24h": "Async with a 24h completion window",
|
||||
"batchConceptUseCases": "Best for bulk classification, evaluations, and embeddings",
|
||||
"batchHeaderSubtitle": "Run many requests as one job",
|
||||
"batchStep1": "1 · Upload JSONL",
|
||||
"batchStep1Desc": "Add requests",
|
||||
"batchStep2": "2 · Create batch",
|
||||
"batchStep2Desc": "Run job",
|
||||
"batchStep3": "3 · Get results",
|
||||
"batchStep3Desc": "Download output",
|
||||
"filesConceptTitle": "Batch files",
|
||||
"filesConceptSubtitle": "JSONL files used by batches: input requests, results, and errors.",
|
||||
"filesConceptInput": "Input — your JSONL with one request per line",
|
||||
|
||||
@@ -974,7 +974,14 @@
|
||||
"batchFileUsedByCount": "{count, plural, one {# batch} other {# batch}}",
|
||||
"batchFilePreview": "Bản xem trước",
|
||||
"batchFilePreviewTruncated": "Hiển thị {shown} dòng đầu tiên (tổng cộng {total} dòng)",
|
||||
"batchFileDownloadFull": "Tải xuống toàn bộ tệp"
|
||||
"batchFileDownloadFull": "Tải xuống toàn bộ tệp",
|
||||
"batchHeaderSubtitle": "Chạy nhiều yêu cầu như một job duy nhất",
|
||||
"batchStep1": "1 · Tải lên JSONL",
|
||||
"batchStep1Desc": "Thêm yêu cầu",
|
||||
"batchStep2": "2 · Tạo batch",
|
||||
"batchStep2Desc": "Chạy job",
|
||||
"batchStep3": "3 · Nhận kết quả",
|
||||
"batchStep3Desc": "Tải xuống đầu ra"
|
||||
},
|
||||
"disabled": "Đã tắt",
|
||||
"featureFlagOmnirouteEmergencyFallbackDescription": "Định tuyến các yêu cầu đã hết ngân sách đến nhà cung cấp/mô hình dự phòng khẩn cấp miễn phí.",
|
||||
|
||||
74
tests/unit/batch-page-static.test.ts
Normal file
74
tests/unit/batch-page-static.test.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
||||
const pagePath = path.join(
|
||||
repoRoot,
|
||||
"src/app/(dashboard)/dashboard/batch/page.tsx"
|
||||
);
|
||||
const enPath = path.join(repoRoot, "src/i18n/messages/en.json");
|
||||
|
||||
function readBatchPage() {
|
||||
return fs.readFileSync(pagePath, "utf8");
|
||||
}
|
||||
|
||||
function readEnKeys() {
|
||||
return Object.keys(JSON.parse(fs.readFileSync(enPath, "utf8")));
|
||||
}
|
||||
|
||||
test("batch page stable header uses t() for subtitle", () => {
|
||||
const source = readBatchPage();
|
||||
assert.match(source, /batchHeaderSubtitle/);
|
||||
});
|
||||
|
||||
test("batch page stable header uses t() for three-step strip", () => {
|
||||
const source = readBatchPage();
|
||||
assert.match(source, /batchStep1/);
|
||||
assert.match(source, /batchStep2/);
|
||||
assert.match(source, /batchStep3/);
|
||||
assert.match(source, /batchStep1Desc/);
|
||||
assert.match(source, /batchStep2Desc/);
|
||||
assert.match(source, /batchStep3Desc/);
|
||||
});
|
||||
|
||||
test("batch page stable header keeps Create batch CTA using t()", () => {
|
||||
const source = readBatchPage();
|
||||
assert.match(source, /batchListNewButton/);
|
||||
});
|
||||
|
||||
test("batch page still renders collapsible BatchConceptCard as optional deeper explanation", () => {
|
||||
const source = readBatchPage();
|
||||
assert.match(source, /BatchConceptCard/);
|
||||
});
|
||||
|
||||
test("batch page stable header does not contain hardcoded English", () => {
|
||||
const source = readBatchPage();
|
||||
assert.doesNotMatch(source, /Run many requests as one job/);
|
||||
assert.doesNotMatch(source, /1 \· Upload JSONL/);
|
||||
assert.doesNotMatch(source, /2 \· Create batch/);
|
||||
assert.doesNotMatch(source, /3 \· Get results/);
|
||||
});
|
||||
|
||||
test("new i18n keys exist in en.json common namespace", () => {
|
||||
const enKeys = JSON.parse(
|
||||
fs.readFileSync(
|
||||
path.join(repoRoot, "src/i18n/messages/en.json"),
|
||||
"utf8"
|
||||
)
|
||||
).common || {};
|
||||
const requiredKeys = [
|
||||
"batchHeaderSubtitle",
|
||||
"batchStep1",
|
||||
"batchStep2",
|
||||
"batchStep3",
|
||||
"batchStep1Desc",
|
||||
"batchStep2Desc",
|
||||
"batchStep3Desc",
|
||||
];
|
||||
for (const key of requiredKeys) {
|
||||
assert.ok(key in enKeys, `Missing i18n key in en.json common namespace: ${key}`);
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user