From c91caf3fa30ec98853b4f5cda6ef35bbba064d3e Mon Sep 17 00:00:00 2001
From: Scott Willrich <swillrich@gmail.com>
Date: Wed, 29 Sep 2021 10:34:08 -0500
Subject: [PATCH] task/FP-1102 - Add ability to compress folder to folder
 download message modal (#499)

* Updated DataFilesDownloadMessageModal.js to compress folders for download

* added fixture and updated DataFilesDownloadMessageModal.test.js

* fixed linting errors

* fixed one more linting error

* Added styleName to match recent changes to DataFileCompressModal.js

Co-authored-by: Sal Tijerina <r.sal.tijerina@gmail.com>
---
 .../DataFilesDownloadMessageModal.js          | 195 +++++++++++++++---
 .../DataFilesDownloadMessageModal.fixture.js  |  37 ++++
 .../DataFilesDownloadMessageModal.test.js     |   7 +-
 3 files changed, 209 insertions(+), 30 deletions(-)
 create mode 100644 client/src/components/DataFiles/DataFilesModals/tests/DataFilesDownloadMessageModal.fixture.js

diff --git a/client/src/components/DataFiles/DataFilesModals/DataFilesDownloadMessageModal.js b/client/src/components/DataFiles/DataFilesModals/DataFilesDownloadMessageModal.js
index c22777245..82cbccb1d 100644
--- a/client/src/components/DataFiles/DataFilesModals/DataFilesDownloadMessageModal.js
+++ b/client/src/components/DataFiles/DataFilesModals/DataFilesDownloadMessageModal.js
@@ -1,13 +1,102 @@
-import React from 'react';
-import { useSelector, useDispatch } from 'react-redux';
-import { Button, Modal, ModalHeader, ModalBody, ModalFooter } from 'reactstrap';
-import { Icon } from '_common';
-import './DataFilesDownloadMessageModal.module.scss';
+import React, { useMemo } from 'react';
+import { useSelector, useDispatch, shallowEqual } from 'react-redux';
+import {
+  Button,
+  Modal,
+  ModalHeader,
+  ModalBody,
+  ModalFooter,
+  Input,
+  InputGroupAddon
+} from 'reactstrap';
+import {
+  LoadingSpinner,
+  FormField,
+  Icon,
+  InlineMessage,
+  SectionMessage
+} from '_common';
+import { useHistory, useLocation } from 'react-router-dom';
+import { Formik, Form } from 'formik';
+import * as yup from 'yup';
+import './DataFilesCompressModal.module.scss';
 
 const DataFilesDownloadMessageModal = () => {
+  const history = useHistory();
+  const location = useLocation();
+  const dispatch = useDispatch();
+  const status = useSelector(
+    state => state.files.operationStatus.compress,
+    shallowEqual
+  );
+
   const isOpen = useSelector(state => state.files.modals.downloadMessage);
 
-  const dispatch = useDispatch();
+  const params = useSelector(
+    state => state.files.params.FilesListing,
+    shallowEqual
+  );
+
+  const selectedFiles = useSelector(
+    ({ files: { selected, listing } }) =>
+      selected.FilesListing.map(i => ({
+        ...listing.FilesListing[i]
+      })),
+    shallowEqual
+  );
+  const selected = useMemo(() => selectedFiles, [isOpen]);
+  const formRef = React.useRef();
+
+  const onOpened = () => {
+    dispatch({
+      type: 'FETCH_FILES_MODAL',
+      payload: { ...params, section: 'modal' }
+    });
+  };
+
+  const onClosed = () => {
+    dispatch({ type: 'DATA_FILES_MODAL_CLOSE' });
+    if (status) {
+      dispatch({
+        type: 'DATA_FILES_SET_OPERATION_STATUS',
+        payload: { status: {}, operation: 'compress' }
+      });
+      history.push(location.pathname);
+    }
+  };
+
+  const compressCallback = () => {
+    const { filenameDisplay, filetype } = formRef.current.values;
+    const filename = `${filenameDisplay}${filetype}`;
+    dispatch({
+      type: 'DATA_FILES_COMPRESS',
+      payload: { filename, files: selected }
+    });
+  };
+
+  let buttonIcon;
+  if (status === 'RUNNING') {
+    buttonIcon = <LoadingSpinner placement="inline" />;
+  } else if (status === 'ERROR') {
+    buttonIcon = <Icon name="alert" />;
+  } else {
+    buttonIcon = null;
+  }
+  const initialValues = {
+    filenameDisplay:
+      selectedFiles[0] && selectedFiles.length === 1
+        ? selectedFiles[0].name
+        : '',
+    filetype: '.zip'
+  };
+  const validationSchema = yup.object().shape({
+    filenameDisplay: yup
+      .string()
+      .trim('The filename cannot include leading and trailing spaces')
+      .strict(true)
+      .required('The filename is required')
+  });
+
   const toggle = () => {
     dispatch({
       type: 'DATA_FILES_TOGGLE_MODAL',
@@ -16,27 +105,83 @@ const DataFilesDownloadMessageModal = () => {
   };
 
   return (
-    <Modal isOpen={isOpen} toggle={toggle} size="md" className="dataFilesModal">
+    <Modal
+      isOpen={isOpen}
+      onOpened={onOpened}
+      onClosed={onClosed}
+      toggle={toggle}
+      className="dataFilesModal"
+    >
       <ModalHeader toggle={toggle} charCode="&#xe912;">
-        Download
+        Download Folder
       </ModalHeader>
-      <ModalBody>
-        <p styleName="title">Folders must be compressed before download.</p>
-        <ol>
-          <li>Select the folder(s).</li>
-          <li>
-            Press the &quot;
-            <Icon name="compress">↩</Icon> Compress&quot; icon above the table.
-          </li>
-          <li>Complete and submit the form.</li>
-          <li>After the job finishes, you may download the compressed file.</li>
-        </ol>
-      </ModalBody>
-      <ModalFooter>
-        <Button type="button" className="data-files-btn" onClick={toggle}>
-          Close
-        </Button>
-      </ModalFooter>
+      <Formik
+        innerRef={formRef}
+        initialValues={initialValues}
+        validationSchema={validationSchema}
+        onSubmit={compressCallback}
+      >
+        {({ setFieldValue, values, isValid }) => {
+          const handleSelectChange = e => {
+            setFieldValue('filetype', e.target.value);
+          };
+          const formDisabled = status === 'RUNNING' || status === 'SUCCESS';
+          const buttonDisabled =
+            formDisabled || !isValid || values.filenameDisplay === '';
+          return (
+            <Form>
+              <ModalBody>
+                <SectionMessage type="warning">
+                  Folders must be compressed before downloading
+                </SectionMessage>
+                <FormField
+                  label="Compressed File Name"
+                  name="filenameDisplay"
+                  disabled={formDisabled}
+                  addonType="append"
+                  addon={
+                    <InputGroupAddon addonType="append" styleName="input-field">
+                      <Input
+                        type="select"
+                        name="filetype"
+                        bsSize="sm"
+                        onChange={handleSelectChange}
+                        disabled={formDisabled}
+                      >
+                        <option value=".zip">.zip</option>
+                        <option value=".tar.gz">.tar.gz</option>
+                      </Input>
+                    </InputGroupAddon>
+                  }
+                />
+                <p>
+                  <em>
+                    A job to compress this folder will be submitted. The
+                    compressed file will appear in this directory for you to
+                    download.
+                  </em>
+                </p>
+              </ModalBody>
+              <ModalFooter>
+                <InlineMessage isVisible={status === 'SUCCESS'} type="success">
+                  Successfully started compress job
+                </InlineMessage>
+                <Button
+                  className="data-files-btn"
+                  disabled={buttonDisabled}
+                  styleName="submit-button"
+                  type="submit"
+                >
+                  {buttonIcon}
+                  <span styleName={buttonIcon ? 'with-icon' : ''}>
+                    Compress
+                  </span>
+                </Button>
+              </ModalFooter>
+            </Form>
+          );
+        }}
+      </Formik>
     </Modal>
   );
 };
diff --git a/client/src/components/DataFiles/DataFilesModals/tests/DataFilesDownloadMessageModal.fixture.js b/client/src/components/DataFiles/DataFilesModals/tests/DataFilesDownloadMessageModal.fixture.js
new file mode 100644
index 000000000..a93e12873
--- /dev/null
+++ b/client/src/components/DataFiles/DataFilesModals/tests/DataFilesDownloadMessageModal.fixture.js
@@ -0,0 +1,37 @@
+const DataFilesDownloadMessageModalFixture = {
+  operationStatus: {
+    copy: {},
+    compress: {}
+  },
+  listing: {
+    FilesListing: {
+      name: 'testfile',
+      path: '/testfile',
+      lastModified: '2020-07-01T10:12:36-05:00',
+      length: 4096,
+      permissions: 'ALL',
+      format: 'folder',
+      system: 'test.system',
+      mimeType: 'text/directory',
+      type: 'dir'
+    }
+  },
+  params: {
+    FilesListing: {
+      api: 'tapis',
+      scheme: 'private',
+      system: 'test.system',
+      path: ''
+    }
+  },
+  selected: {
+    FilesListing: [0],
+    modal: []
+  },
+  modals: {
+    copy: true,
+    downloadMessage: true
+  }
+};
+
+export default DataFilesDownloadMessageModalFixture;
diff --git a/client/src/components/DataFiles/DataFilesModals/tests/DataFilesDownloadMessageModal.test.js b/client/src/components/DataFiles/DataFilesModals/tests/DataFilesDownloadMessageModal.test.js
index 3afb9154c..164cb3d3f 100644
--- a/client/src/components/DataFiles/DataFilesModals/tests/DataFilesDownloadMessageModal.test.js
+++ b/client/src/components/DataFiles/DataFilesModals/tests/DataFilesDownloadMessageModal.test.js
@@ -1,15 +1,12 @@
 import React from 'react';
 import configureStore from 'redux-mock-store';
 import renderComponent from 'utils/testing';
+import DataFilesDownloadMessageModalFixture from './DataFilesDownloadMessageModal.fixture';
 import DataFilesDownloadMessageModal from '../DataFilesDownloadMessageModal';
 
 const mockStore = configureStore();
 const initialMockState = {
-  files: {
-    modals: {
-      downloadMessage: true
-    }
-  }
+  files: DataFilesDownloadMessageModalFixture
 };
 
 describe('DataFilesDownloadMessageModal', () => {