From 98d43933879b2d02e7b9288b09976fa184fa7720 Mon Sep 17 00:00:00 2001 From: Developer Date: Sat, 13 Jun 2026 22:04:17 +0000 Subject: [PATCH] feat: redesign mobile Config Profiles detail, preview, and edit pages - Rewrite ConfigProfilesMobileView to match desktop functionality: full detail view with all fields, preview action showing resolved profile, and edit view with project/tool selects, includes, environment variables, runtime hints, files, mounts with nested files, and git mounts. - Update useConfigProfiles.handleSubmit to return boolean success. - Update ConfigProfilesPage to pass required state and callbacks. - Add mobile-specific CSS for config profile forms, includes, mount/file cards, and preview panels. - Allow MobileDetailView to render extra children. Quality gates: npm run typecheck, npm run lint, npm test -- --run (87 passed) Refs: openspec/changes/mobile-config-profiles-ui --- .pi-map.index.md | 2 +- .pi-map.md | 4 +- apps/.pi-map.index.md | 2 +- apps/.pi-map.md | 4 +- apps/web/.pi-map.index.md | 2 +- apps/web/.pi-map.md | 4 +- apps/web/src/.pi-map.index.md | 2 +- apps/web/src/.pi-map.md | 4 +- apps/web/src/components/.pi-map.index.md | 2 +- apps/web/src/components/.pi-map.md | 4 +- .../src/components/features/.pi-map.index.md | 2 +- apps/web/src/components/features/.pi-map.md | 4 +- .../features/config-profiles/.pi-map.index.md | 2 +- .../features/config-profiles/.pi-map.md | 8 +- .../ConfigProfilesMobileView.tsx | 1078 ++++++++++++----- .../features/mobile/.pi-map.index.md | 2 +- .../src/components/features/mobile/.pi-map.md | 10 +- .../features/mobile/mobile-detail-view.tsx | 3 + apps/web/src/hooks/.pi-map.index.md | 2 +- apps/web/src/hooks/.pi-map.md | 6 +- apps/web/src/hooks/use-config-profiles.ts | 6 +- apps/web/src/pages/.pi-map.index.md | 2 +- apps/web/src/pages/.pi-map.md | 8 +- apps/web/src/pages/ConfigProfilesPage.tsx | 26 +- apps/web/src/styles/.pi-map.index.md | 2 +- apps/web/src/styles/.pi-map.md | 8 +- apps/web/src/styles/utilities.css | 229 ++++ .../mobile-config-profiles-ui/change.md | 34 + .../mobile-config-profiles-ui/tasks.md | 10 + 29 files changed, 1135 insertions(+), 337 deletions(-) create mode 100644 openspec/changes/mobile-config-profiles-ui/change.md create mode 100644 openspec/changes/mobile-config-profiles-ui/tasks.md diff --git a/.pi-map.index.md b/.pi-map.index.md index 015bdf0..b1801fb 100644 --- a/.pi-map.index.md +++ b/.pi-map.index.md @@ -16,7 +16,7 @@ dir: . Trust boundary: index routes, map orients, source decides. ## role -Infrastructure and deployment configuration for a self-hosted project management and git repository platform with OAuth2 authentication. +A self-hosted, containerized project management and Git repository platform with OAuth2 SSO integration, designed for deployment via Docker Compose with or without Traefik reverse proxy. ## parent - ## children diff --git a/.pi-map.md b/.pi-map.md index e13d4d8..beec14b 100644 --- a/.pi-map.md +++ b/.pi-map.md @@ -18,7 +18,7 @@ index: ./.pi-map.index.md Trust boundary: index routes, map orients, source decides. ## role -Infrastructure and deployment configuration for a self-hosted project management and git repository platform with OAuth2 authentication. +Infrastructure and deployment configuration for a self-hosted project management and git repository platform with multi-service Docker orchestration. ## files - .env.example | Provides a template of environment variables for configuring a Headquarter application with PostgreSQL, Redis, Authentik SSO, and Docker/Traefik deployment - .gitignore | Specifies files and directories for Git to ignore across a multi-language project with Python, Node, and custom tooling | dep: Git @@ -31,7 +31,7 @@ Infrastructure and deployment configuration for a self-hosted project management - progress.md | Tracks completed and remaining tasks for a backend-frontend code refactoring project organized in 7 phases - swap-pane | Empty file with no functionality ## arch -Containerized microservices architecture using Docker Compose with PostgreSQL/Redis data layer, Traefik reverse proxy for TLS/ingress, and environment-driven configuration across frontend/backend services. +Docker Compose-based microservices with PostgreSQL/Redis backend, Traefik reverse proxy, OAuth2 SSO integration, and environment-driven configuration management. ## tags docker, redis, git, application, postgresql, compose, traefik, project ## symbols diff --git a/apps/.pi-map.index.md b/apps/.pi-map.index.md index 9d5e74a..aa9812c 100644 --- a/apps/.pi-map.index.md +++ b/apps/.pi-map.index.md @@ -2,7 +2,7 @@ dir: apps ## role -Contains the main deployable application entry points and top-level configurations for the project. +Contains the main deployable application entry points and executable configurations for the project. ## parent index: ./.pi-map.index.md map: ./.pi-map.md diff --git a/apps/.pi-map.md b/apps/.pi-map.md index ee59b25..ecbd43c 100644 --- a/apps/.pi-map.md +++ b/apps/.pi-map.md @@ -4,10 +4,10 @@ dir: apps index: apps/.pi-map.index.md ## role -Contains the main deployable application entry points and top-level configurations for the project. +Contains the main application entry points and executable modules for the project. ## files ## arch -Monorepo-style app separation with distinct application boundaries, likely sharing common libraries/modules while maintaining independent deployment artifacts. +Modular application structure with separate deployable units, typically following framework-specific conventions (e.g., Next.js, React, or other app-based architectures). ## tags - ## symbols diff --git a/apps/web/.pi-map.index.md b/apps/web/.pi-map.index.md index 5595ada..7ed2ebf 100644 --- a/apps/web/.pi-map.index.md +++ b/apps/web/.pi-map.index.md @@ -2,7 +2,7 @@ dir: apps/web ## role -Frontend web application for a React-based developer workspace with code editing, terminal, and routing capabilities. +Frontend web application for a React-based "headquarter" project providing code editing, terminal, and routing capabilities via Vite/TypeScript. ## parent index: apps/.pi-map.index.md map: apps/.pi-map.md diff --git a/apps/web/.pi-map.md b/apps/web/.pi-map.md index 3bb69f3..7974384 100644 --- a/apps/web/.pi-map.md +++ b/apps/web/.pi-map.md @@ -4,7 +4,7 @@ dir: apps/web index: apps/web/.pi-map.index.md ## role -Frontend web application for a React-based developer workspace with code editing, terminal, and routing capabilities. +Frontend web application for a React-based "headquarter" project providing code editing, terminal, and routing capabilities via Vite/TypeScript. ## files - .env.example | Template file defining example environment variables for frontend API and application URL configuration - .eslintrc.cjs | Configures ESLint for a TypeScript browser project with modern ECMAScript module support | dep: @typescript-eslint/parser, @typescript-eslint/eslint-plugin, eslint @@ -16,7 +16,7 @@ Frontend web application for a React-based developer workspace with code editing - tsconfig.json | TypeScript configuration file for a React project using Vite with modern ES2020 target and bundler module resolution | dep: typescript, react, vite - vite.config.ts | Configures Vite build tool for a React project with custom dev server port and Vitest test settings. | dep: vite, @vitejs/plugin-react ## arch -Modern Vite-based SPA with TypeScript, nginx reverse proxy serving, multi-stage Docker containerization, and client-side routing. +Modern SPA architecture using Vite bundler with React, TypeScript, nginx static serving, multi-stage Docker containerization, and client-side routing with optimized asset caching. ## tags react, eslint, vite, typescript, dom, application, nginx, web ## symbols diff --git a/apps/web/src/.pi-map.index.md b/apps/web/src/.pi-map.index.md index 5ab9050..4ebffb3 100644 --- a/apps/web/src/.pi-map.index.md +++ b/apps/web/src/.pi-map.index.md @@ -2,7 +2,7 @@ dir: apps/web/src ## role -Entry point and core infrastructure for a React web application handling authentication, routing, and domain type definitions. +Provides the core web application entry point, routing infrastructure, and shared domain type definitions for a React-based frontend. ## parent index: apps/web/.pi-map.index.md map: apps/web/.pi-map.md diff --git a/apps/web/src/.pi-map.md b/apps/web/src/.pi-map.md index 8a2f9b8..0c9a390 100644 --- a/apps/web/src/.pi-map.md +++ b/apps/web/src/.pi-map.md @@ -4,13 +4,13 @@ dir: apps/web/src index: apps/web/src/.pi-map.index.md ## role -Entry point and core infrastructure for a React web application handling authentication, routing, and domain type definitions. +Frontend web application entry point and core infrastructure for a React-based collaborative development platform. ## files - main.tsx | Bootstraps a React application with routing, authentication, and session management providers. | dep: react, react-dom/client, react-router-dom, ./router, ./state/auth, ./state/sessions, ./styles/tokens.css, ./styles/global.css, ./styles/utilities.css, ./styles/syntax-highlight.css, ./styles/pages/git-history.css, ./styles/pages/projects.css, ./styles/pages/sessions.css, ./styles/pages/ssh-keys.css, ./styles/pages/workspace-detail.css, ./styles/pages/workspaces.css, react-dom - router.tsx | Defines the React Router configuration for a web application with protected routes, nested layouts, and redirects. | exp: AppRouter | dep: react-router-dom, ./components/app-shell, ./components/protected-route, ./pages/DashboardPage, ./pages/PlaceholderPage, ./pages/ProfilePage, ./pages/ProjectsPage, ./pages/GitRepositoriesPage, ./pages/GitHistoryPage, ./pages/ProjectSettingsPage, ./pages/SettingsPage, ./pages/TerminalPage, ./pages/ToolWorkshopPage, ./pages/SshKeysPage, ./pages/ConfigProfilesPage, ./pages/SessionsPage, ./pages/WorkspacesPage, ./pages/WorkspaceDetailPage - types.ts | Defines TypeScript type definitions for user sessions, projects, repositories, and workspaces in an application. | exp: SessionUser, SessionPayload, Project, WorkspaceSummary, RepositorySummary, ProjectWithRepos ## arch -Provider pattern wrapping React Router with nested route layouts, protected route guards, and centralized TypeScript type definitions for session-based auth and workspace/project domain models. +Modular React SPA with provider-pattern state management, nested routing with authentication guards, and centralized TypeScript domain modeling for multi-tenant workspace/project hierarchy. ## tags pages, styles, css, router, react, session, dom, project ## symbols diff --git a/apps/web/src/components/.pi-map.index.md b/apps/web/src/components/.pi-map.index.md index 92d53a9..9c9b73d 100644 --- a/apps/web/src/components/.pi-map.index.md +++ b/apps/web/src/components/.pi-map.index.md @@ -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 layout, data states, icons, code display, routing guards, and toast notifications. +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. ## parent index: apps/web/src/.pi-map.index.md map: apps/web/src/.pi-map.md diff --git a/apps/web/src/components/.pi-map.md b/apps/web/src/components/.pi-map.md index c68a7c0..afc841e 100644 --- a/apps/web/src/components/.pi-map.md +++ b/apps/web/src/components/.pi-map.md @@ -4,7 +4,7 @@ dir: apps/web/src/components index: apps/web/src/components/.pi-map.index.md ## role -Provides reusable, accessible UI components and utilities for rendering the application shell, data states, icons, code display, notifications, and route protection in a React web application. +Provides reusable UI components, layout primitives, and auth guards for the web application's React-based frontend. ## 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, accessible UI components and utilities for rendering the appl - 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 vs logic), and test coverage for critical utilities; integrates third-party libraries (Phosphor icons, react-simple-code-editor) and React Router for SPA navigation. +Component-based React architecture with functional components, composition patterns, separation of concerns (presentation, auth, feedback), and test colocation. ## tags toast, icon, react, state, code, loading, event, editor ## symbols diff --git a/apps/web/src/components/features/.pi-map.index.md b/apps/web/src/components/features/.pi-map.index.md index 3f75e0e..48738e1 100644 --- a/apps/web/src/components/features/.pi-map.index.md +++ b/apps/web/src/components/features/.pi-map.index.md @@ -2,7 +2,7 @@ dir: apps/web/src/components/features ## role -Reusable UI feature components that compose domain-specific functionality for the web application +Reusable UI feature components for the web application that compose domain-specific user interface elements. ## parent index: apps/web/src/components/.pi-map.index.md map: apps/web/src/components/.pi-map.md diff --git a/apps/web/src/components/features/.pi-map.md b/apps/web/src/components/features/.pi-map.md index 4f690bf..1d1b6f2 100644 --- a/apps/web/src/components/features/.pi-map.md +++ b/apps/web/src/components/features/.pi-map.md @@ -4,10 +4,10 @@ dir: apps/web/src/components/features index: apps/web/src/components/features/.pi-map.index.md ## role -Contains reusable React components that implement specific user-facing features and functionality across the web application. +Reusable UI feature components for the web application that compose domain-specific user interface elements. ## files ## arch -Feature-based component organization with domain-specific UI building blocks following React composition patterns, likely co-located with related hooks, utilities, or sub-components for each feature area. +Modular feature-based component organization with domain-specific grouping, likely following atomic design or container/presentational patterns with shared component composition. ## tags - ## symbols diff --git a/apps/web/src/components/features/config-profiles/.pi-map.index.md b/apps/web/src/components/features/config-profiles/.pi-map.index.md index 91cf2a9..8d8510b 100644 --- a/apps/web/src/components/features/config-profiles/.pi-map.index.md +++ b/apps/web/src/components/features/config-profiles/.pi-map.index.md @@ -2,7 +2,7 @@ dir: apps/web/src/components/features/config-profiles ## role -Provides a complete UI subsystem for managing configuration profiles with desktop and mobile views, supporting CRUD operations, profile relationships, and advanced configuration options. +Provides a complete UI subsystem for managing container configuration profiles across desktop and mobile form factors. ## parent index: apps/web/src/components/features/.pi-map.index.md map: apps/web/src/components/features/.pi-map.md diff --git a/apps/web/src/components/features/config-profiles/.pi-map.md b/apps/web/src/components/features/config-profiles/.pi-map.md index ae0e606..c2ee5cc 100644 --- a/apps/web/src/components/features/config-profiles/.pi-map.md +++ b/apps/web/src/components/features/config-profiles/.pi-map.md @@ -4,15 +4,15 @@ 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 configuration profiles with desktop and mobile views, supporting CRUD operations, profile relationships, and advanced configuration options. +Provides a complete UI subsystem for managing container configuration profiles across desktop and mobile form factors. ## 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-responsive CRUD interface for managing configuration profiles with list, detail, and edit views. | exp: ConfigProfilesMobileView | dep: ../mobile/mobile-list-view, ../mobile/mobile-detail-view, ../mobile/mobile-edit-view, ../mobile/mobile-fab, ../../icon, ../../../api/config-profiles, MobileListView, MobileDetailView, MobileEditView, MobileFAB, Icon, ConfigProfile, CreateConfigProfileRequest +- 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 ## arch -Compound component architecture with panel/sidebar split for desktop, dedicated mobile view with state-driven routing, shared state management across list/editor views, and form-based configuration with nested array/object handling for includes, env vars, files, and mounts. +Compound component architecture with state-driven mobile adaptation, separating list navigation from editing concerns with explicit responsive breakpoint handling. ## tags -config, mobile, profiles, profile, view, editor, icon, list +mobile, config, view, profiles, editor, profile, list, icon ## symbols - ConfigProfileEditorPanel - ConfigProfileListSidebar diff --git a/apps/web/src/components/features/config-profiles/ConfigProfilesMobileView.tsx b/apps/web/src/components/features/config-profiles/ConfigProfilesMobileView.tsx index 075e772..def409d 100644 --- a/apps/web/src/components/features/config-profiles/ConfigProfilesMobileView.tsx +++ b/apps/web/src/components/features/config-profiles/ConfigProfilesMobileView.tsx @@ -1,314 +1,810 @@ +import { useState } from "react"; + import { MobileListView } from "../mobile/mobile-list-view"; import { MobileDetailView } from "../mobile/mobile-detail-view"; import { MobileEditView } from "../mobile/mobile-edit-view"; import { MobileFAB } from "../mobile/mobile-fab"; +import { GitMountEditor } from "../git/git-mount-editor"; import { Icon } from "../../icon"; -import type { ConfigProfile, CreateConfigProfileRequest } from "../../../api/config-profiles"; +import type { + ConfigProfile, + CreateConfigProfileRequest, + ResolvedProfile, +} from "../../../api/config-profiles"; +import type { ProjectWithRepos } from "../../../types"; +import type { ToolType } from "../../../api/tool-types"; -type MobileView = "list" | "detail" | "edit"; +type MobileView = "list" | "detail" | "edit" | "preview"; interface Props { - profiles: ConfigProfile[]; - selectedProfile: ConfigProfile | null; - mobileView: MobileView; - isCreating: boolean; - formData: CreateConfigProfileRequest; - saveStatus: "idle" | "saving" | "saved" | "error"; - onViewChange: (view: MobileView) => void; - onSelect: (profile: ConfigProfile) => void; - onCreate: () => void; - onDelete: (id: string) => void; - onFormChange: (key: K, value: CreateConfigProfileRequest[K]) => void; - onSubmit: () => void; - getScopeLabel: (profile: ConfigProfile) => string; + profiles: ConfigProfile[]; + projects: ProjectWithRepos[]; + toolTypes: ToolType[]; + selectedProfile: ConfigProfile | null; + mobileView: MobileView; + isCreating: boolean; + formData: CreateConfigProfileRequest; + includedProfileIds: string[]; + availableProfiles: ConfigProfile[]; + previewData: ResolvedProfile | null; + previewingId: string | null; + saveStatus: "idle" | "saving" | "saved" | "error"; + error: string | null; + onViewChange: (view: MobileView) => void; + onSelect: (profile: ConfigProfile) => void; + onCreate: () => void; + onDelete: (id: string) => void; + onFormChange: ( + key: K, + value: CreateConfigProfileRequest[K], + ) => void; + onSubmit: (e?: React.FormEvent) => Promise; + getScopeLabel: (profile: ConfigProfile) => string; + getIncludedProfile: (id: string) => ConfigProfile | undefined; + onAddInclude: (id: string) => void; + onRemoveInclude: (index: number) => void; + onAddEnvVar: () => void; + onUpdateEnvVar: (oldKey: string, newKey: string, value: string) => void; + onRemoveEnvVar: (key: string) => void; + onAddFile: () => void; + onUpdateFile: (oldPath: string, newPath: string, content: string) => void; + onRemoveFile: (path: string) => void; + onAddMount: () => void; + onUpdateMount: ( + index: number, + updates: Partial, + ) => void; + onRemoveMount: (index: number) => void; + onAddMountFile: (mountIndex: number) => void; + onUpdateMountFile: ( + mountIndex: number, + oldPath: string, + newPath: string, + content: string, + ) => void; + onRemoveMountFile: (mountIndex: number, path: string) => void; + onPreview: (id: string) => void; + onClosePreview: () => void; } export const ConfigProfilesMobileView = ({ - profiles, - selectedProfile, - mobileView, - isCreating, - formData, - saveStatus, - onViewChange, - onSelect, - onCreate, - onDelete, - onFormChange, - onSubmit, - getScopeLabel, + profiles, + projects, + toolTypes, + selectedProfile, + mobileView, + isCreating, + formData, + includedProfileIds, + availableProfiles, + previewData, + previewingId, + saveStatus, + error, + onViewChange, + onSelect, + onCreate, + onDelete, + onFormChange, + onSubmit, + getScopeLabel, + getIncludedProfile, + onAddInclude, + onRemoveInclude, + onAddEnvVar, + onUpdateEnvVar, + onRemoveEnvVar, + onAddFile, + onUpdateFile, + onRemoveFile, + onAddMount, + onUpdateMount, + onRemoveMount, + onAddMountFile, + onUpdateMountFile, + onRemoveMountFile, + onPreview, + onClosePreview, }: Props) => { - if (mobileView === "list") { - return ( -
-
-

Config Profiles

-
- ({ - id: profile.id, - title: profile.name, - subtitle: profile.description || getScopeLabel(profile), - }))} - onItemClick={(id: string) => { - const profile = profiles.find((p) => p.id === id); - if (profile) { - onSelect(profile); - onViewChange("detail"); - } - }} - onItemDelete={(id: string) => onDelete(id)} - emptyMessage="No config profiles yet" - /> - { - onCreate(); - onViewChange("edit"); - }} - /> -
- ); - } + const [isSaving, setIsSaving] = useState(false); - if (mobileView === "detail" && selectedProfile) { - const fields = [ - { label: "Name", value: selectedProfile.name }, - { label: "Description", value: selectedProfile.description || "-" }, - { label: "Scope", value: getScopeLabel(selectedProfile) }, - { label: "Default", value: selectedProfile.is_default ? "Yes" : "No" }, - { - label: "Environment Variables", - value: - Object.keys(selectedProfile.env_vars).length > 0 - ? Object.entries(selectedProfile.env_vars) - .map(([k, v]) => `${k}=${v}`) - .join(", ") - : "-", - }, - { - label: "Mounts", - value: - selectedProfile.mounts.length > 0 - ? selectedProfile.mounts.map((m) => `${m.target} (${m.mode})`).join(", ") - : "-", - }, - { - label: "Includes", - value: - selectedProfile.includes.length > 0 - ? `${selectedProfile.includes.length} profile(s)` - : "-", - }, - ]; + const handleSave = async () => { + setIsSaving(true); + try { + const ok = await onSubmit(); + if (ok) { + onViewChange(isCreating ? "list" : "detail"); + } + } finally { + setIsSaving(false); + } + }; - return ( - onViewChange("list")} - onEdit={() => { - onSelect(selectedProfile); - onViewChange("edit"); - }} - onDelete={() => onDelete(selectedProfile.id)} - /> - ); - } + const handleDeleteClick = () => { + if (selectedProfile) { + onDelete(selectedProfile.id); + onViewChange("list"); + } + }; - if (mobileView === "edit") { - return ( - { - onViewChange(isCreating ? "list" : "detail"); - }} - onSave={onSubmit} - isSaving={saveStatus === "saving"} - > -
- - onFormChange("name", e.target.value)} - placeholder="Profile name" - required - /> -
+ const projectName = (id?: string) => + projects.find((p) => p.id === id)?.name || id || "None"; -
- -