From 70664553c2ef80a1098b6be40c669b4275813396 Mon Sep 17 00:00:00 2001 From: Aaron Plave Date: Fri, 19 Jul 2024 15:26:20 -0700 Subject: [PATCH] Refactor invalid date handling --- .../ActivityDirectiveChangelog.svelte | 12 +++++---- .../activity/ActivityDirectiveForm.svelte | 10 +++---- .../ActivityDirectivesTablePanel.svelte | 7 ++--- .../activity/ActivitySpanForm.svelte | 9 ++++--- .../activity/ActivitySpansTablePanel.svelte | 10 ++++--- .../ConstraintViolationButton.svelte | 12 ++++++--- .../constraints/ConstraintsPanel.svelte | 24 ++++++++--------- src/components/plan/PlanForm.svelte | 14 +++++----- .../simulation/SimulationEventsPanel.svelte | 14 +++++----- .../SimulationHistoryDataset.svelte | 15 ++++++----- .../simulation/SimulationPanel.svelte | 26 +++++++------------ src/components/timeline/Timeline.svelte | 16 +++++++----- .../timeline/TimelineCursors.svelte | 4 +-- .../timeline/TimelineHistogram.svelte | 9 +++++-- src/components/timeline/Tooltip.svelte | 15 +++++------ src/constants/time.ts | 1 + src/routes/plans/+page.svelte | 21 ++++++++++----- src/utilities/time.ts | 11 ++++++++ 18 files changed, 128 insertions(+), 102 deletions(-) create mode 100644 src/constants/time.ts diff --git a/src/components/activity/ActivityDirectiveChangelog.svelte b/src/components/activity/ActivityDirectiveChangelog.svelte index 1e168dbc02..c4011255af 100644 --- a/src/components/activity/ActivityDirectiveChangelog.svelte +++ b/src/components/activity/ActivityDirectiveChangelog.svelte @@ -16,7 +16,7 @@ import effects from '../../utilities/effects'; import { permissionHandler } from '../../utilities/permissionHandler'; import { featurePermissions } from '../../utilities/permissions'; - import { convertUsToDurationString, getUnixEpochTimeFromInterval } from '../../utilities/time'; + import { convertUsToDurationString, formatDate, getUnixEpochTimeFromInterval } from '../../utilities/time'; import { tooltip } from '../../utilities/tooltip'; const dispatch = createEventDispatcher<{ @@ -49,7 +49,7 @@ let effectiveRevisionArguments: (ArgumentsMap | undefined)[]; $: effectiveRevisionArguments = []; - function formatDate(dateString: string) { + function formatRevisionDate(dateString: string) { return new Date(dateString).toLocaleString(undefined, { day: 'numeric', hour: 'numeric', @@ -113,11 +113,13 @@ // Manually check remaining fields that could have changed and require extra formatting if (current.start_offset !== previous.start_offset) { - const currentStartTime = $plugins.time.primary.format( + const currentStartTime = formatDate( new Date(getUnixEpochTimeFromInterval(planStartTimeYmd, current.start_offset)), + $plugins.time.primary.format, ); - const previousStartTime = $plugins.time.primary.format( + const previousStartTime = formatDate( new Date(getUnixEpochTimeFromInterval(planStartTimeYmd, previous.start_offset)), + $plugins.time.primary.format, ); differences[`Start Time (${$plugins.time.primary.label})`] = { @@ -245,7 +247,7 @@ {#if activityRevisionChangeMap.length} {#each activityRevisions as revision, i}
-
{formatDate(revision.changed_at)}
+
{formatRevisionDate(revision.changed_at)}
= {}; let parametersWithErrorsCount: number = 0; - let startTime: string | null; + let startTime: string; let startTimeField: FieldStore; $: if (user !== null && $plan !== null) { @@ -102,12 +102,12 @@ planStartTimeYmd, revision ? revision.start_offset : activityDirective.start_offset, ); - startTime = $plugins.time.primary.format(new Date(startTimeMs)); + startTime = formatDate(new Date(startTimeMs), $plugins.time.primary.format); } - $: startTimeField = field(startTime ?? 'Invalid Date', [required, $plugins.time.primary.validate]); + $: startTimeField = field(startTime, [required, $plugins.time.primary.validate]); $: activityNameField = field(activityDirective.name); - $: startTimeField.validateAndSet(startTime ?? 'Invalid Date'); + $: startTimeField.validateAndSet(startTime); $: activityNameField.validateAndSet(activityDirective.name); $: if (activityType && activityDirective.arguments) { diff --git a/src/components/activity/ActivityDirectivesTablePanel.svelte b/src/components/activity/ActivityDirectivesTablePanel.svelte index d489da32a4..9dfc2087a3 100644 --- a/src/components/activity/ActivityDirectivesTablePanel.svelte +++ b/src/components/activity/ActivityDirectivesTablePanel.svelte @@ -11,6 +11,7 @@ ValueGetterParams, } from 'ag-grid-community'; import { debounce } from 'lodash-es'; + import { InvalidDate } from '../../constants/time'; import { activityDirectivesMap, selectActivity, selectedActivityDirectiveId } from '../../stores/activities'; import { activityErrorRollupsMap } from '../../stores/errors'; import { plan, planReadOnly } from '../../stores/plan'; @@ -20,7 +21,7 @@ import type { User } from '../../types/app'; import type { AutoSizeColumns, ViewGridSection, ViewTable } from '../../types/view'; import { filterEmpty } from '../../utilities/generic'; - import { getUnixEpochTimeFromInterval } from '../../utilities/time'; + import { formatDate, getUnixEpochTimeFromInterval } from '../../utilities/time'; import { tooltip } from '../../utilities/tooltip'; import GridMenu from '../menus/GridMenu.svelte'; import DataGrid from '../ui/DataGrid/DataGrid.svelte'; @@ -180,12 +181,12 @@ sortable: true, valueGetter: (params: ValueGetterParams) => { if ($plan && params && params.data && typeof params.data.start_time_ms === 'number') { - return $plugins.time.primary.format(new Date(params.data.start_time_ms)) ?? 'Invalid Date'; + return formatDate(new Date(params.data.start_time_ms), $plugins.time.primary.format); } return ''; }, cellRenderer: (params: ICellRendererParams) => { - if (params.value !== 'Invalid Date') { + if (params.value !== InvalidDate) { return params.value; } const div = document.createElement('div'); diff --git a/src/components/activity/ActivitySpanForm.svelte b/src/components/activity/ActivitySpanForm.svelte index 49211d7dda..23cc37d016 100644 --- a/src/components/activity/ActivitySpanForm.svelte +++ b/src/components/activity/ActivitySpanForm.svelte @@ -11,6 +11,7 @@ import { getSpanRootParent } from '../../utilities/activities'; import effects from '../../utilities/effects'; import { getFormParameters } from '../../utilities/parameters'; + import { formatDate } from '../../utilities/time'; import { tooltip } from '../../utilities/tooltip'; import Collapse from '../Collapse.svelte'; import Input from '../form/Input.svelte'; @@ -36,16 +37,16 @@ let rootSpan: Span | null; let rootSpanHasChildren: boolean; let seqId: string | null; - let startTime: string | null; + let startTime: string; $: activityType = (activityTypes ?? []).find(({ name: activityTypeName }) => span.type === activityTypeName) ?? null; $: rootSpan = getSpanRootParent(spansMap, span.id); $: rootSpanHasChildren = (rootSpan && spanUtilityMaps.spanIdToChildIdsMap[rootSpan.id]?.length > 0) ?? false; - $: startTime = $plugins.time.primary.format(new Date(span.startMs)); + $: startTime = formatDate(new Date(span.startMs), $plugins.time.primary.format); $: if (span.duration) { - endTime = $plugins.time.primary.format(new Date(span.endMs)); + endTime = formatDate(new Date(span.endMs), $plugins.time.primary.format); } else { endTime = null; } @@ -170,7 +171,7 @@ - + diff --git a/src/components/activity/ActivitySpansTablePanel.svelte b/src/components/activity/ActivitySpansTablePanel.svelte index d81ed81318..86bef722d4 100644 --- a/src/components/activity/ActivitySpansTablePanel.svelte +++ b/src/components/activity/ActivitySpansTablePanel.svelte @@ -5,6 +5,7 @@ import TableFitIcon from '@nasa-jpl/stellar/icons/table_fit.svg?component'; import type { ColDef, ColumnResizedEvent, ColumnState, ICellRendererParams } from 'ag-grid-community'; import { debounce } from 'lodash-es'; + import { InvalidDate } from '../../constants/time'; import { selectActivity } from '../../stores/activities'; import { plugins } from '../../stores/plugins'; import { selectedSpanId, spans } from '../../stores/simulation'; @@ -12,6 +13,7 @@ import type { Span } from '../../types/simulation'; import type { AutoSizeColumns, ViewGridSection, ViewTable } from '../../types/view'; import { filterEmpty } from '../../utilities/generic'; + import { formatDate } from '../../utilities/time'; import { tooltip } from '../../utilities/tooltip'; import GridMenu from '../menus/GridMenu.svelte'; import type DataGrid from '../ui/DataGrid/DataGrid.svelte'; @@ -91,12 +93,12 @@ valueGetter: params => { if (params && params.data && typeof params.data.startMs === 'number') { /* TODO could use short format here to skip ms but do we need short(er) format somewhere else? */ - return $plugins.time.primary.format(new Date(params.data.startMs)) ?? 'Invalid Date'; + return formatDate(new Date(params.data.startMs), $plugins.time.primary.format); } return ''; }, cellRenderer: (params: ICellRendererParams) => { - if (params.value !== 'Invalid Date') { + if (params.value !== InvalidDate) { return params.value; } const div = document.createElement('div'); @@ -119,12 +121,12 @@ sortable: true, valueGetter: params => { if (params && params.data && typeof params.data.endMs === 'number') { - return $plugins.time.primary.format(new Date(params.data.endMs)) ?? 'Invalid Date'; + return formatDate(new Date(params.data.endMs), $plugins.time.primary.format); } return ''; }, cellRenderer: (params: ICellRendererParams) => { - if (params.value !== 'Invalid Date') { + if (params.value !== InvalidDate) { return params.value; } const div = document.createElement('div'); diff --git a/src/components/constraints/ConstraintViolationButton.svelte b/src/components/constraints/ConstraintViolationButton.svelte index 7db7282ee6..940a46118e 100644 --- a/src/components/constraints/ConstraintViolationButton.svelte +++ b/src/components/constraints/ConstraintViolationButton.svelte @@ -5,13 +5,17 @@ import { viewTimeRange } from '../../stores/plan'; import { plugins } from '../../stores/plugins'; import type { TimeRange } from '../../types/timeline'; - import { getDoyTimeComponents, validateTime } from '../../utilities/time'; + import { formatDate, getDoyTimeComponents, validateTime } from '../../utilities/time'; export let window: TimeRange; let isDoyPattern = false; + let startDateString: string = ''; + let endDateString: string = ''; - $: isDoyPattern = validateTime($plugins.time.primary.format(new Date(window.start)) ?? '', TimeTypes.ABSOLUTE); + $: startDateString = formatDate(new Date(window.start), $plugins.time.primary.format); + $: endDateString = formatDate(new Date(window.end), $plugins.time.primary.format); + $: isDoyPattern = validateTime(startDateString, TimeTypes.ABSOLUTE); function zoomToViolation(window: TimeRange): void { $viewTimeRange = window; @@ -32,7 +36,7 @@ {startYear}-{startDoy} T {startHours}:{startMins}:{startSecs}.{startMsecs} {$plugins.time.primary.label} {:else} - {$plugins.time.primary.format(new Date(window.start)) ?? 'Invalid Date'} + {startDateString} {/if}
@@ -49,7 +53,7 @@ {endYear}-{endDoy} T {endHours}:{endMins}:{endSecs}.{endMsecs} {$plugins.time.primary.label} {:else} - {$plugins.time.primary.format(new Date(window.start)) ?? 'Invalid Date'} + {endDateString} {/if}
diff --git a/src/components/constraints/ConstraintsPanel.svelte b/src/components/constraints/ConstraintsPanel.svelte index 3d875ccf8e..d89988d375 100644 --- a/src/components/constraints/ConstraintsPanel.svelte +++ b/src/components/constraints/ConstraintsPanel.svelte @@ -37,7 +37,7 @@ import effects from '../../utilities/effects'; import { permissionHandler } from '../../utilities/permissionHandler'; import { featurePermissions } from '../../utilities/permissions'; - import { convertDoyToYmd } from '../../utilities/time'; + import { convertDoyToYmd, formatDate } from '../../utilities/time'; import { tooltip } from '../../utilities/tooltip'; import { required } from '../../utilities/validators'; import CollapsibleListControls from '../CollapsibleListControls.svelte'; @@ -55,27 +55,27 @@ let showAll: boolean = true; let filterText: string = ''; let filteredConstraints: ConstraintPlanSpec[] = []; - let endTime: string | null; + let endTime: string; let endTimeField: FieldStore; let numOfPrivateConstraints: number = 0; - let startTime: string | null; + let startTime: string; let startTimeField: FieldStore; let showFilters: boolean = false; let showConstraintsWithNoViolations: boolean = true; let constraintToConstraintResponseMap: Record = {}; $: if ($plan) { - startTime = $plugins.time.primary.format(new Date($plan.start_time)); + startTime = formatDate(new Date($plan.start_time), $plugins.time.primary.format); const endTimeYmd = convertDoyToYmd($plan.end_time_doy); if (endTimeYmd) { - endTime = $plugins.time.primary.format(new Date(endTimeYmd)); + endTime = formatDate(new Date(endTimeYmd), $plugins.time.primary.format); } else { endTime = ''; } } - $: startTimeField = field(startTime ?? 'Invalid Date', [required, $plugins.time.primary.validate]); - $: endTimeField = field(endTime ?? 'Invalid Date', [required, $plugins.time.primary.validate]); + $: startTimeField = field(startTime, [required, $plugins.time.primary.validate]); + $: endTimeField = field(endTime, [required, $plugins.time.primary.validate]); $: startTimeMs = typeof startTime === 'string' ? $plugins.time.primary.parse(startTime)?.getTime() : null; $: endTimeMs = typeof endTime === 'string' ? $plugins.time.primary.parse(endTime)?.getTime() : null; $: if ($allowedConstraintSpecs && $constraintResponseMap && startTimeMs && endTimeMs) { @@ -168,15 +168,15 @@ } async function setTimeBoundsToView() { - await startTimeField.validateAndSet($plugins.time.primary.format(new Date($viewTimeRange.start)) ?? 'Invalid Date'); - await endTimeField.validateAndSet($plugins.time.primary.format(new Date($viewTimeRange.end)) ?? 'Invalid Date'); + await startTimeField.validateAndSet(formatDate(new Date($viewTimeRange.start), $plugins.time.primary.format)); + await endTimeField.validateAndSet(formatDate(new Date($viewTimeRange.end), $plugins.time.primary.format)); onUpdateStartTime(); onUpdateEndTime(); } async function onPlanStartTimeClick() { if ($plan) { - await startTimeField.validateAndSet($plugins.time.primary.format(new Date($plan.start_time)) ?? 'Invalid Date'); + await startTimeField.validateAndSet(formatDate(new Date($plan.start_time), $plugins.time.primary.format)); onUpdateStartTime(); } } @@ -185,8 +185,8 @@ if ($plan) { const endTimeYmd = convertDoyToYmd($plan.end_time_doy); if (endTimeYmd) { - endTime = $plugins.time.primary.format(new Date(endTimeYmd)); - await endTimeField.validateAndSet(endTime ?? 'Invalid Date'); + endTime = formatDate(new Date(endTimeYmd), $plugins.time.primary.format); + await endTimeField.validateAndSet(endTime); onUpdateEndTime(); } } diff --git a/src/components/plan/PlanForm.svelte b/src/components/plan/PlanForm.svelte index 795c7ff5b4..af576be102 100644 --- a/src/components/plan/PlanForm.svelte +++ b/src/components/plan/PlanForm.svelte @@ -22,7 +22,7 @@ import { permissionHandler } from '../../utilities/permissionHandler'; import { featurePermissions } from '../../utilities/permissions'; import { getPlanForTransfer } from '../../utilities/plan'; - import { convertDoyToYmd, getShortISOForDate } from '../../utilities/time'; + import { convertDoyToYmd, formatDate, getShortISOForDate } from '../../utilities/time'; import { tooltip } from '../../utilities/tooltip'; import { required, unique } from '../../utilities/validators'; import Collapse from '../Collapse.svelte'; @@ -57,16 +57,16 @@ ), ]); let planExportProgress: number | null = null; - let planStartTime: string | null = ''; - let planEndTime: string | null = ''; + let planStartTime: string = ''; + let planEndTime: string = ''; $: permissionError = $planReadOnly ? PlanStatusMessages.READ_ONLY : 'You do not have permission to edit this plan.'; $: if (plan) { hasCreateSnapshotPermission = featurePermissions.planSnapshot.canCreate(user, plan, plan.model) && !$planReadOnly; - planStartTime = $plugins.time.primary.format(new Date(plan.start_time)); + planStartTime = formatDate(new Date(plan.start_time), $plugins.time.primary.format); const endTime = convertDoyToYmd(plan.end_time_doy); if (endTime) { - planEndTime = $plugins.time.primary.format(new Date(endTime)); + planEndTime = formatDate(new Date(endTime), $plugins.time.primary.format); } else { planEndTime = ''; } @@ -278,13 +278,13 @@ > Start Time ({$plugins.time.primary.label}) - + - + diff --git a/src/components/simulation/SimulationEventsPanel.svelte b/src/components/simulation/SimulationEventsPanel.svelte index ca6e2f807f..22402cce7a 100644 --- a/src/components/simulation/SimulationEventsPanel.svelte +++ b/src/components/simulation/SimulationEventsPanel.svelte @@ -5,13 +5,14 @@ import TableFitIcon from '@nasa-jpl/stellar/icons/table_fit.svg?component'; import type { ColDef, ColumnResizedEvent, ColumnState, ICellRendererParams } from 'ag-grid-community'; import { debounce } from 'lodash-es'; + import { InvalidDate } from '../../constants/time'; import { plugins } from '../../stores/plugins'; import { simulationDataset, simulationEvents } from '../../stores/simulation'; import { view, viewUpdateSimulationEventsTable } from '../../stores/views'; import type { SimulationEvent } from '../../types/simulation'; import type { AutoSizeColumns, ViewGridSection, ViewTable } from '../../types/view'; import { filterEmpty } from '../../utilities/generic'; - import { getUnixEpochTimeFromInterval } from '../../utilities/time'; + import { formatDate, getUnixEpochTimeFromInterval } from '../../utilities/time'; import { tooltip } from '../../utilities/tooltip'; import ActivityTableMenu from '../activity/ActivityTableMenu.svelte'; import GridMenu from '../menus/GridMenu.svelte'; @@ -47,18 +48,15 @@ sortable: true, valueGetter: params => { if ($simulationDataset && $simulationDataset.simulation_start_time && params.data) { - return ( - $plugins.time.primary.format( - new Date( - getUnixEpochTimeFromInterval($simulationDataset?.simulation_start_time, params.data.start_offset), - ), - ) ?? 'Invalid Date' + return formatDate( + new Date(getUnixEpochTimeFromInterval($simulationDataset?.simulation_start_time, params.data.start_offset)), + $plugins.time.primary.format, ); } return ''; }, cellRenderer: (params: ICellRendererParams) => { - if (params.value !== 'Invalid Date') { + if (params.value !== InvalidDate) { return params.value; } const div = document.createElement('div'); diff --git a/src/components/simulation/SimulationHistoryDataset.svelte b/src/components/simulation/SimulationHistoryDataset.svelte index b4165e47cc..6b05d13a75 100644 --- a/src/components/simulation/SimulationHistoryDataset.svelte +++ b/src/components/simulation/SimulationHistoryDataset.svelte @@ -3,6 +3,7 @@