Skip to content

Commit 37d8de1

Browse files
committed
fix: use Navigate instead of WebUINavigate for /service/:endpointId/edit redirect
1 parent 1787f26 commit 37d8de1

25 files changed

Lines changed: 3340 additions & 13 deletions

File tree

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# FR-1368 Implementation Findings
2+
3+
## Wave 1 (2026-04-22)
4+
5+
### i18n namespace decision (FR-2666)
6+
Per the Jira issue wording (not the spec's modelService.* suggestion), the foundation adds keys under **`deployment.*`** and **`replicaStatus.*`** (flat namespaces).
7+
- 143 keys added to `resources/i18n/en.json`.
8+
- Other 21 languages will be filled via `/fw:i18n` later.
9+
- Downstream: all status tags/form fields/tabs must reference `deployment.*` / `replicaStatus.*`, NOT `modelService.*`.
10+
11+
### Routes decision (FR-2664)
12+
- `/admin-serving/:serviceId` redirects to `/deployments/:serviceId` (shared detail page for admin + user), NOT a separate `/admin-deployments/:serviceId`. Admin list is at `/admin-deployments` only.
13+
- Legacy launcher paths `/service/start` and `/service/update/:endpointId` preserved until FR-2675 launcher lands.
14+
- Stub pages use `'use memo'` directive and render TODO placeholders.
15+
16+
### Menu hook follow-on (FR-2665)
17+
- `StartPage.tsx` had a typed `requiredMenuKey: MenuKeys = 'serving'` that had to be updated to `'deployments'` alongside the hook change (MenuKeys type narrows).
18+
19+
### Graphite quirks (common across Wave 1)
20+
- Worktree `pnpm install` generates `pnpm-lock.worktree-<id>.yaml`. `gt create --all` picks it up → filter it before creating.
21+
- Agent worktree branches are not tracked in Graphite metadata. Run `gt track --parent main` before first `gt submit`.
22+
23+
## PR Index
24+
| Sub-task | PR | Branch |
25+
|----------|----|--------|
26+
| FR-2663 | [#6904](https://github.com/lablup/backend.ai-webui/pull/6904) | 04-22-feat_fr-2663_... |
27+
| FR-2664 | [#6907](https://github.com/lablup/backend.ai-webui/pull/6907) | 04-22-feat_fr-2664_... |
28+
| FR-2665 | [#6905](https://github.com/lablup/backend.ai-webui/pull/6905) | 04-22-feat_fr-2665_... |
29+
| FR-2666 | [#6906](https://github.com/lablup/backend.ai-webui/pull/6906) | 04-22-feat_fr-2666_... |
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Endpoint → Deployment UI Migration Progress
2+
3+
## Last Session: 2026-04-22 (plan creation)
4+
5+
### 1. Current Phase
6+
7+
Planning complete. 6 Stories + 22 Sub-tasks created in Jira under Epic FR-1368. Ready for Wave 1 (Foundation) implementation.
8+
9+
### 2. Next Action
10+
11+
Run `/batch-implement .specs/FR-1368-endpoint-deployment-migration/dev-plan.md` to start Wave 1 (FR-2663, FR-2664, FR-2665, FR-2666 — Foundation layer).
12+
13+
All four Wave 1 sub-tasks have no blockers and can run in parallel, though they may be bundled into a single "Foundation" PR since they are small / mechanical-tier changes.
14+
15+
### 3. Current Goal
16+
17+
Land Story 1 (Foundation) so downstream Stories 2 – 6 are unblocked.
18+
19+
### 4. Lessons Learned
20+
21+
- Jira issuetype is `Subtask` (single word), not `Sub-task`. Using hyphenated form returns HTTP 400.
22+
- `fw-jira` binary symlink in `~/.local/bin/fw-jira` is broken; use `bash /Users/sujinkim/.claude/plugins/fw/bin/jira.sh` directly.
23+
- Dependency modeling prioritized *code-level* blockers: shared components (FR-2667, FR-2668) block list table (FR-2670); list table blocks list pages; list pages block cleanup; etc. Cross-story relations that aren't strict blockers are modeled with `relates` (e.g. FR-2675 ↔ FR-2676).
24+
25+
### 5. Completed Work
26+
27+
- Spec finalized: `.specs/FR-1368-endpoint-deployment-migration/spec.md` (PR-review feedback applied)
28+
- Dev plan created: `.specs/FR-1368-endpoint-deployment-migration/dev-plan.md`
29+
- 6 Stories created under Epic FR-1368: FR-2657, FR-2658, FR-2659, FR-2660, FR-2661, FR-2662
30+
- 22 Sub-tasks created with parent Story links: FR-2663 – FR-2684
31+
- Dependency links (`blocks` + one `relates`) set between sub-tasks to enable `/batch-implement` wave scheduling
32+
- `metadata.json` updated with Story / Sub-task mapping
33+
- `.context/` initialized (this file + tasks.md + findings.md)
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# Endpoint → Deployment UI Migration Tasks
2+
3+
> Last synced: 2026-04-22 (plan creation)
4+
> Source: Jira (project FR)
5+
6+
## Progress: 0/22 complete
7+
8+
### Wave 1 — Foundation (Story 1)
9+
10+
- [ ] FR-2663: Add model-deployment-extended-filter feature flag (26.4.3+) — created
11+
- [ ] FR-2664: Add routes for /deployments + /admin-deployments with legacy fallbacks — created
12+
- [ ] FR-2665: Update sidebar menu: Serving → Deployments — created
13+
- [ ] FR-2666: Add Deployment i18n keys (en.json) + propagate to 21 languages — created
14+
15+
### Wave 2 — Shared components + launcher body + quick-deploy hook
16+
17+
- [ ] FR-2667: Add ReplicaStatusTag component + Storybook story — blocked by FR-2666
18+
- [ ] FR-2668: Add DeploymentStatusTag component — blocked by FR-2666
19+
- [ ] FR-2669: Add DeploymentOwnerInfo component — blocked by FR-2666
20+
- [ ] FR-2674: Add DeploymentLauncherPageContent — multi-step form body — after Wave 1
21+
- [ ] FR-2683: Add useDeploymentLauncher hook (GQL-based Quick Deploy) — blocked by FR-2663
22+
23+
### Wave 3 — List table, launcher page, detail sub-components
24+
25+
- [ ] FR-2670: Add DeploymentList fragment-receiving table — blocked by FR-2667, FR-2668
26+
- [ ] FR-2675: Add DeploymentLauncherPage (create/edit entry) + GQL mutations — blocked by FR-2674, FR-2664
27+
- [ ] FR-2676: Add DeploymentConfigurationSection (Overview card + Edit button) — relates FR-2675
28+
- [ ] FR-2677: Add DeploymentReplicasTab + Replica detail Drawer — blocked by FR-2667
29+
- [ ] FR-2678: Add DeploymentRevisionHistoryTab + Rollback flow
30+
- [ ] FR-2679: Add DeploymentAccessTokensTab (list + create + delete)
31+
- [ ] FR-2680: Add DeploymentAutoScalingTab (wrap existing AutoScalingRuleList)
32+
33+
### Wave 4 — User-visible pages
34+
35+
- [ ] FR-2671: Add DeploymentListPage (user) — owns myDeployments query — blocked by FR-2670, FR-2664
36+
- [ ] FR-2672: Add AdminDeploymentListPage + admin-only filters/columns (26.4.3+) — blocked by FR-2670, FR-2669, FR-2664
37+
- [ ] FR-2681: Add DeploymentDetailPage (header + Overview + 4 tabs + URL tab sync) — blocked by FR-2676, FR-2677, FR-2678, FR-2679, FR-2680, FR-2668, FR-2664
38+
39+
### Wave 5 — Cleanup + consumer migration
40+
41+
- [ ] FR-2673: Remove legacy ServingPage and EndpointList / EndpointStatusTag — blocked by FR-2671, FR-2672
42+
- [ ] FR-2682: Remove legacy EndpointDetailPage + related Endpoint components — blocked by FR-2681
43+
- [ ] FR-2684: Migrate Deploy button sites to [Deploy | ▼] split button — blocked by FR-2683, FR-2675, FR-2664

react/src/hooks/useWebUIMenuItems.tsx

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ export const VALID_MENU_KEYS = [
8585
'summary', // alias to dashboard for backward compatibility
8686
'session',
8787
'job', // alias to session for backward compatibility
88-
'serving',
88+
'deployments',
8989
'model-store',
9090
'ai-agent',
9191
'chat',
@@ -101,7 +101,7 @@ export const VALID_MENU_KEYS = [
101101
'scheduler',
102102
'resource-policy',
103103
'reservoir',
104-
'admin-serving',
104+
'admin-deployments',
105105
'admin-dashboard',
106106
'admin-data',
107107
'project-admin-users',
@@ -126,7 +126,7 @@ const ALL_ADMIN_PAGE_KEYS: ReadonlySet<string> = new Set([
126126
'scheduler',
127127
'resource-policy',
128128
'reservoir',
129-
'admin-serving',
129+
'admin-deployments',
130130
'admin-dashboard',
131131
'admin-data',
132132
'project-admin-users',
@@ -141,13 +141,13 @@ const ALL_ADMIN_PAGE_KEYS: ReadonlySet<string> = new Set([
141141
]);
142142

143143
// Admin-category page keys reachable by a project admin (3-tier admin gating).
144-
// Project admins see Sessions, Serving, Data (vfolders) and Members within the
145-
// admin category. Other admin pages remain visible only to domain admins or
144+
// Project admins see Sessions, Deployments, Data (vfolders) and Members within
145+
// the admin category. Other admin pages remain visible only to domain admins or
146146
// superadmins. Kept as a plain array so it can be exported and reused (e.g. for
147147
// per-page route gating in follow-up PRs).
148148
export const PROJECT_ADMIN_PAGE_KEYS = [
149149
// 'admin-session',
150-
// 'admin-serving',
150+
// 'admin-deployments',
151151
// 'admin-data',
152152
'project-admin-users',
153153
] as const;
@@ -158,7 +158,7 @@ const PROJECT_ADMIN_PAGE_KEY_SET: ReadonlySet<string> = new Set(
158158

159159
// Page keys that additionally require superadmin role
160160
const SUPERADMIN_ONLY_PAGE_KEYS: ReadonlySet<string> = new Set([
161-
'admin-serving',
161+
'admin-deployments',
162162
'admin-dashboard',
163163
'admin-data',
164164
'agent',
@@ -268,10 +268,10 @@ export const useWebUIMenuItems = (props?: UseWebUIMenuItemsProps) => {
268268
'workload',
269269
),
270270
createMenuItem(
271-
'/serving',
272-
t('webui.menu.Serving'),
271+
'/deployments',
272+
t('webui.menu.Deployments'),
273273
<BAIEndpointsIcon style={{ color: token.colorPrimary }} />,
274-
'serving',
274+
'deployments',
275275
'service',
276276
),
277277
createMenuItem(
@@ -379,10 +379,12 @@ export const useWebUIMenuItems = (props?: UseWebUIMenuItemsProps) => {
379379
},
380380
isSuperAdmin && {
381381
label: (
382-
<WebUILink to="/admin-serving">{t('webui.menu.Serving')}</WebUILink>
382+
<WebUILink to="/admin-deployments">
383+
{t('webui.menu.Deployments')}
384+
</WebUILink>
383385
),
384386
icon: <BAIEndpointsIcon style={{ color: token.colorInfo }} />,
385-
key: 'admin-serving' as MenuKeys,
387+
key: 'admin-deployments' as MenuKeys,
386388
group: 'admin-operations' as AdminMenuGroupName,
387389
},
388390
{

react/src/pages/StartPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ const StartPage: React.FC = () => {
208208
enableModelFolders && {
209209
id: 'modelService',
210210
rowSpan: 3,
211-
requiredMenuKey: 'serving',
211+
requiredMenuKey: 'deployments',
212212
columnSpan: 1,
213213
columnOffset: { 6: 0, 4: 0 },
214214
data: {

resources/i18n/de.json

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,144 @@
753753
"Used": "gebraucht"
754754
}
755755
},
756+
"deployment": {
757+
"AccessToken": "Zugriffstoken",
758+
"AccessTokens": "Zugriffstoken",
759+
"ActivePool": "Active Pool",
760+
"ActivePoolDescription": "Replikate, die derzeit über AppProxy Datenverkehr empfangen.",
761+
"ActivenessStatus": "Aktivitätsstatus",
762+
"AdminDeployments": "Admin-Bereitstellungen",
763+
"AutoScaling": "Automatische Skalierung",
764+
"AutoScalingRules": "Regeln für automatische Skalierung",
765+
"ClusterMode": "Cluster-Modus",
766+
"ClusterSize": "Cluster-Größe",
767+
"Command": "Befehl",
768+
"Configuration": "Konfiguration",
769+
"ConfigurationSummary": "Konfigurationsübersicht",
770+
"ConfirmDeleteDeployment": "Möchten Sie die Bereitstellung \"{{name}}\" wirklich löschen?",
771+
"CopyEndpointUrl": "Endpunkt-URL kopieren",
772+
"CreateDeployment": "Bereitstellung erstellen",
773+
"CreatedAt": "Erstellt am",
774+
"CreatedBy": "Erstellt von",
775+
"DeleteDeployment": "Bereitstellung löschen",
776+
"Deployment": "Bereitstellung",
777+
"DeploymentCreated": "Bereitstellung wurde erstellt.",
778+
"DeploymentDeleted": "Bereitstellung wurde gelöscht.",
779+
"DeploymentDetail": "Bereitstellungsdetail",
780+
"DeploymentId": "Bereitstellungs-ID",
781+
"DeploymentName": "Bereitstellungsname",
782+
"DeploymentUpdated": "Bereitstellung wurde aktualisiert.",
783+
"Deployments": "Bereitstellungen",
784+
"DesiredReplicas": "Gewünschte Replikate",
785+
"Domain": "Domäne",
786+
"EditConfiguration": "Konfiguration bearbeiten",
787+
"EditDeployment": "Bereitstellung bearbeiten",
788+
"EditModeBanner": "Beim Speichern wird eine neue Revision erstellt. Die vorherige Revision wird aufbewahrt.",
789+
"EndpointUrl": "Endpunkt-URL",
790+
"Environ": "Umgebungsvariablen",
791+
"ExtraMounts": "Zusätzliche Einhängepunkte",
792+
"FailedToCreateDeployment": "Bereitstellung konnte nicht erstellt werden.",
793+
"FailedToDeleteDeployment": "Bereitstellung konnte nicht gelöscht werden.",
794+
"FailedToRollback": "Rollback zur ausgewählten Revision ist fehlgeschlagen.",
795+
"FailedToUpdateDeployment": "Bereitstellung konnte nicht aktualisiert werden.",
796+
"HealthStatus": "Gesundheitsstatus",
797+
"HealthySummary": "{{healthy}} / {{total}} Gesund",
798+
"Image": "Image",
799+
"LegacyRouteRedirected": "Die Endpunkt-URL wurde verschoben. Sie wurden zur neuen Bereitstellungsseite weitergeleitet.",
800+
"LivenessStatus": "Liveness-Status",
801+
"Model": "Modell",
802+
"ModelDefinitionPath": "Pfad zur Modelldefinitionsdatei",
803+
"ModelFolder": "Modellordner",
804+
"ModelMountDestination": "Einhängeziel für Modellordner",
805+
"ModelVersion": "Modellversion",
806+
"Name": "Name",
807+
"NamePlaceholder": "Bereitstellungsname eingeben",
808+
"NameRequired": "Bereitstellungsname ist erforderlich.",
809+
"NewDeployment": "Neue Bereitstellung",
810+
"NewRevisionWillBeCreated": "Eine neue Revision wird erstellt. Die vorherige Revision wird aufbewahrt.",
811+
"NewRevisionWillBeCreatedConfirm": "Beim Speichern wird eine neue Revision erstellt. Fortfahren?",
812+
"NoDeployments": "Keine Bereitstellungen gefunden.",
813+
"NumberOfReplicas": "Anzahl der Replikate",
814+
"OpenToPublic": "Öffentlich",
815+
"Owner": "Eigentümer",
816+
"Project": "Projekt",
817+
"QuickDeploy": "Bereitstellen",
818+
"QuickDeployDetailed": "Konfigurieren und bereitstellen...",
819+
"ReadinessStatus": "Bereitschaftsstatus",
820+
"Replica": "Replikat",
821+
"ReplicaDetail": "Replikat-Detail",
822+
"ReplicaId": "Replikat-ID",
823+
"ReplicaSummary": "Replikat-Übersicht",
824+
"Replicas": "Replikate",
825+
"ResourceGroup": "Ressourcengruppe",
826+
"ResourcePreset": "Ressourcen-Voreinstellung",
827+
"Resources": "Ressourcen",
828+
"Revision": "Revision",
829+
"RevisionHistory": "Revisionsverlauf",
830+
"RevisionId": "Revisions-ID",
831+
"Revisions": "Revisionen",
832+
"Rollback": "Rollback",
833+
"RollbackConfirm": "Möchten Sie wirklich auf Revision #{{revisionNumber}} zurücksetzen? Die aktuelle Revision wird ersetzt.",
834+
"RollbackSuccess": "Rollback zu Revision #{{revisionNumber}} wurde angefordert.",
835+
"RuntimeVariant": "Laufzeitvariante",
836+
"SessionId": "Sitzungs-ID",
837+
"Tags": "Tags",
838+
"TrafficRatio": "Datenverkehrsanteil",
839+
"TrafficStatus": "Datenverkehrsstatus",
840+
"UpdateDeployment": "Bereitstellung aktualisieren",
841+
"UpdatedAt": "Aktualisiert am",
842+
"accessToken": {
843+
"Create": "Zugriffstoken erstellen",
844+
"Created": "Zugriffstoken wurde erstellt.",
845+
"CustomExpiration": "Benutzerdefiniertes Ablaufdatum",
846+
"Delete": "Zugriffstoken löschen",
847+
"Deleted": "Zugriffstoken wurde gelöscht.",
848+
"Expiration": "Ablaufdatum",
849+
"NoExpiration": "Kein Ablaufdatum",
850+
"Token": "Token"
851+
},
852+
"filter": {
853+
"CreatedAt": "Erstellt am",
854+
"DomainName": "Domäne",
855+
"EndpointUrl": "Endpunkt-URL",
856+
"Name": "Name",
857+
"OpenToPublic": "Öffentlich",
858+
"ResourceGroup": "Ressourcengruppe",
859+
"Status": "Status",
860+
"Tags": "Tags"
861+
},
862+
"nav": {
863+
"Next": "Weiter",
864+
"Previous": "Zurück",
865+
"SkipToReview": "Zur Überprüfung springen"
866+
},
867+
"status": {
868+
"Degraded": "Beeinträchtigt",
869+
"Deploying": "Wird bereitgestellt",
870+
"Healthy": "Gesund",
871+
"NotChecked": "Nicht geprüft",
872+
"Pending": "Ausstehend",
873+
"Ready": "Bereit",
874+
"Scaling": "Skalierung",
875+
"Stopped": "Gestoppt",
876+
"Stopping": "Wird gestoppt",
877+
"Terminated": "Beendet",
878+
"Unhealthy": "Nicht gesund"
879+
},
880+
"step": {
881+
"BasicInfo": "Grunddaten",
882+
"ModelAndRuntime": "Modell & Laufzeit",
883+
"ResourcesAndReplicas": "Ressourcen & Replikate",
884+
"ReviewAndCreate": "Überprüfen & Erstellen"
885+
},
886+
"tab": {
887+
"AccessTokens": "Zugriffstoken",
888+
"AutoScaling": "Automatische Skalierung",
889+
"Overview": "Übersicht",
890+
"Replicas": "Replikate",
891+
"RevisionHistory": "Revisionsverlauf"
892+
}
893+
},
756894
"desktopNotification": {
757895
"NotSupported": "Dieser Browser unterstützt keine Benachrichtigungen.",
758896
"PermissionDenied": "Sie haben den Benachrichtigungszugriff abgelehnt. \nUm Warnungen zu verwenden, lassen Sie diese bitte in Ihren Browsereinstellungen zu."
@@ -1910,6 +2048,27 @@
19102048
"Username": "Nutzername",
19112049
"UsernameOptional": "Benutzername (optional)"
19122050
},
2051+
"replicaStatus": {
2052+
"Active": "Aktiv",
2053+
"Degraded": "Beeinträchtigt",
2054+
"FailedToStart": "Start fehlgeschlagen",
2055+
"Healthy": "Gesund",
2056+
"Inactive": "Inaktiv",
2057+
"NotChecked": "Nicht geprüft",
2058+
"Provisioning": "Wird bereitgestellt",
2059+
"Running": "Läuft",
2060+
"Terminated": "Beendet",
2061+
"Terminating": "Wird beendet",
2062+
"Unhealthy": "Nicht gesund",
2063+
"tooltip": {
2064+
"Degraded": "Der Health-Checker kann dieses Replikat nicht erreichen. Es wird vorübergehend aus dem Active Pool ausgeschlossen.",
2065+
"Healthy": "Gesund. Im AppProxy Active Pool enthalten und empfängt Datenverkehr.",
2066+
"NotChecked": "Innerhalb der anfänglichen Verzögerungszeit. Wartet auf die erste Gesundheitsprüfung.",
2067+
"Provisioning": "Das Replikat wird bereitgestellt.",
2068+
"Terminating": "Das Replikat wird beendet.",
2069+
"Unhealthy": "Nach aufeinanderfolgenden Fehlern als nicht gesund eingestuft. Aus dem Active Pool ausgeschlossen."
2070+
}
2071+
},
19132072
"reservoirPage": {
19142073
"Activate": "Aktivieren",
19152074
"Active": "Aktiv",
@@ -2978,6 +3137,7 @@
29783137
"AIAgents": "AI-Agenten",
29793138
"AboutBackendAI": "Über Backend.AI",
29803139
"AdminDashboard": "Admin -Dashboard",
3140+
"AdminDeployments": "Admin-Bereitstellungen",
29813141
"AdminSettings": "Admin-Einstellungen",
29823142
"Administration": "Verwaltung",
29833143
"AgentSummary": "Agent Zusammenfassung",
@@ -2995,6 +3155,7 @@
29953155
"Data": "Daten",
29963156
"Data&Model": "Daten",
29973157
"Data&Storage": "Datenspeicher",
3158+
"Deployments": "Bereitstellungen",
29983159
"Diagnostics": "Diagnose",
29993160
"DisMatchUserEmail": "Die E-Mail-Adresse des Benutzers stimmt nicht überein",
30003161
"Endpoint": "Endpunkt",

0 commit comments

Comments
 (0)