mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-30 16:20:35 +02:00
The page added in #2462 lived at `/{slug}/dashboard` and was titled "Dashboard", which collides with the conventional meaning ("personal landing surface") and doesn't tell new users what the page is for. Its actual contents — token spend, cost, run time, task counts — map cleanly onto the OpenAI / Anthropic / Vercel "Usage" surface, so rename to that. Renames (user-visible) - Route: `/{slug}/dashboard` → `/{slug}/usage` (web App Router + desktop memory router) - Sidebar entry: label "Dashboard" / "看板" → "Usage" / "用量", icon LayoutDashboard → BarChart3 (page header icon swapped in sync) - Page title in en/zh-Hans - Reserved-slugs: add `usage` to workspace route segments group; `dashboard` stays reserved in the marketing group (back-compat against workspace slug collisions + keeps the name free for a future Home page) - i18n namespace `dashboard` → `usage` across resources-types.ts, locales/index.ts, and the moved JSON files - WORKSPACE_ROUTE_SEGMENTS in editor link-handler - paths.workspace(slug).dashboard() → .usage(), with matching test expectation updates Per-agent leaderboard polish (`packages/views/dashboard/components/ dashboard-page.tsx`) - Card title "Cost & run time by agent" → "Leaderboard" with a 4-way Segmented control: Tokens / Cost / Time / Tasks - Active metric drives row order, progress-bar width, and the emphasised column header / cell — keeping ranking, visual quantity, and column emphasis in lockstep so users always see what's being measured - Default sort = Tokens (most universally meaningful; Cost still one click away) - Project filter dropdown: - Show ProjectIcon next to the selected project + each list item; FolderKanban as the "All projects" fallback (matches ProjectPicker language) - alignItemWithTrigger={false} so "All projects" doesn't get pushed above the trigger and clipped when the header sits at the top of the viewport (was the root cause of "can't re-select All projects" once a project was selected) - max-h-72 to cap the dropdown when workspaces accrue many projects; matches the runtime-detail Select precedent - Folder name `packages/views/dashboard/*` and `DashboardPage` component name intentionally left in place — user-visible rename only, no broad code refactor. Old `/dashboard` routes are not redirected because the page only landed in #2462 (a few days ago); no real users, external links, or desktop-tab persistence have settled on it yet.
51 lines
1.9 KiB
TypeScript
51 lines
1.9 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { paths, isGlobalPath } from "./paths";
|
|
|
|
describe("paths.workspace(slug)", () => {
|
|
const ws = paths.workspace("acme");
|
|
|
|
it("builds workspace paths with slug prefix", () => {
|
|
expect(ws.usage()).toBe("/acme/usage");
|
|
expect(ws.issues()).toBe("/acme/issues");
|
|
expect(ws.issueDetail("abc-123")).toBe("/acme/issues/abc-123");
|
|
expect(ws.projects()).toBe("/acme/projects");
|
|
expect(ws.projectDetail("p1")).toBe("/acme/projects/p1");
|
|
expect(ws.autopilots()).toBe("/acme/autopilots");
|
|
expect(ws.autopilotDetail("a1")).toBe("/acme/autopilots/a1");
|
|
expect(ws.agents()).toBe("/acme/agents");
|
|
expect(ws.inbox()).toBe("/acme/inbox");
|
|
expect(ws.myIssues()).toBe("/acme/my-issues");
|
|
expect(ws.runtimes()).toBe("/acme/runtimes");
|
|
expect(ws.skills()).toBe("/acme/skills");
|
|
expect(ws.skillDetail("skl_123")).toBe("/acme/skills/skl_123");
|
|
expect(ws.settings()).toBe("/acme/settings");
|
|
});
|
|
|
|
it("URL-encodes special characters in ids", () => {
|
|
expect(ws.issueDetail("id with space")).toBe("/acme/issues/id%20with%20space");
|
|
});
|
|
});
|
|
|
|
describe("paths (global)", () => {
|
|
it("builds global paths without slug", () => {
|
|
expect(paths.login()).toBe("/login");
|
|
expect(paths.newWorkspace()).toBe("/workspaces/new");
|
|
expect(paths.invite("inv-1")).toBe("/invite/inv-1");
|
|
expect(paths.authCallback()).toBe("/auth/callback");
|
|
});
|
|
});
|
|
|
|
describe("isGlobalPath", () => {
|
|
it("returns true for pre-workspace routes", () => {
|
|
expect(isGlobalPath("/login")).toBe(true);
|
|
expect(isGlobalPath("/workspaces/new")).toBe(true);
|
|
expect(isGlobalPath("/invite/abc")).toBe(true);
|
|
expect(isGlobalPath("/auth/callback")).toBe(true);
|
|
});
|
|
|
|
it("returns false for workspace-scoped paths", () => {
|
|
expect(isGlobalPath("/acme/issues")).toBe(false);
|
|
expect(isGlobalPath("/")).toBe(false);
|
|
});
|
|
});
|