Skip to content

Commit

Permalink
feat(PaginatedStorage): add grouping
Browse files Browse the repository at this point in the history
  • Loading branch information
artemmufazalov committed Sep 26, 2024
1 parent 960e97f commit c53bc8f
Show file tree
Hide file tree
Showing 45 changed files with 963 additions and 664 deletions.
16 changes: 11 additions & 5 deletions src/components/PaginatedTable/PaginatedTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import './PaginatedTable.scss';

export interface PaginatedTableProps<T, F> {
limit: number;
initialEntitiesCount?: number;
fetchData: FetchData<T, F>;
filters?: F;
tableName: string;
Expand All @@ -42,6 +43,7 @@ export interface PaginatedTableProps<T, F> {

export const PaginatedTable = <T, F>({
limit,
initialEntitiesCount,
fetchData,
filters,
tableName,
Expand All @@ -56,9 +58,12 @@ export const PaginatedTable = <T, F>({
renderEmptyDataMessage,
containerClassName,
}: PaginatedTableProps<T, F>) => {
const initialTotal = initialEntitiesCount || limit;
const initialFound = initialEntitiesCount || 0;

const [sortParams, setSortParams] = React.useState<SortParams | undefined>(initialSortParams);
const [totalEntities, setTotalEntities] = React.useState(limit);
const [foundEntities, setFoundEntities] = React.useState(0);
const [totalEntities, setTotalEntities] = React.useState(initialTotal);
const [foundEntities, setFoundEntities] = React.useState(initialFound);
const [activeChunks, setActiveChunks] = React.useState<number[]>([]);
const [isInitialLoad, setIsInitialLoad] = React.useState(true);

Expand All @@ -82,8 +87,8 @@ export const PaginatedTable = <T, F>({

// reset table on filters change
React.useLayoutEffect(() => {
setTotalEntities(limit);
setFoundEntities(0);
setTotalEntities(initialTotal);
setFoundEntities(initialFound);
setIsInitialLoad(true);
if (parentContainer) {
parentContainer.scrollTo(0, 0);
Expand All @@ -92,7 +97,7 @@ export const PaginatedTable = <T, F>({
}

setActiveChunks([0]);
}, [filters, limit, parentContainer]);
}, [filters, initialFound, initialTotal, limit, parentContainer]);

const renderChunks = () => {
if (!observer) {
Expand All @@ -117,6 +122,7 @@ export const PaginatedTable = <T, F>({
key={value}
id={value}
limit={limit}
totalLength={totalLength}
rowHeight={rowHeight}
columns={columns}
fetchData={fetchData}
Expand Down
8 changes: 7 additions & 1 deletion src/components/PaginatedTable/TableChunk.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ const typedMemo: <T>(Component: T) => T = React.memo;
interface TableChunkProps<T, F> {
id: number;
limit: number;
totalLength: number;
rowHeight: number;
columns: Column<T>[];
filters?: F;
Expand All @@ -35,6 +36,7 @@ interface TableChunkProps<T, F> {
export const TableChunk = typedMemo(function TableChunk<T, F>({
id,
limit,
totalLength,
rowHeight,
columns,
fetchData,
Expand Down Expand Up @@ -101,7 +103,11 @@ export const TableChunk = typedMemo(function TableChunk<T, F>({
}
}, [currentData, isActive, onDataFetched]);

const dataLength = currentData?.data?.length || limit;
const chunkOffset = id * limit;
const remainingLenght = totalLength - chunkOffset;
const calculatedChunkLength = remainingLenght < limit ? remainingLenght : limit;

const dataLength = currentData?.data?.length || calculatedChunkLength;

const renderContent = () => {
if (!isActive) {
Expand Down
8 changes: 7 additions & 1 deletion src/components/TableSkeleton/TableSkeleton.scss
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
.table-skeleton {
width: 100%;
&__wrapper {
width: 100%;

&_hidden {
visibility: hidden;
}
}

&__row {
display: flex;
Expand Down
36 changes: 21 additions & 15 deletions src/components/TableSkeleton/TableSkeleton.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {Skeleton} from '@gravity-ui/uikit';

import {cn} from '../../utils/cn';
import {useDelayed} from '../../utils/hooks/useDelayed';

import './TableSkeleton.scss';

Expand All @@ -9,21 +10,26 @@ const b = cn('table-skeleton');
interface TableSkeletonProps {
className?: string;
rows?: number;
delay?: number;
}

export const TableSkeleton = ({rows = 2, className}: TableSkeletonProps) => (
<div className={b(null, className)}>
<div className={b('row')}>
<Skeleton className={b('col-1')} />
<Skeleton className={b('col-2')} />
<Skeleton className={b('col-3')} />
<Skeleton className={b('col-4')} />
<Skeleton className={b('col-5')} />
</div>
{[...new Array(rows)].map((_, index) => (
<div className={b('row')} key={`skeleton-row-${index}`}>
<Skeleton className={b('col-full')} />
export const TableSkeleton = ({rows = 2, delay = 600, className}: TableSkeletonProps) => {
const [show] = useDelayed(delay);

return (
<div className={b('wrapper', {hidden: !show}, className)}>
<div className={b('row')}>
<Skeleton className={b('col-1')} />
<Skeleton className={b('col-2')} />
<Skeleton className={b('col-3')} />
<Skeleton className={b('col-4')} />
<Skeleton className={b('col-5')} />
</div>
))}
</div>
);
{[...new Array(rows)].map((_, index) => (
<div className={b('row')} key={`skeleton-row-${index}`}>
<Skeleton className={b('col-full')} />
</div>
))}
</div>
);
};
5 changes: 2 additions & 3 deletions src/containers/Cluster/Cluster.scss
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
@import '../../styles/mixins.scss';

.cluster {
position: relative;

overflow: auto;
flex-grow: 1;

height: 100%;
padding: 0 20px;

@include flex-container();

&__header {
position: sticky;
left: 0;
Expand Down
2 changes: 1 addition & 1 deletion src/containers/Storage/PDisk/PDisk.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
}

&__vdisks-item {
flex-basis: 5px;
flex-basis: 3px;
flex-shrink: 0;

.stack__layer {
Expand Down
157 changes: 8 additions & 149 deletions src/containers/Storage/PaginatedStorage.tsx
Original file line number Diff line number Diff line change
@@ -1,163 +1,22 @@
import {StringParam, useQueryParams} from 'use-query-params';
import {PaginatedStorageGroups} from './PaginatedStorageGroups';
import {PaginatedStorageNodes} from './PaginatedStorageNodes';
import {useStorageQueryParams} from './useStorageQueryParams';

import {AccessDenied} from '../../components/Errors/403/AccessDenied';
import {ResponseError} from '../../components/Errors/ResponseError/ResponseError';
import type {RenderControls, RenderErrorMessage} from '../../components/PaginatedTable';
import {useClusterBaseInfo} from '../../store/reducers/cluster/cluster';
import {VISIBLE_ENTITIES} from '../../store/reducers/storage/constants';
import {storageTypeSchema, visibleEntitiesSchema} from '../../store/reducers/storage/types';
import type {StorageType, VisibleEntities} from '../../store/reducers/storage/types';
import {NodesUptimeFilterValues, nodesUptimeFilterValuesSchema} from '../../utils/nodes';
import {useAdditionalNodeProps} from '../AppWithClusters/useClusterData';

import {StorageControls} from './StorageControls/StorageControls';
import {PaginatedStorageGroups} from './StorageGroups/PaginatedStorageGroups';
import {useStorageGroupsSelectedColumns} from './StorageGroups/columns/hooks';
import {PaginatedStorageNodes} from './StorageNodes/PaginatedStorageNodes';
import {useStorageNodesSelectedColumns} from './StorageNodes/columns/hooks';

interface PaginatedStorageProps {
export interface PaginatedStorageProps {
database?: string;
nodeId?: string;
groupId?: string;
parentContainer?: Element | null;
}

export const PaginatedStorage = ({
database,
nodeId,
groupId,
parentContainer,
}: PaginatedStorageProps) => {
const {balancer} = useClusterBaseInfo();
const {additionalNodesProps} = useAdditionalNodeProps({balancer});
export const PaginatedStorage = (props: PaginatedStorageProps) => {
const {storageType} = useStorageQueryParams();

const [queryParams, setQueryParams] = useQueryParams({
type: StringParam,
visible: StringParam,
search: StringParam,
uptimeFilter: StringParam,
});
const storageType = storageTypeSchema.parse(queryParams.type);
const isGroups = storageType === 'groups';
const isNodes = storageType === 'nodes';

const visibleEntities = visibleEntitiesSchema.parse(queryParams.visible);
const searchValue = queryParams.search ?? '';
const nodesUptimeFilter = nodesUptimeFilterValuesSchema.parse(queryParams.uptimeFilter);

const {
columnsToShow: storageNodesColumnsToShow,
columnsToSelect: storageNodesColumnsToSelect,
setColumns: setStorageNodesSelectedColumns,
} = useStorageNodesSelectedColumns({
additionalNodesProps,
visibleEntities,
database,
groupId,
});

const {
columnsToShow: storageGroupsColumnsToShow,
columnsToSelect: storageGroupsColumnsToSelect,
setColumns: setStorageGroupsSelectedColumns,
} = useStorageGroupsSelectedColumns(visibleEntities);

const handleTextFilterChange = (value: string) => {
setQueryParams({search: value || undefined}, 'replaceIn');
};

const handleGroupVisibilityChange = (value: VisibleEntities) => {
setQueryParams({visible: value}, 'replaceIn');
};

const handleStorageTypeChange = (value: StorageType) => {
setQueryParams({type: value}, 'replaceIn');
};

const handleUptimeFilterChange = (value: NodesUptimeFilterValues) => {
setQueryParams({uptimeFilter: value}, 'replaceIn');
};

const handleShowAllGroups = () => {
handleGroupVisibilityChange(VISIBLE_ENTITIES.all);
};

const handleShowAllNodes = () => {
setQueryParams(
{
visible: VISIBLE_ENTITIES.all,
uptimeFilter: NodesUptimeFilterValues.All,
},
'replaceIn',
);
};

const renderControls: RenderControls = ({totalEntities, foundEntities, inited}) => {
const columnsToSelect = isGroups
? storageGroupsColumnsToSelect
: storageNodesColumnsToSelect;

const handleSelectedColumnsUpdate = isGroups
? setStorageGroupsSelectedColumns
: setStorageNodesSelectedColumns;

return (
<StorageControls
searchValue={searchValue}
handleSearchValueChange={handleTextFilterChange}
withTypeSelector
storageType={storageType}
handleStorageTypeChange={handleStorageTypeChange}
visibleEntities={visibleEntities}
handleVisibleEntitiesChange={handleGroupVisibilityChange}
nodesUptimeFilter={nodesUptimeFilter}
handleNodesUptimeFilterChange={handleUptimeFilterChange}
withGroupsUsageFilter={false}
entitiesCountCurrent={foundEntities}
entitiesCountTotal={totalEntities}
entitiesLoading={!inited}
columnsToSelect={columnsToSelect}
handleSelectedColumnsUpdate={handleSelectedColumnsUpdate}
/>
);
};

const renderErrorMessage: RenderErrorMessage = (error) => {
if (error.status === 403) {
return <AccessDenied position="left" />;
}

return <ResponseError error={error} />;
};

if (isNodes) {
return (
<PaginatedStorageNodes
searchValue={searchValue}
visibleEntities={visibleEntities}
nodesUptimeFilter={nodesUptimeFilter}
database={database}
onShowAll={handleShowAllNodes}
parentContainer={parentContainer}
renderControls={renderControls}
renderErrorMessage={renderErrorMessage}
columns={storageNodesColumnsToShow}
/>
);
return <PaginatedStorageNodes {...props} />;
}

return (
<PaginatedStorageGroups
searchValue={searchValue}
visibleEntities={visibleEntities}
database={database}
nodeId={nodeId}
onShowAll={handleShowAllGroups}
parentContainer={parentContainer}
renderControls={renderControls}
renderErrorMessage={renderErrorMessage}
columns={storageGroupsColumnsToShow}
/>
);
return <PaginatedStorageGroups {...props} />;
};
Loading

0 comments on commit c53bc8f

Please sign in to comment.