diff --git a/hrm-form-definitions/form-definitions/as/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/as/v1/flexUiLocales.json new file mode 100644 index 0000000000..801eaf1757 --- /dev/null +++ b/hrm-form-definitions/form-definitions/as/v1/flexUiLocales.json @@ -0,0 +1,5 @@ +[ + { "label": "English", "aseloLocale": "en-US" }, + { "label": "Thai", "aseloLocale": "th-TH" }, + { "label": "Español", "aseloLocale": "es-CL" } +] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/br/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/br/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/br/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/ca/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/ca/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/ca/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/cl/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/cl/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/cl/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/co/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/co/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/co/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/e2e/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/e2e/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/e2e/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/et/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/et/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/et/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/hu/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/hu/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/hu/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/in/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/in/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/in/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/jm/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/jm/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/jm/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/mt/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/mt/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/mt/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/mw/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/mw/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/mw/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/nz/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/nz/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/nz/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/ph/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/ph/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/ph/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/sg/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/sg/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/sg/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/th/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/th/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/th/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/tz/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/tz/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/tz/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/ukmh/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/ukmh/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/ukmh/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/usch/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/usch/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/usch/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/uscr/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/uscr/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/uscr/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/za/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/za/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/za/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/zm/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/zm/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/zm/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/form-definitions/zw/v1/flexUiLocales.json b/hrm-form-definitions/form-definitions/zw/v1/flexUiLocales.json new file mode 100644 index 0000000000..0637a088a0 --- /dev/null +++ b/hrm-form-definitions/form-definitions/zw/v1/flexUiLocales.json @@ -0,0 +1 @@ +[] \ No newline at end of file diff --git a/hrm-form-definitions/src/__tests__/fetchDefinitionsMock.ts b/hrm-form-definitions/src/__tests__/fetchDefinitionsMock.ts index ffc45846cc..87278619be 100644 --- a/hrm-form-definitions/src/__tests__/fetchDefinitionsMock.ts +++ b/hrm-form-definitions/src/__tests__/fetchDefinitionsMock.ts @@ -56,6 +56,7 @@ const files = [ 'profileForms/FlagDurations.json', 'customStrings/Messages.json', 'customStrings/Substitutions.json', + 'flexUiLocales.json', ]; const getDefinitionVersionId = (formDefinitionsBaseUrl: string) => diff --git a/hrm-form-definitions/src/formDefinition/loadDefinition.ts b/hrm-form-definitions/src/formDefinition/loadDefinition.ts index 941a7c1f25..222060d636 100644 --- a/hrm-form-definitions/src/formDefinition/loadDefinition.ts +++ b/hrm-form-definitions/src/formDefinition/loadDefinition.ts @@ -34,6 +34,7 @@ import { ProfileFlagDurationDefinition, ProfileSectionDefinition, LocalizedStringMap, + FlexUILocaleEntry, } from './types'; import { OneToManyConfigSpecs, OneToOneConfigSpec } from './insightsConfig'; import { LayoutVersion } from './layoutVersion'; @@ -196,6 +197,7 @@ export async function loadDefinition(baseUrl: string): Promise('LayoutDefinitions.json'), fetchDefinition('tabbedForms/CallerInformationTab.json'), @@ -225,6 +227,7 @@ export async function loadDefinition(baseUrl: string): Promise('profileForms/FlagDurations.json', []), fetchDefinition('customStrings/Messages.json', {}), fetchDefinition('customStrings/Substitutions.json', {}), + fetchDefinition('flexUiLocales.json', []), ] as const); const expandedCaseSections: CaseSectionTypeDefinitions = await loadAndExpandCaseSections( caseSections, @@ -267,5 +270,6 @@ export async function loadDefinition(baseUrl: string): Promise; const setUpLocalization = (config: ReturnType) => { const manager = Flex.Manager.getInstance(); - const { helplineLanguage } = config; + const { counselorLanguage, helplineLanguage } = config; const twilioStrings = { ...manager.strings }; // save the originals @@ -72,7 +72,10 @@ const setUpLocalization = (config: ReturnType) => { const localizationConfig = { twilioStrings, setNewStrings, afterNewStrings }; - return initLocalization(localizationConfig, helplineLanguage); + return initLocalization( + localizationConfig, + localStorage.getItem('ASELO_PLUGIN_USER_LOCALE') || counselorLanguage || helplineLanguage || defaultLanguage, + ); }; const setUpComponents = ( @@ -127,6 +130,8 @@ const setUpComponents = ( TeamsView.setUpWorkerDirectoryFilters(); if (featureFlags.enable_conferencing) setupConferenceComponents(); + + Components.setupWorkerLanguageSelect(translateUI); }; const setUpActions = ( diff --git a/plugin-hrm-form/src/___tests__/components/case/Case.test.tsx b/plugin-hrm-form/src/___tests__/components/case/Case.test.tsx index 1a87efaa7b..d58e7161a5 100644 --- a/plugin-hrm-form/src/___tests__/components/case/Case.test.tsx +++ b/plugin-hrm-form/src/___tests__/components/case/Case.test.tsx @@ -84,10 +84,17 @@ beforeEach(() => { describe('useState mocked', () => { const verifyTimelineActions = () => { ['household', 'incident', 'perpetrator'].forEach(sectionType => { - expect(mockNewGetTimelineAction).toHaveBeenCalledWith(case1.connectedCase.id, sectionType, [sectionType], false, { - limit: 100, - offset: 0, - }); + expect(mockNewGetTimelineAction).toHaveBeenCalledWith( + case1.connectedCase.id, + sectionType, + [sectionType], + false, + { + limit: 100, + offset: 0, + }, + `case-${case1.connectedCase.id}`, + ); }); expect(mockNewGetTimelineAction).toHaveBeenCalledWith( @@ -96,6 +103,7 @@ describe('useState mocked', () => { expect.arrayContaining(['referral', 'note']), true, { limit: 5, offset: 0 }, + `case-${case1.connectedCase.id}`, ); }; @@ -136,12 +144,8 @@ describe('useState mocked', () => { twilioWorkerId: WORKER_SID, status: 'open', info: { definitionVersion: DefinitionVersionId.v1 }, - categories: {}, accountSid: 'AC-accountSid', helpline: 'helpline', - firstContact: { - id: 'contact1', - } as Contact, }, availableStatusTransitions: [], references: new Set(['x']), diff --git a/plugin-hrm-form/src/___tests__/states/case/timeline.test.ts b/plugin-hrm-form/src/___tests__/states/case/timeline.test.ts index 0f372b24ac..5146d7e0b3 100644 --- a/plugin-hrm-form/src/___tests__/states/case/timeline.test.ts +++ b/plugin-hrm-form/src/___tests__/states/case/timeline.test.ts @@ -111,6 +111,7 @@ describe('newGetTimelineAsyncAction', () => { ['note', 'household'], true, SAMPLE_PAGINATION, + 'test-reference', ); expect(getCaseTimeline).toHaveBeenCalledWith(TEST_CASE_ID, ['note', 'household'], true, SAMPLE_PAGINATION); @@ -120,6 +121,7 @@ describe('newGetTimelineAsyncAction', () => { timelineId: TEST_TIMELINE_ID, caseId: TEST_CASE_ID, pagination: SAMPLE_PAGINATION, + reference: 'test-reference', }); }); @@ -197,7 +199,14 @@ describe('newGetTimelineAsyncAction', () => { } = getState() as HrmState; mockGetCaseTimeline.mockResolvedValue(apiResponse); await ((dispatch( - newGetTimelineAsyncAction(TEST_CASE_ID, TEST_TIMELINE_ID, ['note', 'household'], true, pagination), + newGetTimelineAsyncAction( + TEST_CASE_ID, + TEST_TIMELINE_ID, + ['note', 'household'], + true, + pagination, + 'test-reference', + ), ) as unknown) as PromiseLike); const { connectedCase: { diff --git a/plugin-hrm-form/src/components/case/CaseSection.tsx b/plugin-hrm-form/src/components/case/CaseSection.tsx index 8e29c4f64b..3fa6a5caad 100644 --- a/plugin-hrm-form/src/components/case/CaseSection.tsx +++ b/plugin-hrm-form/src/components/case/CaseSection.tsx @@ -65,7 +65,14 @@ const CaseSection: React.FC = ({ taskSid, canAdd, sectionType }) => { // eslint-disable-next-line no-console console.log(`Fetching ${sectionType} sections for case ${caseId}`); asyncDispatcher( - newGetTimelineAsyncAction(caseId, sectionType, [sectionType], false, { offset: 0, limit: MAX_SECTIONS }), + newGetTimelineAsyncAction( + caseId, + sectionType, + [sectionType], + false, + { offset: 0, limit: MAX_SECTIONS }, + `case-${caseId}`, + ), ); } // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/plugin-hrm-form/src/components/case/casePrint/CasePrintView.tsx b/plugin-hrm-form/src/components/case/casePrint/CasePrintView.tsx index 9bfb733e4c..9e9ba51063 100644 --- a/plugin-hrm-form/src/components/case/casePrint/CasePrintView.tsx +++ b/plugin-hrm-form/src/components/case/casePrint/CasePrintView.tsx @@ -115,10 +115,17 @@ const CasePrintView: React.FC = ({ task }) => { useEffect(() => { if (!contactTimeline) { dispatch( - newGetTimelineAsyncAction(connectedCase.id, 'print-contacts', [], true, { - offset: 0, - limit: MAX_PRINTOUT_CONTACTS, - }), + newGetTimelineAsyncAction( + connectedCase.id, + 'print-contacts', + [], + true, + { + offset: 0, + limit: MAX_PRINTOUT_CONTACTS, + }, + `case-${connectedCase.id}`, + ), ); } // eslint-disable-next-line react-hooks/exhaustive-deps @@ -129,10 +136,17 @@ const CasePrintView: React.FC = ({ task }) => { useEffect( () => { dispatch( - newGetTimelineAsyncAction(connectedCase.id, sectionType, [sectionType], false, { - offset: 0, - limit: MAX_SECTIONS, - }), + newGetTimelineAsyncAction( + connectedCase.id, + sectionType, + [sectionType], + false, + { + offset: 0, + limit: MAX_SECTIONS, + }, + `case-${connectedCase.id}`, + ), ); }, // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/plugin-hrm-form/src/components/case/timeline/FullTimelineView.tsx b/plugin-hrm-form/src/components/case/timeline/FullTimelineView.tsx index ef7789d48c..aecdbd7c27 100644 --- a/plugin-hrm-form/src/components/case/timeline/FullTimelineView.tsx +++ b/plugin-hrm-form/src/components/case/timeline/FullTimelineView.tsx @@ -53,10 +53,17 @@ const mapDispatchToProps = (dispatch: Dispatch, { task }: MyProps) => { changeRoute({ route: 'case', subroute: 'timeline', caseId, page }, task.taskSid, ChangeRouteMode.Replace), ); dispatch( - newGetTimelineAsyncAction(caseId, 'prime-timeline', ['note', 'referral'], true, { - offset: page * TIMELINE_PAGE_SIZE, - limit: TIMELINE_PAGE_SIZE, - }), + newGetTimelineAsyncAction( + caseId, + 'prime-timeline', + ['note', 'referral'], + true, + { + offset: page * TIMELINE_PAGE_SIZE, + limit: TIMELINE_PAGE_SIZE, + }, + `case-${caseId}`, + ), ); }, }; diff --git a/plugin-hrm-form/src/components/case/timeline/Timeline.tsx b/plugin-hrm-form/src/components/case/timeline/Timeline.tsx index 2419125853..b7212625e0 100644 --- a/plugin-hrm-form/src/components/case/timeline/Timeline.tsx +++ b/plugin-hrm-form/src/components/case/timeline/Timeline.tsx @@ -92,10 +92,17 @@ const Timeline: React.FC = ({ if (caseId) { console.log(`Fetching main timeline sections for case ${caseId}`); asyncDispatch(dispatch)( - newGetTimelineAsyncAction(caseId, timelineId, timelineCaseSectionTypes, true, { - offset: page * pageSize, - limit: pageSize, - }), + newGetTimelineAsyncAction( + caseId, + timelineId, + timelineCaseSectionTypes, + true, + { + offset: page * pageSize, + limit: pageSize, + }, + `case-${caseId}`, + ), ); } // eslint-disable-next-line react-hooks/exhaustive-deps diff --git a/plugin-hrm-form/src/components/caseList/CaseListTableRow.tsx b/plugin-hrm-form/src/components/caseList/CaseListTableRow.tsx index e2cf23c33a..2bfa38f348 100644 --- a/plugin-hrm-form/src/components/caseList/CaseListTableRow.tsx +++ b/plugin-hrm-form/src/components/caseList/CaseListTableRow.tsx @@ -95,7 +95,9 @@ const CaseListTableRow: React.FC = ({ caseId, handleClickViewCase }) => { useEffect(() => { if (!timelineCategories) { - dispatch(newGetTimelineAsyncAction(caseId, CONTACTS_TIMELINE_ID, [], true, { offset: 0, limit: 10000 })); + dispatch( + newGetTimelineAsyncAction(caseId, CONTACTS_TIMELINE_ID, [], true, { offset: 0, limit: 10000 }, `case-list`), + ); } }, [timelineCategories, caseId, dispatch]); diff --git a/plugin-hrm-form/src/components/search/CasePreview/index.tsx b/plugin-hrm-form/src/components/search/CasePreview/index.tsx index 3b1ae0bd4d..f521c311e8 100644 --- a/plugin-hrm-form/src/components/search/CasePreview/index.tsx +++ b/plugin-hrm-form/src/components/search/CasePreview/index.tsx @@ -39,6 +39,7 @@ import { newGetTimelineAsyncAction, selectCaseLabel, selectTimelineContactCategories, + selectTimelineCount, } from '../../../states/case/timeline'; type Props = { @@ -60,6 +61,9 @@ const CasePreview: React.FC = ({ currentCase, onClickViewCase, counselors const timelineCategories = useSelector((state: RootState) => selectTimelineContactCategories(state, currentCase.id, CONTACTS_TIMELINE_ID), ); + const contactCount = useSelector((state: RootState) => + selectTimelineCount(state, currentCase.id, CONTACTS_TIMELINE_ID), + ); const caseLabel = useSelector((state: RootState) => selectCaseLabel(state, currentCase.id, CONTACTS_TIMELINE_ID, { substituteForId: false, @@ -71,15 +75,24 @@ const CasePreview: React.FC = ({ currentCase, onClickViewCase, counselors useEffect(() => { if (!timelineCategories) { - dispatch(newGetTimelineAsyncAction(currentCase.id, CONTACTS_TIMELINE_ID, [], true, { offset: 0, limit: 10000 })); + dispatch( + newGetTimelineAsyncAction( + currentCase.id, + CONTACTS_TIMELINE_ID, + [], + true, + { offset: 0, limit: 10000 }, + `search-${task.taskSid}`, + ), + ); } - }, [timelineCategories, currentCase.id, dispatch]); + }, [timelineCategories, currentCase.id, dispatch, task.taskSid]); const { id, createdAt, status, info, twilioWorkerId } = currentCase; const updatedAtObj = getUpdatedDate(currentCase); const followUpDateObj = info.followUpDate ? new Date(info.followUpDate) : undefined; const { definitionVersion: versionId } = info; - const orphanedCase = !timelineCategories; + const orphanedCase = contactCount === 0; const summary = info?.summary; const counselor = counselorsHash[twilioWorkerId]; diff --git a/plugin-hrm-form/src/components/translator/index.jsx b/plugin-hrm-form/src/components/translator/index.jsx deleted file mode 100644 index cbbfe86a01..0000000000 --- a/plugin-hrm-form/src/components/translator/index.jsx +++ /dev/null @@ -1,84 +0,0 @@ -/** - * Copyright (C) 2021-2023 Technology Matters - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published - * by the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see https://www.gnu.org/licenses/. - */ - -import React from 'react'; -import PropTypes from 'prop-types'; -import { CircularProgress, FormControl, InputLabel, Select, MenuItem } from '@material-ui/core'; -import { connect } from 'react-redux'; - -import { configurationBase, namespace } from '../../states/storeNamespaces'; - -class Translator extends React.PureComponent { - static displayName = 'Translator'; - - static propTypes = { - manager: PropTypes.shape({ - strings: PropTypes.shape({ - TranslateButtonAriaLabel: PropTypes.string, - }), - }).isRequired, - translateUI: PropTypes.func.isRequired, - language: PropTypes.string.isRequired, - }; - - state = { - loading: false, - }; - - // receives the new language selected (passed via event value) - handleChange = e => { - const language = e.target.value; - if (!this.state.loading && language !== this.props.language) { - this.setState({ loading: true }, async () => { - await this.props.translateUI(language); - setTimeout(() => this.setState({ loading: false }), 1000); - }); - } - }; - - render() { - const { TranslateButtonAriaLabel } = this.props.manager.strings; - - return ( - - {TranslateButtonAriaLabel} - - {this.state.loading && } - - ); - } -} - -const mapStateToProps = (state, ownProps) => { - const configurationState = state[namespace][configurationBase]; - - return { - language: configurationState.language, - }; -}; - -export default connect(mapStateToProps, null)(Translator); diff --git a/plugin-hrm-form/src/components/translator/index.tsx b/plugin-hrm-form/src/components/translator/index.tsx new file mode 100644 index 0000000000..1abd9b278d --- /dev/null +++ b/plugin-hrm-form/src/components/translator/index.tsx @@ -0,0 +1,117 @@ +/** + * Copyright (C) 2021-2023 Technology Matters + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ + +import React, { useState } from 'react'; +import * as Flex from '@twilio/flex-ui'; +import { CircularProgress, FormControl, InputLabel, Select, MenuItem } from '@material-ui/core'; +import { useDispatch, useSelector } from 'react-redux'; + +import { selectLanguage } from '../../states/configuration/selectLanguage'; +import { selectCurrentDefinitionVersion } from '../../states/configuration/selectDefinitions'; +import { changeLanguage } from '../../states/configuration/actions'; + +type Props = { + translateUI: (language: string) => Promise; + manager: Flex.Manager; +}; + +const Translator: React.FC = ({ manager }) => { + const language = useSelector(selectLanguage); + const { flexUiLocales } = useSelector(selectCurrentDefinitionVersion); + const dispatch = useDispatch(); + const [loading, setLoading] = useState(false); + + if (flexUiLocales.length < 2) return null; + + // receives the new language selected (passed via event value) + // eslint-disable-next-line sonarjs/cognitive-complexity + const handleChange = async e => { + const selectedLocale = e.target.value; + if (!loading && selectedLocale !== language) { + setLoading(true); + // await translateUI(selectedLanguage); + localStorage.setItem('ASELO_PLUGIN_USER_LOCALE', selectedLocale); + dispatch(changeLanguage(selectedLocale)); + const { availableLocales } = manager.localization; + const specifiedFlexLocale = flexUiLocales.find(locale => locale === selectedLocale)?.flexLocale; + if (specifiedFlexLocale) { + // + if (availableLocales.find(locale => locale.tag === selectedLocale)) { + await manager.localization.setLocalePreference(specifiedFlexLocale); + return; + } + console.warn( + `The configured Flex Locale '${specifiedFlexLocale}' for Aselo Locale '${selectedLocale}' is not supported in this version of Flex UI. Attempting to find a best match from available locales`, + ); + } else { + const exactMatch = availableLocales.find(({ tag }) => tag === selectedLocale)?.tag; + if (exactMatch) { + console.info( + `Aselo Locale '${selectedLocale}' is also a supported Flex Locale, setting Flex Llocale to '${selectedLocale}'`, + ); + await manager.localization.setLocalePreference(exactMatch); + } else { + const [selectedLanguage] = selectedLocale.split('-'); + const languageMatch = availableLocales.find(l => { + const [availableLocaleLanguage] = l.tag.split('-'); + return availableLocaleLanguage === selectedLanguage; + })?.tag; + if (languageMatch) { + console.info( + `Aselo Locale '${selectedLocale}' is not supported, but a locale with the same language, '${languageMatch}' is supported, so using that`, + ); + await manager.localization.setLocalePreference(languageMatch); + } else { + console.info( + `Aselo Locale '${selectedLocale}' is not supported, nor are any with the same language, falling back to global default (en-US)`, + ); + await manager.localization.setLocalePreference('en-US'); + } + } + } + setLoading(false); + } + }; + + const { TranslateButtonAriaLabel } = manager.strings as any; + + return ( + + {TranslateButtonAriaLabel} + + {loading && } + + ); +}; + +Translator.displayName = 'Translator'; + +export default Translator; diff --git a/plugin-hrm-form/src/states/case/hooks/useCaseSections.ts b/plugin-hrm-form/src/states/case/hooks/useCaseSections.ts index 9cf05c55d2..c2e1174527 100644 --- a/plugin-hrm-form/src/states/case/hooks/useCaseSections.ts +++ b/plugin-hrm-form/src/states/case/hooks/useCaseSections.ts @@ -52,7 +52,9 @@ const useCaseSectionsLoader = ({ return; } - asyncDispatch(dispatch)(newGetTimelineAsyncAction(caseId, sectionType, [sectionType], false, { offset, limit })); + asyncDispatch(dispatch)( + newGetTimelineAsyncAction(caseId, sectionType, [sectionType], false, { offset, limit }, `case-${caseId}`), + ); }, [caseId, dispatch, limit, offset, sectionType]); const safeToLoad = Boolean(caseId) && exists; diff --git a/plugin-hrm-form/src/states/case/timeline.ts b/plugin-hrm-form/src/states/case/timeline.ts index c4f46624ec..4c1fb92fcd 100644 --- a/plugin-hrm-form/src/states/case/timeline.ts +++ b/plugin-hrm-form/src/states/case/timeline.ts @@ -47,17 +47,20 @@ export const newGetTimelineAsyncAction = createAsyncAction( sectionTypes: string[], includeContacts: boolean, pagination: PaginationSettings, + reference: string, ): Promise<{ timelineResult: TimelineResult; caseId: Case['id']; timelineId: string; pagination: PaginationSettings; + reference: string; }> => { return { timelineResult: await getCaseTimeline(caseId, sectionTypes, includeContacts, pagination), caseId, timelineId, pagination, + reference, }; }, ); @@ -211,5 +214,5 @@ export const selectCaseLabel = ( return contactLabelFromHrmContact(def, firstContact.activity, contactLabelOptions); } } - return undefined; + return contactLabelOptions?.placeholder ?? ''; }; diff --git a/plugin-hrm-form/src/states/configuration/reducer.ts b/plugin-hrm-form/src/states/configuration/reducer.ts index 572affd0c9..5c02d1554a 100644 --- a/plugin-hrm-form/src/states/configuration/reducer.ts +++ b/plugin-hrm-form/src/states/configuration/reducer.ts @@ -32,7 +32,7 @@ export type ConfigurationState = { }; const initialState: ConfigurationState = { - language: defaultLanguage, + language: localStorage.getItem('ASELO_PLUGIN_USER_LOCALE') || defaultLanguage, counselors: { list: [], hash: {}, diff --git a/plugin-hrm-form/src/states/configuration/selectLanguage.ts b/plugin-hrm-form/src/states/configuration/selectLanguage.ts new file mode 100644 index 0000000000..16fe75f668 --- /dev/null +++ b/plugin-hrm-form/src/states/configuration/selectLanguage.ts @@ -0,0 +1,20 @@ +/** + * Copyright (C) 2021-2025 Technology Matters + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published + * by the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see https://www.gnu.org/licenses/. + */ + +import { RootState } from '..'; +import { namespace } from '../storeNamespaces'; + +export const selectLanguage = (state: RootState) => state[namespace].configuration.language; diff --git a/plugin-hrm-form/src/states/contacts/reducer.ts b/plugin-hrm-form/src/states/contacts/reducer.ts index deabcd47a6..159517c7c0 100644 --- a/plugin-hrm-form/src/states/contacts/reducer.ts +++ b/plugin-hrm-form/src/states/contacts/reducer.ts @@ -258,9 +258,9 @@ export function reduce( return loadContactListIntoState(state, rootState.configuration, action.searchResult.contacts, `${action.taskId}-search-contact`); } case GET_CASE_TIMELINE_ACTION_FULFILLED: { - const { payload: { caseId, timelineResult: { activities } } } = action; + const { payload: { timelineResult: { activities }, reference } } = action; const contacts = activities.filter(isContactTimelineActivity).map(({ activity })=> activity); - return loadContactListIntoState(state, rootState.configuration, contacts, `case-${caseId}`, false); + return loadContactListIntoState(state, rootState.configuration, contacts, reference, false); } case CREATE_CASE_ACTION_FULFILLED: { const { payload: { connectedContact } } = action as CreateCaseAsyncActionFulfilled; diff --git a/plugin-hrm-form/src/utils/setUpComponents.tsx b/plugin-hrm-form/src/utils/setUpComponents.tsx index 0ca0b264bf..b70a7464ab 100644 --- a/plugin-hrm-form/src/utils/setUpComponents.tsx +++ b/plugin-hrm-form/src/utils/setUpComponents.tsx @@ -263,7 +263,7 @@ export const setUpDeveloperComponents = (translateUI: (language: string) => Prom key="SettingsSideLink" onClick={() => Flex.Actions.invokeAction('NavigateToView', { viewName: 'settings' })} reserveSpace={false} - showLabel={false} + showLabel={true} />, { align: 'end', @@ -414,3 +414,10 @@ export const setupCannedResponses = () => { export const setupEmojiPicker = () => { Flex.MessageInputActions.Content.add(); }; + +export const setupWorkerLanguageSelect = (translateUI: (language: string) => Promise) => { + Flex.MainHeader.Content.add( + , + { align: 'end', sortOrder: 0 }, + ); +};