Skip to content

Commit 3dd5098

Browse files
committed
Merge branch 'main' into 26.4
2 parents aaf544a + 731574c commit 3dd5098

99 files changed

Lines changed: 35881 additions & 1769 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
# Deployment Preset Management UI Dev Plan
2+
3+
## Spec Reference
4+
`.specs/FR-2750-deployment-revision-preset-ui/spec.md`
5+
6+
## Epic: FR-2750
7+
8+
## Overview
9+
10+
Three tasks, grouped by functional area. Task 1 and Task 2 are parallel-friendly once the Relay schema is clear (both depend on generated types but can be developed simultaneously). Task 3 depends on Task 2 because it reuses `DeploymentPresetDetailContent`.
11+
12+
- **Task 1 — Admin tab + List page** (1차 마일스톤 core): Relay scaffolding + list table + tab wiring
13+
- **Task 2 — Admin create/edit/delete modal** (1차 마일스톤 core): full form, delete confirmation, extracted detail content
14+
- **Task 3 — User-facing selector detail** (2차 마일스톤): ⓘ button in launcher + read-only modal
15+
16+
## Naming & Pattern Conventions
17+
18+
- **Table components**: Name them `AdminDeploymentPresetTable` (i.e. `OOOTable` not `OOONodes`). May be kept as an inner component inside the page file if it is not reused elsewhere.
19+
- **customizeColumns pattern**: Follow `BAIUserNodes` pattern — base columns defined inside the component, `customizeColumns` prop for consumer overrides, `BAINameActionCell` with customizable action options.
20+
- **No unnecessary modal/content splitting**: Do NOT split a modal into `SettingModal` + `SettingModalContent` unless the content is genuinely reused elsewhere. Keep form logic inside the modal file. The one legitimate split is `DeploymentPresetDetailContent`, which IS reused by both the admin delete-confirm preview and the user-facing read-only modal.
21+
22+
## Sub-tasks (Implementation Order)
23+
24+
---
25+
26+
### Task 1: Admin tab + List page
27+
28+
**Was**: sub-tasks 1 + 2 + 3
29+
30+
**Changed files**:
31+
- `react/src/__generated__/` (auto-generated, tracked)
32+
- `react/src/pages/AdminDeploymentPresetListPage.tsx` (new — contains both the list query and the `AdminDeploymentPresetTable` inner component)
33+
- `react/src/pages/AdminServingPage.tsx` (tab wiring)
34+
- `resources/i18n/en.json` (all 21 locale files under `resources/i18n/` — add keys to `en.json` first, then run `fw:i18n-translator` skill to auto-translate remaining locales) (`adminDeploymentPreset.*` namespace, tab title)
35+
36+
**Dependencies**: None (Relay scaffolding is included here)
37+
38+
**Review complexity**: Medium
39+
40+
**Description**:
41+
42+
1. **Relay scaffolding** — Define in the colocated component file (or a shared fragment file):
43+
- List query: `deploymentRevisionPresets` connection with fields `name`, `description`, `runtimeVariantId`, `rank`, `cluster`, `resource`, `execution`, `deploymentDefaults`, `modelDefinition`, `presetValues`, `createdAt`, `updatedAt`, `runtimeVariant { id, name }`, `resourceSlots`.
44+
- Three mutations: `adminCreateDeploymentRevisionPreset`, `adminUpdateDeploymentRevisionPreset`, `adminDeleteDeploymentRevisionPreset` — matching `CreateDeploymentRevisionPresetInput` / `UpdateDeploymentRevisionPresetInput` from `data/schema.graphql`.
45+
- Fragment `DeploymentRevisionPresetDetailFragment` — covers all fields needed by both the admin list and the user-facing read-only modal (used in Task 3).
46+
- Run `pnpm run relay` to materialize types under `react/src/__generated__/`.
47+
48+
2. **AdminDeploymentPresetListPage** — Mirror `AdminModelCardListPage.tsx` structure:
49+
- `useLazyLoadQuery` + `BAITable` + `BAIGraphQLPropertyFilter` + `BAIFetchKeyButton` + `useBAIPaginationOptionStateOnSearchParam` + `useBAISettingUserState('table_column_overrides.AdminDeploymentPresetListPage')`.
50+
- **Inner component naming**: `AdminDeploymentPresetTable` (not `DeploymentPresetNodes`).
51+
- **customizeColumns**: follow `BAIUserNodes` pattern — base columns defined in `AdminDeploymentPresetTable`, `customizeColumns` prop for consumer overrides.
52+
- Columns: Name (fixed-left, `BAINameActionCell` with Edit/Delete action options), Description, Runtime (`runtimeVariant.name`), Image (from `execution`), Cluster (mode × size), Replicas (default), Strategy, Rank, Created at.
53+
- Defaults hidden: Description, Cluster, Strategy, Rank.
54+
- Filter properties: `name` (string contains), `runtimeVariantId` (UUID).
55+
- Sortable: `name`, `rank`, `createdAt``DeploymentRevisionPresetOrderField`.
56+
- The page component is mounted as a tab body (not a route).
57+
58+
3. **Tab wiring in AdminServingPage**:
59+
- `React.lazy(() => import('./AdminDeploymentPresetListPage'))`.
60+
- Gate on `isSuperAdmin && baiClient.supports('deployment-preset')`. Tab key: `deployment-presets`.
61+
- Fallback to `serving` tab when the user lacks access (plain effect on primitives is fine).
62+
- Wrap in `BAIErrorBoundary` + `Suspense fallback={<Skeleton active />}` matching the existing `model-store` branch pattern.
63+
64+
---
65+
66+
### Task 2: Admin create/edit/delete modal
67+
68+
**Was**: sub-tasks 4 + 5
69+
70+
**Changed files**:
71+
- `react/src/components/AdminDeploymentPresetSettingModal.tsx` (new)
72+
- `react/src/components/DeploymentPresetDetailContent.tsx` (new — pure read-only summary, no Modal chrome)
73+
- `react/src/pages/AdminDeploymentPresetListPage.tsx` (wire create/edit/delete triggers)
74+
- `resources/i18n/en.json` (all 21 locale files under `resources/i18n/` — add keys to `en.json` first, then run `fw:i18n-translator` skill to auto-translate remaining locales) (form labels, section titles, placeholders, required-field errors, delete-confirm copy, summary section labels)
75+
76+
**Dependencies**: Task 1 (list page and Relay types exist)
77+
78+
**Review complexity**: High (largest single piece; covers all form fields, two mutation paths, delete flow)
79+
80+
**Description**:
81+
82+
1. **AdminDeploymentPresetSettingModal**`BAIModal` + Ant `Form`. Keep all form logic inside this file (do NOT split into SettingModal + SettingModalContent).
83+
- Sections per the spec:
84+
1. **Basic info**: `name` (required), `description`, `runtimeVariantId` (required, Select from `runtimeVariants` query — reuse existing dropdown from `ServiceLauncherPageContent` runtime selector if applicable), `imageId` (required — reuse `ImageEnvironmentSelectFormItems` which already queries `image.id` UUID; pass `values.environments.image?.id` as `imageId` on submit).
85+
2. **Cluster**: `clusterMode` (single-node / multi-node), `clusterSize`.
86+
3. **Resources**: `resourceSlots`, `resourceOpts` (reuse session/service launcher patterns if applicable; compact JSON textarea with parse validation otherwise).
87+
4. **Execution**: `startupCommand`, `bootstrapScript`, `environ`, `presetValues`.
88+
5. **Deployment defaults**: `openToPublic`, `replicaCount`, `revisionHistoryLimit`, `deploymentStrategy`.
89+
6. **Advanced**: `modelDefinition`.
90+
7. **Edit-only**: `rank`.
91+
- Create vs. Update mode determined by whether a `presetId` prop is passed. `runtimeVariantId` and `imageId` are not in `UpdateDeploymentRevisionPresetInput` — render as read-only display in edit mode.
92+
- Apply `'use memo'`, plain handlers (no `useCallback`), antd v6 prop names.
93+
- `BAIButton` `action` prop on Save for automatic loading state.
94+
95+
2. **Delete with typed confirmation** (wired in `AdminDeploymentPresetListPage`):
96+
- `BAIConfirmModalWithInput` (per `destructive-confirmation.md`). `confirmText` = preset name; OK disabled until typed.
97+
- On success: `commitDeleteMutation` + `updateFetchKey()`. On rejection: surface server error message verbatim; defer cascade UX to follow-up.
98+
- Show `DeploymentPresetDetailContent` inside the confirm modal as a preview of what will be deleted.
99+
100+
3. **DeploymentPresetDetailContent** (the one legitimate split):
101+
- Pure read-only component. Takes a Relay fragment ref (`DeploymentRevisionPresetDetailFragment`).
102+
- Renders: name, description, rank, runtime, image, cluster, resource block (CPU / memory / GPU / shmem from `resourceSlots`), deployment defaults (replicas, limit, strategy, public).
103+
- No buttons, no edit affordance. Reused in Task 3's read-only modal.
104+
105+
---
106+
107+
### Task 3: User-facing selector detail
108+
109+
**Was**: sub-tasks 6 + 7
110+
111+
**Changed files**:
112+
- `react/src/components/ServiceLauncherPageContent.tsx` (⓪ Select + ⓘ button + modal mount)
113+
- `react/src/components/DeploymentPresetDetailModal.tsx` (new — thin `BAIModal` wrapper)
114+
- `resources/i18n/en.json` (all 21 locale files under `resources/i18n/` — add keys to `en.json` first, then run `fw:i18n-translator` skill to auto-translate remaining locales) (`modelService.DeploymentPreset`, tooltip text, modal title, close button)
115+
116+
**Dependencies**: Task 2 (`DeploymentPresetDetailContent` must exist)
117+
118+
**Review complexity**: Medium (touches a long/complex form file; the addition is read-only and does not wire preset values into the deployment payload — explicitly out of scope)
119+
120+
**Description**:
121+
122+
1. **ⓘ button in ServiceLauncherPageContent**:
123+
- Locate the form area near the existing `runtimeVariant` Form.Item.
124+
- Add a `Form.Item` (or wrapper) with a `Space.Compact` containing: a `Select` of presets (sourced via `availablePresets` connection, scoped by `runtimeVariant` if needed) + a `Button` with info icon (`InfoIcon` from lucide or `<InfoCircleOutlined />`).
125+
- `Button` disabled when no preset selected. On click: open `DeploymentPresetDetailModal`.
126+
- Gate the entire row behind `baiClient.supports('deployment-preset')`.
127+
- Pattern reference: `VFolderSelect.tsx` (Select + `Space.Compact` button row).
128+
- **Out of scope**: applying chosen preset values to form state. Selector is display/inspect only.
129+
130+
2. **DeploymentPresetDetailModal**:
131+
- `BAIModal` with `footer={null}` (or single Close button), title = preset name.
132+
- Body = `DeploymentPresetDetailContent` from Task 2.
133+
- Loads selected preset by id via `useLazyLoadQuery` for `deploymentRevisionPreset(id: $id)` so it works even when Select options are paginated/truncated. Wrap in `Suspense` with skeleton fallback.
134+
- No Edit, no Delete, no destructive actions.
135+
136+
---
137+
138+
## Dependency Graph
139+
140+
```
141+
Task 1 (Relay + List + Tab) ──────────────────────► Task 2 (Modals + DetailContent)
142+
│ │
143+
│ │
144+
└─────────────────── parallel-friendly ───────────────┘
145+
146+
147+
Task 3 (Selector + Read-only Modal)
148+
```
149+
150+
- Task 1 and Task 2 are **parallel-friendly** once the schema/fragment shape is agreed upon.
151+
- Task 3 **cannot start** until `DeploymentPresetDetailContent` (Task 2) is available.
152+
153+
Jira `blocks` links:
154+
- Task 1 blocks Task 2 (generated types needed)
155+
- Task 2 blocks Task 3 (DeploymentPresetDetailContent)
156+
157+
---
158+
159+
## Risks and Open Questions
160+
161+
1. **Runtime variant source query**: Sub-task 2 needs a Select source for `runtimeVariantId`. Plan: reuse the existing runtime selector from `ServiceLauncherPageContent` (lines ~1639–1677) if it wraps `runtimeVariants` with the same shape; otherwise use `runtimeVariants(filter: ..., limit: 100)` from `data/schema.graphql:13827`.
162+
2. **Delete-while-in-use behavior**: Until backend semantics are confirmed (cascade vs. reject), surface the server error message as-is. Worth a Jira comment on FR-2750 once verified.
163+
3. **`modelRuntimeConfig` / `modelMountConfig` / `extraMounts`**: explicitly out of scope per spec. Do not add form fields.
164+
4. **Capability key `deployment-preset`**: confirmed for backend 26.4.2+ per spec. Task 1 (tab) and Task 3 (selector row) must gate on `baiClient.supports('deployment-preset')`.
165+
5. **Preset application is out of scope**: Task 3 only adds the selector and ⓘ button. Applying preset values to form state is a follow-up belonging to a separate spec (the spec lists this as a non-goal).
166+
167+
---
168+
169+
## Estimated Effort
170+
171+
| Task | Complexity | Estimate |
172+
|------|-----------|----------|
173+
| Task 1: Admin tab + List page | Medium | 1.5 days |
174+
| Task 2: Admin create/edit/delete modal | High | 2–2.5 days |
175+
| Task 3: User-facing selector detail | Medium | 1 day |
176+
177+
**Total**: ~4.5–5 days. Task 1 and Task 2 can run in parallel, compressing wall-clock time to ~3 days if two engineers work simultaneously.
178+
179+
---
180+
181+
## Jira Sub-tasks
182+
183+
| Task | Jira Key | URL |
184+
|------|----------|-----|
185+
| Task 1: Add Deployment Preset admin tab and list page | FR-2760 | https://lablup.atlassian.net/browse/FR-2760 |
186+
| Task 2: Add Deployment Preset create/edit/delete modal | FR-2761 | https://lablup.atlassian.net/browse/FR-2761 |
187+
| Task 3: Add Deployment Preset detail view in service launcher | FR-2762 | https://lablup.atlassian.net/browse/FR-2762 |
188+
189+
Links:
190+
- FR-2760 blocks FR-2761
191+
- FR-2761 blocks FR-2762
192+
- All tasks relate to FR-2750

0 commit comments

Comments
 (0)