Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
0203af4
fix: replace score label for confirmed occurrences
annavik Mar 25, 2026
020a188
feat: add tooltip and adjust copy for score column
annavik Mar 25, 2026
e5c9799
feat: add more info to filter controls
annavik Mar 25, 2026
46802e6
layout: tweak column order for pipeline table
annavik Mar 25, 2026
55e8325
feat: add filter control tooltips and streamline UI
annavik Mar 26, 2026
23b7a31
feat: add input content tooltips
annavik Mar 26, 2026
f5010cf
layout: streamline sort control order
annavik Mar 26, 2026
240c9a5
fix: hide pipeline stages from detail view
annavik Mar 26, 2026
e0956a6
feat: add algorithm description to table view
annavik Mar 26, 2026
c157411
fix: pass project ID with processing service details request
annavik Mar 26, 2026
37f733c
layout: cleanup processing views
annavik Mar 26, 2026
1b198b7
style: add more explicit navigation links for occurrences
annavik Mar 26, 2026
779230e
fix: bring back taxa icon
annavik Mar 26, 2026
8fbccae
fix: typo
annavik Mar 26, 2026
1fcab18
feat: make action columns sticky
annavik Mar 27, 2026
429fa7b
feat: conditionally hide action columns
annavik Mar 30, 2026
431dd52
chore: tweak order of actions
annavik Mar 30, 2026
2505a1c
layout: use compact action buttons
annavik Mar 30, 2026
61e1694
layout: streamline delete form layout
annavik Mar 30, 2026
0972e93
feat: add more tooltips
annavik Mar 31, 2026
853e44d
copy: tweak copy after feedback
annavik Mar 31, 2026
10d2123
feat: add column settings to algorithm table and bring back category …
annavik Mar 31, 2026
df94b0e
chore: extract occurrence actions to a reusable component
annavik Mar 31, 2026
8394afd
layout: prepare occurrence content in session detail view to include …
annavik Mar 31, 2026
4b8e07e
layout: apply floating toolbar layout to action columns
annavik Apr 1, 2026
ef7ccec
Merge branch 'main' into chore/ui-cleanup
annavik Apr 2, 2026
476abf7
fix: cleanup
annavik Apr 2, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"leaflet": "^1.9.3",
"lodash": "^4.17.21",
"lucide-react": "^1.0.1",
"nova-ui-kit": "^1.1.33",
"nova-ui-kit": "^1.1.34",
"plotly.js": "^2.25.2",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,10 @@
align-items: center;
max-width: 100%;

.blueprintImage {
position: relative;
margin-bottom: 8px;

img {
max-width: 100%;
height: inherit;
vertical-align: middle;
}
img {
max-width: 100%;
height: inherit;
vertical-align: middle;
}

.blueprintInfo {
Expand Down
32 changes: 10 additions & 22 deletions ui/src/components/blueprint-collection/blueprint-collection.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import classNames from 'classnames'
import { LicenseInfo } from 'components/license-info/license-info'
import { BasicTooltip } from 'design-system/components/tooltip/basic-tooltip'
import { EyeIcon } from 'lucide-react'
import { ChevronRightIcon } from 'lucide-react'
import { buttonVariants } from 'nova-ui-kit'
import { ReactNode, useState } from 'react'
import { Link } from 'react-router-dom'
import { STRING, translate } from 'utils/language'
import styles from './blueprint-collection.module.scss'

export interface BlueprintItem {
Expand Down Expand Up @@ -57,7 +57,7 @@ export const BlueprintItem = ({
{item.timeLabel}
</span>
</div>
<div className={styles.blueprintImage}>
<div className="flex flex-col items-center gap-2">
<img
src={item.image.src}
alt=""
Expand All @@ -71,27 +71,15 @@ export const BlueprintItem = ({
}}
/>
{item.to ? (
<BasicTooltip asChild content="Show in session capture">
<Link
to={item.to}
className={classNames(
buttonVariants({ size: 'icon', variant: 'outline' }),
'flex w-8 h-8 absolute right-2 bottom-2 invisible group-hover:visible'
)}
>
<EyeIcon className="w-4 h-4" />
</Link>
</BasicTooltip>
<Link
className={buttonVariants({ size: 'small', variant: 'ghost' })}
to={item.to}
>
<span>{translate(STRING.VIEW_IN_SESSION)}</span>
<ChevronRightIcon className="w-4 h-4" />
</Link>
Comment thread
annavik marked this conversation as resolved.
) : null}
</div>
<span
className={classNames(
styles.blueprintLabel,
'body-small text-foreground'
)}
>
{item.label}
</span>
</div>
)
}
8 changes: 4 additions & 4 deletions ui/src/components/determination-score.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ import { IdentificationScore } from 'nova-ui-kit'
import { STRING, translate } from 'utils/language'

export const DeterminationScore = ({
confirmed,
score,
scoreLabel,
tooltip,
verified,
}: {
confirmed?: boolean
score?: number
scoreLabel?: string
tooltip?: string
verified?: boolean
}) => {
if (score === undefined || scoreLabel === undefined) {
return <span>{translate(STRING.VALUE_NOT_AVAILABLE)}</span>
Expand All @@ -20,8 +20,8 @@ export const DeterminationScore = ({
return (
<BasicTooltip content={tooltip}>
<div className="flex items-center gap-3">
<IdentificationScore confirmed={confirmed} confidenceScore={score} />
<span>{scoreLabel}</span>
<IdentificationScore confirmed={verified} confidenceScore={score} />
<span>{verified ? translate(STRING.VERIFIED) : scoreLabel}</span>
</div>
</BasicTooltip>
)
Expand Down
78 changes: 39 additions & 39 deletions ui/src/components/filtering/default-filter-control.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
import {
FormActions,
FormRow,
FormSection,
} from 'components/form/layout/layout'
import classNames from 'classnames'
import { FormRow } from 'components/form/layout/layout'
import { useProjectDetails } from 'data-services/hooks/projects/useProjectDetails'
import { ProjectDetails } from 'data-services/models/project-details'
import { InputValue } from 'design-system/components/input/input'
Expand Down Expand Up @@ -30,7 +27,7 @@ export const DefaultFiltersControl = ({ field }: { field: string }) => {
<span className="text-muted-foreground body-overline-small font-bold pt-0.5">
{filter.label}
</span>
{project ? <DefaultFiltersPopover project={project} /> : null}
{project ? <DefaultFiltersTooltip project={project} /> : null}
</div>
<Switch
checked={stringToBoolean(filter.value) ?? true}
Expand All @@ -46,7 +43,7 @@ export const DefaultFiltersControl = ({ field }: { field: string }) => {
)
}

export const DefaultFiltersPopover = ({
export const DefaultFiltersTooltip = ({
className,
project,
}: {
Expand All @@ -64,43 +61,46 @@ export const DefaultFiltersPopover = ({
<InfoIcon className="w-4 h-4" />
</Button>
</Popover.Trigger>
<Popover.Content className="p-0">
<FormSection
title={translate(STRING.NAV_ITEM_DEFAULT_FILTERS)}
description="Data is filtered by default based on global project configuration."
>
<FormRow>
<InputValue
label="Score threshold"
value={project.settings.scoreThreshold}
/>
</FormRow>
<FormRow>
<InputValue
label="Include taxa"
value={project.settings.includeTaxa
.map((taxon) => taxon.name)
.join(', ')}
/>
<InputValue
label="Exclude taxa"
value={project.settings.excludeTaxa
.map((taxon) => taxon.name)
.join(', ')}
/>
</FormRow>
</FormSection>
{project.canUpdate ? (
<FormActions>
<Popover.Content className="p-4 max-w-xs">
<div className="flex flex-col gap-4">
<div className="flex flex-col gap-4 border-b border-border pb-4">
<p className="body-small italic text-muted-foreground">
{translate(STRING.MESSAGE_DEFAULT_FILTERS)}
</p>
<FormRow>
<InputValue
label="Score threshold"
value={project.settings.scoreThreshold}
/>
</FormRow>
<FormRow>
<InputValue
label="Include taxa"
value={project.settings.includeTaxa
.map((taxon) => taxon.name)
.join(', ')}
/>
<InputValue
label="Exclude taxa"
value={project.settings.excludeTaxa
.map((taxon) => taxon.name)
.join(', ')}
/>
</FormRow>
</div>
{project.canUpdate ? (
<Link
className={buttonVariants({ size: 'small', variant: 'outline' })}
className={classNames(
buttonVariants({ size: 'small', variant: 'ghost' }),
'!w-auto self-end'
)}
to={APP_ROUTES.DEFAULT_FILTERS({ projectId: project.id })}
>
<span>Configure</span>
<span>{translate(STRING.CONFIGURE)}</span>
<ChevronRightIcon className="w-4 h-4" />
</Link>
</FormActions>
) : null}
) : null}
</div>
</Popover.Content>
</Popover.Root>
)
43 changes: 4 additions & 39 deletions ui/src/components/filtering/filter-control.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import classNames from 'classnames'
import { ChevronRightIcon, InfoIcon, XIcon } from 'lucide-react'
import { Button, buttonVariants, Tooltip } from 'nova-ui-kit'
import { Link } from 'react-router-dom'
import { InfoTooltip } from 'design-system/components/info-tooltip'
import { XIcon } from 'lucide-react'
import { Button } from 'nova-ui-kit'
import { STRING, translate } from 'utils/language'
import { useFilters } from 'utils/useFilters'
import { AlgorithmFilter, NotAlgorithmFilter } from './filters/algorithm-filter'
Expand Down Expand Up @@ -79,9 +78,7 @@ export const FilterControl = ({
<span className="text-muted-foreground body-overline-small font-bold pt-0.5">
{filter.label}
</span>
{filter.info ? (
<FilterInfo text={filter.info.text} to={filter.info.to} />
) : null}
{filter.tooltip ? <InfoTooltip {...filter.tooltip} /> : null}
</div>
<div className="flex items-center justify-between gap-2">
<FilterComponent
Expand Down Expand Up @@ -111,35 +108,3 @@ export const FilterControl = ({
</div>
)
}

export const FilterInfo = ({ text, to }: { text: string; to?: string }) => (
<Tooltip.Provider delayDuration={0}>
<Tooltip.Root>
<Tooltip.Trigger asChild>
<Button
aria-label={translate(STRING.INFO)}
className="text-muted-foreground"
size="icon"
variant="ghost"
>
<InfoIcon className="w-4 h-4" />
</Button>
</Tooltip.Trigger>
<Tooltip.Content side="bottom" className="p-4 space-y-4 max-w-xs">
<p className="whitespace-normal">{text}</p>
{to ? (
<Link
className={classNames(
buttonVariants({ size: 'small', variant: 'outline' }),
'!w-auto'
)}
to={to}
>
<span>Configure</span>
<ChevronRightIcon className="w-4 h-4" />
</Link>
) : null}
</Tooltip.Content>
</Tooltip.Root>
</Tooltip.Provider>
)
2 changes: 1 addition & 1 deletion ui/src/components/filtering/filters/image-filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const ImageFilter = ({ value }: FilterProps) => {
})()

return (
<div className="px-2 pt-0.5">
<div className="pt-0.5">
<span className="text-muted-foreground">{label}</span>
</div>
)
Expand Down
2 changes: 1 addition & 1 deletion ui/src/components/filtering/filters/session-filter.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const SessionFilter = ({ value }: FilterProps) => {
})()

return (
<div className="px-2">
<div className="pt-0.5">
<span className="text-muted-foreground">{label}</span>
</div>
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,23 @@ import { STRING, translate } from 'utils/language'
import { booleanToString, stringToBoolean } from '../utils'
import { FilterProps } from './types'

const OPTIONS = [
{ value: true, label: 'Verified' },
{ value: false, label: 'Not verified' },
]

export const VerificationStatusFilter = ({
value: string,
onAdd,
}: FilterProps) => {
const value = stringToBoolean(string)
const options = [
{ value: true, label: translate(STRING.VERIFIED) },
{ value: false, label: translate(STRING.NOT_VERIFIED) },
]

return (
<Select.Root value={booleanToString(value)} onValueChange={onAdd}>
<Select.Trigger>
<Select.Value placeholder={translate(STRING.SELECT_PLACEHOLDER)} />
</Select.Trigger>
<Select.Content>
{OPTIONS.map((option) => (
{options.map((option) => (
<Select.Item
key={booleanToString(option.value)}
value={booleanToString(option.value)}
Expand Down
21 changes: 8 additions & 13 deletions ui/src/components/form/delete-form/delete-form.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { CheckIcon, Loader2Icon } from 'lucide-react'
import { Button } from 'nova-ui-kit'
import { STRING, translate } from 'utils/language'
import { parseServerError } from 'utils/parseServerError/parseServerError'
import { FormError } from '../layout/layout'
import { FormError, FormSection } from '../layout/layout'

export const DeleteForm = ({
type,
Expand All @@ -23,17 +23,12 @@ export const DeleteForm = ({

return (
<>
{errorMessage ? (
<FormError message={errorMessage} style={{ padding: '8px 16px' }} />
) : null}
<div className="grid gap-4 px-4 py-6">
<span className="body-overline-small font-semibold text-muted-foreground">
{translate(STRING.ENTITY_DELETE, { type })}
</span>
<span className="body-small">
{translate(STRING.MESSAGE_DELETE_CONFIRM, { type })}
</span>
<div className="grid grid-cols-2 gap-4">
{errorMessage && <FormError message={errorMessage} />}
<FormSection
title={translate(STRING.ENTITY_DELETE, { type })}
description={translate(STRING.MESSAGE_DELETE_CONFIRM, { type })}
>
<div className="flex justify-end gap-4">
<Button onClick={onCancel} size="small" variant="outline">
<span>{translate(STRING.CANCEL)}</span>
</Button>
Expand All @@ -53,7 +48,7 @@ export const DeleteForm = ({
) : null}
</Button>
</div>
</div>
</FormSection>
</>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,18 @@ const convertServerRecord = (record: ServerProcessingService) =>
new ProcessingService(record)

export const useProcessingServiceDetails = (
processingServiceId: string,
projectId?: string
id: string,
projectId: string
): {
processingService?: ProcessingService
isLoading: boolean
isFetching: boolean
error?: unknown
} => {
const params = projectId ? `?project_id=${projectId}` : ''
const { data, isLoading, isFetching, error } =
useAuthorizedQuery<ProcessingService>({
queryKey: [
API_ROUTES.PROCESSING_SERVICES,
processingServiceId,
projectId,
],
url: `${API_URL}/${API_ROUTES.PROCESSING_SERVICES}/${processingServiceId}/${params}`,
queryKey: [API_ROUTES.PROCESSING_SERVICES, id, projectId],
url: `${API_URL}/${API_ROUTES.PROCESSING_SERVICES}/${id}/?project_id=${projectId}`,
})

const processingService = useMemo(
Expand Down
Loading