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

RHSTOR-6195: Bucket - list page #1598

Merged
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
20 changes: 15 additions & 5 deletions locales/en/plugin__odf-console.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,6 @@
"Clusters": "Clusters",
"Connected applications": "Connected applications",
"Cannot delete while connected to an application.": "Cannot delete while connected to an application.",
"Loading Empty Page": "Loading Empty Page",
"You are not authorized to complete this action. See your cluster administrator for role-based access control information.": "You are not authorized to complete this action. See your cluster administrator for role-based access control information.",
"Not Authorized": "Not Authorized",
"Empty Page": "Empty Page",
"Clean up application resources on current primary cluster {{ failoverCluster }} to start the relocation.": "Clean up application resources on current primary cluster {{ failoverCluster }} to start the relocation.",
"Cleanup Pending": "Cleanup Pending",
"Relocating to cluster {{ preferredCluster }}": "Relocating to cluster {{ preferredCluster }}",
Expand Down Expand Up @@ -1115,6 +1111,16 @@
"MCG": "MCG",
"Object path: ": "Object path: ",
"Copy to share": "Copy to share",
"Erase the contents of your bucket": "Erase the contents of your bucket",
"Storage endpoint": "Storage endpoint",
"Create on": "Create on",
"Owner": "Owner",
"Create and manage your buckets": "Create and manage your buckets",
"Navigate through your buckets effortlessly. View the contents of your S3-managed and Openshift-managed buckets, making it easy to locate and inspect objects.": "Navigate through your buckets effortlessly. View the contents of your S3-managed and Openshift-managed buckets, making it easy to locate and inspect objects.",
"No buckets found": "No buckets found",
"Search a bucket by name": "Search a bucket by name",
"Create bucket": "Create bucket",
"Browse, upload, and manage objects in buckets.": "Browse, upload, and manage objects in buckets.",
"Create Bucket": "Create Bucket",
"An object bucket is a cloud storage container that organizes and manages files (objects), allowing users to store, retrieve and control access to data efficiently.": "An object bucket is a cloud storage container that organizes and manages files (objects), allowing users to store, retrieve and control access to data efficiently.",
"Select bucket creation method": "Select bucket creation method",
Expand Down Expand Up @@ -1394,7 +1400,6 @@
"{{count}} annotation_one": "{{count}} annotation",
"{{count}} annotation_other": "{{count}} annotation",
"Created at": "Created at",
"Owner": "Owner",
"No labels": "No labels",
"No owner": "No owner",
"Select input": "Select input",
Expand All @@ -1403,6 +1408,10 @@
"No resources available": "No resources available",
"Select {{resourceLabel}}": "Select {{resourceLabel}}",
"Error Loading": "Error Loading",
"Loading empty page": "Loading empty page",
"You are not authorized to complete this action. See your cluster administrator for role-based access control information.": "You are not authorized to complete this action. See your cluster administrator for role-based access control information.",
"Not Authorized": "Not Authorized",
"Empty Page": "Empty Page",
"Reset": "Reset",
"An error occurred. Please try again.": "An error occurred. Please try again.",
"Error Loading {{label}}: {{message}}": "Error Loading {{label}}: {{message}}",
Expand Down Expand Up @@ -1462,6 +1471,7 @@
"Infrastructures": "Infrastructures",
"Subscriptions": "Subscriptions",
"Project": "Project",
"Composable table": "Composable table",
"Show password": "Show password",
"Enter node": "Enter node",
"Deployment details": "Deployment details",
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
"dev:c": "yarn ocp-console & PLUGIN=${PLUGIN} I8N_NS=${I8N_NS} yarn server:plugin & wait"
},
"dependencies": {
"@aws-sdk/client-s3": "3.614.0",
"@aws-sdk/client-s3": "3.667.0",
"@aws-sdk/s3-request-presigner": "3.614.0",
"@openshift-console/dynamic-plugin-sdk": "1.3.0",
"@openshift-console/dynamic-plugin-sdk-internal": "1.0.0",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,6 @@ describe('Test drpolicy list page', () => {
test('Empty page loading test', async () => {
testCase = 3;
render(<DRPolicyListPage />);
expect(screen.getByLabelText('Loading Empty Page')).toBeInTheDocument();
expect(screen.getByLabelText('Loading empty page')).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as React from 'react';
import { pluralize } from '@odf/core/components/utils';
import EmptyPage from '@odf/shared/empty-state-page/empty-page';
import { useAccessReview } from '@odf/shared/hooks/rbac-hook';
import { Kebab } from '@odf/shared/kebab/kebab';
import { getName } from '@odf/shared/selectors';
Expand Down Expand Up @@ -28,7 +29,6 @@ import {
import { DRPolicyModel } from '../../models';
import { DRPolicyKind } from '../../types';
import { getReplicationType, isDRPolicyValidated } from '../../utils';
import EmptyPage from '../empty-state-page/empty-page';
import { Header, kebabActionItems, tableColumnInfo } from './helper';
import './drpolicy-list-page.scss';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as React from 'react';
import { DRPolicyKind, DRPlacementControlKind } from '@odf/mco/types';
import { getDRPolicyStatus, parseSyncInterval } from '@odf/mco/utils';
import { formatTime, getLatestDate } from '@odf/shared/details-page/datetime';
import EmptyPage from '@odf/shared/empty-state-page/empty-page';
import { StatusBox } from '@odf/shared/generic/status-box';
import { Labels } from '@odf/shared/labels';
import { ModalBody, ModalFooter } from '@odf/shared/modals/Modal';
Expand Down Expand Up @@ -31,7 +32,6 @@ import {
SYNC_SCHEDULE_DISPLAY_TEXT,
} from '../../../constants';
import { getDRPlacementControlResourceObj } from '../../../hooks';
import EmptyPage from '../../empty-state-page/empty-page';
import {
doNotDeletePVCAnnotationPromises,
unAssignPromises,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
ActionDropdown,
ToggleVariant,
} from '@odf/shared/dropdown/action-dropdown';
import EmptyPage from '@odf/shared/empty-state-page/empty-page';
import { DataUnavailableError } from '@odf/shared/generic/Error';
import { NamespaceModel } from '@odf/shared/models';
import { ResourceNameWIcon } from '@odf/shared/resource-link/resource-link';
Expand Down Expand Up @@ -36,7 +37,6 @@ import {
} from '../../constants';
import { DRPlacementControlModel } from '../../models';
import { DRPlacementControlKind } from '../../types';
import EmptyPage from '../empty-state-page/empty-page';
import { getCurrentActivity } from '../mco-dashboard/disaster-recovery/cluster-app-card/application';
import {
getAlertMessages,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import * as React from 'react';
import {
BUCKET_BOOKMARKS_USER_SETTINGS_KEY,
BUCKETS_BASE_ROUTE,
} from '@odf/core/constants';
import { BucketCrFormat } from '@odf/core/types';
import { Timestamp } from '@odf/shared/details-page/timestamp';
import { EmptyPage } from '@odf/shared/empty-state-page';
import { useUserSettingsLocalStorage } from '@odf/shared/hooks/useUserSettingsLocalStorage';
import {
ComposableTable,
RowComponentType,
} from '@odf/shared/table/composable-table';
import { useCustomTranslation } from '@odf/shared/useCustomTranslationHook';
import { sortRows } from '@odf/shared/utils';
import { TFunction } from 'react-i18next';
import { Link } from 'react-router-dom-v5-compat';
import { Bullseye, Label } from '@patternfly/react-core';
import { UserIcon } from '@patternfly/react-icons';
import {
ActionsColumn,
IAction,
TableVariant,
Td,
Tr,
} from '@patternfly/react-table';

const getRowActions = (t: TFunction<string>): IAction[] => [
// ToDo: add empty/delete bucket action
{
title: (
<>
{t('Empty bucket')}
<p className="text-muted pf-v5-u-font-size-xs">
{t('Erase the contents of your bucket')}
</p>
</>
),
onClick: () => undefined,
},
{
title: t('Delete bucket'),
onClick: () => undefined,
},
];

const getColumnNames = (t: TFunction<string>) => [
'', // favoritable,
t('Name'),
t('Storage endpoint'),
t('Create on'),
t('Owner'),
'', // action kebab
];

const getHeaderColumns = (t: TFunction<string>, favorites: string[]) => {
const columnNames = getColumnNames(t);
return [
{
columnName: columnNames[0],
sortFunction: (a, b, c) => sortRows(a, b, c, 'metadata.name', favorites),
},
{
columnName: columnNames[1],
sortFunction: (a, b, c) => sortRows(a, b, c, 'metadata.name'),
},
{
columnName: columnNames[2],
thProps: {
className: 'pf-v5-u-w-16-on-lg',
},
},
{
columnName: columnNames[3],
sortFunction: (a, b, c) =>
sortRows(a, b, c, 'metadata.creationTimestamp'),
thProps: {
className: 'pf-v5-u-w-16-on-lg',
},
},
{
columnName: columnNames[4],
thProps: {
className: 'pf-v5-u-w-16-on-lg',
},
},
{
columnName: columnNames[5],
},
];
};

const NoBucketMessage: React.FC = () => {
const { t } = useCustomTranslation();
return (
<EmptyPage
ButtonComponent={() => <></>}
title={t('Create and manage your buckets')}
isLoaded
canAccess
>
{t(
'Navigate through your buckets effortlessly. View the contents of your S3-managed and Openshift-managed buckets, making it easy to locate and inspect objects.'
)}
</EmptyPage>
);
};

const EmptyRowMessage: React.FC = () => {
const { t } = useCustomTranslation();
return <Bullseye className="pf-v5-u-mt-xl">{t('No buckets found')}</Bullseye>;
};

const BucketsTableRow: React.FC<RowComponentType<BucketCrFormat>> = ({
row: bucket,
rowIndex,
extraProps,
}) => {
const { t } = useCustomTranslation();
const columnNames = getColumnNames(t);
const {
apiResponse: { owner },
metadata: { name, creationTimestamp },
} = bucket;
const { favorites, setFavorites }: RowExtraPropsType = extraProps;

const onSetFavorite = (key, active) => {
setFavorites((oldFavorites) => [
...oldFavorites.filter((oldFavorite) => oldFavorite !== key),
...(active ? [key] : []),
]);
};

return (
<Tr translate={null} key={rowIndex}>
<Td
translate={null}
dataLabel={columnNames[0]}
favorites={{
isFavorited: favorites.includes(name),
onFavorite: (_event, isFavoriting) =>
onSetFavorite(name, isFavoriting),
rowIndex,
}}
/>
<Td translate={null} dataLabel={columnNames[1]}>
<Link to={`${BUCKETS_BASE_ROUTE}/${name}`}>{name}</Link>
</Td>
<Td translate={null} dataLabel={columnNames[2]}>
{/* ToDo: Currently we only support MCG, make is configurable once RGW is supported as well */}
<Label color="gold">{t('MCG')}</Label>
</Td>
<Td translate={null} dataLabel={columnNames[3]}>
{<Timestamp timestamp={creationTimestamp} ignoreRelativeTime />}
</Td>
<Td translate={null} dataLabel={columnNames[4]}>
<UserIcon /> <span data-test="owner">{owner}</span>
</Td>
<Td translate={null} dataLabel={columnNames[5]} isActionCell>
<ActionsColumn items={getRowActions(t)} translate={null} />
</Td>
</Tr>
);
};

export const BucketsListTable: React.FC<BucketsListTableProps> = ({
allBuckets,
filteredBuckets,
loaded,
error,
}) => {
const { t } = useCustomTranslation();
const [favorites, setFavorites] = useUserSettingsLocalStorage<string[]>(
BUCKET_BOOKMARKS_USER_SETTINGS_KEY,
true,
[]
);
return (
<ComposableTable
rows={filteredBuckets}
columns={getHeaderColumns(t, favorites)}
RowComponent={BucketsTableRow}
noDataMsg={NoBucketMessage}
emptyRowMessage={EmptyRowMessage}
unfilteredData={allBuckets as []}
loaded={loaded}
loadError={error}
isFavorites={true}
variant={TableVariant.compact}
extraProps={{ favorites, setFavorites }}
/>
);
};

type BucketsListTableProps = {
allBuckets: BucketCrFormat[];
filteredBuckets: BucketCrFormat[];
loaded: boolean;
error: any;
};

type RowExtraPropsType = {
favorites: string[];
setFavorites: React.Dispatch<React.SetStateAction<string[]>>;
};
Loading
Loading