diff --git a/service/lib/dinstaller/dbus/storage/proposal.rb b/service/lib/dinstaller/dbus/storage/proposal.rb index a0713d50e9..12546bf47e 100644 --- a/service/lib/dinstaller/dbus/storage/proposal.rb +++ b/service/lib/dinstaller/dbus/storage/proposal.rb @@ -170,7 +170,7 @@ def register_callbacks SETTINGS_CONVERSIONS = { "CandidateDevices" => ["candidate_devices=", proc { |v| v }], "LVM" => ["lvm=", proc { |v| v }], - "EncryptionPassword" => ["encryption_password=", proc { |v| v }], + "EncryptionPassword" => ["encryption_password=", proc { |v| v.empty? ? nil : v }], "Volumes" => ["volumes=", proc { |v, o| o.send(:to_proposal_volumes, v) }] }.freeze private_constant :SETTINGS_CONVERSIONS diff --git a/web/src/StorageActions.jsx b/web/src/StorageActions.jsx index a2d21276ef..753fb1616a 100644 --- a/web/src/StorageActions.jsx +++ b/web/src/StorageActions.jsx @@ -22,14 +22,19 @@ import React, { useState } from "react"; import { List, ListItem, ExpandableSection } from "@patternfly/react-core"; -const renderActionsList = actions => { - const items = actions.map((a, i) => { +// Some actions (e.g., deleting a LV) are reported as several actions joined by a line break +const actionItems = (action, id) => { + return action.text.split("\n").map((text, index) => { return ( - - {a.text} + + {text} ); }); +}; + +const renderActionsList = actions => { + const items = actions.map(actionItems).flat(); return {items}; }; diff --git a/web/src/StorageActionsSection.jsx b/web/src/StorageActionsSection.jsx new file mode 100644 index 0000000000..425a673546 --- /dev/null +++ b/web/src/StorageActionsSection.jsx @@ -0,0 +1,35 @@ +/* + * Copyright (c) [2022] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact SUSE LLC. + * + * To contact SUSE LLC about this file by physical or electronic mail, you may + * find current contact information at www.suse.com. + */ + +import React from "react"; + +import { EOS_VOLUME as Icon } from "eos-icons-react"; + +import Category from "./Category"; +import StorageActions from "./StorageActions"; + +export default function StorageSettingsSection({ proposal }) { + return ( + + + + ); +} diff --git a/web/src/StoragePage.jsx b/web/src/StoragePage.jsx index 0878379347..8c27e577c9 100644 --- a/web/src/StoragePage.jsx +++ b/web/src/StoragePage.jsx @@ -24,18 +24,20 @@ import { useNavigate } from "react-router-dom"; import { Button, + Flex, + FlexItem, Text, TextVariants } from "@patternfly/react-core"; -import { EOS_PRODUCT_SUBSCRIPTIONS as Icon } from "eos-icons-react"; +import { EOS_VOLUME as Icon } from "eos-icons-react"; import { useInstallerClient } from "./context/installer"; import { useCancellablePromise } from "./utils"; import { Title, PageIcon, MainActions } from "./Layout"; -import StorageActions from "./StorageActions"; -import DeviceSelector from "./DeviceSelector"; -import StorageVolumes from "./StorageVolumes"; +import StorageTargetSection from "./StorageTargetSection"; +import StorageSettingsSection from "./StorageSettingsSection"; +import StorageActionsSection from "./StorageActionsSection"; import InstallerSkeleton from "./InstallerSkeleton"; const reducer = (state, action) => { @@ -88,16 +90,6 @@ export default function StoragePage() { dispatch({ type: "CALCULATE" }); }; - const onCandidateDevicesChange = candidateDevice => { - calculateProposal({ candidateDevices: [candidateDevice] }); - }; - - const onVolumesChange = volumes => { - calculateProposal({ volumes }); - }; - - const proposal = state.proposal; - const content = () => { if (state.busy) return ; @@ -107,17 +99,19 @@ export default function StoragePage() { ); + const categories = [ + , + , + , + ]; + return ( - <> - - - - + + {categories.map((category, i) => ( + + {category} + ))} + ); }; diff --git a/web/src/StorageSettingsForm.jsx b/web/src/StorageSettingsForm.jsx new file mode 100644 index 0000000000..cb5e9e2dec --- /dev/null +++ b/web/src/StorageSettingsForm.jsx @@ -0,0 +1,111 @@ +/* + * Copyright (c) [2022] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact SUSE LLC. + * + * To contact SUSE LLC about this file by physical or electronic mail, you may + * find current contact information at www.suse.com. + */ + +import React, { useReducer } from "react"; + +import { + Form, + FormGroup, + Switch, + TextInput +} from "@patternfly/react-core"; + +const reducer = (state, action) => { + switch (action.type) { + case "LVM_CHANGE" : { + return { ...state, lvm: action.payload.lvm }; + } + + case "ENCRYPTION_CHANGE": { + return { ...state, encryption: action.payload.encryption }; + } + + case "PASSWORD_CHANGE": { + return { ...state, encryptionPassword: action.payload.encryptionPassword }; + } + + default: { + return state; + } + } +}; + +export default function StorageSettingsForm({ id, proposal, onSubmit }) { + const [state, dispatch] = useReducer(reducer, { + lvm: proposal.lvm, + encryption: proposal.encryptionPassword.length !== 0, + encryptionPassword: proposal.encryptionPassword + }); + + const onLvmChange = (value) => { + dispatch({ type: "LVM_CHANGE", payload: { lvm: value } }); + }; + + const onEncryptionChange = (value) => { + dispatch({ type: "ENCRYPTION_CHANGE", payload: { encryption: value } }); + }; + + const onPasswordChange = (value) => { + dispatch({ type: "PASSWORD_CHANGE", payload: { encryptionPassword: value } }); + }; + + const accept = (e) => { + e.preventDefault(); + + const lvm = state.lvm; + const encryptionPassword = state.encryption ? state.encryptionPassword : ""; + + onSubmit({ lvm, encryptionPassword }); + }; + + return ( +
+ +
+ + + + + + +
+ + ); +} diff --git a/web/src/StorageSettingsSection.jsx b/web/src/StorageSettingsSection.jsx new file mode 100644 index 0000000000..8002d34fad --- /dev/null +++ b/web/src/StorageSettingsSection.jsx @@ -0,0 +1,68 @@ +/* + * Copyright (c) [2022] SUSE LLC + * + * All Rights Reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of version 2 of the GNU General Public License as published + * by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, contact SUSE LLC. + * + * To contact SUSE LLC about this file by physical or electronic mail, you may + * find current contact information at www.suse.com. + */ + +import React, { useState } from "react"; + +import { + Button, +} from "@patternfly/react-core"; + +import { + EOS_VOLUME as SectionIcon, + EOS_SETTINGS as EditIcon +} from "eos-icons-react"; + +import Category from "./Category"; +import StorageVolumes from "./StorageVolumes"; +import StorageSettingsForm from "./StorageSettingsForm"; +import Popup from "./Popup"; + +export default function StorageSettingsSection({ proposal, calculateProposal }) { + const [isOpen, setIsOpen] = useState(false); + + const onSettingsChange = ({ lvm, encryptionPassword }) => { + calculateProposal({ lvm, encryptionPassword }); + }; + + const onVolumesChange = volumes => { + calculateProposal({ volumes }); + }; + + return ( + +