123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- import React, { useCallback } from 'react';
- import { useTranslation } from 'react-i18next';
- import { useDispatch } from 'react-redux';
- import { makeStyles } from 'tss-react/mui';
-
- import { hideDialog } from '../../../base/dialog/actions';
- import Icon from '../../../base/icons/components/Icon';
- import { IconSearch } from '../../../base/icons/svg';
- import { getFieldValue } from '../../../base/react/functions';
- import { withPixelLineHeight } from '../../../base/styles/functions.web';
- import Dialog from '../../../base/ui/components/web/Dialog';
- import Spinner from '../../../base/ui/components/web/Spinner';
- import { NOTES_MAX_LENGTH } from '../../constants';
- import { useSalesforceLinkDialog } from '../../useSalesforceLinkDialog';
-
- import { RecordItem } from './RecordItem';
-
- const useStyles = makeStyles()(theme => {
- return {
- container: {
- height: '450px',
- overflowY: 'auto',
- position: 'relative'
- },
- recordsSearchContainer: {
- position: 'relative',
- padding: '1px'
- },
- searchIcon: {
- display: 'block',
- position: 'absolute',
- color: theme.palette.text03,
- left: 16,
- top: 10,
- width: 20,
- height: 20
- },
- resultLabel: {
- fontSize: '15px',
- margin: '16px 0 8px'
- },
- recordsSearch: {
- backgroundColor: theme.palette.field01,
- border: '1px solid',
- borderRadius: theme.shape.borderRadius,
- borderColor: theme.palette.ui05,
- color: theme.palette.text01,
- padding: '10px 16px 10px 44px',
- width: '100%',
- height: 40,
- '&::placeholder': {
- color: theme.palette.text03,
- ...withPixelLineHeight(theme.typography.bodyShortRegular)
- }
- },
- spinner: {
- alignItems: 'center',
- display: 'flex',
- height: 'calc(100% - 70px)',
- justifyContent: 'center',
- width: '100%',
-
- '@media (max-width: 448px)': {
- height: 'auto',
- marginTop: '24px'
- }
- },
- noRecords: {
- height: 'calc(100% - 150px)',
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- flexDirection: 'column',
-
- '@media (max-width: 448px)': {
- height: 'auto',
- marginTop: '24px'
- }
- },
- recordsError: {
- height: 'calc(100% - 42px)',
- display: 'flex',
- alignItems: 'center',
- justifyContent: 'center',
- flexDirection: 'column',
-
- '@media (max-width: 448px)': {
- height: 'auto',
- marginTop: '24px'
- }
- },
- recordList: {
- listStyle: 'none',
- margin: '10px 0',
- padding: 0
- },
- recordInfo: {
- backgroundColor: theme.palette.ui03,
- padding: '0 16px',
- borderRadius: theme.shape.borderRadius,
- marginBottom: '28px'
- },
- detailsError: {
- padding: '10px 0'
- },
- addNote: {
- padding: '10px 0'
- },
- notes: {
- lineHeight: '18px',
- minHeight: '130px',
- resize: 'vertical',
- width: '100%',
- boxSizing: 'border-box',
- overflow: 'hidden',
- border: '1px solid',
- borderColor: theme.palette.ui05,
- backgroundColor: theme.palette.field01,
- color: theme.palette.text01,
- borderRadius: theme.shape.borderRadius,
- padding: '10px 16px'
- }
- };
- });
-
-
- /**
- * Component that renders the Salesforce link dialog.
- *
- * @returns {React$Element<any>}
- */
- function SalesforceLinkDialog() {
- const { t } = useTranslation();
- const { classes, theme } = useStyles();
- const dispatch = useDispatch();
- const {
- hasDetailsErrors,
- hasRecordsErrors,
- isLoading,
- linkMeeting,
- notes,
- records,
- searchTerm,
- selectedRecord,
- selectedRecordOwner,
- setNotes,
- setSearchTerm,
- setSelectedRecord,
- showNoResults,
- showSearchResults
- } = useSalesforceLinkDialog();
-
- const handleChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
- const value = getFieldValue(event);
-
- setSearchTerm(value);
- }, [ getFieldValue ]);
-
- const handleSubmit = useCallback(() => {
- dispatch(hideDialog());
- selectedRecord && linkMeeting();
- }, [ hideDialog, linkMeeting ]);
-
- const renderSpinner = () => (
- <div className = { classes.spinner }>
- <Spinner />
- </div>
- );
-
- const renderDetailsErrors = () => (
- <div className = { classes.detailsError }>
- {t('dialog.searchResultsDetailsError')}
- </div>
- );
-
- const renderSelection = () => (
- <div>
- <div className = { classes.recordInfo }>
- <RecordItem { ...selectedRecord } />
- {selectedRecordOwner && <RecordItem { ...selectedRecordOwner } />}
- {hasDetailsErrors && renderDetailsErrors()}
- </div>
- <div className = { classes.addNote }>{t('dialog.addOptionalNote')}</div>
- <textarea
- autoFocus = { true }
- className = { classes.notes }
- maxLength = { NOTES_MAX_LENGTH }
- /* eslint-disable-next-line react/jsx-no-bind */
- onChange = { e => setNotes(e.target.value) }
- placeholder = { t('dialog.addMeetingNote') }
- rows = { 4 }
- value = { notes } />
- </div>
- );
-
- const renderRecordsSearch = () => !selectedRecord && (
- <div className = { classes.recordsSearchContainer }>
- <Icon
- className = { classes.searchIcon }
- color = { theme.palette.icon03 }
- src = { IconSearch } />
- <input
- autoComplete = 'off'
- autoFocus = { false }
- className = { classes.recordsSearch }
- name = 'recordsSearch'
- onChange = { handleChange }
- placeholder = { t('dialog.searchInSalesforce') }
- tabIndex = { 0 }
- value = { searchTerm ?? '' } />
- {(!isLoading && !hasRecordsErrors) && (
- <div className = { classes.resultLabel }>
- {showSearchResults
- ? t('dialog.searchResults', { count: records.length })
- : t('dialog.recentlyUsedObjects')
- }
- </div>
- )}
- </div>
- );
-
- const renderNoRecords = () => showNoResults && (
- <div className = { classes.noRecords }>
- <div>{t('dialog.searchResultsNotFound')}</div>
- <div>{t('dialog.searchResultsTryAgain')}</div>
- </div>
- );
-
- const renderRecordsError = () => (
- <div className = { classes.recordsError }>
- {t('dialog.searchResultsError')}
- </div>
- );
-
- const renderContent = () => {
- if (isLoading) {
- return renderSpinner();
- }
- if (hasRecordsErrors) {
- return renderRecordsError();
- }
- if (showNoResults) {
- return renderNoRecords();
- }
- if (selectedRecord) {
- return renderSelection();
- }
-
- return (
- <ul className = { classes.recordList }>
- {records.map((item: any) => (
- <RecordItem
- key = { `record-${item.id}` }
- /* eslint-disable-next-line react/jsx-no-bind */
- onClick = { () => setSelectedRecord(item) }
- { ...item } />
- ))}
- </ul>
- );
- };
-
- return (
- <Dialog
- back = {{
- hidden: !selectedRecord,
- onClick: () => setSelectedRecord(null),
- translationKey: 'dialog.Back'
- }}
- cancel = {{ hidden: true }}
- disableEnter = { true }
- ok = {{
- translationKey: 'dialog.linkMeeting',
- hidden: !selectedRecord
- }}
- onSubmit = { handleSubmit }
- titleKey = 'dialog.linkMeetingTitle'>
- <div className = { classes.container } >
- {renderRecordsSearch()}
- {renderContent()}
- </div>
- </Dialog>
- );
- }
-
- export default SalesforceLinkDialog;
|