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

Show cancelled/rejected occurrences #3742

Merged
merged 10 commits into from Feb 5, 2019
1 change: 1 addition & 0 deletions indico/modules/rb_new/client/js/common/filters/index.js
Expand Up @@ -20,4 +20,5 @@ import * as actions from './actions';

export {filterReducerFactory} from './reducers';
export {validateFilters} from './validation';
export {default as FilterFormComponent} from './FilterFormComponent';
mic4ael marked this conversation as resolved.
Show resolved Hide resolved
export {actions};
Expand Up @@ -69,6 +69,7 @@ export function initialRoomFilterStateFactory(namespace) {
Object.assign(state, {
myBookings: false,
hideUnused: false,
showInactive: false,
});
}

Expand Down
Expand Up @@ -174,7 +174,7 @@ export default class DateNavigator extends React.Component {
renderModeSwitcher(disabled) {
const {mode} = this.props;
return !!mode && (
<Button.Group size="small" style={{marginRight: 10}}>
<Button.Group size="tiny" style={{marginRight: 10}}>
<Button content={Translate.string('Day')}
onClick={() => this.handleModeChange('days')}
primary={mode === 'days'}
Expand Down Expand Up @@ -256,7 +256,7 @@ export default class DateNavigator extends React.Component {
const nextDisabled = disabled || !this.isValidChange(1, mode);

return (
<Button.Group size="small">
<Button.Group size="tiny">
<Button icon="left arrow"
onClick={() => this.changeSelectedDate('prev')}
disabled={prevDisabled} />
Expand Down
Expand Up @@ -77,7 +77,7 @@ export default class ElasticTimeline extends React.Component {
const {bookingAllowed} = this.props;
return ({
candidates, preBookings, bookings, preConflicts, conflicts, blockings, nonbookablePeriods,
unbookableHours
unbookableHours, cancellations, rejections
}) => {
const hasConflicts = !!(conflicts[dt] || []).length;
return {
Expand All @@ -88,7 +88,9 @@ export default class ElasticTimeline extends React.Component {
preConflicts: preConflicts[dt] || [],
blockings: blockings[dt] || [],
nonbookablePeriods: nonbookablePeriods[dt] || [],
unbookableHours: unbookableHours || []
unbookableHours: unbookableHours || [],
cancellations: cancellations[dt] || [],
rejections: rejections[dt] || [],
};
};
}
Expand Down
28 changes: 24 additions & 4 deletions indico/modules/rb_new/client/js/common/timeline/TimelineItem.jsx
Expand Up @@ -53,6 +53,23 @@ const types = {
other: 'other',
};

const reservationTypes = new Set(['booking', 'pre-booking', 'cancellation', 'rejection']);

const renderOrder = [
'other',
'candidates',
'preBookings',
'preConflicts',
'cancellations',
'rejections',
'bookings',
'conflicts',
'blockings',
'nonbookablePeriods',
'unbookableHours',
'pendingCancellations',
];

function getKeyForOccurrence(name, {reservation, startTime, endTime, startDt, endDt}) {
let key = '';
if (reservation) {
Expand Down Expand Up @@ -201,7 +218,7 @@ class TimelineItem extends React.Component {
<div className={additionalClasses} onClick={() => {
if (onClickCandidate && bookable && type === 'candidate') {
onClickCandidate(room);
} else if (onClickReservation && (type === 'booking' || type === 'pre-booking')) {
} else if (onClickReservation && reservationTypes.has(type)) {
onClickReservation(reservation.id);
}
}}
Expand All @@ -218,12 +235,15 @@ class TimelineItem extends React.Component {

renderOccurrences = () => {
const {data} = this.props;
return Object.entries(data).map(([name, occurrences]) => (
occurrences.map(occurrence => (

return renderOrder.filter((name) => name in data).map((name) => {
const occurrences = data[name];
return occurrences.map(occurrence => (
<React.Fragment key={getKeyForOccurrence(name, occurrence)}>
{this.renderOccurrence(occurrence, classes[name] || 'default', types[name])}
</React.Fragment>
))));
));
});
};

render() {
Expand Down
Expand Up @@ -62,13 +62,13 @@
}

&:global(.unbookable-periods) {
background-color: lighten($gray, 25%);
background-color: lighten($gray, 15%);
z-index: 2;
height: 30px;
}

&:global(.unbookable-hours) {
background-color: lighten($gray, 25%);
background-color: lighten($gray, 15%);
z-index: 1;
height: 30px;
}
Expand All @@ -82,7 +82,7 @@
}

&:global(.cancellation) {
background-color: lighten($gray, 15%);
background-color: lighten($gray, 5%);
}

&:global(.pending-cancellations) {
Expand Down
Expand Up @@ -38,7 +38,7 @@ export default function TimelineLegend({labels, aside, compact}) {
</List>
) : (
<Segment styleName="legend" basic>
<Label.Group as="span" size="large" styleName="labels">
<Label.Group as="span" size="medium" styleName="labels">
{labels.map(({label, color, style}) => (
<Label color={color} styleName={style || ''} key={label}>{label}</Label>
))}
Expand Down
Expand Up @@ -22,6 +22,10 @@
display: flex;
justify-content: space-between;

&:not(.compact) {
padding: 1em 0 !important;
}

&.compact {
flex-direction: column;
}
Expand All @@ -34,6 +38,7 @@

:global(.label) {
color: white;
margin-bottom: 0;

&.compact {
padding: 0.5em;
Expand All @@ -43,7 +48,11 @@
}
}

&.conflict, &.booking, &.new-booking, &.available {
&.booking {
border: 1px solid lighten($gray, 10%);
}

&.conflict, &.new-booking, &.available {
&.compact {
border: 1px solid lighten($gray, 10%);
}
Expand All @@ -52,10 +61,7 @@
&.pre-booking {
@include stripes($pre-booking-stripe-colors...);
background-size: 35px 35px;

&.compact {
border: 1px solid lighten($gray, 10%);
}
border: 1px solid lighten($gray, 10%);
}

&.pre-booking-conflict {
Expand All @@ -67,10 +73,16 @@
}
}

&.unbookable, &.cancellation {
&.cancellation {
background-color: lighten($gray, 5%);
border: 1px solid lighten($gray, 10%);
color: lighten($gray, 30%);
}

&.unbookable {
background-color: lighten($gray, 15%);
border: 1px solid lighten($gray, 10%);
color: $gray;
color: darken($gray, 20%);
}

&.pending-cancellation {
Expand Down
Expand Up @@ -16,8 +16,6 @@
*/

.room-filters {
margin-bottom: 10px;

.text-filter input {
width: 300px;

Expand Down
8 changes: 6 additions & 2 deletions indico/modules/rb_new/client/js/modules/bookRoom/BookRoom.jsx
Expand Up @@ -336,12 +336,16 @@ class BookRoom extends React.Component {
const listBtn = (
<Button icon={<Icon name="grid layout" styleName="switcher-icon" />}
className={toClasses({active: !isTimelineVisible})}
onClick={this.switchToRoomList} circular />
onClick={this.switchToRoomList}
size="small"
circular />
);
const timelineBtn = (
<Button icon={<Icon name="calendar outline" styleName="switcher-icon"
disabled={!this.timelineButtonEnabled} />}
className={classes} circular />
className={classes}
size="small"
circular />
);
return (
<div styleName="view-icons">
Expand Down
Expand Up @@ -70,7 +70,7 @@ class BookingFilterBar extends React.Component {
} = this.props;

return (
<Button.Group size="large">
<Button.Group size="small">
<Button icon="calendar alternate outline" as="div" disabled />
<FilterBarController>
<FilterDropdownFactory name="recurrence"
Expand Down
Expand Up @@ -20,7 +20,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import {CalendarSingleDatePicker, CalendarRangeDatePicker} from 'indico/react/components';
import {serializeDate, toMoment} from 'indico/utils/date';
import FilterFormComponent from '../../../common/filters/FilterFormComponent';
import {FilterFormComponent} from '../../../common/filters';


export default class DateForm extends FilterFormComponent {
Expand Down
Expand Up @@ -21,7 +21,7 @@ import PropTypes from 'prop-types';

import {Translate} from 'indico/react/i18n';

import FilterFormComponent from '../../../common/filters/FilterFormComponent';
import {FilterFormComponent} from '../../../common/filters';

import './RecurrenceForm.module.scss';

Expand Down
Expand Up @@ -19,7 +19,7 @@ import React from 'react';
import PropTypes from 'prop-types';

import {serializeTime, toMoment} from 'indico/utils/date';
import FilterFormComponent from '../../../common/filters/FilterFormComponent';
import {FilterFormComponent} from '../../../common/filters';
import TimeRangePicker from '../../../components/TimeRangePicker';

import './TimeForm.module.scss';
Expand Down
60 changes: 42 additions & 18 deletions indico/modules/rb_new/client/js/modules/calendar/Calendar.jsx
Expand Up @@ -123,19 +123,23 @@ class Calendar extends React.Component {
setFilterParameter('hideUnused', !hideUnused);
};

toggleShowInactive = () => {
const {calendarFilters: {showInactive}, actions: {setFilterParameter}} = this.props;
setFilterParameter('showInactive', !showInactive);
};

renderExtraButtons = () => {
const {
calendarFilters: {myBookings},
calendarFilters: {myBookings, showInactive},
localFilters: {hideUnused},
actions: {setFilterParameter},
isFetching, isFetchingActiveBookings
} = this.props;
const {view} = this.props;

return (
<>
<Popup trigger={<Button size="large"
primary={myBookings}
<Button.Group size="small">
<Popup trigger={<Button primary={myBookings}
icon="user circle"
disabled={isFetching || isFetchingActiveBookings}
onClick={() => setFilterParameter('myBookings', !myBookings || null)} />}>
Expand All @@ -144,17 +148,26 @@ class Calendar extends React.Component {
</Translate>
</Popup>
{view === 'timeline' && (
<Popup trigger={<Button size="large"
primary={hideUnused}
icon={hideUnused ? 'plus square outline' : 'minus square outline'}
disabled={isFetching}
onClick={this.toggleHideUnused} />}>
{hideUnused
? <Translate>Show unused rooms</Translate>
: <Translate>Hide unused rooms</Translate>}
</Popup>
<>
<Popup trigger={<Button primary={showInactive}
icon="ban"
disabled={isFetching}
onClick={this.toggleShowInactive} />}>
{showInactive
? <Translate>Hide rejected/cancelled bookings</Translate>
: <Translate>Show rejected/cancelled bookings</Translate>}
</Popup>
<Popup trigger={<Button primary={hideUnused}
icon={hideUnused ? 'plus square outline' : 'minus square outline'}
disabled={isFetching}
onClick={this.toggleHideUnused} />}>
{hideUnused
? <Translate>Show unused spaces</Translate>
: <Translate>Hide unused spaces</Translate>}
</Popup>
</>
)}
</>
</Button.Group>
);
};

Expand All @@ -166,6 +179,7 @@ class Calendar extends React.Component {
primary={view === 'timeline'}
onClick={() => changeView('timeline')}
disabled={isFetching || isFetchingActiveBookings}
size="tiny"
circular />}
position="bottom center">
<Translate>
Expand All @@ -176,6 +190,7 @@ class Calendar extends React.Component {
primary={view === 'list'}
onClick={() => changeView('list')}
disabled={isFetching || isFetchingActiveBookings}
size="tiny"
circular />}
position="bottom center">
<Translate>
Expand All @@ -192,17 +207,26 @@ class Calendar extends React.Component {
isFetching,
isFetchingActiveBookings,
localFilters: {hideUnused},
calendarFilters: {showInactive},
calendarData: {rows},
actions: {openRoomDetails, setDate, openBookingDetails, setMode},
datePicker,
allowDragDrop,
} = this.props;
const legendLabels = [
{label: Translate.string('Booked'), color: 'orange'},
{label: Translate.string('Booked'), color: 'orange', style: 'booking'},
{label: Translate.string('Pre-Booking'), style: 'pre-booking'},
{label: Translate.string('Blocked'), style: 'blocking'},
{label: Translate.string('Not bookable'), style: 'unbookable'}
{label: Translate.string('Not bookable'), style: 'unbookable'},
];

if (showInactive) {
legendLabels.push(
{label: Translate.string('Cancelled'), style: 'cancellation'},
{label: Translate.string('Rejected'), style: 'rejection'},
);
}

const editable = datePicker.mode === 'days' && allowDragDrop;
const isTimelineVisible = view === 'timeline';
return (
Expand All @@ -214,8 +238,8 @@ class Calendar extends React.Component {
<Grid.Row styleName="calendar-filters">
<div className="filter-row">
<div className="filter-row-filters">
<RoomFilterBar disabled={isFetching || isFetchingActiveBookings}
extraButtons={this.renderExtraButtons()} />
<RoomFilterBar disabled={isFetching || isFetchingActiveBookings} />
{this.renderExtraButtons()}
<SearchBar disabled={isFetching || isFetchingActiveBookings} />
</div>
</div>
Expand Down