LandingZone.tsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. import * as React from "react";
  2. import { CommandBar, ContextualMenuItemType, ICommandBarItemProps, mergeStyleSets } from "@fluentui/react";
  3. import { useConst, useOnEvent } from "@fluentui/react-hooks";
  4. import * as domClass from "dojo/dom-class";
  5. import * as iframe from "dojo/request/iframe";
  6. import * as put from "put-selector/put";
  7. import { TpDropZoneQuery } from "src/WsTopology";
  8. import * as FileSpray from "src/FileSpray";
  9. import * as ESPRequest from "src/ESPRequest";
  10. import * as Utility from "src/Utility";
  11. import nlsHPCC from "src/nlsHPCC";
  12. import { useGrid } from "../hooks/grid";
  13. import { HolyGrail } from "../layouts/HolyGrail";
  14. import { pushParams } from "../util/history";
  15. import { ShortVerticalDivider } from "./Common";
  16. import { selector, tree } from "./DojoGrid";
  17. import { Fields } from "./forms/Fields";
  18. import { Filter } from "./forms/Filter";
  19. import { AddFileForm } from "./forms/landing-zone/AddFileForm";
  20. import { BlobImportForm } from "./forms/landing-zone/BlobImportForm";
  21. import { DelimitedImportForm } from "./forms/landing-zone/DelimitedImportForm";
  22. import { FixedImportForm } from "./forms/landing-zone/FixedImportForm";
  23. import { JsonImportForm } from "./forms/landing-zone/JsonImportForm";
  24. import { VariableImportForm } from "./forms/landing-zone/VariableImportForm";
  25. import { XmlImportForm } from "./forms/landing-zone/XmlImportForm";
  26. import { FileListForm } from "./forms/landing-zone/FileListForm";
  27. function formatQuery(targetDropzones, filter) {
  28. const dropzones = targetDropzones.filter(row => row.Name === filter?.DropZoneName);
  29. const machines = targetDropzones[0]?.TpMachines?.TpMachine?.filter(row => row.ConfigNetaddress === filter?.Server);
  30. return {
  31. id: "*",
  32. filter: (filter?.DropZoneName && dropzones.length && machines.length) ? {
  33. filter: {
  34. DropZoneName: filter.DropZoneName,
  35. Server: filter.Server,
  36. NameFilter: filter.NameFilter,
  37. ECLWatchVisibleOnly: true
  38. },
  39. ECLWatchVisibleOnly: true,
  40. __dropZone: {
  41. ...targetDropzones.filter(row => row.Name === filter?.DropZoneName)[0],
  42. machine: machines[0]
  43. }
  44. } : undefined
  45. };
  46. }
  47. const FilterFields: Fields = {
  48. "DropZoneName": { type: "target-dropzone", label: nlsHPCC.DropZone },
  49. "Server": { type: "target-server", label: nlsHPCC.Server },
  50. "NameFilter": { type: "string", label: nlsHPCC.FileName, placeholder: nlsHPCC.somefile },
  51. };
  52. interface LandingZoneFilter {
  53. DropZoneName?: string;
  54. Server?: string;
  55. NameFilter?: string;
  56. ECLWatchVisibleOnly?: boolean;
  57. __dropZone?: any;
  58. }
  59. const emptyFilter: LandingZoneFilter = {};
  60. interface LandingZoneProps {
  61. filter?: LandingZoneFilter;
  62. }
  63. export const LandingZone: React.FunctionComponent<LandingZoneProps> = ({
  64. filter = emptyFilter
  65. }) => {
  66. const [showFilter, setShowFilter] = React.useState(false);
  67. const [showAddFile, setShowAddFile] = React.useState(false);
  68. const [showFixed, setShowFixed] = React.useState(false);
  69. const [showDelimited, setShowDelimited] = React.useState(false);
  70. const [showXml, setShowXml] = React.useState(false);
  71. const [showJson, setShowJson] = React.useState(false);
  72. const [showVariable, setShowVariable] = React.useState(false);
  73. const [showBlob, setShowBlob] = React.useState(false);
  74. const [showDropZone, setShowDropzone] = React.useState(false);
  75. const [uploadFiles, setUploadFiles] = React.useState([]);
  76. const [showFileUpload, setShowFileUpload] = React.useState(false);
  77. const [targetDropzones, setTargetDropzones] = React.useState([]);
  78. React.useEffect(() => {
  79. TpDropZoneQuery({}).then(({ TpDropZoneQueryResponse }) => {
  80. setTargetDropzones(TpDropZoneQueryResponse?.TpDropZones?.TpDropZone || []);
  81. });
  82. }, []);
  83. // Grid ---
  84. const store = useConst(FileSpray.CreateLandingZonesStore({}));
  85. const [Grid, selection, refreshTable, copyButtons] = useGrid({
  86. store,
  87. query: formatQuery(targetDropzones, filter),
  88. sort: [{ attribute: "modifiedtime", "descending": true }],
  89. filename: "landingZones",
  90. getSelected: function () {
  91. if (filter?.__dropZone) {
  92. return this.inherited(arguments, [FileSpray.CreateLandingZonesFilterStore({})]);
  93. }
  94. return this.inherited(arguments, [FileSpray.CreateFileListStore({})]);
  95. },
  96. columns: {
  97. col1: selector({
  98. width: 27,
  99. disabled: function (item) {
  100. if (item.type) {
  101. switch (item.type) {
  102. case "dropzone":
  103. case "folder":
  104. case "machine":
  105. return true;
  106. }
  107. }
  108. return false;
  109. },
  110. selectorType: "checkbox"
  111. }),
  112. displayName: tree({
  113. label: nlsHPCC.Name,
  114. sortable: false,
  115. formatter: function (_name, row) {
  116. let img = "";
  117. let name = row.displayName;
  118. if (row.isDir === undefined) {
  119. img = Utility.getImageHTML("server.png");
  120. name += " [" + row.Path + "]";
  121. } else if (row.isMachine) {
  122. img = Utility.getImageHTML("machine.png");
  123. } else if (row.isDir) {
  124. img = Utility.getImageHTML("folder.png");
  125. } else {
  126. img = Utility.getImageHTML("file.png");
  127. }
  128. return img + "&nbsp;" + name;
  129. },
  130. renderExpando: function (level, hasChildren, expanded, object) {
  131. const dir = this.grid.isRTL ? "right" : "left";
  132. let cls = ".dgrid-expando-icon";
  133. if (hasChildren) {
  134. cls += ".ui-icon.ui-icon-triangle-1-" + (expanded ? "se" : "e");
  135. }
  136. //@ts-ignore
  137. const node = put("div" + cls + "[style=margin-" + dir + ": " + (level * (this.indentWidth || 9)) + "px; float: " + dir + "; margin-top: 3px]");
  138. node.innerHTML = "&nbsp;";
  139. return node;
  140. }
  141. }),
  142. filesize: {
  143. label: nlsHPCC.Size, width: 100,
  144. renderCell: function (object, value, node, options) {
  145. domClass.add(node, "justify-right");
  146. node.innerText = Utility.convertedSize(value);
  147. },
  148. },
  149. modifiedtime: { label: nlsHPCC.Date, width: 162 }
  150. }
  151. });
  152. // Command Bar ---
  153. const buttons = React.useMemo((): ICommandBarItemProps[] => [
  154. {
  155. key: "refresh", text: nlsHPCC.Refresh, iconProps: { iconName: "Refresh" },
  156. onClick: () => refreshTable()
  157. },
  158. { key: "divider_1", itemType: ContextualMenuItemType.Divider, onRender: () => <ShortVerticalDivider /> },
  159. {
  160. key: "preview", text: nlsHPCC.Preview, disabled: !selection.length, iconProps: { iconName: "ComplianceAudit" },
  161. onClick: () => {
  162. if (selection.length === 1) {
  163. window.location.href = `#/landingzone/preview/${selection[0].getLogicalFile()}`;
  164. }
  165. }
  166. },
  167. { key: "divider_2", itemType: ContextualMenuItemType.Divider, onRender: () => <ShortVerticalDivider /> },
  168. {
  169. key: "upload", text: nlsHPCC.Upload, iconProps: { iconName: "Upload" },
  170. onClick: () => {
  171. document.getElementById("uploaderBtn").click();
  172. }
  173. },
  174. {
  175. key: "download", text: nlsHPCC.Download, disabled: !selection.length, iconProps: { iconName: "Download" },
  176. onClick: () => {
  177. selection.forEach(item => {
  178. const downloadIframeName = "downloadIframe_" + item.calculatedID;
  179. const frame = iframe.create(downloadIframeName);
  180. const url = ESPRequest.getBaseURL("FileSpray") + "/DownloadFile?Name=" + encodeURIComponent(item.name) + "&NetAddress=" + item.NetAddress + "&Path=" + encodeURIComponent(item.fullFolderPath) + "&OS=" + item.OS;
  181. iframe.setSrc(frame, url, true);
  182. });
  183. }
  184. },
  185. {
  186. key: "delete", text: nlsHPCC.Delete, disabled: !selection.length, iconProps: { iconName: "Delete" },
  187. onClick: () => {
  188. const list = selection.map(s => s.name);
  189. if (confirm(nlsHPCC.DeleteSelectedFiles + "\n" + list)) {
  190. selection.forEach((item, idx) => {
  191. if (item._isUserFile) {
  192. store.removeUserFile(item);
  193. refreshTable(true);
  194. } else {
  195. FileSpray.DeleteDropZoneFile({
  196. request: {
  197. NetAddress: item.NetAddress,
  198. Path: item.fullFolderPath,
  199. OS: item.OS,
  200. Names: item.name
  201. },
  202. load: function (response) {
  203. refreshTable(true);
  204. }
  205. });
  206. }
  207. });
  208. }
  209. }
  210. },
  211. { key: "divider_3", itemType: ContextualMenuItemType.Divider, onRender: () => <ShortVerticalDivider /> },
  212. {
  213. key: "filter", text: nlsHPCC.Filter, iconProps: { iconName: "Filter" },
  214. onClick: () => setShowFilter(true)
  215. },
  216. { key: "divider_4", itemType: ContextualMenuItemType.Divider, onRender: () => <ShortVerticalDivider /> },
  217. {
  218. key: "addFile", text: nlsHPCC.AddFile,
  219. onClick: () => setShowAddFile(true)
  220. },
  221. { key: "divider_5", itemType: ContextualMenuItemType.Divider, onRender: () => <ShortVerticalDivider /> },
  222. {
  223. key: "fixed", text: nlsHPCC.Fixed, disabled: !selection.length,
  224. onClick: () => setShowFixed(true)
  225. },
  226. {
  227. key: "delimited", text: nlsHPCC.Delimited, disabled: !selection.length,
  228. onClick: () => setShowDelimited(true)
  229. },
  230. {
  231. key: "xml", text: nlsHPCC.XML, disabled: !selection.length,
  232. onClick: () => setShowXml(true)
  233. },
  234. {
  235. key: "json", text: nlsHPCC.JSON, disabled: !selection.length,
  236. onClick: () => setShowJson(true)
  237. },
  238. {
  239. key: "variable", text: nlsHPCC.Variable, disabled: !selection.length,
  240. onClick: () => setShowVariable(true)
  241. },
  242. {
  243. key: "blob", text: nlsHPCC.Blob, disabled: !selection.length,
  244. onClick: () => setShowBlob(true)
  245. },
  246. { key: "divider_6", itemType: ContextualMenuItemType.Divider, onRender: () => <ShortVerticalDivider /> }
  247. ], [store, refreshTable, selection]);
  248. // Filter ---
  249. const filterFields: Fields = {};
  250. for (const field in FilterFields) {
  251. filterFields[field] = { ...FilterFields[field], value: filter[field] };
  252. }
  253. const dropStyles = React.useMemo(() => mergeStyleSets({
  254. dzWrapper: {
  255. position: "absolute",
  256. top: "118px",
  257. bottom: 0,
  258. background: "rgba(0, 0, 0, 0.12)",
  259. left: "240px",
  260. right: 0,
  261. display: showDropZone ? "block" : "none",
  262. zIndex: 1,
  263. },
  264. dzInner: {
  265. position: "absolute",
  266. background: "white",
  267. top: "20px",
  268. left: "30px",
  269. right: "40px",
  270. height: "80px",
  271. display: "flex",
  272. alignItems: "center",
  273. justifyContent: "center",
  274. border: "1px solid #aaa",
  275. selectors: {
  276. p: {
  277. fontSize: "1.333rem",
  278. color: "#aaa"
  279. }
  280. }
  281. },
  282. displayNone: {
  283. display: "none"
  284. }
  285. }), [showDropZone]);
  286. const handleFileDragEnter = React.useCallback((evt) => {
  287. evt.preventDefault();
  288. evt.stopPropagation();
  289. setShowDropzone(true);
  290. }, [setShowDropzone]);
  291. useOnEvent(document, "dragenter", handleFileDragEnter);
  292. const handleFileDragOver = React.useCallback((evt) => {
  293. evt.preventDefault();
  294. evt.stopPropagation();
  295. evt.dataTransfer.dropEffect = "copy";
  296. }, []);
  297. const handleFileDrop = React.useCallback((evt) => {
  298. evt.preventDefault();
  299. evt.stopPropagation();
  300. setShowDropzone(false);
  301. const files = [...evt.dataTransfer.files];
  302. setUploadFiles(files);
  303. setShowFileUpload(true);
  304. }, [setShowDropzone, setShowFileUpload, setUploadFiles]);
  305. const handleFileSelect = React.useCallback((evt) => {
  306. evt.preventDefault();
  307. evt.stopPropagation();
  308. const files = [...evt.target.files];
  309. setUploadFiles(files);
  310. setShowFileUpload(true);
  311. }, [setShowFileUpload, setUploadFiles]);
  312. return <HolyGrail
  313. header={<CommandBar items={buttons} farItems={copyButtons} />}
  314. main={
  315. <>
  316. <input
  317. id="uploaderBtn" type="file" accept="*.txt, *.csv, *.json, *.xml"
  318. className={dropStyles.displayNone} onChange={handleFileSelect} multiple={true}
  319. />
  320. <div className={dropStyles.dzWrapper} onDragOver={handleFileDragOver} onDrop={handleFileDrop}>
  321. <div className={dropStyles.dzInner}>
  322. <p>Drop file(s) to upload.</p>
  323. </div>
  324. </div>
  325. <Grid />
  326. <Filter
  327. showFilter={showFilter} setShowFilter={setShowFilter}
  328. filterFields={filterFields} onApply={pushParams}
  329. />
  330. {uploadFiles &&
  331. <FileListForm
  332. formMinWidth={360} selection={uploadFiles}
  333. showForm={showFileUpload} setShowForm={setShowFileUpload}
  334. onSubmit={() => refreshTable()}
  335. />
  336. }
  337. <AddFileForm
  338. formMinWidth={620} store={store} refreshGrid={refreshTable}
  339. showForm={showAddFile} setShowForm={setShowAddFile}
  340. />
  341. <FixedImportForm
  342. formMinWidth={620} selection={selection}
  343. showForm={showFixed} setShowForm={setShowFixed}
  344. />
  345. <DelimitedImportForm
  346. formMinWidth={620} selection={selection}
  347. showForm={showDelimited} setShowForm={setShowDelimited}
  348. />
  349. <XmlImportForm
  350. formMinWidth={620} selection={selection}
  351. showForm={showXml} setShowForm={setShowXml}
  352. />
  353. <JsonImportForm
  354. formMinWidth={620} selection={selection}
  355. showForm={showJson} setShowForm={setShowJson}
  356. />
  357. <VariableImportForm
  358. formMinWidth={620} selection={selection}
  359. showForm={showVariable} setShowForm={setShowVariable}
  360. />
  361. <BlobImportForm
  362. formMinWidth={620} selection={selection}
  363. showForm={showBlob} setShowForm={setShowBlob}
  364. />
  365. </>
  366. }
  367. />;
  368. };