Skip to content

Commit

Permalink
Add filter UI to jobs (+ some more job related updates) (#626)
Browse files Browse the repository at this point in the history
* fix: update status colors in list view

* feat: add pipeline info to list view

* refactor: update filter component to be more flexible

* feat: add filters to jobs (station, pipeline and source image collection)

* feat: add source images column

* feat: add deployment info to job table

* fix: update filter keys to streamline

* feat: add station info to job details

* refactor: centralize job status handling

* feat: setup job status filter

* feat: update job columns with links and sorting where possible

* feat: update job fields with links where possible

* feat: prepare type and source image filter

* fix: tweak filter keys

* feat: add column settings to jobs

* fix: hide controls for missing backend filters

---------

Co-authored-by: Michael Bunsen <notbot@gmail.com>
  • Loading branch information
annavik and mihow authored Nov 28, 2024
1 parent c860b44 commit 1aec668
Show file tree
Hide file tree
Showing 24 changed files with 519 additions and 373 deletions.
10 changes: 9 additions & 1 deletion ui/src/components/filtering/filter-control.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,13 @@ import { AlgorithmFilter, NotAlgorithmFilter } from './filters/algorithm-filter'
import { CollectionFilter } from './filters/collection-filter'
import { DateFilter } from './filters/date-filter'
import { ImageFilter } from './filters/image-filter'
import { PipelineFilter } from './filters/pipeline-filter'
import { ScoreFilter } from './filters/score-filter'
import { SessionFilter } from './filters/session-filter'
import { StationFilter } from './filters/station-filter'
import { StatusFilter } from './filters/status-filter'
import { TaxonFilter } from './filters/taxon-filter'
import { TypeFilter } from './filters/type-filter'
import { FilterProps } from './filters/types'
import { VerificationStatusFilter } from './filters/verification-status-filter'

Expand All @@ -23,9 +26,14 @@ const ComponentMap: {
deployment: StationFilter,
detections__source_image: ImageFilter,
event: SessionFilter,
verified: VerificationStatusFilter,
pipeline: PipelineFilter,
not_algorithm: NotAlgorithmFilter,
source_image_collection: CollectionFilter,
source_image_single: ImageFilter,
status: StatusFilter,
taxon: TaxonFilter,
type: TypeFilter,
verified: VerificationStatusFilter,
}

interface FilterControlProps {
Expand Down
28 changes: 4 additions & 24 deletions ui/src/components/filtering/filtering.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,13 @@
import { BREAKPOINTS } from 'components/constants'
import { ChevronsUpDown } from 'lucide-react'
import { Box, Button, Collapsible } from 'nova-ui-kit'
import { FilterControl } from './filter-control'
import { ReactNode } from 'react'

interface FilteringProps {
config: {
capture?: boolean
endDate?: boolean
scoreThreshold?: boolean
session?: boolean
startDate?: boolean
station?: boolean
taxon?: boolean
verified?: boolean
}
children?: ReactNode
}

export const Filtering = ({ config }: FilteringProps) => (
export const Filtering = ({ children }: FilteringProps) => (
<Box className="w-full h-min shrink-0 p-2 rounded-lg md:w-72 md:p-4 md:rounded-xl">
<Collapsible.Root
className="space-y-4"
Expand All @@ -31,18 +22,7 @@ export const Filtering = ({ config }: FilteringProps) => (
</Collapsible.Trigger>
</div>
<Collapsible.Content className="space-y-6">
{config.capture && (
<FilterControl field="detections__source_image" readonly />
)}
{config.session && <FilterControl field="event" readonly />}
{config.startDate && <FilterControl field="date_start" />}
{config.endDate && <FilterControl field="date_end" />}
{config.taxon && <FilterControl field="taxon" />}
{config.scoreThreshold && (
<FilterControl clearable={false} field="classification_threshold" />
)}
{config.verified && <FilterControl field="verified" />}
{config.station && <FilterControl field="deployment" />}
{children}
</Collapsible.Content>
</Collapsible.Root>
</Box>
Expand Down
4 changes: 2 additions & 2 deletions ui/src/components/filtering/filters/image-filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const ImageFilter = ({ value }: FilterProps) => {

const label = (() => {
if (capture) {
return capture?.dateTimeLabel
return `#${capture.id}`
}
if (value && isLoading) {
return 'Loading...'
Expand All @@ -15,7 +15,7 @@ export const ImageFilter = ({ value }: FilterProps) => {
})()

return (
<div className="px-2">
<div className="px-2 pt-0.5">
<span className="text-muted-foreground">{label}</span>
</div>
)
Expand Down
30 changes: 30 additions & 0 deletions ui/src/components/filtering/filters/pipeline-filter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { usePipelines } from 'data-services/hooks/pipelines/usePipelines'
import { Select } from 'nova-ui-kit'
import { useParams } from 'react-router-dom'
import { FilterProps } from './types'

export const PipelineFilter = ({ value, onAdd }: FilterProps) => {
const { projectId } = useParams()
const { pipelines = [], isLoading } = usePipelines({
projectId: projectId as string,
})

return (
<Select.Root
disabled={pipelines.length === 0}
value={value ?? ''}
onValueChange={onAdd}
>
<Select.Trigger loading={isLoading}>
<Select.Value placeholder="All pipelines" />
</Select.Trigger>
<Select.Content className="max-h-72">
{pipelines.map((p) => (
<Select.Item key={p.id} value={p.id}>
{p.name}
</Select.Item>
))}
</Select.Content>
</Select.Root>
)
}
32 changes: 32 additions & 0 deletions ui/src/components/filtering/filters/status-filter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { Job, SERVER_JOB_STATUS_CODES } from 'data-services/models/job'
import { Select } from 'nova-ui-kit'
import { FilterProps } from './types'

const OPTIONS = SERVER_JOB_STATUS_CODES.map((code) => {
const statusInfo = Job.getStatusInfo(code)

return {
...statusInfo,
}
}).sort((o1, o2) => o1.type - o2.type)

export const StatusFilter = ({ value, onAdd }: FilterProps) => (
<Select.Root value={value ?? ''} onValueChange={onAdd}>
<Select.Trigger>
<Select.Value placeholder="Select a value" />
</Select.Trigger>
<Select.Content>
{OPTIONS.map((option) => (
<Select.Item key={option.code} value={option.code}>
<span className="flex items-center gap-2">
<span
className="w-3 h-3 rounded-full mb-0.5"
style={{ backgroundColor: option.color }}
/>
<span>{option.label}</span>
</span>
</Select.Item>
))}
</Select.Content>
</Select.Root>
)
26 changes: 26 additions & 0 deletions ui/src/components/filtering/filters/type-filter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Job, SERVER_JOB_TYPES } from 'data-services/models/job'
import { Select } from 'nova-ui-kit'
import { FilterProps } from './types'

const OPTIONS = SERVER_JOB_TYPES.map((key) => {
const typrInfo = Job.getJobTypeInfo(key)

return {
...typrInfo,
}
})

export const TypeFilter = ({ value, onAdd }: FilterProps) => (
<Select.Root value={value ?? ''} onValueChange={onAdd}>
<Select.Trigger>
<Select.Value placeholder="Select a value" />
</Select.Trigger>
<Select.Content>
{OPTIONS.map((option) => (
<Select.Item key={option.key} value={option.key}>
{option.label}
</Select.Item>
))}
</Select.Content>
</Select.Root>
)
8 changes: 4 additions & 4 deletions ui/src/data-services/models/capture-details.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Capture, ServerCapture } from './capture'
import { Job, JobStatus } from './job'
import { Job } from './job'

export type ServerCaptureDetails = ServerCapture & any // TODO: Update this type

Expand All @@ -21,9 +21,9 @@ export class CaptureDetails extends Capture {
get hasJobInProgress(): boolean {
return this._jobs.some(
(job) =>
job.status === JobStatus.Created ||
job.status === JobStatus.Pending ||
job.status === JobStatus.Started
job.status.code === 'CREATED' ||
job.status.code === 'PENDING' ||
job.status.code === 'STARTED'
)
}

Expand Down
46 changes: 11 additions & 35 deletions ui/src/data-services/models/job-details.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Job, JobStatus, ServerJob } from './job'
import { Pipeline } from './pipeline'
import { Job, JobStatusType, ServerJob, ServerJobStatusCode } from './job'

export type ServerJobDetails = ServerJob & any // TODO: Update this type

Expand All @@ -20,25 +19,23 @@ export class JobDetails extends Job {
return this._job.progress.errors ?? []
}

get pipeline(): Pipeline | undefined {
return this._job.pipeline ? new Pipeline(this._job.pipeline) : undefined
}

get logs(): string[] {
return this._job.progress.logs ?? []
}

get stages(): {
details: string
fields: { key: string; label: string; value?: string | number }[]
name: string
key: string
status: JobStatus
statusLabel: string
statusDetails: string
name: string
status: {
code: ServerJobStatusCode
label: string
type: JobStatusType
color: string
}
}[] {
return this._job.progress.stages.map((stage: any) => {
const status = this.getStatus(stage.status)

const fields: { key: string; label: string; value?: string | number }[] =
stage.params.map((param: any) => ({
key: param.key,
Expand All @@ -47,33 +44,12 @@ export class JobDetails extends Job {
}))

return {
details: stage.status_label,
fields,
key: stage.key,
name: stage.name,
status,
statusLabel: this.getStatusLabel(status),
statusDetails: stage.status_label,
status: Job.getStatusInfo(stage.status),
}
})
}

get sourceImages(): { id: string; name: string } | undefined {
const collection = this._job.source_image_collection

return collection
? { id: `${collection.id}`, name: collection.name }
: undefined
}

get sourceImage() {
const capture = this._job.source_image_single

return capture
? {
id: `${capture.id}`,
label: `${capture.id}`,
sessionId: capture.event_id ? `${capture.event_id}` : undefined,
}
: undefined
}
}
Loading

0 comments on commit 1aec668

Please sign in to comment.