Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
joseivanlopez committed Nov 24, 2022
1 parent 30fd4a8 commit 2b70b64
Show file tree
Hide file tree
Showing 9 changed files with 367 additions and 29 deletions.
2 changes: 1 addition & 1 deletion service/lib/dinstaller/dbus/storage/proposal.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 9 additions & 4 deletions web/src/StorageActions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<ListItem key={i} className={a.delete ? "proposal-action--delete" : null}>
{a.text}
<ListItem key={`${id}-${index}`} className={action.delete ? "proposal-action--delete" : null}>
{text}
</ListItem>
);
});
};

const renderActionsList = actions => {
const items = actions.map(actionItems).flat();
return <List className="proposal-actions">{items}</List>;
};

Expand Down
35 changes: 35 additions & 0 deletions web/src/StorageActionsSection.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<Category title="Actions" icon={Icon}>
<StorageActions actions={proposal.actions} />
</Category>
);
}
42 changes: 18 additions & 24 deletions web/src/StoragePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -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 <InstallerSkeleton lines={3} />;

Expand All @@ -107,17 +99,19 @@ export default function StoragePage() {
</Text>
);

const categories = [
<StorageTargetSection key="target" proposal={state.proposal} calculateProposal={calculateProposal} />,
<StorageSettingsSection key="settings" proposal={state.proposal} calculateProposal={calculateProposal} />,
<StorageActionsSection key="actions" proposal={state.proposal} />,
];

return (
<>
<DeviceSelector
key={proposal.candidateDevices[0]}
value={proposal.candidateDevices[0]}
options={proposal.availableDevices}
onChange={onCandidateDevicesChange}
/>
<StorageVolumes volumes={proposal.volumes} onChange={onVolumesChange} />
<StorageActions actions={proposal.actions} />
</>
<Flex direction={{ default: "column" }}>
{categories.map((category, i) => (
<FlexItem key={i} className="installation-overview-section">
{category}
</FlexItem>))}
</Flex>
);
};

Expand Down
111 changes: 111 additions & 0 deletions web/src/StorageSettingsForm.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<Form id={id} onSubmit={accept}>
<Switch
id="lvm"
label="Use LVM"
isReversed
isChecked={state.lvm}
onChange={onLvmChange}
/>
<fieldset>
<legend>
<Switch
id="encryption"
label="Encrypt devices"
isReversed
isChecked={state.encryption}
onChange={onEncryptionChange}
/>
</legend>
<FormGroup fieldId="encryptionPassword" label="Encryption password">
<TextInput
id="encryptionPassword"
type="password"
isDisabled={!state.encryption}
aria-label="password"
value={state.encryptionPassword}
onChange={onPasswordChange}
/>
</FormGroup>
</fieldset>
</Form>
);
}
68 changes: 68 additions & 0 deletions web/src/StorageSettingsSection.jsx
Original file line number Diff line number Diff line change
@@ -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 (
<Category title="Settings" icon={SectionIcon}>
<Button
isSmall
variant="plain"
icon={<EditIcon />}
aria-label="settings"
onClick={() => setIsOpen(true)}
/>
<Popup title="Edit proposal settings" isOpen={isOpen}>
<StorageSettingsForm id="settings-form" proposal={proposal} onSubmit={onSettingsChange} />
<Popup.Actions>
<Popup.Confirm form="settings-form" type="submit">Accept</Popup.Confirm>
<Popup.Cancel onClick={() => setIsOpen(false)} autoFocus />
</Popup.Actions>
</Popup>
<StorageVolumes volumes={proposal.volumes} onChange={onVolumesChange} />
</Category>
);
}
48 changes: 48 additions & 0 deletions web/src/StorageTargetForm.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/*
* 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 {
Form,
} from "@patternfly/react-core";

import DeviceSelector from "./DeviceSelector";

export default function StorageTargetForm({ id, proposal, onSubmit }) {
const [candidateDevices, setCandidateDevices] = useState(proposal.candidateDevices);

const accept = (e) => {
e.preventDefault();
onSubmit({ candidateDevices });
};

return (
<Form id={id} onSubmit={accept}>
<DeviceSelector
key={candidateDevices[0]}
value={candidateDevices[0]}
options={proposal.availableDevices}
onChange={(value) => setCandidateDevices([value])}
/>
</Form>
);
}
Loading

0 comments on commit 2b70b64

Please sign in to comment.