WorkunitDetails.tsx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. import * as React from "react";
  2. import { CommandBar, ContextualMenuItemType, ICommandBarItemProps, mergeStyleSets, Pivot, PivotItem, ScrollablePane, ScrollbarVisibility, Sticky, StickyPositionType } from "@fluentui/react";
  3. import { SizeMe } from "react-sizeme";
  4. import { ReflexContainer, ReflexSplitter, ReflexElement } from "react-reflex";
  5. import nlsHPCC from "src/nlsHPCC";
  6. import { getImageURL } from "src/Utility";
  7. import { getStateIconClass } from "src/ESPWorkunit";
  8. import { WUStatus } from "src/react/index";
  9. import { useWorkunit } from "../hooks/Workunit";
  10. import { DojoAdapter } from "../layouts/DojoAdapter";
  11. import { pushUrl } from "../util/history";
  12. import { ShortVerticalDivider } from "./Common";
  13. import { Results } from "./Results";
  14. import { Variables } from "./Variables";
  15. import { SourceFiles } from "./SourceFiles";
  16. import { Details } from "./Details";
  17. import "react-reflex/styles.css";
  18. const classNames = mergeStyleSets({
  19. reflexScrollPane: {
  20. borderWidth: 1,
  21. borderStyle: "solid",
  22. borderColor: "darkgray"
  23. },
  24. reflexPane: {
  25. borderWidth: 1,
  26. borderStyle: "solid",
  27. borderColor: "darkgray",
  28. overflow: "hidden"
  29. },
  30. reflexSplitter: {
  31. position: "relative",
  32. height: "5px",
  33. backgroundColor: "transparent",
  34. borderStyle: "none"
  35. },
  36. reflexSplitterDiv: {
  37. fontFamily: "Lucida Sans,Lucida Grande,Arial !important",
  38. fontSize: "13px !important",
  39. cursor: "row-resize",
  40. position: "absolute",
  41. left: "49%",
  42. background: "#9e9e9e",
  43. height: "1px",
  44. top: "2px",
  45. width: "19px"
  46. }
  47. });
  48. const pivotItemStyle = (size, padding: number = 4) => {
  49. if (isNaN(size.width)) {
  50. return { position: "absolute", padding: `${padding}px`, overflow: "auto", zIndex: 0 } as React.CSSProperties;
  51. }
  52. return { position: "absolute", padding: `${padding}px`, overflow: "auto", zIndex: 0, width: size.width - padding * 2, height: size.height - 45 - padding * 2 } as React.CSSProperties;
  53. };
  54. interface InfoGridProps {
  55. wuid: string;
  56. dimensions?: any;
  57. }
  58. const InfoGrid: React.FunctionComponent<InfoGridProps> = ({
  59. wuid,
  60. dimensions
  61. }) => {
  62. return <div className="pane-content" style={{ height: dimensions.height }}>
  63. <DojoAdapter widgetClassID="InfoGridWidget" params={{ Wuid: wuid }} delayProps={{ showToolbar: true }} />
  64. </div>;
  65. };
  66. interface WorkunitDetailsProps {
  67. wuid: string;
  68. tab?: string;
  69. }
  70. export const WorkunitDetails: React.FunctionComponent<WorkunitDetailsProps> = ({
  71. wuid,
  72. tab = "summary"
  73. }) => {
  74. const [workunit] = useWorkunit(wuid, true);
  75. const [jobname, setJobname] = React.useState("");
  76. const [description, setDescription] = React.useState("");
  77. const [_protected, setProtected] = React.useState(false);
  78. React.useEffect(() => {
  79. setJobname(jobname || workunit?.Jobname);
  80. setDescription(description || workunit?.Description);
  81. setProtected(_protected || workunit?.Protected);
  82. // eslint-disable-next-line react-hooks/exhaustive-deps
  83. }, [workunit?.Jobname, workunit?.Jobname, workunit?.Jobname]);
  84. const canSave = workunit && (
  85. jobname !== workunit.Jobname ||
  86. description !== workunit.Description ||
  87. _protected !== workunit.Protected
  88. );
  89. const buttons: ICommandBarItemProps[] = [
  90. {
  91. key: "refresh", text: nlsHPCC.Refresh, iconProps: { iconName: "Refresh" },
  92. onClick: () => {
  93. workunit.refresh();
  94. }
  95. },
  96. { key: "divider_1", itemType: ContextualMenuItemType.Divider, onRender: () => <ShortVerticalDivider /> },
  97. {
  98. key: "save", text: nlsHPCC.Save, iconProps: { iconName: "Save" }, disabled: !canSave,
  99. onClick: () => {
  100. workunit?.update({
  101. Jobname: jobname,
  102. Description: description,
  103. Protected: _protected
  104. });
  105. }
  106. },
  107. {
  108. key: "copy", text: nlsHPCC.CopyWUID, iconProps: { iconName: "Copy" },
  109. onClick: () => {
  110. navigator?.clipboard?.writeText(wuid);
  111. }
  112. },
  113. { key: "divider_2", itemType: ContextualMenuItemType.Divider, onRender: () => <ShortVerticalDivider /> },
  114. ];
  115. const protectedImage = getImageURL(workunit?.Protected ? "locked.png" : "unlocked.png");
  116. const stateIconClass = getStateIconClass(workunit?.StateID, workunit?.isComplete(), workunit?.Archived);
  117. const serviceNames = workunit?.ServiceNames?.Item?.join("\n") || "";
  118. return <SizeMe monitorHeight>{({ size }) =>
  119. <Pivot overflowBehavior="menu" style={{ height: "100%" }} defaultSelectedKey={tab} onLinkClick={evt => pushUrl(`/workunits/${wuid}/${evt.props.itemKey}`)}>
  120. <PivotItem headerText={wuid} itemKey="summary" style={pivotItemStyle(size)}>
  121. <div style={{ height: "100%", position: "relative" }}>
  122. <ReflexContainer orientation="horizontal">
  123. <ReflexElement className={classNames.reflexScrollPane}>
  124. <div className="pane-content">
  125. <ScrollablePane scrollbarVisibility={ScrollbarVisibility.auto}>
  126. <Sticky stickyPosition={StickyPositionType.Header}>
  127. <CommandBar items={buttons} />
  128. </Sticky>
  129. <Sticky stickyPosition={StickyPositionType.Header}>
  130. <div style={{ display: "inline-block" }}>
  131. <h2>
  132. <img src={protectedImage} />&nbsp;<div className={stateIconClass}></div>&nbsp;<span className="bold">{wuid}</span>
  133. </h2>
  134. </div>
  135. <div style={{ width: "512px", height: "64px", float: "right" }}>
  136. <WUStatus wuid={wuid}></WUStatus>
  137. </div>
  138. </Sticky>
  139. <Details fields={{
  140. "wuid": { label: nlsHPCC.WUID, type: "string", value: wuid, readonly: true },
  141. "action": { label: nlsHPCC.Action, type: "string", value: workunit?.ActionEx, readonly: true },
  142. "state": { label: nlsHPCC.State, type: "string", value: workunit?.State, readonly: true },
  143. "owner": { label: nlsHPCC.Owner, type: "string", value: workunit?.Owner, readonly: true },
  144. "jobname": { label: nlsHPCC.JobName, type: "string", value: jobname },
  145. "description": { label: nlsHPCC.Description, type: "string", value: description },
  146. "protected": { label: nlsHPCC.Protected, type: "checkbox", value: _protected },
  147. "cluster": { label: nlsHPCC.Cluster, type: "string", value: workunit?.Cluster, readonly: true },
  148. "totalClusterTime": { label: nlsHPCC.TotalClusterTime, type: "string", value: workunit?.TotalClusterTime, readonly: true },
  149. "abortedBy": { label: nlsHPCC.AbortedBy, type: "string", value: workunit?.AbortBy, readonly: true },
  150. "abortedTime": { label: nlsHPCC.AbortedTime, type: "string", value: workunit?.AbortTime, readonly: true },
  151. "ServiceNamesCustom": { label: nlsHPCC.Services, type: "string", value: serviceNames, readonly: true, multiline: true },
  152. }} onChange={(id, value) => {
  153. switch (id) {
  154. case "jobname":
  155. setJobname(value);
  156. break;
  157. case "description":
  158. setDescription(value);
  159. break;
  160. case "protected":
  161. setProtected(value);
  162. break;
  163. default:
  164. console.log(id, value);
  165. }
  166. }} />
  167. </ScrollablePane>
  168. </div>
  169. </ReflexElement>
  170. <ReflexSplitter style={{ position: "relative", height: "5px", backgroundColor: "transparent", borderStyle: "none" }}>
  171. <div className={classNames.reflexSplitterDiv}></div>
  172. </ReflexSplitter>
  173. <ReflexElement propagateDimensions={true} className={classNames.reflexPane}>
  174. <InfoGrid wuid={wuid} />
  175. </ReflexElement>
  176. </ReflexContainer>
  177. </div>
  178. </PivotItem>
  179. <PivotItem headerText={nlsHPCC.Variables} itemCount={(workunit?.VariableCount || 0) + (workunit?.ApplicationValueCount || 0) + (workunit?.DebugValueCount || 0)} itemKey="variables" style={pivotItemStyle(size, 0)}>
  180. <Variables wuid={wuid} />
  181. </PivotItem>
  182. <PivotItem headerText={nlsHPCC.Outputs} itemKey="outputs" itemCount={workunit?.ResultCount} style={pivotItemStyle(size, 0)}>
  183. <Results wuid={wuid} />
  184. </PivotItem>
  185. <PivotItem headerText={nlsHPCC.Inputs} itemKey="inputs" itemCount={workunit?.SourceFileCount} style={pivotItemStyle(size, 0)}>
  186. <SourceFiles wuid={wuid} />
  187. </PivotItem>
  188. <PivotItem headerText={nlsHPCC.Timers} itemKey="timers" itemCount={workunit?.TimerCount} style={pivotItemStyle(size, 0)}>
  189. <DojoAdapter widgetClassID="TimingPageWidget" params={{ Wuid: wuid }} />
  190. </PivotItem>
  191. <PivotItem headerText={nlsHPCC.Graphs} itemKey="graphs" itemCount={workunit?.GraphCount} style={pivotItemStyle(size, 0)}>
  192. <DojoAdapter widgetClassID="GraphsWUWidget" params={{ Wuid: wuid }} />
  193. </PivotItem>
  194. <PivotItem headerText={nlsHPCC.Workflows} itemKey="workflows" itemCount={workunit?.WorkflowCount} style={pivotItemStyle(size, 0)}>
  195. <DojoAdapter widgetClassID="WorkflowsWidget" params={{ Wuid: wuid }} />
  196. </PivotItem>
  197. <PivotItem headerText={nlsHPCC.Queries} itemIcon="Search" itemKey="queries" style={pivotItemStyle(size, 0)}>
  198. <DojoAdapter widgetClassID="QuerySetQueryWidget" params={{ Wuid: wuid }} />
  199. </PivotItem>
  200. <PivotItem headerText={nlsHPCC.Resources} itemKey="resources" style={pivotItemStyle(size, 0)}>
  201. <DojoAdapter widgetClassID="ResourcesWidget" params={{ Wuid: wuid }} />
  202. </PivotItem>
  203. <PivotItem headerText={nlsHPCC.Helpers} itemKey="helpers" itemCount={workunit?.HelpersCount} style={pivotItemStyle(size, 0)}>
  204. <DojoAdapter widgetClassID="HelpersWidget" params={{ Wuid: wuid }} />
  205. </PivotItem>
  206. <PivotItem headerText={nlsHPCC.ECL} itemKey="eclsummary" style={pivotItemStyle(size, 0)}>
  207. <DojoAdapter widgetClassID="ECLArchiveWidget" params={{ Wuid: wuid }} />
  208. </PivotItem>
  209. <PivotItem headerText={nlsHPCC.XML} itemKey="xml" style={pivotItemStyle(size, 0)}>
  210. <DojoAdapter widgetClassID="ECLSourceWidget" params={{ Wuid: wuid }} delayProps={{ WUXml: true }} />
  211. </PivotItem>
  212. </Pivot>
  213. }</SizeMe>;
  214. };