Skip to content

Commit 9920398

Browse files
committed
refactor: update routing and terminology for metrics and apps
- Replaced the MetricsPage with a new structure under the metrics route, introducing MetricsAppsPage, MetricsAppDeploymentsPage, and MetricsDeploymentDetailPage. - Updated the Breadcrumbs and Sidebar components to reflect the change from "Apps" to "Releases" for better clarity. - Adjusted the AppsPage and TeamOverviewPage to use "Releases" instead of "Apps" in titles and labels. - Added new entries to .gitignore for local development files related to the full-stack environment.
1 parent 91e2cd2 commit 9920398

14 files changed

Lines changed: 1020 additions & 302 deletions

web-dashboard/src/components/shell/Breadcrumbs.tsx

Lines changed: 38 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ export function Breadcrumbs() {
7575

7676
/** Static labels for team-scoped leaf segments (multi-team trails only). */
7777
const TEAM_LEAF_LABELS = new Map<string, string>([
78-
["apps", "Apps"],
78+
["apps", "Releases"],
7979
["members", "Members"],
8080
["metrics", "Metrics"],
8181
]);
@@ -89,6 +89,42 @@ function buildCrumbs(
8989
const segments = pathname.split("/").filter((segment) => segment.length > 0);
9090

9191
if (teamId !== undefined) {
92+
const metricsIndex = segments.indexOf("metrics");
93+
if (metricsIndex !== -1) {
94+
const routeAppId = segments[metricsIndex + 2];
95+
const routeDepId = segments[metricsIndex + 4];
96+
if (segments[metricsIndex + 1] !== "apps" || routeAppId === undefined) {
97+
return [];
98+
}
99+
100+
const crumbs: Crumb[] = [
101+
{
102+
key: "metrics",
103+
to: `/teams/${teamId}/metrics`,
104+
node: "Metrics",
105+
},
106+
{
107+
key: "app",
108+
to: `/teams/${teamId}/metrics/apps/${routeAppId}`,
109+
node: <AppName appId={routeAppId} />,
110+
},
111+
];
112+
113+
if (
114+
segments[metricsIndex + 3] === "deployments" &&
115+
routeDepId !== undefined
116+
) {
117+
crumbs.push({
118+
key: "deployment",
119+
node: (
120+
<DeploymentName appId={routeAppId} deploymentId={routeDepId} />
121+
),
122+
});
123+
}
124+
125+
return crumbs;
126+
}
127+
92128
// Single-team OSS: the team root crumb is just the fixed `default-team`
93129
// slug, so omit it; show it only in multi-team mode where it disambiguates.
94130
const crumbs: Crumb[] = isMultiTeam
@@ -103,7 +139,7 @@ function buildCrumbs(
103139

104140
if (appId !== undefined) {
105141
crumbs.push(
106-
{ key: "apps", to: `/teams/${teamId}/apps`, node: "Apps" },
142+
{ key: "apps", to: `/teams/${teamId}/apps`, node: "Releases" },
107143
{
108144
key: "app",
109145
to: `/teams/${teamId}/apps/${appId}`,

web-dashboard/src/components/shell/Sidebar.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// Team-scoped sidebar: brand, main nav (Apps, Metrics), and a foot section
1+
// Team-scoped sidebar: brand, main nav (Releases, Metrics), and a foot section
22
// (Members for `iam.manage`, GitHub repo link) above Collapse.
33
// against the route map — the DOM/class structure is ported, its hardcoded
44
// `.html` hrefs and global `DB` are not. Members is HIDDEN, not disabled,
@@ -127,7 +127,7 @@ interface TeamNavItem {
127127
const MAIN_NAV_ITEMS: readonly TeamNavItem[] = [
128128
{
129129
key: "apps",
130-
label: "Apps",
130+
label: "Releases",
131131
segment: "apps",
132132
icon: (
133133
<NavIcon>

web-dashboard/src/lib/appTile.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
const APP_ICON_GRADIENTS = [
2+
"linear-gradient(135deg,#0051ff,#008bf7)",
3+
"linear-gradient(135deg,#00ceff,#008bf7)",
4+
"linear-gradient(135deg,#fe19ff,#b517d6)",
5+
"linear-gradient(135deg,#ff9100,#ff4d13)",
6+
"linear-gradient(135deg,#0031ea,#0051ff)",
7+
"linear-gradient(135deg,#008bf7,#00ceff)",
8+
"linear-gradient(135deg,#10b981,#059669)",
9+
"linear-gradient(135deg,#ff4d13,#ec0c43)",
10+
] as const;
11+
12+
/** Stable gradient pick so an app keeps its tile color across refetches. */
13+
export function gradientFor(appId: string): string {
14+
let hash = 0;
15+
for (let index = 0; index < appId.length; index += 1) {
16+
hash = (hash * 31 + appId.charCodeAt(index)) >>> 0;
17+
}
18+
return APP_ICON_GRADIENTS[hash % APP_ICON_GRADIENTS.length];
19+
}
20+
21+
/** Tile initials: "harbor-android" → "ha", single words → first two. */
22+
export function initialsFor(appName: string): string {
23+
const segments = appName
24+
.split(/[^\p{L}\p{N}]+/u)
25+
.filter((segment) => segment.length > 0);
26+
const first = segments[0] ?? appName;
27+
const second = segments[1];
28+
const initials =
29+
second !== undefined
30+
? `${first.charAt(0)}${second.charAt(0)}`
31+
: first.slice(0, 2);
32+
return initials === "" ? "?" : initials.toLowerCase();
33+
}

web-dashboard/src/pages/AppsPage.tsx

Lines changed: 2 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { useTeamRole } from "../rbac/useTeamRole";
2828
import { buildReleaseReactSnippet } from "../lib/cliSnippet";
2929
import { formatDate } from "../model/format";
3030
import type { App } from "../model/app";
31+
import { gradientFor, initialsFor } from "../lib/appTile";
3132
import type { Deployment } from "../model/deployment";
3233
import type { FormEvent } from "react";
3334
import { buttonVariants } from "../components/ui/Button";
@@ -76,7 +77,7 @@ export function AppsPage() {
7677
<div className="mb-6 flex flex-wrap items-start gap-[18px]">
7778
<div className="min-w-0 flex-1">
7879
<h1 className={PAGE_TITLE}>
79-
Apps
80+
Releases
8081
</h1>
8182
<p className={PAGE_SUB}>
8283
React Native OTA targets in this team. Each app auto-creates a
@@ -551,41 +552,6 @@ function chipToneFor(deploymentName: string): ChipTone {
551552
return "blue";
552553
}
553554

554-
// Identicon gradients — decorative only; the initials are aria-hidden.
555-
const APP_ICON_GRADIENTS = [
556-
"linear-gradient(135deg,#0051ff,#008bf7)",
557-
"linear-gradient(135deg,#00ceff,#008bf7)",
558-
"linear-gradient(135deg,#fe19ff,#b517d6)",
559-
"linear-gradient(135deg,#ff9100,#ff4d13)",
560-
"linear-gradient(135deg,#0031ea,#0051ff)",
561-
"linear-gradient(135deg,#008bf7,#00ceff)",
562-
"linear-gradient(135deg,#10b981,#059669)",
563-
"linear-gradient(135deg,#ff4d13,#ec0c43)",
564-
] as const;
565-
566-
/** Stable gradient pick so an app keeps its tile color across refetches. */
567-
function gradientFor(appId: string): string {
568-
let hash = 0;
569-
for (let index = 0; index < appId.length; index += 1) {
570-
hash = (hash * 31 + appId.charCodeAt(index)) >>> 0;
571-
}
572-
return APP_ICON_GRADIENTS[hash % APP_ICON_GRADIENTS.length];
573-
}
574-
575-
/** Tile initials: "harbor-android" → "ha", single words → first two. */
576-
function initialsFor(appName: string): string {
577-
const segments = appName
578-
.split(/[^\p{L}\p{N}]+/u)
579-
.filter((segment) => segment.length > 0);
580-
const first = segments[0] ?? appName;
581-
const second = segments[1];
582-
const initials =
583-
second !== undefined
584-
? `${first.charAt(0)}${second.charAt(0)}`
585-
: first.slice(0, 2);
586-
return initials === "" ? "?" : initials.toLowerCase();
587-
}
588-
589555
// --- Icons (paths mirror the shared icon set) --------------------------------
590556

591557
function PlusIcon() {

web-dashboard/src/pages/TeamOverviewPage.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -117,7 +117,7 @@ function TeamOverview({ teamId }: { teamId: string }) {
117117
icon={<AppsIcon />}
118118
iconBackground="var(--color-blue-tint)"
119119
iconColor="var(--color-blue)"
120-
title="Apps"
120+
title="Releases"
121121
subtitle="Manage apps & deployments"
122122
/>
123123
{!membersHidden ? (
@@ -157,13 +157,13 @@ function AppsTile({ teamId }: { teamId: string }) {
157157
<CountTile
158158
accent="var(--color-blue)"
159159
icon={<AppsIcon />}
160-
label="Apps"
160+
label="Releases"
161161
count={appsQuery.data?.length}
162162
isPending={appsQuery.isPending}
163163
isError={appsQuery.isError}
164164
onRetry={() => void appsQuery.refetch()}
165165
to={`/teams/${teamId}/apps`}
166-
linkText="View apps"
166+
linkText="View releases"
167167
/>
168168
);
169169
}

0 commit comments

Comments
 (0)