Skip to content

Commit

Permalink
HPCC-31304 ECL Watch v9 replace DetailsList with DataGrid
Browse files Browse the repository at this point in the history
Signed-off-by: Jeremy Clements <[email protected]>
  • Loading branch information
jeclrsg committed Mar 21, 2024
1 parent 1771da6 commit 4868c62
Show file tree
Hide file tree
Showing 9 changed files with 1,349 additions and 893 deletions.
1 change: 1 addition & 0 deletions esp/src/eclwatch/css/hpcc.css
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ body {
font-family: Lucida Sans, Lucida Grande, Arial !important;
font-size: 13px !important;
padding: 0;
overflow: hidden;
}

button:hover {
Expand Down
1,285 changes: 649 additions & 636 deletions esp/src/package-lock.json

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions esp/src/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@
},
"main": "src/stub.js",
"dependencies": {
"@fluentui/react": "8.115.6",
"@fluentui/react-components": "9.46.4",
"@fluentui/react-experiments": "8.14.133",
"@fluentui/react-hooks": "8.6.36",
"@fluentui/react-icons-mdl2": "1.3.57",
"@fluentui/react-migration-v8-v9": "9.5.8",
"@fluentui/react": "8.116.0",
"@fluentui/react-components": "9.47.1",
"@fluentui/react-experiments": "8.14.135",
"@fluentui/react-hooks": "8.6.37",
"@fluentui/react-icons-mdl2": "1.3.58",
"@fluentui/react-migration-v8-v9": "9.6.2",
"@hpcc-js/chart": "2.81.10",
"@hpcc-js/codemirror": "2.61.2",
"@hpcc-js/common": "2.71.15",
Expand Down
2 changes: 1 addition & 1 deletion esp/src/src-react/components/AppPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ export const AppPanel: React.FunctionComponent<AppPanelProps> = ({
const retVal = [];
webLinks?.forEach(webLink => {
webLink.Annotations.NamedValue.forEach(nv => {
retVal.push(<DefaultButton text={`${webLink.ServiceName} - ${nv.Name}`} href={`/${nv.Value}`} target="_blank" />);
retVal.push(<DefaultButton key={`${webLink.ServiceName}_${nv.Name}`} text={`${webLink.ServiceName} - ${nv.Name}`} href={`/${nv.Value}`} target="_blank" />);
});
});
// Include HPCC Systems link when there are no other web links available
Expand Down
258 changes: 162 additions & 96 deletions esp/src/src-react/components/Files.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import * as React from "react";
import { CommandBar, ContextualMenuItemType, ICommandBarItemProps, Icon, Link } from "@fluentui/react";
import { CommandBar, ContextualMenuItemType, ICommandBarItemProps, Link } from "@fluentui/react";
import { FolderZip20Regular, LockClosedFilled } from "@fluentui/react-icons";
import { TableCellLayout, TableColumnDefinition, TableRowId, createTableColumn, Tooltip } from "@fluentui/react-components";
import { WsDfu as HPCCWsDfu } from "@hpcc-js/comms";
import { scopedLogger } from "@hpcc-js/util";
import { SizeMe } from "react-sizeme";
import * as WsDfu from "src/WsDfu";
import { CreateDFUQueryStore, Get } from "src/ESPLogicalFile";
import { CreateDFUQueryStore } from "src/ESPLogicalFile";
import { formatCost } from "src/Session";
import * as Utility from "src/Utility";
import { QuerySortItem } from "src/store/Store";
import nlsHPCC from "src/nlsHPCC";
import { useConfirm } from "../hooks/confirm";
import { useUserTheme } from "../hooks/theme";
import { useMyAccount } from "../hooks/user";
import { useHasFocus, useIsMounted } from "../hooks/util";
import { HolyGrail } from "../layouts/HolyGrail";
import { pushParams } from "../util/history";
import { FluentPagedGrid, FluentPagedFooter, useCopyButtons, useFluentStoreState, FluentColumns } from "./controls/Grid";
import { FluentPagedDataGrid, FluentPagedFooter, useCopyButtons, useFluentStoreState } from "./controls/Grid";
import { AddToSuperfile } from "./forms/AddToSuperfile";
import { CopyFile } from "./forms/CopyFile";
import { DesprayFile } from "./forms/DesprayFile";
Expand All @@ -21,10 +26,11 @@ import { Filter } from "./forms/Filter";
import { RemoteCopy } from "./forms/RemoteCopy";
import { RenameFile } from "./forms/RenameFile";
import { ShortVerticalDivider } from "./Common";
import { SizeMe } from "react-sizeme";

const logger = scopedLogger("src-react/components/Files.tsx");

type LogicalFile = HPCCWsDfu.DFULogicalFile;

const FilterFields: Fields = {
"LogicalName": { type: "string", label: nlsHPCC.Name, placeholder: nlsHPCC.somefile },
"Description": { type: "string", label: nlsHPCC.Description, placeholder: nlsHPCC.SomeDescription },
Expand Down Expand Up @@ -99,6 +105,15 @@ export const Files: React.FunctionComponent<FilesProps> = ({
store
}) => {

const { themeV9 } = useUserTheme();
const footerStyles = React.useMemo(() => {
return {
zIndex: 2,
background: themeV9.colorNeutralBackground1,
borderTop: `1px solid ${themeV9.colorNeutralStroke1}`
};
}, [themeV9]);

const hasFilter = React.useMemo(() => Object.keys(filter).length > 0, [filter]);

const [showFilter, setShowFilter] = React.useState(false);
Expand All @@ -117,6 +132,12 @@ export const Files: React.FunctionComponent<FilesProps> = ({
total, setTotal,
refreshTable } = useFluentStoreState({ page });

const [selectedRows, setSelectedRows] = React.useState(new Set<TableRowId>());
const onSelectionChange = (items, rowIds) => {
setSelectedRows(rowIds);
setSelection(items);
};

// Refresh on focus ---
const isMounted = useIsMounted();
const hasFocus = useHasFocus();
Expand All @@ -136,98 +157,140 @@ export const Files: React.FunctionComponent<FilesProps> = ({
return formatQuery(filter);
}, [filter]);

const columns = React.useMemo((): FluentColumns => {
const columnSizingOptions = React.useMemo(() => {
return {
col1: {
width: 16,
disabled: (item) => {
return item ? item.__hpcc_isDir : true;
},
selectorType: "checkbox"
},
IsProtected: {
headerIcon: "LockSolid",
headerTooltip: nlsHPCC.Protected,
width: 16,
sortable: false,
formatter: (_protected) => {
if (_protected === true) {
return <Icon iconName="LockSolid" />;
}
return "";
},
},
IsCompressed: {
headerIcon: "ZipFolder",
headerTooltip: nlsHPCC.Compressed,
width: 16,
sortable: false,
formatter: (compressed) => {
if (compressed === true) {
return <Icon iconName="ZipFolder" />;
}
return "";
},
},
Name: {
label: nlsHPCC.LogicalName,
width: 360,
formatter: (name, row) => {
const file = Get(row.NodeGroup, name, row);
if (row.__hpcc_isDir) {
return name;
}
const url = "#/files/" + (row.NodeGroup ? row.NodeGroup + "/" : "") + name;
return <>
<Icon iconName={file.getStateIcon ? file.getStateIcon() : ""} />
&nbsp;
<Link href={url}>{name}</Link>
</>;
},
},
Owner: { label: nlsHPCC.Owner },
SuperOwners: { label: nlsHPCC.SuperOwner, sortable: false },
Description: { label: nlsHPCC.Description, sortable: false },
NodeGroup: { label: nlsHPCC.Cluster },
Records: {
label: nlsHPCC.Records,
formatter: (value, row) => {
return Utility.formatNum(row.IntRecordCount);
},
},
FileSize: {
label: nlsHPCC.Size,
formatter: (value, row) => {
return Utility.convertedSize(row.IntSize);
},
},
Parts: {
label: nlsHPCC.Parts, width: 40,
},
MinSkew: {
label: nlsHPCC.MinSkew, width: 60, formatter: (value, row) => value ? `${Utility.formatDecimal(value / 100)}%` : ""
},
MaxSkew: {
label: nlsHPCC.MaxSkew, width: 60, formatter: (value, row) => value ? `${Utility.formatDecimal(value / 100)}%` : ""
},
Modified: { label: nlsHPCC.ModifiedUTCGMT },
Accessed: { label: nlsHPCC.LastAccessed },
AtRestCost: {
label: nlsHPCC.FileCostAtRest,
formatter: (cost, row) => {
return `${formatCost(cost)}`;
},
},
AccessCost: {
label: nlsHPCC.FileAccessCost,
formatter: (cost, row) => {
return `${formatCost(cost)}`;
},
}
IsProtected: { minWidth: 16, defaultWidth: 16 },
IsCompressed: { minWidth: 16, defaultWidth: 16 },
Name: { minWidth: 160, idealWidth: 360, defaultWidth: 360 },
Owner: { minWidth: 90, idealWidth: 120, defaultWidth: 120 },
SuperOwners: { minWidth: 90, idealWidth: 90, defaultWidth: 90 },
Description: { minWidth: 60, idealWidth: 80, defaultWidth: 80 },
Cluster: { minWidth: 64, defaultWidth: 64 },
Records: { minWidth: 64, defaultWidth: 64 },
Size: { minWidth: 32, defaultWidth: 64 },
Parts: { minWidth: 32, defaultWidth: 64 },
MinSkew: { minWidth: 64, defaultWidth: 64 },
MaxSkew: { minWidth: 64, defaultWidth: 64 },
Modified: { minWidth: 160, idealWidth: 160, defaultWidth: 160 },
Accessed: { minWidth: 130, idealWidth: 130, defaultWidth: 130 },
};
}, []);

const copyButtons = useCopyButtons(columns, selection, "files");
const columns: TableColumnDefinition<LogicalFile>[] = React.useMemo(() => [
createTableColumn<LogicalFile>({
columnId: "IsProtected",
renderHeaderCell: () => <Tooltip content={nlsHPCC.Protected} relationship="label"><LockClosedFilled fontSize={18} /></Tooltip>,
renderCell: (cell) => cell.IsProtected ? <LockClosedFilled /> : "",
}),
createTableColumn<LogicalFile>({
columnId: "IsCompressed",
renderHeaderCell: () => <Tooltip content={nlsHPCC.Compressed} relationship="label"><FolderZip20Regular /></Tooltip>,
renderCell: (cell) => cell.IsCompressed ? <FolderZip20Regular /> : "",
}),
createTableColumn<LogicalFile>({
columnId: "Name",
compare: (a, b) => a.Name?.localeCompare(b.Name),
renderHeaderCell: () => nlsHPCC.LogicalName,
renderCell: (cell) => {
return <TableCellLayout>
<Link href={`#/files/${cell.Name}`}>{cell.Name}</Link>
</TableCellLayout>;
},
}),
createTableColumn<LogicalFile>({
columnId: "Owner",
compare: (a, b) => a.Owner?.localeCompare(b.Owner),
renderHeaderCell: () => nlsHPCC.Owner,
renderCell: (cell) => <TableCellLayout>{cell?.Owner}</TableCellLayout>,
}),
createTableColumn<LogicalFile>({
columnId: "SuperOwners",
compare: (a, b) => a.SuperOwners?.localeCompare(b.SuperOwners),
renderHeaderCell: () => nlsHPCC.SuperOwner,
renderCell: (cell) => <TableCellLayout>{cell?.SuperOwners}</TableCellLayout>,
}),
createTableColumn<LogicalFile>({
columnId: "Description",
compare: (a, b) => a.Description?.localeCompare(b.Description),
renderHeaderCell: () => nlsHPCC.Description,
renderCell: (cell) => <TableCellLayout>{cell?.Description}</TableCellLayout>,
}),
createTableColumn<LogicalFile>({
columnId: "Cluster",
compare: (a, b) => a["Cluster"]?.localeCompare(b["Cluster"]),
renderHeaderCell: () => nlsHPCC.Cluster,
renderCell: (cell) => <TableCellLayout>{cell["Cluster"]}</TableCellLayout>,
}),
createTableColumn<LogicalFile>({
columnId: "Records",
compare: (a, b) => a["Records"]?.localeCompare(b["Records"]),
renderHeaderCell: () => nlsHPCC.Records,
renderCell: (cell) => <TableCellLayout>{cell["Records"]}</TableCellLayout>,
}),
createTableColumn<LogicalFile>({
columnId: "Size",
compare: (a, b) => a["Size"]?.localeCompare(b["Size"]),
renderHeaderCell: () => nlsHPCC.Size,
renderCell: (cell) => <TableCellLayout>{cell["Size"]}</TableCellLayout>,
}),
createTableColumn<LogicalFile>({
columnId: "Parts",
compare: (a, b) => a["Parts"]?.localeCompare(b["Parts"]),
renderHeaderCell: () => nlsHPCC.Parts,
renderCell: (cell) => <TableCellLayout>{cell["Parts"]}</TableCellLayout>,
}),
createTableColumn<LogicalFile>({
columnId: "MinSkew",
compare: (a, b) => a["MinSkew"] - b["MinSkew"],
renderHeaderCell: () => nlsHPCC.MinSkew,
renderCell: (cell) => <TableCellLayout>{cell.MinSkew ? `${Utility.formatDecimal(cell.MinSkew / 100)}%` : ""}</TableCellLayout>,
}),
createTableColumn<LogicalFile>({
columnId: "MaxSkew",
compare: (a, b) => a["MaxSkew"] - b["MaxSkew"],
renderHeaderCell: () => nlsHPCC.MaxSkew,
renderCell: (cell) => <TableCellLayout>{cell.MaxSkew ? `${Utility.formatDecimal(cell.MaxSkew / 100)}%` : ""}</TableCellLayout>,
}),
createTableColumn<LogicalFile>({
columnId: "Modified",
compare: (a, b) => a.Modified?.localeCompare(b.Modified),
renderHeaderCell: () => nlsHPCC.ModifiedUTCGMT,
renderCell: (cell) => <TableCellLayout>{cell?.Modified}</TableCellLayout>,
}),
createTableColumn<LogicalFile>({
columnId: "Accessed",
compare: (a, b) => a.Accessed?.localeCompare(b.Accessed),
renderHeaderCell: () => nlsHPCC.LastAccessed,
renderCell: (cell) => <TableCellLayout>{cell?.Accessed}</TableCellLayout>,
}),
createTableColumn<LogicalFile>({
columnId: "AtRestCost",
compare: (a, b) => a.AtRestCost - b.AtRestCost,
renderHeaderCell: () => nlsHPCC.FileCostAtRest,
renderCell: (cell) => <TableCellLayout>{formatCost(cell.AtRestCost)}</TableCellLayout>,
}),
createTableColumn<LogicalFile>({
columnId: "AccessCost",
compare: (a, b) => a.AccessCost - b.AccessCost,
renderHeaderCell: () => nlsHPCC.FileAccessCost,
renderCell: (cell) => <TableCellLayout>{formatCost(cell.AccessCost)}</TableCellLayout>,
}),
], []);

const columnMap: Utility.ColumnMap = React.useMemo(() => {
const retVal: Utility.ColumnMap = {};
columns.forEach((col, idx) => {
const columnId = col.columnId.toString();
retVal[columnId] = {
id: `${columnId}_${idx}`,
field: columnId,
label: columnId
};
});
return retVal;
}, [columns]);

const copyButtons = useCopyButtons(columnMap, selection, "files");

const [DeleteConfirm, setShowDeleteConfirm] = useConfirm({
title: nlsHPCC.Delete,
Expand Down Expand Up @@ -341,19 +404,22 @@ export const Files: React.FunctionComponent<FilesProps> = ({
<SizeMe monitorHeight>{({ size }) =>
<div style={{ position: "relative", width: "100%", height: "100%" }}>
<div style={{ position: "absolute", width: "100%", height: `${size.height}px` }}>
<FluentPagedGrid
<FluentPagedDataGrid
store={gridStore}
query={query}
sort={sort}
pageNum={pageNum}
pageSize={pageSize}
total={total}
columns={columns}
height={`${size.height}px`}
sizingOptions={columnSizingOptions}
height={"calc(100vh - 176px)"}
onSelect={onSelectionChange}
selectedItems={selectedRows}
setSelection={setSelection}
setTotal={setTotal}
refresh={refreshTable}
></FluentPagedGrid>
></FluentPagedDataGrid>
</div>
</div>
}</SizeMe>
Expand All @@ -374,6 +440,6 @@ export const Files: React.FunctionComponent<FilesProps> = ({
setPageSize={setPageSize}
total={total}
></FluentPagedFooter>}
footerStyles={{}}
footerStyles={footerStyles}
/>;
};
Loading

0 comments on commit 4868c62

Please sign in to comment.