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/view.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/view.js index e344913493ee89326aec3d14f0b634b3891c2400..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 = []; @@ -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 }); } /** @@ -732,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 @@ -746,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 @@ -1480,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> @@ -1550,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 3dae7b1572f96c2490190f21b45bd16a23545bf9..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 = []; @@ -401,7 +401,7 @@ export class WeekTimelineView extends Component { * @param {Event} evt */ onItemMouseOut(evt) { - this.popOver.toggle(evt); + this.setState({popPosition: {display: 'none'}}); } /** @@ -410,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; @@ -441,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 }); } /** @@ -465,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); @@ -553,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) { @@ -760,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; @@ -774,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; @@ -840,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 @@ -1102,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> @@ -1158,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} />}