Skip to content

Commit 794c434

Browse files
authored
Merge pull request #1217 from RolnickLab/chore/ui-cleanup
UI cleanup (part 4)
2 parents 83a970c + 7478302 commit 794c434

20 files changed

Lines changed: 299 additions & 142 deletions

File tree

ui/src/components/form/layout/layout.module.scss

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -57,23 +57,8 @@
5757
gap: 16px;
5858
}
5959

60-
.formMessage {
61-
width: 100%;
62-
@include paragraph-small();
63-
padding: 8px 16px;
64-
background-color: $color-success-100;
65-
color: $color-success-700;
66-
box-sizing: border-box;
67-
border-radius: 6px;
68-
}
69-
7060
.formError {
71-
width: 100%;
72-
@include paragraph-small();
7361
padding: 8px 32px;
74-
background-color: $color-destructive-100;
75-
color: $color-destructive-600;
76-
box-sizing: border-box;
7762

7863
&.inDialog {
7964
position: sticky;

ui/src/components/form/layout/layout.tsx

Lines changed: 41 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,47 @@
11
import classNames from 'classnames'
2+
import { CircleAlert, InfoIcon, LightbulbIcon } from 'lucide-react'
23
import { CSSProperties, ReactNode } from 'react'
34
import styles from './layout.module.scss'
45

56
export const FormMessage = ({
6-
intro,
7+
children,
8+
className,
79
message,
8-
style,
10+
theme = 'success',
11+
withIcon,
912
}: {
10-
intro?: string
13+
className?: string
1114
message: string
12-
style?: CSSProperties
13-
}) => (
14-
<div className={styles.formMessage} style={style}>
15-
{intro ? <span className={styles.intro}>{intro}: </span> : null}
16-
<span>{message}</span>
17-
</div>
18-
)
15+
theme?: 'success' | 'warning' | 'destructive'
16+
withIcon?: boolean
17+
children?: ReactNode
18+
}) => {
19+
const Icon = {
20+
success: LightbulbIcon,
21+
warning: InfoIcon,
22+
destructive: CircleAlert,
23+
}[theme]
24+
25+
return (
26+
<div
27+
className={classNames(
28+
'px-4 py-2 rounded-md body-small',
29+
{
30+
'bg-[#d8f2ec] text-[#078c6e]': theme === 'success',
31+
'bg-warning-50 text-warning-700': theme === 'warning',
32+
'bg-destructive-50 text-destructive-700': theme === 'destructive',
33+
},
34+
className
35+
)}
36+
>
37+
<span>
38+
{withIcon ? <Icon className="inline w-4 h-4 mr-2" /> : null}
39+
{message}
40+
</span>
41+
{children}
42+
</div>
43+
)
44+
}
1945

2046
export const FormError = ({
2147
inDialog,
@@ -29,7 +55,11 @@ export const FormError = ({
2955
style?: CSSProperties
3056
}) => (
3157
<div
32-
className={classNames(styles.formError, { [styles.inDialog]: inDialog })}
58+
className={classNames(
59+
styles.formError,
60+
'w-full bg-destructive-50 text-destructive-700 body-small',
61+
{ [styles.inDialog]: inDialog }
62+
)}
3363
style={style}
3464
>
3565
{intro ? <span className={styles.intro}>{intro}: </span> : null}

ui/src/design-system/components/button/docs-link.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { BookOpenIcon } from 'lucide-react'
1+
import { BookOpenIcon, ChevronRight } from 'lucide-react'
22
import { buttonVariants } from 'nova-ui-kit'
33
import { STRING, translate } from 'utils/language'
44
import { BasicTooltip } from '../tooltip/basic-tooltip'
@@ -24,8 +24,14 @@ export const DocsLink = ({
2424
rel="noreferrer"
2525
target="_blank"
2626
>
27-
<BookOpenIcon className="w-4 h-4" />
28-
{isCompact ? null : <span>{translate(STRING.VIEW_DOCS)}</span>}
27+
{isCompact ? (
28+
<BookOpenIcon className="w-4 h-4" />
29+
) : (
30+
<>
31+
<span>{translate(STRING.VIEW_DOCS)}</span>
32+
<ChevronRight className="w-4 h-4" />
33+
</>
34+
)}
2935
</a>
3036
</BasicTooltip>
3137
)

ui/src/design-system/components/info-tooltip.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@ export const InfoTooltip = (props: {
1818
<InfoIcon className="w-4 h-4" />
1919
</Button>
2020
</Tooltip.Trigger>
21-
<Tooltip.Content side="bottom" className="p-4 max-w-xs">
22-
<Info {...props} />
23-
</Tooltip.Content>
21+
<Tooltip.Portal>
22+
<Tooltip.Content side="bottom" className="p-4 max-w-xs">
23+
<Info {...props} />
24+
</Tooltip.Content>
25+
</Tooltip.Portal>
2426
</Tooltip.Root>
2527
</Tooltip.Provider>
2628
)
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
import { FormMessage } from 'components/form/layout/layout'
2+
import { useCaptureSets } from 'data-services/hooks/capture-sets/useCaptureSets'
3+
import { ChevronRight, XIcon } from 'lucide-react'
4+
import { Button, Select } from 'nova-ui-kit'
5+
import { Link, useParams } from 'react-router-dom'
6+
import { APP_ROUTES } from 'utils/constants'
7+
import { STRING, translate } from 'utils/language'
8+
9+
export const CaptureSetPicker = ({
10+
clearable,
11+
value: _value,
12+
onValueChange,
13+
}: {
14+
clearable?: boolean
15+
value?: string
16+
onValueChange: (value?: string) => void
17+
}) => {
18+
const { projectId } = useParams()
19+
const { captureSets = [], isLoading } = useCaptureSets({
20+
projectId: projectId as string,
21+
})
22+
const captureSet = captureSets.find((c) => c.id === _value)
23+
const value = captureSet ? _value : ''
24+
25+
return (
26+
<div className="flex flex-col gap-4">
27+
<div className="flex items-center justify-between gap-2">
28+
<Select.Root
29+
key={value}
30+
disabled={isLoading || captureSets.length === 0}
31+
onValueChange={onValueChange}
32+
value={value}
33+
>
34+
<Select.Trigger loading={isLoading}>
35+
<Select.Value placeholder={translate(STRING.SELECT_PLACEHOLDER)} />
36+
</Select.Trigger>
37+
<Select.Content className="max-h-72">
38+
{captureSets.map((c) => (
39+
<Select.Item key={c.id} value={c.id}>
40+
{c.name}
41+
</Select.Item>
42+
))}
43+
</Select.Content>
44+
</Select.Root>
45+
{clearable && _value && (
46+
<Button
47+
aria-label={translate(STRING.CLEAR)}
48+
className="shrink-0 text-muted-foreground"
49+
onClick={() => onValueChange()}
50+
size="icon"
51+
variant="ghost"
52+
>
53+
<XIcon className="w-4 h-4" />
54+
</Button>
55+
)}
56+
</div>
57+
{captureSet?.numImages !== undefined ? (
58+
captureSet.numImages === 0 ? (
59+
<div className="flex flex-col gap-4">
60+
<FormMessage
61+
className="flex justify-between gap-4"
62+
message={translate(STRING.MESSAGE_CAPTURE_SET_EMPTY)}
63+
theme="warning"
64+
withIcon
65+
>
66+
{captureSet.canPopulate ? (
67+
<Link
68+
className="font-bold"
69+
to={APP_ROUTES.CAPTURE_SETS({
70+
projectId: projectId as string,
71+
})}
72+
>
73+
<span>{translate(STRING.POPULATE)}</span>
74+
<ChevronRight className="inline w-4 h-4 ml-2" />
75+
</Link>
76+
) : null}
77+
</FormMessage>
78+
</div>
79+
) : (
80+
<FormMessage
81+
message={translate(STRING.MESSAGE_CAPTURE_SET_COUNT, {
82+
total: captureSet.numImages.toLocaleString(),
83+
})}
84+
withIcon
85+
/>
86+
)
87+
) : null}
88+
</div>
89+
)
90+
}

ui/src/pages/captures/capture-columns.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export const columns = ({
4747
},
4848
{
4949
id: 'timestamp',
50-
name: translate(STRING.FIELD_LABEL_TIMESTAMP),
50+
name: translate(STRING.FIELD_LABEL_CAPTURE),
5151
sortField: 'timestamp',
5252
renderCell: (item: Capture) => {
5353
const detailsRoute = item.sessionId
@@ -67,6 +67,7 @@ export const columns = ({
6767
<Link to={detailsRoute}>
6868
<BasicTableCell
6969
value={item.dateTimeLabel}
70+
details={[`${translate(STRING.FIELD_LABEL_ID)}: ${item.id}`]}
7071
theme={CellTheme.Primary}
7172
/>
7273
</Link>

ui/src/pages/deployment-details/deployment-details-form/deployment-details-form.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,15 @@ export const DeploymentDetailsForm = ({
5151
latitude: deployment.latitude,
5252
longitude: deployment.longitude,
5353
},
54-
isValid: startValid,
54+
isValid: true,
5555
},
5656
[Section.SourceImages]: {
5757
values: {
5858
dataSourceId: deployment.dataSource?.id,
5959
dataSourceSubdir: deployment.dataSourceSubdir,
6060
dataSourceRegex: deployment.dataSourceRegex,
6161
},
62-
isValid: startValid,
62+
isValid: true,
6363
},
6464
}}
6565
>

ui/src/pages/deployment-details/deployment-details-info.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ export const DeploymentDetailsInfo = ({
4444
</Dialog.Header>
4545
<div className={styles.content}>
4646
<FormSection title={translate(STRING.FIELD_LABEL_GENERAL)}>
47+
<FormRow>
48+
<InputValue
49+
label={translate(STRING.FIELD_LABEL_ID)}
50+
value={deployment.id}
51+
/>
52+
</FormRow>
4753
<FormRow>
4854
<InputValue
4955
label={translate(STRING.FIELD_LABEL_NAME)}

ui/src/pages/deployments/deployment-columns.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,11 @@ export const columns = ({
5454
keepSearchParams: true,
5555
})}
5656
>
57-
<BasicTableCell value={item.name} theme={CellTheme.Primary} />
57+
<BasicTableCell
58+
value={item.name}
59+
details={[`${translate(STRING.FIELD_LABEL_ID)}: ${item.id}`]}
60+
theme={CellTheme.Primary}
61+
/>
5862
</Link>
5963
),
6064
},

ui/src/pages/job-details/job-details-form/job-details-form.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,22 @@ import { FormField } from 'components/form/form-field'
33
import {
44
FormActions,
55
FormError,
6+
FormMessage,
67
FormRow,
78
FormSection,
89
} from 'components/form/layout/layout'
910
import { FormConfig } from 'components/form/types'
1011
import { API_ROUTES } from 'data-services/constants'
1112
import { useProjectDetails } from 'data-services/hooks/projects/useProjectDetails'
13+
import { DocsLink } from 'design-system/components/button/docs-link'
1214
import { SaveButton } from 'design-system/components/button/save-button'
1315
import { Checkbox } from 'design-system/components/checkbox/checkbox'
1416
import { InputContent } from 'design-system/components/input/input'
17+
import { CaptureSetPicker } from 'design-system/components/select/capture-set-picker'
1518
import { EntityPicker } from 'design-system/components/select/entity-picker'
1619
import { useForm } from 'react-hook-form'
1720
import { useParams } from 'react-router-dom'
18-
import { APP_ROUTES } from 'utils/constants'
21+
import { APP_ROUTES, DOCS_LINKS } from 'utils/constants'
1922
import { STRING, translate } from 'utils/language'
2023
import { useFormError } from 'utils/useFormError'
2124

@@ -96,14 +99,16 @@ export const JobDetailsForm = ({
9699
intro={translate(STRING.MESSAGE_COULD_NOT_SAVE)}
97100
message={errorMessage}
98101
/>
99-
) : (
100-
<FormError
101-
inDialog
102-
intro="Warning"
103-
message="Batch processing is currently in development and problems are likely to occur. If you need data processed, we recommend to reach out to the team for support. Thank you for your patience!"
104-
/>
105-
)}
102+
) : null}
106103
<FormSection>
104+
<div className="flex flex-col items-end gap-4">
105+
<FormMessage
106+
message="Batch processing is currently in development and problems are likely to occur. If you need data processed, we recommend to reach out to the team for support. Thank you for your patience!"
107+
theme="warning"
108+
withIcon
109+
/>
110+
<DocsLink href={DOCS_LINKS.PROCESSING_DATA} />
111+
</div>
107112
<FormRow>
108113
<FormField
109114
name="name"
@@ -142,8 +147,7 @@ export const JobDetailsForm = ({
142147
},
143148
}}
144149
>
145-
<EntityPicker
146-
collection={API_ROUTES.CAPTURE_SETS}
150+
<CaptureSetPicker
147151
onValueChange={field.onChange}
148152
value={field.value}
149153
/>

0 commit comments

Comments
 (0)