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
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,8 @@
"@babel/preset-typescript": "^7.24.7",
"@babel/types": "^7.25.2",
"@electron/packager": "^18.3.3",
"@jest/expect": "30.0.0-beta.3",
"@jest/globals": "30.0.0-beta.3",
"@playwright/test": "^1.49.1",
"@rollup/plugin-commonjs": "^25.0.8",
"@rollup/plugin-node-resolve": "^15.2.3",
Expand Down
505 changes: 419 additions & 86 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

143 changes: 143 additions & 0 deletions react/src/components/FolderInvitationResponseModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
import { useVFolderInvitations } from '../hooks/backendai';
import BAIModal, { BAIModalProps } from './BAIModal';
import Flex from './Flex';
import VFolderPermissionCell from './VFolderPermissionCell';
import { FolderOutlined } from '@ant-design/icons';
import { List, Button, Typography, theme, Descriptions, App } from 'antd';
import React, { useCallback } from 'react';
import { useTranslation } from 'react-i18next';

interface InvitationItem {
id: string;
vfolder_id: string;
vfolder_name: string;
invitee_user_email: string;
inviter_user_email: string;
mount_permission: string;
created_at: string;
modified_at: string | null;
status: string;
perm: string;
}

interface FolderInvitationResponseModalProps extends BAIModalProps {
onRequestClose?: (success: boolean) => void;
fetchKey?: string;
}

const FolderInvitationResponseModal: React.FC<
FolderInvitationResponseModalProps
> = ({ onRequestClose, fetchKey, ...baiModalProps }) => {
const { token } = theme.useToken();
const { message } = App.useApp();
const { t } = useTranslation();
const [
{ invitations },
{ acceptInvitation, rejectInvitation: declineInvitation },
] = useVFolderInvitations(fetchKey);

const renderInvitationItem = useCallback(
(item: InvitationItem) => (
<List.Item
actions={[
<Button
type="primary"
onClick={() =>
acceptInvitation(item.id, {
onSuccess: () => {
onRequestClose?.(true);
message.success(
t('data.invitation.SuccessfullyAcceptedInvitation'),
);
},
onError: (e) => {
onRequestClose?.(false);
message.error(
e.message || t('data.invitation.FailedToAcceptInvitation'),
);
},
})
}
key="accept"
>
{t('summary.Accept')}
</Button>,
<Button
danger
onClick={() =>
declineInvitation(item.id, {
onSuccess: () => {
onRequestClose?.(true);
message.success(
t('data.invitation.SuccessfullyDeclinedInvitation'),
);
},
onError: (e) => {
onRequestClose?.(false);
message.error(
e.message || t('data.invitation.FailedToDeclineInvitation'),
);
},
})
}
key="decline"
>
{t('summary.Decline')}
</Button>,
]}
style={{
padding: token.paddingSM,
}}
>
<List.Item.Meta
title={
<Flex gap={'xxs'}>
<FolderOutlined />
<Typography.Text strong>{item.vfolder_name}</Typography.Text>
</Flex>
}
description={
<Descriptions
size="small"
column={1}
items={[
{
key: 'from',
label: t('data.From'),
children: item.inviter_user_email,
},
{
key: 'permission',
label: t('data.Permission'),
children: <VFolderPermissionCell permission={item.perm} />,
},
]}
/>
}
/>
</List.Item>
),
// eslint-disable-next-line react-hooks/exhaustive-deps
[acceptInvitation, declineInvitation, onRequestClose],
);

return (
<BAIModal
onCancel={() => onRequestClose?.(false)}
title={t('data.InvitedFolders')}
footer={null}
{...baiModalProps}
>
<List
dataSource={invitations}
renderItem={(item) =>
renderInvitationItem({
...item,
})
}
/>
</BAIModal>
);
};

export default FolderInvitationResponseModal;
188 changes: 98 additions & 90 deletions react/src/components/StorageStatusPanelCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,12 @@ const useStyles = createStyles(({ css, token }) => ({

interface StorageStatusPanelProps extends BAICardProps {
fetchKey?: string;
onRequestBadgeClick?: () => void;
}

const StorageStatusPanelCard: React.FC<StorageStatusPanelProps> = ({
fetchKey,
onRequestBadgeClick,
...cardProps
}) => {
const { t } = useTranslation();
Expand All @@ -40,7 +42,7 @@ const StorageStatusPanelCard: React.FC<StorageStatusPanelProps> = ({
const baiClient = useSuspendedBackendaiClient();
const currentProject = useCurrentProjectValue();
const deferredFetchKey = useDeferredValue(fetchKey);
const [{ count }] = useVFolderInvitations();
const [{ count }] = useVFolderInvitations(deferredFetchKey);

const isExcludedCount = (status: string) => {
return _.includes(
Expand All @@ -50,7 +52,7 @@ const StorageStatusPanelCard: React.FC<StorageStatusPanelProps> = ({
};

const { data: vfolders } = useSuspenseTanQuery({
queryKey: ['vfolders', { deferredFetchKey }],
queryKey: ['vfolders', { deferredFetchKey, id: currentProject?.id }],
queryFn: () => {
return baiClient.vfolder.list(currentProject?.id);
},
Expand Down Expand Up @@ -92,96 +94,102 @@ const StorageStatusPanelCard: React.FC<StorageStatusPanelProps> = ({
);

return (
<BAICard {...cardProps} title={t('data.StorageStatus')}>
<Row gutter={[24, 16]}>
<Col
span={8}
style={{
borderRight: `1px solid ${token.colorBorderSecondary}`,
justifyItems: 'center',
}}
>
<BAIPanelItem
title={t('data.MyFolders')}
value={createdCount}
unit={
user_resource_policy?.max_vfolder_count
? `/ ${user_resource_policy?.max_vfolder_count}`
: undefined
}
/>
</Col>
<Col
span={8}
style={{
borderRight: `1px solid ${token.colorBorderSecondary}`,
justifyItems: 'center',
}}
>
<BAIPanelItem
title={t('data.ProjectFolders')}
value={projectCount}
unit={
project_resource_policy?.max_vfolder_count
? `/ ${project_resource_policy?.max_vfolder_count}`
: undefined
}
/>
</Col>
<Col
span={8}
style={{
justifyItems: 'center',
}}
>
<BAIPanelItem
title={
count > 0 ? (
// Add a tag to the Tooltip to make it clickable
// eslint-disable-next-line
<a>
<Tooltip
title={
count > 0
? t('data.InvitedFoldersTooltip', {
count: count,
})
: null
}
rootClassName={styles.invitationTooltip}
placement="topRight"
<>
<BAICard {...cardProps} title={t('data.StorageStatus')}>
<Row gutter={[24, 16]}>
<Col
span={8}
style={{
borderRight: `1px solid ${token.colorBorderSecondary}`,
justifyItems: 'center',
}}
>
<BAIPanelItem
title={t('data.MyFolders')}
value={createdCount}
unit={
user_resource_policy?.max_vfolder_count
? `/ ${user_resource_policy?.max_vfolder_count}`
: undefined
}
/>
</Col>
<Col
span={8}
style={{
borderRight: `1px solid ${token.colorBorderSecondary}`,
justifyItems: 'center',
}}
>
<BAIPanelItem
title={t('data.ProjectFolders')}
value={projectCount}
unit={
project_resource_policy?.max_vfolder_count
? `/ ${project_resource_policy?.max_vfolder_count}`
: undefined
}
/>
</Col>
<Col
span={8}
style={{
justifyItems: 'center',
}}
>
<BAIPanelItem
title={
count > 0 ? (
// Add <a></a> to make tooltip clickable
// eslint-disable-next-line
<a
onClick={() => {
onRequestBadgeClick?.();
}}
>
<Badge
count={count > 0 ? `+${count}` : null}
offset={[0, -`${token.sizeXS}`]}
<Tooltip
title={
count > 0
? t('data.InvitedFoldersTooltip', {
count: count,
})
: null
}
rootClassName={styles.invitationTooltip}
placement="topRight"
>
<Typography.Title level={5} style={{ margin: 0 }}>
{t('data.InvitedFolders')}
</Typography.Title>
</Badge>
</Tooltip>
</a>
) : (
<Typography.Title level={5} style={{ margin: 0 }}>
{t('data.InvitedFolders')}
</Typography.Title>
)
}
value={
<Typography.Text
strong
style={{
fontSize: token.fontSizeHeading1,
color: token.Layout?.headerBg,
}}
>
{invitedCount}
</Typography.Text>
}
/>
</Col>
</Row>
</BAICard>
<Badge
count={count > 0 ? `+${count}` : null}
offset={[0, -`${token.sizeXS}`]}
>
<Typography.Title level={5} style={{ margin: 0 }}>
{t('data.InvitedFolders')}
</Typography.Title>
</Badge>
</Tooltip>
</a>
) : (
<Typography.Title level={5} style={{ margin: 0 }}>
{t('data.InvitedFolders')}
</Typography.Title>
)
}
value={
<Typography.Text
strong
style={{
fontSize: token.fontSizeHeading1,
color: token.Layout?.headerBg,
}}
>
{invitedCount}
</Typography.Text>
}
/>
</Col>
</Row>
</BAICard>
</>
);
};

Expand Down
Loading