feat: rework mobile UI for Tool Workshop and Profile pages
- Rework ToolWorkshopMobileView to support full desktop functionality: definition type selection (Compose/Dockerfile/Manifest), manifest editor, conditional port, startup command, readiness probe, required variables, and validation feedback. - Add ProfileMobileView and wire ProfilePage to render it on mobile. - Update useToolWorkshop hook to return boolean success from submit. - Add responsive CSS for mobile forms, edit views, and manifest editor. - Update project maps. Quality gates: npm run typecheck, npm run lint, npm test -- --run (87 passed) Refs: openspec/changes/mobile-tool-profile-ui
This commit is contained in:
@@ -3,13 +3,16 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { getProfile, updateProfile, uploadAvatar } from "../api/profile";
|
||||
import { ErrorState, LoadingState } from "../components/data-states";
|
||||
import { Icon } from "../components/icon";
|
||||
import { ProfileMobileView } from "../components/features/profile/ProfileMobileView";
|
||||
import { useAuth } from "../state/auth";
|
||||
import { useAsyncData } from "../hooks/use-async-data";
|
||||
import { useMobileViewport } from "../hooks/use-mobile-viewport";
|
||||
import type { UserProfile } from "../api/profile";
|
||||
|
||||
type ProfileStatus = "loading" | "ready" | "error" | "saving";
|
||||
|
||||
export const ProfilePage = () => {
|
||||
const isMobile = useMobileViewport();
|
||||
const { refreshSession } = useAuth();
|
||||
const { data: profile, status: loadStatus, reload } = useAsyncData<UserProfile>(getProfile, []);
|
||||
const [displayStatus, setDisplayStatus] = useState<ProfileStatus>("loading");
|
||||
@@ -97,7 +100,23 @@ export const ProfilePage = () => {
|
||||
{displayStatus === "error" && <ErrorState message="Failed to load profile" onRetry={reload} />}
|
||||
|
||||
{(displayStatus === "ready" || displayStatus === "saving") && profile && (
|
||||
<div className="card stack">
|
||||
isMobile ? (
|
||||
<ProfileMobileView
|
||||
profile={profile}
|
||||
name={name}
|
||||
email={email}
|
||||
error={error}
|
||||
isSaving={displayStatus === "saving"}
|
||||
avatarUrl={avatarUrl}
|
||||
fileInputRef={fileInputRef}
|
||||
onNameChange={setName}
|
||||
onEmailChange={setEmail}
|
||||
onAvatarButtonClick={() => fileInputRef.current?.click()}
|
||||
onAvatarChange={handleAvatarChange}
|
||||
onSave={() => void handleSave()}
|
||||
/>
|
||||
) : (
|
||||
<div className="card stack">
|
||||
<div className="profile-avatar-section">
|
||||
<div className="avatar-preview">
|
||||
{avatarUrl ? (
|
||||
@@ -178,7 +197,7 @@ export const ProfilePage = () => {
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
))}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user