diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.summary.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.summary.js index 08e159f100f4fe368e5f5f4f280c6848a8db6899..f9e0d4405ed5e26e2a5315f0bf3bebf4ed556e9f 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.summary.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Reservation/reservation.summary.js @@ -111,12 +111,13 @@ export class ReservationSummary extends Component { delete specifications['$schema']; } + const hasStopTime = !!reservation.stop_time let summaryFields = [ {labelName:"Name", value: reservation.name, formatTime: false}, {labelName:"Description", value: reservation.description, formatTime: false}, {labelName:"Project", value: reservation.project_id, formatTime: false}, {labelName:"Start Time", value: reservation.start_time, formatTime: true}, - {labelName:"Stop Time", value: reservation.stop_time, formatTime: true}, + {labelName:"Stop Time", value: hasStopTime ? reservation.stop_time : "-", formatTime: hasStopTime}, {labelName:"Duration (HH:mm:ss)", value: reservation.stop_time ? UnitConverter.getSecsToHHmmss(reservation.duration) : "Unknown", formatTime: false}, ] diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/WeekView.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/WeekView.js index ffcdaa204f67b0abb473474f9f729c925f5c2f39..5b135cdd50740a7d646ae68e93c706aecf2519ab 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/WeekView.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/WeekView.js @@ -107,7 +107,7 @@ export default function WeekView() { let actionsMenuRef = useRef() //general page info - const [permissions, setPermissions] = useState(undefined) + const [permissions, setPermissions] = useState() const [schedulerSettings, setSchedulerSettings] = useState({ isDynamicSchedulerOn: undefined, isFixedTimeSchedulerOn: undefined, @@ -133,8 +133,8 @@ export default function WeekView() { sunTimingsItems: [] }) const [headerSettings, setHeaderSettings] = useState({ - timeSteps: timelineStore.timeSteps === undefined ? UIConstants.ALL_TIMESTEPS[0] : timelineStore.timeSteps, - unit: timelineStore.headerUnit === undefined ? "hour" : timelineStore.headerUnit, + timeSteps: timelineStore.timeSteps ?? UIConstants.ALL_TIMESTEPS[0], + unit: timelineStore.headerUnit ?? "hour", lstShiftInSeconds: undefined }) const [visibleTime, setVisibleTime] = useState({ @@ -143,12 +143,12 @@ export default function WeekView() { }) //timeline item pop-over component - const [mouseOverItem, setMouseOverItem] = useState(undefined) + const [mouseOverItem, setMouseOverItem] = useState() const [popPosition, setPopPosition] = useState({display: 'none'}) - const [summaryItem, setSummaryItem] = useState(undefined) + const [summaryItem, setSummaryItem] = useState() //marker that also displays time information on thge group - const [cursorTimeUTC, setCursorTimeUTC] = useState(undefined) + const [cursorTimeUTC, setCursorTimeUTC] = useState() const [cursorInformation, setCursorInformation] = useState({ utc: undefined, lst: undefined, diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/TimelineItemPopover.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/TimelineItemPopover.js index fd3c18f78aef08715f63f0642cd7c0a93b017caf..6485cf1026a66d85530c964a15b97148a4d6621f 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/TimelineItemPopover.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/TimelineItemPopover.js @@ -4,25 +4,30 @@ export function getFieldsHtml(fields, status = undefined, style = {}) { const labelClassName = status ? `col-5 su-${status}-icon` : 'col-5' return fields.map((field, index) => ( - <div key={index} className="entry"> + <div key={field.label + "-" + index} className="entry"> <label className={labelClassName} style={style}>{field.label}</label> <div className="col-7 initial-line-height">{field.value}</div> </div> )) } +const labelObject = (label, value) => ({ + label: label, + value: value, +}) + function getScheduleItem(mouseOverItem) { const scheduleItemFields = [ - {label: "Project:", value: mouseOverItem.project}, - {label: "Scheduling Unit:", value: mouseOverItem.name}, - {label: "Scheduler:", value: mouseOverItem.scheduleMethod}, - {label: "Start Time:", value: mouseOverItem.real_start_time}, - {label: "End Time:", value: mouseOverItem.real_end_time}, - {label: "Antenna Set:", value: mouseOverItem.antennaSet}, - {label: "Stations:", value: mouseOverItem.stations}, - {label: "Status:", value: mouseOverItem.status}, - {label: "Exposure Time:", value: mouseOverItem.on_sky_duration}, - {label: "Duration:", value: mouseOverItem.duration}, + labelObject("Project:", mouseOverItem.project), + labelObject("Scheduling Unit:", mouseOverItem.name), + labelObject("Scheduler:", mouseOverItem.scheduleMethod), + labelObject("Start Time:", mouseOverItem.real_start_time), + labelObject("End Time:", mouseOverItem.real_end_time), + labelObject("Antenna Set:", mouseOverItem.antennaSet), + labelObject("Stations:", mouseOverItem.stations), + labelObject("Status:", mouseOverItem.status), + labelObject("Exposure Time:", mouseOverItem.on_sky_duration), + labelObject("Duration:", mouseOverItem.duration), ]; if (mouseOverItem.status === 'unschedulable') { @@ -38,17 +43,18 @@ function getScheduleItem(mouseOverItem) { </div> ); } + function getReservationItem(mouseOverItem) { let reservationItemFields = [ - {label: "Name:", value: mouseOverItem.name}, - {label: "Description:", value: mouseOverItem.desc}, - {label: "Type:", value: mouseOverItem.activity_type}, - {label: "Stations:", value: mouseOverItem.stations,}, - {label: "Project:", value: mouseOverItem.project ? mouseOverItem.project : "-"}, - {label: "Start Time:", value: mouseOverItem.real_start_time}, - {label: "End Time:", value: mouseOverItem.real_end_time}, - {label: "Duration:", value: mouseOverItem.duration}, - {label: "Planned:", value: mouseOverItem.planned ? "Yes" : "No"}, + labelObject("Name:", mouseOverItem.name), + labelObject("Description:", mouseOverItem.desc), + labelObject("Type:", mouseOverItem.activity_type), + labelObject("Stations:", mouseOverItem.stations,), + labelObject("Project:", mouseOverItem.project ? mouseOverItem.project : "-"), + labelObject("Start Time:", mouseOverItem.real_start_time), + labelObject("End Time:", mouseOverItem.real_end_time), + labelObject("Duration:", mouseOverItem.duration), + labelObject("Planned:", mouseOverItem.planned ? "Yes" : "No"), ]; const reservationStyle = { diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/DateTimeInfo.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/DateTimeInfo.js index 0ce9a61aa31d5fbab4a2f0a489bc10e42c862d3d..3f299a3bd23878cd2fbe0db0f5ba62d1d9152830 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/DateTimeInfo.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/DateTimeInfo.js @@ -13,8 +13,8 @@ function getDateOrTimeElement(title, element) { } export default function DateTimeInfo() { - const [currentUTC, setCurrentUTC] = useState(undefined) - const [currentLST, setCurrentLST] = useState(undefined) + const [currentUTC, setCurrentUTC] = useState() + const [currentLST, setCurrentLST] = useState() useEffect(() => { // initializer diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/DateTimeNavigator.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/DateTimeNavigator.js index 3b4df1106018ac1aee12869260ee750994148ffc..d26ea8093e61e37e6592218969bb33348a00bb9a 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/DateTimeNavigator.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/DateTimeNavigator.js @@ -56,7 +56,7 @@ function getDateSelector(isRange, startTime, setStartAndEndTimeCallback) { } function getWeekChanger(isRange, startTime, endTime, setStartAndEndTimeCallback) { - if (isRange || startTime === undefined || endTime === undefined) { + if (isRange || !startTime|| !endTime) { return null } const weekNumbers = startTime.week() === endTime.week() ? startTime.week() : startTime.week() + "," + endTime.week() @@ -124,7 +124,7 @@ export default function DateTimeNavigator(props) { } = props - const [suIdInput, setSUIdInput] = useState(undefined) + const [suIdInput, setSUIdInput] = useState() const [searchForSUIdTrigger, setSearchForSUIdTrigger] = useState(false) useEffect(() => { diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/Filters.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/Filters.js index 679d8b754b805e596052bfcb83873393bf4c3f0b..6f0d48387342e0f1021de87756b9de899da9139a 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/Filters.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/Filters.js @@ -92,16 +92,16 @@ export default function Filters(props) { projectNames: [] }) - const [onSkyToggle, setOnSkyToggle] = useState(timelineStore.onSkyToggle === undefined ? true : timelineStore.onSkyToggle) + const [onSkyToggle, setOnSkyToggle] = useState(timelineStore.onSkyToggle ?? true) - const [reservationsToggle, setReservationsToggle] = useState(timelineStore.reservationsToggle === undefined ? false : timelineStore.reservationsToggle) - const [reservationFilter, setReservationFilter] = useState(timelineStore.reservationFilter === undefined ? multiSelectAllOptions.reservationReasons : timelineStore.reservationFilter) + const [reservationsToggle, setReservationsToggle] = useState(timelineStore.reservationsToggle ?? false) + const [reservationFilter, setReservationFilter] = useState(timelineStore.reservationFilter ?? multiSelectAllOptions.reservationReasons) - const [schedulingUnitsToggle, setSchedulingUnitsToggle] = useState(timelineStore.schedulingUnitsToggle === undefined ? true : timelineStore.schedulingUnitsToggle) - const [schedulingUnitFilter, setSchedulingUnitFilter] = useState(timelineStore.schedulingUnitFilter === undefined ? multiSelectAllOptions.schedulingUnitStatuses : timelineStore.schedulingUnitFilter) + const [schedulingUnitsToggle, setSchedulingUnitsToggle] = useState(timelineStore.schedulingUnitsToggle ?? true) + const [schedulingUnitFilter, setSchedulingUnitFilter] = useState(timelineStore.schedulingUnitFilter ?? multiSelectAllOptions.schedulingUnitStatuses) - const [projectToggle, setProjectToggle] = useState(timelineStore.projectToggle === undefined ? true : timelineStore.projectToggle) - const [projectFilter, setProjectFilter] = useState(timelineStore.projectFilter === undefined ? multiSelectAllOptions.projectNames : timelineStore.projectFilter) + const [projectToggle, setProjectToggle] = useState(timelineStore.projectToggle ?? true) + const [projectFilter, setProjectFilter] = useState(timelineStore.projectFilter ?? multiSelectAllOptions.projectNames) useEffect(() => { //initializer diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/ZoomAndMove.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/ZoomAndMove.js index 1a0a99084e32bf984eb6459ab391be7c9dc459c9..ba36400db6ede1c300e81e65ec136d572982abc8 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/ZoomAndMove.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/components/toolbar/ZoomAndMove.js @@ -157,9 +157,7 @@ export default function ZoomAndMove(props) { } = props const [zoomSettings, setZoomSettings] = useState({ - zoomLevelName: timelineStore.zoomLevelName === undefined - ? UIConstants.DEFAULT_ZOOM_LEVEL.name - : timelineStore.zoomLevelName, + zoomLevelName: timelineStore.zoomLevelName ?? UIConstants.DEFAULT_ZOOM_LEVEL.name, selectedTime: timelineStore.selectedTime, }); diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/data/filters.data.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/data/filters.data.js index dbafc86aa7e9ed4d7e9bf0330dd6d9f9974e272d..a4fd79bb19672672a59b16997c17f38fddff02b6 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/data/filters.data.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/data/filters.data.js @@ -6,7 +6,7 @@ import UtilService from "../../../services/util.service"; async function fetchReservationTemplatesGetReasons() { let reasons = [] UtilService.getReservationTemplates().then(templates => { - const reservationTemplate = templates.length > 0 ? templates[0] : null; //use the first if there are templates found + const reservationTemplate = templates[0] ?? null; //use the first if there are templates found if (reservationTemplate) { let foundTemplateReason = reservationTemplate.ref_resolved_schema?.properties?.activity?.properties?.type?.enum; foundTemplateReason.forEach(reason => { diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/helpers/timeline.headers.helper.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/helpers/timeline.headers.helper.js index 8ffc3e85c72b131b7e5fa5c42ed7db691bd0beb5..72c42e181ed74172a109b8885825e3f9f93e156b 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/helpers/timeline.headers.helper.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/helpers/timeline.headers.helper.js @@ -1,13 +1,10 @@ import React from "react"; -import moment from "moment/moment"; -import UIConstants from "../../../utils/ui.constants"; -// parameters 'getIntervalProps, intervalContext' are mandatory for the library /** * Shifts the header intervals for the LST time header * Library's documentation about date header renderers: https://github.com/namespace-ee/react-calendar-timeline#dateheader * @param getIntervalProps mandatory by library - * @param intervalContext + * @param intervalContext mandatory by library * @param lstShiftInSeconds based on the visible start time of the Timeline in utc, the corresponding lst time is retrieved * and the approximate shift between the two is calculated for the header intervals. It is not a 100% correct, but close enough * @return {JSX.Element} diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/helpers/toolbar/filters.helper.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/helpers/toolbar/filters.helper.js index a6889e5ae7e0aa25cf00c7d9a5de2353dc80d3d2..5f9d4e92a71c6fcece7b563b8d40bdeb69529b84 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/helpers/toolbar/filters.helper.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/helpers/toolbar/filters.helper.js @@ -62,7 +62,7 @@ export function splitObjectIfSpanIsMultipleDays(element, shouldShowOnSkyTimes) { throw new TypeError("Start and/or stop times are not invalid for:\n" + JSON.stringify(element)) } - if (spanInDays > 0) { + if (spanInDays) { return splitElementPerDay(element, spanInDays, shouldShowOnSkyTimes) } return [element] diff --git a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/helpers/week.view.helper.js b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/helpers/week.view.helper.js index 6d35aa46274a1498401a5d3eccfef385557be9f5..57e1f529255cb08d65bda749059cd9a93ce88f5f 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/helpers/week.view.helper.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/routes/Timeline/helpers/week.view.helper.js @@ -65,53 +65,66 @@ export async function updateSchedulerStatuses(switchValue, status, setSchedulerS } } +const actionObject = (title, icon, classes, className, callbackProp, content, type, actOn) => ({ + title: title, + icon: icon, + classes: classes, + className: className, + content: content, + type: type, + actOn: actOn, + props: callbackProp +}) + +const actionButtonObject = (title, icon, classes, callbackProp) => ( + actionObject(title, icon, classes, undefined, callbackProp, undefined,"button", "click") +) + +const actionTagObject = (title, content, className) => ( + actionObject(title, undefined, undefined, className, undefined, content, "tag", "click") +) + export function getHeaderActions(isLoading, setRefetchToggle, refetchToggle, actionsMenuRef) { return [ - { - title: (isLoading ? "System is already reloading the timeline data" : "Reload the timeline data"), - icon: "fa-" + (isLoading ? "hourglass fa-spin" : "sync-alt"), - classes: `subsystem subsystem--${isLoading ? "on" : "standard"}`, - type: "button", - actOn: "click", - props: {callback: () => setRefetchToggle(!refetchToggle)} - }, - { - title: "Options", - icon: "fa-bars", - classes: "subsystem subsystem--standard", - type: "button", - actOn: "click", - props: {callback: (ev) => actionsMenuRef.current.toggle(ev)}, - }, + actionButtonObject( + isLoading ? "System is already reloading the timeline data" : "Reload the timeline data", + "fa-" + (isLoading ? "hourglass fa-spin" : "sync-alt"), + `subsystem subsystem--${isLoading ? "on" : "standard"}`, + {callback: () => setRefetchToggle(!refetchToggle)} + ), + actionButtonObject( + "Options", + "fa-bars", + "subsystem subsystem--standard", + {callback: (ev) => actionsMenuRef.current.toggle(ev)} + ), ]; } + export function getSchedulerActions(schedulerSettings, setShowSchedulerSettingsDialog) { return [ - { - content: "D", - title: schedulerSettings.isDynamicSchedulerOn ? "Dynamic Scheduling is On" : "Dynamic Scheduling is Off", - className: "tag tag--" + (schedulerSettings.isDynamicSchedulerOn ? "on" : "off"), - type: "tag" - }, - { - content: "F", - title: schedulerSettings.isFixedTimeSchedulerOn ? "Fixed time Scheduling is On" : "Fixed time Scheduling is Off", - className: "tag tag--" + (schedulerSettings.isFixedTimeSchedulerOn ? "on" : "off"), - type: "tag" - }, - { - title: (schedulerSettings.isDynamicSchedulerActive ? "Dynamic Scheduler is Active" : (schedulerSettings.isDynamicSchedulerOn ? "Dynamic Scheduler is Idle" : "Dynamic Scheduler is stopped")) + ". Click to change the scheduler", - icon: "fa-" + (schedulerSettings.isDynamicSchedulerActive ? "play" : (schedulerSettings.isDynamicSchedulerOn ? "pause" : "stop")), - classes: "subsystem subsystem--" + (schedulerSettings.isDynamicSchedulerActive ? "on" : (schedulerSettings.isDynamicSchedulerOn ? "paused" : "off")), - type: "button", - actOn: "click", - props: {callback: () => setShowSchedulerSettingsDialog(true)} - }, + actionTagObject( + schedulerSettings.isDynamicSchedulerOn ? "Dynamic Scheduling is On" : "Dynamic Scheduling is Off", + "D", + "tag tag--" + (schedulerSettings.isDynamicSchedulerOn ? "on" : "off") + ), + actionTagObject( + schedulerSettings.isFixedTimeSchedulerOn ? "Fixed time Scheduling is On" : "Fixed time Scheduling is Off", + "F", + "tag tag--" + (schedulerSettings.isFixedTimeSchedulerOn ? "on" : "off") + ), + actionButtonObject( + (schedulerSettings.isDynamicSchedulerActive ? "Dynamic Scheduler is Active" : + (schedulerSettings.isDynamicSchedulerOn ? "Dynamic Scheduler is Idle" : "Dynamic Scheduler is stopped")) + ". Click to change the scheduler", + "fa-" + (schedulerSettings.isDynamicSchedulerActive ? "play" : (schedulerSettings.isDynamicSchedulerOn ? "pause" : "stop")), + "subsystem subsystem--" + (schedulerSettings.isDynamicSchedulerActive ? "on" : (schedulerSettings.isDynamicSchedulerOn ? "paused" : "off")), + {callback: () => setShowSchedulerSettingsDialog(true)} + ) ]; } -export function getPageHeaderOptionsMenuItems(permissions) { +export function getPageHeaderOptionsMenuItems(permissions) { //TODO factory method return [ { label: 'Add Reservation', diff --git a/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.js b/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.js index 1f0b98dcbb9bf9bf0f6105b736f9004709b5a68f..56ba195f8ac0066a93142f07f626fd8dadb55dc5 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.js @@ -98,8 +98,7 @@ const UnitConverter = { }, getSecsToHrsWithFractionDigits: function (seconds, fractionDigits = 2) { if (typeof seconds !== "number" || isNaN(seconds)) { - console.error("Cannot convert a wrong value. Must be a number", seconds) - return "Invalid" + throw TypeError("Cannot convert a wrong value. Must be a number") } return (seconds / 3600).toFixed(fractionDigits) }, diff --git a/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.test.js b/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.test.js index c52cf8c7ef3f536bfdbe08f6cae0c5ea73350a79..c48d61acda20b9d220dc3b331708c6bd833eb3a0 100644 --- a/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.test.js +++ b/SAS/TMSS/frontend/tmss_webapp/src/utils/unit.converter.test.js @@ -1,5 +1,4 @@ import UnitConverter from "./unit.converter"; -import {getConstraintTemplate} from "../routes/Scheduling/create.helper"; describe('getSubbandOutput', () => { test.each(['', null, undefined])('should return empty array on not specified input', (subbandStringInput) => { @@ -76,16 +75,9 @@ describe('getSecsToHrsWithFractionDigits', () => { }); test.each(["12", null, undefined, []])('Should log an error and return "Invalid" for wrong input: %s', (wrongSeconds) => { - const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation(); - const result = UnitConverter.getSecsToHrsWithFractionDigits(wrongSeconds); - - expect(result).toBe("Invalid"); - expect(consoleErrorSpy).toHaveBeenCalledWith( - 'Cannot convert a wrong value. Must be a number', - wrongSeconds - ); - - consoleErrorSpy.mockRestore(); + expect(() => { + UnitConverter.getSecsToHrsWithFractionDigits(wrongSeconds); + }).toThrow(TypeError) }) }); \ No newline at end of file