Files
headquarter/apps/web/src/pages/ConfigProfilesPage.tsx
T
alex 2247ec47c9 fix(config-profiles): synchronize shared mount working copies
Use canonical profile files and writable Git working copies so editor and container changes share one source. Require confirmation before destructive Git refreshes and overlay profile files without composite snapshots.
2026-07-22 11:23:19 +02:00

216 lines
5.6 KiB
TypeScript

import { useState } from "react";
import { ErrorState, LoadingState } from "../components/data-states";
import { useMobileViewport } from "../hooks/use-mobile-viewport";
import { useConfigProfiles } from "../hooks/use-config-profiles";
import { ConfigProfileListSidebar } from "../components/features/config-profiles/ConfigProfileListSidebar";
import { ConfigProfileEditorPanel } from "../components/features/config-profiles/ConfigProfileEditorPanel";
import { ConfigProfilesMobileView } from "../components/features/config-profiles/ConfigProfilesMobileView";
type MobileView = "list" | "detail" | "edit" | "preview";
export const ConfigProfilesPage = () => {
const isMobile = useMobileViewport();
const [mobileView, setMobileView] = useState<MobileView>("list");
const {
status,
profiles,
projects,
toolTypes,
selectedProfile,
selectedProfileId,
isCreating,
saveStatus,
error,
previewData,
previewingId,
isRefreshingGitMounts,
isReloadingWorkingCopy,
formData,
includedProfileIds,
dragOverIndex,
loadData,
handleSelectProfile,
handleCreateNew,
handleSubmit,
handleDelete,
handlePreview,
handleReloadWorkingCopy,
handleRefreshGitMounts,
updateFormField,
addEnvVar,
updateEnvVar,
removeEnvVar,
addFile,
updateFile,
removeFile,
addMount,
updateMount,
removeMount,
addMountFile,
updateMountFile,
removeMountFile,
getIncludedProfile,
getScopeLabel,
availableProfilesForInclude,
addInclude,
removeInclude,
handleDragStart,
handleDragOver,
handleDragLeave,
handleDrop,
setPreviewData,
setFormData,
setSaveStatus,
setIncludedProfileIds,
populateForm,
} = useConfigProfiles();
const handleReset = () => {
if (isCreating) {
setFormData({
name: "",
description: "",
env_vars: {},
runtime_hints: {},
mounts: [],
git_mounts: [],
files: {},
is_default: false,
});
setIncludedProfileIds([]);
setSaveStatus("idle");
} else if (selectedProfile) {
populateForm(selectedProfile);
}
};
if (status === "loading")
return (
<div className="container">
<LoadingState message="Loading Config Profiles..." />
</div>
);
if (status === "error")
return (
<div className="container">
<ErrorState
message="Failed to load Config Profiles."
onRetry={loadData}
/>
</div>
);
if (isMobile) {
return (
<ConfigProfilesMobileView
profiles={profiles}
projects={projects}
toolTypes={toolTypes}
selectedProfile={selectedProfile}
mobileView={mobileView}
isCreating={isCreating}
formData={formData}
includedProfileIds={includedProfileIds}
availableProfiles={availableProfilesForInclude()}
previewData={previewData}
previewingId={previewingId}
isRefreshingGitMounts={isRefreshingGitMounts}
isReloadingWorkingCopy={isReloadingWorkingCopy}
saveStatus={saveStatus}
error={error}
onViewChange={setMobileView}
onSelect={handleSelectProfile}
onCreate={handleCreateNew}
onDelete={handleDelete}
onFormChange={updateFormField}
onSubmit={handleSubmit}
getScopeLabel={getScopeLabel}
getIncludedProfile={getIncludedProfile}
onAddInclude={addInclude}
onRemoveInclude={removeInclude}
onAddEnvVar={addEnvVar}
onUpdateEnvVar={updateEnvVar}
onRemoveEnvVar={removeEnvVar}
onAddFile={addFile}
onUpdateFile={updateFile}
onRemoveFile={removeFile}
onAddMount={addMount}
onUpdateMount={updateMount}
onRemoveMount={removeMount}
onAddMountFile={addMountFile}
onUpdateMountFile={updateMountFile}
onRemoveMountFile={removeMountFile}
onPreview={handlePreview}
onReloadWorkingCopy={() => void handleReloadWorkingCopy()}
onRefreshGitMounts={(id) => void handleRefreshGitMounts(id)}
onClosePreview={() => setPreviewData(null)}
/>
);
}
return (
<div
className="container"
style={{
display: "flex",
height: "calc(100vh - 4rem)",
gap: 0,
padding: 0,
}}
>
<ConfigProfileListSidebar
profiles={profiles}
selectedProfileId={selectedProfileId}
onSelect={handleSelectProfile}
onCreate={handleCreateNew}
onDelete={handleDelete}
/>
<ConfigProfileEditorPanel
isCreating={isCreating}
selectedProfile={selectedProfile}
formData={formData}
includedProfileIds={includedProfileIds}
dragOverIndex={dragOverIndex}
error={error}
saveStatus={saveStatus}
previewData={previewData}
previewingId={previewingId}
isRefreshingGitMounts={isRefreshingGitMounts}
isReloadingWorkingCopy={isReloadingWorkingCopy}
projects={projects}
toolTypes={toolTypes}
availableProfiles={availableProfilesForInclude()}
getIncludedProfile={getIncludedProfile}
getScopeLabel={getScopeLabel}
onFormChange={updateFormField}
onSubmit={handleSubmit}
onReset={handleReset}
onPreview={() => selectedProfile && handlePreview(selectedProfile.id)}
onReloadWorkingCopy={() => void handleReloadWorkingCopy()}
onRefreshGitMounts={() =>
selectedProfile && handleRefreshGitMounts(selectedProfile.id)
}
onAddInclude={addInclude}
onRemoveInclude={removeInclude}
onDragStart={handleDragStart}
onDragOver={handleDragOver}
onDragLeave={handleDragLeave}
onDrop={handleDrop}
onAddEnvVar={addEnvVar}
onUpdateEnvVar={updateEnvVar}
onRemoveEnvVar={removeEnvVar}
onAddFile={addFile}
onUpdateFile={updateFile}
onRemoveFile={removeFile}
onAddMount={addMount}
onUpdateMount={updateMount}
onRemoveMount={removeMount}
onAddMountFile={addMountFile}
onUpdateMountFile={updateMountFile}
onRemoveMountFile={removeMountFile}
onClosePreview={() => setPreviewData(null)}
/>
</div>
);
};