UNPKG

@foreverrbum/ethsign

Version:

This package will allow you to electronically sign documents within your application

339 lines (307 loc) 18.6 kB
import React, { useState, useEffect } from 'react'; import SearchIcon from '../../assets/search.svg'; import ClearIcon from '../../assets/clear.svg'; import LeftArrowPage from '../../assets/leftarrow_page.svg'; import LeftArrowPageDisabled from '../../assets/leftarrow_page_disabled.svg'; import RightArrowPage from '../../assets/rightarrow_page.svg'; import RightArrowPageDisabled from '../../assets/rightarrow_page_disabled.svg'; import RightArrow from '../../assets/rightarrow.svg'; import DownArrow from '../../assets/downarrow.svg'; import menu from '../../assets/menu.png' import { getFileArray } from '../../helpers/download'; import Alert from '../Alert'; import { withRouter } from 'react-router-dom'; import { useIntl } from 'react-intl'; import { getDocumentFormattedStatus } from '../../helpers/dashboard'; const Searchbox = (props) => { const { status, handleStatus, name, handleName, filter, handleFilter, tableLabel, handleMobileSidebar, data, contract, provider, triggerSearch, handleActivePage, pageNumber, changePage, contractsPerPage, handleContractsPerPage, totalContractsInCurrentFilter, dataObj } = props; const [showPasswordPopup, handleShowPasswordPopup] = useState(false); const [showStatusSelect, handleShowStatusSelect] = useState(false); const [showClear, handleShowClear] = useState(false); const [search, handleSearch] = useState(false); const [loading, handleLoading] = useState(false); const [doc, handleDoc] = useState(null); const { formatMessage } = useIntl(); let eventListener = null; useEffect(() => { let searchContractNameElement = document.getElementById("search-contract-name"); if(name.length > 0) { searchContractNameElement.value = name; } searchContractNameElement?.addEventListener('input', updateSearch); searchContractNameElement?.addEventListener('focus', (event) => { handleShowClear(true); if(searchContractNameElement?.value.length > 0) { handleSearch(true); } }); searchContractNameElement?.addEventListener('blur', (event) => { if(searchContractNameElement?.value.length == 0) { handleShowClear(false); handleSearch(false); } }); return () => document.getElementById("search-contract-name")?.removeEventListener('input', updateSearch); }, []); useEffect(() => { if(filter !== 'search') { handleShowClear(false); } }, [filter]); useEffect(() => { let searchDropdownListener = hideSearchDropdown.bind(this); document.addEventListener('click', searchDropdownListener); return () => document.removeEventListener('click', searchDropdownListener); }, [search]); /* * Statuses: * -2: 'Search Results' * -1: 'All Status' * 0: 'PDF Not Uploaded' * 1: 'More Signers Needed' * 2: 'Pending Signatures' * 3: 'All Signed' * 4: 'Waiting For Others' */ const updateStatus = (status) => { handleStatus(status); } const updateName = () => { let currName = document.getElementById("search-contract-name").value; handleName(currName?.toLowerCase()); if(currName?.length > 0 && !showClear) { handleShowClear(true); } else if(currName.length == 0 && showClear) { handleShowClear(false); } } const handleSignSubmission = async (doc) => { handleLoading(true); let password = document.getElementById('password_input') ? document.getElementById('password_input').value : ''; let fileArr = await getFileArray(password, doc.ipfsHash, doc.storageProvider, false, formatMessage) if (fileArr!=false){ props.history.push({ pathname: '/sign', state: { doc: doc, contract: contract, provider: provider, idx: -1, fileArr: fileArr }, }) } handleLoading(false); } const updateSearch = (e) => { if(e.target.value.length > 0) { handleSearch(true); } else { handleSearch(false); } updateName(); } const showMenu = show => { if(!show) { hideMenu(); return; } if(!eventListener) { eventListener = hideMenu.bind(this); } document.addEventListener('click', eventListener); handleShowStatusSelect(true); } const hideMenu = (event) => { document.removeEventListener('click', eventListener); handleShowStatusSelect(false); } const performSearch = () => { if(document.getElementById("search-contract-name")?.value.length > 0) { handleFilter('search'); triggerSearch(); handleSearch(false); document.getElementById("search-contract-name").blur(); } } const clearSearch = () => { if(document.getElementById("search-contract-name") !== null) { document.getElementById("search-contract-name").value = ""; document.getElementById("search-contract-name").focus(); } handleName(""); } const hideSearchDropdown = (event) => { if(search) { const searchElement = document.getElementById('search'); const searchDropdown = document.getElementById('search-dropdown'); if(!searchElement || !searchDropdown || (event && !searchElement.contains(event.target) && !searchDropdown.contains(event.target))) { handleSearch(false); } } } const manageContracts = ['pending', 'voted', 'consensus', 'expiring']; return ( <div className="w-full md:w-auto flex flex-col justify-center"> <div className="flex flex-grow flex-col pt-5"> <div className="w-full xs:w-80 relative"> <div id="search" className={`flex h-10 text-15 `}> <div onClick={() => performSearch()} className="select-none h-10 w-auto border-r-0 cursor-pointer focus:outline-none px-2 rounded-l-md py-2 border-gray-150 border-t border-b border-l"> <img src={SearchIcon} /> </div> <form className="w-full flex-1" onSubmit={(e) => {e.preventDefault(); performSearch();}}> <input type="text" placeholder={formatMessage({id: "SEARCH_CONTRACT_NAME"})} id="search-contract-name" className={`w-full flex-1 h-10 focus:outline-none placeholder-gray-150 py-3 text-gray-70 pr-4 border-t mr-0 border-b border-gray-150${showClear ? ' rounded-none' : ' border-r rounded-r-md'}`}> </input> </form> {showClear && <div onClick={() => clearSearch()} className="select-none h-10 w-auto border-l-0 cursor-pointer focus:outline-none px-2 rounded-r-md py-2 border-gray-150 border-t border-b border-r"> <img src={ClearIcon} /> </div> } </div> {search && <div id="search-dropdown" className={`z-50 text-15 select-none origin-top-right absolute right-0 w-full rounded-md shadow-lg bg-white ring-1 ring-gray-150 ring-opacity-100 focus:outline-none`} role="menu" aria-orientation="vertical" aria-labelledby="menu-button" tabIndex="-1"> <div className="py-1" role="none"> <div key={`search`} className="block mx-4 py-2 mb-2 text-15 border-b border-gray-150" role="menuitem" tabIndex="-1" ><div className="flex flex-row text-gray-70"><div className="flex-none" >{formatMessage({id: "SEARCH_FOR"})}: </div><div className="pl-2 text-15 text-gray-80 text-left">{document.getElementById("search-contract-name").value}</div></div></div> {(!data || data.length == 0) && <div key={`no-results`} className="block mx-4 py-2 mb-2 text-15" role="menuitem" tabIndex="-1" ><div className="flex flex-row text-gray-70 text-left">{formatMessage({id: "NO_SEARCH_RESULTS"})}</div></div> } {data?.slice(0, 5).map((docKey, idx)=>{ return ( <div key={`search-${idx}`} onClick={(event) => { event.stopPropagation(); if(dataObj[docKey]?.status === 'PDF Not Uploaded' || dataObj[docKey]?.status === '') { return; } handleDoc(dataObj[docKey]); if(!dataObj[docKey].withPassword) { handleLoading(true); handleSignSubmission(dataObj[docKey]); } handleShowPasswordPopup(true); }} className="hover:bg-gray-25 cursor-pointer block px-4 py-2 text-15 flex flex-row" role="menuitem" tabIndex="-1" ><div className="flex flex-col text-gray-80 text-left" >{dataObj[docKey].name}<div className="text-gray-70 pt-1 text-12">{formatMessage({id: "IN"})} {dataObj[docKey].filter}</div></div></div> ) })} </div> </div> } </div> </div> <div className="flex flex-row flex-wrap mt-5 mb-3"> {_.includes(manageContracts, filter) ? ( <div className="ml-3 flex flex-wrap"> <div className="flex flex-wrap"> <img src={menu} className="lg:hidden mr-3 h-8" onClick={() => { handleMobileSidebar(true); }} /> <div className="select-none text-15 sm:text-20 font-normal flex flex-col justify-center">{tableLabel}</div> </div> </div> ) : ( <div className="ml-3 flex flex-col"> <div className="flex flex-wrap text-15 sm:text-20 font-normal select-none"> <img src={menu} className="lg:hidden mr-3 h-8" onClick={() => { handleMobileSidebar(true); }} /> <div className="my-auto">{tableLabel}</div> <img className="ml-2 h-4 w-4 my-auto" src={RightArrow} /> <div className="relative w-32 sm:w-48 inline-block"> <div onClick={() => showMenu(!showStatusSelect)} className={`flex w-max pl-2 rounded-md text-15 sm:text-20 h-full my-auto ${showStatusSelect ? 'bg-gray-32':''}`}> <div className="my-auto ">{getDocumentFormattedStatus(status, formatMessage)}</div> <img className="mx-1 w-4 h-4 my-auto" src={DownArrow} /> </div> {showStatusSelect && <div id="status-selector" className="origin-top-right w-full absolute right-0 flex flex-col justify-center"> <div className={`z-50 text-15 mt-1 select-none w-full rounded-md shadow-lg bg-white ring-1 ring-gray-150 ring-opacity-100 focus:outline-none`} role="menu" aria-orientation="vertical" aria-labelledby="menu-button" tabIndex="-1"> <div className="py-1" role="none"> <div value="All Status" onClick={() => {updateStatus(-1)}} className="hover:bg-gray-25 cursor-pointer block px-4 py-2 text-15 text-gray-80 text-left" role="menuitem" tabIndex="-1" >{formatMessage({id: 'ALL_STATUS'})}</div> <div value="Pending Signatures" onClick={() => {updateStatus(2)}} className="hover:bg-gray-25 cursor-pointer block px-4 py-2 text-15 text-gray-80 text-left" role="menuitem" tabIndex="-1" >{formatMessage({id: 'PENDING_SIGNATURES'})}</div> <div value="Waiting For Others" onClick={() => {updateStatus(4)}} className="hover:bg-gray-25 cursor-pointer block px-4 py-2 text-15 text-gray-80 text-left" role="menuitem" tabIndex="-1" >{formatMessage({id: 'WAITING_FOR_OTHERS'})}</div> <div value="All Signed" onClick={() => {updateStatus(3)}} className="hover:bg-gray-25 cursor-pointer block px-4 py-2 text-15 text-gray-80 text-left" role="menuitem" tabIndex="-1" >{formatMessage({id: 'ALL_SIGNED'})}</div> <div value="PDF Not Uploaded" onClick={() => {updateStatus(0)}} className="hover:bg-gray-25 cursor-pointer block px-4 py-2 text-15 text-gray-80 text-left" role="menuitem" tabIndex="-1" >{formatMessage({id: 'PDF_NOT_UPLOADED'})}</div> </div> </div> </div> } </div> </div> </div> )} {totalContractsInCurrentFilter > 0 && <div className="ml-auto text-15 mr-3 flex flex-row text-gray-300"> <div className="h-8 sm:h-auto my-auto flex"> <div className="sm:h-auto my-auto"> {formatMessage({id: 'PAGE_COUNT'}, {first: ((pageNumber * contractsPerPage) + 1) > totalContractsInCurrentFilter ? totalContractsInCurrentFilter : (pageNumber * contractsPerPage) + 1, last: ((pageNumber + 1) * contractsPerPage) > totalContractsInCurrentFilter ? totalContractsInCurrentFilter : (pageNumber + 1) * contractsPerPage, total: totalContractsInCurrentFilter})} </div> </div> {pageNumber > 0 ? <img className="ml-2 h-4 w-4 my-auto cursor-pointer select-none" data-tip={"Newer"} src={LeftArrowPage} onClick={() => changePage(false)} /> : <img className="ml-2 h-4 w-4 my-auto select-none" data-tip={"Newer"} src={LeftArrowPageDisabled} /> } {(((pageNumber + 1) * contractsPerPage) + 1) <= totalContractsInCurrentFilter ? <img className="ml-2 h-4 w-4 my-auto cursor-pointer select-none" data-tip={"Older"} src={RightArrowPage} onClick={() => changePage(true)} /> : <img className="ml-2 h-4 w-4 my-auto select-none" data-tip={"Older"} src={RightArrowPageDisabled} /> } </div> } </div> {/* Popups are below */} {showPasswordPopup && <Alert title={formatMessage({id: "DOCUMENT_PASSWORD"})} message={formatMessage({id: "THIS_CONTRACT_IS_PROTECTED"})} loading={loading} displayLoader={doc.withPassword ? false : true} loadingText={doc.withPassword ? formatMessage({id: "DECRYPTING_YOUR_FILE_WITH_POINT"}) : formatMessage({id: "LOADING"})} closeCallback={() => handleShowPasswordPopup(false)} closeButtonText={formatMessage({id: "CANCEL"})} okCallback={() => handleSignSubmission(doc)} okButtonText={formatMessage({id: "OK"})} closeOnOutsideClick={true} customComponent={ <div className="flex flex-wrap sm:flex-nowrap justify-start"> <div className="mb-2 sm:my-auto mr-4 hidden sm:block">{formatMessage({id: "PASSWORD"})}:</div> <form onSubmit={(e) => {e.preventDefault(); handleSignSubmission(doc);}}> <input placeholder={formatMessage({id: "ENTER_YOUR_PASSWORD"})} required={false} name="password" type="password" autoComplete="new-password" autoFocus maxLength={32} id="password_input" className={`flex-grow-1 rounded-sm focus:outline-none rounded-sm px-4 py-1 border mr-0 border-gray-200`} > </input> </form> </div> } /> } </div> ); } export default withRouter(Searchbox);