Skip to content

Commit 3e9eecd

Browse files
committed
feat[FR-738]: add folder invitation badge
1 parent c187592 commit 3e9eecd

25 files changed

Lines changed: 160 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: 63 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.size}px;
21+
}
22+
.ant-tooltip-content {
23+
left: ${token.size}px;
24+
bottom: ${token.size}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,51 @@ const StorageStatusPanelCard: React.FC<StorageStatusPanelProps> = ({
118134
justifyItems: 'center',
119135
}}
120136
>
121-
<BAIPanelItem title={t('data.InvitedFolders')} value={invitedCount} />
137+
<BAIPanelItem
138+
title={
139+
count > 0 ? (
140+
// Add a tag to the Tooltip to make it clickable
141+
// eslint-disable-next-line
142+
<a>
143+
<Tooltip
144+
title={
145+
count > 0
146+
? t('data.InvitedFoldersTooltip', {
147+
count: count,
148+
})
149+
: null
150+
}
151+
rootClassName={styles.invitationTooltip}
152+
placement="topRight"
153+
>
154+
<Badge
155+
count={count > 0 ? `+${count}` : null}
156+
offset={[0, -`${token.sizeXS}`]}
157+
>
158+
<Typography.Title level={5} style={{ margin: 0 }}>
159+
{t('data.InvitedFolders')}
160+
</Typography.Title>
161+
</Badge>
162+
</Tooltip>
163+
</a>
164+
) : (
165+
<Typography.Title level={5} style={{ margin: 0 }}>
166+
{t('data.InvitedFolders')}
167+
</Typography.Title>
168+
)
169+
}
170+
value={
171+
<Typography.Text
172+
strong
173+
style={{
174+
fontSize: token.fontSizeHeading1,
175+
color: token.Layout?.headerBg,
176+
}}
177+
>
178+
{invitedCount}
179+
</Typography.Text>
180+
}
181+
/>
122182
</Col>
123183
</Row>
124184
</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
@@ -252,6 +252,7 @@
252252
"InvalidEmail": "Ungültige E -Mail -Adresse",
253253
"Invited": "eingeladen",
254254
"InvitedFolders": "Eingeladene Ordner",
255+
"InvitedFoldersTooltip": "{{ count }} Ordnereinladungen sind eine Antwort aus.",
255256
"Limit": "Grenze",
256257
"ModelStore": "Modellladen",
257258
"Models": "Modelle",

resources/i18n/el.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@
250250
"HostDetails": "Εμφανίζει πληροφορίες ποσόστωσης για τον επιλεγμένο κεντρικό υπολογιστή αποθήκευσης.",
251251
"Invited": "καλεσμένος",
252252
"InvitedFolders": "Προσκεκλημένοι φακέλοι",
253+
"InvitedFoldersTooltip": "{{ count }} Οι προσκλήσεις φακέλων εκκρεμούν.",
253254
"Limit": "Όριο",
254255
"ModelStore": "Κατάστημα μοντέλων",
255256
"Models": "Μοντέλα",

resources/i18n/en.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@
254254
"InvalidEmail": "Invalid email address",
255255
"Invited": "Invited",
256256
"InvitedFolders": "Invited Folders",
257+
"InvitedFoldersTooltip": "{{ count }} folder invitations are pending response.",
257258
"Limit": "Limit",
258259
"ModelStore": "Model Store",
259260
"Models": "Models",

resources/i18n/es.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@
252252
"InvalidEmail": "Dirección de correo electrónico no válida",
253253
"Invited": "Invitado",
254254
"InvitedFolders": "Carpetas invitadas",
255+
"InvitedFoldersTooltip": "{{ count }} Las invitaciones de la carpeta están pendientes de respuesta.",
255256
"Limit": "Límite",
256257
"ModelStore": "Tienda de modelos",
257258
"Models": "Modelos",

resources/i18n/fi.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,6 +251,7 @@
251251
"InvalidEmail": "Virheellinen sähköpostiosoite",
252252
"Invited": "Kutsuttu",
253253
"InvitedFolders": "Kutsutut kansiot",
254+
"InvitedFoldersTooltip": "{{ count }} -kansiokutsut odottavat vastausta.",
254255
"Limit": "Raja",
255256
"ModelStore": "Mallikauppa",
256257
"Models": "Mallit",

resources/i18n/fr.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@
252252
"InvalidEmail": "Adresse e-mail non valide",
253253
"Invited": "invité",
254254
"InvitedFolders": "Dossiers invités",
255+
"InvitedFoldersTooltip": "{{ count }} Les invitations de dossiers sont en attente de réponse.",
255256
"Limit": "Limite",
256257
"ModelStore": "Magasin de modèles",
257258
"Models": "Des modèles",

0 commit comments

Comments
 (0)