Changeset View
Changeset View
Standalone View
Standalone View
js_modules/dagit/packages/core/src/nav/JobMetadata.tsx
import {gql, useQuery} from '@apollo/client'; | import {gql, useQuery} from '@apollo/client'; | ||||
import {Button, Classes, Colors, Dialog, Icon} from '@blueprintjs/core'; | import {Button, Classes, Colors, Dialog, Icon} from '@blueprintjs/core'; | ||||
import {Tooltip2 as Tooltip} from '@blueprintjs/popover2'; | |||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import {Link} from 'react-router-dom'; | import {Link} from 'react-router-dom'; | ||||
import styled from 'styled-components/macro'; | import styled from 'styled-components/macro'; | ||||
import {timingStringForStatus} from '../runs/RunDetails'; | |||||
import {RunStatus} from '../runs/RunStatusDots'; | import {RunStatus} from '../runs/RunStatusDots'; | ||||
import {TimeElapsed} from '../runs/TimeElapsed'; | import {TimeElapsed} from '../runs/TimeElapsed'; | ||||
import {ScheduleSwitch} from '../schedules/ScheduleSwitch'; | import {ScheduleSwitch} from '../schedules/ScheduleSwitch'; | ||||
import {SCHEDULE_FRAGMENT} from '../schedules/ScheduleUtils'; | import {SCHEDULE_FRAGMENT} from '../schedules/ScheduleUtils'; | ||||
import {TimestampDisplay} from '../schedules/TimestampDisplay'; | |||||
import {SENSOR_FRAGMENT} from '../sensors/SensorFragment'; | import {SENSOR_FRAGMENT} from '../sensors/SensorFragment'; | ||||
import {SensorSwitch} from '../sensors/SensorSwitch'; | import {SensorSwitch} from '../sensors/SensorSwitch'; | ||||
import {Box} from '../ui/Box'; | |||||
import {ButtonLink} from '../ui/ButtonLink'; | import {ButtonLink} from '../ui/ButtonLink'; | ||||
import {Group} from '../ui/Group'; | import {Group} from '../ui/Group'; | ||||
import {MetadataTable} from '../ui/MetadataTable'; | import {MetadataTable, StyledTable} from '../ui/MetadataTable'; | ||||
import {FontFamily} from '../ui/styles'; | import {FontFamily} from '../ui/styles'; | ||||
import {RepoAddress} from '../workspace/types'; | import {RepoAddress} from '../workspace/types'; | ||||
import {workspacePathFromAddress} from '../workspace/workspacePath'; | import {workspacePathFromAddress} from '../workspace/workspacePath'; | ||||
import { | import { | ||||
JobMetadataQuery, | JobMetadataQuery, | ||||
JobMetadataQuery_pipelineOrError_Pipeline as Job, | JobMetadataQuery_pipelineOrError_Pipeline as Job, | ||||
JobMetadataQuery_pipelineRunsOrError_PipelineRuns_results as Run, | JobMetadataQuery_pipelineRunsOrError_PipelineRuns_results as Run, | ||||
▲ Show 20 Lines • Show All 185 Lines • ▼ Show 20 Lines | <Icon | ||||
iconSize={13} | iconSize={13} | ||||
style={{position: 'relative', top: '-2px'}} | style={{position: 'relative', top: '-2px'}} | ||||
/> | /> | ||||
<Link to={workspacePathFromAddress(repoAddress, `/sensors/${sensor.name}`)}>{sensor.name}</Link> | <Link to={workspacePathFromAddress(repoAddress, `/sensors/${sensor.name}`)}>{sensor.name}</Link> | ||||
<SensorSwitch large={false} repoAddress={repoAddress} sensor={sensor} /> | <SensorSwitch large={false} repoAddress={repoAddress} sensor={sensor} /> | ||||
</Group> | </Group> | ||||
); | ); | ||||
const TIME_FORMAT = {showSeconds: true, showTimezone: false}; | |||||
const LatestRun: React.FC<{run: Run}> = ({run}) => { | const LatestRun: React.FC<{run: Run}> = ({run}) => { | ||||
const stats = React.useMemo(() => { | const stats = React.useMemo(() => { | ||||
if (run.stats.__typename === 'PipelineRunStatsSnapshot') { | if (run.stats.__typename === 'PipelineRunStatsSnapshot') { | ||||
return {start: run.stats.startTime, end: run.stats.endTime}; | return {start: run.stats.startTime, end: run.stats.endTime, status: run.status}; | ||||
} | } | ||||
return null; | return null; | ||||
}, [run]); | }, [run]); | ||||
return ( | return ( | ||||
<Group direction="row" spacing={8} alignItems="center"> | <Group direction="row" spacing={8} alignItems="center"> | ||||
<RunStatus status={run.status} /> | <RunStatus status={run.status} /> | ||||
<div style={{fontFamily: FontFamily.monospace}}> | <div style={{fontFamily: FontFamily.monospace}}> | ||||
<Link to={`/instance/runs/${run.id}`}>{run.id.slice(0, 8)}</Link> | <Link to={`/instance/runs/${run.id}`}>{run.id.slice(0, 8)}</Link> | ||||
</div> | </div> | ||||
{stats ? <TimeElapsed startUnix={stats.start} endUnix={stats.end} /> : null} | {stats ? ( | ||||
<Tooltip | |||||
placement="bottom" | |||||
content={ | |||||
<StyledTable> | |||||
<tbody> | |||||
<tr> | |||||
<td style={{color: Colors.GRAY4}}> | |||||
<Box padding={{right: 16}}>Started</Box> | |||||
</td> | |||||
<td> | |||||
{stats.start ? ( | |||||
<TimestampDisplay timestamp={stats.start} timeFormat={TIME_FORMAT} /> | |||||
) : ( | |||||
timingStringForStatus(stats.status) | |||||
)} | |||||
</td> | |||||
</tr> | |||||
<tr> | |||||
<td style={{color: Colors.GRAY4}}>Ended</td> | |||||
<td> | |||||
{stats.end ? ( | |||||
<TimestampDisplay timestamp={stats.end} timeFormat={TIME_FORMAT} /> | |||||
) : ( | |||||
timingStringForStatus(stats.status) | |||||
)} | |||||
</td> | |||||
</tr> | |||||
</tbody> | |||||
</StyledTable> | |||||
} | |||||
> | |||||
<TimeElapsed startUnix={stats.start} endUnix={stats.end} /> | |||||
</Tooltip> | |||||
) : null} | |||||
</Group> | </Group> | ||||
); | ); | ||||
}; | }; | ||||
const RelatedAssets: React.FC<{runs: Run[]}> = ({runs}) => { | const RelatedAssets: React.FC<{runs: Run[]}> = ({runs}) => { | ||||
const [open, setOpen] = React.useState(false); | const [open, setOpen] = React.useState(false); | ||||
const assetMap = {}; | const assetMap = {}; | ||||
▲ Show 20 Lines • Show All 90 Lines • Show Last 20 Lines |