diff --git a/client/apps/Planning/PlanningList.tsx b/client/apps/Planning/PlanningList.tsx index 22af04c36..2a77ac744 100644 --- a/client/apps/Planning/PlanningList.tsx +++ b/client/apps/Planning/PlanningList.tsx @@ -16,6 +16,7 @@ import { LIST_VIEW_TYPE, IContactItem, SORT_FIELD, + ICommonSearchParams, } from '../../interfaces'; import * as actions from '../../actions'; @@ -59,6 +60,7 @@ interface IProps { contacts: {[key: string]: IContactItem}; listViewType: LIST_VIEW_TYPE; sortField: SORT_FIELD; + currentSearch: ICommonSearchParams; openPreview(item: IEventOrPlanningItem): void; edit(item: IEventOrPlanningItem): void; @@ -92,6 +94,7 @@ const mapStateToProps = (state) => ({ contacts: selectors.general.contactsById(state), listViewType: selectors.main.getCurrentListViewType(state), sortField: selectors.main.getCurrentSortField(state), + currentSearch: selectors.main.currentSearch(state) }); const mapDispatchToProps = (dispatch) => ({ @@ -168,6 +171,7 @@ export class PlanningListComponent extends React.PureComponent { contacts, listViewType, sortField, + currentSearch } = this.props; return ( @@ -206,6 +210,7 @@ export class PlanningListComponent extends React.PureComponent { listViewType={listViewType} sortField={sortField} indexItems + searchParams={currentSearch.advancedSearch} /> ); diff --git a/client/components/Events/EventItem.tsx b/client/components/Events/EventItem.tsx index d338bc4de..f61570239 100644 --- a/client/components/Events/EventItem.tsx +++ b/client/components/Events/EventItem.tsx @@ -22,6 +22,7 @@ import {renderFields} from '../fields'; import {CreatedUpdatedColumn} from '../UI/List/CreatedUpdatedColumn'; import {EventDateTimeColumn} from './EventDateTimeColumn'; import * as actions from '../../actions'; +import {getUserInterfaceLanguageFromCV} from '../../utils/users'; interface IState { hover: boolean; @@ -44,7 +45,8 @@ class EventItemComponent extends React.Component { return isItemDifferent(this.props, nextProps) || this.state.hover !== nextState.hover || this.props.minTimeWidth !== nextProps.minTimeWidth || - this.props.lockedItems != nextProps.lockedItems; + this.props.lockedItems != nextProps.lockedItems || + this.props.filterLanguage !== nextProps.filterLanguage; } onItemHoverOn() { @@ -156,6 +158,7 @@ class EventItemComponent extends React.Component { active, refNode, listViewType, + filterLanguage } = this.props; if (!item) { @@ -177,6 +180,7 @@ class EventItemComponent extends React.Component { const isExpired = isItemExpired(item); const secondaryFields = get(listFields, 'event.secondary_fields', EVENTS.LIST.SECONDARY_FIELDS); + const language = filterLanguage || item.language || getUserInterfaceLanguageFromCV(); return ( { {renderFields(get(listFields, 'event.primary_fields', - EVENTS.LIST.PRIMARY_FIELDS), item)} + EVENTS.LIST.PRIMARY_FIELDS), item, {}, language)} diff --git a/client/components/Main/ListGroup.tsx b/client/components/Main/ListGroup.tsx index 82e469aa6..d3cbac9df 100644 --- a/client/components/Main/ListGroup.tsx +++ b/client/components/Main/ListGroup.tsx @@ -2,7 +2,7 @@ import React from 'react'; import moment from 'moment-timezone'; import {ListGroupItem} from './'; import {Group, Header} from '../UI/List'; -import {IEventOrPlanningItem, LIST_VIEW_TYPE, SORT_FIELD} from '../../interfaces'; +import {ICommonAdvancedSearchParams, IEventOrPlanningItem, LIST_VIEW_TYPE, SORT_FIELD} from '../../interfaces'; import {timeUtils} from '../../utils'; const TIME_COLUMN_MIN_WIDTH = { @@ -78,6 +78,7 @@ interface IProps { listViewType?: string; sortField?: string; listBoxGroupProps: {}; + searchParams:ICommonAdvancedSearchParams; } export class ListGroup extends React.Component { @@ -145,6 +146,7 @@ export class ListGroup extends React.Component { listViewType, sortField, listBoxGroupProps, + searchParams, } = this.props; // with defaults @@ -205,6 +207,7 @@ export class ListGroup extends React.Component { listViewType: listViewType, sortField: sortField, minTimeWidth: minTimeWidth, + searchParams: searchParams, }; if (indexItems) { diff --git a/client/components/Main/ListGroupItem.tsx b/client/components/Main/ListGroupItem.tsx index 821d628c9..5290de171 100644 --- a/client/components/Main/ListGroupItem.tsx +++ b/client/components/Main/ListGroupItem.tsx @@ -1,11 +1,10 @@ import React from 'react'; import {debounce, indexOf} from 'lodash'; - import { IEventListItemProps, IPlanningListItemProps, IEventOrPlanningItem, - IEventItem, IPlanningItem, IBaseListItemProps + IEventItem, IPlanningItem, IBaseListItemProps, ICommonAdvancedSearchParams } from '../../interfaces'; import {EventItem, EventItemWithPlanning} from '../Events'; @@ -27,6 +26,7 @@ interface IProps extends Omit< index: number; navigateDown?: boolean; minTimeWidth?: string; + searchParams?: ICommonAdvancedSearchParams; onDoubleClick(item: IEventOrPlanningItem): void; showRelatedPlannings(item: IEventItem): void; @@ -122,6 +122,7 @@ export class ListGroupItem extends React.Component { listViewType, sortField, minTimeWidth, + searchParams, } = this.props; const itemType = getItemType(item); @@ -151,6 +152,7 @@ export class ListGroupItem extends React.Component { ...itemProps, item: item as IEventItem, calendars: calendars, + filterLanguage: searchParams?.language, multiSelected: indexOf(selectedEventIds, item._id) !== -1, [EVENTS.ITEM_ACTIONS.EDIT_EVENT.actionName]: itemActions[EVENTS.ITEM_ACTIONS.EDIT_EVENT.actionName], @@ -193,6 +195,7 @@ export class ListGroupItem extends React.Component { contentTypes: contentTypes, agendas: agendas, date: date, + filterLanguage: searchParams?.language, onAddCoverageClick: onAddCoverageClick, multiSelected: indexOf(selectedPlanningIds, item._id) !== -1, showAddCoverage: showAddCoverage, diff --git a/client/components/Main/ListPanel.tsx b/client/components/Main/ListPanel.tsx index 590d95408..4b503e73e 100644 --- a/client/components/Main/ListPanel.tsx +++ b/client/components/Main/ListPanel.tsx @@ -5,7 +5,7 @@ import {superdeskApi} from '../../superdeskApi'; import {IDesk, IUser} from 'superdesk-api'; import { FILTER_TYPE, - IAgenda, ICalendar, IContactItem, + IAgenda, ICalendar, ICommonAdvancedSearchParams, IContactItem, IEventItem, IEventOrPlanningItem, IG2ContentType, ILockedItems, @@ -68,6 +68,7 @@ interface IProps { listViewType: LIST_VIEW_TYPE; sortField: SORT_FIELD; userInitiatedSearch?: boolean; + searchParams?: ICommonAdvancedSearchParams onItemClick(item: IEventOrPlanningItem): void; onDoubleClick(item: IEventOrPlanningItem): void; @@ -318,6 +319,7 @@ export class ListPanel extends React.Component { contacts, listViewType, sortField, + searchParams } = this.props; let indexFrom = 0; @@ -396,6 +398,7 @@ export class ListPanel extends React.Component { listViewType: listViewType, sortField: sortField, listBoxGroupProps: listBoxGroupProps, + searchParams: searchParams, ...propsForNestedListItems, }; diff --git a/client/components/Planning/PlanningItem.tsx b/client/components/Planning/PlanningItem.tsx index 1f78be4be..b651f19e3 100644 --- a/client/components/Planning/PlanningItem.tsx +++ b/client/components/Planning/PlanningItem.tsx @@ -32,6 +32,7 @@ import { } from '../../utils'; import {renderFields} from '../fields'; import * as actions from '../../actions'; +import {getUserInterfaceLanguageFromCV} from '../../utils/users'; interface IState { hover: boolean; @@ -64,7 +65,8 @@ class PlanningItemComponent extends React.Component { planningUtils.getAgendaNames(this.props.item, this.props.agendas), planningUtils.getAgendaNames(nextProps.item, nextProps.agendas) ) || - this.props.minTimeWidth !== nextProps.minTimeWidth; + this.props.minTimeWidth !== nextProps.minTimeWidth || + this.props.filterLanguage !== nextProps.filterLanguage; } onItemHoverOn() { @@ -184,6 +186,7 @@ class PlanningItemComponent extends React.Component { agendas, contacts, listViewType, + filterLanguage } = this.props; if (!item) { @@ -197,6 +200,7 @@ class PlanningItemComponent extends React.Component { const isExpired = isItemExpired(item); const secondaryFields = get(listFields, 'planning.secondary_fields', PLANNING.LIST.SECONDARY_FIELDS); const {querySelectorParent} = superdeskApi.utilities; + const language = filterLanguage || item.language || getUserInterfaceLanguageFromCV(); return ( { {renderFields(get(listFields, 'planning.primary_fields', - PLANNING.LIST.PRIMARY_FIELDS), item)} + PLANNING.LIST.PRIMARY_FIELDS), item, {}, language)} {event && ( diff --git a/client/components/fields/headline.tsx b/client/components/fields/headline.tsx index 248f1d716..637fe9a11 100644 --- a/client/components/fields/headline.tsx +++ b/client/components/fields/headline.tsx @@ -1,9 +1,13 @@ import PropTypes from 'prop-types'; +import {getTranslatedValue} from '.'; +import {IFieldsProps} from '../../interfaces'; -export const headline = ({item}) => item.headline || null; +export const headline = ({item, language}: IFieldsProps) => getTranslatedValue( + language, item, 'headline') || item.headline || null; headline.propTypes = { item: PropTypes.shape({ headline: PropTypes.string, }).isRequired, -}; \ No newline at end of file + language: PropTypes.string, +}; diff --git a/client/components/fields/index.tsx b/client/components/fields/index.tsx index 96fdbd8ab..342a7c811 100644 --- a/client/components/fields/index.tsx +++ b/client/components/fields/index.tsx @@ -1,9 +1,8 @@ import React from 'react'; import {sortBy} from 'lodash'; -import {IProfileSchema, IRenderPanelType, ISearchProfile, PREVIEW_PANEL} from '../../interfaces'; +import {IEventOrPlanningItem, IProfileSchema, IRenderPanelType, ISearchProfile, PREVIEW_PANEL} from '../../interfaces'; import {superdeskApi} from '../../superdeskApi'; -import {getUserInterfaceLanguageFromCV} from '../../utils/users'; import {name} from './name'; import {slugline} from './slugline'; @@ -42,12 +41,15 @@ export function registerField(id, component) { /** * Render list of fields for given item * @param {Array|String} fields - * @param {Object} item + * @param {IEventOrPlanningItem} item * @param {Object} props */ -export function renderFields(fields, item, props = {}) { - const language = getUserInterfaceLanguageFromCV(); - +export function renderFields( + fields: Array|string, + item: IEventOrPlanningItem, + props: Object = {}, + language: string = '' +) { return (Array.isArray(fields) ? fields : [fields]).map((id) => { const Component = registeredFields[id]; @@ -66,6 +68,23 @@ export function renderFields(fields, item, props = {}) { }); } +/** + * Get translated field value based on language + * @param {String} language + * @param {IEventOrPlanningItem} item + * @param {String} fieldName + */ +export function getTranslatedValue(language: string, item: IEventOrPlanningItem, fieldName: string): string | null { + if (item.translations) { + const matchingTranslation = item.translations.find( + (translation) => translation.field === fieldName && translation.language === language + ); + + return matchingTranslation ? matchingTranslation.value : null; + } + return null; +} + function getFieldsForPanel(panelType: IRenderPanelType) { switch (panelType) { case 'editor': diff --git a/client/components/fields/name.tsx b/client/components/fields/name.tsx index cbd92e50b..b99753214 100644 --- a/client/components/fields/name.tsx +++ b/client/components/fields/name.tsx @@ -1,9 +1,13 @@ import PropTypes from 'prop-types'; +import {getTranslatedValue} from '.'; +import {IFieldsProps} from '../../interfaces'; -export const name = ({item}) => item.name || null; +export const name = ({item, language}: IFieldsProps) => getTranslatedValue(language, item, 'name') || +item.name || null; name.propTypes = { item: PropTypes.shape({ name: PropTypes.string, }).isRequired, -}; \ No newline at end of file + language: PropTypes.string, +}; diff --git a/client/components/fields/slugline.tsx b/client/components/fields/slugline.tsx index afb63a53a..183a2e205 100644 --- a/client/components/fields/slugline.tsx +++ b/client/components/fields/slugline.tsx @@ -2,17 +2,23 @@ import React from 'react'; import PropTypes from 'prop-types'; import {get} from 'lodash'; +import {getTranslatedValue} from '.'; +import {IFieldsProps} from '../../interfaces'; -export const slugline = ({item}) => { +export const slugline = ({item, language}: IFieldsProps) => { if (!get(item, 'slugline', '')) { return null; } - return ({item.slugline}); + return ( + {getTranslatedValue(language, item, 'slugline') || + item.slugline} + ); }; slugline.propTypes = { item: PropTypes.shape({ slugline: PropTypes.string, }).isRequired, + language: PropTypes.string, }; diff --git a/client/interfaces.ts b/client/interfaces.ts index d07679a52..13d159f3f 100644 --- a/client/interfaces.ts +++ b/client/interfaces.ts @@ -855,6 +855,7 @@ export interface IBaseListItemProps { export interface IEventListItemProps extends IBaseListItemProps { relatedPlanningText?: string; calendars: Array; + filterLanguage?: string; toggleRelatedPlanning?(event: React.MouseEvent): void; } @@ -863,6 +864,7 @@ export interface IPlanningListItemProps extends IBaseListItemProps; users: Array; desks: Array; + filterLanguage?: string; // showUnlock?: boolean; // Is this used anymore? hideItemActions: boolean; showAddCoverage: boolean; @@ -1579,6 +1581,11 @@ export interface IContentTemplate extends IBaseRestApiResponse { }; } +export interface IFieldsProps { + item: IEventOrPlanningItem; + language?: string; +} + interface IMainStateSearch { lastRequestParams: T; fulltext?: string;