feat: unify track replay summary
This commit is contained in:
@@ -179,7 +179,14 @@ test('renders a map-first replay workspace and connects stop, event, and panel i
|
|||||||
expect(view.container.querySelector('.v2-track-query-panel')).toHaveClass('is-collapsed');
|
expect(view.container.querySelector('.v2-track-query-panel')).toHaveClass('is-collapsed');
|
||||||
expect(view.container.querySelector('.v2-track-query-summary')).not.toBeInTheDocument();
|
expect(view.container.querySelector('.v2-track-query-summary')).not.toBeInTheDocument();
|
||||||
expect(view.container.querySelector('.v2-track-rail-result')).toBeInTheDocument();
|
expect(view.container.querySelector('.v2-track-rail-result')).toBeInTheDocument();
|
||||||
expect(view.container.querySelector('.v2-track-result-header')).toHaveTextContent('轨迹证据粤A12345 · LTEST0000000000018 km');
|
const tripSummary = screen.getByRole('list', { name: '轨迹行程摘要' });
|
||||||
|
expect(tripSummary.closest('.v2-track-result-metric-rail')).toHaveClass('semi-card', 'v2-workspace-metric-rail', 'is-queue');
|
||||||
|
expect(Array.from(tripSummary.querySelectorAll(':scope > [role="listitem"] > small')).map((item) => item.textContent)).toEqual(['行程里程', '停留点', '事件点', '数据点']);
|
||||||
|
expect(tripSummary).toHaveTextContent('行程里程8 km00:20:00');
|
||||||
|
expect(tripSummary).toHaveTextContent('停留点1超过 3 分钟');
|
||||||
|
expect(tripSummary).toHaveTextContent('事件点3轨迹事件');
|
||||||
|
expect(tripSummary).toHaveTextContent('数据点3时间窗完整');
|
||||||
|
expect(view.container.querySelector('.v2-track-result-header')).not.toBeInTheDocument();
|
||||||
expect(screen.getByRole('button', { name: '修改条件' })).toHaveAttribute('aria-expanded', 'false');
|
expect(screen.getByRole('button', { name: '修改条件' })).toHaveAttribute('aria-expanded', 'false');
|
||||||
expect(screen.queryByRole('textbox', { name: '搜索轨迹车辆' })).not.toBeInTheDocument();
|
expect(screen.queryByRole('textbox', { name: '搜索轨迹车辆' })).not.toBeInTheDocument();
|
||||||
expect(view.container.querySelector('.v2-track-current-card.semi-card')).toBeInTheDocument();
|
expect(view.container.querySelector('.v2-track-current-card.semi-card')).toBeInTheDocument();
|
||||||
@@ -196,7 +203,7 @@ test('renders a map-first replay workspace and connects stop, event, and panel i
|
|||||||
expect(screen.getByRole('button', { name: /停留 00:05:00/ }).closest('.semi-list-item')).toHaveClass('v2-track-evidence-item');
|
expect(screen.getByRole('button', { name: /停留 00:05:00/ }).closest('.semi-list-item')).toHaveClass('v2-track-evidence-item');
|
||||||
expect(view.container.querySelector('.v2-track-evidence-list.semi-list')).toBeInTheDocument();
|
expect(view.container.querySelector('.v2-track-evidence-list.semi-list')).toBeInTheDocument();
|
||||||
expect(screen.getByRole('slider', { name: '轨迹播放进度' })).toBeInTheDocument();
|
expect(screen.getByRole('slider', { name: '轨迹播放进度' })).toBeInTheDocument();
|
||||||
expect(view.container.querySelector('.v2-track-dock-summary')).toHaveTextContent('行程里程8 km00:20:00 · 1 次停留');
|
expect(view.container.querySelector('.v2-track-dock-summary')).not.toBeInTheDocument();
|
||||||
expect(view.container.querySelector('.v2-track-progress-meta')).toHaveTextContent('已暂停 · 数据点 1 / 3');
|
expect(view.container.querySelector('.v2-track-progress-meta')).toHaveTextContent('已暂停 · 数据点 1 / 3');
|
||||||
expect(screen.queryByRole('button', { name: '暂停轨迹播放' })).not.toBeInTheDocument();
|
expect(screen.queryByRole('button', { name: '暂停轨迹播放' })).not.toBeInTheDocument();
|
||||||
await waitFor(() => expect(mocks.reverseGeocode).toHaveBeenCalledTimes(1));
|
await waitFor(() => expect(mocks.reverseGeocode).toHaveBeenCalledTimes(1));
|
||||||
@@ -277,7 +284,12 @@ test('uses the shared Semi editor SideSheet for mobile track criteria and eviden
|
|||||||
expect(screen.queryByLabelText('轨迹查询任务栏')).not.toBeInTheDocument();
|
expect(screen.queryByLabelText('轨迹查询任务栏')).not.toBeInTheDocument();
|
||||||
expect(screen.queryByRole('textbox', { name: '搜索轨迹车辆' })).not.toBeInTheDocument();
|
expect(screen.queryByRole('textbox', { name: '搜索轨迹车辆' })).not.toBeInTheDocument();
|
||||||
expect(screen.getAllByText('07-15 00:00 – 07-15 23:59 · 自动来源')).toHaveLength(2);
|
expect(screen.getAllByText('07-15 00:00 – 07-15 23:59 · 自动来源')).toHaveLength(2);
|
||||||
expect(screen.getByRole('list', { name: '轨迹查询与明细摘要' })).toHaveTextContent('行程里程8 km00:20:00停留 / 事件1 / 3地图证据点数据点3时间窗完整');
|
const mobileSummary = screen.getByRole('list', { name: '轨迹查询与明细摘要' });
|
||||||
|
expect(mobileSummary).toHaveTextContent('行程里程8 km00:20:00');
|
||||||
|
expect(mobileSummary).toHaveTextContent('停留点1超过 3 分钟');
|
||||||
|
expect(mobileSummary).toHaveTextContent('事件点3轨迹事件');
|
||||||
|
expect(mobileSummary).toHaveTextContent('数据点3时间窗完整');
|
||||||
|
expect(mobileSummary.querySelectorAll(':scope > [role="listitem"]')).toHaveLength(4);
|
||||||
expect(screen.getByRole('button', { name: '修改轨迹查询' })).toHaveTextContent('修改查询');
|
expect(screen.getByRole('button', { name: '修改轨迹查询' })).toHaveTextContent('修改查询');
|
||||||
expect(screen.getByRole('button', { name: /查看地图$/ })).toBeInTheDocument();
|
expect(screen.getByRole('button', { name: /查看地图$/ })).toBeInTheDocument();
|
||||||
expect(sheet).toHaveStyle({ height: 'min(88dvh, 394px)' });
|
expect(sheet).toHaveStyle({ height: 'min(88dvh, 394px)' });
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { SegmentedTabs } from '../shared/SegmentedTabs';
|
|||||||
import { VehicleCandidateList } from '../shared/VehicleCandidateList';
|
import { VehicleCandidateList } from '../shared/VehicleCandidateList';
|
||||||
import { mergeVehicleCandidates } from '../shared/vehicleCandidates';
|
import { mergeVehicleCandidates } from '../shared/vehicleCandidates';
|
||||||
import { WorkspaceCommandBar } from '../shared/WorkspaceCommandBar';
|
import { WorkspaceCommandBar } from '../shared/WorkspaceCommandBar';
|
||||||
|
import { WorkspaceMetricRail, type WorkspaceQueueMetricRailItem } from '../shared/WorkspaceMetricRail';
|
||||||
import { WorkspacePanelHeader } from '../shared/WorkspacePanelHeader';
|
import { WorkspacePanelHeader } from '../shared/WorkspacePanelHeader';
|
||||||
import { WorkspaceSideSheet } from '../shared/WorkspaceSideSheet';
|
import { WorkspaceSideSheet } from '../shared/WorkspaceSideSheet';
|
||||||
import { QUERY_MEMORY } from '../queryPolicy';
|
import { QUERY_MEMORY } from '../queryPolicy';
|
||||||
@@ -127,6 +128,39 @@ function mobileTrackSheetHeight(track: TrackPlaybackResponse | undefined, queryC
|
|||||||
return `min(88dvh, ${contentHeight}px)`;
|
return `min(88dvh, ${contentHeight}px)`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function trackSummaryItems(track?: TrackPlaybackResponse): WorkspaceQueueMetricRailItem[] {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
label: '行程里程',
|
||||||
|
value: track ? `${number(track.summary.distanceKm)} km` : '—',
|
||||||
|
note: track ? formatDuration(track.summary.durationSeconds) : '等待查询',
|
||||||
|
tone: 'primary',
|
||||||
|
emphasis: 'primary'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '停留点',
|
||||||
|
value: track ? track.stops.length.toLocaleString('zh-CN') : '—',
|
||||||
|
note: track ? '超过 3 分钟' : '等待查询',
|
||||||
|
tone: track?.stops.length ? 'warning' : 'neutral',
|
||||||
|
emphasis: 'secondary'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '事件点',
|
||||||
|
value: track ? track.events.length.toLocaleString('zh-CN') : '—',
|
||||||
|
note: track ? '轨迹事件' : '等待查询',
|
||||||
|
tone: 'neutral',
|
||||||
|
emphasis: 'secondary'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '数据点',
|
||||||
|
value: track ? track.points.length.toLocaleString('zh-CN') : '—',
|
||||||
|
note: track ? track.coverage.complete ? '时间窗完整' : '已返回切片' : '等待查询',
|
||||||
|
tone: track?.coverage.complete ? 'success' : track ? 'warning' : 'neutral',
|
||||||
|
emphasis: 'secondary'
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
function VehiclePicker({ value, onChange, onSelect }: { value: string; onChange: (value: string) => void; onSelect: (vehicle: VehicleRow) => void }) {
|
function VehiclePicker({ value, onChange, onSelect }: { value: string; onChange: (value: string) => void; onSelect: (vehicle: VehicleRow) => void }) {
|
||||||
const [open, setOpen] = useState(false);
|
const [open, setOpen] = useState(false);
|
||||||
const [debounced, setDebounced] = useState(value.trim());
|
const [debounced, setDebounced] = useState(value.trim());
|
||||||
@@ -195,9 +229,10 @@ function OverviewPanel({ track }: { track: TrackPlaybackResponse }) {
|
|||||||
</div>;
|
</div>;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TrackRail = memo(function TrackRail({ draft, track, loading, activeStopIndexes, activeEventIndexes, tab, queryCollapsed, showCommandBar, onDraft, onSubmit, onTab, onSelectIndex, onToggleQuery, onCollapse }: {
|
const TrackRail = memo(function TrackRail({ draft, track, summaryItems, loading, activeStopIndexes, activeEventIndexes, tab, queryCollapsed, showCommandBar, onDraft, onSubmit, onTab, onSelectIndex, onToggleQuery, onCollapse }: {
|
||||||
draft: Draft;
|
draft: Draft;
|
||||||
track?: TrackPlaybackResponse;
|
track?: TrackPlaybackResponse;
|
||||||
|
summaryItems: WorkspaceQueueMetricRailItem[];
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
activeStopIndexes: readonly number[];
|
activeStopIndexes: readonly number[];
|
||||||
activeEventIndexes: readonly number[];
|
activeEventIndexes: readonly number[];
|
||||||
@@ -254,12 +289,11 @@ const TrackRail = memo(function TrackRail({ draft, track, loading, activeStopInd
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section className="v2-track-rail-result">
|
<section className="v2-track-rail-result">
|
||||||
<WorkspacePanelHeader
|
<WorkspaceMetricRail
|
||||||
variant="compact"
|
variant="queue"
|
||||||
className="v2-track-result-header"
|
ariaLabel="轨迹行程摘要"
|
||||||
title="轨迹证据"
|
className="v2-track-result-metric-rail"
|
||||||
description={track ? track.plate ? `${track.plate} · ${track.vin}` : track.vin : '停留、事件与来源统一呈现'}
|
items={summaryItems}
|
||||||
meta={<Tag color={track ? 'blue' : 'grey'} type="light" size="small">{track ? `${number(track.summary.distanceKm)} km` : '等待查询'}</Tag>}
|
|
||||||
/>
|
/>
|
||||||
<SegmentedTabs className="v2-track-rail-tabs" ariaLabel="轨迹明细分类" value={tab} onChange={onTab} items={[{ key: 'stops', label: '停留点', count: track?.stops.length ?? 0 }, { key: 'events', label: '事件点', count: track?.events.length ?? 0 }, { key: 'overview', label: '概览' }]} />
|
<SegmentedTabs className="v2-track-rail-tabs" ariaLabel="轨迹明细分类" value={tab} onChange={onTab} items={[{ key: 'stops', label: '停留点', count: track?.stops.length ?? 0 }, { key: 'events', label: '事件点', count: track?.events.length ?? 0 }, { key: 'overview', label: '概览' }]} />
|
||||||
<div className="v2-track-rail-scroll">
|
<div className="v2-track-rail-scroll">
|
||||||
@@ -386,6 +420,7 @@ export default function TrackPage() {
|
|||||||
if (mobileLayout) setRailCollapsed(true);
|
if (mobileLayout) setRailCollapsed(true);
|
||||||
}, [mobileLayout, selectIndex]);
|
}, [mobileLayout, selectIndex]);
|
||||||
const collapseRail = useCallback(() => setRailCollapsed(true), []);
|
const collapseRail = useCallback(() => setRailCollapsed(true), []);
|
||||||
|
const toggleQuery = useCallback(() => setQueryCollapsed((value) => !value), []);
|
||||||
const togglePlayback = () => {
|
const togglePlayback = () => {
|
||||||
if (points.length < 2) return;
|
if (points.length < 2) return;
|
||||||
if (!playing && boundedIndex >= points.length - 1) setActiveIndex(0);
|
if (!playing && boundedIndex >= points.length - 1) setActiveIndex(0);
|
||||||
@@ -393,7 +428,8 @@ export default function TrackPage() {
|
|||||||
setPlaying((value) => !value);
|
setPlaying((value) => !value);
|
||||||
};
|
};
|
||||||
const progress = points.length > 1 ? boundedIndex / (points.length - 1) * 100 : 0;
|
const progress = points.length > 1 ? boundedIndex / (points.length - 1) * 100 : 0;
|
||||||
const trackRail = <TrackRail draft={draft} track={track} loading={query.isFetching} activeStopIndexes={activeStopIndexes} activeEventIndexes={activeEventIndexes} tab={panelTab} queryCollapsed={queryCollapsed} showCommandBar={!mobileLayout} onDraft={setDraft} onSubmit={submit} onTab={setPanelTab} onSelectIndex={selectRailIndex} onToggleQuery={() => setQueryCollapsed((value) => !value)} onCollapse={collapseRail} />;
|
const tripSummaryItems = useMemo(() => trackSummaryItems(track), [track]);
|
||||||
|
const trackRail = <TrackRail draft={draft} track={track} summaryItems={tripSummaryItems} loading={query.isFetching} activeStopIndexes={activeStopIndexes} activeEventIndexes={activeEventIndexes} tab={panelTab} queryCollapsed={queryCollapsed} showCommandBar={!mobileLayout} onDraft={setDraft} onSubmit={submit} onTab={setPanelTab} onSelectIndex={selectRailIndex} onToggleQuery={toggleQuery} onCollapse={collapseRail} />;
|
||||||
const appliedWindow = `${draft.dateFrom.replace('T', ' ').slice(5)} – ${draft.dateTo.replace('T', ' ').slice(5)}`;
|
const appliedWindow = `${draft.dateFrom.replace('T', ' ').slice(5)} – ${draft.dateTo.replace('T', ' ').slice(5)}`;
|
||||||
const appliedSource = draft.protocol ? protocolDisplayLabel(draft.protocol) : '自动来源';
|
const appliedSource = draft.protocol ? protocolDisplayLabel(draft.protocol) : '自动来源';
|
||||||
const mobileSheetTitle = track?.plate || draft.keyword.trim() || '轨迹查询与明细';
|
const mobileSheetTitle = track?.plate || draft.keyword.trim() || '轨迹查询与明细';
|
||||||
@@ -405,29 +441,16 @@ export default function TrackPage() {
|
|||||||
: track
|
: track
|
||||||
? track.coverage.complete ? '时间窗完整' : '最新切片'
|
? track.coverage.complete ? '时间窗完整' : '最新切片'
|
||||||
: '待查询';
|
: '待查询';
|
||||||
const mobileSheetSummary = track ? [
|
const mobileSheetSummary = useMemo(() => track ? tripSummaryItems.map((item) => ({
|
||||||
{
|
label: item.label,
|
||||||
label: '行程里程',
|
value: item.value,
|
||||||
value: `${number(track.summary.distanceKm)} km`,
|
detail: item.note,
|
||||||
detail: formatDuration(track.summary.durationSeconds),
|
tone: item.tone
|
||||||
tone: 'primary' as const
|
})) : [
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '停留 / 事件',
|
|
||||||
value: `${track.stops.length} / ${track.events.length}`,
|
|
||||||
detail: '地图证据点'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '数据点',
|
|
||||||
value: track.points.length.toLocaleString('zh-CN'),
|
|
||||||
detail: track.coverage.complete ? '时间窗完整' : '已返回切片',
|
|
||||||
tone: track.coverage.complete ? 'success' as const : 'warning' as const
|
|
||||||
}
|
|
||||||
] : [
|
|
||||||
{ label: '查询状态', value: '待查询', detail: '请先选择车辆', tone: 'primary' as const },
|
{ label: '查询状态', value: '待查询', detail: '请先选择车辆', tone: 'primary' as const },
|
||||||
{ label: '时间范围', value: appliedWindow, detail: '最长连续 7 天' },
|
{ label: '时间范围', value: appliedWindow, detail: '最长连续 7 天' },
|
||||||
{ label: '数据来源', value: appliedSource, detail: '支持自动优选' }
|
{ label: '数据来源', value: appliedSource, detail: '支持自动优选' }
|
||||||
];
|
], [appliedSource, appliedWindow, track, tripSummaryItems]);
|
||||||
|
|
||||||
return <div className={`v2-track-page${mobileLayout ? ' is-mobile-layout' : ''}${railCollapsed ? ' is-rail-collapsed' : ''}${monitorReturn ? ' has-monitor-return' : ''}`}>
|
return <div className={`v2-track-page${mobileLayout ? ' is-mobile-layout' : ''}${railCollapsed ? ' is-rail-collapsed' : ''}${monitorReturn ? ' has-monitor-return' : ''}`}>
|
||||||
<MonitorReturnBar />
|
<MonitorReturnBar />
|
||||||
@@ -451,7 +474,7 @@ export default function TrackPage() {
|
|||||||
label: queryCollapsed ? '修改查询' : '收起查询',
|
label: queryCollapsed ? '修改查询' : '收起查询',
|
||||||
ariaLabel: queryCollapsed ? '修改轨迹查询' : '收起轨迹查询',
|
ariaLabel: queryCollapsed ? '修改轨迹查询' : '收起轨迹查询',
|
||||||
icon: <IconSearch />,
|
icon: <IconSearch />,
|
||||||
onClick: () => setQueryCollapsed((value) => !value)
|
onClick: toggleQuery
|
||||||
}]}
|
}]}
|
||||||
primaryAction={{ label: '查看地图', icon: <IconMapPin />, onClick: collapseRail }}
|
primaryAction={{ label: '查看地图', icon: <IconMapPin />, onClick: collapseRail }}
|
||||||
onCancel={collapseRail}
|
onCancel={collapseRail}
|
||||||
@@ -494,7 +517,6 @@ export default function TrackPage() {
|
|||||||
{query.isError ? <div className="v2-track-error"><InlineError message={query.error instanceof Error ? query.error.message : '轨迹查询失败'} onRetry={() => query.refetch()} /></div> : null}
|
{query.isError ? <div className="v2-track-error"><InlineError message={query.error instanceof Error ? query.error.message : '轨迹查询失败'} onRetry={() => query.refetch()} /></div> : null}
|
||||||
|
|
||||||
<Card className="v2-track-playback-dock" bodyStyle={{ padding: 0 }}>
|
<Card className="v2-track-playback-dock" bodyStyle={{ padding: 0 }}>
|
||||||
<div className="v2-track-dock-summary"><small>行程里程</small><strong>{track ? `${number(track.summary.distanceKm)} km` : '等待查询'}</strong><span>{track ? `${formatDuration(track.summary.durationSeconds)} · ${track.summary.stopCount} 次停留` : '查询后可播放完整轨迹'}</span></div>
|
|
||||||
<div className="v2-track-dock-progress">
|
<div className="v2-track-dock-progress">
|
||||||
{track ? <SegmentRail track={track} /> : <div className="v2-track-segment-placeholder" />}
|
{track ? <SegmentRail track={track} /> : <div className="v2-track-segment-placeholder" />}
|
||||||
<Slider key={`track-progress-${points.length}`} className="v2-track-progress-slider" aria-label="轨迹播放进度" min={0} max={Math.max(0, points.length - 1)} step={1} value={boundedIndex} onChange={(value) => selectIndex(Number(value))} disabled={!points.length} showBoundary={false} tipFormatter={(value) => `第 ${Number(value) + 1} / ${points.length} 个数据点`} />
|
<Slider key={`track-progress-${points.length}`} className="v2-track-progress-slider" aria-label="轨迹播放进度" min={0} max={Math.max(0, points.length - 1)} step={1} value={boundedIndex} onChange={(value) => selectIndex(Number(value))} disabled={!points.length} showBoundary={false} tipFormatter={(value) => `第 ${Number(value) + 1} / ${points.length} 个数据点`} />
|
||||||
|
|||||||
@@ -392,6 +392,10 @@ describe('V2 production entry', () => {
|
|||||||
expect(corePageSources.TrackPage).toContain('<Card className="v2-track-rail-shell"');
|
expect(corePageSources.TrackPage).toContain('<Card className="v2-track-rail-shell"');
|
||||||
expect(corePageSources.TrackPage).toContain('<section className={`v2-track-query-panel${queryCollapsed');
|
expect(corePageSources.TrackPage).toContain('<section className={`v2-track-query-panel${queryCollapsed');
|
||||||
expect(corePageSources.TrackPage).toContain('<section className="v2-track-rail-result">');
|
expect(corePageSources.TrackPage).toContain('<section className="v2-track-rail-result">');
|
||||||
|
expect(corePageSources.TrackPage).toContain("from '../shared/WorkspaceMetricRail'");
|
||||||
|
expect(corePageSources.TrackPage).toContain('<WorkspaceMetricRail');
|
||||||
|
expect(corePageSources.TrackPage).toContain('className="v2-track-result-metric-rail"');
|
||||||
|
expect(corePageSources.TrackPage).not.toContain('className="v2-track-result-header"');
|
||||||
expect(corePageSources.TrackPage).toContain('<Card className="v2-track-current-card"');
|
expect(corePageSources.TrackPage).toContain('<Card className="v2-track-current-card"');
|
||||||
expect(corePageSources.TrackPage).toContain('<Card className="v2-track-empty-state"');
|
expect(corePageSources.TrackPage).toContain('<Card className="v2-track-empty-state"');
|
||||||
expect(corePageSources.TrackPage).toContain('<Card className="v2-track-playback-dock"');
|
expect(corePageSources.TrackPage).toContain('<Card className="v2-track-playback-dock"');
|
||||||
|
|||||||
@@ -1607,29 +1607,6 @@
|
|||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v2-track-result-header {
|
|
||||||
min-height: 64px;
|
|
||||||
background: #fbfcfe;
|
|
||||||
padding: 10px 14px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v2-track-result-header .v2-workspace-panel-copy > h5.semi-typography {
|
|
||||||
color: #25384f;
|
|
||||||
font-size: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v2-track-result-header .v2-workspace-panel-copy > .semi-typography {
|
|
||||||
color: #8592a3;
|
|
||||||
font-size: 11px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v2-track-result-header .v2-workspace-panel-meta .semi-tag {
|
|
||||||
border-radius: 999px;
|
|
||||||
padding-inline: 10px;
|
|
||||||
font-size: 11px;
|
|
||||||
font-weight: 700;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v2-track-rail-tabs {
|
.v2-track-rail-tabs {
|
||||||
height: 46px;
|
height: 46px;
|
||||||
flex-basis: 46px;
|
flex-basis: 46px;
|
||||||
@@ -1935,37 +1912,11 @@
|
|||||||
|
|
||||||
.v2-track-playback-dock > .semi-card-body {
|
.v2-track-playback-dock > .semi-card-body {
|
||||||
min-height: 104px;
|
min-height: 104px;
|
||||||
grid-template-columns: 150px minmax(240px, 1fr) auto;
|
grid-template-columns: minmax(240px, 1fr) auto;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
padding: 11px 16px !important;
|
padding: 11px 16px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v2-track-dock-summary small,
|
|
||||||
.v2-track-dock-summary strong,
|
|
||||||
.v2-track-dock-summary span {
|
|
||||||
display: block;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v2-track-dock-summary small {
|
|
||||||
margin-bottom: 2px;
|
|
||||||
color: #8593a5;
|
|
||||||
font-size: 9px;
|
|
||||||
font-weight: 650;
|
|
||||||
letter-spacing: .04em;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v2-track-dock-summary strong {
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v2-track-dock-summary span {
|
|
||||||
margin-top: 3px;
|
|
||||||
font-size: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.v2-track-segment-rail,
|
.v2-track-segment-rail,
|
||||||
.v2-track-segment-placeholder {
|
.v2-track-segment-placeholder {
|
||||||
height: 8px;
|
height: 8px;
|
||||||
@@ -2028,9 +1979,6 @@
|
|||||||
grid-template-columns: minmax(220px, 1fr) auto;
|
grid-template-columns: minmax(220px, 1fr) auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v2-track-dock-summary {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (max-width: 700px) {
|
@media (max-width: 700px) {
|
||||||
@@ -2055,7 +2003,7 @@
|
|||||||
max-height: 156px;
|
max-height: 156px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.v2-track-detail-sidesheet .v2-track-result-header {
|
.v2-track-detail-sidesheet .v2-track-result-metric-rail {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -23162,6 +23110,62 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Track replay trip summary.
|
||||||
|
* The narrow evidence rail uses the same Semi metric language as monitor and
|
||||||
|
* vehicle workspaces, while playback controls remain focused on time and
|
||||||
|
* transport instead of repeating trip totals.
|
||||||
|
*/
|
||||||
|
.v2-track-result-metric-rail.is-queue.semi-card {
|
||||||
|
flex: 0 0 auto;
|
||||||
|
border-bottom-color: #dfe6ef;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-result-metric-rail.is-queue > .semi-card-body {
|
||||||
|
min-height: 64px;
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-result-metric-rail .v2-workspace-metric-list {
|
||||||
|
grid-template-columns: 1.18fr .9fr .9fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-result-metric-rail .v2-workspace-metric-list > span {
|
||||||
|
min-height: 64px;
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
column-gap: 0;
|
||||||
|
padding: 7px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-result-metric-rail .v2-workspace-metric-list > span > :is(small, strong) {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-result-metric-rail .v2-workspace-metric-list > span > small {
|
||||||
|
font-size: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-result-metric-rail .v2-workspace-metric-list > span > strong,
|
||||||
|
.v2-track-result-metric-rail .v2-workspace-metric-list > span.is-secondary > strong {
|
||||||
|
font-size: clamp(14px, 1.2vw, 17px);
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-result-metric-rail .v2-workspace-metric-list > span > em {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 680px) {
|
||||||
|
.v2-track-detail-sidesheet .v2-workspace-config-summary {
|
||||||
|
grid-template-columns: repeat(4, minmax(0, 1fr));
|
||||||
|
}
|
||||||
|
|
||||||
|
.v2-track-detail-sidesheet .v2-workspace-config-summary > span {
|
||||||
|
padding-inline: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Monitor fleet summary.
|
* Monitor fleet summary.
|
||||||
* Operational state remains prominent; high-volume ingest evidence is kept
|
* Operational state remains prominent; high-volume ingest evidence is kept
|
||||||
|
|||||||
Reference in New Issue
Block a user