diff --git a/web/package/agama-web-ui.changes b/web/package/agama-web-ui.changes index f330f2b9e1..cf4e2281be 100644 --- a/web/package/agama-web-ui.changes +++ b/web/package/agama-web-ui.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Fri Jan 10 13:22:27 UTC 2025 - Imobach Gonzalez Sosa + +- Do not allow changing the storage setup when Agama is using the + new storage settings (gh#agama-project/agama#1881). + ------------------------------------------------------------------- Wed Jan 8 16:07:13 UTC 2025 - Imobach Gonzalez Sosa diff --git a/web/src/api/storage/proposal.ts b/web/src/api/storage/proposal.ts index 3a5335beb6..59b2b94182 100644 --- a/web/src/api/storage/proposal.ts +++ b/web/src/api/storage/proposal.ts @@ -38,9 +38,12 @@ const fetchDefaultVolume = (mountPath: string): Promise => { return get(`/api/storage/product/volume_for?mount_path=${path}`); }; -const fetchSettings = (): Promise => get("/api/storage/proposal/settings"); +// NOTE: the settings might not exist. +const fetchSettings = (): Promise => + get("/api/storage/proposal/settings").catch(() => null); -const fetchActions = (): Promise => get("/api/storage/proposal/actions"); +// NOTE: the actions might not exist. +const fetchActions = (): Promise => get("/api/storage/proposal/actions").catch(() => []); const calculate = (settings: ProposalSettingsPatch) => put("/api/storage/proposal/settings", settings); diff --git a/web/src/components/overview/StorageSection.test.tsx b/web/src/components/overview/StorageSection.test.tsx index 742ed461c5..75484333c3 100644 --- a/web/src/components/overview/StorageSection.test.tsx +++ b/web/src/components/overview/StorageSection.test.tsx @@ -31,15 +31,30 @@ const mockAvailableDevices = [ ]; const mockResultSettings = { target: "disk", targetDevice: "/dev/sda", spacePolicy: "delete" }; +let mockProposalResult; jest.mock("~/queries/storage", () => ({ ...jest.requireActual("~/queries/storage"), useAvailableDevices: () => mockAvailableDevices, - useProposalResult: () => ({ + useProposalResult: () => mockProposalResult, +})); + +beforeEach(() => { + mockProposalResult = { settings: mockResultSettings, actions: [], - }), -})); + }; +}); + +describe("when using the new storage settings", () => { + beforeEach(() => (mockProposalResult = undefined)); + + it("renders a warning message", () => { + plainRender(); + + expect(screen.getByText("Install using an advanced configuration.")).toBeInTheDocument(); + }); +}); describe("when there is a proposal", () => { beforeEach(() => { diff --git a/web/src/components/overview/StorageSection.tsx b/web/src/components/overview/StorageSection.tsx index 3e514a03ba..a4f4526963 100644 --- a/web/src/components/overview/StorageSection.tsx +++ b/web/src/components/overview/StorageSection.tsx @@ -115,7 +115,13 @@ export default function StorageSection() { const availableDevices = useAvailableDevices(); const result = useProposalResult(); - if (result === undefined) return; + if (result === undefined) { + return ( + + {_("Install using an advanced configuration.")} + + ); + } const label = (deviceName) => { const device = availableDevices.find((d) => d.name === deviceName); diff --git a/web/src/components/storage/BootSelection.tsx b/web/src/components/storage/BootSelection.tsx index 27fc49fa29..9a1bcc71e8 100644 --- a/web/src/components/storage/BootSelection.tsx +++ b/web/src/components/storage/BootSelection.tsx @@ -53,16 +53,16 @@ export default function BootSelectionDialog() { }; const [state, setState] = useState({ load: false }); - const { settings } = useProposalResult(); + const proposal = useProposalResult(); const availableDevices = useAvailableDevices(); const updateProposal = useProposalMutation(); const navigate = useNavigate(); useEffect(() => { - if (state.load) return; + if (state.load || !proposal.settings) return; let selectedOption: string; - const { bootDevice, configureBoot, defaultBootDevice } = settings; + const { bootDevice, configureBoot, defaultBootDevice } = proposal.settings; if (!configureBoot) { selectedOption = BOOT_DISABLED_ID; @@ -82,7 +82,7 @@ export default function BootSelectionDialog() { availableDevices, selectedOption, }); - }, [availableDevices, settings, state.load]); + }, [availableDevices, proposal, state.load]); if (!state.load) return; @@ -97,7 +97,7 @@ export default function BootSelectionDialog() { bootDevice: state.selectedOption === BOOT_MANUAL_ID ? state.bootDevice.name : undefined, }; - await updateProposal.mutateAsync({ ...settings, ...newSettings }); + await updateProposal.mutateAsync({ ...proposal.settings, ...newSettings }); navigate(".."); }; diff --git a/web/src/components/storage/DeviceSelection.tsx b/web/src/components/storage/DeviceSelection.tsx index 434880e177..5fc87aae9e 100644 --- a/web/src/components/storage/DeviceSelection.tsx +++ b/web/src/components/storage/DeviceSelection.tsx @@ -49,7 +49,7 @@ type DeviceSelectionState = { * @component */ export default function DeviceSelection() { - const { settings } = useProposalResult(); + const proposal = useProposalResult(); const availableDevices = useAvailableDevices(); const updateProposal = useProposalMutation(); const navigate = useNavigate(); @@ -63,11 +63,13 @@ export default function DeviceSelection() { // FIXME: move to a state/reducer setState({ - target: settings.target, - targetDevice: availableDevices.find((d) => d.name === settings.targetDevice), - targetPVDevices: availableDevices.filter((d) => settings.targetPVDevices?.includes(d.name)), + target: proposal.settings.target, + targetDevice: availableDevices.find((d) => d.name === proposal.settings.targetDevice), + targetPVDevices: availableDevices.filter((d) => + proposal.settings.targetPVDevices?.includes(d.name), + ), }); - }, [settings, availableDevices, state.target]); + }, [proposal, availableDevices, state.target]); const selectTargetDisk = () => setState({ ...state, target: ProposalTarget.DISK }); const selectTargetNewLvmVG = () => setState({ ...state, target: ProposalTarget.NEW_LVM_VG }); @@ -86,7 +88,7 @@ export default function DeviceSelection() { targetPVDevices: isTargetNewLvmVg ? state.targetPVDevices.map((d) => d.name) : [], }; - updateProposal.mutateAsync({ ...settings, ...newSettings }); + updateProposal.mutateAsync({ ...proposal.settings, ...newSettings }); navigate(".."); }; diff --git a/web/src/components/storage/ProposalPage.tsx b/web/src/components/storage/ProposalPage.tsx index 0972c721e8..245b16b064 100644 --- a/web/src/components/storage/ProposalPage.tsx +++ b/web/src/components/storage/ProposalPage.tsx @@ -22,7 +22,7 @@ import React, { useRef } from "react"; import { Grid, GridItem, Stack } from "@patternfly/react-core"; -import { Page, Drawer } from "~/components/core/"; +import { Page, Drawer, EmptyState } from "~/components/core/"; import ProposalTransactionalInfo from "./ProposalTransactionalInfo"; import ProposalSettingsSection from "./ProposalSettingsSection"; import ProposalResultSection from "./ProposalResultSection"; @@ -46,6 +46,22 @@ import { import { useQueryClient } from "@tanstack/react-query"; import { refresh } from "~/api/storage"; +const StorageWarning = () => ( + + +

{_("Storage")}

+
+ + + +
+); + /** * Which UI item is being changed by user */ @@ -78,7 +94,7 @@ export default function ProposalPage() { const volumeDevices = useVolumeDevices(); const volumeTemplates = useVolumeTemplates(); const { encryptionMethods } = useProductParams({ suspense: true }); - const { actions, settings } = useProposalResult(); + const proposal = useProposalResult(); const updateProposal = useProposalMutation(); const deprecated = useDeprecated(); const queryClient = useQueryClient(); @@ -95,6 +111,10 @@ export default function ProposalPage() { .filter((s) => s.severity === IssueSeverity.Error) .map(toValidationError); + if (proposal === undefined) return ; + + const { settings, actions } = proposal; + const changeSettings = async (changing, updated: object) => { const newSettings = { ...settings, ...updated }; updateProposal.mutateAsync(newSettings).catch(console.error); diff --git a/web/src/queries/storage.ts b/web/src/queries/storage.ts index d70e6b053b..d80e3079b6 100644 --- a/web/src/queries/storage.ts +++ b/web/src/queries/storage.ts @@ -269,6 +269,8 @@ const useProposalResult = (): ProposalResult | undefined => { const systemDevices = useDevices("system", { suspense: true }); const { mountPoints: productMountPoints } = useProductParams({ suspense: true }); + if (settings === null) return undefined; + return { settings: { ...settings,