Skip to content

Commit

Permalink
web: Temporarily simplify storage page
Browse files Browse the repository at this point in the history
  • Loading branch information
ancorgs committed Oct 30, 2024
1 parent 144a2d5 commit 0d418d8
Show file tree
Hide file tree
Showing 3 changed files with 18 additions and 198 deletions.
96 changes: 13 additions & 83 deletions web/src/components/storage/ProposalActionsSummary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,40 +22,25 @@

import React from "react";
import { Button, Skeleton, Stack, List, ListItem } from "@patternfly/react-core";
import { Link, Page } from "~/components/core";
import { Page } from "~/components/core";
import DevicesManager from "~/components/storage/DevicesManager";
import { _, n_ } from "~/i18n";
import { sprintf } from "sprintf-js";
import textStyles from "@patternfly/react-styles/css/utilities/Text/text";
import { PATHS } from "~/routes/storage";
import { Action, SpaceAction, StorageDevice } from "~/types/storage";
import { SpacePolicy } from "./utils";
import { Action, StorageDevice } from "~/types/storage";
import { ValidationError } from "~/types/issues";

/**
* Renders information about delete actions
*/
const DeletionsInfo = ({
policy,
manager,
spaceActions,
}: {
policy: SpacePolicy | undefined;
manager: DevicesManager;
spaceActions: SpaceAction[];
}) => {
const DeletionsInfo = ({ manager }: { manager: DevicesManager }) => {
let label: React.ReactNode;
let systemsLabel: React.ReactNode;
const systems = manager.deletedSystems();
const deleteActions = manager.actions.filter((a) => a.delete && !a.subvol).length;
const isDeletePolicy = policy?.id === "delete";
const hasDeleteActions = deleteActions !== 0;

if (!isDeletePolicy && spaceActions.length === 0) {
label = _("Destructive actions are not allowed");
} else if ((isDeletePolicy || spaceActions.length > 0) && !hasDeleteActions) {
label = _("Destructive actions are allowed");
} else if (hasDeleteActions) {
if (hasDeleteActions) {
// TRANSLATORS: %d will be replaced by the amount of destructive actions
label = (
<strong className={textStyles.warningColor_200}>
Expand All @@ -69,6 +54,8 @@ const DeletionsInfo = ({
)}
</strong>
);
} else {
label = _("No destructive actions are planned");
}

if (systems.length) {
Expand All @@ -92,37 +79,20 @@ const DeletionsInfo = ({
/**
* Renders information about resize actions
*/
const ResizesInfo = ({
policy,
manager,
validProposal,
spaceActions,
}: {
policy: SpacePolicy | undefined;
manager: DevicesManager;
validProposal: boolean;
spaceActions: SpaceAction[];
}) => {
const ResizesInfo = ({ manager }: { manager: DevicesManager }) => {
let label: React.ReactNode;
let systemsLabel: React.ReactNode;
const systems = manager.resizedSystems();
const resizeActions = manager.actions.filter((a) => a.resize).length;
const isResizePolicy = policy?.id === "resize";
const hasResizeActions = resizeActions !== 0;

if (!isResizePolicy && spaceActions.length === 0) {
label = _("Shrinking partitions is not allowed");
}

if (!validProposal && (isResizePolicy || spaceActions.length > 0)) {
label = _("Shrinking partitions is allowed");
} else if (validProposal && (isResizePolicy || spaceActions.length > 0) && !hasResizeActions) {
label = _("Shrinking some partitions is allowed but not needed");
} else if (hasResizeActions) {
if (hasResizeActions) {
label = sprintf(
n_("%d partition will be shrunk", "%d partitions will be shrunk", resizeActions),
resizeActions,
);
} else {
label = _("No partitions wil be shrunk");
}

if (systems.length) {
Expand Down Expand Up @@ -194,11 +164,9 @@ const ActionsSkeleton = () => (
export type ProposalActionsSummaryProps = {
isLoading: boolean;
errors: ValidationError[];
policy: SpacePolicy | undefined;
system: StorageDevice[];
staging: StorageDevice[];
actions: Action[];
spaceActions: SpaceAction[];
devices: StorageDevice[];
onActionsClick: () => void | undefined;
};
Expand All @@ -212,59 +180,21 @@ export type ProposalActionsSummaryProps = {
export default function ProposalActionsSummary({
isLoading,
errors = [],
policy,
system = [],
staging = [],
actions = [],
spaceActions = [],
devices,
onActionsClick,
}: ProposalActionsSummaryProps) {
let value: React.ReactNode;

if (isLoading || !policy) {
value = <Skeleton fontSize="sm" width="65%" />;
} else if (policy.summaryLabels.length === 1) {
// eslint-disable-next-line agama-i18n/string-literals
value = _(policy.summaryLabels[0]);
} else {
value = sprintf(
// eslint-disable-next-line agama-i18n/string-literals
n_(policy.summaryLabels[0], policy.summaryLabels[1], devices.length),
devices.length,
);
}

const devicesManager = new DevicesManager(system, staging, actions);

return (
<Page.Section
title={_("Actions")}
value={value}
actions={
isLoading ? (
<Skeleton fontSize="sm" width="100px" />
) : (
<Link to={PATHS.spacePolicy}>{_("Change")}</Link>
)
}
pfCardProps={{ isFullHeight: false }}
>
<Page.Section title={_("Actions")} pfCardProps={{ isFullHeight: false }}>
{isLoading ? (
<ActionsSkeleton />
) : (
<List>
<DeletionsInfo
policy={policy}
manager={devicesManager}
spaceActions={spaceActions.filter((a) => a.action === "force_delete")}
/>
<ResizesInfo
policy={policy}
manager={devicesManager}
validProposal={errors.length === 0}
spaceActions={spaceActions.filter((a) => a.action === "resize")}
/>
<DeletionsInfo manager={devicesManager} />
<ResizesInfo manager={devicesManager} />
<ActionsInfo
actions={actions}
validProposal={errors.length === 0}
Expand Down
45 changes: 2 additions & 43 deletions web/src/components/storage/ProposalPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,26 +23,14 @@
import React, { useRef } from "react";
import { Grid, GridItem, Stack } from "@patternfly/react-core";
import { Page, Drawer } from "~/components/core/";
import ProposalTransactionalInfo from "./ProposalTransactionalInfo";
import ProposalSettingsSection from "./ProposalSettingsSection";
import ProposalResultSection from "./ProposalResultSection";
import ProposalActionsSummary from "~/components/storage/ProposalActionsSummary";
import { ProposalActionsDialog } from "~/components/storage";
import { _ } from "~/i18n";
import { SPACE_POLICIES } from "~/components/storage/utils";
import { toValidationError } from "~/utils";
import { useIssues } from "~/queries/issues";
import { IssueSeverity } from "~/types/issues";
import {
useAvailableDevices,
useDeprecated,
useDevices,
useProductParams,
useProposalMutation,
useProposalResult,
useVolumeDevices,
useVolumeTemplates,
} from "~/queries/storage";
import { useDeprecated, useDevices, useProposalResult } from "~/queries/storage";
import { useQueryClient } from "@tanstack/react-query";
import { refresh } from "~/api/storage";

Expand Down Expand Up @@ -74,12 +62,7 @@ export default function ProposalPage() {
const drawerRef = useRef();
const systemDevices = useDevices("system");
const stagingDevices = useDevices("result");
const availableDevices = useAvailableDevices();
const volumeDevices = useVolumeDevices();
const volumeTemplates = useVolumeTemplates();
const { encryptionMethods } = useProductParams({ suspense: true });
const { actions, settings } = useProposalResult();
const updateProposal = useProposalMutation();
const { actions } = useProposalResult();
const deprecated = useDeprecated();
const queryClient = useQueryClient();

Expand All @@ -95,13 +78,6 @@ export default function ProposalPage() {
.filter((s) => s.severity === IssueSeverity.Error)
.map(toValidationError);

const changeSettings = async (changing, updated: object) => {
const newSettings = { ...settings, ...updated };
updateProposal.mutateAsync(newSettings).catch(console.error);
};

const spacePolicy = SPACE_POLICIES.find((p) => p.id === settings.spacePolicy);

return (
<Page>
<Page.Header>
Expand All @@ -111,34 +87,17 @@ export default function ProposalPage() {
<Page.Content>
<Grid hasGutter>
<GridItem sm={12}>
<ProposalTransactionalInfo settings={settings} />
</GridItem>
<GridItem sm={12} xl={6}>
<ProposalSettingsSection
availableDevices={availableDevices}
volumeDevices={volumeDevices}
encryptionMethods={encryptionMethods}
volumeTemplates={volumeTemplates}
settings={settings}
onChange={changeSettings}
isLoading={false}
/>
</GridItem>
<GridItem sm={12} xl={6}>
<Drawer
ref={drawerRef}
panelHeader={<h4>{_("Planned Actions")}</h4>}
panelContent={<ProposalActionsDialog actions={actions} />}
>
<Stack hasGutter>
<ProposalActionsSummary
policy={spacePolicy}
system={systemDevices}
staging={stagingDevices}
errors={errors}
actions={actions}
spaceActions={settings.spaceActions}
devices={settings.installationDevices}
// @ts-expect-error: we do not know how to specify the type of
// drawerRef properly and TS does not find the "open" property
onActionsClick={drawerRef.current?.open}
Expand Down
75 changes: 3 additions & 72 deletions web/src/queries/storage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,22 +34,13 @@ import {
fetchActions,
fetchDefaultVolume,
fetchProductParams,
fetchSettings,
fetchUsableDevices,
} from "~/api/storage/proposal";
import { useInstallerClient } from "~/context/installer";
import { compact, uniq } from "~/utils";
import {
ProductParams,
Volume as APIVolume,
ProposalSettings as APIProposalSettings,
ProposalTarget as APIProposalTarget,
ProposalSettingsPatch,
} from "~/api/storage/types";
import { ProductParams, Volume as APIVolume, ProposalSettingsPatch } from "~/api/storage/types";
import {
ProposalSettings,
ProposalResult,
ProposalTarget,
StorageDevice,
Volume,
VolumeTarget,
Expand Down Expand Up @@ -200,11 +191,6 @@ const useVolumeDevices = (): StorageDevice[] => {
return [...availableDevices, ...mds, ...vgs];
};

const proposalSettingsQuery = {
queryKey: ["storage", "proposal", "settings"],
queryFn: fetchSettings,
};

const proposalActionsQuery = {
queryKey: ["storage", "devices", "actions"],
queryFn: fetchActions,
Expand All @@ -214,64 +200,9 @@ const proposalActionsQuery = {
* Hook that returns the current proposal (settings and actions).
*/
const useProposalResult = (): ProposalResult | undefined => {
const buildTarget = (value: APIProposalTarget): ProposalTarget => {
// FIXME: handle the case where they do not match
const target = value as ProposalTarget;
return target;
};

/** @todo Read installation devices from D-Bus. */
const buildInstallationDevices = (settings: APIProposalSettings, devices: StorageDevice[]) => {
const findDevice = (name: string) => {
const device = devices.find((d) => d.name === name);

if (device === undefined) console.error("Device object not found: ", name);

return device;
};

// Only consider the device assigned to a volume as installation device if it is needed
// to find space in that device. For example, devices directly formatted or mounted are not
// considered as installation devices.
const volumes = settings.volumes.filter((vol) => {
const target = vol.target as VolumeTarget;
return [VolumeTarget.NEW_PARTITION, VolumeTarget.NEW_VG].includes(target);
});
const { data: actions } = useSuspenseQuery(proposalActionsQuery);

const values = [
settings.targetDevice,
settings.targetPVDevices,
volumes.map((v) => v.targetDevice),
].flat();

if (settings.configureBoot) values.push(settings.bootDevice);

const names = uniq(compact(values)).filter((d) => d.length > 0);

// #findDevice returns undefined if no device is found with the given name.
return compact(names.sort().map(findDevice));
};

const [{ data: settings }, { data: actions }] = useSuspenseQueries({
queries: [proposalSettingsQuery, proposalActionsQuery],
});
const systemDevices = useDevices("system", { suspense: true });
const { mountPoints: productMountPoints } = useProductParams({ suspense: true });

return {
settings: {
...settings,
targetPVDevices: settings.targetPVDevices || [],
target: buildTarget(settings.target),
volumes: settings.volumes.map((v) => buildVolume(v, systemDevices, productMountPoints)),
// NOTE: strictly speaking, installation devices does not belong to the settings. It
// should be a separate method instead of an attribute in the settings object.
// Nevertheless, it was added here for simplicity and to avoid passing more props in some
// react components. Please, do not use settings as a jumble.
installationDevices: buildInstallationDevices(settings, systemDevices),
},
actions,
};
return { actions };
};

const useProposalMutation = () => {
Expand Down

0 comments on commit 0d418d8

Please sign in to comment.