Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,11 @@
}

.columnName {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;

span {
@include paragraph-medium();
font-weight: 600;
Expand All @@ -33,7 +38,6 @@
position: relative;

.columnName {
display: block;
position: relative;
}

Expand All @@ -48,19 +52,13 @@
}

.iconWrapper {
position: absolute;
top: 50%;
right: -6px;
display: flex;
align-items: center;
justify-content: center;
transform-origin: center;
transform: translate(100%, -50%);
visibility: hidden;

&.visible {
visibility: visible;
}

&.ascending {
transform: translate(100%, -50%) rotate(180deg);
transform: rotate(180deg);
}
}

Expand Down
84 changes: 52 additions & 32 deletions ui/src/design-system/components/table/table-header/table-header.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import classNames from 'classnames'
import { Icon, IconTheme, IconType } from 'design-system/components/icon/icon'
import { Tooltip } from 'design-system/components/tooltip/tooltip'
import { TableColumn, TableSortSettings } from '../types'
import styles from './table-header.module.scss'

Expand Down Expand Up @@ -36,22 +37,32 @@ const BasicTableHeader = <T,>({
column,
visuallyHidden,
}: Omit<TableHeaderProps<T>, 'sortSettings' | 'onSortClick'>) => (
<th
key={column.id}
style={{ textAlign: column.styles?.textAlign, width: column.styles?.width }}
className={styles.tableHeader}
>
<div
className={classNames(styles.content, {
[styles.visuallyHidden]: visuallyHidden,
})}
style={{ padding: column.styles?.padding }}
<Tooltip content={column.tooltip}>
<th
key={column.id}
style={{
textAlign: column.styles?.textAlign,
width: column.styles?.width,
}}
className={styles.tableHeader}
>
<div className={styles.columnName}>
<span>{column.name}</span>
<div
className={classNames(styles.content, {
[styles.visuallyHidden]: visuallyHidden,
})}
style={{ padding: column.styles?.padding }}
>
<div className={styles.columnName}>
<span>{column.name}</span>
{column.tooltip ? (
<div className={styles.iconWrapper}>
<Icon type={IconType.Info} theme={IconTheme.Neutral} />
</div>
) : null}
</div>
</div>
</div>
</th>
</th>
</Tooltip>
)

const SortableTableHeader = <T,>({
Expand All @@ -78,25 +89,34 @@ const SortableTableHeader = <T,>({
[styles.active]: sortActive,
})}
>
<button
className={classNames(styles.content, styles.sortButton, {
[styles.visuallyHidden]: visuallyHidden,
})}
style={{ padding: column.styles?.padding }}
onClick={onSortClick}
>
<div className={styles.columnName}>
<span>{column.name}</span>
<div
className={classNames(styles.iconWrapper, {
[styles.visible]: sortActive,
[styles.ascending]: sortActive && sortSettings?.order === 'asc',
})}
>
<Icon type={IconType.Sort} theme={IconTheme.Neutral} />
<Tooltip content={column.tooltip}>
<button
className={classNames(styles.content, styles.sortButton, {
[styles.visuallyHidden]: visuallyHidden,
})}
style={{ padding: column.styles?.padding }}
onClick={onSortClick}
>
<div className={styles.columnName}>
<span>{column.name}</span>
{column.tooltip && !sortActive ? (
<div className={styles.iconWrapper}>
<Icon type={IconType.Info} theme={IconTheme.Neutral} />
</div>
) : null}
{sortActive ? (
<div
className={classNames(styles.iconWrapper, {
[styles.ascending]:
sortActive && sortSettings?.order === 'asc',
})}
>
<Icon type={IconType.Sort} theme={IconTheme.Neutral} />
</div>
) : null}
</div>
</div>
</button>
</button>
</Tooltip>
</th>
)
}
1 change: 1 addition & 0 deletions ui/src/design-system/components/table/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export enum TextAlign {
export interface TableColumn<T> {
id: string
name: string
tooltip?: string
sortField?: string
styles?: {
textAlign?: TextAlign
Expand Down
1 change: 1 addition & 0 deletions ui/src/pages/jobs/jobs-columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ export const columns: (projectId: string) => TableColumn<Job>[] = (
{
id: 'status',
name: translate(STRING.FIELD_LABEL_STATUS),
tooltip: translate(STRING.TOOLTIP_STATUS),
sortField: 'status',
renderCell: (item: Job) => {
const status = (() => {
Expand Down
1 change: 1 addition & 0 deletions ui/src/pages/jobs/jobs.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export const Jobs = () => {
})}
isLoading={isLoading}
isFetching={isFetching}
tooltip={translate(STRING.TOOLTIP_JOB)}
>
{canCreate ? <NewJobDialog /> : null}
</PageHeader>
Expand Down
46 changes: 26 additions & 20 deletions ui/src/utils/language.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,10 +180,12 @@ export enum STRING {
TOOLTIP_COLLECTION,
TOOLTIP_DEPLOYMENT,
TOOLTIP_DEVICE_TYPE,
TOOLTIP_JOB,
TOOLTIP_OCCURRENCE,
TOOLTIP_PIPELINE,
TOOLTIP_SESSION,
TOOLTIP_SITE,
TOOLTIP_STATUS,
TOOLTIP_STORAGE,

/* OTHER */
Expand Down Expand Up @@ -265,16 +267,16 @@ const ENGLISH_STRINGS: { [key in STRING]: string } = {
/* FIELD_LABEL */
[STRING.FIELD_LABEL_AVG_TEMP]: 'Avg temp',
[STRING.FIELD_LABEL_BEST_SCORE]: 'Best score',
[STRING.FIELD_LABEL_CAPTURES]: 'Captures',
[STRING.FIELD_LABEL_CAPTURES]: 'Source images',
[STRING.FIELD_LABEL_COMMENT]: 'Comment',
[STRING.FIELD_LABEL_CONNECTION_STATUS]: 'Connection status',
[STRING.FIELD_LABEL_CREATED_AT]: 'Created at',
[STRING.FIELD_LABEL_DATA_SOURCE_CAPTURES]: 'Data source captures',
[STRING.FIELD_LABEL_DATA_SOURCE_CAPTURES]: 'Data source images',
[STRING.FIELD_LABEL_DATE]: 'Date',
[STRING.FIELD_LABEL_DATE_OBSERVED]: 'Date observed',
[STRING.FIELD_LABEL_DELAY]: 'Delay',
[STRING.FIELD_LABEL_DEVICE]: 'Device type',
[STRING.FIELD_LABEL_DEPLOYMENT]: 'Deployment',
[STRING.FIELD_LABEL_DEPLOYMENT]: 'Station',
[STRING.FIELD_LABEL_DESCRIPTION]: 'Description',
[STRING.FIELD_LABEL_DETECTIONS]: 'Detections',
[STRING.FIELD_LABEL_DURATION]: 'Duration',
Expand Down Expand Up @@ -309,7 +311,7 @@ const ENGLISH_STRINGS: { [key in STRING]: string } = {
[STRING.FIELD_LABEL_SOURCE_IMAGE]: 'Source image',
[STRING.FIELD_LABEL_SOURCE_IMAGES]: 'Source images',
[STRING.FIELD_LABEL_DATA_SOURCE]: 'Data source',
[STRING.FIELD_LABEL_SAMPLE_CAPTURES]: 'Sample captures',
[STRING.FIELD_LABEL_SAMPLE_CAPTURES]: 'Sample images',
[STRING.FIELD_LABEL_SCORE]: 'Score',
[STRING.FIELD_LABEL_SNAPSHOTS]: 'Snapshots',
[STRING.FIELD_LABEL_SPECIES]: 'Species',
Expand All @@ -325,7 +327,7 @@ const ENGLISH_STRINGS: { [key in STRING]: string } = {
[STRING.FIELD_LABEL_TRAINING_IMAGES]: 'Reference images',
[STRING.FIELD_LABEL_FIRST_DATE]: 'First date',
[STRING.FIELD_LABEL_LAST_DATE]: 'Last date',
[STRING.FIELD_LABEL_UPLOAD_CAPTURES]: 'Upload captures',
[STRING.FIELD_LABEL_UPLOAD_CAPTURES]: 'Upload images',
[STRING.FIELD_LABEL_UPDATED_AT]: 'Updated at',
[STRING.FIELD_LABEL_VERSION]: 'Version',
[STRING.FIELD_LABEL_VERSION_NAME]: 'Version',
Expand All @@ -335,8 +337,8 @@ const ENGLISH_STRINGS: { [key in STRING]: string } = {
[STRING.ENTITY_DELETE]: 'Delete {{type}}',
[STRING.ENTITY_DETAILS]: '{{type}} details',
[STRING.ENTITY_EDIT]: 'Edit {{type}}',
[STRING.ENTITY_TYPE_CAPTURE]: 'capture',
[STRING.ENTITY_TYPE_DEPLOYMENT]: 'deployment',
[STRING.ENTITY_TYPE_CAPTURE]: 'source image',
[STRING.ENTITY_TYPE_DEPLOYMENT]: 'station',
[STRING.ENTITY_TYPE_IDENTIFICATION]: 'identification',
[STRING.ENTITY_TYPE_PIPELINE]: 'pipeline',
[STRING.ENTITY_TYPE_JOB]: 'job',
Expand All @@ -347,18 +349,18 @@ const ENGLISH_STRINGS: { [key in STRING]: string } = {
[STRING.MESSAGE_CAPTURE_FILENAME]:
'Image filename must contain a timestamp in the format YYYYMMDDHHMMSS (e.g. 20210101120000-snapshot.jpg).',
[STRING.MESSAGE_CAPTURE_LIMIT]:
'A maximum of {{numCaptures}} captures can be uploaded through the web browser. Configure a data source to upload data in bulk.',
'A maximum of {{numCaptures}} images can be uploaded through the web browser. Configure a data source to upload data in bulk.',
[STRING.MESSAGE_CAPTURE_SYNC_HIDDEN]:
'Deployment must be created before syncing captures.',
'Station must be created before syncing images.',
[STRING.MESSAGE_CAPTURE_TOO_MANY]:
'To upload more than {{numCaptures}} images you must configure a data source.',
[STRING.MESSAGE_CAPTURE_UPLOAD_HIDDEN]:
'Deployment must be created before uploading captures.',
'Station must be created before uploading images.',
[STRING.MESSAGE_CHANGE_PASSWORD]:
'Contact an administrator to change your email or password.',
[STRING.MESSAGE_COULD_NOT_SAVE]: 'Could not save',
[STRING.MESSAGE_DATA_SOURCE_NOT_CONFIGURED]:
'A data source must be configured and saved before syncing captures.',
'A data source must be configured and saved before syncing images.',
[STRING.MESSAGE_DELETE_CONFIRM]:
'Are you sure you want to delete this {{type}}?',
[STRING.MESSAGE_HAS_ACCOUNT]: 'Already have an account?',
Expand All @@ -385,7 +387,7 @@ const ENGLISH_STRINGS: { [key in STRING]: string } = {
[STRING.MESSAGE_VALUE_MISSING]: 'Please provide a value',

/* NAV_ITEM */
[STRING.NAV_ITEM_DEPLOYMENTS]: 'Deployments',
[STRING.NAV_ITEM_DEPLOYMENTS]: 'Stations',
[STRING.NAV_ITEM_JOBS]: 'Jobs',
[STRING.NAV_ITEM_OCCURRENCES]: 'Occurrences',
[STRING.NAV_ITEM_OVERVIEW]: 'Overview',
Expand Down Expand Up @@ -416,21 +418,25 @@ const ENGLISH_STRINGS: { [key in STRING]: string } = {

/* TOOLTIPS */
[STRING.TOOLTIP_COLLECTION]:
'A collection is a group of captures. A collection contains all or some captures in a project. When a processing job is registered, a collection is picked. This list defines the collection options available.',
'A collection is a group of source images. A collection contains all or some images in a project. When a processing job is registered, a collection is picked. This list defines the collection options available.',
[STRING.TOOLTIP_DEPLOYMENT]:
'A deployment is defined as one monitoring station.',
'A monitoring station is a location where a device is deployed to take images of insects in the wild (at a “Site”).',
[STRING.TOOLTIP_DEVICE_TYPE]:
'A device type is the type of equipment used for monitoring, for example an AMI system. One or many deployments can be connected to a device type.',
'A device type is the type of equipment or camera used for collecting source images. One or many deployments can be connected to a device type. Device type refers to the model version, category or description of a kind of hardware, not the serial number of an individual device.',
[STRING.TOOLTIP_JOB]:
'A job is a request for data processing that specifies the data to process and the pipeline to use.',
[STRING.TOOLTIP_OCCURRENCE]:
'An occurrence is a group of detections of one individual. One occurrence consists of one or many detections.',
'An occurrence refers to when an individual is detected in a sequence of one or more images with no time interruption.',
[STRING.TOOLTIP_PIPELINE]:
'A pipeline is a set of algorithms used for processing. When a processing job is registered, a pipeline is picked. This list defines the pipeline options available.',
'A pipeline is a set of algorithms used for processing. A pipeline is picked from a list of algorithm bundle options when a processing job is defined.',
[STRING.TOOLTIP_SESSION]:
'A session is a fixed period of time of monitoring for one deployment. The period is typically one night.',
'A session is a fixed period of time of monitoring for one station. The period is typically one night.',
[STRING.TOOLTIP_SITE]:
'A site is a physical place where monitoring is taking place. One or many deployments can be connected to a site.',
'A site is a physical location where monitoring is taking place. One or many stations can be connected to a site.',
[STRING.TOOLTIP_STATUS]:
'A status is the processing stage of a job once submitted: Created > Pending > Running > Done. A Failed status means the job stopped before it had finished.',
[STRING.TOOLTIP_STORAGE]:
'A storage is a place where captures are kept, for example a S3 bucket. One or many deployments can be connected to a storage.',
'A storage is a place where source images are kept, for example a S3 bucket. One or many stations can be connected to a storage.',

/* OTHER */
[STRING.ALGORITHMS]: 'Algorithms',
Expand Down
4 changes: 2 additions & 2 deletions ui/src/utils/useFilters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ import { useSearchParams } from 'react-router-dom'

const AVAILABLE_FILTERS = [
{
label: 'Deployment',
label: 'Station',
field: 'deployment',
},
{
label: 'Occurrence deployment',
label: 'Occurrence station',
field: 'occurrences__deployment',
},
{
Expand Down