From d9498408fc897fb65f80bb3bbd0506096f755b0e Mon Sep 17 00:00:00 2001 From: josephalexantony-aot Date: Thu, 14 Nov 2024 01:33:05 -0800 Subject: [PATCH 01/12] FWF-3834:[Feature] - Added Reusable Import Modal to MF --- .../CustomComponents/ImportModal.tsx | 331 ++++++++++++++++++ .../src/components/SvgIcons/index.tsx | 20 +- forms-flow-components/src/components/index.ts | 1 + forms-flow-theme/scss/_forms.scss | 42 ++- forms-flow-theme/scss/_mixins.scss | 6 +- forms-flow-theme/scss/_modal.scss | 68 ++++ forms-flow-theme/scss/fileUpload.scss | 8 + 7 files changed, 471 insertions(+), 5 deletions(-) create mode 100644 forms-flow-components/src/components/CustomComponents/ImportModal.tsx diff --git a/forms-flow-components/src/components/CustomComponents/ImportModal.tsx b/forms-flow-components/src/components/CustomComponents/ImportModal.tsx new file mode 100644 index 00000000..b0d0e730 --- /dev/null +++ b/forms-flow-components/src/components/CustomComponents/ImportModal.tsx @@ -0,0 +1,331 @@ +import React, { useEffect, useState } from "react"; +import Modal from "react-bootstrap/Modal"; +import ProgressBar from "react-bootstrap/ProgressBar"; +import Dropdown from "react-bootstrap/Dropdown"; +import { Translation } from "react-i18next"; +import { + CloseIcon, + UploadIcon, + SuccessIcon, + FailedIcon, + IButton, + DropdownIcon, +} from "../SvgIcons"; +import { CustomButton } from "../CustomComponents/Button"; + +// Define the types for props +interface FileItem { + form?: { + majorVersion: number; + minorVersion: number; + }; + workflow?: { + majorVersion: number; + minorVersion: number; + }; +} + +interface ProcessVersion { + majorVersion: number; + minorVersion: number; + type: string; +} + +interface ImportModalProps { + importModal: boolean; + onClose: () => void; + uploadActionType: { + IMPORT: string; + VALIDATE: string; + }; + importError: string | null; + importLoader: boolean; + formName: string; + description: string; + handleImport: (file: File, uploadActionType: string, layoutVersion: string | null, flowVersion: string | null) => void + fileItems: FileItem | null; + fileType: string; + primaryButtonText: string; + headerText: string; + processVersion: ProcessVersion | null; +} + +export const ImportModal: React.FC = React.memo(({ + importModal, + onClose, + uploadActionType, + importError, + importLoader, + formName, + description, + handleImport, + fileItems, + fileType, + primaryButtonText, + headerText, + processVersion +}) => { + const computedStyle = getComputedStyle(document.documentElement); + const redColor = computedStyle.getPropertyValue("--ff-red-000"); + const [selectedFile, setSelectedFile] = useState(null); + const [uploadProgress, setUploadProgress] = useState(0); + const [selectedLayoutVersion, setSelectedLayoutOption] = useState<{ value: any; label: string } | null>(null); + const [selectedFlowVersion, setSelectedFlowOption] = useState<{ value: any; label: string } | null>(null); + const [showFileItems, setShowFileItems] = useState(false); + const [inprogress, setInprogress] = useState(true); + + const layoutOptions = [ + { value: true, label: 'Skip, do not import' }, + { value: 'major', label: `import as version ${fileItems?.form?.majorVersion + 1}.0 (only impacts new submissions)` }, + { value: 'minor', label: `import as version ${fileItems?.form?.majorVersion}.${fileItems?.form?.minorVersion} (impacts previous and new submissions)` } + ]; + + const flowOptions = [ + { value: true, label: 'Skip, do not import' }, + { value: 'major', label: `import as version ${fileItems?.workflow?.majorVersion ?? 1}.${fileItems?.workflow?.minorVersion ?? 0} (only impacts new submissions)` } + ]; + + const handleLayoutChange = (option: { value: any; label: string }) => { + setSelectedLayoutOption(option); + }; + + const handFlowChange = (option: { value: any; label: string }) => { + setSelectedFlowOption(option); + }; + + const onUpload = (evt: React.ChangeEvent) => { + const file = evt.target.files ? evt.target.files[0] : null; + setSelectedFile(file); + }; + + const resetState = () => { + setSelectedFile(null); + setUploadProgress(0); + }; + + const closeModal = () => { + setSelectedFile(null); + setUploadProgress(0); + setSelectedLayoutOption(null); + setSelectedFlowOption(null); + setShowFileItems(false); + onClose(); + }; + + const onImport = () => { + if (selectedFile) { + handleImport(selectedFile, uploadActionType.IMPORT, + selectedLayoutVersion?.value, selectedFlowVersion?.value); + } + }; + + useEffect(() => { + if (fileItems && !importError && Object.values(fileItems).some(item => + item?.majorVersion != null || item?.minorVersion != null)) { + setShowFileItems(true); + } else if (processVersion?.majorVersion != null || processVersion?.minorVersion != null) { + setShowFileItems(true); + } else { + setShowFileItems(false); + } + }, [importError, fileItems]); + + useEffect(() => { + if (!importModal) { + closeModal(); + } + }, [importModal]); + + useEffect(() => { + let isMounted = true; + + if (selectedFile) { + handleImport( + selectedFile, + uploadActionType.VALIDATE, + selectedLayoutVersion?.value ?? null, + selectedFlowVersion?.value ?? null + ); + + let start: number | null = null; + const duration = 2000; + + const animateProgress = (timestamp: number) => { + if (!start) start = timestamp; + const progress = Math.min(((timestamp - start) / duration) * 100, 100); + + if (isMounted) { + setUploadProgress(progress); + setInprogress(progress < 100); + } + + if (progress < 100) { + requestAnimationFrame(animateProgress); + } + }; + + const animation = requestAnimationFrame(animateProgress); + + return () => { + isMounted = false; + cancelAnimationFrame(animation); + }; + } + }, [selectedFile]); + + return ( + + + + {(t) => t(headerText)} + +
+ { resetState(); closeModal(); }} /> +
+
+ + {selectedFile ? ( + <> + +
+
+

{selectedFile.name}

+ + {!importLoader && !importError && !inprogress ? ( + {(t) => t("Upload Successful")} + ) : !importLoader && importError && !inprogress ? ( + {(t) => t("Upload Failed")} + ) : inprogress ? ( + {(t) => t("Import in progress")} + ) : null} + + {!importLoader && importError ? + : !importLoader && !inprogress ? : null} +
+
{formName}
+ {!importError && description &&
{description}
} +
{importError && {importError}}
+ {importError && importError.includes("already exists") && fileType === ".json" && +
+
+ + {(t) => t("Note")} +
+
+ {(t) => t(`If you want to replace an existing form, open the form in the design menu that you want to update, click "Actions", and then click "Import".`)} +
+
} +
+ {importError && !importError.includes("already exists") && + + {(t) => t("A system error occurred during import. Please try again to import.")} + } +
+
+ {showFileItems && !importError && ( +
+
+
+ + + {(t) => t("Import will create a new version.")} + +
+
+
+
Type
+
Import
+
+ + {processVersion?.majorVersion &&
+
{processVersion.type}
+
{`Import as Version ${processVersion?.majorVersion}.${processVersion?.minorVersion} (only impacts new submissions)`}
+
} + {fileItems?.form?.majorVersion &&
+
Layout
+
+ + +
+
+ {selectedLayoutVersion ? selectedLayoutVersion.label : 'Skip, do not import'} +
+ +
+
+ + + {layoutOptions.map((option, index) => ( + handleLayoutChange(option)}> + {option.label} + + ))} + +
+
+
} + + {fileItems?.workflow?.majorVersion &&
+
Flow
+
+ + +
+
+ {selectedFlowVersion ? selectedFlowVersion.label : 'Skip, do not import'} +
+ +
+
+ + {flowOptions.map((option, index) => ( + handFlowChange(option)}> + {option.label} + + ))} + +
+
+
} +
+ )} + + ) : ( +
document.getElementById('file-input')?.click()}> + +
+ +

{(t) => t(`Click or drag a file to this area to import${fileType === ".json, .bpmn" ? " (form, layout or bpmn)" : ""}`)}

+

{(t) => t(`Support for a single ${fileType} file upload. Maximum file size 20MB.`)}

+
+
+ )} +
+ + { primaryButtonText === "Try Again" ? closeModal() : onImport(); }} + buttonLoading={!importError && importLoader} + /> + { resetState(); closeModal(); }} + /> + +
+ ); +}); + + diff --git a/forms-flow-components/src/components/SvgIcons/index.tsx b/forms-flow-components/src/components/SvgIcons/index.tsx index c944bb07..32ff4415 100644 --- a/forms-flow-components/src/components/SvgIcons/index.tsx +++ b/forms-flow-components/src/components/SvgIcons/index.tsx @@ -1,7 +1,6 @@ const computedStyle = getComputedStyle(document.documentElement); const baseColor = computedStyle.getPropertyValue("--ff-base-600"); const grayColor = computedStyle.getPropertyValue("--ff-gray-800"); - export const ChevronIcon = ({ color = baseColor, width = "10", @@ -635,3 +634,22 @@ export const TickIcon = ({ color = baseColor, ...props }) => ( strokeLinejoin="round"/> ); + +export const DropdownIcon = () => ( + + + +); + diff --git a/forms-flow-components/src/components/index.ts b/forms-flow-components/src/components/index.ts index d706dab8..2b6fde66 100644 --- a/forms-flow-components/src/components/index.ts +++ b/forms-flow-components/src/components/index.ts @@ -17,3 +17,4 @@ export * from "./CustomComponents/TableFooter"; export * from "./CustomComponents/CustomInfo"; export * from "./CustomComponents/BuildModal"; export * from "./CustomComponents/ErrorModal"; +export * from "./CustomComponents/ImportModal"; diff --git a/forms-flow-theme/scss/_forms.scss b/forms-flow-theme/scss/_forms.scss index fa976796..0a8fe2af 100644 --- a/forms-flow-theme/scss/_forms.scss +++ b/forms-flow-theme/scss/_forms.scss @@ -411,4 +411,44 @@ select option:hover { .form-preview{ pointer-events: none; cursor: not-allowed; -} \ No newline at end of file +} + +.dropdown-main { + display: block !important; + padding: 0 !important; + + .dropdown-menu.show { + width: 100% !important; + overflow: hidden !important; + padding: 0.4375rem !important; + display: block !important; + } + + .dropdown-item { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 0.875rem !important; + font-weight: var(--font-weight-sm) !important; + } + + .btn.btn-primary, + .btn.btn-success, + .btn.btn-success:hover { + width: 100%; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + background-color: $white !important; + color: $black !important; + display: flex !important; + align-items: left !important; + justify-content: start !important; + font-size: 0.875rem !important; + font-weight: var(--font-weight-sm) !important; + } + + .dropdown-toggle::after { + display: none !important; + } +} diff --git a/forms-flow-theme/scss/_mixins.scss b/forms-flow-theme/scss/_mixins.scss index 10fdf231..fabe7b7d 100644 --- a/forms-flow-theme/scss/_mixins.scss +++ b/forms-flow-theme/scss/_mixins.scss @@ -56,17 +56,17 @@ } -@mixin text-modal-content { +@mixin text-modal-content { font-size: var(--font-size-sm); font-weight: var(--font-weight-sm); - line-height: var(--text-line-height); + line-height: var(--text-line-height); color: var(--ff-gray-800); } @mixin upload-status-styles($color) { color: $color; text-align: right; - font-size: $font-size-xs; + font-size: var(--font-size-xs); font-weight: var(--font-weight-sm); line-height: var(--text-line-height); } \ No newline at end of file diff --git a/forms-flow-theme/scss/_modal.scss b/forms-flow-theme/scss/_modal.scss index d5dbb512..2f687255 100644 --- a/forms-flow-theme/scss/_modal.scss +++ b/forms-flow-theme/scss/_modal.scss @@ -856,3 +856,71 @@ } } } + +.upload-status-success { + @include upload-status-styles($base-600); +} + +.upload-status-error { + @include upload-status-styles(var(--ff-red)); +} + +.upload-status-progress { + @include upload-status-styles($gray-400); +} + +.progress { + height: var(--spacer-150) !important; +} + +.progress, +.progress-stacked { + --ff-progress-border-radius: var(--radius-0150) !important; +} + +///////fileupload.scss +.import-container { + width: 100%; + + .import-details { + display: flex; + padding: var(--spacer-100) 0 var(--spacer-100) 0; + align-items: center; + align-self: stretch; + border-bottom: 1px solid var(--ff-gray-100); + + .file-item-header-text { + display: flex; + justify-content: flex-start; + width: 30%; + color: var(--ff-gray-800); + font-size: 0.875rem; + font-weight: var(--font-weight-lg); + line-height: var(--text-line-height); + } + } + + .file-item-content { + display: flex; + padding: var(--spacer-200) 0 var(--spacer-200) 0; + align-items: center; + align-self: stretch; + border-bottom: 1px solid var(--ff-gray-100); + + .import-layout-text, + .import-workflow-text { + width: 30%; + color: var(--gray-800); + font-size: 0.875rem; + font-weight: var(--font-weight-sm); + line-height: var(--text-line-height); + } + + /* Target the last child */ + &:last-child { + padding-bottom: 0; + border-bottom: none; + } + } +} + diff --git a/forms-flow-theme/scss/fileUpload.scss b/forms-flow-theme/scss/fileUpload.scss index b68d1f5e..70d33c8c 100644 --- a/forms-flow-theme/scss/fileUpload.scss +++ b/forms-flow-theme/scss/fileUpload.scss @@ -35,6 +35,14 @@ $font-size-xs: var(--font-size-xs); margin: 0; } +.upload-size-text { +color: $gray-400; +text-align: center; +font-size: 0.875rem; //14px +font-weight: var(--font-weight-sm); +line-height: var(--text-line-height); +} + .upload-text-description { color: $gray-400; text-align: center; From 8d0e4c5bdcf92748e803dfdfedadc0336fae8b46 Mon Sep 17 00:00:00 2001 From: fahad-aot Date: Thu, 14 Nov 2024 23:34:44 -0800 Subject: [PATCH 02/12] FWF-9879:[Feature]Added no records page --- .../CustomComponents/NoDataFound.tsx | 16 ++++++++++++++ forms-flow-components/src/components/index.ts | 1 + forms-flow-theme/scss/_table.scss | 21 +++++++++++++++++-- 3 files changed, 36 insertions(+), 2 deletions(-) create mode 100644 forms-flow-components/src/components/CustomComponents/NoDataFound.tsx diff --git a/forms-flow-components/src/components/CustomComponents/NoDataFound.tsx b/forms-flow-components/src/components/CustomComponents/NoDataFound.tsx new file mode 100644 index 00000000..056148fd --- /dev/null +++ b/forms-flow-components/src/components/CustomComponents/NoDataFound.tsx @@ -0,0 +1,16 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +export const NoDataFound: React.FC = () => { + const { t } = useTranslation(); + + return ( + + + + {t("Nothing is found. Please try again.")} + + + + ); +}; diff --git a/forms-flow-components/src/components/index.ts b/forms-flow-components/src/components/index.ts index d706dab8..b90efb87 100644 --- a/forms-flow-components/src/components/index.ts +++ b/forms-flow-components/src/components/index.ts @@ -17,3 +17,4 @@ export * from "./CustomComponents/TableFooter"; export * from "./CustomComponents/CustomInfo"; export * from "./CustomComponents/BuildModal"; export * from "./CustomComponents/ErrorModal"; +export * from "./CustomComponents/NoDataFound"; diff --git a/forms-flow-theme/scss/_table.scss b/forms-flow-theme/scss/_table.scss index 2e461ca0..3ed4c5d1 100644 --- a/forms-flow-theme/scss/_table.scss +++ b/forms-flow-theme/scss/_table.scss @@ -20,7 +20,6 @@ $status-radius: 50%; border-collapse: separate !important; border-spacing: 0 !important; border-radius: var(--radius-0150) !important; - table-layout: fixed; th, td { @@ -182,7 +181,25 @@ $status-radius: 50%; justify-content: space-between; width: 100%; } -.text-ellipsis{ +.text-ellipsis { overflow: hidden; text-overflow: ellipsis; } + +.no-data-body { + .no-data-container { + display: flex; + padding: var(--spacer-200) !important ; + justify-content: center; + align-items: center; + gap: var(--spacer-100); + flex: 1 0 0; + .no-data-text { + color: var(--ff-gray-400); + text-align: center; + font-size: var(--font-size-xs); + font-weight: var(--font-weight-sm); + line-height: var(--text-line-height); + } + } +} From 6d7a084872bb8e524e36bceda9239984e4596217 Mon Sep 17 00:00:00 2001 From: fahad-aot Date: Mon, 18 Nov 2024 08:28:10 -0800 Subject: [PATCH 03/12] Footer hidden for length less than 6 --- .../CustomComponents/TableFooter.tsx | 101 +++++++++--------- 1 file changed, 52 insertions(+), 49 deletions(-) diff --git a/forms-flow-components/src/components/CustomComponents/TableFooter.tsx b/forms-flow-components/src/components/CustomComponents/TableFooter.tsx index b51e99b0..be18d9ea 100644 --- a/forms-flow-components/src/components/CustomComponents/TableFooter.tsx +++ b/forms-flow-components/src/components/CustomComponents/TableFooter.tsx @@ -1,9 +1,8 @@ -import React from 'react'; -import { useTranslation } from 'react-i18next'; -import Pagination from 'react-js-pagination'; -import { Dropdown } from 'react-bootstrap'; -import { DownArrowIcon } from '../SvgIcons/index'; - +import React from "react"; +import { useTranslation } from "react-i18next"; +import Pagination from "react-js-pagination"; +import { Dropdown } from "react-bootstrap"; +import { DownArrowIcon } from "../SvgIcons/index"; interface PageOption { value: number; @@ -34,54 +33,58 @@ export const TableFooter: React.FC = ({
- {t("Showing")} {(limit * activePage) - (limit - 1)} {t("to")}  + {t("Showing")} {limit * activePage - (limit - 1)} {t("to")}  {Math.min(limit * activePage, totalCount)} {t("of")}  {totalCount} {t("results")}
- -
- -
- - -
- {t("Rows per page")} -
- - - {limit} - - - {pageOptions.map((option) => ( - onLimitChange(option.value)} + {totalCount > 5 ? ( + <> + +
+ +
+ + +
+ {t("Rows per page")} +
+ + - {option.text} - - ))} - - - -
-
- + {limit} + + + {pageOptions.map((option) => ( + onLimitChange(option.value)} + > + {option.text} + + ))} + +
+ +
+
+ + + ) : null} ); -}; \ No newline at end of file +}; From 97a318b691a788b31d89a152435557e07ac777d7 Mon Sep 17 00:00:00 2001 From: shuhaib-aot Date: Tue, 19 Nov 2024 12:03:30 +0530 Subject: [PATCH 04/12] changed date format function --- .../CustomComponents/HistoryModal.tsx | 23 ++++--------------- 1 file changed, 4 insertions(+), 19 deletions(-) diff --git a/forms-flow-components/src/components/CustomComponents/HistoryModal.tsx b/forms-flow-components/src/components/CustomComponents/HistoryModal.tsx index 2d7aad3c..7e9cfae5 100644 --- a/forms-flow-components/src/components/CustomComponents/HistoryModal.tsx +++ b/forms-flow-components/src/components/CustomComponents/HistoryModal.tsx @@ -4,6 +4,7 @@ import { CustomButton } from "./Button"; import { CloseIcon } from "../SvgIcons/index"; import { ConfirmModal } from "./ConfirmModal"; import { useTranslation } from "react-i18next"; +import { HelperServices } from "@formsflow/service"; interface HistoryModalProps { show: boolean; @@ -43,23 +44,7 @@ interface AllHistory { id?: string; } -const formatDate = (dateString: string) => { - // TBD : to be formatted from backend later - dateString = dateString.replace(" ", "T") + "Z"; - const date = new Date(dateString); - - const day = String(date.getDate()).padStart(2, "0"); - const month = String(date.getMonth() + 1).padStart(2, "0"); // Months are 0-based - const year = String(date.getFullYear()).slice(-2); // Last two digits of year - const rawHours = date.getHours(); - const minutes = String(date.getMinutes()).padStart(2, "0"); - const ampm = rawHours >= 12 ? "PM" : "AM"; - // Convert 24-hour format to 12-hour format - const displayHours = rawHours % 12 || 12; // '0' hour should be '12' - const formattedHours = String(displayHours).padStart(2, "0"); - - return `${day}-${month}-${year} ${formattedHours}:${minutes}${ampm}`; -}; + const HistoryField = ({ fields }) => { return ( @@ -235,9 +220,9 @@ export const HistoryModal: React.FC = React.memo( const isLastEntry = index === allHistory.length - 1; const revertButtonDisabled = disableAllRevertButton || entry[disabledData.key] == disabledData.value || (!ignoreFirstEntryDisable && index === 0); const fields = [ - { id:1, heading: t("Last Edit On"), value: formatDate(entry.created) }, + { id:1, heading: t("Last Edit On"), value: HelperServices?.getLocalDateAndTime(entry.created) }, { id:2, heading: t("Last Edit By"), value: entry.createdBy }, - { id:3, heading: entry.publishedOn ? t("Published On") : "", value: entry.publishedOn ? formatDate(entry.publishedOn) : "" }, + { id:3, heading: entry.publishedOn ? t("Published On") : "", value: entry.publishedOn ? HelperServices?.getLocalDateAndTime(entry.publishedOn) : "" }, ...(categoryType === "WORKFLOW" ? [{ id:4, heading: t("Type"), value: entry.processType }] : []), From ccccbd6abff4f16e56464d4393cc6ed50c50ee9f Mon Sep 17 00:00:00 2001 From: josephalexantony-aot Date: Thu, 14 Nov 2024 01:33:05 -0800 Subject: [PATCH 05/12] FWF-3834:[Feature] - Added Reusable Import Modal to MF --- .../CustomComponents/ImportModal.tsx | 331 ++++++++++++++++++ .../src/components/SvgIcons/index.tsx | 20 +- forms-flow-components/src/components/index.ts | 1 + forms-flow-theme/scss/_forms.scss | 42 ++- forms-flow-theme/scss/_mixins.scss | 6 +- forms-flow-theme/scss/_modal.scss | 68 ++++ forms-flow-theme/scss/fileUpload.scss | 8 + 7 files changed, 471 insertions(+), 5 deletions(-) create mode 100644 forms-flow-components/src/components/CustomComponents/ImportModal.tsx diff --git a/forms-flow-components/src/components/CustomComponents/ImportModal.tsx b/forms-flow-components/src/components/CustomComponents/ImportModal.tsx new file mode 100644 index 00000000..b0d0e730 --- /dev/null +++ b/forms-flow-components/src/components/CustomComponents/ImportModal.tsx @@ -0,0 +1,331 @@ +import React, { useEffect, useState } from "react"; +import Modal from "react-bootstrap/Modal"; +import ProgressBar from "react-bootstrap/ProgressBar"; +import Dropdown from "react-bootstrap/Dropdown"; +import { Translation } from "react-i18next"; +import { + CloseIcon, + UploadIcon, + SuccessIcon, + FailedIcon, + IButton, + DropdownIcon, +} from "../SvgIcons"; +import { CustomButton } from "../CustomComponents/Button"; + +// Define the types for props +interface FileItem { + form?: { + majorVersion: number; + minorVersion: number; + }; + workflow?: { + majorVersion: number; + minorVersion: number; + }; +} + +interface ProcessVersion { + majorVersion: number; + minorVersion: number; + type: string; +} + +interface ImportModalProps { + importModal: boolean; + onClose: () => void; + uploadActionType: { + IMPORT: string; + VALIDATE: string; + }; + importError: string | null; + importLoader: boolean; + formName: string; + description: string; + handleImport: (file: File, uploadActionType: string, layoutVersion: string | null, flowVersion: string | null) => void + fileItems: FileItem | null; + fileType: string; + primaryButtonText: string; + headerText: string; + processVersion: ProcessVersion | null; +} + +export const ImportModal: React.FC = React.memo(({ + importModal, + onClose, + uploadActionType, + importError, + importLoader, + formName, + description, + handleImport, + fileItems, + fileType, + primaryButtonText, + headerText, + processVersion +}) => { + const computedStyle = getComputedStyle(document.documentElement); + const redColor = computedStyle.getPropertyValue("--ff-red-000"); + const [selectedFile, setSelectedFile] = useState(null); + const [uploadProgress, setUploadProgress] = useState(0); + const [selectedLayoutVersion, setSelectedLayoutOption] = useState<{ value: any; label: string } | null>(null); + const [selectedFlowVersion, setSelectedFlowOption] = useState<{ value: any; label: string } | null>(null); + const [showFileItems, setShowFileItems] = useState(false); + const [inprogress, setInprogress] = useState(true); + + const layoutOptions = [ + { value: true, label: 'Skip, do not import' }, + { value: 'major', label: `import as version ${fileItems?.form?.majorVersion + 1}.0 (only impacts new submissions)` }, + { value: 'minor', label: `import as version ${fileItems?.form?.majorVersion}.${fileItems?.form?.minorVersion} (impacts previous and new submissions)` } + ]; + + const flowOptions = [ + { value: true, label: 'Skip, do not import' }, + { value: 'major', label: `import as version ${fileItems?.workflow?.majorVersion ?? 1}.${fileItems?.workflow?.minorVersion ?? 0} (only impacts new submissions)` } + ]; + + const handleLayoutChange = (option: { value: any; label: string }) => { + setSelectedLayoutOption(option); + }; + + const handFlowChange = (option: { value: any; label: string }) => { + setSelectedFlowOption(option); + }; + + const onUpload = (evt: React.ChangeEvent) => { + const file = evt.target.files ? evt.target.files[0] : null; + setSelectedFile(file); + }; + + const resetState = () => { + setSelectedFile(null); + setUploadProgress(0); + }; + + const closeModal = () => { + setSelectedFile(null); + setUploadProgress(0); + setSelectedLayoutOption(null); + setSelectedFlowOption(null); + setShowFileItems(false); + onClose(); + }; + + const onImport = () => { + if (selectedFile) { + handleImport(selectedFile, uploadActionType.IMPORT, + selectedLayoutVersion?.value, selectedFlowVersion?.value); + } + }; + + useEffect(() => { + if (fileItems && !importError && Object.values(fileItems).some(item => + item?.majorVersion != null || item?.minorVersion != null)) { + setShowFileItems(true); + } else if (processVersion?.majorVersion != null || processVersion?.minorVersion != null) { + setShowFileItems(true); + } else { + setShowFileItems(false); + } + }, [importError, fileItems]); + + useEffect(() => { + if (!importModal) { + closeModal(); + } + }, [importModal]); + + useEffect(() => { + let isMounted = true; + + if (selectedFile) { + handleImport( + selectedFile, + uploadActionType.VALIDATE, + selectedLayoutVersion?.value ?? null, + selectedFlowVersion?.value ?? null + ); + + let start: number | null = null; + const duration = 2000; + + const animateProgress = (timestamp: number) => { + if (!start) start = timestamp; + const progress = Math.min(((timestamp - start) / duration) * 100, 100); + + if (isMounted) { + setUploadProgress(progress); + setInprogress(progress < 100); + } + + if (progress < 100) { + requestAnimationFrame(animateProgress); + } + }; + + const animation = requestAnimationFrame(animateProgress); + + return () => { + isMounted = false; + cancelAnimationFrame(animation); + }; + } + }, [selectedFile]); + + return ( + + + + {(t) => t(headerText)} + +
+ { resetState(); closeModal(); }} /> +
+
+ + {selectedFile ? ( + <> + +
+
+

{selectedFile.name}

+ + {!importLoader && !importError && !inprogress ? ( + {(t) => t("Upload Successful")} + ) : !importLoader && importError && !inprogress ? ( + {(t) => t("Upload Failed")} + ) : inprogress ? ( + {(t) => t("Import in progress")} + ) : null} + + {!importLoader && importError ? + : !importLoader && !inprogress ? : null} +
+
{formName}
+ {!importError && description &&
{description}
} +
{importError && {importError}}
+ {importError && importError.includes("already exists") && fileType === ".json" && +
+
+ + {(t) => t("Note")} +
+
+ {(t) => t(`If you want to replace an existing form, open the form in the design menu that you want to update, click "Actions", and then click "Import".`)} +
+
} +
+ {importError && !importError.includes("already exists") && + + {(t) => t("A system error occurred during import. Please try again to import.")} + } +
+
+ {showFileItems && !importError && ( +
+
+
+ + + {(t) => t("Import will create a new version.")} + +
+
+
+
Type
+
Import
+
+ + {processVersion?.majorVersion &&
+
{processVersion.type}
+
{`Import as Version ${processVersion?.majorVersion}.${processVersion?.minorVersion} (only impacts new submissions)`}
+
} + {fileItems?.form?.majorVersion &&
+
Layout
+
+ + +
+
+ {selectedLayoutVersion ? selectedLayoutVersion.label : 'Skip, do not import'} +
+ +
+
+ + + {layoutOptions.map((option, index) => ( + handleLayoutChange(option)}> + {option.label} + + ))} + +
+
+
} + + {fileItems?.workflow?.majorVersion &&
+
Flow
+
+ + +
+
+ {selectedFlowVersion ? selectedFlowVersion.label : 'Skip, do not import'} +
+ +
+
+ + {flowOptions.map((option, index) => ( + handFlowChange(option)}> + {option.label} + + ))} + +
+
+
} +
+ )} + + ) : ( +
document.getElementById('file-input')?.click()}> + +
+ +

{(t) => t(`Click or drag a file to this area to import${fileType === ".json, .bpmn" ? " (form, layout or bpmn)" : ""}`)}

+

{(t) => t(`Support for a single ${fileType} file upload. Maximum file size 20MB.`)}

+
+
+ )} +
+ + { primaryButtonText === "Try Again" ? closeModal() : onImport(); }} + buttonLoading={!importError && importLoader} + /> + { resetState(); closeModal(); }} + /> + +
+ ); +}); + + diff --git a/forms-flow-components/src/components/SvgIcons/index.tsx b/forms-flow-components/src/components/SvgIcons/index.tsx index c944bb07..32ff4415 100644 --- a/forms-flow-components/src/components/SvgIcons/index.tsx +++ b/forms-flow-components/src/components/SvgIcons/index.tsx @@ -1,7 +1,6 @@ const computedStyle = getComputedStyle(document.documentElement); const baseColor = computedStyle.getPropertyValue("--ff-base-600"); const grayColor = computedStyle.getPropertyValue("--ff-gray-800"); - export const ChevronIcon = ({ color = baseColor, width = "10", @@ -635,3 +634,22 @@ export const TickIcon = ({ color = baseColor, ...props }) => ( strokeLinejoin="round"/> ); + +export const DropdownIcon = () => ( + + + +); + diff --git a/forms-flow-components/src/components/index.ts b/forms-flow-components/src/components/index.ts index d706dab8..2b6fde66 100644 --- a/forms-flow-components/src/components/index.ts +++ b/forms-flow-components/src/components/index.ts @@ -17,3 +17,4 @@ export * from "./CustomComponents/TableFooter"; export * from "./CustomComponents/CustomInfo"; export * from "./CustomComponents/BuildModal"; export * from "./CustomComponents/ErrorModal"; +export * from "./CustomComponents/ImportModal"; diff --git a/forms-flow-theme/scss/_forms.scss b/forms-flow-theme/scss/_forms.scss index fa976796..0a8fe2af 100644 --- a/forms-flow-theme/scss/_forms.scss +++ b/forms-flow-theme/scss/_forms.scss @@ -411,4 +411,44 @@ select option:hover { .form-preview{ pointer-events: none; cursor: not-allowed; -} \ No newline at end of file +} + +.dropdown-main { + display: block !important; + padding: 0 !important; + + .dropdown-menu.show { + width: 100% !important; + overflow: hidden !important; + padding: 0.4375rem !important; + display: block !important; + } + + .dropdown-item { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 0.875rem !important; + font-weight: var(--font-weight-sm) !important; + } + + .btn.btn-primary, + .btn.btn-success, + .btn.btn-success:hover { + width: 100%; + text-overflow: ellipsis; + white-space: nowrap; + overflow: hidden; + background-color: $white !important; + color: $black !important; + display: flex !important; + align-items: left !important; + justify-content: start !important; + font-size: 0.875rem !important; + font-weight: var(--font-weight-sm) !important; + } + + .dropdown-toggle::after { + display: none !important; + } +} diff --git a/forms-flow-theme/scss/_mixins.scss b/forms-flow-theme/scss/_mixins.scss index 10fdf231..fabe7b7d 100644 --- a/forms-flow-theme/scss/_mixins.scss +++ b/forms-flow-theme/scss/_mixins.scss @@ -56,17 +56,17 @@ } -@mixin text-modal-content { +@mixin text-modal-content { font-size: var(--font-size-sm); font-weight: var(--font-weight-sm); - line-height: var(--text-line-height); + line-height: var(--text-line-height); color: var(--ff-gray-800); } @mixin upload-status-styles($color) { color: $color; text-align: right; - font-size: $font-size-xs; + font-size: var(--font-size-xs); font-weight: var(--font-weight-sm); line-height: var(--text-line-height); } \ No newline at end of file diff --git a/forms-flow-theme/scss/_modal.scss b/forms-flow-theme/scss/_modal.scss index d5dbb512..2f687255 100644 --- a/forms-flow-theme/scss/_modal.scss +++ b/forms-flow-theme/scss/_modal.scss @@ -856,3 +856,71 @@ } } } + +.upload-status-success { + @include upload-status-styles($base-600); +} + +.upload-status-error { + @include upload-status-styles(var(--ff-red)); +} + +.upload-status-progress { + @include upload-status-styles($gray-400); +} + +.progress { + height: var(--spacer-150) !important; +} + +.progress, +.progress-stacked { + --ff-progress-border-radius: var(--radius-0150) !important; +} + +///////fileupload.scss +.import-container { + width: 100%; + + .import-details { + display: flex; + padding: var(--spacer-100) 0 var(--spacer-100) 0; + align-items: center; + align-self: stretch; + border-bottom: 1px solid var(--ff-gray-100); + + .file-item-header-text { + display: flex; + justify-content: flex-start; + width: 30%; + color: var(--ff-gray-800); + font-size: 0.875rem; + font-weight: var(--font-weight-lg); + line-height: var(--text-line-height); + } + } + + .file-item-content { + display: flex; + padding: var(--spacer-200) 0 var(--spacer-200) 0; + align-items: center; + align-self: stretch; + border-bottom: 1px solid var(--ff-gray-100); + + .import-layout-text, + .import-workflow-text { + width: 30%; + color: var(--gray-800); + font-size: 0.875rem; + font-weight: var(--font-weight-sm); + line-height: var(--text-line-height); + } + + /* Target the last child */ + &:last-child { + padding-bottom: 0; + border-bottom: none; + } + } +} + diff --git a/forms-flow-theme/scss/fileUpload.scss b/forms-flow-theme/scss/fileUpload.scss index b68d1f5e..70d33c8c 100644 --- a/forms-flow-theme/scss/fileUpload.scss +++ b/forms-flow-theme/scss/fileUpload.scss @@ -35,6 +35,14 @@ $font-size-xs: var(--font-size-xs); margin: 0; } +.upload-size-text { +color: $gray-400; +text-align: center; +font-size: 0.875rem; //14px +font-weight: var(--font-weight-sm); +line-height: var(--text-line-height); +} + .upload-text-description { color: $gray-400; text-align: center; From 6b1d45839a3711bffdde6962a475081cd731a271 Mon Sep 17 00:00:00 2001 From: josephalexantony-aot Date: Mon, 18 Nov 2024 23:40:16 -0800 Subject: [PATCH 06/12] FWF-3834:[Feature]- Import Modal HTML reconstruction to functions. --- .../CustomComponents/ImportModal.tsx | 689 +++++++++++------- .../src/components/SvgIcons/index.tsx | 4 +- forms-flow-theme/scss/_forms.scss | 4 +- forms-flow-theme/scss/_modal.scss | 67 -- forms-flow-theme/scss/fileUpload.scss | 71 +- 5 files changed, 497 insertions(+), 338 deletions(-) diff --git a/forms-flow-components/src/components/CustomComponents/ImportModal.tsx b/forms-flow-components/src/components/CustomComponents/ImportModal.tsx index b0d0e730..bbe333cd 100644 --- a/forms-flow-components/src/components/CustomComponents/ImportModal.tsx +++ b/forms-flow-components/src/components/CustomComponents/ImportModal.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import Modal from "react-bootstrap/Modal"; import ProgressBar from "react-bootstrap/ProgressBar"; import Dropdown from "react-bootstrap/Dropdown"; -import { Translation } from "react-i18next"; +import { useTranslation } from "react-i18next"; import { CloseIcon, UploadIcon, @@ -14,6 +14,7 @@ import { import { CustomButton } from "../CustomComponents/Button"; // Define the types for props +const { t } = useTranslation(); interface FileItem { form?: { majorVersion: number; @@ -32,7 +33,7 @@ interface ProcessVersion { } interface ImportModalProps { - importModal: boolean; + showModal: boolean; onClose: () => void; uploadActionType: { IMPORT: string; @@ -42,7 +43,12 @@ interface ImportModalProps { importLoader: boolean; formName: string; description: string; - handleImport: (file: File, uploadActionType: string, layoutVersion: string | null, flowVersion: string | null) => void + handleImport: ( + file: File, + uploadActionType: string, + layoutVersion: string | null, + flowVersion: string | null + ) => void; fileItems: FileItem | null; fileType: string; primaryButtonText: string; @@ -50,282 +56,435 @@ interface ImportModalProps { processVersion: ProcessVersion | null; } -export const ImportModal: React.FC = React.memo(({ - importModal, - onClose, - uploadActionType, - importError, - importLoader, - formName, - description, - handleImport, - fileItems, - fileType, - primaryButtonText, - headerText, - processVersion -}) => { - const computedStyle = getComputedStyle(document.documentElement); - const redColor = computedStyle.getPropertyValue("--ff-red-000"); - const [selectedFile, setSelectedFile] = useState(null); - const [uploadProgress, setUploadProgress] = useState(0); - const [selectedLayoutVersion, setSelectedLayoutOption] = useState<{ value: any; label: string } | null>(null); - const [selectedFlowVersion, setSelectedFlowOption] = useState<{ value: any; label: string } | null>(null); - const [showFileItems, setShowFileItems] = useState(false); - const [inprogress, setInprogress] = useState(true); - - const layoutOptions = [ - { value: true, label: 'Skip, do not import' }, - { value: 'major', label: `import as version ${fileItems?.form?.majorVersion + 1}.0 (only impacts new submissions)` }, - { value: 'minor', label: `import as version ${fileItems?.form?.majorVersion}.${fileItems?.form?.minorVersion} (impacts previous and new submissions)` } - ]; - - const flowOptions = [ - { value: true, label: 'Skip, do not import' }, - { value: 'major', label: `import as version ${fileItems?.workflow?.majorVersion ?? 1}.${fileItems?.workflow?.minorVersion ?? 0} (only impacts new submissions)` } - ]; - - const handleLayoutChange = (option: { value: any; label: string }) => { - setSelectedLayoutOption(option); - }; +export const ImportModal: React.FC = React.memo( + ({ + showModal, + onClose, + uploadActionType, + importError, + importLoader, + formName, + description, + handleImport, + fileItems, + fileType, + primaryButtonText, + headerText, + processVersion, + }) => { + const computedStyle = getComputedStyle(document.documentElement); + const redColor = computedStyle.getPropertyValue("--ff-red-000"); + const [selectedFile, setSelectedFile] = useState(null); + const [uploadProgress, setUploadProgress] = useState(0); + const [selectedLayoutVersion, setSelectedLayoutOption] = useState<{ + value: any; + label: string; + } | null>(null); + const [selectedFlowVersion, setSelectedFlowOption] = useState<{ + value: any; + label: string; + } | null>(null); + const [showFileItems, setShowFileItems] = useState(false); + const [inprogress, setInprogress] = useState(true); - const handFlowChange = (option: { value: any; label: string }) => { - setSelectedFlowOption(option); - }; + const layoutOptions = [ + { value: true, label: "Skip, do not import" }, + { + value: "major", + label: `import as version ${ + fileItems?.form?.majorVersion + 1 + }.0 (only impacts new submissions)`, + }, + { + value: "minor", + label: `import as version ${fileItems?.form?.majorVersion}.${fileItems?.form?.minorVersion} (impacts previous and new submissions)`, + }, + ]; - const onUpload = (evt: React.ChangeEvent) => { - const file = evt.target.files ? evt.target.files[0] : null; - setSelectedFile(file); - }; + const flowOptions = [ + { value: true, label: "Skip, do not import" }, + { + value: "major", + label: `import as version ${fileItems?.workflow?.majorVersion ?? 1}.${ + fileItems?.workflow?.minorVersion ?? 0 + } (only impacts new submissions)`, + }, + ]; - const resetState = () => { - setSelectedFile(null); - setUploadProgress(0); - }; + const handleLayoutChange = (option: { value: any; label: string }) => { + setSelectedLayoutOption(option); + }; - const closeModal = () => { - setSelectedFile(null); - setUploadProgress(0); - setSelectedLayoutOption(null); - setSelectedFlowOption(null); - setShowFileItems(false); - onClose(); - }; + const handFlowChange = (option: { value: any; label: string }) => { + setSelectedFlowOption(option); + }; - const onImport = () => { - if (selectedFile) { - handleImport(selectedFile, uploadActionType.IMPORT, - selectedLayoutVersion?.value, selectedFlowVersion?.value); - } - }; + const onUpload = (evt: React.ChangeEvent) => { + const file = evt.target.files ? evt.target.files[0] : null; + setSelectedFile(file); + }; - useEffect(() => { - if (fileItems && !importError && Object.values(fileItems).some(item => - item?.majorVersion != null || item?.minorVersion != null)) { - setShowFileItems(true); - } else if (processVersion?.majorVersion != null || processVersion?.minorVersion != null) { - setShowFileItems(true); - } else { + const resetState = () => { + setSelectedFile(null); + setUploadProgress(0); + }; + + const closeModal = () => { + setSelectedFile(null); + setUploadProgress(0); + setSelectedLayoutOption(null); + setSelectedFlowOption(null); setShowFileItems(false); - } - }, [importError, fileItems]); - - useEffect(() => { - if (!importModal) { - closeModal(); - } - }, [importModal]); - - useEffect(() => { - let isMounted = true; - - if (selectedFile) { - handleImport( - selectedFile, - uploadActionType.VALIDATE, - selectedLayoutVersion?.value ?? null, - selectedFlowVersion?.value ?? null + onClose(); + }; + + const onImport = () => { + if (selectedFile) { + handleImport( + selectedFile, + uploadActionType.IMPORT, + selectedLayoutVersion?.value, + selectedFlowVersion?.value + ); + } + }; + + useEffect(() => { + if ( + fileItems && + !importError && + Object.values(fileItems).some( + (item) => item?.majorVersion != null || item?.minorVersion != null + ) + ) { + setShowFileItems(true); + } else if ( + processVersion?.majorVersion != null || + processVersion?.minorVersion != null + ) { + setShowFileItems(true); + } else { + setShowFileItems(false); + } + }, [importError, fileItems]); + + useEffect(() => { + if (!showModal) { + closeModal(); + } + }, [showModal]); + + useEffect(() => { + let isMounted = true; + + if (selectedFile) { + handleImport( + selectedFile, + uploadActionType.VALIDATE, + selectedLayoutVersion?.value ?? null, + selectedFlowVersion?.value ?? null + ); + + let start: number | null = null; + const duration = 2000; + + const animateProgress = (timestamp: number) => { + if (!start) start = timestamp; + const progress = Math.min( + ((timestamp - start) / duration) * 100, + 100 + ); + + if (isMounted) { + setUploadProgress(progress); + setInprogress(progress < 100); + } + + if (progress < 100) { + requestAnimationFrame(animateProgress); + } + }; + + const animation = requestAnimationFrame(animateProgress); + + return () => { + isMounted = false; + cancelAnimationFrame(animation); + }; + } + }, [selectedFile]); + + const renderUploadDetails = () => { + return ( +
+
+

{selectedFile?.name}

+ + {renderUploadStatusText()} + + {renderUploadStatusIcon()} +
+
+ {formName} +
+ {!importError && description && ( +
{description}
+ )} + {renderImportError()} +
+ ); + }; + + // Function to determine the upload status class based on conditions + const getUploadStatusClass = () => { + return !importLoader && !importError && !inprogress + ? "upload-status-success" + : !importLoader && importError && !inprogress + ? "upload-status-error" + : inprogress + ? "upload-status-progress" + : ""; + }; + + // Function to render the status text based on the upload condition + const renderUploadStatusText = () => { + if (!importLoader && !importError && !inprogress) { + return t("Upload Successful"); + } + if (!importLoader && importError && !inprogress) { + return t("Upload Failed"); + } + if (inprogress) { + return t("Import in progress"); + } + return null; + }; + + // Function to render the correct status icon based on upload progress + const renderUploadStatusIcon = () => { + if (!importLoader && importError) { + return ; + } + if (!importLoader && !inprogress) { + return ; + } + return null; + }; + + // Function to render import errors + const renderImportError = () => { + return ( + importError && ( + {importError} + ) + ); + }; + + // Function to render the import file items and version options + const renderFileItems = () => { + if (showFileItems && !importError) { + return ( +
+ {renderImportNote()} + {renderFileItemDetails()} + {renderLayoutOptions()} + {renderFlowOptions()} +
+ ); + } + return null; + }; + + // Function to render import note when file items are shown + const renderImportNote = () => { + return ( +
+
+ + + {t("Import will create a new version.")} + +
+
); + }; - let start: number | null = null; - const duration = 2000; - - const animateProgress = (timestamp: number) => { - if (!start) start = timestamp; - const progress = Math.min(((timestamp - start) / duration) * 100, 100); - - if (isMounted) { - setUploadProgress(progress); - setInprogress(progress < 100); - } - - if (progress < 100) { - requestAnimationFrame(animateProgress); - } - }; - - const animation = requestAnimationFrame(animateProgress); - - return () => { - isMounted = false; - cancelAnimationFrame(animation); - }; - } - }, [selectedFile]); - - return ( - - - - {(t) => t(headerText)} - -
- { resetState(); closeModal(); }} /> + // Function to render the file item details (e.g. type and import version) + const renderFileItemDetails = () => { + return ( +
+
{t("Type")}
+
{t("Import")}
- - - {selectedFile ? ( - <> - -
-
-

{selectedFile.name}

- - {!importLoader && !importError && !inprogress ? ( - {(t) => t("Upload Successful")} - ) : !importLoader && importError && !inprogress ? ( - {(t) => t("Upload Failed")} - ) : inprogress ? ( - {(t) => t("Import in progress")} - ) : null} - - {!importLoader && importError ? - : !importLoader && !inprogress ? : null} -
-
{formName}
- {!importError && description &&
{description}
} -
{importError && {importError}}
- {importError && importError.includes("already exists") && fileType === ".json" && -
-
- - {(t) => t("Note")} -
-
- {(t) => t(`If you want to replace an existing form, open the form in the design menu that you want to update, click "Actions", and then click "Import".`)} + ); + }; + + // Function to render layout version options + const renderLayoutOptions = () => { + return ( + fileItems?.form?.majorVersion && ( +
+
{t("Layout")}
+
+ + +
+
+ {selectedLayoutVersion + ? selectedLayoutVersion.label + : "Skip, do not import"} +
+
-
} -
- {importError && !importError.includes("already exists") && - - {(t) => t("A system error occurred during import. Please try again to import.")} - } -
+ + + {layoutOptions.map((option, index) => ( + handleLayoutChange(option)} + > + {option.label} + + ))} + +
- {showFileItems && !importError && ( -
-
-
- - - {(t) => t("Import will create a new version.")} - -
-
-
-
Type
-
Import
-
- - {processVersion?.majorVersion &&
-
{processVersion.type}
-
{`Import as Version ${processVersion?.majorVersion}.${processVersion?.minorVersion} (only impacts new submissions)`}
-
} - {fileItems?.form?.majorVersion &&
-
Layout
-
- - -
-
- {selectedLayoutVersion ? selectedLayoutVersion.label : 'Skip, do not import'} -
- -
-
- - - {layoutOptions.map((option, index) => ( - handleLayoutChange(option)}> - {option.label} - - ))} - -
-
-
} - - {fileItems?.workflow?.majorVersion &&
-
Flow
-
- - -
-
- {selectedFlowVersion ? selectedFlowVersion.label : 'Skip, do not import'} -
- -
-
- - {flowOptions.map((option, index) => ( - handFlowChange(option)}> - {option.label} - - ))} - -
+
+ ) + ); + }; + + // Function to render flow version options + const renderFlowOptions = () => { + return ( + fileItems?.workflow?.majorVersion && ( +
+
Flow
+
+ + +
+
+ {selectedFlowVersion + ? selectedFlowVersion.label + : "Skip, do not import"} +
+
-
} -
- )} - - ) : ( -
document.getElementById('file-input')?.click()}> - -
- -

{(t) => t(`Click or drag a file to this area to import${fileType === ".json, .bpmn" ? " (form, layout or bpmn)" : ""}`)}

-

{(t) => t(`Support for a single ${fileType} file upload. Maximum file size 20MB.`)}

+ + + {flowOptions.map((option, index) => ( + handFlowChange(option)} + > + {option.label} + + ))} + +
- )} - - - { primaryButtonText === "Try Again" ? closeModal() : onImport(); }} - buttonLoading={!importError && importLoader} - /> - { resetState(); closeModal(); }} - /> - - - ); -}); + ) + ); + }; + // Function to render the file upload area when no file is selected + const renderFileUploadArea = () => { + return ( +
document.getElementById("file-input")?.click()} + > + +
+ +

+ {t( + `Click or drag a file to this area to import${ + fileType === ".json, .bpmn" ? " (form, layout or bpmn)" : "" + }` + )} +

+

+ {t( `Support for a single ${fileType} file upload. Maximum file + size 20MB.` )} +

+
+
+ ); + }; + return ( + + + + {t(headerText)} + +
+ { + resetState(); + closeModal(); + }} + /> +
+
+ + {selectedFile ? ( + <> + + {renderUploadDetails()} + {renderFileItems()} + + ) : ( + renderFileUploadArea() + )} + + + { + primaryButtonText === "Try Again" ? closeModal() : onImport(); + }} + buttonLoading={!importError && importLoader} + /> + { + resetState(); + closeModal(); + }} + /> + +
+ ); + } +); diff --git a/forms-flow-components/src/components/SvgIcons/index.tsx b/forms-flow-components/src/components/SvgIcons/index.tsx index 32ff4415..19293b89 100644 --- a/forms-flow-components/src/components/SvgIcons/index.tsx +++ b/forms-flow-components/src/components/SvgIcons/index.tsx @@ -635,7 +635,7 @@ export const TickIcon = ({ color = baseColor, ...props }) => ( ); -export const DropdownIcon = () => ( +export const DropdownIcon = ({color = baseColor}) => ( ( > Date: Mon, 18 Nov 2024 23:40:16 -0800 Subject: [PATCH 07/12] FWF-3834:[Feature]- Import Modal HTML reconstruction to functions. --- .../CustomComponents/ImportModal.tsx | 689 +++++++++++------- .../src/components/SvgIcons/index.tsx | 4 +- forms-flow-theme/scss/_forms.scss | 4 +- forms-flow-theme/scss/_modal.scss | 67 -- forms-flow-theme/scss/fileUpload.scss | 71 +- 5 files changed, 497 insertions(+), 338 deletions(-) diff --git a/forms-flow-components/src/components/CustomComponents/ImportModal.tsx b/forms-flow-components/src/components/CustomComponents/ImportModal.tsx index b0d0e730..bbe333cd 100644 --- a/forms-flow-components/src/components/CustomComponents/ImportModal.tsx +++ b/forms-flow-components/src/components/CustomComponents/ImportModal.tsx @@ -2,7 +2,7 @@ import React, { useEffect, useState } from "react"; import Modal from "react-bootstrap/Modal"; import ProgressBar from "react-bootstrap/ProgressBar"; import Dropdown from "react-bootstrap/Dropdown"; -import { Translation } from "react-i18next"; +import { useTranslation } from "react-i18next"; import { CloseIcon, UploadIcon, @@ -14,6 +14,7 @@ import { import { CustomButton } from "../CustomComponents/Button"; // Define the types for props +const { t } = useTranslation(); interface FileItem { form?: { majorVersion: number; @@ -32,7 +33,7 @@ interface ProcessVersion { } interface ImportModalProps { - importModal: boolean; + showModal: boolean; onClose: () => void; uploadActionType: { IMPORT: string; @@ -42,7 +43,12 @@ interface ImportModalProps { importLoader: boolean; formName: string; description: string; - handleImport: (file: File, uploadActionType: string, layoutVersion: string | null, flowVersion: string | null) => void + handleImport: ( + file: File, + uploadActionType: string, + layoutVersion: string | null, + flowVersion: string | null + ) => void; fileItems: FileItem | null; fileType: string; primaryButtonText: string; @@ -50,282 +56,435 @@ interface ImportModalProps { processVersion: ProcessVersion | null; } -export const ImportModal: React.FC = React.memo(({ - importModal, - onClose, - uploadActionType, - importError, - importLoader, - formName, - description, - handleImport, - fileItems, - fileType, - primaryButtonText, - headerText, - processVersion -}) => { - const computedStyle = getComputedStyle(document.documentElement); - const redColor = computedStyle.getPropertyValue("--ff-red-000"); - const [selectedFile, setSelectedFile] = useState(null); - const [uploadProgress, setUploadProgress] = useState(0); - const [selectedLayoutVersion, setSelectedLayoutOption] = useState<{ value: any; label: string } | null>(null); - const [selectedFlowVersion, setSelectedFlowOption] = useState<{ value: any; label: string } | null>(null); - const [showFileItems, setShowFileItems] = useState(false); - const [inprogress, setInprogress] = useState(true); - - const layoutOptions = [ - { value: true, label: 'Skip, do not import' }, - { value: 'major', label: `import as version ${fileItems?.form?.majorVersion + 1}.0 (only impacts new submissions)` }, - { value: 'minor', label: `import as version ${fileItems?.form?.majorVersion}.${fileItems?.form?.minorVersion} (impacts previous and new submissions)` } - ]; - - const flowOptions = [ - { value: true, label: 'Skip, do not import' }, - { value: 'major', label: `import as version ${fileItems?.workflow?.majorVersion ?? 1}.${fileItems?.workflow?.minorVersion ?? 0} (only impacts new submissions)` } - ]; - - const handleLayoutChange = (option: { value: any; label: string }) => { - setSelectedLayoutOption(option); - }; +export const ImportModal: React.FC = React.memo( + ({ + showModal, + onClose, + uploadActionType, + importError, + importLoader, + formName, + description, + handleImport, + fileItems, + fileType, + primaryButtonText, + headerText, + processVersion, + }) => { + const computedStyle = getComputedStyle(document.documentElement); + const redColor = computedStyle.getPropertyValue("--ff-red-000"); + const [selectedFile, setSelectedFile] = useState(null); + const [uploadProgress, setUploadProgress] = useState(0); + const [selectedLayoutVersion, setSelectedLayoutOption] = useState<{ + value: any; + label: string; + } | null>(null); + const [selectedFlowVersion, setSelectedFlowOption] = useState<{ + value: any; + label: string; + } | null>(null); + const [showFileItems, setShowFileItems] = useState(false); + const [inprogress, setInprogress] = useState(true); - const handFlowChange = (option: { value: any; label: string }) => { - setSelectedFlowOption(option); - }; + const layoutOptions = [ + { value: true, label: "Skip, do not import" }, + { + value: "major", + label: `import as version ${ + fileItems?.form?.majorVersion + 1 + }.0 (only impacts new submissions)`, + }, + { + value: "minor", + label: `import as version ${fileItems?.form?.majorVersion}.${fileItems?.form?.minorVersion} (impacts previous and new submissions)`, + }, + ]; - const onUpload = (evt: React.ChangeEvent) => { - const file = evt.target.files ? evt.target.files[0] : null; - setSelectedFile(file); - }; + const flowOptions = [ + { value: true, label: "Skip, do not import" }, + { + value: "major", + label: `import as version ${fileItems?.workflow?.majorVersion ?? 1}.${ + fileItems?.workflow?.minorVersion ?? 0 + } (only impacts new submissions)`, + }, + ]; - const resetState = () => { - setSelectedFile(null); - setUploadProgress(0); - }; + const handleLayoutChange = (option: { value: any; label: string }) => { + setSelectedLayoutOption(option); + }; - const closeModal = () => { - setSelectedFile(null); - setUploadProgress(0); - setSelectedLayoutOption(null); - setSelectedFlowOption(null); - setShowFileItems(false); - onClose(); - }; + const handFlowChange = (option: { value: any; label: string }) => { + setSelectedFlowOption(option); + }; - const onImport = () => { - if (selectedFile) { - handleImport(selectedFile, uploadActionType.IMPORT, - selectedLayoutVersion?.value, selectedFlowVersion?.value); - } - }; + const onUpload = (evt: React.ChangeEvent) => { + const file = evt.target.files ? evt.target.files[0] : null; + setSelectedFile(file); + }; - useEffect(() => { - if (fileItems && !importError && Object.values(fileItems).some(item => - item?.majorVersion != null || item?.minorVersion != null)) { - setShowFileItems(true); - } else if (processVersion?.majorVersion != null || processVersion?.minorVersion != null) { - setShowFileItems(true); - } else { + const resetState = () => { + setSelectedFile(null); + setUploadProgress(0); + }; + + const closeModal = () => { + setSelectedFile(null); + setUploadProgress(0); + setSelectedLayoutOption(null); + setSelectedFlowOption(null); setShowFileItems(false); - } - }, [importError, fileItems]); - - useEffect(() => { - if (!importModal) { - closeModal(); - } - }, [importModal]); - - useEffect(() => { - let isMounted = true; - - if (selectedFile) { - handleImport( - selectedFile, - uploadActionType.VALIDATE, - selectedLayoutVersion?.value ?? null, - selectedFlowVersion?.value ?? null + onClose(); + }; + + const onImport = () => { + if (selectedFile) { + handleImport( + selectedFile, + uploadActionType.IMPORT, + selectedLayoutVersion?.value, + selectedFlowVersion?.value + ); + } + }; + + useEffect(() => { + if ( + fileItems && + !importError && + Object.values(fileItems).some( + (item) => item?.majorVersion != null || item?.minorVersion != null + ) + ) { + setShowFileItems(true); + } else if ( + processVersion?.majorVersion != null || + processVersion?.minorVersion != null + ) { + setShowFileItems(true); + } else { + setShowFileItems(false); + } + }, [importError, fileItems]); + + useEffect(() => { + if (!showModal) { + closeModal(); + } + }, [showModal]); + + useEffect(() => { + let isMounted = true; + + if (selectedFile) { + handleImport( + selectedFile, + uploadActionType.VALIDATE, + selectedLayoutVersion?.value ?? null, + selectedFlowVersion?.value ?? null + ); + + let start: number | null = null; + const duration = 2000; + + const animateProgress = (timestamp: number) => { + if (!start) start = timestamp; + const progress = Math.min( + ((timestamp - start) / duration) * 100, + 100 + ); + + if (isMounted) { + setUploadProgress(progress); + setInprogress(progress < 100); + } + + if (progress < 100) { + requestAnimationFrame(animateProgress); + } + }; + + const animation = requestAnimationFrame(animateProgress); + + return () => { + isMounted = false; + cancelAnimationFrame(animation); + }; + } + }, [selectedFile]); + + const renderUploadDetails = () => { + return ( +
+
+

{selectedFile?.name}

+ + {renderUploadStatusText()} + + {renderUploadStatusIcon()} +
+
+ {formName} +
+ {!importError && description && ( +
{description}
+ )} + {renderImportError()} +
+ ); + }; + + // Function to determine the upload status class based on conditions + const getUploadStatusClass = () => { + return !importLoader && !importError && !inprogress + ? "upload-status-success" + : !importLoader && importError && !inprogress + ? "upload-status-error" + : inprogress + ? "upload-status-progress" + : ""; + }; + + // Function to render the status text based on the upload condition + const renderUploadStatusText = () => { + if (!importLoader && !importError && !inprogress) { + return t("Upload Successful"); + } + if (!importLoader && importError && !inprogress) { + return t("Upload Failed"); + } + if (inprogress) { + return t("Import in progress"); + } + return null; + }; + + // Function to render the correct status icon based on upload progress + const renderUploadStatusIcon = () => { + if (!importLoader && importError) { + return ; + } + if (!importLoader && !inprogress) { + return ; + } + return null; + }; + + // Function to render import errors + const renderImportError = () => { + return ( + importError && ( + {importError} + ) + ); + }; + + // Function to render the import file items and version options + const renderFileItems = () => { + if (showFileItems && !importError) { + return ( +
+ {renderImportNote()} + {renderFileItemDetails()} + {renderLayoutOptions()} + {renderFlowOptions()} +
+ ); + } + return null; + }; + + // Function to render import note when file items are shown + const renderImportNote = () => { + return ( +
+
+ + + {t("Import will create a new version.")} + +
+
); + }; - let start: number | null = null; - const duration = 2000; - - const animateProgress = (timestamp: number) => { - if (!start) start = timestamp; - const progress = Math.min(((timestamp - start) / duration) * 100, 100); - - if (isMounted) { - setUploadProgress(progress); - setInprogress(progress < 100); - } - - if (progress < 100) { - requestAnimationFrame(animateProgress); - } - }; - - const animation = requestAnimationFrame(animateProgress); - - return () => { - isMounted = false; - cancelAnimationFrame(animation); - }; - } - }, [selectedFile]); - - return ( - - - - {(t) => t(headerText)} - -
- { resetState(); closeModal(); }} /> + // Function to render the file item details (e.g. type and import version) + const renderFileItemDetails = () => { + return ( +
+
{t("Type")}
+
{t("Import")}
- - - {selectedFile ? ( - <> - -
-
-

{selectedFile.name}

- - {!importLoader && !importError && !inprogress ? ( - {(t) => t("Upload Successful")} - ) : !importLoader && importError && !inprogress ? ( - {(t) => t("Upload Failed")} - ) : inprogress ? ( - {(t) => t("Import in progress")} - ) : null} - - {!importLoader && importError ? - : !importLoader && !inprogress ? : null} -
-
{formName}
- {!importError && description &&
{description}
} -
{importError && {importError}}
- {importError && importError.includes("already exists") && fileType === ".json" && -
-
- - {(t) => t("Note")} -
-
- {(t) => t(`If you want to replace an existing form, open the form in the design menu that you want to update, click "Actions", and then click "Import".`)} + ); + }; + + // Function to render layout version options + const renderLayoutOptions = () => { + return ( + fileItems?.form?.majorVersion && ( +
+
{t("Layout")}
+
+ + +
+
+ {selectedLayoutVersion + ? selectedLayoutVersion.label + : "Skip, do not import"} +
+
-
} -
- {importError && !importError.includes("already exists") && - - {(t) => t("A system error occurred during import. Please try again to import.")} - } -
+ + + {layoutOptions.map((option, index) => ( + handleLayoutChange(option)} + > + {option.label} + + ))} + +
- {showFileItems && !importError && ( -
-
-
- - - {(t) => t("Import will create a new version.")} - -
-
-
-
Type
-
Import
-
- - {processVersion?.majorVersion &&
-
{processVersion.type}
-
{`Import as Version ${processVersion?.majorVersion}.${processVersion?.minorVersion} (only impacts new submissions)`}
-
} - {fileItems?.form?.majorVersion &&
-
Layout
-
- - -
-
- {selectedLayoutVersion ? selectedLayoutVersion.label : 'Skip, do not import'} -
- -
-
- - - {layoutOptions.map((option, index) => ( - handleLayoutChange(option)}> - {option.label} - - ))} - -
-
-
} - - {fileItems?.workflow?.majorVersion &&
-
Flow
-
- - -
-
- {selectedFlowVersion ? selectedFlowVersion.label : 'Skip, do not import'} -
- -
-
- - {flowOptions.map((option, index) => ( - handFlowChange(option)}> - {option.label} - - ))} - -
+
+ ) + ); + }; + + // Function to render flow version options + const renderFlowOptions = () => { + return ( + fileItems?.workflow?.majorVersion && ( +
+
Flow
+
+ + +
+
+ {selectedFlowVersion + ? selectedFlowVersion.label + : "Skip, do not import"} +
+
-
} -
- )} - - ) : ( -
document.getElementById('file-input')?.click()}> - -
- -

{(t) => t(`Click or drag a file to this area to import${fileType === ".json, .bpmn" ? " (form, layout or bpmn)" : ""}`)}

-

{(t) => t(`Support for a single ${fileType} file upload. Maximum file size 20MB.`)}

+ + + {flowOptions.map((option, index) => ( + handFlowChange(option)} + > + {option.label} + + ))} + +
- )} - - - { primaryButtonText === "Try Again" ? closeModal() : onImport(); }} - buttonLoading={!importError && importLoader} - /> - { resetState(); closeModal(); }} - /> - - - ); -}); + ) + ); + }; + // Function to render the file upload area when no file is selected + const renderFileUploadArea = () => { + return ( +
document.getElementById("file-input")?.click()} + > + +
+ +

+ {t( + `Click or drag a file to this area to import${ + fileType === ".json, .bpmn" ? " (form, layout or bpmn)" : "" + }` + )} +

+

+ {t( `Support for a single ${fileType} file upload. Maximum file + size 20MB.` )} +

+
+
+ ); + }; + return ( + + + + {t(headerText)} + +
+ { + resetState(); + closeModal(); + }} + /> +
+
+ + {selectedFile ? ( + <> + + {renderUploadDetails()} + {renderFileItems()} + + ) : ( + renderFileUploadArea() + )} + + + { + primaryButtonText === "Try Again" ? closeModal() : onImport(); + }} + buttonLoading={!importError && importLoader} + /> + { + resetState(); + closeModal(); + }} + /> + +
+ ); + } +); diff --git a/forms-flow-components/src/components/SvgIcons/index.tsx b/forms-flow-components/src/components/SvgIcons/index.tsx index 32ff4415..19293b89 100644 --- a/forms-flow-components/src/components/SvgIcons/index.tsx +++ b/forms-flow-components/src/components/SvgIcons/index.tsx @@ -635,7 +635,7 @@ export const TickIcon = ({ color = baseColor, ...props }) => ( ); -export const DropdownIcon = () => ( +export const DropdownIcon = ({color = baseColor}) => ( ( > Date: Tue, 19 Nov 2024 01:09:34 -0800 Subject: [PATCH 08/12] added data-testid --- .../src/components/CustomComponents/NoDataFound.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/forms-flow-components/src/components/CustomComponents/NoDataFound.tsx b/forms-flow-components/src/components/CustomComponents/NoDataFound.tsx index 056148fd..44d228f0 100644 --- a/forms-flow-components/src/components/CustomComponents/NoDataFound.tsx +++ b/forms-flow-components/src/components/CustomComponents/NoDataFound.tsx @@ -8,7 +8,7 @@ export const NoDataFound: React.FC = () => {
-
{t("Nothing is found. Please try again.")} + {t("Nothing is found. Please try again.")} From 9e9a4ee4f29480d8f9f2781f74e004b8b0e6f37e Mon Sep 17 00:00:00 2001 From: josephalexantony-aot Date: Tue, 19 Nov 2024 01:18:33 -0800 Subject: [PATCH 09/12] FWF-3834:[Feature]-Sonar-Qube fix --- .../CustomComponents/ImportModal.tsx | 52 ++++++++++++------- .../src/components/SvgIcons/index.tsx | 6 +-- forms-flow-theme/scss/fileUpload.scss | 34 +++--------- 3 files changed, 42 insertions(+), 50 deletions(-) diff --git a/forms-flow-components/src/components/CustomComponents/ImportModal.tsx b/forms-flow-components/src/components/CustomComponents/ImportModal.tsx index bbe333cd..22b3189f 100644 --- a/forms-flow-components/src/components/CustomComponents/ImportModal.tsx +++ b/forms-flow-components/src/components/CustomComponents/ImportModal.tsx @@ -2,7 +2,6 @@ import React, { useEffect, useState } from "react"; import Modal from "react-bootstrap/Modal"; import ProgressBar from "react-bootstrap/ProgressBar"; import Dropdown from "react-bootstrap/Dropdown"; -import { useTranslation } from "react-i18next"; import { CloseIcon, UploadIcon, @@ -12,9 +11,9 @@ import { DropdownIcon, } from "../SvgIcons"; import { CustomButton } from "../CustomComponents/Button"; +import { useTranslation } from "react-i18next"; // Define the types for props -const { t } = useTranslation(); interface FileItem { form?: { majorVersion: number; @@ -72,6 +71,7 @@ export const ImportModal: React.FC = React.memo( headerText, processVersion, }) => { + const { t } = useTranslation(); const computedStyle = getComputedStyle(document.documentElement); const redColor = computedStyle.getPropertyValue("--ff-red-000"); const [selectedFile, setSelectedFile] = useState(null); @@ -80,7 +80,7 @@ export const ImportModal: React.FC = React.memo( value: any; label: string; } | null>(null); - const [selectedFlowVersion, setSelectedFlowOption] = useState<{ + const [selectedFlowVersion, setSelectedFlowVersion] = useState<{ value: any; label: string; } | null>(null); @@ -116,7 +116,7 @@ export const ImportModal: React.FC = React.memo( }; const handFlowChange = (option: { value: any; label: string }) => { - setSelectedFlowOption(option); + setSelectedFlowVersion(option); }; const onUpload = (evt: React.ChangeEvent) => { @@ -133,7 +133,7 @@ export const ImportModal: React.FC = React.memo( setSelectedFile(null); setUploadProgress(0); setSelectedLayoutOption(null); - setSelectedFlowOption(null); + setSelectedFlowVersion(null); setShowFileItems(false); onClose(); }; @@ -241,16 +241,22 @@ export const ImportModal: React.FC = React.memo( ); }; - // Function to determine the upload status class based on conditions const getUploadStatusClass = () => { - return !importLoader && !importError && !inprogress - ? "upload-status-success" - : !importLoader && importError && !inprogress - ? "upload-status-error" - : inprogress - ? "upload-status-progress" - : ""; + if (!importLoader && !importError && !inprogress) { + return "upload-status-success"; + } + + if (!importLoader && importError && !inprogress) { + return "upload-status-error"; + } + + if (inprogress) { + return "upload-status-progress"; + } + + return ""; }; + // Function to render the status text based on the upload condition const renderUploadStatusText = () => { @@ -344,9 +350,9 @@ export const ImportModal: React.FC = React.memo(
- {layoutOptions.map((option, index) => ( + {layoutOptions.map((option) => ( handleLayoutChange(option)} > {option.label} @@ -379,9 +385,9 @@ export const ImportModal: React.FC = React.memo(
- {flowOptions.map((option, index) => ( + {flowOptions.map((option) => ( handFlowChange(option)} > {option.label} @@ -399,8 +405,16 @@ export const ImportModal: React.FC = React.memo( const renderFileUploadArea = () => { return (
document.getElementById("file-input")?.click()} + onKeyDown={(e) => { + if (e.key === "Enter" || e.key === " ") { + document.getElementById("file-input")?.click(); + } + }} + aria-label="Upload file" > = React.memo( )}

- {t( `Support for a single ${fileType} file upload. Maximum file - size 20MB.` )} + {t(`Support for a single ${fileType} file upload. Maximum file + size 20MB.`)}

diff --git a/forms-flow-components/src/components/SvgIcons/index.tsx b/forms-flow-components/src/components/SvgIcons/index.tsx index 19293b89..50c38907 100644 --- a/forms-flow-components/src/components/SvgIcons/index.tsx +++ b/forms-flow-components/src/components/SvgIcons/index.tsx @@ -646,9 +646,9 @@ export const DropdownIcon = ({color = baseColor}) => ( ); diff --git a/forms-flow-theme/scss/fileUpload.scss b/forms-flow-theme/scss/fileUpload.scss index 4003440d..a5d436a3 100644 --- a/forms-flow-theme/scss/fileUpload.scss +++ b/forms-flow-theme/scss/fileUpload.scss @@ -36,11 +36,11 @@ $font-size-xs: var(--font-size-xs); } .upload-size-text { -color: $gray-400; -text-align: center; -font-size: var(--font-size-xs); -font-weight: var(--font-weight-sm); -line-height: var(--text-line-height); + color: $gray-400; + text-align: center; + font-size: var(--font-size-xs); + font-weight: var(--font-weight-sm); + line-height: var(--text-line-height); } .upload-text-description { @@ -61,7 +61,7 @@ line-height: var(--text-line-height); align-self: stretch; } -.upload-boady { +.upload-body { display: flex; flex-direction: column; align-items: flex-start; @@ -112,35 +112,13 @@ line-height: var(--text-line-height); .progress { height: var(--spacer-150) !important; -} - -.progress, -.progress-stacked { --ff-progress-border-radius: var(--radius-0150) !important; } -.upload-status-success { - @include upload-status-styles($base-600); -} - -.upload-status-error { - @include upload-status-styles(var(--ff-red)); -} - -.upload-status-progress { - @include upload-status-styles($gray-400); -} - -.progress { - height: var(--spacer-150) !important; -} - -.progress, .progress-stacked { --ff-progress-border-radius: var(--radius-0150) !important; } -//fileupload.scss .import-container { width: 100%; From 999b5afc0e400c925f6df27ddc2cfaaaa69bf2ba Mon Sep 17 00:00:00 2001 From: josephalexantony-aot Date: Tue, 19 Nov 2024 01:34:56 -0800 Subject: [PATCH 10/12] FWF-3834:[Feature]-Sonar Qube fix- 2 --- .../src/components/CustomComponents/ImportModal.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/forms-flow-components/src/components/CustomComponents/ImportModal.tsx b/forms-flow-components/src/components/CustomComponents/ImportModal.tsx index 22b3189f..acbec564 100644 --- a/forms-flow-components/src/components/CustomComponents/ImportModal.tsx +++ b/forms-flow-components/src/components/CustomComponents/ImportModal.tsx @@ -76,7 +76,7 @@ export const ImportModal: React.FC = React.memo( const redColor = computedStyle.getPropertyValue("--ff-red-000"); const [selectedFile, setSelectedFile] = useState(null); const [uploadProgress, setUploadProgress] = useState(0); - const [selectedLayoutVersion, setSelectedLayoutOption] = useState<{ + const [selectedLayoutVersion, setSelectedLayoutVersion] = useState<{ value: any; label: string; } | null>(null); @@ -112,7 +112,7 @@ export const ImportModal: React.FC = React.memo( ]; const handleLayoutChange = (option: { value: any; label: string }) => { - setSelectedLayoutOption(option); + setSelectedLayoutVersion(option); }; const handFlowChange = (option: { value: any; label: string }) => { @@ -132,7 +132,7 @@ export const ImportModal: React.FC = React.memo( const closeModal = () => { setSelectedFile(null); setUploadProgress(0); - setSelectedLayoutOption(null); + setSelectedLayoutVersion(null); setSelectedFlowVersion(null); setShowFileItems(false); onClose(); From 58e9f273186a21206e86310d8c4e66d77e283067 Mon Sep 17 00:00:00 2001 From: shuhaib s <95394061+shuhaib-aot@users.noreply.github.com> Date: Wed, 20 Nov 2024 10:13:25 +0530 Subject: [PATCH 11/12] Resetting cache ttile on change (#346) --- .../src/components/CustomComponents/FormBuilderModal.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/forms-flow-components/src/components/CustomComponents/FormBuilderModal.tsx b/forms-flow-components/src/components/CustomComponents/FormBuilderModal.tsx index 4b241d2d..e425696e 100644 --- a/forms-flow-components/src/components/CustomComponents/FormBuilderModal.tsx +++ b/forms-flow-components/src/components/CustomComponents/FormBuilderModal.tsx @@ -89,6 +89,7 @@ export const FormBuilderModal: React.FC = React.memo( value = e.target.checked ? "wizard" : "form"; } setValues(prev => ({...prev,[name]:value})) + setCachedTitle(""); //reseting caching on type } const handleOnBlur = (e)=>{ @@ -98,7 +99,7 @@ export const FormBuilderModal: React.FC = React.memo( const isCancelButton = relatedTargetName == "cancelButton"; if((!values.title || values.title !== cachedTitle) && !isCancelButton){ nameValidationOnBlur({...values,createButtonClicked}) - setCachedTitle(values.title); + setCachedTitle(values.title); //caching this title to avoid calling handling blur } } From ea1e48fe173d3dd9a30290229e0074ac205ff6f1 Mon Sep 17 00:00:00 2001 From: Bonymol-aot Date: Wed, 20 Nov 2024 02:00:39 -0800 Subject: [PATCH 12/12] FWF-3816: Removed table2-paginator from package-lock --- forms-flow-admin/package-lock.json | 21 ++------------------- 1 file changed, 2 insertions(+), 19 deletions(-) diff --git a/forms-flow-admin/package-lock.json b/forms-flow-admin/package-lock.json index f656f432..d22e1eea 100644 --- a/forms-flow-admin/package-lock.json +++ b/forms-flow-admin/package-lock.json @@ -1,12 +1,12 @@ { "name": "@formsflow/admin", - "version": "6.1.0-alpha", + "version": "7.0.0-alpha", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@formsflow/admin", - "version": "6.1.0-alpha", + "version": "7.0.0-alpha", "dependencies": { "@types/react": "^17.0.19", "@types/react-dom": "^17.0.9", @@ -15,7 +15,6 @@ "i18next-browser-languagedetector": "^7.0.1", "react-bootstrap": "^2.7.0", "react-bootstrap-table-next": "^4.0.3", - "react-bootstrap-table2-paginator": "^2.1.2", "react-i18next": "^12.1.4", "react-router-dom": "5.1.2", "react-select": "^5.8.0", @@ -11696,16 +11695,6 @@ "react-dom": "^16.3.0" } }, - "node_modules/react-bootstrap-table2-paginator": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/react-bootstrap-table2-paginator/-/react-bootstrap-table2-paginator-2.1.2.tgz", - "integrity": "sha512-LC5znEphhgKJvaSY1q8d+Gj0Nc/1X+VS3tKJjkmWmfv9P61YC/BnwJ+aoqEmQzsLiVGowrzss+i/u+Tip5H+Iw==", - "peerDependencies": { - "prop-types": "^15.0.0", - "react": "^16.3.0", - "react-dom": "^16.3.0" - } - }, "node_modules/react-dom": { "version": "16.14.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz", @@ -23221,12 +23210,6 @@ "underscore": "1.9.1" } }, - "react-bootstrap-table2-paginator": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/react-bootstrap-table2-paginator/-/react-bootstrap-table2-paginator-2.1.2.tgz", - "integrity": "sha512-LC5znEphhgKJvaSY1q8d+Gj0Nc/1X+VS3tKJjkmWmfv9P61YC/BnwJ+aoqEmQzsLiVGowrzss+i/u+Tip5H+Iw==", - "requires": {} - }, "react-dom": { "version": "16.14.0", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz",