From 3924908636d7b7e394af03813f7aca8be6de793a Mon Sep 17 00:00:00 2001 From: Jeremy Clements <79224539+jeclrsg@users.noreply.github.com> Date: Tue, 20 Aug 2024 10:57:25 -0400 Subject: [PATCH 1/9] HPCC-32424 ECL Watch v9 Source Files default sorting changes the default sorting of files listed on the Source Files tab of the Workunit Details page, such that subfiles immediately follow their respective superfiles Signed-off-by: Jeremy Clements <79224539+jeclrsg@users.noreply.github.com> --- esp/src/src-react/components/SourceFiles.tsx | 11 ++++------- esp/src/src-react/hooks/workunit.ts | 19 +++++++++++++++++-- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/esp/src/src-react/components/SourceFiles.tsx b/esp/src/src-react/components/SourceFiles.tsx index ed1e083edfb..cda748e8845 100644 --- a/esp/src/src-react/components/SourceFiles.tsx +++ b/esp/src/src-react/components/SourceFiles.tsx @@ -25,7 +25,7 @@ interface SourceFilesProps { } const emptyFilter: { [id: string]: any } = {}; -const defaultSort = { attribute: "Name", descending: false }; +const defaultSort = { attribute: undefined, descending: false }; export const SourceFiles: React.FunctionComponent = ({ wuid, @@ -52,7 +52,7 @@ export const SourceFiles: React.FunctionComponent = ({ selectorType: "checkbox" }, Name: { - label: "Name", sortable: true, + label: "Name", width: 400, sortable: true, formatter: (Name, row) => { let fileUrl = `#/files/${Name}`; if (row?.FileCluster) { @@ -65,11 +65,8 @@ export const SourceFiles: React.FunctionComponent = ({ ; } }, - FileCluster: { label: nlsHPCC.FileCluster, width: 300, sortable: false }, - Count: { - label: nlsHPCC.Usage, width: 72, sortable: true, - justify: "right" - } + FileCluster: { label: nlsHPCC.FileCluster, width: 200, sortable: false }, + Count: { label: nlsHPCC.Usage, width: 72, sortable: true, justify: "right" } }; }, []); diff --git a/esp/src/src-react/hooks/workunit.ts b/esp/src/src-react/hooks/workunit.ts index 662b44dccdb..3bc1c22314a 100644 --- a/esp/src/src-react/hooks/workunit.ts +++ b/esp/src/src-react/hooks/workunit.ts @@ -132,6 +132,21 @@ export function useWorkunitSourceFiles(wuid: string): [SourceFile[], Workunit, W const [sourceFiles, setSourceFiles] = React.useState([]); const [count, inc] = useCounter(); + // sorts the WU source files alphabetically by parent name, then name + // with children immediately following parents + const sortFiles = React.useCallback(files => { + const sortedFiles = []; + const temp = files.sort((a, b) => a.Name.localeCompare(b.Name)); + + temp.filter(item => item.__hpcc_parentName === "").forEach(parent => { + sortedFiles.push(parent); + const relatedChildren = temp.filter(child => child.__hpcc_parentName === parent.Name); + sortedFiles.push(...relatedChildren); + }); + + return sortedFiles; + }, []); + React.useEffect(() => { if (workunit) { const fetchInfo = singletonDebounce(workunit, "fetchInfo"); @@ -151,10 +166,10 @@ export function useWorkunitSourceFiles(wuid: string): [SourceFile[], Workunit, W }); }); }); - setSourceFiles(sourceFiles); + setSourceFiles(sortFiles(sourceFiles)); }).catch(err => logger.error(err)); } - }, [workunit, state, count]); + }, [count, sortFiles, state, workunit]); return [sourceFiles, workunit, state, inc]; } From 283909eade15d4cb2b03758443d7fd1d40a64d33 Mon Sep 17 00:00:00 2001 From: Jeremy Clements <79224539+jeclrsg@users.noreply.github.com> Date: Fri, 6 Sep 2024 11:24:48 -0400 Subject: [PATCH 2/9] HPCC-32600 ECL Watch change DFU Workunits nav tab label change the label of tabs for DFU Workunits in the navigation to read "DFU Workunits" rather than "Workunits" Signed-off-by: Jeremy Clements <79224539+jeclrsg@users.noreply.github.com> --- esp/src/eclwatch/templates/GetDFUWorkunitsWidget.html | 2 +- esp/src/eclwatch/templates/HPCCPlatformFilesWidget.html | 2 +- esp/src/src-react/components/Menu.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/esp/src/eclwatch/templates/GetDFUWorkunitsWidget.html b/esp/src/eclwatch/templates/GetDFUWorkunitsWidget.html index da361ca3d54..f2d3eb9ec81 100644 --- a/esp/src/eclwatch/templates/GetDFUWorkunitsWidget.html +++ b/esp/src/eclwatch/templates/GetDFUWorkunitsWidget.html @@ -1,7 +1,7 @@
-
+
${i18n.Refresh}
diff --git a/esp/src/eclwatch/templates/HPCCPlatformFilesWidget.html b/esp/src/eclwatch/templates/HPCCPlatformFilesWidget.html index 6a6795ff9be..d7bf3127f4f 100644 --- a/esp/src/eclwatch/templates/HPCCPlatformFilesWidget.html +++ b/esp/src/eclwatch/templates/HPCCPlatformFilesWidget.html @@ -8,7 +8,7 @@
-
+
diff --git a/esp/src/src-react/components/Menu.tsx b/esp/src/src-react/components/Menu.tsx index ad77f8c47d3..3ef9b0953ff 100644 --- a/esp/src/src-react/components/Menu.tsx +++ b/esp/src/src-react/components/Menu.tsx @@ -164,7 +164,7 @@ const subMenuItems: SubMenuItems = { "files": [ { headerText: nlsHPCC.LogicalFiles, itemKey: "/files" }, { headerText: nlsHPCC.LandingZones, itemKey: "/landingzone" }, - { headerText: nlsHPCC.Workunits, itemKey: "/dfuworkunits" }, + { headerText: nlsHPCC.title_GetDFUWorkunits, itemKey: "/dfuworkunits" }, { headerText: nlsHPCC.XRef + " (L)", itemKey: "/xref" }, ], "queries": [ From bf533d6598876c32f5b0f220176ea18bb5b2bec4 Mon Sep 17 00:00:00 2001 From: Jeremy Clements <79224539+jeclrsg@users.noreply.github.com> Date: Thu, 5 Sep 2024 15:52:12 -0400 Subject: [PATCH 3/9] HPCC-32592 ECL Watch v9 remote copy dialog feedback displays a loading message and disables the remote copy dialog's submit button until the request comes back Signed-off-by: Jeremy Clements <79224539+jeclrsg@users.noreply.github.com> --- esp/src/src-react/components/forms/RemoteCopy.tsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/esp/src/src-react/components/forms/RemoteCopy.tsx b/esp/src/src-react/components/forms/RemoteCopy.tsx index baf5fcce2c2..2eaa0e3b7c3 100644 --- a/esp/src/src-react/components/forms/RemoteCopy.tsx +++ b/esp/src/src-react/components/forms/RemoteCopy.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { Checkbox, DefaultButton, IDropdownOption, mergeStyleSets, MessageBar, MessageBarType, PrimaryButton, Stack, TextField } from "@fluentui/react"; +import { Checkbox, DefaultButton, IDropdownOption, mergeStyleSets, MessageBar, MessageBarType, PrimaryButton, Spinner, Stack, TextField } from "@fluentui/react"; import { Controller, useForm } from "react-hook-form"; import { scopedLogger } from "@hpcc-js/util"; import nlsHPCC from "src/nlsHPCC"; @@ -58,20 +58,28 @@ export const RemoteCopy: React.FunctionComponent = ({ const [showError, setShowError] = React.useState(false); const [errorMessage, setErrorMessage] = React.useState(""); - + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const [selectedDestGroup, setSelectedDestGroup] = React.useState(""); const [replicateDisabled, setReplicateDisabled] = React.useState(true); const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); + FileSpray.Copy({ request: data }).then(({ CopyResponse, Exceptions }) => { if (Exceptions?.Exception) { + setSubmitDisabled(false); + setSpinnerHidden(true); setShowError(true); setErrorMessage(Exceptions?.Exception[0]?.Message); } else { + setSubmitDisabled(false); + setSpinnerHidden(true); setShowForm(false); reset(defaultValues); if (refreshGrid) refreshGrid(true); @@ -113,7 +121,8 @@ export const RemoteCopy: React.FunctionComponent = ({ return - + + setShowForm(false)} /> }> {showError && From f8773e48e3c81a10cf9fbe6250a8fae949fb81ae Mon Sep 17 00:00:00 2001 From: Jeremy Clements <79224539+jeclrsg@users.noreply.github.com> Date: Thu, 5 Sep 2024 15:19:33 -0400 Subject: [PATCH 4/9] HPCC-32601 ECL Watch v9 fix disappearing subnavigation fixes an issue where the subnavigation would not display properly on page load if a query-string existed in the url Signed-off-by: Jeremy Clements <79224539+jeclrsg@users.noreply.github.com> --- esp/src/src-react/components/Menu.tsx | 7 ++++--- esp/src/src-react/util/history.ts | 8 ++++++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/esp/src/src-react/components/Menu.tsx b/esp/src/src-react/components/Menu.tsx index ad77f8c47d3..2b8fdad4eb8 100644 --- a/esp/src/src-react/components/Menu.tsx +++ b/esp/src/src-react/components/Menu.tsx @@ -4,6 +4,7 @@ import { useConst } from "@fluentui/react-hooks"; import nlsHPCC from "src/nlsHPCC"; import { hasLogAccess } from "src/ESPLog"; import { containerized, bare_metal } from "src/BuildInfo"; +import { navCategory } from "../util/history"; import { MainNav, routes } from "../routes"; import { useFavorite, useFavorites, useHistory } from "../hooks/favorite"; import { useSessionStore } from "../hooks/store"; @@ -98,7 +99,7 @@ routes.forEach((route: any) => { }); function navSelectedKey(hashPath) { - const rootPath = navIdx(`/${hashPath?.split("/")[1]}`); + const rootPath = navIdx(`/${navCategory(hashPath)?.split("/")[1]}`); if (rootPath?.length) { return rootPath[0]; } @@ -214,8 +215,8 @@ for (const key in subMenuItems) { } function subNavSelectedKey(hashPath) { - const hashCategory = hashPath.split("/").slice(0, 3).join("/"); - return subNavIdx(hashCategory).length ? hashCategory : null; + const category = navCategory(hashPath); + return subNavIdx(category).length ? category : null; } interface SubNavigationProps { diff --git a/esp/src/src-react/util/history.ts b/esp/src/src-react/util/history.ts index d4420fc4d7e..6f54845db45 100644 --- a/esp/src/src-react/util/history.ts +++ b/esp/src/src-react/util/history.ts @@ -278,3 +278,11 @@ export function updateState(key: string, val?: string | string[] | number | bool } globalHistory.replaceState(state, ""); } + +export function navCategory(hash: string): string { + let category = hash.split("/").slice(0, 2).join("/"); + if (category.indexOf("?") > -1) { + category = category.substring(0, category.indexOf("?")); + } + return category; +} \ No newline at end of file From da1c54ed9df00707a4c35775a3c3a98a25b8e7b3 Mon Sep 17 00:00:00 2001 From: Jeremy Clements <79224539+jeclrsg@users.noreply.github.com> Date: Thu, 5 Sep 2024 17:00:05 -0400 Subject: [PATCH 5/9] HPCC-32593 ECL Watch v9 WU details fix sticky infogrid headers fixes the sticky column headers of the Infogrid component Signed-off-by: Jeremy Clements <79224539+jeclrsg@users.noreply.github.com> --- esp/src/src-react/components/InfoGrid.tsx | 26 +++++----- esp/src/src-react/components/LogViewer.tsx | 19 ++++---- .../src-react/components/controls/Grid.tsx | 47 +++++-------------- 3 files changed, 34 insertions(+), 58 deletions(-) diff --git a/esp/src/src-react/components/InfoGrid.tsx b/esp/src/src-react/components/InfoGrid.tsx index 1bcca1178b9..9b17d896e86 100644 --- a/esp/src/src-react/components/InfoGrid.tsx +++ b/esp/src/src-react/components/InfoGrid.tsx @@ -237,23 +237,19 @@ export const InfoGrid: React.FunctionComponent = ({ } }, [data.length]); - return
+ return
{({ size }) => -
-
- { }} - setTotal={setTotal} - refresh={refreshTable} - height={`${size.height - (44 + 8 + 45 + 12)}px`} - selectionMode={SelectionMode.none} - > -
-
+ { }} + setTotal={setTotal} + refresh={refreshTable} + height={`${size.height - (44 + 8 + 45 + 12)}px`} + selectionMode={SelectionMode.none} + > }
; }; diff --git a/esp/src/src-react/components/LogViewer.tsx b/esp/src/src-react/components/LogViewer.tsx index 82e027a55b4..2e44c369051 100644 --- a/esp/src/src-react/components/LogViewer.tsx +++ b/esp/src/src-react/components/LogViewer.tsx @@ -102,13 +102,16 @@ export const LogViewer: React.FunctionComponent = ({ return } main={ - } +
+ +
+ } />; }; diff --git a/esp/src/src-react/components/controls/Grid.tsx b/esp/src/src-react/components/controls/Grid.tsx index 770b5b759ba..129271d47b3 100644 --- a/esp/src/src-react/components/controls/Grid.tsx +++ b/esp/src/src-react/components/controls/Grid.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { DetailsList, DetailsListLayoutMode, Dropdown, IColumn as _IColumn, ICommandBarItemProps, IDetailsHeaderProps, IDetailsListStyles, mergeStyleSets, Selection, Stack, TooltipHost, TooltipOverflowMode, IRenderFunction, IDetailsRowProps, SelectionMode, ConstrainMode, ISelection } from "@fluentui/react"; +import { DetailsList, DetailsListLayoutMode, Dropdown, IColumn as _IColumn, ICommandBarItemProps, IDetailsHeaderProps, IDetailsListStyles, mergeStyleSets, Selection, Stack, TooltipHost, TooltipOverflowMode, IRenderFunction, IDetailsRowProps, SelectionMode, ConstrainMode, ISelection, ScrollablePane, Sticky } from "@fluentui/react"; import { Pagination } from "@fluentui/react-experiments/lib/Pagination"; import { useConst } from "@fluentui/react-hooks"; import { BaseStore, Memory, QueryRequest, QuerySortItem } from "src/store/Memory"; @@ -293,45 +293,22 @@ const FluentStoreGrid: React.FunctionComponent = ({ }, [memoizedColumns]); const renderDetailsHeader = React.useCallback((props: IDetailsHeaderProps, defaultRender?: any) => { - return defaultRender({ - ...props, - onRenderColumnHeaderTooltip: (tooltipHostProps) => { - return ; - }, - styles: { root: { paddingTop: 1 } } - }); + return + {defaultRender({ + ...props, + onRenderColumnHeaderTooltip: (tooltipHostProps) => { + return ; + }, + styles: { root: { paddingTop: 1 } } + })} + ; }, []); const columnResize = React.useCallback((column: IColumn, newWidth: number, columnIndex?: number) => { columnWidths.set(column.key, newWidth); }, [columnWidths]); - /* Monitor Scroll Events (hack) - - Essentially we are setting the scrollElement of the DetailsList to the div that contains the DetailsList (rather than a scrollable pane host). - See: https://github.com/microsoft/fluentui/blob/55d3a31042e8972ea373841bef616c68e6ab69f9/packages/react/src/components/List/List.tsx#L355-L369 - - Note: Not sure if `_onScroll` call is needed, but excluding for now as it seems to work without it and is more performant. - */ - // const id = useId("fluent-store-grid-"); - // const detailListScrollComponent = React.useRef(null); - // const detailListComponent = React.useRef(null); - // const [detailListElement, setDetailListElement] = React.useState(null); - // useMount(() => { - // const detailListElement = document.querySelector(`#${id} .ms-DetailsList`); - // setDetailListElement(detailListElement); - // if (detailListComponent.current?._list?.current) { - // detailListComponent.current._list.current._scrollElement = detailListElement; - // } - // }); - // useOnEvent(detailListScrollComponent, "scroll", () => { - // detailListComponent.current?._list?.current?._onScroll(); - // }); - // useOnEvent(detailListScrollComponent, "scroll", () => { - // detailListComponent.current?._list?.current?._onAsyncScrollDebounced(); - // }); - - return
+ return = ({ styles={gridStyles(height)} selectionMode={selectionMode} /> -
; + ; }; interface FluentGridProps { From ad7509d4d256d9d2f6e4cde989bf9f68a74c2ad0 Mon Sep 17 00:00:00 2001 From: Jeremy Clements <79224539+jeclrsg@users.noreply.github.com> Date: Tue, 10 Sep 2024 15:02:36 -0400 Subject: [PATCH 6/9] HPCC-32604 ECL Watch v9 forms add progress indicator add progress indicators to various form components, and disable the primary button until completion to block multiple unintentional submits Signed-off-by: Jeremy Clements <79224539+jeclrsg@users.noreply.github.com> --- esp/src/src-react/components/forms/AddBinding.tsx | 12 ++++++++++-- esp/src/src-react/components/forms/AddGroup.tsx | 11 +++++++++-- .../src-react/components/forms/AddGroupResource.tsx | 11 +++++++++-- .../src-react/components/forms/AddPackageMap.tsx | 13 +++++++++++-- .../components/forms/AddPackageMapPart.tsx | 13 +++++++++++-- .../src-react/components/forms/AddPermission.tsx | 13 +++++++++++-- .../src-react/components/forms/AddToSuperfile.tsx | 11 +++++++++-- esp/src/src-react/components/forms/AddUser.tsx | 13 +++++++++++-- esp/src/src-react/components/forms/CopyFile.tsx | 13 +++++++++++-- esp/src/src-react/components/forms/DesprayFile.tsx | 13 +++++++++++-- esp/src/src-react/components/forms/GroupAddUser.tsx | 13 +++++++++++-- esp/src/src-react/components/forms/PublishQuery.tsx | 11 +++++++++-- esp/src/src-react/components/forms/PushEvent.tsx | 13 +++++++++++-- esp/src/src-react/components/forms/RenameFile.tsx | 11 +++++++++-- .../src-react/components/forms/ReplicateFile.tsx | 11 +++++++++-- .../src-react/components/forms/TitlebarConfig.tsx | 11 +++++++++-- esp/src/src-react/components/forms/UserAddGroup.tsx | 13 +++++++++++-- .../components/forms/landing-zone/AddFileForm.tsx | 11 +++++++++-- .../forms/landing-zone/BlobImportForm.tsx | 11 +++++++++-- .../forms/landing-zone/DelimitedImportForm.tsx | 11 +++++++++-- .../forms/landing-zone/FixedImportForm.tsx | 11 +++++++++-- .../forms/landing-zone/JsonImportForm.tsx | 11 +++++++++-- .../forms/landing-zone/VariableImportForm.tsx | 11 +++++++++-- .../components/forms/landing-zone/XmlImportForm.tsx | 11 +++++++++-- 24 files changed, 235 insertions(+), 48 deletions(-) diff --git a/esp/src/src-react/components/forms/AddBinding.tsx b/esp/src/src-react/components/forms/AddBinding.tsx index 33df4fa0127..abc26d8d4c9 100644 --- a/esp/src/src-react/components/forms/AddBinding.tsx +++ b/esp/src/src-react/components/forms/AddBinding.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { DefaultButton, IDropdownOption, PrimaryButton, TextField, } from "@fluentui/react"; +import { DefaultButton, IDropdownOption, PrimaryButton, Spinner, TextField, } from "@fluentui/react"; import { scopedLogger } from "@hpcc-js/util"; import { useForm, Controller } from "react-hook-form"; import { EsdlDefinitionsTextField, EsdlEspProcessesTextField } from "./Fields"; @@ -39,6 +39,8 @@ export const AddBindingForm: React.FunctionComponent = ({ }) => { const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const closeForm = React.useCallback(() => { setShowForm(false); @@ -47,11 +49,16 @@ export const AddBindingForm: React.FunctionComponent = ({ const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); + const request: any = data; request.Overwrite = true; WsESDLConfig.PublishESDLBinding({ request: request }) .then(() => { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); reset(defaultValues); if (refreshGrid) refreshGrid(); @@ -66,7 +73,8 @@ export const AddBindingForm: React.FunctionComponent = ({ return - + + closeForm()} /> }> = ({ }) => { const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const closeForm = React.useCallback(() => { setShowForm(false); @@ -41,10 +43,14 @@ export const AddGroupForm: React.FunctionComponent = ({ const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); const request: any = data; WsAccess.GroupAdd({ request: request }) .then(() => { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); reset(defaultValues); if (refreshGrid) refreshGrid(); @@ -58,7 +64,8 @@ export const AddGroupForm: React.FunctionComponent = ({ return - + + { reset(defaultValues); closeForm(); }} /> }> { const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const closeForm = React.useCallback(() => { setShowForm(false); @@ -51,6 +53,8 @@ export const AddGroupResourceForm: React.FunctionComponent { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); const request: any = data; request["action"] = "update"; @@ -60,6 +64,8 @@ export const AddGroupResourceForm: React.FunctionComponent { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); reset(defaultValues); if (refreshGrid) refreshGrid(); @@ -73,7 +79,8 @@ export const AddGroupResourceForm: React.FunctionComponent - + + { reset(defaultValues); closeForm(); }} /> }> = ({ }) => { const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const [remoteTargets, setRemoteTargets] = React.useState([]); @@ -67,15 +69,21 @@ export const AddPackageMap: React.FunctionComponent = ({ const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); WsPackageMaps.AddPackage({ request: data }) .then(({ AddPackageResponse, Exceptions }) => { if (AddPackageResponse?.status?.Code === 0) { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); refreshData(true); reset(defaultValues); } else if (Exceptions) { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); logger.error(Exceptions.Exception[0].Message); } @@ -99,7 +107,8 @@ export const AddPackageMap: React.FunctionComponent = ({ return - + + closeForm()} /> }> diff --git a/esp/src/src-react/components/forms/AddPackageMapPart.tsx b/esp/src/src-react/components/forms/AddPackageMapPart.tsx index cae4d80a7c1..23193c02259 100644 --- a/esp/src/src-react/components/forms/AddPackageMapPart.tsx +++ b/esp/src/src-react/components/forms/AddPackageMapPart.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { Checkbox, DefaultButton, PrimaryButton, Stack, TextField, } from "@fluentui/react"; +import { Checkbox, DefaultButton, PrimaryButton, Spinner, Stack, TextField, } from "@fluentui/react"; import { useForm, Controller } from "react-hook-form"; import { scopedLogger } from "@hpcc-js/util"; import * as WsPackageMaps from "src/WsPackageMaps"; @@ -50,6 +50,8 @@ export const AddPackageMapPart: React.FunctionComponent refreshData, }) => { const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const closeForm = React.useCallback(() => { setShowForm(false); @@ -58,15 +60,21 @@ export const AddPackageMapPart: React.FunctionComponent const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); WsPackageMaps.AddPartToPackageMap({ request: { ...data, Target: target, PackageMap: packageMap } }) .then(({ AddPartToPackageMapResponse, Exceptions }) => { if (AddPartToPackageMapResponse?.status?.Code === 0) { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); if (refreshData) refreshData(); reset(defaultValues); } else if (Exceptions) { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); logger.error(Exceptions.Exception[0].Message); } @@ -82,7 +90,8 @@ export const AddPackageMapPart: React.FunctionComponent return - + + closeForm()} /> }> diff --git a/esp/src/src-react/components/forms/AddPermission.tsx b/esp/src/src-react/components/forms/AddPermission.tsx index 5b44b6eb7e2..40a31d8a604 100644 --- a/esp/src/src-react/components/forms/AddPermission.tsx +++ b/esp/src/src-react/components/forms/AddPermission.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { DefaultButton, IDropdownOption, MessageBar, MessageBarType, PrimaryButton, TextField, } from "@fluentui/react"; +import { DefaultButton, IDropdownOption, MessageBar, MessageBarType, PrimaryButton, Spinner, TextField, } from "@fluentui/react"; import { scopedLogger } from "@hpcc-js/util"; import { useForm, Controller } from "react-hook-form"; import nlsHPCC from "src/nlsHPCC"; @@ -34,6 +34,8 @@ export const AddPermissionForm: React.FunctionComponent }) => { const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const [showError, setShowError] = React.useState(false); const [errorMessage, setErrorMessage] = React.useState(""); @@ -45,6 +47,8 @@ export const AddPermissionForm: React.FunctionComponent const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); const request: any = data; WsAccess.ResourceAdd({ request: request }) @@ -52,9 +56,13 @@ export const AddPermissionForm: React.FunctionComponent if (ResourceAddResponse?.retcode < 0) { //log exception from API setShowError(true); + setSubmitDisabled(false); + setSpinnerHidden(true); setErrorMessage(ResourceAddResponse?.retmsg); logger.error(ResourceAddResponse?.retmsg); } else { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); reset(defaultValues); if (refreshGrid) refreshGrid(); @@ -69,7 +77,8 @@ export const AddPermissionForm: React.FunctionComponent return - + + { reset(defaultValues); closeForm(); }} /> }> = ({ }) => { const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const closeForm = React.useCallback(() => { setShowForm(false); @@ -56,8 +58,12 @@ export const AddToSuperfile: React.FunctionComponent = ({ const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); WsDfu.AddtoSuperfile(data.names, data.superFile, data.existingFile) .then(response => { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); if (refreshGrid) refreshGrid(true); }) @@ -83,7 +89,8 @@ export const AddToSuperfile: React.FunctionComponent = ({ return - + + closeForm()} /> }> diff --git a/esp/src/src-react/components/forms/AddUser.tsx b/esp/src/src-react/components/forms/AddUser.tsx index b74977e1403..377025fdabd 100644 --- a/esp/src/src-react/components/forms/AddUser.tsx +++ b/esp/src/src-react/components/forms/AddUser.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { DefaultButton, MessageBar, MessageBarType, PrimaryButton, TextField, } from "@fluentui/react"; +import { DefaultButton, MessageBar, MessageBarType, PrimaryButton, Spinner, TextField, } from "@fluentui/react"; import { scopedLogger } from "@hpcc-js/util"; import { useForm, Controller } from "react-hook-form"; import nlsHPCC from "src/nlsHPCC"; @@ -41,6 +41,8 @@ export const AddUserForm: React.FunctionComponent = ({ }) => { const { handleSubmit, control, reset, watch } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const pwd1 = watch("password1"); @@ -54,6 +56,8 @@ export const AddUserForm: React.FunctionComponent = ({ const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); const request: any = data; WsAccess.AddUser({ request: request }) @@ -61,9 +65,13 @@ export const AddUserForm: React.FunctionComponent = ({ if (AddUserResponse?.retcode < 0) { //log exception from API setShowError(true); + setSubmitDisabled(false); + setSpinnerHidden(true); setErrorMessage(AddUserResponse?.retmsg); logger.error(AddUserResponse?.retmsg); } else { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); reset(defaultValues); if (refreshGrid) refreshGrid(); @@ -78,7 +86,8 @@ export const AddUserForm: React.FunctionComponent = ({ return - + + { reset(defaultValues); closeForm(); }} /> }> = ({ }) => { const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const closeForm = React.useCallback(() => { setShowForm(false); @@ -64,10 +66,14 @@ export const CopyFile: React.FunctionComponent = ({ const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); if (logicalFiles.length > 0) { if (logicalFiles.length === 1) { const request = { ...data, sourceLogicalName: logicalFiles[0] }; FileSpray.Copy({ request: request }).then(response => { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); pushUrl(`/dfuworkunits/${response.CopyResponse.result}`); }); @@ -77,6 +83,8 @@ export const CopyFile: React.FunctionComponent = ({ const requests = []; requests.push(FileSpray.Copy({ request: request })); Promise.all(requests).then(_ => { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); if (refreshGrid) refreshGrid(); }); @@ -115,7 +123,8 @@ export const CopyFile: React.FunctionComponent = ({ return - + + closeForm()} /> }> diff --git a/esp/src/src-react/components/forms/DesprayFile.tsx b/esp/src/src-react/components/forms/DesprayFile.tsx index b57a171d0d1..ee70122c138 100644 --- a/esp/src/src-react/components/forms/DesprayFile.tsx +++ b/esp/src/src-react/components/forms/DesprayFile.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { Checkbox, DefaultButton, IDropdownOption, mergeStyleSets, PrimaryButton, Stack, TextField, } from "@fluentui/react"; +import { Checkbox, DefaultButton, IDropdownOption, mergeStyleSets, PrimaryButton, Spinner, Stack, TextField, } from "@fluentui/react"; import { useForm, Controller } from "react-hook-form"; import { FileSpray, FileSprayService } from "@hpcc-js/comms"; import { scopedLogger } from "@hpcc-js/util"; @@ -67,6 +67,8 @@ export const DesprayFile: React.FunctionComponent = ({ const [os, setOs] = React.useState(); const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const closeForm = React.useCallback(() => { setShowForm(false); @@ -75,6 +77,8 @@ export const DesprayFile: React.FunctionComponent = ({ const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); if (logicalFiles.length > 0) { if (logicalFiles.length === 1) { const request = { @@ -84,6 +88,8 @@ export const DesprayFile: React.FunctionComponent = ({ sourceLogicalName: logicalFiles[0] } as FileSpray.Despray; myFileSprayService.Despray(request).then(response => { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); reset(defaultValues); if (refreshGrid) refreshGrid(true); @@ -99,6 +105,8 @@ export const DesprayFile: React.FunctionComponent = ({ requests.push(myFileSprayService.Despray(request)); }); Promise.all(requests).then(_ => { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); if (refreshGrid) refreshGrid(true); }).catch(err => logger.error(err)); @@ -136,7 +144,8 @@ export const DesprayFile: React.FunctionComponent = ({ return - + + closeForm()} /> }> diff --git a/esp/src/src-react/components/forms/GroupAddUser.tsx b/esp/src/src-react/components/forms/GroupAddUser.tsx index c8f1cef78b9..518231f1d17 100644 --- a/esp/src/src-react/components/forms/GroupAddUser.tsx +++ b/esp/src/src-react/components/forms/GroupAddUser.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { DefaultButton, IDropdownOption, MessageBar, MessageBarType, PrimaryButton, } from "@fluentui/react"; +import { DefaultButton, IDropdownOption, MessageBar, MessageBarType, PrimaryButton, Spinner, } from "@fluentui/react"; import { scopedLogger } from "@hpcc-js/util"; import { useForm, Controller } from "react-hook-form"; import nlsHPCC from "src/nlsHPCC"; @@ -32,6 +32,8 @@ export const GroupAddUserForm: React.FunctionComponent = ({ }) => { const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const [showError, setShowError] = React.useState(false); const [errorMessage, setErrorMessage] = React.useState(""); @@ -43,6 +45,8 @@ export const GroupAddUserForm: React.FunctionComponent = ({ const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); const request: any = data; request.groupname = groupname; request.action = "add"; @@ -52,9 +56,13 @@ export const GroupAddUserForm: React.FunctionComponent = ({ if (GroupMemberEditResponse?.retcode < 0) { //log exception from API setShowError(true); + setSubmitDisabled(false); + setSpinnerHidden(true); setErrorMessage(GroupMemberEditResponse?.retmsg); logger.error(GroupMemberEditResponse?.retmsg); } else { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); reset(defaultValues); if (refreshGrid) refreshGrid(); @@ -69,7 +77,8 @@ export const GroupAddUserForm: React.FunctionComponent = ({ return - + + { reset(defaultValues); closeForm(); }} /> }> = ({ const [workunit] = useWorkunit(wuid); const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const closeForm = React.useCallback(() => { setShowForm(false); @@ -54,6 +56,8 @@ export const PublishQueryForm: React.FunctionComponent = ({ const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); const request = { Wuid: workunit?.Wuid, Cluster: workunit?.Cluster, @@ -70,6 +74,8 @@ export const PublishQueryForm: React.FunctionComponent = ({ workunit.publishEx(request).then(() => { return workunit.update({ Jobname: data.jobName }); }).then(() => { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); reset(defaultValues); }).catch(err => logger.error(err)); @@ -86,7 +92,8 @@ export const PublishQueryForm: React.FunctionComponent = ({ return - + + closeForm()} /> }> = ({ }) => { const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const [showError, setShowError] = React.useState(false); const [errorMessage, setErrorMessage] = React.useState(""); @@ -40,6 +42,8 @@ export const PushEventForm: React.FunctionComponent = ({ const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); const request: any = data; WsWorkunits.WUPushEvent({ request: request }) @@ -47,9 +51,13 @@ export const PushEventForm: React.FunctionComponent = ({ if (WUPushEventResponse?.retcode < 0) { //log exception from API setShowError(true); + setSubmitDisabled(false); + setSpinnerHidden(true); setErrorMessage(WUPushEventResponse?.retmsg); logger.error(WUPushEventResponse?.retmsg); } else { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); reset(defaultValues); } @@ -62,7 +70,8 @@ export const PushEventForm: React.FunctionComponent = ({ return - + + { reset(defaultValues); closeForm(); }} /> }> = ({ }) => { const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const service = useConst(() => new FileSprayService({ baseUrl: "" })); @@ -49,6 +51,8 @@ export const RenameFile: React.FunctionComponent = ({ const onSubmit = React.useCallback(() => { handleSubmit( async (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); const renameRequests = []; const getDfuWuRequests = []; @@ -87,6 +91,8 @@ export const RenameFile: React.FunctionComponent = ({ } } }); + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); if (refreshGrid) refreshGrid(true); }, @@ -116,7 +122,8 @@ export const RenameFile: React.FunctionComponent = ({ return - + + closeForm()} /> }> diff --git a/esp/src/src-react/components/forms/ReplicateFile.tsx b/esp/src/src-react/components/forms/ReplicateFile.tsx index 4a8b61034f9..ed504ec3b23 100644 --- a/esp/src/src-react/components/forms/ReplicateFile.tsx +++ b/esp/src/src-react/components/forms/ReplicateFile.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { DefaultButton, IDropdownOption, PrimaryButton, Stack, TextField, } from "@fluentui/react"; +import { DefaultButton, IDropdownOption, PrimaryButton, Spinner, Stack, TextField, } from "@fluentui/react"; import { useForm, Controller } from "react-hook-form"; import nlsHPCC from "src/nlsHPCC"; import * as FileSpray from "src/FileSpray"; @@ -37,6 +37,8 @@ export const ReplicateFile: React.FunctionComponent = ({ const [file] = useFile(cluster, logicalFile); const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const closeForm = React.useCallback(() => { setShowForm(false); @@ -45,8 +47,12 @@ export const ReplicateFile: React.FunctionComponent = ({ const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); const request = { ...data, srcname: logicalFile }; FileSpray.Replicate({ request: request }).then(response => { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); pushUrl(`/dfuworkunits/${response?.ReplicateResponse?.wuid}`); }); @@ -64,7 +70,8 @@ export const ReplicateFile: React.FunctionComponent = ({ return - + + closeForm()} /> }> diff --git a/esp/src/src-react/components/forms/TitlebarConfig.tsx b/esp/src/src-react/components/forms/TitlebarConfig.tsx index b0682975468..59d49193871 100644 --- a/esp/src/src-react/components/forms/TitlebarConfig.tsx +++ b/esp/src/src-react/components/forms/TitlebarConfig.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { Checkbox, ColorPicker, DefaultButton, getColorFromString, IColor, Label, PrimaryButton, TextField, TooltipHost } from "@fluentui/react"; +import { Checkbox, ColorPicker, DefaultButton, getColorFromString, IColor, Label, PrimaryButton, Spinner, TextField, TooltipHost } from "@fluentui/react"; import { useForm, Controller } from "react-hook-form"; import { MessageBox } from "../../layouts/MessageBox"; import { useGlobalStore } from "../../hooks/store"; @@ -32,6 +32,8 @@ export const TitlebarConfig: React.FunctionComponent = ({ setShowForm }) => { const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const [color, setColor] = React.useState(white); const updateColor = React.useCallback((evt: any, colorObj: IColor) => setColor(colorObj), []); const [showEnvironmentTitle, setShowEnvironmentTitle] = useGlobalStore("HPCCPlatformWidget_Toolbar_Active", toolbarThemeDefaults.active, true); @@ -45,6 +47,8 @@ export const TitlebarConfig: React.FunctionComponent = ({ const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); const request: any = data; request.titlebarColor = color.str; @@ -52,6 +56,8 @@ export const TitlebarConfig: React.FunctionComponent = ({ setEnvironmentTitle(request?.environmentTitle); setTitlebarColor(request.titlebarColor); + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); }, )(); @@ -78,7 +84,8 @@ export const TitlebarConfig: React.FunctionComponent = ({ return - + + { reset(defaultValues); closeForm(); }} /> { onReset(); }} /> }> diff --git a/esp/src/src-react/components/forms/UserAddGroup.tsx b/esp/src/src-react/components/forms/UserAddGroup.tsx index b09393c30f1..467271fdeb9 100644 --- a/esp/src/src-react/components/forms/UserAddGroup.tsx +++ b/esp/src/src-react/components/forms/UserAddGroup.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { DefaultButton, IDropdownOption, MessageBar, MessageBarType, PrimaryButton, } from "@fluentui/react"; +import { DefaultButton, IDropdownOption, MessageBar, MessageBarType, PrimaryButton, Spinner, } from "@fluentui/react"; import { scopedLogger } from "@hpcc-js/util"; import { useForm, Controller } from "react-hook-form"; import nlsHPCC from "src/nlsHPCC"; @@ -32,6 +32,8 @@ export const UserAddGroupForm: React.FunctionComponent = ({ }) => { const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const [showError, setShowError] = React.useState(false); const [errorMessage, setErrorMessage] = React.useState(""); @@ -43,6 +45,8 @@ export const UserAddGroupForm: React.FunctionComponent = ({ const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); const request: any = data; request.username = username; request.action = "add"; @@ -52,9 +56,13 @@ export const UserAddGroupForm: React.FunctionComponent = ({ if (UserGroupEditResponse?.retcode < 0) { //log exception from API setShowError(true); + setSubmitDisabled(false); + setSpinnerHidden(true); setErrorMessage(UserGroupEditResponse?.retmsg); logger.error(UserGroupEditResponse?.retmsg); } else { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); reset(defaultValues); if (refreshGrid) refreshGrid(); @@ -69,7 +77,8 @@ export const UserAddGroupForm: React.FunctionComponent = ({ return - + + { reset(defaultValues); closeForm(); }} /> }> = ({ }) => { const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const closeForm = React.useCallback(() => { setShowForm(false); @@ -39,6 +41,8 @@ export const AddFileForm: React.FunctionComponent = ({ const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); const dropZone = { ...store.get(data.NetAddress), NetAddress: data.NetAddress @@ -57,6 +61,8 @@ export const AddFileForm: React.FunctionComponent = ({ }; store.addUserFile(file); refreshGrid(); + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); reset(defaultValues); }, @@ -68,7 +74,8 @@ export const AddFileForm: React.FunctionComponent = ({ return - + + closeForm()} /> }> = ({ const [, { isContainer }] = useBuildInfo(); const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const closeForm = React.useCallback(() => { setShowForm(false); @@ -74,6 +76,8 @@ export const BlobImportForm: React.FunctionComponent = ({ const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); let request = {}; const files = data.selectedFiles; @@ -115,6 +119,8 @@ export const BlobImportForm: React.FunctionComponent = ({ } }); if (errors.length === 0) { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); } } @@ -153,7 +159,8 @@ export const BlobImportForm: React.FunctionComponent = ({ return - + + closeForm()} /> }> diff --git a/esp/src/src-react/components/forms/landing-zone/DelimitedImportForm.tsx b/esp/src/src-react/components/forms/landing-zone/DelimitedImportForm.tsx index 93510cf8191..ceb386cf2a4 100644 --- a/esp/src/src-react/components/forms/landing-zone/DelimitedImportForm.tsx +++ b/esp/src/src-react/components/forms/landing-zone/DelimitedImportForm.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { Checkbox, DefaultButton, Dropdown, IDropdownOption, mergeStyleSets, PrimaryButton, Stack, TextField } from "@fluentui/react"; +import { Checkbox, DefaultButton, Dropdown, IDropdownOption, mergeStyleSets, PrimaryButton, Spinner, Stack, TextField } from "@fluentui/react"; import { scopedLogger } from "@hpcc-js/util"; import { useForm, Controller } from "react-hook-form"; import * as FileSpray from "src/FileSpray"; @@ -82,6 +82,8 @@ export const DelimitedImportForm: React.FunctionComponent({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const closeForm = React.useCallback(() => { setShowForm(false); @@ -91,6 +93,8 @@ export const DelimitedImportForm: React.FunctionComponent { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); let request = {}; const files = data.selectedFiles; @@ -136,6 +140,8 @@ export const DelimitedImportForm: React.FunctionComponent - + + closeForm()} /> }> diff --git a/esp/src/src-react/components/forms/landing-zone/FixedImportForm.tsx b/esp/src/src-react/components/forms/landing-zone/FixedImportForm.tsx index 6b8a5cbaa02..34846ef4bef 100644 --- a/esp/src/src-react/components/forms/landing-zone/FixedImportForm.tsx +++ b/esp/src/src-react/components/forms/landing-zone/FixedImportForm.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { Checkbox, DefaultButton, IDropdownOption, mergeStyleSets, PrimaryButton, Stack, TextField } from "@fluentui/react"; +import { Checkbox, DefaultButton, IDropdownOption, mergeStyleSets, PrimaryButton, Spinner, Stack, TextField } from "@fluentui/react"; import { scopedLogger } from "@hpcc-js/util"; import { useForm, Controller } from "react-hook-form"; import * as FileSpray from "src/FileSpray"; @@ -65,6 +65,8 @@ export const FixedImportForm: React.FunctionComponent = ({ const [, { isContainer }] = useBuildInfo(); const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const closeForm = React.useCallback(() => { setShowForm(false); @@ -74,6 +76,8 @@ export const FixedImportForm: React.FunctionComponent = ({ const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); let request = {}; const files = data.selectedFiles; @@ -120,6 +124,8 @@ export const FixedImportForm: React.FunctionComponent = ({ } }); if (errors.length === 0) { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); } } @@ -160,7 +166,8 @@ export const FixedImportForm: React.FunctionComponent = ({ return - + + closeForm()} /> }> diff --git a/esp/src/src-react/components/forms/landing-zone/JsonImportForm.tsx b/esp/src/src-react/components/forms/landing-zone/JsonImportForm.tsx index 7132df1e0fa..590f26e9aba 100644 --- a/esp/src/src-react/components/forms/landing-zone/JsonImportForm.tsx +++ b/esp/src/src-react/components/forms/landing-zone/JsonImportForm.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { Checkbox, DefaultButton, Dropdown, IDropdownOption, mergeStyleSets, PrimaryButton, Stack, TextField } from "@fluentui/react"; +import { Checkbox, DefaultButton, Dropdown, IDropdownOption, mergeStyleSets, PrimaryButton, Spinner, Stack, TextField } from "@fluentui/react"; import { scopedLogger } from "@hpcc-js/util"; import { useForm, Controller } from "react-hook-form"; import * as FileSpray from "src/FileSpray"; @@ -69,6 +69,8 @@ export const JsonImportForm: React.FunctionComponent = ({ const [, { isContainer }] = useBuildInfo(); const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const closeForm = React.useCallback(() => { setShowForm(false); @@ -77,6 +79,8 @@ export const JsonImportForm: React.FunctionComponent = ({ const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); let request = {}; const files = data.selectedFiles; @@ -125,6 +129,8 @@ export const JsonImportForm: React.FunctionComponent = ({ } }); if (errors.length === 0) { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); } } @@ -165,7 +171,8 @@ export const JsonImportForm: React.FunctionComponent = ({ return - + + closeForm()} /> }> diff --git a/esp/src/src-react/components/forms/landing-zone/VariableImportForm.tsx b/esp/src/src-react/components/forms/landing-zone/VariableImportForm.tsx index 64fadc72493..6ae0bbf3c1c 100644 --- a/esp/src/src-react/components/forms/landing-zone/VariableImportForm.tsx +++ b/esp/src/src-react/components/forms/landing-zone/VariableImportForm.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { Checkbox, DefaultButton, Dropdown, IDropdownOption, mergeStyleSets, PrimaryButton, Stack, TextField } from "@fluentui/react"; +import { Checkbox, DefaultButton, Dropdown, IDropdownOption, mergeStyleSets, PrimaryButton, Spinner, Stack, TextField } from "@fluentui/react"; import { scopedLogger } from "@hpcc-js/util"; import { useForm, Controller } from "react-hook-form"; import * as FileSpray from "src/FileSpray"; @@ -65,6 +65,8 @@ export const VariableImportForm: React.FunctionComponent({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const closeForm = React.useCallback(() => { setShowForm(false); @@ -73,6 +75,8 @@ export const VariableImportForm: React.FunctionComponent { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); let request = {}; const files = data.selectedFiles; @@ -117,6 +121,8 @@ export const VariableImportForm: React.FunctionComponent - + + closeForm()} /> }> diff --git a/esp/src/src-react/components/forms/landing-zone/XmlImportForm.tsx b/esp/src/src-react/components/forms/landing-zone/XmlImportForm.tsx index e5f01992294..f40fd9aca23 100644 --- a/esp/src/src-react/components/forms/landing-zone/XmlImportForm.tsx +++ b/esp/src/src-react/components/forms/landing-zone/XmlImportForm.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { Checkbox, DefaultButton, Dropdown, IDropdownOption, mergeStyleSets, PrimaryButton, Stack, TextField } from "@fluentui/react"; +import { Checkbox, DefaultButton, Dropdown, IDropdownOption, mergeStyleSets, PrimaryButton, Spinner, Stack, TextField } from "@fluentui/react"; import { scopedLogger } from "@hpcc-js/util"; import { useForm, Controller } from "react-hook-form"; import * as FileSpray from "src/FileSpray"; @@ -69,6 +69,8 @@ export const XmlImportForm: React.FunctionComponent = ({ const [, { isContainer }] = useBuildInfo(); const { handleSubmit, control, reset } = useForm({ defaultValues }); + const [submitDisabled, setSubmitDisabled] = React.useState(false); + const [spinnerHidden, setSpinnerHidden] = React.useState(true); const closeForm = React.useCallback(() => { setShowForm(false); @@ -77,6 +79,8 @@ export const XmlImportForm: React.FunctionComponent = ({ const onSubmit = React.useCallback(() => { handleSubmit( (data, evt) => { + setSubmitDisabled(true); + setSpinnerHidden(false); let request = {}; const files = data.selectedFiles; @@ -123,6 +127,8 @@ export const XmlImportForm: React.FunctionComponent = ({ } }); if (errors.length === 0) { + setSubmitDisabled(false); + setSpinnerHidden(true); closeForm(); } } @@ -163,7 +169,8 @@ export const XmlImportForm: React.FunctionComponent = ({ return - + + closeForm()} /> }> From 67a5ea97ab207ccf1fb39569ae9309ad30b3633c Mon Sep 17 00:00:00 2001 From: Gavin Halliday Date: Wed, 11 Sep 2024 16:06:41 +0100 Subject: [PATCH 7/9] HPCC-32651 Support reading zero length file as a compressed file Signed-off-by: Gavin Halliday --- system/jlib/jlzw.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/system/jlib/jlzw.cpp b/system/jlib/jlzw.cpp index 2dbf48aca62..3c18d04dc93 100644 --- a/system/jlib/jlzw.cpp +++ b/system/jlib/jlzw.cpp @@ -2466,7 +2466,7 @@ static bool isCompressedType(__int64 compressedType) return 0 != getCompressedMethod(compressedType); } -bool isCompressedFile(IFileIO *iFileIO, CompressedFileTrailer *trailer=nullptr) +static bool isCompressedFile(IFileIO *iFileIO, CompressedFileTrailer *trailer=nullptr) { if (iFileIO) { @@ -2484,6 +2484,16 @@ bool isCompressedFile(IFileIO *iFileIO, CompressedFileTrailer *trailer=nullptr) return true; } } + else if ((fsize == 0) && trailer) + { + //If the file is empty, but we are expecting a compressed file, fill in the trailer with default information + memset(trailer,0,sizeof(*trailer)); + trailer->crc = ~0U; + trailer->compressedType = LZ4COMPRESSEDFILEFLAG; + trailer->blockSize = LZ4COMPRESSEDFILEBLOCKSIZE; + trailer->recordSize = 0; + return true; + } } return false; } From 8f05acab2828fc78f878c9210d9031354adcbd28 Mon Sep 17 00:00:00 2001 From: Gordon Smith Date: Fri, 13 Sep 2024 12:00:31 +0100 Subject: [PATCH 8/9] HPCC-32593 ECL Watch v9 WU details fix sticky infogrid headers fixes the sticky column headers of the Infogrid component Signed-off-by: Gordon Smith --- esp/src/src-react/components/InfoGrid.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/esp/src/src-react/components/InfoGrid.tsx b/esp/src/src-react/components/InfoGrid.tsx index 9b17d896e86..ec028305d8b 100644 --- a/esp/src/src-react/components/InfoGrid.tsx +++ b/esp/src/src-react/components/InfoGrid.tsx @@ -5,7 +5,6 @@ import { formatCost, formatTwoDigits } from "src/Session"; import nlsHPCC from "src/nlsHPCC"; import { useWorkunitExceptions } from "../hooks/workunit"; import { FluentGrid, useCopyButtons, useFluentStoreState, FluentColumns } from "./controls/Grid"; -import { pivotItemStyle } from "../layouts/pivot"; function extractGraphInfo(msg) { const regex = /^([a-zA-Z0-9 :]+: )(graph graph(\d+)\[(\d+)\], )(([a-zA-Z]+)\[(\d+)\]: )?(.*)$/gmi; From 46540e52d90b47d9821d03d4c6ad42ed6312d8a2 Mon Sep 17 00:00:00 2001 From: Gordon Smith Date: Fri, 13 Sep 2024 13:01:31 +0100 Subject: [PATCH 9/9] HPCC-32663 New TypeScript Error Bumped tsc version highlighting new error Signed-off-by: Gordon Smith --- esp/src/src-react/hooks/platform.ts | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/esp/src/src-react/hooks/platform.ts b/esp/src/src-react/hooks/platform.ts index 8e90b4ab363..66dac5d9487 100644 --- a/esp/src/src-react/hooks/platform.ts +++ b/esp/src/src-react/hooks/platform.ts @@ -137,8 +137,14 @@ export function useCheckFeatures(): Features { timestamp }; } - -const fetchReleases = () => { +interface OctokitRelease { + id: number; + draft: boolean; + prerelease: boolean; + tag_name: string; + html_url: string; +} +const fetchReleases = (): Promise<{ data: OctokitRelease[] }> => { const octokit = new Octokit({}); return octokit.request("GET /repos/{owner}/{repo}/releases", { owner: "hpcc-systems", @@ -149,16 +155,12 @@ const fetchReleases = () => { } }); }; -type ReleasesPromise = ReturnType; -type ReleasesResponse = Awaited; -type Releases = ReleasesResponse["data"]; -type Release = Releases[number]; -const _fetchLatestReleases = (): Promise => { +const _fetchLatestReleases = (): Promise => { return fetchReleases().then(response => { - const latest: { [id: string]: Release } = response.data + const latest: { [releaseID: string]: OctokitRelease } = response.data .filter(release => !release.draft || !release.prerelease) - .reduce((prev, curr: Release) => { + .reduce((prev, curr: OctokitRelease) => { const versionParts = curr.tag_name.split("."); versionParts.length = 2; const partialVersion = versionParts.join("."); @@ -167,14 +169,14 @@ const _fetchLatestReleases = (): Promise => { } return prev; }, {}); - return Object.values(latest) as Releases; + return Object.values(latest); }).catch(err => { logger.error(err); - return [] as Releases; + return []; }); }; -let releasesPromise: Promise | undefined; -export const fetchLatestReleases = (): Promise => { +let releasesPromise: Promise | undefined; +export const fetchLatestReleases = (): Promise => { if (!releasesPromise) { releasesPromise = _fetchLatestReleases(); }