Skip to content

Commit 5d2c6bf

Browse files
authored
ITEP-32025 Improve FuxNotification component [PART 1] (#673)
1 parent d637fb5 commit 5d2c6bf

12 files changed

Lines changed: 348 additions & 112 deletions

web_ui/src/core/user-settings/dtos/user-settings.interface.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export enum FUX_NOTIFICATION_KEYS {
4848
ANNOTATOR_CONTINUE_ANNOTATING = 'annotatorContinueAnnotating',
4949
AUTO_TRAINING_MODAL = 'autoTrainingCreditModal',
5050
AUTO_TRAINING_NOTIFICATION = 'autoTrainingCreditNotification',
51+
CREDIT_BALANCE_BUTTON = 'creditBalanceButton',
5152
}
5253

5354
export enum FUX_SETTINGS_KEYS {

web_ui/src/core/user-settings/utils.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,9 @@ export const initialFuxNotificationsConfig: FuxNotificationsConfig = {
184184
[FUX_NOTIFICATION_KEYS.AUTO_TRAINING_NOTIFICATION]: {
185185
isEnabled: false,
186186
},
187+
[FUX_NOTIFICATION_KEYS.CREDIT_BALANCE_BUTTON]: {
188+
isEnabled: false,
189+
},
187190
};
188191

189192
export const initialFuxSettingsConfig: FuxSettingsConfig = {
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
// Copyright (C) 2022-2025 Intel Corporation
2+
// LIMITED EDGE SOFTWARE DISTRIBUTION LICENSE
3+
4+
import { ComponentProps, MutableRefObject, ReactNode } from 'react';
5+
6+
import { ActionButton, Button, CustomPopover, Divider, Flex, Popover, Text, View } from '@geti/ui';
7+
import { Close } from '@geti/ui/icons';
8+
import { isFunction } from 'lodash-es';
9+
10+
import { FUX_NOTIFICATION_KEYS } from '../../../core/user-settings/dtos/user-settings.interface';
11+
import { useDocsUrl } from '../../../hooks/use-docs-url/use-docs-url.hook';
12+
import { openNewTab } from '../../utils';
13+
import { onPressLearnMore } from '../tutorials/utils';
14+
import { getFuxNotificationData } from './utils';
15+
16+
import classes from './fux-notification.module.scss';
17+
18+
interface CustomPopoverProps extends Omit<ComponentProps<typeof Popover>, 'triggerRef' | 'children'> {
19+
settingsKey: FUX_NOTIFICATION_KEYS;
20+
customDocUrl?: string;
21+
children?: ReactNode;
22+
triggerRef: MutableRefObject<null>;
23+
onClose?: () => void;
24+
}
25+
26+
export const FuxNotification = ({
27+
settingsKey,
28+
customDocUrl,
29+
placement,
30+
triggerRef,
31+
state,
32+
onClose,
33+
children,
34+
}: CustomPopoverProps) => {
35+
const { description, showDismissAll, docUrl } = getFuxNotificationData(settingsKey);
36+
const message = children ? children : description;
37+
const url = useDocsUrl();
38+
const newDocUrl = customDocUrl ?? (docUrl && `${url}${docUrl}`) ?? undefined;
39+
if (!showDismissAll) {
40+
return (
41+
<CustomPopover
42+
ref={triggerRef}
43+
hideArrow={false}
44+
placement={placement}
45+
state={state}
46+
UNSAFE_className={classes.container}
47+
isKeyboardDismissDisabled
48+
>
49+
<View UNSAFE_className={classes.dialogWrapper}>
50+
<Text UNSAFE_className={classes.dialogDescription}>{message}</Text>
51+
{newDocUrl && (
52+
<Button
53+
variant='primary'
54+
id={`${settingsKey}-learn-more-button-id`}
55+
onPress={() => {
56+
onPressLearnMore(newDocUrl);
57+
}}
58+
marginStart={'size-300'}
59+
UNSAFE_style={{ border: 'none' }}
60+
>
61+
Learn more
62+
</Button>
63+
)}
64+
65+
<Divider orientation='vertical' size='S' UNSAFE_className={classes.fuxDivider} />
66+
<ActionButton
67+
isQuiet
68+
onPress={() => {
69+
state.close();
70+
isFunction(onClose) && onClose();
71+
}}
72+
aria-label={'Dismiss help dialog'}
73+
UNSAFE_className={classes.close}
74+
>
75+
<Close />
76+
</ActionButton>
77+
</View>
78+
</CustomPopover>
79+
);
80+
}
81+
// todo: not implemented anywhere yet, to do in next PR
82+
return (
83+
<CustomPopover
84+
ref={triggerRef}
85+
hideArrow={false}
86+
placement={placement}
87+
state={state}
88+
UNSAFE_className={classes.container}
89+
isKeyboardDismissDisabled
90+
>
91+
<Flex direction={'row'} gap={'size-200'} alignItems={'center'}>
92+
<Text order={1}>{children}</Text>
93+
94+
{newDocUrl && (
95+
<Button
96+
order={2}
97+
variant='primary'
98+
UNSAFE_style={{ border: 'none' }}
99+
onPress={() => openNewTab(newDocUrl)}
100+
>
101+
Learn more
102+
</Button>
103+
)}
104+
105+
<Divider order={3} orientation='vertical' size='S' UNSAFE_className={classes.divider} />
106+
107+
<ActionButton
108+
isQuiet
109+
order={4}
110+
onPress={() => {
111+
state.close();
112+
isFunction(onClose) && onClose();
113+
}}
114+
aria-label={'close first user experience notification'}
115+
UNSAFE_className={classes.close}
116+
>
117+
<Close />
118+
</ActionButton>
119+
</Flex>
120+
</CustomPopover>
121+
);
122+
};
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
@use '../../shared.module';
2+
3+
.container {
4+
@extend .blueGreenGradient;
5+
padding: var(--spectrum-global-dimension-size-200);
6+
7+
& [class*='tip-triangle'] {
8+
fill: #89ad4e !important;
9+
}
10+
11+
margin-top: var(--spectrum-global-dimension-size-65) !important;
12+
min-width: var(--spectrum-global-dimension-size-4600) !important;
13+
}
14+
15+
.dialogWrapper {
16+
display: flex;
17+
height: auto;
18+
border-radius: var(--spectrum-alias-border-radius-small) !important;
19+
align-items: center;
20+
21+
.close {
22+
color: var(--spectrum-toast-neutral-text-color) !important;
23+
24+
&:hover,
25+
&:focus,
26+
&[class*='is-active'] {
27+
background-color: var(--brand-opacity-white-12) !important;
28+
border-color: var(--spectrum-toast-neutral-text-color) !important;
29+
}
30+
}
31+
32+
.fuxDivider {
33+
background-color: var(--brand-opacity-white-12);
34+
margin: 0 var(--spectrum-global-dimension-size-150);
35+
}
36+
}
37+
38+
.fuxHeader {
39+
margin-block-end: var(--spectrum-global-dimension-size-100);
40+
width: 100%;
41+
justify-content: space-between;
42+
align-items: start;
43+
44+
.header {
45+
font-size: var(--spectrum-global-dimension-font-size-400);
46+
font-weight: 500;
47+
width: 85%;
48+
}
49+
50+
.steps {
51+
font-size: var(--spectrum-global-dimension-font-size-50);
52+
}
53+
}
54+
55+
.dialogButtonGroup {
56+
margin-top: var(--spectrum-global-dimension-size-200) !important;
57+
margin-bottom: var(--spectrum-global-dimension-size-0) !important;
58+
width: 100%;
59+
display: flex;
60+
justify-content: space-between;
61+
62+
button {
63+
border: var(--spectrum-alias-border-size-thin) solid !important;
64+
margin-inline-start: 0 !important;
65+
}
66+
67+
.moreMenu {
68+
color: var(--spectrum-toast-neutral-text-color) !important;
69+
border: none !important;
70+
71+
&:hover,
72+
&:focus,
73+
&[class*='is-active'] {
74+
background-color: var(--brand-opacity-white-12) !important;
75+
}
76+
}
77+
78+
.backButton {
79+
border: none !important;
80+
min-inline-size: 32px !important;
81+
padding-left: var(--spectrum-global-dimension-size-50) !important;
82+
padding-right: var(--spectrum-global-dimension-size-50) !important;
83+
}
84+
}
85+
86+
.dialogDescription {
87+
white-space: pre-line;
88+
}
Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
// Copyright (C) 2022-2025 Intel Corporation
2+
// LIMITED EDGE SOFTWARE DISTRIBUTION LICENSE
3+
4+
import { FUX_NOTIFICATION_KEYS } from '../../../core/user-settings/dtos/user-settings.interface';
5+
import { DocsUrl } from '../tutorials/utils';
6+
7+
interface FuxNotificationData {
8+
header: string | undefined;
9+
description: string;
10+
docUrl: DocsUrl | undefined;
11+
nextStepId: FUX_NOTIFICATION_KEYS | undefined;
12+
previousStepId: FUX_NOTIFICATION_KEYS | undefined;
13+
showDismissAll: boolean;
14+
}
15+
16+
export const getFuxNotificationData = (fuxNotificationId: string): FuxNotificationData => {
17+
switch (fuxNotificationId) {
18+
case FUX_NOTIFICATION_KEYS.ANNOTATE_INTERACTIVELY:
19+
return {
20+
header: '',
21+
description: 'Click here to start annotating your dataset.',
22+
docUrl: DocsUrl.ACTIVE_LEARNING,
23+
nextStepId: undefined,
24+
previousStepId: undefined,
25+
showDismissAll: false,
26+
};
27+
case FUX_NOTIFICATION_KEYS.ANNOTATOR_AUTO_TRAINING_STARTED:
28+
return {
29+
header: '',
30+
description:
31+
`This auto-training job is scheduled and ready to start when resources are available.` +
32+
` Click the 'bell' icon to see the training progress.`,
33+
docUrl: undefined,
34+
nextStepId: undefined,
35+
previousStepId: undefined,
36+
showDismissAll: false,
37+
};
38+
case FUX_NOTIFICATION_KEYS.ANNOTATOR_TOOLS:
39+
return {
40+
header: 'How to annotate my data?',
41+
description:
42+
`Effective data annotation lays the groundwork for a model’s ability to accurately interpret and` +
43+
` learn from information. By annotating your data, you teach the model what patterns to ` +
44+
`recognize. Intel® Geti™ provides you with various smart annotation assistants to accelerate` +
45+
` this annotation process.`,
46+
docUrl: DocsUrl.ANNOTATION_TOOLS,
47+
nextStepId: FUX_NOTIFICATION_KEYS.ANNOTATOR_ACTIVE_SET,
48+
previousStepId: undefined,
49+
showDismissAll: true,
50+
};
51+
case FUX_NOTIFICATION_KEYS.ANNOTATOR_ACTIVE_SET:
52+
return {
53+
header: `What’s Active set?`,
54+
description:
55+
`In the media gallery you can switch between Active set and Dataset. Active set is set by default` +
56+
` in Intel® Geti™ and it displays the media items in an order that is optimal for creating a ` +
57+
`well-balanced model, based on their informative features compared to the rest of your dataset.` +
58+
` However, you can switch to Dataset to display the media items in the order that was arranged ` +
59+
`in your dataset folder.`,
60+
docUrl: DocsUrl.MEDIA_GALLERY,
61+
nextStepId: undefined,
62+
previousStepId: FUX_NOTIFICATION_KEYS.ANNOTATOR_TOOLS,
63+
showDismissAll: true,
64+
};
65+
case FUX_NOTIFICATION_KEYS.ANNOTATOR_SUCCESSFULLY_TRAINED:
66+
return {
67+
header: 'Your model has been successfully trained',
68+
description: '',
69+
docUrl: DocsUrl.MODELS,
70+
nextStepId: FUX_NOTIFICATION_KEYS.ANNOTATOR_CHECK_PREDICTIONS,
71+
previousStepId: undefined,
72+
showDismissAll: true,
73+
};
74+
case FUX_NOTIFICATION_KEYS.ANNOTATOR_CHECK_PREDICTIONS:
75+
return {
76+
header: 'Check predictions',
77+
description:
78+
`Click here to review the accuracy of your model by comparing the model’s predictions against` +
79+
` your original annotations. Use our tools to analyze and adjust the predictions as needed, so` +
80+
` they can be used during the next training rounds. This will help to further improve your ` +
81+
`model's performance.`,
82+
docUrl: DocsUrl.TESTS,
83+
nextStepId: undefined,
84+
previousStepId: FUX_NOTIFICATION_KEYS.ANNOTATOR_SUCCESSFULLY_TRAINED,
85+
showDismissAll: true,
86+
};
87+
case FUX_NOTIFICATION_KEYS.ANNOTATOR_CONTINUE_ANNOTATING:
88+
return {
89+
header: 'Continue annotating',
90+
description: '',
91+
docUrl: DocsUrl.ANNOTATION_EDITOR,
92+
nextStepId: undefined,
93+
previousStepId: undefined,
94+
showDismissAll: true,
95+
};
96+
case FUX_NOTIFICATION_KEYS.CREDIT_BALANCE_BUTTON:
97+
return {
98+
header: '',
99+
description: '',
100+
docUrl: undefined,
101+
nextStepId: undefined,
102+
previousStepId: undefined,
103+
showDismissAll: false,
104+
};
105+
default:
106+
return {
107+
header: '',
108+
description: '',
109+
docUrl: undefined,
110+
previousStepId: undefined,
111+
nextStepId: undefined,
112+
showDismissAll: true,
113+
};
114+
}
115+
};

web_ui/src/shared/components/header/credit-balance/credit-balance-button.component.tsx

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
import { forwardRef, useEffect, useRef } from 'react';
55

66
import { paths } from '@geti/core';
7-
import { useUsers } from '@geti/core/src/users/hook/use-users.hook';
87
import { ActionButton, Tooltip, TooltipTrigger, type FocusableRef } from '@geti/ui';
98
import { CreditCard } from '@geti/ui/icons';
109
import { isNil } from 'lodash-es';
@@ -18,8 +17,10 @@ import { useOrganizationIdentifier } from '../../../../hooks/use-organization-id
1817
import { usePrevious } from '../../../../hooks/use-previous/use-previous.hook';
1918
import { useProject } from '../../../../pages/project-details/providers/project-provider/project-provider.component';
2019
import { ONE_MINUTE } from '../../../utils';
20+
import { FuxNotification } from '../../fux-notification/fux-notification.component';
21+
import { useCheckPermission } from '../../has-permission/has-permission.component';
22+
import { OPERATION } from '../../has-permission/has-permission.interface';
2123
import { CreditsToConsume } from './credits-to-consume.component';
22-
import { FuxNotification } from './fux-notification/fux-notification.component';
2324
import { isBalanceLow } from './util';
2425

2526
import classes from './credit-balance.module.scss';
@@ -83,11 +84,10 @@ const CreditBalanceButtonFuxNotification = ({ isDarkMode }: { isDarkMode: boolea
8384
const firstAutoTrainedProject = settings.config[FUX_SETTINGS_KEYS.FIRST_AUTOTRAINED_PROJECT_ID].value;
8485
const prevFuxEnabled = usePrevious(isFuxNotificationEnabled);
8586

86-
const { useActiveUser } = useUsers();
8787
const { organizationId } = useOrganizationIdentifier();
88-
const { data: activeUser } = useActiveUser(organizationId);
8988
const { project } = useProject();
9089
const isFirstAutoTrainedProject = firstAutoTrainedProject === project.id;
90+
const canCheckUsageTab = useCheckPermission([OPERATION.USAGE_TAB]);
9191

9292
useEffect(() => {
9393
if (isFuxNotificationEnabled && prevFuxEnabled !== isFuxNotificationEnabled) {
@@ -117,14 +117,15 @@ const CreditBalanceButtonFuxNotification = ({ isDarkMode }: { isDarkMode: boolea
117117

118118
{isFirstAutoTrainedProject ? (
119119
<FuxNotification
120+
settingsKey={FUX_NOTIFICATION_KEYS.CREDIT_BALANCE_BUTTON}
120121
state={fuxState}
121-
docUrl={activeUser?.isAdmin ? paths.account.usage({ organizationId }) : undefined}
122+
customDocUrl={canCheckUsageTab ? paths.account.usage({ organizationId }) : undefined}
122123
triggerRef={triggerRef}
123124
placement={'bottom right'}
124125
onClose={handleCloseTrainingNotification}
125126
>
126127
The auto-training job has been started, <CreditsToConsume /> credits deducted.
127-
{activeUser?.isAdmin ? ' Check your credit balance here.' : null}
128+
{canCheckUsageTab ? ' Check your credit balance here.' : null}
128129
</FuxNotification>
129130
) : (
130131
<></>

0 commit comments

Comments
 (0)