From 27c39f9cfcbd140e025cd373d1f0b1b83df6ec8b Mon Sep 17 00:00:00 2001 From: Alex Blank Date: Mon, 25 May 2026 12:27:47 +0200 Subject: [PATCH] feat: mobile config profiles with list-detail pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add mobile list view showing all config profiles - Add mobile detail view with profile information display - Add mobile edit/create view with full form - Implement list→detail→edit navigation - Fix TypeScript errors and build issues --- apps/web/src/pages/config-profiles.tsx | 270 ++++++++++++++++++ .../changes/mobile-pages-overhaul/tasks.md | 20 +- 2 files changed, 280 insertions(+), 10 deletions(-) diff --git a/apps/web/src/pages/config-profiles.tsx b/apps/web/src/pages/config-profiles.tsx index dda0812..4d28e28 100644 --- a/apps/web/src/pages/config-profiles.tsx +++ b/apps/web/src/pages/config-profiles.tsx @@ -1,5 +1,10 @@ import { useCallback, useEffect, useState } from "react"; import { Icon } from "../components/icon"; +import { useMobileViewport } from "../hooks/use-mobile-viewport"; +import { MobileListView } from "../components/mobile-list-view"; +import { MobileDetailView } from "../components/mobile-detail-view"; +import { MobileEditView } from "../components/mobile-edit-view"; +import { MobileFAB } from "../components/mobile-fab"; import { createConfigProfile, deleteConfigProfile, @@ -16,8 +21,11 @@ import type { Project } from "../types"; import { listToolTypes, type ToolType } from "../api/tool_types"; type Status = "loading" | "ready" | "error"; +type MobileView = "list" | "detail" | "edit"; export const ConfigProfilesPage = () => { + const isMobile = useMobileViewport(); + const [mobileView, setMobileView] = useState("list"); const [status, setStatus] = useState("loading"); const [profiles, setProfiles] = useState([]); const [projects, setProjects] = useState([]); @@ -425,6 +433,268 @@ export const ConfigProfilesPage = () => { ); } + // Mobile view rendering + if (isMobile) { + if (mobileView === "list") { + return ( +
+
+

Config Profiles

+
+ ({ + id: profile.id, + title: profile.name, + subtitle: profile.description || getScopeLabel(profile), + }))} + onItemClick={(id: string) => { + setSelectedProfileId(id); + setIsCreating(false); + const profile = profiles.find((p) => p.id === id); + if (profile) populateForm(profile); + setMobileView("detail"); + }} + onItemDelete={(id: string) => handleDelete(id)} + emptyMessage="No config profiles yet" + /> + { + setIsCreating(true); + setSelectedProfileId(null); + resetForm(); + setMobileView("edit"); + }} /> +
+ ); + } + + 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)` : "-" }, + ]; + + return ( + setMobileView("list")} + onEdit={() => { + populateForm(selectedProfile); + setIsCreating(false); + setMobileView("edit"); + }} + onDelete={() => handleDelete(selectedProfile.id)} + /> + ); + } + + if (mobileView === "edit") { + return ( + { + if (isCreating) { + setMobileView("list"); + } else if (selectedProfile) { + setMobileView("detail"); + } else { + setMobileView("list"); + } + }} + onSave={() => handleSubmit({ preventDefault: () => {} } as React.FormEvent)} + isSaving={saveStatus === "saving"} + > + {/* Profile form fields */} +
+ + updateFormField("name", e.target.value)} + placeholder="Profile name" + required + /> +
+ +
+ +