Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[SDCP-409] [Client] Scheduled exports of search filters #1519

Merged
merged 6 commits into from
Jan 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions client/components/EventsPlanningFilters/EditFilter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ export class EditFilter extends React.Component<IEventsPlanningContentPanelProps
key="save"
onClick={this.onSaveHandler}
disabled={pristine || invalid}
data-test-id="manage-filters--save-filter"
>
{this.props.filter?._id == null ?
gettext('Create') :
Expand Down Expand Up @@ -213,6 +214,7 @@ export class EditFilter extends React.Component<IEventsPlanningContentPanelProps
label: gettext('Filter Name'),
autoFocus: true,
required: true,
testId: 'field-filter_name',
},
item_type: {
onChange: this.onTypeChanged,
Expand Down
220 changes: 220 additions & 0 deletions client/components/EventsPlanningFilters/EditFilterSchedule.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,220 @@
import React from 'react';
import {connect} from 'react-redux';
import {cloneDeep} from 'lodash';

import {superdeskApi} from '../../superdeskApi';
import {IEventsPlanningContentPanelProps, ISearchFilterSchedule, SCHEDULE_FREQUENCY, WEEK_DAY} from '../../interfaces';

import {SidePanel, ToggleBox} from '../UI';
import {renderFieldsForPanel} from '../fields';
import {IDesk} from 'superdesk-api';
import {desks as getDesks} from '../../selectors/general';

interface IProps extends IEventsPlanningContentPanelProps {
desks: Array<IDesk>;
}

interface IState {
pristine: boolean;
schedule: Partial<ISearchFilterSchedule>;
invalid: boolean;
errors: {[key: string]: string};
}

const mapStateToProps = (state) => ({
desks: getDesks(state),
});

export class EditFilterScheduleComponent extends React.Component<IProps, IState> {
constructor(props) {
super(props);
this.state = {
pristine: false,
schedule: cloneDeep(this.props.filter.schedules?.[0] ?? {
frequency: SCHEDULE_FREQUENCY.HOURLY,
desk: this.props.desks[0]._id,
}),
invalid: false,
errors: {},
};

this.onSaveHandler = this.onSaveHandler.bind(this);
this.previewFilter = this.previewFilter.bind(this);
this.onChange = this.onChange.bind(this);
}

onSaveHandler() {
const schedule = Object.assign({}, this.state.schedule);

switch (schedule.frequency) {
case SCHEDULE_FREQUENCY.HOURLY:
delete schedule.hour;
delete schedule.day;
delete schedule.week_days;
break;
case SCHEDULE_FREQUENCY.WEEKLY:
delete schedule.day;
if (!schedule.week_days?.length) {
schedule.week_days = [
WEEK_DAY.SUNDAY,
WEEK_DAY.MONDAY,
WEEK_DAY.TUESDAY,
WEEK_DAY.WEDNESDAY,
WEEK_DAY.THURSDAY,
WEEK_DAY.FRIDAY,
WEEK_DAY.SATURDAY,
];
}
break;
case SCHEDULE_FREQUENCY.MONTHLY:
delete schedule.week_days;
break;
}

this.props.onSave({
...this.props.filter,
schedules: [schedule],
}).then(() => this.props.onClose());
}

previewFilter() {
this.props.previewFilter(this.props.filter);
}

onChange<T extends keyof ISearchFilterSchedule>(field: T, value: ISearchFilterSchedule[T]) {
const schedule = {...this.state.schedule};

schedule[field] = value;

if (field === 'frequency' && value === 'weekly') {
// When changing the frequency to weekly
// If the week_days array is empty, delete the attribute
// This enabled the input field to automatically select all days
// Which converts this to a 'daily' schedule automatically
if (!schedule.week_days?.length) {
delete schedule.week_days;
}
}

this.setState({schedule});
}

getScheduleProfile() {
const profile = {
frequency: {enabled: true, index: 1},
week_days: {enabled: false, index: 2},
month_day: {enabled: false, index: 3},
hour: {enabled: false, index: 4},
};

switch (this.state.schedule.frequency) {
case SCHEDULE_FREQUENCY.HOURLY:
break;
case SCHEDULE_FREQUENCY.WEEKLY:
profile.week_days.enabled = true;
profile.hour.enabled = true;
break;
case SCHEDULE_FREQUENCY.MONTHLY:
profile.month_day.enabled = true;
profile.hour.enabled = true;
break;
}

return profile;
}

render() {
const {gettext} = superdeskApi.localization;
const scheduleProfile = this.getScheduleProfile();

return (
<React.Fragment>
<SidePanel.Header className="side-panel__header--border-b">
<div className="subnav__sliding-toolbar">
<h3 className="side-panel__heading">
{gettext('Filter Schedule')}
</h3>
<div className="button-group button-group--right">
<button
className="btn"
key="cancel"
onClick={this.previewFilter}
>
{gettext('Cancel')}
</button>
<button
className="btn btn--primary"
key="save"
onClick={this.onSaveHandler}
disabled={this.state.pristine || this.state.invalid}
data-test-id="manage-filters--save-schedule"
>
{this.props.filter?._id == null ?
gettext('Create') :
gettext('Save')
}
</button>
</div>
</div>
</SidePanel.Header>
<SidePanel.Content>
<SidePanel.ContentBlock flex={true}>
<SidePanel.ContentBlockInner grow={true}>
{renderFieldsForPanel(
'editor',
scheduleProfile,
{
item: this.state.schedule,
onChange: this.onChange,
},
{
week_days: {
defaultAllOn: true,
},
month_day: {
field: 'day',
},
}
)}

<ToggleBox
title={gettext('Destination')}
isOpen={true}
style="toggle-box--circle"
noMargin={true}
>
{renderFieldsForPanel(
'editor',
{
desk: {enabled: true, index: 1},
content_template: {enabled: true, index: 2},
export_template: {enabled: true, index: 3},
},
{
item: this.state.schedule,
onChange: this.onChange,
},
{
content_template: {
field: 'article_template',
label: gettext('Article Template'),
desk: this.state.schedule.desk,
},
export_template: {
field: 'template',
label: gettext('Custom Layout'),
itemType: this.props.filter.item_type,
clearable: true,
},
},
)}
</ToggleBox>
</SidePanel.ContentBlockInner>
</SidePanel.ContentBlock>
</SidePanel.Content>
</React.Fragment>
);
}
}

export const EditFilterSchedule = connect(mapStateToProps)(EditFilterScheduleComponent);
76 changes: 55 additions & 21 deletions client/components/EventsPlanningFilters/FilterItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import moment from 'moment';

import {PRIVILEGES} from '../../constants';
import {List} from '../UI';
import {ItemActionsMenu} from '../ItemActionsMenu';
import {ISearchFilter, ICalendar, IAgenda} from '../../interfaces';
import {superdeskApi} from '../../superdeskApi';
import {renderFieldsForPanel} from '../fields';
Expand All @@ -15,6 +16,8 @@ interface IProps {
calendars: Array<ICalendar>;
agendas: Array<IAgenda>;
previewFilter(filter: ISearchFilter): void;
editFilterSchedule(filter: ISearchFilter): void;
deleteFilterSchedule(filter: ISearchFilter): void;
}

export class FilterItem extends React.PureComponent<IProps> {
Expand All @@ -24,23 +27,52 @@ export class FilterItem extends React.PureComponent<IProps> {
this.previewFilter = this.previewFilter.bind(this);
this.editFilter = this.editFilter.bind(this);
this.deleteFilter = this.deleteFilter.bind(this);
this.editFilterSchedule = this.editFilterSchedule.bind(this);
}

previewFilter() {
this.props.previewFilter(this.props.filter);
}

editFilter(event) {
event.stopPropagation();
editFilter() {
this.props.editFilter(this.props.filter);
}

deleteFilter() {
this.props.deleteFilter(this.props.filter);
}

editFilterSchedule() {
this.props.editFilterSchedule(this.props.filter);
}

render() {
const {gettext} = superdeskApi.localization;
const actions = !this.props.privileges[PRIVILEGES.EVENTS_PLANNING_FILTERS_MANAGEMENT] ?
[] :
[{
icon: 'icon-pencil',
label: gettext('Edit Filter'),
callback: this.editFilter,
}, {
icon: 'icon-trash',
label: gettext('Delete'),
callback: this.deleteFilter,
}, {
icon: 'icon-time',
label: !this.props.filter.schedules?.length ?
gettext('Create Scheduled Export') :
gettext('Edit Scheduled Export'),
callback: this.editFilterSchedule,
}];

if (actions.length && this.props.filter.schedules?.length) {
actions.push({
icon: 'icon-trash',
label: gettext('Delete Scheduled Export'),
callback: () => this.props.deleteFilterSchedule(this.props.filter),
});
}

return (
<List.Item shadow={1} onClick={this.previewFilter}>
Expand Down Expand Up @@ -89,27 +121,29 @@ export class FilterItem extends React.PureComponent<IProps> {
{}
)}
</List.Row>
{!this.props.filter.schedules?.length ? null : (
<List.Row>
{renderFieldsForPanel(
'list',
{
filter_schedule: {enabled: true, index: 1},
},
{
item: this.props.filter,
},
{
filter_schedule: {
editSchedule: this.props.editFilterSchedule,
deleteSchedule: this.props.deleteFilterSchedule,
},
}
)}
</List.Row>
)}
</List.Column>
{!!this.props.privileges[PRIVILEGES.EVENTS_PLANNING_FILTERS_MANAGEMENT] && (
{!actions.length ? null : (
<List.ActionMenu>
{this.props.editFilter && (
<button
onClick={this.editFilter}
className="dropdown__toggle"
data-sd-tooltip={gettext('Edit Filter')}
data-flow="left"
>
<i className="icon-pencil" />
</button>
)}
<button
onClick={this.deleteFilter}
className="dropdown__toggle"
data-sd-tooltip={gettext('Delete Filter')}
data-flow="left"
>
<i className="icon-trash" />
</button>
<ItemActionsMenu actions={actions} />
</List.ActionMenu>
)}
</List.Item>
Expand Down
6 changes: 6 additions & 0 deletions client/components/EventsPlanningFilters/FiltersList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ interface IProps {
editFilter(filter: ISearchFilter): void;
deleteFilter(filter: ISearchFilter): void;
previewFilter(filter: ISearchFilter): void;
editFilterSchedule(filter: ISearchFilter): void;
deleteFilterSchedule(filter: ISearchFilter): void;

filters: Array<ISearchFilter>;
calendars: Array<ICalendar>;
Expand All @@ -39,6 +41,8 @@ export class FiltersListComponent extends React.PureComponent<IProps> {
calendars,
agendas,
previewFilter,
editFilterSchedule,
deleteFilterSchedule,
} = this.props;

return (
Expand All @@ -56,6 +60,8 @@ export class FiltersListComponent extends React.PureComponent<IProps> {
calendars={calendars}
agendas={agendas}
previewFilter={previewFilter}
editFilterSchedule={editFilterSchedule}
deleteFilterSchedule={deleteFilterSchedule}
/>
))
)}
Expand Down
Loading