disable save all when any section is invalid

This commit is contained in:
Josh Hawkins 2026-02-27 08:55:55 -06:00
parent 3b5f84c601
commit 49f4bc48b6
3 changed files with 33 additions and 7 deletions

View File

@ -121,6 +121,7 @@ export interface BaseSectionProps {
onStatusChange?: (status: {
hasChanges: boolean;
isOverridden: boolean;
hasValidationErrors: boolean;
}) => void;
/** Pending form data keyed by "sectionKey" or "cameraName::sectionKey" */
pendingDataBySection?: Record<string, unknown>;
@ -371,8 +372,8 @@ export function ConfigSection({
}, [formData, pendingData, extraHasChanges]);
useEffect(() => {
onStatusChange?.({ hasChanges, isOverridden });
}, [hasChanges, isOverridden, onStatusChange]);
onStatusChange?.({ hasChanges, isOverridden, hasValidationErrors });
}, [hasChanges, isOverridden, hasValidationErrors, onStatusChange]);
// Handle form data change
const handleChange = useCallback(

View File

@ -668,6 +668,13 @@ export default function Settings() {
const { data: fullSchema } = useSWR<RJSFSchema>("config/schema.json");
const hasPendingChanges = Object.keys(pendingDataBySection).length > 0;
const hasPendingValidationErrors = useMemo(
() =>
Object.values(sectionStatusByKey).some(
(status) => !!status && status.hasChanges && status.hasValidationErrors,
),
[sectionStatusByKey],
);
const pendingChangesPreview = useMemo<SaveAllPreviewItem[]>(() => {
if (!config || !fullSchema) return [];
@ -734,7 +741,13 @@ export default function Settings() {
);
const handleSaveAll = useCallback(async () => {
if (!config || !fullSchema || !hasPendingChanges) return;
if (
!config ||
!fullSchema ||
!hasPendingChanges ||
hasPendingValidationErrors
)
return;
setIsSavingAll(true);
let successCount = 0;
@ -812,6 +825,7 @@ export default function Settings() {
updated[menuKey] = {
...updated[menuKey],
hasChanges: false,
hasValidationErrors: false,
};
}
}
@ -865,6 +879,7 @@ export default function Settings() {
config,
fullSchema,
hasPendingChanges,
hasPendingValidationErrors,
pendingDataBySection,
pendingKeyToMenuKey,
t,
@ -885,6 +900,7 @@ export default function Settings() {
updated[menuKey] = {
...updated[menuKey],
hasChanges: false,
hasValidationErrors: false,
};
}
}
@ -1011,7 +1027,9 @@ export default function Settings() {
useEffect(() => {
if (!selectedCamera || !cameraOverrides) return;
const overrideMap: Partial<Record<SettingsType, SectionStatus>> = {};
const overrideMap: Partial<
Record<SettingsType, Pick<SectionStatus, "hasChanges" | "isOverridden">>
> = {};
// Set override status for all camera sections using the shared mapping
Object.entries(CAMERA_SECTION_MAPPING).forEach(
@ -1031,7 +1049,12 @@ export default function Settings() {
// Merge and update both hasChanges and isOverridden for camera sections
const merged = { ...prev };
Object.entries(overrideMap).forEach(([key, status]) => {
merged[key as SettingsType] = status;
const existingStatus = merged[key as SettingsType];
merged[key as SettingsType] = {
hasChanges: status.hasChanges,
isOverridden: status.isOverridden,
hasValidationErrors: existingStatus?.hasValidationErrors ?? false,
};
});
return merged;
});
@ -1164,7 +1187,7 @@ export default function Settings() {
onClick={handleSaveAll}
variant="select"
size="sm"
disabled={isSavingAll}
disabled={isSavingAll || hasPendingValidationErrors}
className="flex w-full items-center justify-center gap-2"
>
{isSavingAll ? (
@ -1306,7 +1329,7 @@ export default function Settings() {
variant="select"
size="sm"
onClick={handleSaveAll}
disabled={isSavingAll}
disabled={isSavingAll || hasPendingValidationErrors}
className="flex items-center justify-center gap-2"
>
{isSavingAll ? (

View File

@ -31,6 +31,7 @@ export type SettingsPageProps = {
export type SectionStatus = {
hasChanges: boolean;
isOverridden: boolean;
hasValidationErrors: boolean;
};
export type SingleSectionPageOptions = {
@ -67,6 +68,7 @@ export function SingleSectionPage({
const [sectionStatus, setSectionStatus] = useState<SectionStatus>({
hasChanges: false,
isOverridden: false,
hasValidationErrors: false,
});
const resolvedSectionConfig = useMemo(
() => sectionConfig ?? getSectionConfig(sectionKey, level),