Skip to content

Commit 8ad03c8

Browse files
committed
ui: fix: timezone handling
1 parent 0f35a5c commit 8ad03c8

File tree

7 files changed

+50
-38
lines changed

7 files changed

+50
-38
lines changed

ui/src/App.tsx

+20-22
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,17 @@ import { SWRConfig } from 'swr';
99
import fetchJson from './lib/fetchJson';
1010
import Search from './pages/search';
1111
import { UserPreferencesProvider } from './contexts/UserPreference';
12-
13-
export type Config = {
14-
apiURL: string;
15-
title: string;
16-
navbarColor: string;
17-
tz: string;
18-
version: string;
19-
};
12+
import { Config, ConfigContext } from './contexts/ConfigContext';
13+
import moment from 'moment-timezone';
2014

2115
type Props = {
2216
config: Config;
2317
};
2418

2519
function App({ config }: Props) {
2620
const [title, setTitle] = React.useState<string>('');
21+
config.tz ||= moment.tz.guess();
22+
2723
return (
2824
<SWRConfig
2925
value={{
@@ -39,20 +35,22 @@ function App({ config }: Props) {
3935
setTitle,
4036
}}
4137
>
42-
<UserPreferencesProvider>
43-
<BrowserRouter>
44-
<Layout {...config}>
45-
<Routes>
46-
<Route path="/" element={<Dashboard />} />
47-
<Route path="/dashboard" element={<Dashboard />} />
48-
<Route path="/dags/" element={<DAGs />} />
49-
<Route path="/dags/:name/:tab" element={<DAGDetails />} />
50-
<Route path="/dags/:name/" element={<DAGDetails />} />
51-
<Route path="/search/" element={<Search />} />
52-
</Routes>
53-
</Layout>
54-
</BrowserRouter>
55-
</UserPreferencesProvider>
38+
<ConfigContext.Provider value={config}>
39+
<UserPreferencesProvider>
40+
<BrowserRouter>
41+
<Layout {...config}>
42+
<Routes>
43+
<Route path="/" element={<Dashboard />} />
44+
<Route path="/dashboard" element={<Dashboard />} />
45+
<Route path="/dags/" element={<DAGs />} />
46+
<Route path="/dags/:name/:tab" element={<DAGDetails />} />
47+
<Route path="/dags/:name/" element={<DAGDetails />} />
48+
<Route path="/search/" element={<Search />} />
49+
</Routes>
50+
</Layout>
51+
</BrowserRouter>
52+
</UserPreferencesProvider>
53+
</ConfigContext.Provider>
5654
</AppBarContext.Provider>
5755
</SWRConfig>
5856
);

ui/src/components/molecules/DAGTable.tsx

+2-4
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ import {
4444
KeyboardArrowUp,
4545
} from '@mui/icons-material';
4646
import LiveSwitch from './LiveSwitch';
47-
import moment from 'moment';
4847
import 'moment-duration-format';
4948
import Ticker from '../atoms/Ticker';
5049
import VisuallyHidden from '../atoms/VisuallyHidden';
50+
import moment from 'moment-timezone';
5151

5252
type Props = {
5353
DAGs: DAGItem[];
@@ -251,9 +251,7 @@ const defaultColumns = [
251251
}),
252252
columnHelper.accessor('Type', {
253253
id: 'Schedule',
254-
header: getConfig().tz
255-
? `Schedule in ${getConfig().tz}`
256-
: 'Schedule',
254+
header: `Schedule in ${getConfig().tz || moment.tz.guess()}`,
257255
enableSorting: true,
258256
cell: (props) => {
259257
const data = props.row.original!;

ui/src/components/molecules/DashboardTimechart.tsx

+6-9
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'vis-timeline/styles/vis-timeline-graph2d.css';
66
import { statusColorMapping } from '../../consts';
77
import { DAGStatus } from '../../models';
88
import { WorkflowListItem } from '../../models/api';
9+
import { useConfig } from '../../contexts/ConfigContext';
910

1011
type Props = { data: DAGStatus[] | WorkflowListItem[] };
1112

@@ -21,15 +22,11 @@ type TimelineItem = {
2122
function DashboardTimechart({ data: input }: Props) {
2223
const timelineRef = useRef<HTMLDivElement>(null);
2324
const timelineInstance = useRef<Timeline | null>(null);
25+
const config = useConfig();
2426

2527
useEffect(() => {
2628
if (!timelineRef.current) return;
2729

28-
let timezone = getConfig().tz;
29-
if (!timezone) {
30-
timezone = moment.tz.guess();
31-
}
32-
3330
const items: TimelineItem[] = [];
3431
const now = moment();
3532
const startOfDay = moment().startOf('day');
@@ -47,8 +44,8 @@ function DashboardTimechart({ data: input }: Props) {
4744
items.push({
4845
id: status.Name + `_${status.RequestId}`,
4946
content: status.Name,
50-
start: startMoment.tz(timezone).toDate(),
51-
end: end.tz(timezone).toDate(),
47+
start: startMoment.tz(config.tz).toDate(),
48+
end: end.tz(config.tz).toDate(),
5249
group: 'main',
5350
className: `status-${status.Status}`,
5451
});
@@ -59,7 +56,7 @@ function DashboardTimechart({ data: input }: Props) {
5956

6057
if (!timelineInstance.current) {
6158
timelineInstance.current = new Timeline(timelineRef.current, dataset, {
62-
moment: (date: MomentInput) => moment(date).tz(timezone),
59+
moment: (date: MomentInput) => moment(date).tz(config.tz),
6360
start: startOfDay.toDate(),
6461
end: now.endOf('day').toDate(),
6562
orientation: 'top',
@@ -76,7 +73,7 @@ function DashboardTimechart({ data: input }: Props) {
7673
hour: 'HH:mm',
7774
},
7875
majorLabels: {
79-
hour: 'HH:mm',
76+
hour: 'ddd D MMMM',
8077
day: 'ddd D MMMM',
8178
},
8279
},

ui/src/contexts/ConfigContext.tsx

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { createContext, useContext } from 'react';
2+
3+
export type Config = {
4+
apiURL: string;
5+
title: string;
6+
navbarColor: string;
7+
tz: string;
8+
version: string;
9+
};
10+
11+
export const ConfigContext = createContext<Config>(null!);
12+
13+
export function useConfig() {
14+
return useContext(ConfigContext);
15+
}

ui/src/index.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import React from 'react';
22
import { createRoot } from 'react-dom/client';
33

4-
import App, { Config } from './App';
4+
import App from './App';
55
import './styles/styles.css';
66
import './styles/prism.css';
77

@@ -14,6 +14,7 @@ import '@fontsource/roboto/300.css';
1414
import '@fontsource/roboto/400.css';
1515
import '@fontsource/roboto/500.css';
1616
import '@fontsource/roboto/700.css';
17+
import { Config } from './contexts/ConfigContext';
1718

1819
declare global {
1920
const getConfig: () => Config;

ui/src/models/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import cronParser from 'cron-parser';
2+
import moment from 'moment-timezone';
23
import { WorkflowListItem } from './api';
34

45
export enum SchedulerStatus {
@@ -150,7 +151,7 @@ export function getNextSchedule(data: WorkflowListItem): number {
150151
if (!schedules || schedules.length == 0 || data.Suspended) {
151152
return Number.MAX_SAFE_INTEGER;
152153
}
153-
const tz = getConfig().tz;
154+
const tz = getConfig().tz || moment.tz.guess();
154155
const datesToRun = schedules.map((s) => {
155156
const expression = tz
156157
? cronParser.parseExpression(s.Expression, {

ui/src/pages/index.tsx

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import DashboardTimechart from '../components/molecules/DashboardTimechart';
88
import Title from '../components/atoms/Title';
99
import { AppBarContext } from '../contexts/AppBarContext';
1010
import useSWR from 'swr';
11+
import { useConfig } from '../contexts/ConfigContext';
1112

1213
type metrics = Record<SchedulerStatus, number>;
1314

@@ -25,6 +26,7 @@ function Dashboard() {
2526
const { data } = useSWR<ListWorkflowsResponse>(`/dags`, null, {
2627
refreshInterval: 10000,
2728
});
29+
const config = useConfig();
2830

2931
React.useEffect(() => {
3032
if (!data) {
@@ -79,7 +81,7 @@ function Dashboard() {
7981
height: '100%',
8082
}}
8183
>
82-
<Title>{getConfig().tz ? `Timeline in ${getConfig().tz}` : "Timeline"}</Title>
84+
<Title>{`Timeline in ${config.tz}`}</Title>
8385
<DashboardTimechart data={data?.DAGs || []} />
8486
</Box>
8587
</Grid>

0 commit comments

Comments
 (0)