diff --git a/ui/package.json b/ui/package.json index c7db5adae..b0ef85d78 100644 --- a/ui/package.json +++ b/ui/package.json @@ -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", diff --git a/ui/src/components/blueprint-collection/blueprint-collection.module.scss b/ui/src/components/blueprint-collection/blueprint-collection.module.scss index a532fdeb2..4a0812dc6 100644 --- a/ui/src/components/blueprint-collection/blueprint-collection.module.scss +++ b/ui/src/components/blueprint-collection/blueprint-collection.module.scss @@ -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 { diff --git a/ui/src/components/blueprint-collection/blueprint-collection.tsx b/ui/src/components/blueprint-collection/blueprint-collection.tsx index fee6b85a3..c12c3d7f5 100644 --- a/ui/src/components/blueprint-collection/blueprint-collection.tsx +++ b/ui/src/components/blueprint-collection/blueprint-collection.tsx @@ -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 { @@ -57,7 +57,7 @@ export const BlueprintItem = ({ {item.timeLabel} -
+
{item.to ? ( - - - - - + + {translate(STRING.VIEW_IN_SESSION)} + + ) : null}
- - {item.label} -
) } diff --git a/ui/src/components/determination-score.tsx b/ui/src/components/determination-score.tsx index 1267612aa..3ab0735be 100644 --- a/ui/src/components/determination-score.tsx +++ b/ui/src/components/determination-score.tsx @@ -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 {translate(STRING.VALUE_NOT_AVAILABLE)} @@ -20,8 +20,8 @@ export const DeterminationScore = ({ return (
- - {scoreLabel} + + {verified ? translate(STRING.VERIFIED) : scoreLabel}
) diff --git a/ui/src/components/filtering/default-filter-control.tsx b/ui/src/components/filtering/default-filter-control.tsx index d35c09804..b9204bcf2 100644 --- a/ui/src/components/filtering/default-filter-control.tsx +++ b/ui/src/components/filtering/default-filter-control.tsx @@ -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' @@ -30,7 +27,7 @@ export const DefaultFiltersControl = ({ field }: { field: string }) => { {filter.label} - {project ? : null} + {project ? : null} { ) } -export const DefaultFiltersPopover = ({ +export const DefaultFiltersTooltip = ({ className, project, }: { @@ -64,43 +61,46 @@ export const DefaultFiltersPopover = ({ - - - - - - - taxon.name) - .join(', ')} - /> - taxon.name) - .join(', ')} - /> - - - {project.canUpdate ? ( - + +
+
+

+ {translate(STRING.MESSAGE_DEFAULT_FILTERS)} +

+ + + + + taxon.name) + .join(', ')} + /> + taxon.name) + .join(', ')} + /> + +
+ {project.canUpdate ? ( - Configure + {translate(STRING.CONFIGURE)} - - ) : null} + ) : null} +
) diff --git a/ui/src/components/filtering/filter-control.tsx b/ui/src/components/filtering/filter-control.tsx index bdbb04b66..d5000abf0 100644 --- a/ui/src/components/filtering/filter-control.tsx +++ b/ui/src/components/filtering/filter-control.tsx @@ -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' @@ -79,9 +78,7 @@ export const FilterControl = ({ {filter.label} - {filter.info ? ( - - ) : null} + {filter.tooltip ? : null}
) } - -export const FilterInfo = ({ text, to }: { text: string; to?: string }) => ( - - - - - - -

{text}

- {to ? ( - - Configure - - - ) : null} -
-
-
-) diff --git a/ui/src/components/filtering/filters/image-filter.tsx b/ui/src/components/filtering/filters/image-filter.tsx index ad0a88ebe..787bc7dfc 100644 --- a/ui/src/components/filtering/filters/image-filter.tsx +++ b/ui/src/components/filtering/filters/image-filter.tsx @@ -10,7 +10,7 @@ export const ImageFilter = ({ value }: FilterProps) => { })() return ( -
+
{label}
) diff --git a/ui/src/components/filtering/filters/session-filter.tsx b/ui/src/components/filtering/filters/session-filter.tsx index a4c3a4a4f..03001f201 100644 --- a/ui/src/components/filtering/filters/session-filter.tsx +++ b/ui/src/components/filtering/filters/session-filter.tsx @@ -15,7 +15,7 @@ export const SessionFilter = ({ value }: FilterProps) => { })() return ( -
+
{label}
) diff --git a/ui/src/components/filtering/filters/verification-status-filter.tsx b/ui/src/components/filtering/filters/verification-status-filter.tsx index ee5d5a3b7..bea7bd7e3 100644 --- a/ui/src/components/filtering/filters/verification-status-filter.tsx +++ b/ui/src/components/filtering/filters/verification-status-filter.tsx @@ -3,16 +3,15 @@ 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 ( @@ -20,7 +19,7 @@ export const VerificationStatusFilter = ({ - {OPTIONS.map((option) => ( + {options.map((option) => ( - {errorMessage ? ( - - ) : null} -
- - {translate(STRING.ENTITY_DELETE, { type })} - - - {translate(STRING.MESSAGE_DELETE_CONFIRM, { type })} - -
+ {errorMessage && } + +
@@ -53,7 +48,7 @@ export const DeleteForm = ({ ) : null}
-
+ ) } diff --git a/ui/src/data-services/hooks/processing-services/useProcessingServiceDetails.ts b/ui/src/data-services/hooks/processing-services/useProcessingServiceDetails.ts index 492585620..fffe614cd 100644 --- a/ui/src/data-services/hooks/processing-services/useProcessingServiceDetails.ts +++ b/ui/src/data-services/hooks/processing-services/useProcessingServiceDetails.ts @@ -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({ - 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( diff --git a/ui/src/data-services/models/capture.ts b/ui/src/data-services/models/capture.ts index 4e8534775..c2f80bdec 100644 --- a/ui/src/data-services/models/capture.ts +++ b/ui/src/data-services/models/capture.ts @@ -1,5 +1,6 @@ import { getFormatedDateTimeString } from 'utils/date/getFormatedDateTimeString/getFormatedDateTimeString' import { getFormatedTimeString } from 'utils/date/getFormatedTimeString/getFormatedTimeString' +import { STRING, translate } from 'utils/language' import { UserPermission } from 'utils/user/types' export type ServerCapture = any // TODO: Update this type @@ -15,20 +16,15 @@ export type CaptureDetection = { bbox: number[] id: string label: string - score: number - occurrenceId?: string occurrence?: DetectionOccurrence + occurrenceId?: string occurrenceMeetsCriteria: boolean + score: number + scoreLabel: string } const getDetectionLabel = (detection: CaptureDetection) => { if (detection.occurrence?.determination) { - const score = getDetectionScore(detection) - - if (score) { - return `${detection.occurrence.determination.name} (${score.toFixed(2)})` - } - return detection.occurrence.determination.name } @@ -39,12 +35,24 @@ const getDetectionScore = (detection: CaptureDetection) => { // This score label is the confidence of the best & most recent classification of the detection's occurrence // There will also be a score for the localization of the detection as well. const occurrence: DetectionOccurrence | undefined = detection.occurrence + if (occurrence && occurrence.determination_score) { return occurrence.determination_score } + return 0 } +const getDetectionScoreLabel = (detection: CaptureDetection) => { + const score = getDetectionScore(detection) + + if (score === 1) { + return translate(STRING.VERIFIED) + } + + return score.toFixed(2) +} + export class Capture { protected readonly _capture: ServerCapture private readonly _detections: CaptureDetection[] = [] @@ -59,11 +67,12 @@ export class Capture { bbox: detection.bbox, id: `${detection.id}`, label: getDetectionLabel(detection), - score: getDetectionScore(detection), occurrenceId: detection.occurrence ? `${detection.occurrence.id}` : undefined, occurrenceMeetsCriteria: detection.occurrence_meets_criteria, + score: getDetectionScore(detection), + scoreLabel: getDetectionScoreLabel(detection), } } ) diff --git a/ui/src/data-services/models/species.ts b/ui/src/data-services/models/species.ts index 082144af4..e507eca4f 100644 --- a/ui/src/data-services/models/species.ts +++ b/ui/src/data-services/models/species.ts @@ -64,14 +64,12 @@ export class Species extends Taxon { : undefined } - get lastSeenLabel() { + get lastSeen() { if (!this._species.last_detected) { return undefined } - const date = new Date(this._species.last_detected) - - return `${date.toLocaleDateString()} ${date.toLocaleTimeString()}` + return new Date(this._species.last_detected) } get numDetections(): number { diff --git a/ui/src/design-system/components/info-tooltip.tsx b/ui/src/design-system/components/info-tooltip.tsx new file mode 100644 index 000000000..3bb81df39 --- /dev/null +++ b/ui/src/design-system/components/info-tooltip.tsx @@ -0,0 +1,53 @@ +import classNames from 'classnames' +import { ChevronRightIcon, InfoIcon } from 'lucide-react' +import { Button, buttonVariants, Tooltip } from 'nova-ui-kit' +import { Link } from 'react-router-dom' +import { STRING, translate } from 'utils/language' + +export const InfoTooltip = (props: { + text: string + link?: { + text: string + to: string + } +}) => ( + + + + + + + + + + +) + +export const Info = ({ + link, + text, +}: { + text: string + link?: { + text: string + to: string + } +}) => ( +
+

{text}

+ {link ? ( + + {link.text} + + + ) : null} +
+) diff --git a/ui/src/design-system/components/input/input.module.scss b/ui/src/design-system/components/input/input.module.scss index 64c4c91ad..d9097e29c 100644 --- a/ui/src/design-system/components/input/input.module.scss +++ b/ui/src/design-system/components/input/input.module.scss @@ -12,14 +12,6 @@ margin-bottom: 4px; } -.label { - display: block; - @include paragraph-small(); - font-weight: 600; - color: $color-neutral-700; - white-space: nowrap; -} - .error { display: block; @include paragraph-xx-small(); @@ -32,18 +24,6 @@ } } -.value { - display: block; - @include paragraph-small(); - color: $color-neutral-300; - white-space: pre-wrap; - - &.link { - color: $color-primary-1-600; - font-weight: 600; - } -} - .description { display: block; @include paragraph-small(); diff --git a/ui/src/design-system/components/input/input.tsx b/ui/src/design-system/components/input/input.tsx index 9b8a283d2..c853bf760 100644 --- a/ui/src/design-system/components/input/input.tsx +++ b/ui/src/design-system/components/input/input.tsx @@ -14,6 +14,7 @@ import { import { Link } from 'react-router-dom' import { getFormatedDateTimeString } from 'utils/date/getFormatedDateTimeString/getFormatedDateTimeString' import { STRING, translate } from 'utils/language' +import { InfoTooltip } from '../info-tooltip' import { BasicTooltip } from '../tooltip/basic-tooltip' import styles from './input.module.scss' @@ -57,7 +58,7 @@ export const Input = forwardRef( return (
-