import { useEffect, useState } from 'react'; import TextField from '@mui/material/TextField'; import { Utils } from '@pega/react-sdk-components/lib/components/helpers/utils'; import download from 'downloadjs'; // import SummaryList from '../../SummaryList'; // import ActionButtonsForFileUtil from '../ActionButtonsForFileUtil'; import './FileUtility.css'; import { IconButton, Menu, MenuItem, Button, CircularProgress, Card, debounce } from '@mui/material'; import MoreVertIcon from '@mui/icons-material/MoreVert'; import { validateMaxSize } from '@pega/react-sdk-components/lib/components/helpers/attachmentShared'; import { getResolvedConstantValue } from '@pega/react-sdk-components/lib/components/helpers/object-utils'; import { getComponentFromMap } from '@pega/react-sdk-components/lib/bridge/helpers/sdk_component_map'; import type { PConnProps } from '@pega/react-sdk-components/lib/types/PConnProps'; interface FileUtilityProps extends PConnProps { // If any, enter additional props that only exist on this component caseId?: string; } export default function FileUtility(props: FileUtilityProps) { // Get emitted components from map (so we can get any override that may exist) const SummaryList = getComponentFromMap('SummaryList'); const ActionButtonsForFileUtil = getComponentFromMap('ActionButtonsForFileUtil'); const { getPConnect, caseId } = props; const thePConn = getPConnect(); const caseID = caseId ?? getResolvedConstantValue(thePConn, PCore.getConstants().CASE_INFO.CASE_INFO_ID); const required = true; const listTemp = { data: [], count: 0 }; const [list, setList] = useState(listTemp); const headerSvgIcon$ = Utils.getImageSrc('paper-clip', Utils.getSDKStaticConentUrl()); const closeSvgIcon = Utils.getImageSrc('times', Utils.getSDKStaticConentUrl()); const configProps: any = thePConn.resolveConfigProps(thePConn.getConfigProps()); const header = configProps.label; const fileTemp = { showfileModal: false, fileList: [], attachedFiles: [], fileMainButtons: [ { actionID: 'attach', jsAction: 'attachFiles', name: thePConn.getLocalizedValue('Attach files', '', '') } ], // 2nd and 3rd args empty string until typedef marked correctly fileSecondaryButtons: [ { actionID: 'cancel', jsAction: 'cancel', name: thePConn.getLocalizedValue('Cancel', '', '') } ] // 2nd and 3rd args empty string until typedef marked correctly }; const [fileData, setFileData] = useState(fileTemp); const linkTemp = { showLinkModal: false, linksList: [], attachedLinks: [], linkMainButtons: [ { actionID: 'attach', jsAction: 'attachLinks', name: thePConn.getLocalizedValue('Attach links', '', '') } ], // 2nd and 3rd args empty string until typedef marked correctly linkSecondaryButtons: [ { actionID: 'cancel', jsAction: 'cancel', name: thePConn.getLocalizedValue('Cancel', '', '') } ] // 2nd and 3rd args empty string until typedef marked correctly }; const [linkData, setLinkData] = useState(linkTemp); const [anchorEl, setAnchorEl] = useState(null); const open = Boolean(anchorEl); const [link, setLink] = useState({ title: '', url: '', disable: true }); const [inProgress, setProgress] = useState(false); const [showViewAllModal, setViewAll] = useState(false); const [vaItems, setFullAttachments] = useState([]); function addAttachments(attsFromResp: any[] = []) { attsFromResp = attsFromResp.map(respAtt => { const updatedAtt = { ...respAtt, meta: `${respAtt.category} . ${Utils.generateDateTime(respAtt.createTime, 'DateTime-Since')}, ${respAtt.createdBy}` }; if (updatedAtt.type === 'FILE') { updatedAtt.nameWithExt = updatedAtt.fileName; } return updatedAtt; }); return attsFromResp; } function getListUtilityItemProps({ att, cancelFile, downloadFile, deleteFile, removeFile }) { let actions; if (att.progress && att.progress !== 100) { actions = [ { id: `Cancel-${att.ID}`, text: thePConn.getLocalizedValue('Cancel', '', ''), // 2nd and 3rd args empty string until typedef marked correctly icon: 'times', onClick: cancelFile } ]; } else if (att.links) { const isFile = att.type === 'FILE'; const ID = att.ID.replace(/\s/gi, ''); const actionsMap = new Map([ [ 'download', { id: `download-${ID}`, text: isFile ? thePConn.getLocalizedValue('Download', '', '') : thePConn.getLocalizedValue('Open', '', ''), // 2nd and 3rd args empty string until typedef marked correctly icon: isFile ? 'download' : 'open', onClick: downloadFile } ], [ 'delete', { id: `Delete-${ID}`, text: thePConn.getLocalizedValue('Delete', '', ''), // 2nd and 3rd args empty string until typedef marked correctly icon: 'trash', onClick: deleteFile } ] ]); actions = []; actionsMap.forEach((action, actionKey) => { if (att.links[actionKey]) { actions.push(action); } }); } else if (att.error) { actions = [ { id: `Remove-${att.ID}`, text: thePConn.getLocalizedValue('Remove', '', ''), // 2nd and 3rd args empty string until typedef marked correctly icon: 'trash', onClick: removeFile } ]; } return { id: att.ID, visual: { icon: Utils.getIconForAttachment(att), progress: att.progress === 100 ? undefined : att.progress }, primary: { type: att.type, name: att.name, icon: 'open', click: downloadFile }, secondary: { text: att.meta }, actions }; } function fileDownload(data, fileName, ext) { const file = ext ? `${fileName}.${ext}` : fileName; download(atob(data), file); } function downloadAttachedFile(att: any) { const attachUtils = PCore.getAttachmentUtils(); const { ID, name, extension, type } = att; const context = thePConn.getContextName(); attachUtils // @ts-expect-error - 3rd parameter "responseEncoding" is optional .downloadAttachment(ID, context) .then((content: any) => { if (type === 'FILE') { fileDownload(content.data, name, extension); } else if (type === 'URL') { let { data } = content; if (!/^(http|https):\/\//.test(data)) { data = `//${data}`; } window.open(content.data, '_blank'); } }) .catch(); } function deleteAttachedFile(att: any) { const attachUtils = PCore.getAttachmentUtils(); const { ID } = att; const context = thePConn.getContextName(); attachUtils .deleteAttachment(ID, context) .then(() => { getAttachments(); }) .catch(); } const getAttachments = () => { const attachmentUtils = PCore.getAttachmentUtils(); if (caseID && caseID !== '') { const attPromise = attachmentUtils.getCaseAttachments(caseID, thePConn.getContextName()); attPromise.then((resp: any) => { const arFullListAttachments = addAttachments(resp); const attachmentsCount = arFullListAttachments.length; const arItems: any = arFullListAttachments.slice(0, 3).map(att => { return getListUtilityItemProps({ att, downloadFile: !att.progress ? () => downloadAttachedFile(att) : null, cancelFile: null, deleteFile: !att.progress ? () => deleteAttachedFile(att) : null, removeFile: null }); }); const viewAllarItems: any = arFullListAttachments.map(att => { return getListUtilityItemProps({ att, downloadFile: !att.progress ? () => downloadAttachedFile(att) : null, cancelFile: null, deleteFile: !att.progress ? () => deleteAttachedFile(att) : null, removeFile: null }); }); setProgress(false); setList(current => { return { ...current, count: attachmentsCount, data: arItems }; }); setFullAttachments(viewAllarItems); }); } }; const debouncedGetAttachments = debounce(getAttachments, 1000); useEffect(() => { debouncedGetAttachments(); }, []); useEffect(() => { const attachSubObject = { matcher: 'ATTACHMENTS', criteria: { ID: caseID } }; const attachSubId = PCore.getMessagingServiceManager().subscribe(attachSubObject, debouncedGetAttachments, getPConnect().getContextName()); return function cleanup() { PCore.getMessagingServiceManager().unsubscribe(attachSubId); }; }, []); useEffect(() => { thePConn.registerAdditionalProps({ lastRefreshTime: `@P ${PCore.getConstants().SUMMARY_OF_ATTACHMENTS_LAST_REFRESH_TIME}` }); }, [thePConn]); function setNewFiles(arFiles) { let index = 0; for (const file of arFiles) { if (!validateMaxSize(file, '5')) { file.error = true; file.meta = 'File is too big. Max allowed size is 5MB.'; } file.mimeType = file.type; file.icon = Utils.getIconFromFileType(file.type); file.ID = `${new Date().getTime()}I${index}`; index += 1; } return arFiles; } function getFiles(arFiles: any[]): any[] { return setNewFiles(arFiles); } function uploadMyFiles(event) { // alert($event.target.files[0]); // outputs the first file const arFiles = getFiles(event.target.files); // convert FileList to an array const myFiles: any = Array.from(arFiles); const arFileList$: any = myFiles.map((att: any) => { return getListUtilityItemProps({ att, downloadFile: !att.progress ? () => downloadAttachedFile(att) : null, cancelFile: null, deleteFile: !att.progress ? () => deleteAttachedFile(att) : null, removeFile: null }); }); setFileData(current => { return { ...current, fileList: arFileList$, attachedFiles: myFiles }; }); } const handleClick = event => { setAnchorEl(event.currentTarget); }; const handleClose = () => { setAnchorEl(null); }; function onAddFilesClick() { setFileData(current => { return { ...current, showfileModal: true }; }); setAnchorEl(null); } function removeFileFromList(item: any) { let attachedFiles: any = fileData.attachedFiles; let fileList: any = fileData.fileList; if (item !== null) { attachedFiles = attachedFiles.filter(ele => ele.ID !== item.id); fileList = fileList.filter(ele => ele.id !== item.id); setFileData(current => { return { ...current, fileList, attachedFiles }; }); } } function closeFilePopup() { setFileData(current => { return { ...current, showfileModal: false }; }); } function onAttachFiles() { const attachmentUtils = PCore.getAttachmentUtils(); const caseID = thePConn.getValue(PCore.getConstants().CASE_INFO.CASE_INFO_ID, ''); // 2nd arg empty string until typedef marked correctly const onUploadProgress = () => {}; const errorHandler = () => {}; closeFilePopup(); if (fileData.attachedFiles && fileData.attachedFiles.length > 0) { setProgress(true); } Promise.allSettled( fileData.attachedFiles.map(file => attachmentUtils.uploadAttachment(file, onUploadProgress, errorHandler, thePConn.getContextName())) ) .then((fileResponses: any) => { const uploadedFiles: any = []; fileResponses.forEach(fileResponse => { if (fileResponse.status === 'fulfilled') { uploadedFiles.push(fileResponse.value); } }); if (uploadedFiles.length > 0) { (attachmentUtils.linkAttachmentsToCase(caseID, uploadedFiles, 'File', thePConn.getContextName()) as Promise) .then(() => { setFileData(current => { return { ...current, fileList: [], attachedFiles: [] }; }); getAttachments(); }) .catch(); } }) .catch(); } function onAddLinksClick() { setLinkData(current => { return { ...current, showLinkModal: true }; }); setAnchorEl(null); } function closeAddLinksPopup() { setLinkData(current => { return { ...current, showLinkModal: false }; }); } const fieldlinkOnChange = event => { const title = event.target.value; setLink(current => { const updatedData = { ...current, title }; updatedData.disable = !(updatedData.title && updatedData.url); return updatedData; }); }; function fieldurlOnChange(event) { const url = event.target.value; setLink(current => { const updatedData = { ...current, url }; updatedData.disable = !(updatedData.title && updatedData.url); return updatedData; }); } function addLink() { // copy list locally const localList: any = linkData.linksList.slice(); const url = link.url; if (!/^(http|https):\/\//.test(link.url)) { link.url = `http://${link.url}`; } // list for display let oLink: any = {}; oLink.icon = 'chain'; oLink.ID = `${new Date().getTime()}`; oLink = getListUtilityItemProps({ att: oLink, downloadFile: null, cancelFile: null, deleteFile: null, removeFile: null }); oLink.type = 'URL'; oLink.primary.type = oLink.type; oLink.visual.icon = 'chain'; oLink.primary.name = link.title; oLink.primary.icon = 'open'; oLink.secondary.text = url; localList.push(oLink); // list for actually attachments const attachedListTemp: any = linkData.attachedLinks.slice(); const attachedLink: any = {}; attachedLink.id = oLink.id; attachedLink.linkTitle = link.title; attachedLink.type = oLink.type; attachedLink.url = url; attachedListTemp.push(attachedLink); setLinkData(current => { return { ...current, linksList: localList, attachedLinks: attachedListTemp }; }); // clear values setLink({ title: '', url: '', disable: true }); } function removeLinksFromList(item: any) { let attachedLinks: any = linkData.attachedLinks; let linksList: any = linkData.linksList; if (item !== null) { attachedLinks = attachedLinks.filter(ele => ele.id !== item.id); linksList = linksList.filter(ele => ele.id !== item.id); setLinkData(current => { return { ...current, linksList, attachedLinks }; }); } } function onAttachLinks() { const attachmentUtils = PCore.getAttachmentUtils(); const caseID = thePConn.getValue(PCore.getConstants().CASE_INFO.CASE_INFO_ID, ''); // 2nd arg empty string until typedef marked correctly const links = linkData.attachedLinks; closeAddLinksPopup(); const linksToAttach = links.map((item: any) => ({ type: 'URL', category: 'URL', url: item.url, name: item.linkTitle })); if (linksToAttach && linksToAttach.length > 0) { setProgress(true); (attachmentUtils.linkAttachmentsToCase(caseID, linksToAttach, 'URL', thePConn.getContextName()) as Promise) .then(() => { setLinkData(current => { return { ...current, linksList: [], attachedLinks: [] }; }); getAttachments(); }) .catch(() => setProgress(false)); } } return ( {inProgress && (
)}
{header}
{list.count}
{thePConn.getLocalizedValue('Add files', '', '')} {' '} {/* 2nd and 3rd args empty string until typedef marked correctly */} {thePConn.getLocalizedValue('Add links', '', '')} {' '} {/* 2nd and 3rd args empty string until typedef marked correctly */}
{list.data.length > 0 && (
)} {list.count > 3 && (
)} {fileData.showfileModal && (

{thePConn.getLocalizedValue('Add local files', '', '')}

{fileData.fileList.length > 0 && (
)} {fileData.fileList.length === 0 &&
}
)} {linkData.showLinkModal && (

{thePConn.getLocalizedValue('Add links', '', '')}

{/* 2nd and 3rd args empty string until typedef marked correctly */}
{' '} {/* 2nd and 3rd args empty string until typedef marked correctly */}
{linkData.linksList.length > 0 && (
)}
)} {showViewAllModal && (

{thePConn.getLocalizedValue('Attachments', '', '')}

{/* 2nd and 3rd args empty string until typedef marked correctly */}
)} ); }