Refine insight workspace global filters (#285)

This commit is contained in:
shenlan 2025-09-21 22:36:36 +08:00 committed by GitHub
parent 2addc93814
commit 8b95a0d764
4 changed files with 40 additions and 172 deletions

View File

@ -47,12 +47,6 @@ const defaultRecord = <T,>(value: T): Record<QueryLanguage, T> => ({
traceql: value
})
const orgOptions = ['global-org', 'ecommerce', 'payments']
const projectOptions = ['observability', 'checkout', 'growth']
const envOptions = ['production', 'staging', 'dev']
const regionOptions = ['us-west-2', 'eu-central-1', 'ap-southeast-1']
const timeRanges = ['15m', '1h', '6h', '24h', '7d']
export function ExploreBuilder({
state,
updateState,
@ -236,18 +230,9 @@ export function ExploreBuilder({
{!isCollapsed && (
<div className="mt-4 flex flex-1 flex-col gap-4">
{inputMode === 'menu' ? (
<div className="grid gap-3 text-xs text-slate-200 sm:grid-cols-2 lg:grid-cols-4">
<MenuSelect label="Org" value={state.org} onChange={value => updateState({ org: value })} options={orgOptions} />
<MenuSelect
label="Project"
value={state.project}
onChange={value => updateState({ project: value })}
options={projectOptions}
/>
<MenuSelect label="Environment" value={state.env} onChange={value => updateState({ env: value })} options={envOptions} />
<MenuSelect label="Region" value={state.region} onChange={value => updateState({ region: value })} options={regionOptions} />
<MenuTimeRange value={state.timeRange} onChange={value => updateState({ timeRange: value })} />
<div className="sm:col-span-2 lg:col-span-4">
<div className="space-y-3 text-xs text-slate-200">
<ContextSummary state={state} />
<div>
<p className="text-[11px] uppercase tracking-wide text-slate-500">Query preview</p>
<pre className="mt-2 max-h-32 overflow-auto rounded-2xl border border-slate-800 bg-slate-950/60 p-3 text-xs text-emerald-200">
{state.queries[language] || meta.placeholder}
@ -328,63 +313,29 @@ export function ExploreBuilder({
return <div className="space-y-4">{panels}</div>
}
interface MenuSelectProps {
label: string
value: string
onChange: (value: string) => void
options: string[]
}
function ContextSummary({ state }: { state: InsightState }) {
const context = [
{ label: 'Org', value: state.org },
{ label: 'Environment', value: state.env },
{ label: 'Region', value: state.region },
{ label: 'Project', value: state.project },
{ label: 'Time range', value: state.timeRange }
]
function MenuSelect({ label, value, onChange, options }: MenuSelectProps) {
return (
<label className="flex flex-col gap-1">
<span className="text-[11px] uppercase tracking-wide text-slate-500">{label}</span>
<select
value={value}
onChange={event => onChange(event.target.value)}
className="rounded-xl border border-slate-800 bg-slate-950/60 px-3 py-2 text-sm text-slate-200"
>
{options.map(option => (
<option key={option} value={option} className="bg-slate-900 text-slate-200">
{option}
</option>
))}
</select>
</label>
)
}
interface MenuTimeRangeProps {
value: string
onChange: (value: string) => void
}
function MenuTimeRange({ value, onChange }: MenuTimeRangeProps) {
return (
<label className="flex flex-col gap-1 sm:col-span-2 lg:col-span-4">
<span className="text-[11px] uppercase tracking-wide text-slate-500">Time range</span>
<div className="flex flex-wrap items-center gap-2">
{timeRanges.map(range => (
<button
key={range}
type="button"
onClick={() => onChange(range)}
className={`rounded-xl px-3 py-1 text-xs font-medium transition ${
value === range
? 'bg-emerald-500/20 text-emerald-200'
: 'border border-slate-800 bg-slate-950/60 text-slate-400 hover:text-slate-100'
}`}
<div className="rounded-2xl border border-slate-800 bg-slate-950/60 p-3">
<p className="text-[11px] uppercase tracking-wide text-slate-500">Global context</p>
<div className="mt-2 flex flex-wrap items-center gap-2">
{context.map(item => (
<span
key={item.label}
className="flex items-center gap-2 rounded-full border border-slate-800 bg-slate-900/70 px-3 py-1 text-xs text-slate-200"
>
{range}
</button>
<span className="text-slate-500">{item.label}</span>
<span className="font-medium text-slate-100">{item.value}</span>
</span>
))}
<input
value={value}
onChange={event => onChange(event.target.value)}
className="w-32 rounded-xl border border-slate-800 bg-slate-950/60 px-3 py-1.5 text-xs text-slate-200"
placeholder="Custom"
/>
</div>
</label>
</div>
)
}

View File

@ -2,6 +2,7 @@
import { useState } from 'react'
import { InsightState } from '../../insight/store/urlState'
import { TimeRangePicker } from './TimeRangePicker'
interface BreadcrumbBarProps {
state: InsightState
@ -28,17 +29,19 @@ export function BreadcrumbBar({ state, updateState, shareableLink }: BreadcrumbB
}
return (
<div className="flex flex-wrap items-center gap-3 text-sm text-slate-200">
<div className="flex w-full flex-wrap items-center gap-3 text-sm text-slate-200">
<Selector label="Org" value={state.org} options={orgs} onChange={org => updateState({ org })} />
<Separator />
<Selector label="Environment" value={state.env} options={envs} onChange={env => updateState({ env })} />
<Separator />
<Selector label="Region" value={state.region} options={regions} onChange={region => updateState({ region })} />
<Separator />
<Selector label="Project" value={state.project} options={projects} onChange={project => updateState({ project })} />
<Separator />
<Selector label="Env" value={state.env} options={envs} onChange={env => updateState({ env })} />
<Separator />
<Selector label="Region" value={state.region} options={regions} onChange={region => updateState({ region })} />
<TimeRangePicker state={state} updateState={updateState} />
<button
onClick={handleCopy}
className="ml-auto flex items-center gap-2 rounded-xl border border-slate-700 px-3 py-1.5 text-xs font-medium text-slate-200 hover:bg-slate-800 transition"
className="ml-auto flex items-center gap-2 rounded-xl border border-slate-700 px-3 py-1.5 text-xs font-medium text-slate-200 transition hover:bg-slate-800"
>
{copied ? 'Link copied!' : 'Copy share link'}
</button>
@ -58,12 +61,12 @@ function Selector({
onChange: (value: string) => void
}) {
return (
<label className="flex items-center gap-2 rounded-xl border border-slate-800 bg-slate-900/80 px-3 py-1.5 shadow-inner">
<label className="flex min-w-[160px] flex-1 items-center gap-2 rounded-xl border border-slate-800 bg-slate-900/80 px-3 py-1.5 shadow-inner">
<span className="text-xs uppercase tracking-wide text-slate-500">{label}</span>
<select
value={value}
onChange={event => onChange(event.target.value)}
className="bg-transparent text-sm font-medium focus:outline-none"
className="w-full bg-transparent text-sm font-medium focus:outline-none"
>
{options.map(option => (
<option key={option} value={option} className="bg-slate-900">

View File

@ -12,12 +12,12 @@ const ranges = ['15m', '1h', '6h', '24h', '7d']
export function TimeRangePicker({ state, updateState }: TimeRangePickerProps) {
return (
<div className="flex items-center gap-2 rounded-xl border border-slate-800 bg-slate-900/80 px-3 py-2 text-sm text-slate-200 shadow-inner">
<span className="text-xs uppercase tracking-wide text-slate-500">Time Range</span>
<label className="flex min-w-[160px] flex-1 items-center gap-2 rounded-xl border border-slate-800 bg-slate-900/80 px-3 py-1.5 text-sm text-slate-200 shadow-inner">
<span className="text-xs uppercase tracking-wide text-slate-500">Time range</span>
<select
value={state.timeRange}
onChange={event => updateState({ timeRange: event.target.value })}
className="bg-transparent focus:outline-none text-sm font-medium"
className="w-full bg-transparent text-sm font-medium focus:outline-none"
>
{ranges.map(range => (
<option key={range} value={range} className="bg-slate-900">
@ -28,6 +28,6 @@ export function TimeRangePicker({ state, updateState }: TimeRangePickerProps) {
Custom window
</option>
</select>
</div>
</label>
)
}

View File

@ -10,14 +10,8 @@ interface NetworkTopologyPanelProps {
updateState: (partial: Partial<InsightState>) => void
}
const orgOptions = ['global-org', 'ecommerce', 'payments']
const projectOptions = ['observability', 'checkout', 'growth']
const envOptions = ['production', 'staging', 'dev']
const regionOptions = ['us-west-2', 'eu-central-1', 'ap-southeast-1']
const timeRanges = ['15m', '1h', '6h', '24h', '7d']
export function NetworkTopologyPanel({ state, updateState }: NetworkTopologyPanelProps) {
const [filtersCollapsed, setFiltersCollapsed] = useState(false)
const [advancedVisible, setAdvancedVisible] = useState(false)
const subtitle = useMemo(
() => `${state.env} · ${state.region} · ${state.org}/${state.project}`,
@ -37,10 +31,10 @@ export function NetworkTopologyPanel({ state, updateState }: NetworkTopologyPane
<div className="ml-auto flex flex-wrap items-center gap-2 text-xs text-slate-300">
<button
type="button"
onClick={() => setFiltersCollapsed(prev => !prev)}
onClick={() => setAdvancedVisible(prev => !prev)}
className="rounded-xl border border-slate-800 px-3 py-1 transition hover:border-slate-700 hover:text-slate-100"
>
{filtersCollapsed ? 'Expand filters' : 'Collapse filters'}
{advancedVisible ? 'Hide advanced filters' : 'Show advanced filters'}
</button>
<button
type="button"
@ -52,20 +46,7 @@ export function NetworkTopologyPanel({ state, updateState }: NetworkTopologyPane
</div>
</header>
<div className="mt-4 grid gap-3 text-xs text-slate-200 sm:grid-cols-2 lg:grid-cols-4">
<SelectField label="Org" value={state.org} onChange={value => updateState({ org: value })} options={orgOptions} />
<SelectField
label="Project"
value={state.project}
onChange={value => updateState({ project: value })}
options={projectOptions}
/>
<SelectField label="Environment" value={state.env} onChange={value => updateState({ env: value })} options={envOptions} />
<SelectField label="Region" value={state.region} onChange={value => updateState({ region: value })} options={regionOptions} />
<TimeRangeControls value={state.timeRange} onChange={value => updateState({ timeRange: value })} />
</div>
{!filtersCollapsed && (
{advancedVisible && (
<div className="mt-4 flex flex-wrap gap-3 text-xs text-slate-300">
<TextField
label="Namespace"
@ -79,12 +60,6 @@ export function NetworkTopologyPanel({ state, updateState }: NetworkTopologyPane
placeholder="all services"
onChange={value => updateState({ service: value })}
/>
<TextField
label="Time window"
value={state.timeRange}
placeholder="1h"
onChange={value => updateState({ timeRange: value })}
/>
</div>
)}
@ -95,67 +70,6 @@ export function NetworkTopologyPanel({ state, updateState }: NetworkTopologyPane
)
}
interface SelectFieldProps {
label: string
value: string
onChange: (value: string) => void
options: string[]
}
function SelectField({ label, value, onChange, options }: SelectFieldProps) {
return (
<label className="flex flex-col gap-1">
<span className="text-[11px] uppercase tracking-wide text-slate-500">{label}</span>
<select
value={value}
onChange={event => onChange(event.target.value)}
className="rounded-xl border border-slate-800 bg-slate-950/60 px-3 py-2 text-sm text-slate-200"
>
{options.map(option => (
<option key={option} value={option} className="bg-slate-900 text-slate-200">
{option}
</option>
))}
</select>
</label>
)
}
interface TimeRangeControlsProps {
value: string
onChange: (value: string) => void
}
function TimeRangeControls({ value, onChange }: TimeRangeControlsProps) {
return (
<label className="flex flex-col gap-1 sm:col-span-2 lg:col-span-4">
<span className="text-[11px] uppercase tracking-wide text-slate-500">Time range</span>
<div className="flex flex-wrap items-center gap-2">
{timeRanges.map(range => (
<button
key={range}
type="button"
onClick={() => onChange(range)}
className={`rounded-xl px-3 py-1 text-xs font-medium transition ${
value === range
? 'bg-emerald-500/20 text-emerald-200'
: 'border border-slate-800 bg-slate-950/60 text-slate-400 hover:text-slate-100'
}`}
>
{range}
</button>
))}
<input
value={value}
onChange={event => onChange(event.target.value)}
className="w-32 rounded-xl border border-slate-800 bg-slate-950/60 px-3 py-1.5 text-xs text-slate-200"
placeholder="Custom"
/>
</div>
</label>
)
}
interface TextFieldProps {
label: string
value: string