Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UI support cloud storage #3372

Merged
merged 117 commits into from
Sep 17, 2021
Merged
Show file tree
Hide file tree
Changes from 99 commits
Commits
Show all changes
117 commits
Select commit Hold shift + click to select a range
2a18ed9
Update server-proxy
Marishka17 Jun 10, 2021
c64410e
Add cloud storage implementation && update enums
Marishka17 Jun 10, 2021
3e5264d
Add api && fix server-proxy
Marishka17 Jun 10, 2021
3aebaa9
Add fixes
Marishka17 Jun 15, 2021
e86dd6f
small update
Marishka17 Jun 15, 2021
113a4fa
Init
Marishka17 Jun 15, 2021
0bc4b9d
Initial commit
Jun 15, 2021
c35a9dd
Merged branch
Jun 15, 2021
617e996
Reworked search field to be the same component
Jun 15, 2021
b5d0bf6
Minor fix for project search
Jun 15, 2021
516369e
Minor style fix
Jun 15, 2021
da5a25d
Update create page: using functional component
Marishka17 Jun 16, 2021
472381d
Remove redundant file
Marishka17 Jun 16, 2021
a048fac
Fix form item names
Marishka17 Jun 16, 2021
e4edd4a
Merge branch 'develop' into ui_support_cloud_storage
Jun 16, 2021
1f5e8ae
Some initial list
Jun 16, 2021
1a64d07
Setup search & pagination
Jun 16, 2021
fa98d54
Better version of cards was added
Jun 16, 2021
bd92b9d
Fixed search tooltip
Jun 16, 2021
de10bb1
Implemented menu & deleting
Jun 16, 2021
1c75e71
Fix cloud storage creating
Marishka17 Jun 17, 2021
6fef181
Fix missing id
Marishka17 Jun 17, 2021
d7ea3d5
Actions refactoring
Jun 17, 2021
e318d37
Add range
Marishka17 Jun 22, 2021
d658530
Add updating cloud storage
Marishka17 Jun 22, 2021
abb56c5
Merge branch 'upstream/develop' into ui_support_cloud_storage
Marishka17 Jun 26, 2021
314ca3c
Add simple creating task with files from cloud storage
Marishka17 Jun 27, 2021
e6e5d2d
Tmp fixes
Marishka17 Jun 28, 2021
2c54fa8
Refactoring & styles fixes
Jun 28, 2021
0c0f66f
Improved update form
Jun 28, 2021
3603c2f
Adjusted urls
Jun 28, 2021
af360b8
Merge branch 'develop' into ui_support_cloud_storage
Jun 28, 2021
057f22c
Fixed update
Jun 29, 2021
e4056f2
Minor fixes on create a task page
Jun 29, 2021
9f23ba4
Improved task creation
Jun 29, 2021
771fd63
Adjusted list
Jun 29, 2021
6d792da
Fixed syntax
Jun 29, 2021
ed8530b
Some code refactoring
Jun 29, 2021
3a74cd0
Added tooltip
Jun 29, 2021
8f8fc09
Minor tree fixes
Jun 29, 2021
35715ee
Fix saving first image
Marishka17 Jun 29, 2021
5169a71
Batch of fixes
Jun 29, 2021
7ece10b
Merge branch 'ui_support_cloud_storage' of https://github.com/openvin…
Marishka17 Jun 29, 2021
7888e80
Fixed a couple of issues when search cloud storages in create task
Jun 30, 2021
a36d35b
Removed outdated 'withRemote' prop
Jun 30, 2021
a0abde9
Fixed search, added description
Jun 30, 2021
e6a5316
Fixed description
Jun 30, 2021
96c2bbe
Add temp preview
Marishka17 Jul 3, 2021
b305836
Merge branch 'ui_support_cloud_storage' of https://github.com/openvin…
Marishka17 Jul 3, 2021
8fa9caf
Add preview && some fixes
Marishka17 Jul 3, 2021
5185ff9
Add anonymous access to azure container
Marishka17 Jul 5, 2021
46587a9
Fix case with sub dirs on cloud storage
Marishka17 Jul 5, 2021
93eea37
Move server part from ui_support_cloud_storage && fix missing id field
Marishka17 Jul 6, 2021
21a35ec
Remove unnecessary && fix missing dispatching action
Marishka17 Jul 6, 2021
037bfa7
Remove extra
Marishka17 Jul 7, 2021
3dd7f97
Fix notification
Marishka17 Jul 7, 2021
e3687d3
Update cards styles
Marishka17 Jul 7, 2021
c371c57
Reset checked files on form after creating a task
Marishka17 Jul 7, 2021
cd65b94
Add support key_secret_key_pair && fix updating credentials
Marishka17 Jul 9, 2021
0aebb6e
Add support_key_secret_key_pair
Marishka17 Jul 9, 2021
5c8dbfb
Merge commit '830c7b2' into mk/ui_support_cloud_storage
Marishka17 Jul 9, 2021
03cb319
Move from ui_support_cloud_storage
Marishka17 Jul 9, 2021
777f83b
Update case_35_search_task_feature
Marishka17 Jul 9, 2021
63b62c1
Fix sas token initialization
Marishka17 Jul 12, 2021
91eadb0
Fix updating page
Marishka17 Jul 12, 2021
cb219ea
Fix several moments
Marishka17 Jul 18, 2021
2c64d20
Remove temporary credentials && fix typos
Marishka17 Jul 18, 2021
41aa91d
Fix several moments
Marishka17 Jul 18, 2021
7e56fa9
Add index resetting
Marishka17 Jul 18, 2021
f94ef7c
Fix pylint errors
Marishka17 Jul 18, 2021
1f2915b
Remove excess migration
Marishka17 Aug 2, 2021
2f5a6ef
Merge branch 'develop' into mk/expansion_server_cloud_storage
Marishka17 Aug 2, 2021
e8eb665
Merge develop && resolve conflicts
Marishka17 Aug 2, 2021
90a2473
Remove excess
Marishka17 Aug 10, 2021
ba87515
More changes
Marishka17 Aug 10, 2021
9a8faf4
tmp
Marishka17 Aug 10, 2021
95efc71
Merge branch 'mk/expansion_server_cloud_storage' into ui_support_clou…
Marishka17 Aug 10, 2021
7187bb3
Merge branch 'develop' into mk/ui_support_cloud_storage
Marishka17 Aug 10, 2021
45d569f
Remove trailing spaces
Marishka17 Aug 10, 2021
9df733d
Merge branch 'develop' into ui_support_cloud_storage
Marishka17 Aug 10, 2021
29ef6e8
Merge branch 'mk/ui_support_cloud_storage' into ui_support_cloud_storage
Marishka17 Aug 10, 2021
c3bb676
Recover deleted
Marishka17 Aug 10, 2021
0920d14
Apply lost changes
Marishka17 Aug 11, 2021
bbf2438
Merge branch 'mk/ui_support_cloud_storage' into ui_support_cloud_storage
Marishka17 Aug 11, 2021
01eb0ad
Remove excess
Marishka17 Aug 11, 2021
26ff2aa
Some fixes
Marishka17 Aug 11, 2021
39b1a13
manifest_set -> manifests
Marishka17 Aug 13, 2021
7c8a813
Refactor code with manifest block
Marishka17 Aug 13, 2021
34eda9d
Add cloud storage status && several fixes
Marishka17 Aug 26, 2021
3e6b712
Merge develop && resolve conflict
Marishka17 Aug 26, 2021
e1b0434
Open links in new page
Marishka17 Aug 27, 2021
a3edc72
Fix styles
Marishka17 Aug 27, 2021
122b98d
Update svg
Marishka17 Aug 30, 2021
c5cc403
fix
Marishka17 Aug 31, 2021
e2188c0
Merge develop && resolve conflicts
Marishka17 Aug 31, 2021
b1b3965
Remove excess migration
Marishka17 Aug 31, 2021
8ae8cc4
Remove excess
Marishka17 Aug 31, 2021
390ae8b
Fix typos
Marishka17 Sep 1, 2021
61d5c45
Merge branch 'develop' into ui_support_cloud_storage
Marishka17 Sep 2, 2021
2e02060
styles
Marishka17 Sep 6, 2021
78e1d1b
Add initial value for selected manifest
Marishka17 Sep 9, 2021
143815f
Small fixes
Marishka17 Sep 9, 2021
0ca8a0e
Refactoring && several fixes && styles
Marishka17 Sep 9, 2021
b67d8ac
Add checkbox 'select all' files
Marishka17 Sep 9, 2021
404b53f
t
Marishka17 Sep 13, 2021
cbebbd5
Refactor region component
Marishka17 Sep 13, 2021
d4a4695
Refactored: useForm hook
Marishka17 Sep 13, 2021
e4c6a61
Clear selected cloud storage name after the task was created
Marishka17 Sep 13, 2021
21d6a5b
Remove excess
Marishka17 Sep 14, 2021
47b8d62
lazy creating
Marishka17 Sep 14, 2021
bb1acff
Refactor getting cloud storage status
Marishka17 Sep 15, 2021
87560ac
Remove delusion
Marishka17 Sep 15, 2021
4ce089b
Merge develop
Marishka17 Sep 15, 2021
cdb4ccf
Update changelog
Marishka17 Sep 15, 2021
595ed58
Increase version
Marishka17 Sep 15, 2021
d9307be
Merge develop
Marishka17 Sep 16, 2021
ab86b00
Merge branch develop
Marishka17 Sep 17, 2021
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
1 change: 0 additions & 1 deletion cvat-core/src/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ function build() {
const { FrameData } = require('./frames');
const { CloudStorage } = require('./cloud-storage');


const enums = require('./enums');

const {
Expand Down
175 changes: 175 additions & 0 deletions cvat-ui/src/actions/cloud-storage-actions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
// Copyright (C) 2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

import { Dispatch, ActionCreator } from 'redux';
import { ActionUnion, createAction, ThunkAction } from 'utils/redux';
import getCore from 'cvat-core-wrapper';
import { CloudStoragesQuery, CloudStorage } from 'reducers/interfaces';

const cvat = getCore();

export enum CloudStorageActionTypes {
UPDATE_CLOUD_STORAGES_GETTING_QUERY = 'UPDATE_CLOUD_STORAGES_GETTING_QUERY',
GET_CLOUD_STORAGES = 'GET_CLOUD_STORAGES',
GET_CLOUD_STORAGE_SUCCESS = 'GET_CLOUD_STORAGES_SUCCESS',
GET_CLOUD_STORAGE_FAILED = 'GET_CLOUD_STORAGES_FAILED',
GET_CLOUD_STORAGE_STATUS = 'GET_CLOUD_STORAGE_STATUS',
GET_CLOUD_STORAGE_STATUS_SUCCESS = 'GET_CLOUD_STORAGE_STATUS_SUCCESS',
GET_CLOUD_STORAGE_STATUS_FAILED = 'GET_CLOUD_STORAGE_STATUS_FAILED',
GET_CLOUD_STORAGE_PREVIEW_FAILED = 'GET_CLOUD_STORAGE_PREVIEW_FAILED',
CREATE_CLOUD_STORAGE = 'CREATE_CLOUD_STORAGE',
CREATE_CLOUD_STORAGE_SUCCESS = 'CREATE_CLOUD_STORAGE_SUCCESS',
CREATE_CLOUD_STORAGE_FAILED = 'CREATE_CLOUD_STORAGE_FAILED',
DELETE_CLOUD_STORAGE = 'DELETE_CLOUD_STORAGE',
DELETE_CLOUD_STORAGE_SUCCESS = 'DELETE_CLOUD_STORAGE_SUCCESS',
DELETE_CLOUD_STORAGE_FAILED = 'DELETE_CLOUD_STORAGE_FAILED',
UPDATE_CLOUD_STORAGE = 'UPDATE_CLOUD_STORAGE',
UPDATE_CLOUD_STORAGE_SUCCESS = 'UPDATE_CLOUD_STORAGE_SUCCESS',
UPDATE_CLOUD_STORAGE_FAILED = 'UPDATE_CLOUD_STORAGE_FAILED',
LOAD_CLOUD_STORAGE_CONTENT = 'LOAD_CLOUD_STORAGE_CONTENT',
LOAD_CLOUD_STORAGE_CONTENT_FAILED = 'LOAD_CLOUD_STORAGE_CONTENT_FAILED',
LOAD_CLOUD_STORAGE_CONTENT_SUCCESS = 'LOAD_CLOUD_STORAGE_CONTENT_SUCCESS',
}

const cloudStoragesActions = {
updateCloudStoragesGettingQuery: (query: Partial<CloudStoragesQuery>) =>
createAction(CloudStorageActionTypes.UPDATE_CLOUD_STORAGES_GETTING_QUERY, { query }),
getCloudStorages: () => createAction(CloudStorageActionTypes.GET_CLOUD_STORAGES),
getCloudStoragesSuccess: (array: any[], previews: string[], count: number, query: Partial<CloudStoragesQuery>) =>
createAction(CloudStorageActionTypes.GET_CLOUD_STORAGE_SUCCESS, {
array,
previews,
count,
query,
}),
getCloudStoragesFailed: (error: any, query: Partial<CloudStoragesQuery>) =>
createAction(CloudStorageActionTypes.GET_CLOUD_STORAGE_FAILED, { error, query }),
deleteCloudStorage: (cloudStorageID: number) =>
createAction(CloudStorageActionTypes.DELETE_CLOUD_STORAGE, { cloudStorageID }),
deleteCloudStorageSuccess: (cloudStorageID: number) =>
createAction(CloudStorageActionTypes.DELETE_CLOUD_STORAGE_SUCCESS, { cloudStorageID }),
deleteCloudStorageFailed: (error: any, cloudStorageID: number) =>
createAction(CloudStorageActionTypes.DELETE_CLOUD_STORAGE_FAILED, { error, cloudStorageID }),
createCloudStorage: () => createAction(CloudStorageActionTypes.CREATE_CLOUD_STORAGE),
createCloudStorageSuccess: (cloudStorageID: number) =>
createAction(CloudStorageActionTypes.CREATE_CLOUD_STORAGE_SUCCESS, { cloudStorageID }),
createCloudStorageFailed: (error: any) =>
createAction(CloudStorageActionTypes.CREATE_CLOUD_STORAGE_FAILED, { error }),
updateCloudStorage: () => createAction(CloudStorageActionTypes.UPDATE_CLOUD_STORAGE, {}),
updateCloudStorageSuccess: (cloudStorage: CloudStorage) =>
createAction(CloudStorageActionTypes.UPDATE_CLOUD_STORAGE_SUCCESS, { cloudStorage }),
updateCloudStorageFailed: (cloudStorage: CloudStorage, error: any) =>
createAction(CloudStorageActionTypes.UPDATE_CLOUD_STORAGE_FAILED, { cloudStorage, error }),
loadCloudStorageContent: () => createAction(CloudStorageActionTypes.LOAD_CLOUD_STORAGE_CONTENT),
loadCloudStorageContentSuccess: (cloudStorageID: number, content: any) =>
createAction(CloudStorageActionTypes.LOAD_CLOUD_STORAGE_CONTENT_SUCCESS, { cloudStorageID, content }),
loadCloudStorageContentFailed: (cloudStorageID: number, error: any) =>
createAction(CloudStorageActionTypes.LOAD_CLOUD_STORAGE_CONTENT_FAILED, { cloudStorageID, error }),
getCloudStorageStatus: () => createAction(CloudStorageActionTypes.GET_CLOUD_STORAGE_STATUS),
getCloudStorageStatusSuccess: (cloudStorageID: number, status: string) =>
createAction(CloudStorageActionTypes.GET_CLOUD_STORAGE_STATUS_SUCCESS, { cloudStorageID, status }),
getCloudStorageStatusFailed: (cloudStorageID: number, error: any) =>
createAction(CloudStorageActionTypes.GET_CLOUD_STORAGE_STATUS_FAILED, { cloudStorageID, error }),
getCloudStoragePreiewFailed: (cloudStorageID: number, error: any) =>
createAction(CloudStorageActionTypes.GET_CLOUD_STORAGE_PREVIEW_FAILED, { cloudStorageID, error }),
};

export type CloudStorageActions = ActionUnion<typeof cloudStoragesActions>;

export function getCloudStoragesAsync(query: Partial<CloudStoragesQuery>): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
dispatch(cloudStoragesActions.getCloudStorages());
dispatch(cloudStoragesActions.updateCloudStoragesGettingQuery(query));

const filteredQuery = { ...query };
for (const key in filteredQuery) {
if (filteredQuery[key] === null) {
delete filteredQuery[key];
}
}

let result = null;
try {
result = await cvat.cloudStorages.get(filteredQuery);
} catch (error) {
dispatch(cloudStoragesActions.getCloudStoragesFailed(error, query));
return;
}

const array = Array.from(result);
const promises = array.map((cloudStorage: CloudStorage): string =>
(cloudStorage as any).getPreview().catch((error: any) => {
dispatch(cloudStoragesActions.getCloudStoragePreiewFailed(cloudStorage.id, error));
return '';
}));

dispatch(cloudStoragesActions.getCloudStoragesSuccess(array, await Promise.all(promises), result.count, query));
};
}

export function deleteCloudStorageAsync(cloudStorageInstance: any): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
try {
dispatch(cloudStoragesActions.deleteCloudStorage(cloudStorageInstance.id));
await cloudStorageInstance.delete();
} catch (error) {
dispatch(cloudStoragesActions.deleteCloudStorageFailed(error, cloudStorageInstance.id));
return;
}

dispatch(cloudStoragesActions.deleteCloudStorageSuccess(cloudStorageInstance.id));
};
}

export function createCloudStorageAsync(data: any): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
const cloudStorageInstance = new cvat.classes.CloudStorage(data);

dispatch(cloudStoragesActions.createCloudStorage());
try {
const savedCloudStorage = await cloudStorageInstance.save();
dispatch(cloudStoragesActions.createCloudStorageSuccess(savedCloudStorage.id));
} catch (error) {
dispatch(cloudStoragesActions.createCloudStorageFailed(error));
}
};
}

export function updateCloudStorageAsync(data: any): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
const cloudStorageInstance = new cvat.classes.CloudStorage(data);

dispatch(cloudStoragesActions.updateCloudStorage());
try {
const savedCloudStorage = await cloudStorageInstance.save();
dispatch(cloudStoragesActions.updateCloudStorageSuccess(savedCloudStorage));
} catch (error) {
dispatch(cloudStoragesActions.updateCloudStorageFailed(data, error));
}
};
}

export function loadCloudStorageContentAsync(cloudStorage: CloudStorage): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
dispatch(cloudStoragesActions.loadCloudStorageContent());
try {
const result = await cloudStorage.getContent();
dispatch(cloudStoragesActions.loadCloudStorageContentSuccess(cloudStorage.id, result));
} catch (error) {
dispatch(cloudStoragesActions.loadCloudStorageContentFailed(cloudStorage.id, error));
}
};
}

export function getCloudStorageStatusAsync(cloudStorage: CloudStorage): ThunkAction {
return async (dispatch: ActionCreator<Dispatch>): Promise<void> => {
dispatch(cloudStoragesActions.getCloudStorageStatus());
try {
const result = await cloudStorage.getStatus();
dispatch(cloudStoragesActions.getCloudStorageStatusSuccess(cloudStorage.id, result));
} catch (error) {
dispatch(cloudStoragesActions.getCloudStorageStatusFailed(cloudStorage.id, error));
}
};
}
5 changes: 4 additions & 1 deletion cvat-ui/src/actions/tasks-actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -386,10 +386,13 @@ export function createTaskAsync(data: any): ThunkAction<Promise<void>, {}, {}, A
if (data.subset) {
description.subset = data.subset;
}
if (data.cloudStorageId) {
description.cloud_storage_id = data.cloudStorageId;
}

const taskInstance = new cvat.classes.Task(description);
taskInstance.clientFiles = data.files.local;
taskInstance.serverFiles = data.files.share;
taskInstance.serverFiles = data.files.share.concat(data.files.cloudStorage);
taskInstance.remoteFiles = data.files.remote;

if (data.advanced.repository) {
Expand Down
21 changes: 21 additions & 0 deletions cvat-ui/src/assets/S3.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions cvat-ui/src/assets/vscode-icons_file-type-azure.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions cvat-ui/src/base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ $border-color-3: #242424;
$border-color-hover: #40a9ff;
$background-color-1: white;
$background-color-2: #f1f1f1;
$notification-background-color-1: #d9ecff;
$notification-border-color-1: #1890ff;
$transparent-color: rgba(0, 0, 0, 0);
$player-slider-color: #979797;
$player-buttons-color: #242424;
Expand Down
145 changes: 145 additions & 0 deletions cvat-ui/src/components/cloud-storages-page/cloud-storage-item.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright (C) 2021 Intel Corporation
//
// SPDX-License-Identifier: MIT

import React, { useCallback } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useHistory } from 'react-router';
import { CloudSyncOutlined, MoreOutlined, QuestionCircleOutlined } from '@ant-design/icons';
import Card from 'antd/lib/card';
import Meta from 'antd/lib/card/Meta';
import Paragraph from 'antd/lib/typography/Paragraph';
import Text from 'antd/lib/typography/Text';
import Button from 'antd/lib/button';
import Dropdown from 'antd/lib/dropdown';
import Menu from 'antd/lib/menu';
import Modal from 'antd/lib/modal';
import moment from 'moment';

import { CloudStorage, CombinedState } from 'reducers/interfaces';
import { deleteCloudStorageAsync } from 'actions/cloud-storage-actions';
import CVATTooltip from 'components/common/cvat-tooltip';
import Status from './cloud-storage-status';

interface Props {
cloudStorageInstance: CloudStorage;
}

export default function CloudStorageItemComponent(props: Props): JSX.Element {
const history = useHistory();
const dispatch = useDispatch();

// cloudStorageInstance: {storage, preview}
const { cloudStorageInstance } = props;
const {
id,
displayName,
providerType,
owner,
createdDate,
updatedDate,
description,
} = cloudStorageInstance.storage;
const { preview } = cloudStorageInstance;
const deletes = useSelector((state: CombinedState) => state.cloudStorages.activities.deletes);
const deleted = cloudStorageInstance.storage.id in deletes ? deletes[cloudStorageInstance.storage.id] : false;

const style: React.CSSProperties = {};

if (deleted) {
style.pointerEvents = 'none';
style.opacity = 0.5;
}

const onUpdate = useCallback(() => {
history.push(`/cloudstorages/update/${id}`);
}, []);

const onDelete = useCallback(() => {
Modal.confirm({
title: 'Please, confirm your action',
content: `You are going to remove the cloudstorage "${displayName}". Continue?`,
className: 'cvat-delete-cloud-storage-modal',
onOk: () => {
dispatch(deleteCloudStorageAsync(cloudStorageInstance.storage));
},
okButtonProps: {
type: 'primary',
danger: true,
},
okText: 'Delete',
});
}, [cloudStorageInstance]);
Marishka17 marked this conversation as resolved.
Show resolved Hide resolved

return (
<Card
cover={(
<>
{preview ? (
<img
className='cvat-cloud-storage-item-preview'
src={preview}
alt='Preview image'
aria-hidden
/>
) : (
<div className='cvat-cloud-storage-item-empty-preview' aria-hidden>
<CloudSyncOutlined />
</div>
)}
{description ? (
<CVATTooltip overlay={description}>
<QuestionCircleOutlined className='cvat-cloud-storage-description-icon' />
</CVATTooltip>
) : null}
</>
)}
size='small'
style={style}
className='cvat-cloud-storage-item'
>
<Meta
title={(
<Paragraph>
<Text strong>{`#${id}: `}</Text>
<Text>{displayName}</Text>
</Paragraph>
)}
description={(
<>
<Paragraph>
<Text type='secondary'>Provider: </Text>
<Text>{providerType}</Text>
</Paragraph>
<Paragraph>
<Text type='secondary'>Created </Text>
{owner ? <Text type='secondary'>{`by ${owner.username}`}</Text> : null}
<Text type='secondary'> on </Text>
<Text type='secondary'>{moment(createdDate).format('MMMM Do YYYY')}</Text>
</Paragraph>
<Paragraph>
<Text type='secondary'>Last updated </Text>
<Text type='secondary'>{moment(updatedDate).fromNow()}</Text>
</Paragraph>
<Status cloudStorage={cloudStorageInstance.storage} />
<Dropdown
overlay={(
<Menu className='cvat-project-actions-menu'>
<Menu.Item onClick={onUpdate}>Update</Menu.Item>
<Menu.Item onClick={onDelete}>Delete</Menu.Item>
</Menu>
)}
>
<Button
className='cvat-cloud-storage-item-menu-button'
type='link'
size='large'
icon={<MoreOutlined />}
/>
</Dropdown>
</>
)}
/>
</Card>
);
}
Loading