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
+1 -1
View File
@@ -2,7 +2,7 @@
dir: apps/web/src/components
## role
Provides reusable, accessible UI primitives and layout components for a React web application, including shell structure, data visualization states, iconography, code editing/display, authentication guards, and toast notifications.
Provides foundational React UI components and utilities for rendering layouts, navigation, data states, icons, code editing, syntax highlighting, authentication guards, and toast notifications in the web application.
## parent
index: apps/web/src/.pi-map.index.md
map: apps/web/src/.pi-map.md
+2 -2
View File
@@ -4,7 +4,7 @@ dir: apps/web/src/components
index: apps/web/src/components/.pi-map.index.md
## role
Provides reusable UI components, layout primitives, and auth guards for the web application's React-based frontend.
Provides reusable, accessible UI components and utilities for a React web application including layout shells, data state handling, iconography, routing guards, and toast notifications.
## files
- app-shell.tsx | Renders the main application shell layout with navigation, header, session management, and mobile-responsive behavior for a React Router-based SPA. | exp: AppShell | dep: react-router-dom, ../api/sessions, ../hooks/use-theme, ../state/auth, ../state/sessions, ../hooks/use-mobile-viewport, ../state/events, ../state/toast, ../state/notifications, ./features/notification/event-toast-bridge, ./features/notification/notification-center, ./icon, ./features/mobile/mobile-nav, ./features/tool/start-tool-fab, ../utils/icons
- code-editor.tsx | A React component that renders a syntax-highlighted code editor with line numbers using react-simple-code-editor. | exp: CodeEditor | dep: react, react-simple-code-editor, ../utils/language
@@ -17,7 +17,7 @@ Provides reusable UI components, layout primitives, and auth guards for the web
- toast-rules.test.ts | Unit tests for mapping instance events to toast notification categories and severities | dep: vitest, ./toast-rules, ../types/events
- toast-rules.ts | Maps instance events to toast notifications with deduplication logic to prevent spam | exp: func:mapEventToCategory(event: InstanceEventPayload) → string, call:event.event.startsWith, func:mapEventToSeverity(event: InstanceEventPayload) → "info" | "warning" | "error" | "success", func:handleEventToast(event: InstanceEventPayload) → void, call:shouldShowToast, call:toast.info, call:toast.success, call:toast.warning, call:toast.error, func:clearToastDedup() → void, call:lastToastTime.clear | dep: ../state/toast, ../types/events, toast state module, InstanceEventPayload type
## arch
Component-based React architecture with functional components, composition patterns, separation of concerns (presentation, auth, feedback), and test colocation.
Component-based React architecture with functional components, custom hooks for auth/session management, utility modules for pure logic (toast rules), centralized icon abstraction layer, and test coverage for critical routing and notification logic.
## tags
toast, icon, react, state, code, loading, event, editor
## symbols
@@ -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"