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/InfoGrid.tsx b/esp/src/src-react/components/InfoGrid.tsx index 160a0dc0271..da3a212ba8b 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; @@ -237,23 +236,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/Menu.tsx b/esp/src/src-react/components/Menu.tsx index 7f291c40944..ea6b9601d00 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]; } @@ -164,7 +165,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": [ @@ -215,8 +216,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/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/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 { 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 [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 && diff --git a/esp/src/src-react/components/forms/RenameFile.tsx b/esp/src/src-react/components/forms/RenameFile.tsx index eeedfba61a3..b003f1cc280 100644 --- a/esp/src/src-react/components/forms/RenameFile.tsx +++ b/esp/src/src-react/components/forms/RenameFile.tsx @@ -1,5 +1,5 @@ import * as React from "react"; -import { Checkbox, DefaultButton, mergeStyleSets, PrimaryButton, Stack, TextField, } from "@fluentui/react"; +import { Checkbox, DefaultButton, mergeStyleSets, PrimaryButton, Spinner, Stack, TextField, } from "@fluentui/react"; import { useConst } from "@fluentui/react-hooks"; import { useForm, Controller } from "react-hook-form"; import { FileSprayService, FileSprayStates } from "@hpcc-js/comms"; @@ -39,6 +39,8 @@ export const RenameFile: React.FunctionComponent = ({ }) => { 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()} /> }> 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(); } 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]; } 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 diff --git a/system/jlib/jlzw.cpp b/system/jlib/jlzw.cpp index 827223950b1..f289c185717 100644 --- a/system/jlib/jlzw.cpp +++ b/system/jlib/jlzw.cpp @@ -2529,7 +2529,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) { @@ -2547,6 +2547,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; }