Skip to content

Commit 10643d7

Browse files
committed
feat[FR-738]: add folder invitation badge
1 parent 6bd0644 commit 10643d7

25 files changed

Lines changed: 151 additions & 13 deletions

react/src/components/BAIPanelItem.tsx

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import React, { ReactNode } from 'react';
66

77
interface BAIPanelItemProps {
88
title: ReactNode | string;
9-
value: string | number;
9+
value: ReactNode | string | number;
1010
unit?: string;
1111
percent?: number;
1212
color?: string;
@@ -54,15 +54,19 @@ const BAIPanelItem: React.FC<BAIPanelItemProps> = ({
5454
title
5555
)}
5656
<Flex align="baseline" gap={'xxs'}>
57-
<Typography.Text
58-
strong
59-
style={{
60-
fontSize: token.fontSizeHeading1,
61-
color: color ?? token.Layout?.headerBg,
62-
}}
63-
>
64-
{value}
65-
</Typography.Text>
57+
{_.isString(value) || _.isNumber(value) ? (
58+
<Typography.Text
59+
strong
60+
style={{
61+
fontSize: token.fontSizeHeading1,
62+
color: color ?? token.Layout?.headerBg,
63+
}}
64+
>
65+
{value}
66+
</Typography.Text>
67+
) : (
68+
value
69+
)}
6670
{unit && <Typography.Text>{unit}</Typography.Text>}
6771
</Flex>
6872
{_.isNumber(percent) && (

react/src/components/StorageStatusPanelCard.tsx

Lines changed: 54 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,31 @@
11
import { useSuspendedBackendaiClient } from '../hooks';
2+
import { useVFolderInvitations } from '../hooks/backendai';
23
import { useSuspenseTanQuery } from '../hooks/reactQueryAlias';
34
import { useCurrentProjectValue } from '../hooks/useCurrentProject';
45
import BAICard, { BAICardProps } from './BAICard';
56
import BAIPanelItem from './BAIPanelItem';
67
import { StorageStatusPanelCardQuery } from './__generated__/StorageStatusPanelCardQuery.graphql';
7-
import { Col, Row, theme } from 'antd';
8+
import { Badge, Col, Row, theme, Tooltip, Typography } from 'antd';
9+
import { createStyles } from 'antd-style';
810
import graphql from 'babel-plugin-relay/macro';
911
import _ from 'lodash';
1012
import React, { useDeferredValue } from 'react';
1113
import { useTranslation } from 'react-i18next';
1214
import { useLazyLoadQuery } from 'react-relay';
1315

16+
const useStyles = createStyles(({ css, token }) => ({
17+
invitationTooltip: css`
18+
.ant-tooltip-arrow {
19+
right: -${token.sizeXS}px;
20+
bottom: ${token.sizeXS}px;
21+
}
22+
.ant-tooltip-content {
23+
left: ${token.size}px;
24+
bottom: ${token.sizeXS}px;
25+
}
26+
`,
27+
}));
28+
1429
interface StorageStatusPanelProps extends BAICardProps {
1530
fetchKey?: string;
1631
}
@@ -21,9 +36,11 @@ const StorageStatusPanelCard: React.FC<StorageStatusPanelProps> = ({
2136
}) => {
2237
const { t } = useTranslation();
2338
const { token } = theme.useToken();
39+
const { styles } = useStyles();
2440
const baiClient = useSuspendedBackendaiClient();
2541
const currentProject = useCurrentProjectValue();
2642
const deferredFetchKey = useDeferredValue(fetchKey);
43+
const [{ count }] = useVFolderInvitations();
2744

2845
const isExcludedCount = (status: string) => {
2946
return _.includes(
@@ -38,7 +55,6 @@ const StorageStatusPanelCard: React.FC<StorageStatusPanelProps> = ({
3855
return baiClient.vfolder.list(currentProject?.id);
3956
},
4057
});
41-
4258
// FIXME: vfolder_node query does not provide a information about the vfolder's owner.
4359
// So, even if we use fragment, we still need to filter the vfolders by each conditions in client side.
4460
const createdCount = vfolders?.filter(
@@ -118,7 +134,42 @@ const StorageStatusPanelCard: React.FC<StorageStatusPanelProps> = ({
118134
justifyItems: 'center',
119135
}}
120136
>
121-
<BAIPanelItem title={t('data.InvitedFolders')} value={invitedCount} />
137+
<BAIPanelItem
138+
title={
139+
// Add a tag to the Tooltip to make it clickable
140+
// eslint-disable-next-line
141+
<a>
142+
<Tooltip
143+
title={
144+
count > 0
145+
? t('data.InvitedFoldersTooltip', {
146+
count: count,
147+
})
148+
: null
149+
}
150+
rootClassName={styles.invitationTooltip}
151+
placement="topRight"
152+
>
153+
<Badge count={count > 0 ? `+${count}` : null}>
154+
<Typography.Title level={5} style={{ margin: 0 }}>
155+
{t('data.InvitedFolders')}
156+
</Typography.Title>
157+
</Badge>
158+
</Tooltip>
159+
</a>
160+
}
161+
value={
162+
<Typography.Text
163+
strong
164+
style={{
165+
fontSize: token.fontSizeHeading1,
166+
color: token.Layout?.headerBg,
167+
}}
168+
>
169+
{invitedCount}
170+
</Typography.Text>
171+
}
172+
/>
122173
</Col>
123174
</Row>
124175
</BAICard>

react/src/hooks/backendai.tsx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,3 +305,63 @@ export const useAllowedHostNames = () => {
305305
});
306306
return allowedHosts?.allowed;
307307
};
308+
309+
export const useVFolderInvitations = () => {
310+
const baiClient = useSuspendedBackendaiClient();
311+
const { data: vfolderInvitations } = useTanQuery({
312+
queryKey: ['vfolderInvitations'],
313+
queryFn: () => {
314+
return baiClient.vfolder.invitations();
315+
},
316+
});
317+
318+
const mutationToAcceptInvitation = useTanMutation({
319+
mutationFn: (values: { inv_id: string }) => {
320+
return baiClient.vfolder.accept_invitation(values.inv_id);
321+
},
322+
});
323+
324+
const mutationToRejectInvitation = useTanMutation({
325+
mutationFn: (values: { inv_id: string }) => {
326+
return baiClient.vfolder.delete_invitation(values.inv_id);
327+
},
328+
});
329+
330+
return [
331+
{
332+
...vfolderInvitations?.invitations,
333+
count: vfolderInvitations?.invitations?.length,
334+
isPendingMutation:
335+
mutationToAcceptInvitation.isPending ||
336+
mutationToRejectInvitation.isPending,
337+
},
338+
{
339+
acceptInvitation: (inv_id: string, options?: mutationOptions<string>) => {
340+
mutationToAcceptInvitation.mutate(
341+
{ inv_id },
342+
{
343+
onSuccess: () => {
344+
options?.onSuccess && options.onSuccess(inv_id);
345+
},
346+
onError: (error: any) => {
347+
options?.onError && options.onError(error);
348+
},
349+
},
350+
);
351+
},
352+
rejectInvitation: (inv_id: string, options?: mutationOptions<string>) => {
353+
mutationToRejectInvitation.mutate(
354+
{ inv_id },
355+
{
356+
onSuccess: () => {
357+
options?.onSuccess && options.onSuccess(inv_id);
358+
},
359+
onError: (error: any) => {
360+
options?.onError && options.onError(error);
361+
},
362+
},
363+
);
364+
},
365+
},
366+
] as const;
367+
};

react/src/hooks/index.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,9 @@ export type BackendAIClient = {
131131
delete_from_trash_bin: (id: string) => Promise<any>;
132132
rename: (newName: string, id: string) => Promise<any>;
133133
update_folder: (input: any, id: string) => Promise<any>;
134+
invitations: () => Promise<any>;
134135
invite: (perm: string, emails: string[], id: string) => Promise<any>;
136+
accept_invitation: (inv_id: string) => Promise<any>;
135137
delete_invitation: (inv_id: string) => Promise<any>;
136138
modify_invitee_permission(input: {
137139
perm: string | null;

resources/i18n/de.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@
236236
"InvalidEmail": "Ungültige E -Mail -Adresse",
237237
"Invited": "eingeladen",
238238
"InvitedFolders": "Eingeladene Ordner",
239+
"InvitedFoldersTooltip": "{{ count }} Ordnereinladungen sind eine Antwort aus.",
239240
"Limit": "Grenze",
240241
"ModelStore": "Modellladen",
241242
"Models": "Modelle",

resources/i18n/el.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,7 @@
234234
"HostDetails": "Εμφανίζει πληροφορίες ποσόστωσης για τον επιλεγμένο κεντρικό υπολογιστή αποθήκευσης.",
235235
"Invited": "καλεσμένος",
236236
"InvitedFolders": "Προσκεκλημένοι φακέλοι",
237+
"InvitedFoldersTooltip": "{{ count }} Οι προσκλήσεις φακέλων εκκρεμούν.",
237238
"Limit": "Όριο",
238239
"ModelStore": "Κατάστημα μοντέλων",
239240
"Models": "Μοντέλα",

resources/i18n/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@
238238
"InvalidEmail": "Invalid email address",
239239
"Invited": "Invited",
240240
"InvitedFolders": "Invited Folders",
241+
"InvitedFoldersTooltip": "{{ count }} folder invitations are pending response.",
241242
"Limit": "Limit",
242243
"ModelStore": "Model Store",
243244
"Models": "Models",

resources/i18n/es.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@
236236
"InvalidEmail": "Dirección de correo electrónico no válida",
237237
"Invited": "Invitado",
238238
"InvitedFolders": "Carpetas invitadas",
239+
"InvitedFoldersTooltip": "{{ count }} Las invitaciones de la carpeta están pendientes de respuesta.",
239240
"Limit": "Límite",
240241
"ModelStore": "Tienda de modelos",
241242
"Models": "Modelos",

resources/i18n/fi.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@
235235
"InvalidEmail": "Virheellinen sähköpostiosoite",
236236
"Invited": "Kutsuttu",
237237
"InvitedFolders": "Kutsutut kansiot",
238+
"InvitedFoldersTooltip": "{{ count }} -kansiokutsut odottavat vastausta.",
238239
"Limit": "Raja",
239240
"ModelStore": "Mallikauppa",
240241
"Models": "Mallit",

resources/i18n/fr.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,7 @@
236236
"InvalidEmail": "Adresse e-mail non valide",
237237
"Invited": "invité",
238238
"InvitedFolders": "Dossiers invités",
239+
"InvitedFoldersTooltip": "{{ count }} Les invitations de dossiers sont en attente de réponse.",
239240
"Limit": "Limite",
240241
"ModelStore": "Magasin de modèles",
241242
"Models": "Des modèles",

0 commit comments

Comments
 (0)