Untitled
unknown
plain_text
6 months ago
9.0 kB
2
Indexable
Never
import { ApolloClient, useQuery } from '@apollo/client'; import * as React from 'react'; import { createSelector } from 'reselect'; import { EReputationProvider, WorkflowState, MainFrameProps, Permission, User, WorkflowProgressBar, } from '../../../../src/contexts/mainframe/_module_/types'; import callAPI2, { CallAPIResultType } from '../../../server/callAPI2'; import LoadingSpinner from '../../../components/LoadingSpinner'; import { fileAPIConfigURI } from '../../../withFileAPI'; import { LOGOUT_URL } from '../../../withAuth'; import Alert from '../../../components/Alert'; import AsyncCallManager from '../../../asyncHelper'; import { initializeI18n } from '../../../i18n'; import { NNU } from '../../../helpers'; import { AlertParams, MainFrameContext } from './withMainFrameContext'; import SettingsQuery from './Settings.graphql'; interface UserSettingsData { currentUser: User & { primaryValidatingUser: User | null }; permissions: Permission[]; stakeholderPermissions: Permission[]; eReputationAlertsPermissions: Permission[]; eReputationPermissions: Permission[]; questionnairePermissions: Permission[]; documentsStoragePermissions: Permission[]; folderFilePermissions: Permission[]; parameters: { languageTag: string; languageTexts: { content: string; }; documentationUrl: string | null; beneficiaryOwnerThreshold: number; eReputationProvider: EReputationProvider; }; features: { corpMasterDataIntegration: boolean; personMasterDataIntegration: boolean; dccpAnalysis: boolean; dccpFollowUp: boolean; folderRiskPeriodicUpdate: boolean; folderStatus: boolean; eReputationAlerts: boolean; votingRights: boolean; standaloneEreputation: boolean; questionnaires: boolean; customerSubsidiaries: boolean; folderFileTag: boolean; downloadDocsFromKyc: boolean; documentPortal: boolean; factivaContentIntegration: boolean; triggerExternalFolderFileAction: boolean; recreateCurrentStakeholderRoles: boolean; countryRiskMap: boolean; transformAttachmentIntoDocument: boolean; groupFilesByFamily: boolean; monitoring: boolean; beneficialOwners: boolean; displayKycReviewStatus: boolean; }; workflow: { states: WorkflowState[]; progressBars: WorkflowProgressBar[]; }; } export interface Props { children: (props: MainFrameProps) => React.ReactElement<MainFrameProps>; } interface State { alertParams: AlertParams | null; isLoading: number; documentServiceError: boolean; documentServiceLoading: boolean; isInitialized: boolean; documentServiceConfig: { maxContentLength: number; mimeTypes: string; extensions: string; } | null; } export interface MimeType { [mimeTypeElement: string]: string[]; } const MainFrameContainer: React.FC<Props> = (props) => { const [state, setState] = React.useState<State>({ alertParams: null, isLoading: 0, documentServiceError: false, documentServiceLoading: true, isInitialized: false, documentServiceConfig: null, }); const asyncOperations = React.useMemo(() => new AsyncCallManager(), []); // Specifies if the document service config is loading. // This is used to prevent making multiple calls to the document service config endpoint. // `state.documentServiceLoading` is not sufficient because if we requests were made during one rerender // then the `state.documentServiceLoading` will be updated only after all calls have been already made. const isDocumentServiceLoading = React.useRef(false); const fetchDocumentServiceConfig = React.useCallback(() => { isDocumentServiceLoading.current = true; setState((state) => ({ ...state, documentServiceLoading: true })); asyncOperations.execute({ operation: async () => { const response = await makeGetDocumentsConfigRequest(); if (response.type === CallAPIResultType.JSON_SUCCESS) { return mapDocumentConfig(response.body); } throw new Error('Unexpected response received from file API'); }, success: (documentServiceConfig) => { (isDocumentServiceLoading.current = false), setState((state) => ({ ...state, documentServiceLoading: false, isInitialized: true, documentServiceConfig, })), () => (isDocumentServiceLoading.current = true); }, error: () => { isDocumentServiceLoading.current = false; setState((state) => ({ ...state, documentServiceError: true, isInitialized: true, documentServiceLoading: false, })); }, }); }, [asyncOperations]); const userLogoutRequested = async () => { // logout from the server await callAPI2(LOGOUT_URL); }; const userAuthenticationExpired = async () => { // reload the current page to re-authenticate window.location.reload(); }; React.useEffect(() => { fetchDocumentServiceConfig(); return () => asyncOperations.ignoreResults(); }, [asyncOperations, fetchDocumentServiceConfig]); const { loading, error, data, client } = useQuery<UserSettingsData>(SettingsQuery, { nextFetchPolicy: 'cache-only', }); const getMainframeContext = createSelector( (userSettingsData: UserSettingsData) => userSettingsData, (_: UserSettingsData, documentServiceConfig: State['documentServiceConfig']) => documentServiceConfig, (_: UserSettingsData, __: State['documentServiceConfig'], client: ApolloClient<any>) => client, (userSettingsData, documentServiceConfig, client): MainFrameContext => { const userSettings = NNU(userSettingsData); const alert = (alertParams: AlertParams) => setState((state) => ({ ...state, alertParams })); const updateDocumentsServiceConfig = () => { if (state.documentServiceLoading || isDocumentServiceLoading.current) return; fetchDocumentServiceConfig(); }; return { userSettings: { ...userSettings, parameters: { ...userSettings.parameters, documentServiceConfig, }, }, alert, updateDocumentsServiceConfig, isLoading: (status) => setState((state) => ({ ...state, isLoading: state.isLoading + (status ? 1 : -1) })), mutate: (options, errorAlertOptions, updatingCallback?: () => void) => asyncOperations.execute({ operation: () => client.mutate(options), error: () => alert(errorAlertOptions), finally: () => updatingCallback && updatingCallback(), }), }; } ); if (error) { return ( <div data-notification className='main-frame-container-error bx--inline-notification bx--inline-notification--warning' role='alert' > <div className='bx--inline-notification__details'> <div className='bx--inline-notification__text-wrapper'> <p className='bx--inline-notification__title'> Erreur d'initialisation de l'application </p> <p className='bx--inline-notification__subtitle'>Veuillez réessayer</p> </div> </div> </div> ); } if (loading || (state.documentServiceLoading && !state.isInitialized)) { return <LoadingSpinner className='main-frame-container-loading' spinnerSize='fa-2x' />; } const userSettings = NNU(data); initializeI18n( JSON.parse(userSettings.parameters.languageTexts.content), userSettings.parameters.languageTag ); return ( <div className='main-frame-container'> <MainFrameContext.Provider value={getMainframeContext(NNU(data), state.documentServiceConfig, client)} > {props.children({ userLogoutRequested, userAuthenticationExpired, })} </MainFrameContext.Provider> {state.alertParams && ( <Alert {...state.alertParams} dismissed={() => setState((state) => ({ ...state, alertParams: null }))} /> )} {state.isLoading > 0 && <LoadingSpinner className='main-frame-loading-spinner' />} </div> ); }; export default MainFrameContainer; export const parseMimeType = (mime: MimeType) => Object.entries(mime) .map(([type, extension]) => type.startsWith('application/x-tika') ? `.${extension.toString()}` : type ) .toString(); interface RemoteDocumentsConfig { maxContentLength: number; mimeTypes: MimeType; } const makeGetDocumentsConfigRequest = () => { return callAPI2<RemoteDocumentsConfig>(fileAPIConfigURI); }; const mapDocumentConfig = (remoteConfig: RemoteDocumentsConfig) => ({ maxContentLength: remoteConfig.maxContentLength, mimeTypes: parseMimeType(remoteConfig.mimeTypes), extensions: Object.values(remoteConfig.mimeTypes) .reduce<string[]>((acc, extensions) => [...acc, ...extensions], []) .map((e) => `.${e}`) .join(' '), });