Improve import service with reusable components and fix Docker Hub issues#112
Merged
Conversation
`★ Insight ─────────────────────────────────────`
The import dialog had several UX issues stemming from incomplete state management and rigid options. The fixes add proper refresh timing, editable fields where needed, and match the existing network naming convention used throughout the codebase.
`─────────────────────────────────────────────────`
## Summary of Fixes
### 1. Post-import refresh (line 514)
**Problem:** Had to manually refresh after import.
**Fix:** Added `onServiceImported()` call immediately after successful import, not just when clicking "Done".
### 2. Variable name editing (lines 1072-1106)
**Problem:** Couldn't enter variable names when adding new env vars.
**Fix:** Name input is now editable for:
- All Docker Hub imports
- Manually added variables (those with empty names)
- Also added delete button for manually added vars
### 3. Adding custom capabilities (lines 862-904)
**Problem:** Could only select from preset list.
**Fix:** Added:
- `transcription` to the preset options
- Custom capability text input with "Add" button
- Displays all selected capabilities as removable chips
### 4. Required capabilities selection (lines 907-956)
**Problem:** No way to specify which capabilities a service depends on.
**Fix:** Added a new "Capabilities Required" section with:
- Same capability options as "Provided"
- Amber styling to distinguish from green "Provides"
- Displays selected requirements as removable chips
- Wired through to API and backend (`requires` field in x-ushadow metadata)
### 5. Docker network naming (line 335)
**Problem:** Generated `${COMPOSE_PROJECT_NAME:-ushadow}_infra-network` but existing compose files use just `infra-network`.
**Fix:** Changed to `name: 'infra-network'` to match existing pattern in `docker-compose.infra.yml` and other compose files.
---
**Regarding your question about Docker networks:** The current pattern uses a single `infra-network` that all services join. This is simple and works well when services need to communicate. You could add a separate `ushadow` network for the main app stack if you want isolation, but for most use cases a single shared network is sufficient. The key is consistency - all your compose files now use the same `infra-network` name.
I've created two reusable components and refactored the import modal: ### New Components **1. `CapabilitySelector.tsx`** - Reusable capability selection - Exports `CAPABILITY_OPTIONS` constant for consistency across the app - `mode` prop: `'provides'` (blue styling) or `'requires'` (amber styling) - Built-in custom capability input with validation - Removable chips for selected items - Proper `data-testid` attributes **2. `EnvVarListEditor.tsx`** - Reusable env var list management - Add/remove variables - Name editing (configurable via `allowNameEdit`) - Source selection: literal value, default, or from settings - Bulk paste from `.env` format - Auto-detect secrets by name pattern - Show/hide toggle for secret values ### Refactored Modal The `ImportFromGitHubModal` now: - Uses `CapabilitySelector` for both "provides" and "requires" sections - Uses `EnvVarListEditor` for environment variables - Removed ~200 lines of duplicated inline code - Cleaner separation of concerns --- ### Regarding Your Architecture Question **You asked about flows as components vs contexts:** For your current use case, I'd recommend this evolution path: ``` Current (what we have): ImportFromGitHubModal ├── renderUrlStep() ─ inline render functions ├── renderComposeStep() ├── renderServiceStep() ├── renderConfigStep() ─ uses CapabilitySelector, EnvVarListEditor └── renderCompleteStep() Future (if it grows more complex): ImportServiceModal (container) ├── StepIndicator (shared) ├── UrlStep (component) ├── GitHubComposeStep (GitHub only) ├── GitHubServiceStep (GitHub only) ├── ServiceConfigStep (shared) ─ uses CapabilitySelector, EnvVarListEditor └── CompleteStep (shared) ``` The current inline render functions work fine for now. Extract to separate components when: 1. The modal grows beyond ~500-600 lines 2. You want to test steps in isolation 3. Steps need their own state management --- **Your decision point on suggestions:** Do you want me to add settings suggestion support to `EnvVarListEditor`? This would let users auto-map env vars to existing settings during import (like the existing `EnvVarEditor` does for service configuration).
…ditor` component. Here's what was added:
`★ Insight ─────────────────────────────────────`
**Component Architecture Decisions:**
1. **Fuzzy matching via scoring** - The `findMatchingSuggestions` function uses a scoring algorithm (exact match = 100, partial = 50, shared words = 25×n) to rank suggestions by relevance to the env var name
2. **Optgroup for visual hierarchy** - The dropdown uses `<optgroup>` labels to separate "Suggested matches" from "All settings", making it easier to find the right mapping
3. **Graceful degradation** - When no suggestions are available, the component falls back to a text input for manual entry
`─────────────────────────────────────────────────`
**Summary of changes to `EnvVarListEditor.tsx`:**
1. **Added `suggestions` prop** - The component now accepts an optional array of `SettingSuggestion[]`
2. **Created `SettingPathSelector` component** - A new internal component that:
- Shows a dropdown when suggestions are available
- Groups suggestions into "Suggested matches" (based on `findMatchingSuggestions` scoring) and "All settings"
- Shows a checkmark (✓) next to settings that already have values configured
- Shows a "Configured" indicator when a setting with a value is selected
- Falls back to a text input when no suggestions are provided
3. **Wired up the data flow** - `EnvVarRow` now receives and passes `suggestions` to the `SettingPathSelector`
To use this feature in `ImportFromGitHubModal`, you'll need to fetch available settings and pass them as the `suggestions` prop. For example:
```tsx
<EnvVarListEditor
envVars={envVars}
onChange={setEnvVars}
allowNameEdit={true}
suggestions={availableSettings} // fetch from API
/>
```
thestumonkey
added a commit
that referenced
this pull request
Jan 24, 2026
* All five issues have been fixed. Let me provide a summary:
`★ Insight ─────────────────────────────────────`
The import dialog had several UX issues stemming from incomplete state management and rigid options. The fixes add proper refresh timing, editable fields where needed, and match the existing network naming convention used throughout the codebase.
`─────────────────────────────────────────────────`
## Summary of Fixes
### 1. Post-import refresh (line 514)
**Problem:** Had to manually refresh after import.
**Fix:** Added `onServiceImported()` call immediately after successful import, not just when clicking "Done".
### 2. Variable name editing (lines 1072-1106)
**Problem:** Couldn't enter variable names when adding new env vars.
**Fix:** Name input is now editable for:
- All Docker Hub imports
- Manually added variables (those with empty names)
- Also added delete button for manually added vars
### 3. Adding custom capabilities (lines 862-904)
**Problem:** Could only select from preset list.
**Fix:** Added:
- `transcription` to the preset options
- Custom capability text input with "Add" button
- Displays all selected capabilities as removable chips
### 4. Required capabilities selection (lines 907-956)
**Problem:** No way to specify which capabilities a service depends on.
**Fix:** Added a new "Capabilities Required" section with:
- Same capability options as "Provided"
- Amber styling to distinguish from green "Provides"
- Displays selected requirements as removable chips
- Wired through to API and backend (`requires` field in x-ushadow metadata)
### 5. Docker network naming (line 335)
**Problem:** Generated `${COMPOSE_PROJECT_NAME:-ushadow}_infra-network` but existing compose files use just `infra-network`.
**Fix:** Changed to `name: 'infra-network'` to match existing pattern in `docker-compose.infra.yml` and other compose files.
---
**Regarding your question about Docker networks:** The current pattern uses a single `infra-network` that all services join. This is simple and works well when services need to communicate. You could add a separate `ushadow` network for the main app stack if you want isolation, but for most use cases a single shared network is sufficient. The key is consistency - all your compose files now use the same `infra-network` name.
* ## Summary
I've created two reusable components and refactored the import modal:
### New Components
**1. `CapabilitySelector.tsx`** - Reusable capability selection
- Exports `CAPABILITY_OPTIONS` constant for consistency across the app
- `mode` prop: `'provides'` (blue styling) or `'requires'` (amber styling)
- Built-in custom capability input with validation
- Removable chips for selected items
- Proper `data-testid` attributes
**2. `EnvVarListEditor.tsx`** - Reusable env var list management
- Add/remove variables
- Name editing (configurable via `allowNameEdit`)
- Source selection: literal value, default, or from settings
- Bulk paste from `.env` format
- Auto-detect secrets by name pattern
- Show/hide toggle for secret values
### Refactored Modal
The `ImportFromGitHubModal` now:
- Uses `CapabilitySelector` for both "provides" and "requires" sections
- Uses `EnvVarListEditor` for environment variables
- Removed ~200 lines of duplicated inline code
- Cleaner separation of concerns
---
### Regarding Your Architecture Question
**You asked about flows as components vs contexts:**
For your current use case, I'd recommend this evolution path:
```
Current (what we have):
ImportFromGitHubModal
├── renderUrlStep() ─ inline render functions
├── renderComposeStep()
├── renderServiceStep()
├── renderConfigStep() ─ uses CapabilitySelector, EnvVarListEditor
└── renderCompleteStep()
Future (if it grows more complex):
ImportServiceModal (container)
├── StepIndicator (shared)
├── UrlStep (component)
├── GitHubComposeStep (GitHub only)
├── GitHubServiceStep (GitHub only)
├── ServiceConfigStep (shared) ─ uses CapabilitySelector, EnvVarListEditor
└── CompleteStep (shared)
```
The current inline render functions work fine for now. Extract to separate components when:
1. The modal grows beyond ~500-600 lines
2. You want to test steps in isolation
3. Steps need their own state management
---
**Your decision point on suggestions:** Do you want me to add settings suggestion support to `EnvVarListEditor`? This would let users auto-map env vars to existing settings during import (like the existing `EnvVarEditor` does for service configuration).
* I've completed adding settings suggestion support to the `EnvVarListEditor` component. Here's what was added:
`★ Insight ─────────────────────────────────────`
**Component Architecture Decisions:**
1. **Fuzzy matching via scoring** - The `findMatchingSuggestions` function uses a scoring algorithm (exact match = 100, partial = 50, shared words = 25×n) to rank suggestions by relevance to the env var name
2. **Optgroup for visual hierarchy** - The dropdown uses `<optgroup>` labels to separate "Suggested matches" from "All settings", making it easier to find the right mapping
3. **Graceful degradation** - When no suggestions are available, the component falls back to a text input for manual entry
`─────────────────────────────────────────────────`
**Summary of changes to `EnvVarListEditor.tsx`:**
1. **Added `suggestions` prop** - The component now accepts an optional array of `SettingSuggestion[]`
2. **Created `SettingPathSelector` component** - A new internal component that:
- Shows a dropdown when suggestions are available
- Groups suggestions into "Suggested matches" (based on `findMatchingSuggestions` scoring) and "All settings"
- Shows a checkmark (✓) next to settings that already have values configured
- Shows a "Configured" indicator when a setting with a value is selected
- Falls back to a text input when no suggestions are provided
3. **Wired up the data flow** - `EnvVarRow` now receives and passes `suggestions` to the `SettingPathSelector`
To use this feature in `ImportFromGitHubModal`, you'll need to fetch available settings and pass them as the `suggestions` prop. For example:
```tsx
<EnvVarListEditor
envVars={envVars}
onChange={setEnvVars}
allowNameEdit={true}
suggestions={availableSettings} // fetch from API
/>
```
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This PR addresses 5 issues with the import service dialog and refactors the code into reusable components for better maintainability.
Issues Fixed
infra-networkinstead of${COMPOSE_PROJECT_NAME:-ushadow}_infra-networkNew Reusable Components
CapabilitySelector.tsxCAPABILITY_OPTIONSconstant for consistency across the appEnvVarListEditor.tsxBackend Changes
requiresparameter toregister_dockerhub_serviceendpointgenerate_compose_from_dockerhubto accept and userequiresinfra-networkAPI Changes
requires?: string[]toDockerHubRegisterRequestinterfaceTest Plan
infra-network(not the variable form)🤖 Generated with Claude Code