feat: mobile edit-as-default with sticky save/delete actions

- Update MobileEditView to render Save and optional Delete in a
  sticky bottom action bar; header now shows Cancel + title only.
- Make ConfigProfilesMobileView open edit view on profile tap.
- Make ToolWorkshopMobileView open edit view on tool type tap.
- Wire delete into MobileEditView for existing profiles and tool
  types.
- Stay on edit view after saving an existing item; create flow
  returns to list as before.
- Update ToolWorkshopPage cancel to return to list.
- Add mobile-edit-actions and mobile-edit-delete CSS.

Quality gates: npm run typecheck, npm run lint, npm test -- --run (87 passed)

Refs: openspec/changes/mobile-edit-default-bottom-actions
This commit is contained in:
Developer
2026-06-13 22:29:15 +00:00
parent 8b15689fb3
commit 23fc0a6b82
29 changed files with 164 additions and 59 deletions
@@ -2,7 +2,7 @@
dir: apps/web/src/components/features
## role
Reusable UI feature components for the web application that compose domain-specific user interface elements.
Contains reusable UI components that implement specific business features or domain logic for the web application.
## parent
index: apps/web/src/components/.pi-map.index.md
map: apps/web/src/components/.pi-map.md
+2 -2
View File
@@ -4,10 +4,10 @@ dir: apps/web/src/components/features
index: apps/web/src/components/features/.pi-map.index.md
## role
Reusable UI feature components for the web application that compose domain-specific user interface elements.
Contains reusable React components that implement specific user-facing features and functionality within the web application.
## files
## arch
Modular feature-based component organization with domain-specific grouping, likely following atomic design or container/presentational patterns with shared component composition.
Feature-based component organization following domain-driven design principles, likely with each sub-directory representing a distinct business capability or user workflow.
## tags
-
## symbols
@@ -2,7 +2,7 @@
dir: apps/web/src/components/features/config-profiles
## role
Provides a complete UI subsystem for managing container configuration profiles across desktop and mobile form factors.
Provides UI components for managing Docker configuration profiles including creation, editing, listing, and mobile-responsive views.
## parent
index: apps/web/src/components/features/.pi-map.index.md
map: apps/web/src/components/features/.pi-map.md
@@ -4,13 +4,13 @@ dir: apps/web/src/components/features/config-profiles
index: apps/web/src/components/features/config-profiles/.pi-map.index.md
## role
Provides a complete UI subsystem for managing container configuration profiles across desktop and mobile form factors.
Provides UI components for managing Docker configuration profiles including creation, editing, listing, and mobile-responsive views.
## files
- ConfigProfileEditorPanel.tsx | React component that renders a form-based editor panel for creating and editing configuration profiles with support for includes, environment variables, runtime hints, files, and mounts. | exp: ConfigProfileEditorPanel | dep: ../../icon, ../git/git-mount-editor, ../../../api/config-profiles, ../../../types, ../../../api/tool-types, React, Icon, GitMountEditor, ConfigProfile, CreateConfigProfileRequest, ResolvedProfile, ProjectWithRepos, ToolType
- ConfigProfileListSidebar.tsx | Renders a sidebar component for listing, selecting, creating, and deleting configuration profiles with visual indicators for default status, scope, and includes. | exp: ConfigProfileListSidebar | dep: ../../icon, ../../../api/config-profiles, Icon, ConfigProfile
- ConfigProfilesMobileView.tsx | Renders a mobile-optimized view for managing configuration profiles with list, detail, edit, and preview states. | exp: ConfigProfilesMobileView | dep: react, ../mobile/mobile-list-view, ../mobile/mobile-detail-view, ../mobile/mobile-edit-view, ../mobile/mobile-fab, ../git/git-mount-editor, ../../icon, ../../../api/config-profiles, ../../../types, ../../../api/tool-types, mobile-list-view, mobile-detail-view, mobile-edit-view, mobile-fab, git-mount-editor, icon, config-profiles API types, project types, tool-types API
- ConfigProfilesMobileView.tsx | Renders a mobile-responsive view for managing configuration profiles with list, detail, edit, and preview states. | exp: ConfigProfilesMobileView | dep: react, ../mobile/mobile-list-view, ../mobile/mobile-detail-view, ../mobile/mobile-edit-view, ../mobile/mobile-fab, ../git/git-mount-editor, ../../icon, ../../../api/config-profiles, ../../../types, ../../../api/tool-types, mobile-list-view, mobile-detail-view, mobile-edit-view, mobile-fab, git-mount-editor, icon, config-profiles API types, types
## arch
Compound component architecture with state-driven mobile adaptation, separating list navigation from editing concerns with explicit responsive breakpoint handling.
Feature-based component composition with state-driven panels (list/detail/edit/preview), form-based editing, and responsive layout adaptation for mobile/desktop.
## tags
mobile, config, view, profiles, editor, profile, list, icon
## symbols
@@ -113,7 +113,7 @@ export const ConfigProfilesMobileView = ({
try {
const ok = await onSubmit();
if (ok) {
onViewChange(isCreating ? "list" : "detail");
onViewChange(isCreating ? "list" : "edit");
}
} finally {
setIsSaving(false);
@@ -150,7 +150,7 @@ export const ConfigProfilesMobileView = ({
const profile = profiles.find((p) => p.id === id);
if (profile) {
onSelect(profile);
onViewChange("detail");
onViewChange("edit");
}
}}
onItemDelete={(id: string) => onDelete(id)}
@@ -395,9 +395,11 @@ export const ConfigProfilesMobileView = ({
<MobileEditView
title={isCreating ? "Create Profile" : "Edit Profile"}
onCancel={() => {
onViewChange(isCreating ? "list" : "detail");
onViewChange("list");
}}
onSave={handleSave}
onDelete={isCreating ? undefined : handleDeleteClick}
deleteLabel="Delete Profile"
isSaving={isSaving || saveStatus === "saving"}
>
<div className="mobile-form-group">
@@ -793,7 +795,7 @@ export const ConfigProfilesMobileView = ({
const profile = profiles.find((p) => p.id === id);
if (profile) {
onSelect(profile);
onViewChange("detail");
onViewChange("edit");
}
}}
onItemDelete={(id: string) => onDelete(id)}
@@ -2,7 +2,7 @@
dir: apps/web/src/components/features/mobile
## role
Provides a complete set of mobile-optimized UI components for building responsive mobile interfaces including navigation, lists, detail/edit views, action sheets, and specialized terminal wrappers.
Provides mobile-optimized UI components for a responsive web application, covering navigation, data views, forms, modals, and specialized terminal interfaces.
## parent
index: apps/web/src/components/features/.pi-map.index.md
map: apps/web/src/components/features/.pi-map.md
@@ -4,11 +4,11 @@ dir: apps/web/src/components/features/mobile
index: apps/web/src/components/features/mobile/.pi-map.index.md
## role
Provides a complete set of mobile-optimized UI components for building responsive mobile interfaces including navigation, lists, detail/edit views, action sheets, and specialized terminal wrappers.
Provides mobile-optimized UI components for a responsive web application, covering navigation, data views, forms, modals, and specialized terminal interfaces.
## files
- mobile-action-sheet.tsx | Renders a mobile-optimized action sheet modal with title, configurable action buttons, and cancel option. | exp: MobileActionSheetItem, func:MobileActionSheet({ isOpen, onClose, title, actions, }: MobileActionSheetProps), call:useRef, call:useEffect, call:onClose, call:document.addEventListener, call:document.removeEventListener, call:e.stopPropagation, call:actions.map, call:action.onClick | dep: react, ../../icon, icon
- mobile-detail-view.tsx | A React component that renders a mobile-optimized detail view with a header, back/edit/delete actions, and a configurable list of typed fields. | exp: MobileDetailView | dep: ../../icon, React
- mobile-edit-view.tsx | A React component that renders a mobile-optimized form for editing data with configurable field types and save/cancel actions. | exp: MobileEditView | dep: react
- mobile-edit-view.tsx | A React component that renders a mobile-optimized form for editing records with configurable field types and save/cancel/delete actions. | exp: MobileEditView | dep: react
- mobile-fab.tsx | Renders a floating action button component for mobile with an add icon and configurable click handler and label. | exp: MobileFAB | dep: ../../icon, react, icon
- mobile-list-view.tsx | Renders a mobile-optimized list view with optional search, empty state, and customizable item rendering | exp: MobileListView | dep: react, ../../icon, ../../../utils/icons, icon
- mobile-nav.tsx | Renders a mobile navigation bar with grouped items that trigger bottom sheets and standard links with active state highlighting | exp: MobileNav | dep: react, react-router-dom, ../../icon, ../tool/tools-bottom-sheet, ./spaces-bottom-sheet, ../../../utils/icons
@@ -17,7 +17,7 @@ Provides a complete set of mobile-optimized UI components for building responsiv
- mobile-terminal-wrapper.tsx | Wraps a terminal component with mobile-specific UI including auto-hiding header, virtual keyboard handling, and special keys interface. | exp: MobileTerminalWrapper | dep: react, ../terminal/terminal, ./mobile-terminal-header, ../terminal/special-keys-strip, ../terminal/special-keys-panel, ../../../hooks/use-mobile-viewport, ../../../hooks/use-virtual-keyboard, ../../../hooks/use-auto-hide, ../../../hooks/use-special-keys
- spaces-bottom-sheet.tsx | Renders a mobile bottom sheet navigation menu for switching between "Projects" and "Workspaces" spaces with active state highlighting. | exp: SpacesBottomSheet | dep: react-router-dom, ../../icon, icon
## arch
Feature-based component library using compound mobile patterns (bottom sheets, FABs, auto-hiding headers) with typed configurable props and progressive enhancement for touch/keyboard interactions.
Compositional React component library with feature-specific mobile adaptations, using bottom sheets/action sheets for mobile navigation patterns, typed configurable props for flexibility, and terminal-specific wrappers with keyboard-aware mobile handling.
## tags
mobile, terminal, sheet, icon, view, react, header, renders
## symbols
@@ -16,6 +16,8 @@ interface MobileEditViewProps {
fields?: FormField[];
onSave: (data: Record<string, string | number | boolean>) => void;
onCancel: () => void;
onDelete?: () => void;
deleteLabel?: string;
isSaving?: boolean;
children?: React.ReactNode;
}
@@ -25,6 +27,8 @@ export const MobileEditView: React.FC<MobileEditViewProps> = ({
fields,
onSave,
onCancel,
onDelete,
deleteLabel = "Delete",
isSaving = false,
children,
}) => {
@@ -59,14 +63,7 @@ export const MobileEditView: React.FC<MobileEditViewProps> = ({
Cancel
</button>
<h1 className="mobile-edit-title">{title}</h1>
<button
className="mobile-edit-save"
onClick={() => onSave(formData)}
type="button"
disabled={isSaving}
>
{isSaving ? "Saving..." : "Save"}
</button>
<div style={{ width: "4rem" }} />
</header>
<form className="mobile-edit-form" onSubmit={handleSubmit}>
@@ -156,6 +153,27 @@ export const MobileEditView: React.FC<MobileEditViewProps> = ({
</div>
))}
</form>
<div className="mobile-edit-actions">
<button
className="mobile-edit-save"
onClick={() => onSave(formData)}
type="button"
disabled={isSaving}
>
{isSaving ? "Saving..." : "Save"}
</button>
{onDelete && (
<button
className="mobile-edit-delete"
onClick={onDelete}
type="button"
disabled={isSaving}
>
{deleteLabel}
</button>
)}
</div>
</div>
);
};
@@ -2,7 +2,7 @@
dir: apps/web/src/components/features/tool-workshop
## role
Provides a complete CRUD interface for managing reusable tool type definitions (Docker Compose, Dockerfile, Manifest) in a workshop-style admin panel with responsive desktop and mobile layouts.
Provides a complete UI for managing custom tool types in a "Tool Workshop" feature, enabling users to create, edit, list, and delete tool definitions with multiple definition formats.
## parent
index: apps/web/src/components/features/.pi-map.index.md
map: apps/web/src/components/features/.pi-map.md
@@ -4,15 +4,15 @@ dir: apps/web/src/components/features/tool-workshop
index: apps/web/src/components/features/tool-workshop/.pi-map.index.md
## role
Provides a complete CRUD interface for managing reusable tool type definitions (Docker Compose, Dockerfile, Manifest) in a workshop-style admin panel with responsive desktop and mobile layouts.
Provides a complete UI for managing custom tool types in a "Tool Workshop" feature, enabling users to create, edit, list, and delete tool definitions with multiple definition formats.
## files
- ToolTypeEditorPanel.tsx | Renders a form panel for creating or editing tool types with support for compose, dockerfile, and manifest definition types | exp: ToolTypeFormState, ToolTypeEditorPanel | dep: ../../icon, ../tool/manifest-editor, ../../../api/tool-types, ../../../api/tool-definitions, React, Icon, ManifestEditor
- ToolTypeListSidebar.tsx | Renders a sidebar component for listing, selecting, creating, and deleting tool types in a "Tool Workshop" interface. | exp: ToolTypeListSidebar | dep: ../../icon, ../../../api/tool-types, React, Icon component, ToolType type
- ToolWorkshopMobileView.tsx | Renders a mobile-responsive three-view (list/detail/edit) interface for managing tool types in a tool workshop, with form handling for creating and editing tool configurations including Docker Compose, Dockerfile, or Manifest definitions. | exp: MobileView, ToolWorkshopMobileView | dep: react, ../mobile/mobile-list-view, ../mobile/mobile-detail-view, ../mobile/mobile-edit-view, ../mobile/mobile-fab, ../tool/manifest-editor, ../../icon, ../../../api/tool-types, ./ToolTypeEditorPanel, ../../../api/tool-definitions, mobile-list-view, mobile-detail-view, mobile-edit-view, mobile-fab, manifest-editor, icon, tool-types, ToolTypeEditorPanel, tool-definitions
- ToolWorkshopMobileView.tsx | Renders a mobile-responsive interface for managing tool types with list, detail, and edit views | exp: MobileView, ToolWorkshopMobileView | dep: react, ../mobile/mobile-list-view, ../mobile/mobile-detail-view, ../mobile/mobile-edit-view, ../mobile/mobile-fab, ../tool/manifest-editor, ../../icon, ../../../api/tool-types, ./ToolTypeEditorPanel, ../../../api/tool-definitions, mobile-list-view, mobile-detail-view, mobile-edit-view, mobile-fab, manifest-editor, icon, tool-types, tool-definitions
## arch
Compound component pattern with split-pane desktop layout (sidebar list + editor panel) and state-driven mobile view with three-view routing (list/detail/edit), using React state for form management and optimistic UI updates.
Implements a split-pane layout pattern with sidebar list navigation and detail/editor panel, plus a dedicated mobile-responsive view with state-driven screen switching, all using React functional components with TypeScript.
## tags
tool, mobile, view, type, types, editor, list, workshop
tool, mobile, view, type, types, editor, list, panel
## symbols
- ToolTypeFormState
- ToolTypeEditorPanel
@@ -60,7 +60,7 @@ export const ToolWorkshopMobileView = ({
try {
const ok = await onSubmit();
if (ok) {
onViewChange("list");
onViewChange(isCreating ? "list" : "edit");
}
} finally {
setIsSaving(false);
@@ -91,7 +91,7 @@ export const ToolWorkshopMobileView = ({
const toolType = toolTypes.find((t) => t.id === id);
if (toolType) {
onSelect(toolType);
onViewChange("detail");
onViewChange("edit");
}
}}
emptyMessage="No tool types yet"
@@ -189,6 +189,8 @@ export const ToolWorkshopMobileView = ({
title={isCreating ? "Create Tool Type" : "Edit Tool Type"}
onCancel={onCancel}
onSave={handleSave}
onDelete={isCreating ? undefined : handleDelete}
deleteLabel="Delete Tool Type"
isSaving={isSaving}
>
<div className="mobile-form-group">
@@ -452,7 +454,7 @@ export const ToolWorkshopMobileView = ({
const toolType = toolTypes.find((t) => t.id === id);
if (toolType) {
onSelect(toolType);
onViewChange("detail");
onViewChange("edit");
}
}}
emptyMessage="No tool types yet"