Skip to content

Commit

Permalink
Merge pull request #1598 from GowthamShanmugam/RHSTOR-6195
Browse files Browse the repository at this point in the history
RHSTOR-6195: Bucket - list page
  • Loading branch information
openshift-merge-bot[bot] authored Oct 9, 2024
2 parents cc59bbf + 1760799 commit 2ba9581
Show file tree
Hide file tree
Showing 28 changed files with 1,557 additions and 665 deletions.
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

0 comments on commit 2ba9581

Please sign in to comment.