diff --git a/SAS/TMSS/frontend/tmss_webapp/src/components/Timeline/CalendarTimeline.js b/SAS/TMSS/frontend/tmss_webapp/src/components/Timeline/CalendarTimeline.js index bed0a742a19ac942a70fa7491b5e09451bcf628c..a35d8400d3fc09694b821ebfc8bf0de5faf98949 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/components/Timeline/CalendarTimeline.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/components/Timeline/CalendarTimeline.js @@ -794,7 +794,7 @@ export class CalendarTimeline extends Component { } { (item.type === "SUNTIME" || item.type === "RESERVATION") && - <div style={itemContentStyle}><span>{item.title}</span> + <div style={itemContentStyle}><span>{item.actType}</span> {item.type === "RESERVATION" && <div style={itemContentStyle}><span>{item.desc}</span></div> } </div> } diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/list.tabs.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/list.tabs.js index 1d6b270679205a88a36a258e980942d7e49b2fbd..d912e1ed14be8889456b2dd5fc74c7df68f68678 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/list.tabs.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/list.tabs.js @@ -27,6 +27,7 @@ class TimelineListTabs extends Component { this.timelineUIAttributes = UtilService.localStore({ type: 'get', key: "TIMELINE_UI_ATTR" }) || {}; this.suListFilterCallback = this.suListFilterCallback.bind(this); this.taskListFilterCallback = this.taskListFilterCallback.bind(this); + this.reservListFilterCallback = this.reservListFilterCallback.bind(this); this.getTaskList = this.getTaskList.bind(this); this.getSUFilterOptions = this.getSUFilterOptions.bind(this); this.getTaskFilterOptions = this.getTaskFilterOptions.bind(this); @@ -70,7 +71,8 @@ class TimelineListTabs extends Component { */ suListFilterCallback(filteredData) { this.filteredSUB = filteredData; - this.props.suListFilterCallback(filteredData); + this.filteredTasks = null; + this.props.suListFilterCallback(filteredData, this.filteredTasks, this.filteredReservs); } /** @@ -79,7 +81,18 @@ class TimelineListTabs extends Component { * @param {Array} filteredData - Array of task table rows */ taskListFilterCallback(filteredData) { - this.props.suListFilterCallback(this.filteredSUB, filteredData); + this.filteredTasks = filteredData; + this.props.suListFilterCallback(this.filteredSUB, filteredData, this.filteredReservs); + } + + /** + * Callback function passed to Reservation list table which in turn call back the parenst (View or Weekview) callback function + * to show or hide the reservations shown in the timeline. + * @param {Array} filteredData - Array of reservation table rows + */ + reservListFilterCallback(filteredData) { + this.filteredReservs = filteredData; + this.props.suListFilterCallback(this.filteredSUB, this.filteredTasks, filteredData); } /** @@ -210,7 +223,7 @@ class TimelineListTabs extends Component { showColumnFilter={true} tablename={`timeline_reservation_list`} showTopTotal={false} - // filterCallback={this.taskListFilterCallback} // TODO: Implementing filter callback to timeline + filterCallback={this.reservListFilterCallback} lsKeySortColumn={"ResListSortColumn"} toggleBySorting={(sortData) => this.storeSortingColumn("ResListSortColumn", sortData)} pageUpdated={this.pageUpdated} diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/view.js index 4be3566be8f1d9b73f068c97e27c9668bb6066e5..7dbaeb520b44657ba6f61fdbc5d4082ea241cbd3 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/view.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/view.js @@ -22,8 +22,6 @@ import UnitConverter from '../../utils/unit.converter'; import Validator from '../../utils/validator'; import SchedulingUnitSummary from '../Scheduling/summary'; import ReservationSummary from '../Reservation/reservation.summary'; -import { Dropdown } from 'primereact/dropdown'; -import { OverlayPanel } from 'primereact/overlaypanel'; import { TieredMenu } from 'primereact/tieredmenu'; import { MultiSelect } from 'primereact/multiselect'; import { Button } from 'primereact/button'; @@ -97,6 +95,7 @@ export class TimelineView extends Component { taskStatusList: [], datasetStartTime: null, datasetEndTime:null, showDialog: false, + popPosition: {display: 'none'} } this.STATUS_BEFORE_SCHEDULED = ['defining', 'defined', 'schedulable']; // Statuses before scheduled to get station_group this.allStationsGroup = []; @@ -146,10 +145,6 @@ export class TimelineView extends Component { ] this.setState({menuOptions: menuOptions, loader: true }); TaskService.getTaskTypes().then(results => {taskTypes = results}); - // Fetch Reservations and keep ready to use in station view - ReservationService.getReservations().then(reservations => { - this.reservations = reservations; - }); UtilService.getReservationTemplates().then(templates => { this.reservationTemplate = templates.length > 0 ? templates[0] : null; if (this.reservationTemplate) { @@ -301,6 +296,11 @@ export class TimelineView extends Component { * @returns - Object with both original SUBs from backend and formatted SUBs for table list. */ async loadSchedulingUnits(startTime, endTime) { + ReservationService.getTimelineReservations(startTime.format(UIConstants.CALENDAR_DATETIME_FORMAT), + endTime.format(UIConstants.CALENDAR_DATETIME_FORMAT)) + .then(reservations => { + this.reservations = _.uniqBy(this.reservations.concat(reservations), 'id'); + }); let suBlueprints = await ScheduleService.getExpandedSUList(startTime.format(UIConstants.CALENDAR_DATETIME_FORMAT), endTime.format(UIConstants.CALENDAR_DATETIME_FORMAT)); suBlueprints = _.filter(suBlueprints, suBlueprint => suBlueprint.status.toLowerCase !== 'obsolete'); @@ -385,7 +385,6 @@ export class TimelineView extends Component { let items = [], itemGroup = []; const subtaskTemplates = this.subtaskTemplates; for (let task of suBlueprint.tasks) { - console.log(task); if (((!this.state.stationView && this.state.selectedTaskTypes.indexOf(task.task_type)>=0) || (this.state.stationView && task.task_type === 'observation')) && ( filteredTasks.length===0 || filteredTasks.indexOf(task.id)>=0 ) @@ -545,7 +544,7 @@ export class TimelineView extends Component { * @param {Event} evt */ onItemMouseOut(evt) { - this.popOver.toggle(evt); + this.setState({popPosition: {display: 'none'}}); } /** @@ -554,6 +553,13 @@ export class TimelineView extends Component { * @param {Object} item */ onItemMouseOver(evt, item) { + let popPosition = {display:"block", + left:`${evt.pageX+400>window.innerWidth?evt.pageX-400:evt.pageX+20}px`}; + if (evt.clientY > window.screen.height/2) { + popPosition.bottom = `${evt.clientY - evt.pageY+30}px`; + } else { + popPosition.top = `${evt.pageY}px`; + } if (item.type === "SCHEDULE" || item.type === "TASK" || item.type==="STATION_TASK" ) { const itemSU = _.find(this.state.suBlueprints, { id: (item.suId ? item.suId : item.id) }); item.suName = itemSU.name; @@ -568,8 +574,7 @@ export class TimelineView extends Component { item.stations = reservStations; item.planned = reservation.specifications_doc.activity.planned; } - this.popOver.toggle(evt); - this.setState({ mouseOverItem: item }); + this.setState({ mouseOverItem: item, popPosition: popPosition }); } /** @@ -682,9 +687,10 @@ export class TimelineView extends Component { * @param {moment} startTime * @param {moment} endTime */ - addStationReservations(items, startTime, endTime) { + addStationReservations(items, startTime, endTime, reservFilterData) { let reservations = this.reservations; let reservationItems = []; + let filteredReservations = reservFilterData?_.map(reservFilterData, data => {data.id = data.actionpath.split("view/")[1]; return parseInt(data.id);}):[]; for (const reservation of reservations) { const reservationStartTime = moment.utc(reservation.start_time); const reservationEndTime = reservation.duration ? reservationStartTime.clone().add(reservation.duration, 'seconds') : endTime; @@ -697,6 +703,7 @@ export class TimelineView extends Component { || reservationEndTime.isBetween(startTime, endTime) || (reservationStartTime.isSameOrBefore(startTime) && reservationEndTime.isSameOrAfter(endTime))) + && (filteredReservations.length === 0 || filteredReservations.indexOf(reservation.id)>=0) && (this.state.reservationFilter.length === 0 || // No reservation filter added this.state.reservationFilter.indexOf(reservationSpec.activity.type) >= 0 )) { // Reservation reason == Filtered reaseon if (this.state.stationView) { @@ -730,7 +737,8 @@ export class TimelineView extends Component { start_time: start_time, end_time: end_time, name: reservationSpec.activity.type, project: reservation.project_id, group: station, type: 'RESERVATION', - title: `${reservationSpec.activity.type}${reservation.project_id ? ("-" + reservation.project_id) : ""}`, + actType: `${reservationSpec.activity.type}${reservation.project_id ? ("-" + reservation.project_id) : ""}`, + title: "", desc: reservation.description, duration: reservation.duration ? UnitConverter.getSecsToHHmmss(reservation.duration) : "Unknown", bgColor: blockColor.bgColor, selectedBgColor: blockColor.bgColor, color: blockColor.color @@ -744,7 +752,8 @@ export class TimelineView extends Component { start_time: start_time, end_time: end_time, name: reservationSpec.activity.type, project: reservation.project_id, group: "RESERVATION", type: 'RESERVATION', - title: `${reservationSpec.activity.type}${reservation.project_id ? ("-" + reservation.project_id) : ""}`, + actType: `${reservationSpec.activity.type}${reservation.project_id ? ("-" + reservation.project_id) : ""}`, + title: "", desc: reservation.description, duration: reservation.duration ? UnitConverter.getSecsToHHmmss(reservation.duration) : "Unknown", bgColor: blockColor.bgColor, selectedBgColor: blockColor.bgColor, color: blockColor.color @@ -790,16 +799,18 @@ export class TimelineView extends Component { let reservations = this.reservations; for (const reservation of reservations) { const reservationStartTime = moment.utc(reservation.start_time); - const reservationEndTime = reservation.duration ? reservationStartTime.clone().add(reservation.duration, 'seconds') : endTime; + const reservationEndTime = reservation.duration ? reservationStartTime.clone().add(reservation.duration, 'seconds') : null; const reservationSpec = reservation.specifications_doc; - if ((reservationStartTime.isSame(startTime) - || reservationStartTime.isSame(endTime) - || reservationStartTime.isBetween(startTime, endTime) - || reservationEndTime.isSame(startTime) - || reservationEndTime.isSame(endTime) - || reservationEndTime.isBetween(startTime, endTime) - || (reservationStartTime.isSameOrBefore(startTime) - && reservationEndTime.isSameOrAfter(endTime))) + // if ((reservationStartTime.isSame(startTime) + // || reservationStartTime.isSame(endTime) + // || reservationStartTime.isBetween(startTime, endTime) + // || reservationEndTime.isSame(startTime) + // || reservationEndTime.isSame(endTime) + // || reservationEndTime.isBetween(startTime, endTime) + // || (reservationStartTime.isSameOrBefore(startTime) + // && reservationEndTime.isSameOrAfter(endTime))) + if ((reservationStartTime.isSameOrBefore(endTime) + && (reservationEndTime === null || reservationEndTime.isSameOrAfter(startTime))) && (this.state.reservationFilter.length === 0 || // No reservation filter added this.state.reservationFilter.indexOf(reservationSpec.activity.type) >= 0 )) { // Reservation reason == Filtered reaseon if (!this.state.stationView || @@ -886,12 +897,13 @@ export class TimelineView extends Component { /** * Callback function to pass to the Scheduling Unit Table component to pass back filtered data. - * The same function is called when the filter applied in the task table so that the tasks shown in the timeline - * are updated accordingly. + * The same function is called when the filter applied in the task and reservation tables so that + * the tasks and reservations shown in the timeline are updated accordingly. * @param {Array} filteredSUData - filtered data from Scheduling Unit list table * @param {Array} filteredTaskData - filtered data from Task list table. This will be null when the function is called by SU List table. + * @param {Array} filteredReservData - filtered data from Reservation list table. */ - suListFilterCallback(filteredSUData, filteredTaskData) { + suListFilterCallback(filteredSUData, filteredTaskData, filteredReservData) { let group = [], items = []; const suBlueprints = this.state.suBlueprints; for (const data of filteredSUData) { @@ -920,7 +932,7 @@ export class TimelineView extends Component { } } if (this.state.stationView || this.state.showReservation) { - items = this.addStationReservations(items, this.state.currentStartTime, this.state.currentEndTime); + items = this.addStationReservations(items, this.state.currentStartTime, this.state.currentEndTime, filteredReservData); } if (this.state.showReservation) { let reservationGroup = [{id: "RESERVATION", parent: "RESERVATION", @@ -1475,7 +1487,8 @@ export class TimelineView extends Component { } {/* SU Item Tooltip popover with SU status color */} - <OverlayPanel className="timeline-popover" ref={(el) => this.popOver = el} dismissable> + <div class="p-overlaypanel p-component timeline-popover" style={{...this.state.popPosition, zIndex: 1324, opacity: 1.944}}> + <div class="p-overlaypanel-content"> {(mouseOverItem && (["SCHEDULE", "TASK", "STATION_TASK"].indexOf(mouseOverItem.type) >= 0)) && <div className={`p-grid su-${mouseOverItem.status}`} style={{ width: '350px' }}> <h3 className={`col-12 su-${mouseOverItem.status}-icon`}>{mouseOverItem.type === 'SCHEDULE' ? 'Scheduling Unit ' : 'Task '}Overview</h3> @@ -1545,7 +1558,8 @@ export class TimelineView extends Component { <div className="col-7">{mouseOverItem.planned ? 'Yes' : 'No'}</div> </div> } - </OverlayPanel> + </div> + </div> {!this.state.isLoading && <Websocket url={process.env.REACT_APP_WEBSOCKET_URL} onOpen={this.onConnect} onMessage={this.handleData} onClose={this.onDisconnect} />} {this.state.showDialog && diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/week.view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/week.view.js index c33cd2a91c08c3af6c976a193570990288a63707..5c97d3c7250de69c2b59bbc4280cf328aa871eac 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/week.view.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/week.view.js @@ -21,7 +21,6 @@ import UnitConverter from '../../utils/unit.converter'; import Validator from '../../utils/validator'; import SchedulingUnitSummary from '../Scheduling/summary'; import UIConstants from '../../utils/ui.constants'; -import { OverlayPanel } from 'primereact/overlaypanel'; import { TieredMenu } from 'primereact/tieredmenu'; import { InputSwitch } from 'primereact/inputswitch'; import { Dropdown } from 'primereact/dropdown'; @@ -76,6 +75,7 @@ export class WeekTimelineView extends Component { datasetStartTime: null, datasetEndTime:null, showDialog: false, userrole: AuthStore.getState(), + popPosition: {display: 'none'} } this.STATUS_BEFORE_SCHEDULED = ['defining', 'defined', 'schedulable']; // Statuses before scheduled to get station_group this.reservations = []; @@ -115,9 +115,9 @@ export class WeekTimelineView extends Component { ] this.setState({menuOptions: menuOptions, userPermission: weekviewPermission}); - ReservationService.getReservations().then(reservations => { - this.reservations = reservations; - }); + // ReservationService.getReservations().then(reservations => { + // this.reservations = reservations; + // }); UtilService.getReservationTemplates().then(templates => { this.reservationTemplate = templates.length > 0 ? templates[0] : null; if (this.reservationTemplate) { @@ -244,6 +244,11 @@ export class WeekTimelineView extends Component { */ async loadSchedulingUnits(startTime, endTime) { let suList = []; + ReservationService.getTimelineReservations(startTime.format(UIConstants.CALENDAR_DATETIME_FORMAT), + endTime.format(UIConstants.CALENDAR_DATETIME_FORMAT)) + .then(reservations => { + this.reservations = _.uniqBy(this.reservations.concat(reservations), 'id'); + }); let suBlueprints = await ScheduleService.getExpandedSUList(startTime.format(UIConstants.CALENDAR_DATETIME_FORMAT), endTime.format(UIConstants.CALENDAR_DATETIME_FORMAT)); suBlueprints = _.filter(suBlueprints, suBlueprint => suBlueprint.status.toLowerCase !== 'obsolete'); @@ -396,7 +401,7 @@ export class WeekTimelineView extends Component { * @param {Event} evt */ onItemMouseOut(evt) { - this.popOver.toggle(evt); + this.setState({popPosition: {display: 'none'}}); } /** @@ -405,6 +410,13 @@ export class WeekTimelineView extends Component { * @param {Object} item */ onItemMouseOver(evt, item) { + let popPosition = {display:"block", + left:`${evt.pageX+400>window.innerWidth?evt.pageX-400:evt.pageX+20}px`}; + if (evt.clientY > window.screen.height/2) { + popPosition.bottom = `${evt.clientY - evt.pageY+30}px`; + } else { + popPosition.top = `${evt.pageY}px`; + } if (item.type === "SCHEDULE") { const itemSU = _.find(this.state.suBlueprints, { id: parseInt(item.id.split("-")[0]) }); const itemStations = itemSU.stations; @@ -436,8 +448,7 @@ export class WeekTimelineView extends Component { item.displayStartTime = moment.utc(reservation.start_time); item.displayEndTime = reservation.duration ? moment.utc(reservation.stop_time) : null; } - this.popOver.toggle(evt); - this.setState({ mouseOverItem: item }); + this.setState({ mouseOverItem: item, popPosition: popPosition }); } /** @@ -460,10 +471,11 @@ export class WeekTimelineView extends Component { currentUTC = this.state.currentUTC.clone().add(direction * 7, 'days'); if (startTime && endTime) { for (const suBlueprint of suBlueprints) { - if (moment.utc(suBlueprint.start_time).isBetween(startTime, endTime) + if ((moment.utc(suBlueprint.start_time).isBetween(startTime, endTime) || moment.utc(suBlueprint.stop_time).isBetween(startTime, endTime) || (moment.utc(suBlueprint.start_time).isSameOrBefore(startTime, endTime) && - moment.utc(suBlueprint.stop_time).isSameOrAfter(startTime, endTime))) { + moment.utc(suBlueprint.stop_time).isSameOrAfter(startTime, endTime))) + ) { suBlueprintList.push(suBlueprint); const suStartTime = moment.utc(suBlueprint.start_time); const suEndTime = moment.utc(suBlueprint.stop_time); @@ -548,22 +560,47 @@ export class WeekTimelineView extends Component { } /** - * Callback function to pass to the ViewTable component to pass back filtered data - * @param {Array} filteredData + * Callback function to pass to the list tab component to pass back filtered data from the tables. + * Updates the timeline with filtered SU and reservation blocks. + * @param {Array} filteredSUData + * @param {Array} filteredTaskData + * @param {Array} filteredReservData */ - suListFilterCallback(filteredData) { - /*let group=[], items = []; + async suListFilterCallback(filteredSUData, filteredTaskData, filteredReservData) { + let currentUTC = this.state.currentUTC; + const suFilters = _.map(filteredSUData, data => parseInt(data.Id)); + let group=[], items = []; + const startTime = this.state.startTime; + const endTime = this.state.endTime; const suBlueprints = this.state.suBlueprints; - for (const data of filteredData) { - const suBlueprint = _.find(suBlueprints, {actionpath: data.actionpath}); - items.push(this.getTimelineItem(suBlueprint)); - if (!_.find(group, {'id': suBlueprint.suDraft.id})) { - group.push({'id': suBlueprint.suDraft.id, title: suBlueprint.suDraft.name}); + for (const suBlueprint of suBlueprints) { + if ((moment.utc(suBlueprint.start_time).isBetween(startTime, endTime) + || moment.utc(suBlueprint.stop_time).isBetween(startTime, endTime) + || (moment.utc(suBlueprint.start_time).isSameOrBefore(startTime, endTime) && + moment.utc(suBlueprint.stop_time).isSameOrAfter(startTime, endTime))) + && (suFilters.indexOf(suBlueprint.id) >= 0) + ) { + const suStartTime = moment.utc(suBlueprint.start_time); + const suEndTime = moment.utc(suBlueprint.stop_time); + if (suStartTime.format("MM-DD-YYYY") !== suEndTime.format("MM-DD-YYYY")) { + let suBlueprintStart = _.cloneDeep(suBlueprint); + let suBlueprintEnd = _.cloneDeep(suBlueprint); + suBlueprintStart.stop_time = suStartTime.hour(23).minutes(59).seconds(59).format('YYYY-MM-DDTHH:mm:ss.00000'); + suBlueprintEnd.start_time = suEndTime.hour(0).minutes(0).seconds(0).format('YYYY-MM-DDTHH:mm:ss.00000'); + items.push(await this.getTimelineItem(suBlueprintStart, currentUTC)); + items.push(await this.getTimelineItem(suBlueprintEnd, currentUTC)); + + } else { + items.push(await this.getTimelineItem(suBlueprint, currentUTC)); + } } } + if (this.state.reservationEnabled) { + items = this.addWeekReservations(items, startTime, endTime, currentUTC, filteredReservData); + } if (this.timeline) { - this.timeline.updateTimeline({group: group, items: items}); - }*/ + this.timeline.updateTimeline({group: this.state.group, items: items}); + } } filterByProject(project) { @@ -755,8 +792,9 @@ export class WeekTimelineView extends Component { * @param {moment} startTime * @param {moment} endTime */ - addWeekReservations(items, startTime, endTime, currentUTC) { + addWeekReservations(items, startTime, endTime, currentUTC, reservFilterData) { let reservations = _.cloneDeep(this.reservations); + let filteredReservations = reservFilterData?_.map(reservFilterData, data => {data.id = data.actionpath.split("view/")[1]; return parseInt(data.id);}):[]; for (const reservation of reservations) { const reservationStartTime = moment.utc(reservation.start_time); const reservationEndTime = reservation.duration ? reservationStartTime.clone().add(reservation.duration, 'seconds') : endTime; @@ -769,6 +807,7 @@ export class WeekTimelineView extends Component { || reservationEndTime.isBetween(startTime, endTime) || (reservationStartTime.isSameOrBefore(startTime) && reservationEndTime.isSameOrAfter(endTime))) + && (filteredReservations.length === 0 || filteredReservations.indexOf(reservation.id)>=0) && (!this.state.reservationFilter || // No reservation filter added reservationSpec.activity.type === this.state.reservationFilter)) { // Reservation reason == Filtered reaseon reservation.stop_time = reservationEndTime; @@ -835,7 +874,8 @@ export class WeekTimelineView extends Component { name: reservationSpec.activity.type, project: reservation.project_id, group: group, type: 'RESERVATION', - title: `${reservationSpec.activity.type}${reservation.project_id ? ("-" + reservation.project_id) : ""}`, + actType: `${reservationSpec.activity.type}${reservation.project_id ? ("-" + reservation.project_id) : ""}`, + title: '', desc: reservation.description, duration: reservation.duration ? UnitConverter.getSecsToHHmmss(reservation.duration) : "Unknown", bgColor: blockColor.bgColor, selectedBgColor: blockColor.bgColor, color: blockColor.color @@ -871,18 +911,12 @@ export class WeekTimelineView extends Component { let reservations = this.reservations; for (const reservation of reservations) { const reservationStartTime = moment.utc(reservation.start_time); - const reservationEndTime = reservation.duration ? reservationStartTime.clone().add(reservation.duration, 'seconds') : endTime; + const reservationEndTime = reservation.duration ? reservationStartTime.clone().add(reservation.duration, 'seconds') : null; const reservationSpec = reservation.specifications_doc; - if ((reservationStartTime.isSame(startTime) - || reservationStartTime.isSame(endTime) - || reservationStartTime.isBetween(startTime, endTime) - || reservationEndTime.isSame(startTime) - || reservationEndTime.isSame(endTime) - || reservationEndTime.isBetween(startTime, endTime) - || (reservationStartTime.isSameOrBefore(startTime) - && reservationEndTime.isSameOrAfter(endTime))) + if ((reservationStartTime.isSameOrBefore(endTime) + && (reservationEndTime === null || reservationEndTime.isSameOrAfter(startTime))) && (!this.state.reservationFilter || // No reservation filter added - reservationSpec.activity.type === this.state.reservationFilter)) { // Reservation reason == Filtered reaseon + reservationSpec.activity.type === this.state.reservationFilter)) { // Reservation reason == Filtered reaseon let item = _.cloneDeep(reservation); item.stop_time = item.stop_time || "Unknown"; item.duration = UnitConverter.getSecsToDDHHmmss(item.duration) || "Unknown"; @@ -1103,7 +1137,8 @@ export class WeekTimelineView extends Component { </> } {/* SU Item Tooltip popover with SU status color */} - <OverlayPanel className="timeline-popover" ref={(el) => this.popOver = el} dismissable> + <div class="p-overlaypanel p-component timeline-popover" style={{...this.state.popPosition, zIndex: 1324, opacity: 1.944}}> + <div class="p-overlaypanel-content"> {mouseOverItem && mouseOverItem.type === "SCHEDULE" && <div className={`p-grid su-${mouseOverItem.status}`} style={{ width: '350px' }}> <label className={`col-5 su-${mouseOverItem.status}-icon`}>Project:</label> @@ -1159,7 +1194,8 @@ export class WeekTimelineView extends Component { <div className="col-7">{mouseOverItem.planned ? 'Yes' : 'No'}</div> </div> } - </OverlayPanel> + </div> + </div> {/* Open Websocket after loading all initial data */} {!this.state.isLoading && <Websocket url={process.env.REACT_APP_WEBSOCKET_URL} onOpen={this.onConnect} onMessage={this.handleData} onClose={this.onDisconnect} />} diff --git a/SAS/TMSS/frontend/tmss_webapp/src/services/reservation.service.js b/SAS/TMSS/frontend/tmss_webapp/src/services/reservation.service.js index c0a348feefdcc9f90b0a8ab4f158b59f484c6af5..3ca64af657c4f18c6e2fd8893bc6d5e1ae8b2578 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/services/reservation.service.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/services/reservation.service.js @@ -45,6 +45,33 @@ const ReservationService = { } return reservations; }, + /** Get the reservations that are present during the period specified by startTime and stopTime */ + getTimelineReservations: async(startTime, stopTime) => { + let reservations = []; + try { + let url = `/api/reservation/?ordering=id`; + if (stopTime) { // Gets the reservations started and stopped between the period + url = `${url}&start_time_before=${stopTime || ''}&stop_time_after=${startTime || ''}`; + } else if (startTime) { // Gets the reservations started before the startTime and still exists + url = `${url}&start_time_before=${startTime || ''}&stop_time_isnull=true`; + } + let initialResponse = await axios.get(url); + const totalCount = initialResponse.data.count; + const initialCount = initialResponse.data.results.length + reservations = reservations.concat(initialResponse.data.results); + if (totalCount > initialCount) { + let secondResponse = await axios.get(`${url}&limit=${totalCount-initialCount}&offset=${initialCount}`); + reservations = reservations.concat(secondResponse.data.results); + } + if (stopTime) { // Get the reservations started before the stopTime and exists. + let indefiniteReservations = await ReservationService.getTimelineReservations(stopTime); + reservations = reservations.concat(indefiniteReservations); + } + } catch(error) { + console.error('[schedule.services.getTimelineReservations]',error); + } + return reservations; + }, getReservation: async function (id) { try { const response = await axios.get(`/api/reservation/${id}`);