Skip to content

Commit

Permalink
feat: connect sort table to backend (#5338)
Browse files Browse the repository at this point in the history
Now FE sorting is done in backend.
  • Loading branch information
sjaanus committed Nov 15, 2023
1 parent db77962 commit 4e1040c
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 17 deletions.
Expand Up @@ -63,11 +63,18 @@ import { ListItemType } from './ProjectFeatureToggles.types';
import { createFeatureToggleCell } from './FeatureToggleSwitch/createFeatureToggleCell';
import { useFeatureToggleSwitch } from './FeatureToggleSwitch/useFeatureToggleSwitch';
import useLoading from 'hooks/useLoading';
import { DEFAULT_PAGE_LIMIT } from '../ProjectOverview';

const StyledResponsiveButton = styled(ResponsiveButton)(() => ({
whiteSpace: 'nowrap',
}));

export interface ISortingRules {
sortBy: string;
sortOrder: 'asc' | 'desc';
isFavoritesPinned: boolean;
}

interface IPaginatedProjectFeatureTogglesProps {
features: IProject['features'];
environments: IProject['environments'];
Expand All @@ -78,6 +85,8 @@ interface IPaginatedProjectFeatureTogglesProps {
searchValue: string;
setSearchValue: React.Dispatch<React.SetStateAction<string>>;
paginationBar: JSX.Element;
sortingRules: ISortingRules;
setSortingRules: (sortingRules: ISortingRules) => void;
}

const staticColumns = ['Select', 'Actions', 'name', 'favorite'];
Expand All @@ -96,6 +105,8 @@ export const PaginatedProjectFeatureToggles = ({
searchValue,
setSearchValue,
paginationBar,
sortingRules,
setSortingRules,
}: IPaginatedProjectFeatureTogglesProps) => {
const { classes: styles } = useStyles();
const bodyLoadingRef = useLoading(loading);
Expand Down Expand Up @@ -223,7 +234,11 @@ export const PaginatedProjectFeatureToggles = ({
{
Header: 'Name',
accessor: 'name',
Cell: ({ value }: { value: string }) => (
Cell: ({
value,
}: {
value: string;
}) => (
<Tooltip title={value} arrow describeChild>
<span>
<LinkCell
Expand Down Expand Up @@ -285,7 +300,7 @@ export const PaginatedProjectFeatureToggles = ({
return {
Header: loading ? () => '' : name,
maxWidth: 90,
id: `environments.${name}`,
id: `environment:${name}`,
accessor: (row: ListItemType) =>
row.environments[name]?.enabled,
align: 'center',
Expand All @@ -301,7 +316,11 @@ export const PaginatedProjectFeatureToggles = ({
id: 'Actions',
maxWidth: 56,
width: 56,
Cell: (props: { row: { original: ListItemType } }) => (
Cell: (props: {
row: {
original: ListItemType;
};
}) => (
<ActionsCell
projectId={projectId}
onOpenArchiveDialog={setFeatureArchiveState}
Expand Down Expand Up @@ -372,7 +391,10 @@ export const PaginatedProjectFeatureToggles = ({
name: `Feature name ${index}`,
createdAt: new Date().toISOString(),
environments: {
production: { name: 'production', enabled: false },
production: {
name: 'production',
enabled: false,
},
},
}));
// Coerce loading data to FeatureSchema[]
Expand Down Expand Up @@ -419,7 +441,7 @@ export const PaginatedProjectFeatureToggles = ({
id:
searchParams.get('sort') ||
storedParams.id ||
'createdAt',
sortingRules.sortBy,
desc: searchParams.has('order')
? searchParams.get('order') === 'desc'
: storedParams.desc,
Expand Down Expand Up @@ -462,10 +484,12 @@ export const PaginatedProjectFeatureToggles = ({
if (loading) {
return;
}
const sortedByColumn = sortBy[0].id;
const sortOrder = sortBy[0].desc ? 'desc' : 'asc';
const tableState: Record<string, string> = {};
tableState.sort = sortBy[0].id;
tableState.sort = sortedByColumn;
if (sortBy[0].desc) {
tableState.order = 'desc';
tableState.order = sortOrder;
}
if (searchValue) {
tableState.search = searchValue;
Expand All @@ -490,10 +514,17 @@ export const PaginatedProjectFeatureToggles = ({
desc: sortBy[0].desc || false,
columns: tableState.columns.split(','),
}));
const favoritesPinned = Boolean(isFavoritesPinned);
setGlobalStore((params) => ({
...params,
favorites: Boolean(isFavoritesPinned),
favorites: favoritesPinned,
}));
setSortingRules({
sortBy: sortedByColumn,
sortOrder,
isFavoritesPinned: favoritesPinned,
});

// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
loading,
Expand All @@ -504,9 +535,12 @@ export const PaginatedProjectFeatureToggles = ({
isFavoritesPinned,
]);

const showPaginationBar = Boolean(total && total > 25);
const showPaginationBar = Boolean(total && total > DEFAULT_PAGE_LIMIT);
const style = showPaginationBar
? { borderBottomLeftRadius: 0, borderBottomRightRadius: 0 }
? {
borderBottomLeftRadius: 0,
borderBottomRightRadius: 0,
}
: {};

return (
Expand Down
31 changes: 26 additions & 5 deletions frontend/src/component/project/Project/ProjectOverview.tsx
Expand Up @@ -12,10 +12,14 @@ import { ProjectStats } from './ProjectStats/ProjectStats';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { useUiFlag } from 'hooks/useUiFlag';
import { useFeatureSearch } from 'hooks/api/getters/useFeatureSearch/useFeatureSearch';
import { PaginatedProjectFeatureToggles } from './ProjectFeatureToggles/PaginatedProjectFeatureToggles';
import {
ISortingRules,
PaginatedProjectFeatureToggles,
} from './ProjectFeatureToggles/PaginatedProjectFeatureToggles';
import { useSearchParams } from 'react-router-dom';

import { PaginationBar } from 'component/common/PaginationBar/PaginationBar';
import { SortingRule } from 'react-table';

const refreshInterval = 15 * 1000;

Expand All @@ -38,28 +42,43 @@ const StyledContentContainer = styled(Box)(() => ({
minWidth: 0,
}));

export const DEFAULT_PAGE_LIMIT = 25;

const PaginatedProjectOverview = () => {
const projectId = useRequiredPathParam('projectId');
const [searchParams, setSearchParams] = useSearchParams();
const { project, loading: projectLoading } = useProject(projectId, {
refreshInterval,
});
const [pageLimit, setPageLimit] = useState(25);
const [pageLimit, setPageLimit] = useState(DEFAULT_PAGE_LIMIT);
const [currentOffset, setCurrentOffset] = useState(0);

const [searchValue, setSearchValue] = useState(
searchParams.get('search') || '',
);

const [sortingRules, setSortingRules] = useState<ISortingRules>({
sortBy: 'createdBy',
sortOrder: 'desc',
isFavoritesPinned: false,
});

const {
features: searchFeatures,
total,
refetch,
loading,
initialLoad,
} = useFeatureSearch(currentOffset, pageLimit, projectId, searchValue, {
refreshInterval,
});
} = useFeatureSearch(
currentOffset,
pageLimit,
sortingRules,
projectId,
searchValue,
{
refreshInterval,
},
);

const { members, features, health, description, environments, stats } =
project;
Expand Down Expand Up @@ -102,6 +121,8 @@ const PaginatedProjectOverview = () => {
total={total}
searchValue={searchValue}
setSearchValue={setSearchValue}
sortingRules={sortingRules}
setSortingRules={setSortingRules}
paginationBar={
<StickyPaginationBar>
<PaginationBar
Expand Down
Expand Up @@ -4,6 +4,7 @@ import { IFeatureToggleListItem } from 'interfaces/featureToggle';
import { formatApiPath } from 'utils/formatPath';
import handleErrorResponses from '../httpErrorResponseHandler';
import { translateToQueryParams } from './searchToQueryParams';
import { ISortingRules } from 'component/project/Project/ProjectFeatureToggles/PaginatedProjectFeatureToggles';

type IFeatureSearchResponse = {
features: IFeatureToggleListItem[];
Expand Down Expand Up @@ -62,6 +63,7 @@ const createFeatureSearch = () => {
return (
offset: number,
limit: number,
sortingRules: ISortingRules,
projectId = '',
searchValue = '',
options: SWRConfiguration = {},
Expand All @@ -71,6 +73,7 @@ const createFeatureSearch = () => {
offset,
limit,
searchValue,
sortingRules,
);

useEffect(() => {
Expand Down Expand Up @@ -113,9 +116,11 @@ const getFeatureSearchFetcher = (
offset: number,
limit: number,
searchValue: string,
sortingRules: ISortingRules,
) => {
const searchQueryParams = translateToQueryParams(searchValue);
const KEY = `api/admin/search/features?projectId=${projectId}&offset=${offset}&limit=${limit}&${searchQueryParams}`;
const sortQueryParams = translateToSortQueryParams(sortingRules);
const KEY = `api/admin/search/features?projectId=${projectId}&offset=${offset}&limit=${limit}&${searchQueryParams}&${sortQueryParams}`;
const fetcher = () => {
const path = formatApiPath(KEY);
return fetch(path, {
Expand All @@ -130,3 +135,9 @@ const getFeatureSearchFetcher = (
KEY,
};
};

const translateToSortQueryParams = (sortingRules: ISortingRules) => {
const { sortBy, sortOrder, isFavoritesPinned } = sortingRules;
const sortQueryParams = `sortBy=${sortBy}&sortOrder=${sortOrder}&favoritesFirst=${isFavoritesPinned}`;
return sortQueryParams;
};
Expand Up @@ -713,7 +713,7 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
const [, envName] = sortBy.split(':');
query = query
.orderByRaw(
`CASE WHEN feature_environments.environment = ? THEN feature_environments.enabled ELSE NULL END ${validatedSortOrder}`,
`CASE WHEN feature_environments.environment = ? THEN feature_environments.enabled ELSE NULL END ${validatedSortOrder} NULLS LAST`,
[envName],
)
.orderBy('created_at', 'asc');
Expand All @@ -733,6 +733,7 @@ class FeatureStrategiesStore implements IFeatureStrategiesStore {
.select(selectColumns)
.limit(limit * environmentCount)
.offset(offset * environmentCount);

const rows = await query;

if (rows.length > 0) {
Expand Down

0 comments on commit 4e1040c

Please sign in to comment.