diff --git a/client/components/Main/CreateNewSubnavDropdown.tsx b/client/components/Main/CreateNewSubnavDropdown.tsx index ee7380cc6..35227949c 100644 --- a/client/components/Main/CreateNewSubnavDropdown.tsx +++ b/client/components/Main/CreateNewSubnavDropdown.tsx @@ -2,12 +2,14 @@ import React from 'react'; import {connect} from 'react-redux'; import {superdeskApi} from '../../superdeskApi'; -import {IEventTemplate} from '../../interfaces'; +import {ICalendar, IEventTemplate} from '../../interfaces'; import {PRIVILEGES} from '../../constants'; import * as actions from '../../actions'; import {eventTemplates} from '../../selectors/events'; import {Dropdown, IDropdownItem} from '../UI/SubNav'; +import {showModal} from '@superdesk/common'; +import PlanningTemplatesModal from '../../components/PlanningTemplatesModal'; interface IProps { addEvent(): void; @@ -16,6 +18,7 @@ interface IProps { privileges: {[key: string]: number}; createEventFromTemplate(template: IEventTemplate): void; eventTemplates: Array; + calendars: Array; } class CreateNewSubnavDropdownFn extends React.PureComponent { @@ -43,14 +46,36 @@ class CreateNewSubnavDropdownFn extends React.PureComponent { id: 'create_event', }); - this.props.eventTemplates.forEach((template) => { - items.push({ - label: template.template_name, - icon: 'icon-event icon--blue', - group: gettext('From template'), - action: () => createEventFromTemplate(template), - id: template._id, + this.props.eventTemplates + .sort((templ1, templ2) => templ1.template_name.localeCompare(templ2.template_name)) + .slice(0, 5) + .forEach((template) => { + items.push({ + label: template.template_name, + icon: 'icon-event icon--blue', + group: gettext('From template'), + action: () => createEventFromTemplate(template), + id: template._id, + }); }); + + items.push({ + label: gettext('More templates...'), + icon: 'icon-event icon--blue', + group: gettext('From template'), + action: () => { + showModal(({closeModal}) => ( + templ1.template_name.localeCompare(templ2.template_name)) + } + /> + )); + }, + id: 'more_templates', }); } @@ -79,6 +104,7 @@ class CreateNewSubnavDropdownFn extends React.PureComponent { function mapStateToProps(state) { return { + calendars: state.events.calendars, eventTemplates: eventTemplates(state), }; } diff --git a/client/components/PlanningTemplatesModal.tsx b/client/components/PlanningTemplatesModal.tsx new file mode 100644 index 000000000..f9a1ae7ba --- /dev/null +++ b/client/components/PlanningTemplatesModal.tsx @@ -0,0 +1,143 @@ +import {ICalendar, IEventTemplate} from '../interfaces'; +import React from 'react'; +import {SearchBar, BoxedListItem, Heading, BoxedList, Modal, TreeSelect, Spacer} from 'superdesk-ui-framework/react'; +import {superdeskApi} from '../superdeskApi'; + +interface IProps { + closeModal: () => void; + calendars: Array; + eventTemplates: Array; + createEventFromTemplate(template: IEventTemplate): void; +} + +interface IState { + activeCalendarFilter?: string; + searchQuery: string; +} + +export default class PlanningTemplatesModal extends React.Component { + constructor(props) { + super(props); + + this.state = { + activeCalendarFilter: null, + searchQuery: '', + }; + } + + render(): React.ReactNode { + const {gettext} = superdeskApi.localization; + const {closeModal, createEventFromTemplate, calendars, eventTemplates} = this.props; + + type ICloseModalProps = { + closeModal: () => void; + } + + const TemplatesListView: React.FC = ({closeModal}) => { + const calendarFilter = (template: IEventTemplate) => this.state.activeCalendarFilter + ? template.data.calendars.map(({qcode}) => qcode).includes(this.state.activeCalendarFilter) + : template; + /** + * Groups the templates by calendar, filters the templates that match the current search query, + * filters the templates by the current selected calendar if one is selected, + * filters out the groups that don't have any templates + */ + const filteredTemplates = calendars.map((calendar) => ({ + calendarName: calendar.name, + templates: eventTemplates + .filter((template) => template.template_name.includes(this.state.searchQuery)) + .filter((template) => template.data.calendars.map(({qcode}) => qcode).includes(calendar.qcode)) + .filter(calendarFilter) + })).filter((templatesGroup) => templatesGroup.templates.length > 0); + + return ( + <> + { + filteredTemplates.map(({calendarName, templates}) => ( + + {calendarName} + + {templates + .map((template) => ( + { + createEventFromTemplate(template); + closeModal(); + }} + > + {template.template_name} + + )) + } + + + )) + } + + ); + }; + + return ( + +
+ + this.state.activeCalendarFilter === qcode)] + : [] + } + kind="synchronous" + labelHidden + inlineLabel + getOptions={() => calendars.map((calendar) => ({value: calendar}))} + getLabel={(item) => item.name} + getId={(item) => item.qcode} + placeholder={gettext('All Calendars')} + optionTemplate={(item: any) =>
{item.name}
} + valueTemplate={(item: any, Wrapper) => ( + + {item.name} + + )} + onChange={([value]) => { + if (value?.qcode != null) { + this.setState({ + activeCalendarFilter: value?.qcode, + searchQuery: '', + }); + } else { + this.setState({ + activeCalendarFilter: value?.qcode, + }); + } + }} + /> + { + this.setState({ + activeCalendarFilter: undefined, + searchQuery: value, + }); + }} + placeholder={gettext('Search templates')} + boxed + /> +
+ +
+
+ ); + } +}