diff --git a/web/src/assets/styles/blocks.scss b/web/src/assets/styles/blocks.scss
index 30e95e4aa4..85fb4643bf 100644
--- a/web/src/assets/styles/blocks.scss
+++ b/web/src/assets/styles/blocks.scss
@@ -482,6 +482,34 @@ ul[data-type="agama/list"][role="grid"] {
}
}
+table[data-type="agama/tree-table"] {
+ tr.dimmed-row {
+ background-color: #fff;
+ opacity: 0.8;
+ background: repeating-linear-gradient( -45deg, #fcfcff, #fcfcff 3px, #fff 3px, #fff 10px );
+
+ td {
+ color: var(--color-gray-dimmed);
+ padding-block: 0;
+ }
+ }
+
+ @media (width > 768px) {
+ th.text-end,
+ td.text-end {
+ text-align: end;
+ padding-inline-end: var(--spacer-large);
+ }
+ }
+
+ @media (width <= 768px) {
+ td:empty {
+ display: none;
+ }
+ }
+}
+
+
.tpm-hint {
container-type: inline-size;
container-name: tpm-info;
diff --git a/web/src/components/core/TreeTable.jsx b/web/src/components/core/TreeTable.jsx
index dca2e866b9..8b766ad18f 100644
--- a/web/src/components/core/TreeTable.jsx
+++ b/web/src/components/core/TreeTable.jsx
@@ -36,7 +36,10 @@ export default function TreeTable({
}) {
const renderColumns = (item, treeRow) => {
return columns.map((c, cIdx) => {
- const props = { dataLabel: c.title };
+ const props = {
+ dataLabel: c.title,
+ className: c.classNames
+ };
if (cIdx === 0) props.treeRow = treeRow;
@@ -85,10 +88,10 @@ export default function TreeTable({
};
return (
-
+
- { columns.map((c, i) => {c.title} | ) }
+ { columns.map((c, i) => {c.title} | ) }
diff --git a/web/src/components/storage/ProposalActionsSection.jsx b/web/src/components/storage/ProposalActionsSection.jsx
index b4baee4413..0b038dd5a5 100644
--- a/web/src/components/storage/ProposalActionsSection.jsx
+++ b/web/src/components/storage/ProposalActionsSection.jsx
@@ -30,7 +30,7 @@ import { sprintf } from "sprintf-js";
import { _, n_ } from "~/i18n";
import { deviceSize } from "~/components/storage/utils";
-import { If, Section } from "~/components/core";
+import { If, Section, TreeTable } from "~/components/core";
import { partition } from "~/utils";
// TODO: would be nice adding an aria-description to these lists, but aria-description still in
@@ -105,130 +105,93 @@ const ActionsSkeleton = () => {
);
};
-class DevicesManager {
- constructor(system, staging) {
- this.system = system;
- this.staging = staging;
- }
+const DeleteActions = ({ actions }) => {
+ const deleteActions = actions.filter(a => a.delete);
- systemDevice(sid) {
- return this.system.find(d => d.sid === sid);
- }
-
- device(sid) {
- return this.staging.find(d => d.sid === sid);
- }
-
- lvmVgsWithMountPoints() {
- const vgs = this.staging.filter(d => d.type === "lvmVg");
- return vgs.filter(v => v.logicalVolumes.find(l => l.filesystem?.mountPath !== undefined));
- }
-
- children(device) {
- if (device.partitionTable) return this.partitionTableChildren(device.partitionTable);
- if (device.type === "lvmVg") return this.lvmVgChildren(device);
- return [];
- }
-
- partitionTableChildren(partitionTable) {
- const { partitions, unusedSlots } = partitionTable;
- const children = partitions.concat(unusedSlots);
-
- return children.sort((a, b) => a.start < b.start ? -1 : 1);
- }
-
- lvmVgChildren(lvmVg) {
- return lvmVg.logicalVolumes.sort((a, b) => a.name < b.name ? -1 : 1);
- }
-
- isSlot(storageElement) {
- return storageElement.sid === undefined;
- }
-}
-
-class SlotPresenter {
- constructor(slot) {
- this.slot = slot;
- }
-
- name() {
- return "";
- }
-
- details() {
- return "Unused space";
- }
-
- size() {
- return deviceSize(this.slot.size);
- }
-
- mountPoint() {
- return "";
- }
-}
-
-class DevicePresenter {
- constructor(device, devicesManager) {
- this.device = device;
- this.devicesManager = devicesManager;
- }
-
- name() {
- return this.device.name;
- }
-
- details() {
- return this.device.description;
- }
-
- size() {
- return deviceSize(this.device.size);
- }
-
- mountPoint() {
- return this.device.filesystem?.mountPath || "";
- }
-}
-
-const DeviceResult = ({ presenter }) => {
+ // TODO Add show the deleted system if any
return (
- - {presenter.name()}
-
- - {presenter.details()}
- - {presenter.size()}
- - {presenter.mountPoint()}
-
+ {deleteActions.map((a, i) => - {a.text}
)}
);
};
const DevicesResult = ({ settings, devices }) => {
const { system = [], staging = [] } = devices;
- const devicesManager = new DevicesManager(system, staging);
const usedDevices = () => {
- const diskDevices = settings.installationDevices.map(d => devicesManager.device(d.sid));
- const lvmVgs = devicesManager.lvmVgsWithMountPoints();
+ const sids = settings.installationDevices.map(d => d.sid);
+ const instDevices = staging.filter(d => sids.includes(d.sid));
+ const lvmVgs = staging.filter(d => d.logicalVolumes?.find(l => l.filesystem?.mountPath !== undefined));
+
+ // FIXME This is only to avoid showing the existing VG of my VM. Remove and use lvmVgs.
+ const newLvmVgs = lvmVgs.filter(v => system.find(d => d.sid === v.sid) === undefined);
+
+ return instDevices.concat(newLvmVgs);
+ };
+
+ const children = (device) => {
+ const partitionTableChildren = (partitionTable) => {
+ const { partitions, unusedSlots } = partitionTable;
+ const children = partitions.concat(unusedSlots);
+
+ return children.sort((a, b) => a.start < b.start ? -1 : 1);
+ };
- return diskDevices.concat(lvmVgs);
+ const lvmVgChildren = (lvmVg) => {
+ return lvmVg.logicalVolumes.sort((a, b) => a.name < b.name ? -1 : 1);
+ };
+
+ if (device.partitionTable) return partitionTableChildren(device.partitionTable);
+ if (device.type === "lvmVg") return lvmVgChildren(device);
+ return [];
};
- const presenter = (storageElement) => {
- if (devicesManager.isSlot(storageElement))
- return new SlotPresenter(storageElement);
- else
- return new DevicePresenter(storageElement, devicesManager);
+ const renderDeviceName = (item) => {
+ const newLabel = (device) => {
+ const systemDevice = system.find(d => d.sid === device.sid);
+ if (!systemDevice) return _("(new)");
+ };
+
+ return (
+
+ {item.sid && item.name}
+ {newLabel(item)}
+
+ );
};
- return usedDevices().map(device => {
- const devices = [device].concat(devicesManager.children(device));
+ const renderDetails = (item) => {
+ if (!item.sid) return _("Unused space");
- return devices.map((d, i) => {
- return ;
- });
- });
+ return (
+
+ {/* {newLabel(item)} */}
+ {item.description}
+ {item.filesystem?.label}
+
+ );
+ };
+
+ const renderSize = (item) => deviceSize(item.size);
+
+ const renderMountPoint = (item) => item.sid && item.filesystem?.mountPath;
+
+ return (
+ {
+ if (!item.sid) return "dimmed-row";
+ }}
+ />
+ );
};
/**
@@ -267,6 +230,7 @@ export default function ProposalActionsSection({
then={}
else={
<>
+
>