mirror of
https://github.com/portainer/portainer.git
synced 2025-08-05 13:55:21 +02:00
refactor(groups): migrate groups selectors to react [EE-3842] (#8936)
This commit is contained in:
parent
2018529add
commit
e91b4f5c83
38 changed files with 543 additions and 627 deletions
|
@ -17,6 +17,8 @@ import { ReactNode, useMemo } from 'react';
|
|||
import clsx from 'clsx';
|
||||
import _ from 'lodash';
|
||||
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
import { IconProps } from '@@/Icon';
|
||||
|
||||
import { DatatableHeader } from './DatatableHeader';
|
||||
|
@ -32,7 +34,7 @@ import { TableRow } from './TableRow';
|
|||
export interface Props<
|
||||
D extends Record<string, unknown>,
|
||||
TSettings extends BasicTableSettings = BasicTableSettings
|
||||
> {
|
||||
> extends AutomationTestingProps {
|
||||
dataset: D[];
|
||||
columns: TableOptions<D>['columns'];
|
||||
renderTableSettings?(instance: TableInstance<D>): ReactNode;
|
||||
|
@ -82,6 +84,7 @@ export function Datatable<D extends Record<string, unknown>>({
|
|||
highlightedItemId,
|
||||
noWidget,
|
||||
getRowCanExpand,
|
||||
'data-cy': dataCy,
|
||||
}: Props<D>) {
|
||||
const isServerSidePagination = typeof pageCount !== 'undefined';
|
||||
const enableRowSelection = getIsSelectionEnabled(
|
||||
|
@ -156,6 +159,7 @@ export function Datatable<D extends Record<string, unknown>>({
|
|||
emptyContentLabel={emptyContentLabel}
|
||||
isLoading={isLoading}
|
||||
onSortChange={handleSortChange}
|
||||
data-cy={dataCy}
|
||||
/>
|
||||
|
||||
<DatatableFooter
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import { Row, Table as TableInstance } from '@tanstack/react-table';
|
||||
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
import { Table } from './Table';
|
||||
|
||||
interface Props<D extends Record<string, unknown>> {
|
||||
interface Props<D extends Record<string, unknown>>
|
||||
extends AutomationTestingProps {
|
||||
tableInstance: TableInstance<D>;
|
||||
renderRow(row: Row<D>): React.ReactNode;
|
||||
onSortChange?(colId: string, desc: boolean): void;
|
||||
|
@ -16,12 +19,13 @@ export function DatatableContent<D extends Record<string, unknown>>({
|
|||
onSortChange,
|
||||
isLoading,
|
||||
emptyContentLabel,
|
||||
'data-cy': dataCy,
|
||||
}: Props<D>) {
|
||||
const headerGroups = tableInstance.getHeaderGroups();
|
||||
const pageRowModel = tableInstance.getPaginationRowModel();
|
||||
|
||||
return (
|
||||
<Table>
|
||||
<Table data-cy={dataCy}>
|
||||
<thead>
|
||||
{headerGroups.map((headerGroup) => (
|
||||
<Table.HeaderRow<D>
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import clsx from 'clsx';
|
||||
import { PropsWithChildren } from 'react';
|
||||
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
import { TableContainer } from './TableContainer';
|
||||
import { TableActions } from './TableActions';
|
||||
import { TableFooter } from './TableFooter';
|
||||
|
@ -12,14 +14,19 @@ import { TableHeaderCell } from './TableHeaderCell';
|
|||
import { TableHeaderRow } from './TableHeaderRow';
|
||||
import { TableRow } from './TableRow';
|
||||
|
||||
interface Props {
|
||||
interface Props extends AutomationTestingProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
function MainComponent({ children, className }: PropsWithChildren<Props>) {
|
||||
function MainComponent({
|
||||
children,
|
||||
className,
|
||||
'data-cy': dataCy,
|
||||
}: PropsWithChildren<Props>) {
|
||||
return (
|
||||
<div className="table-responsive">
|
||||
<table
|
||||
data-cy={dataCy}
|
||||
className={clsx(
|
||||
'table-hover table-filters nowrap-cells table',
|
||||
className
|
||||
|
|
|
@ -14,6 +14,9 @@ export function createSelectColumn<T>(): ColumnDef<T> {
|
|||
indeterminate={table.getIsSomeRowsSelected()}
|
||||
onChange={table.getToggleAllRowsSelectedHandler()}
|
||||
disabled={table.getRowModel().rows.every((row) => !row.getCanSelect())}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
cell: ({ row, table }) => (
|
||||
|
@ -24,6 +27,8 @@ export function createSelectColumn<T>(): ColumnDef<T> {
|
|||
onChange={row.getToggleSelectedHandler()}
|
||||
disabled={!row.getCanSelect()}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
if (e.shiftKey) {
|
||||
const { rows, rowsById } = table.getRowModel();
|
||||
const rowsToToggle = getRowRange(rows, row.id, lastSelectedId);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { useMemo } from 'react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useStore } from 'zustand';
|
||||
|
||||
import { useSearchBarState } from './SearchBar';
|
||||
|
@ -27,3 +27,23 @@ export function useTableState<
|
|||
[settings, search, setSearch]
|
||||
);
|
||||
}
|
||||
|
||||
export function useTableStateWithoutStorage(
|
||||
defaultSortKey: string
|
||||
): BasicTableSettings & {
|
||||
setSearch: (search: string) => void;
|
||||
search: string;
|
||||
} {
|
||||
const [search, setSearch] = useState('');
|
||||
const [pageSize, setPageSize] = useState(10);
|
||||
const [sortBy, setSortBy] = useState({ id: defaultSortKey, desc: false });
|
||||
|
||||
return {
|
||||
search,
|
||||
setSearch,
|
||||
pageSize,
|
||||
setPageSize,
|
||||
setSortBy: (id: string, desc: boolean) => setSortBy({ id, desc }),
|
||||
sortBy,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
import { EdgeTypes, EnvironmentId } from '@/react/portainer/environments/types';
|
||||
|
||||
import { EdgeGroupAssociationTable } from './EdgeGroupAssociationTable';
|
||||
|
||||
export function AssociatedEdgeEnvironmentsSelector({
|
||||
onChange,
|
||||
value,
|
||||
}: {
|
||||
onChange: (
|
||||
value: EnvironmentId[],
|
||||
meta: { type: 'add' | 'remove'; value: EnvironmentId }
|
||||
) => void;
|
||||
value: EnvironmentId[];
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<div className="col-sm-12 small text-muted">
|
||||
You can select which environment should be part of this group by moving
|
||||
them to the associated environments table. Simply click on any
|
||||
environment entry to move it from one table to the other.
|
||||
</div>
|
||||
|
||||
<div className="col-sm-12 mt-4">
|
||||
<div className="flex">
|
||||
<div className="w-1/2">
|
||||
<EdgeGroupAssociationTable
|
||||
title="Available environments"
|
||||
emptyContentLabel="No environment available"
|
||||
query={{
|
||||
types: EdgeTypes,
|
||||
}}
|
||||
onClickRow={(env) => {
|
||||
if (!value.includes(env.Id)) {
|
||||
onChange([...value, env.Id], { type: 'add', value: env.Id });
|
||||
}
|
||||
}}
|
||||
data-cy="edgeGroupCreate-availableEndpoints"
|
||||
hideEnvironmentIds={value}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-1/2">
|
||||
<EdgeGroupAssociationTable
|
||||
title="Associated environments"
|
||||
emptyContentLabel="No associated environment'"
|
||||
query={{
|
||||
types: EdgeTypes,
|
||||
endpointIds: value,
|
||||
}}
|
||||
onClickRow={(env) => {
|
||||
if (value.includes(env.Id)) {
|
||||
onChange(
|
||||
value.filter((id) => id !== env.Id),
|
||||
{ type: 'remove', value: env.Id }
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
117
app/react/edge/components/EdgeGroupAssociationTable.tsx
Normal file
117
app/react/edge/components/EdgeGroupAssociationTable.tsx
Normal file
|
@ -0,0 +1,117 @@
|
|||
import { createColumnHelper } from '@tanstack/react-table';
|
||||
import { truncate } from 'lodash';
|
||||
import { useMemo, useState } from 'react';
|
||||
|
||||
import { useEnvironmentList } from '@/react/portainer/environments/queries';
|
||||
import {
|
||||
Environment,
|
||||
EnvironmentId,
|
||||
} from '@/react/portainer/environments/types';
|
||||
import { useGroups } from '@/react/portainer/environments/environment-groups/queries';
|
||||
import { useTags } from '@/portainer/tags/queries';
|
||||
import { EnvironmentsQueryParams } from '@/react/portainer/environments/environment.service';
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
import { useTableStateWithoutStorage } from '@@/datatables/useTableState';
|
||||
import { Datatable, TableRow } from '@@/datatables';
|
||||
|
||||
type DecoratedEnvironment = Environment & {
|
||||
Tags: string[];
|
||||
Group: string;
|
||||
};
|
||||
|
||||
const columHelper = createColumnHelper<DecoratedEnvironment>();
|
||||
|
||||
const columns = [
|
||||
columHelper.accessor('Name', {
|
||||
header: 'Name',
|
||||
id: 'Name',
|
||||
cell: ({ getValue }) => truncate(getValue(), { length: 64 }),
|
||||
}),
|
||||
columHelper.accessor('Group', {
|
||||
header: 'Group',
|
||||
id: 'Group',
|
||||
cell: ({ getValue }) => truncate(getValue(), { length: 64 }),
|
||||
}),
|
||||
columHelper.accessor((row) => row.Tags.join(','), {
|
||||
header: 'Tags',
|
||||
id: 'tags',
|
||||
enableSorting: false,
|
||||
cell: ({ getValue }) => truncate(getValue(), { length: 64 }),
|
||||
}),
|
||||
];
|
||||
|
||||
export function EdgeGroupAssociationTable({
|
||||
title,
|
||||
query,
|
||||
emptyContentLabel,
|
||||
onClickRow,
|
||||
'data-cy': dataCy,
|
||||
hideEnvironmentIds = [],
|
||||
}: {
|
||||
title: string;
|
||||
query: EnvironmentsQueryParams;
|
||||
emptyContentLabel: string;
|
||||
onClickRow: (env: Environment) => void;
|
||||
hideEnvironmentIds?: EnvironmentId[];
|
||||
} & AutomationTestingProps) {
|
||||
const tableState = useTableStateWithoutStorage('Name');
|
||||
const [page, setPage] = useState(1);
|
||||
const environmentsQuery = useEnvironmentList({
|
||||
pageLimit: tableState.pageSize,
|
||||
page,
|
||||
search: tableState.search,
|
||||
sort: tableState.sortBy.id as 'Group' | 'Name',
|
||||
order: tableState.sortBy.desc ? 'desc' : 'asc',
|
||||
...query,
|
||||
});
|
||||
const groupsQuery = useGroups({
|
||||
enabled: environmentsQuery.environments.length > 0,
|
||||
});
|
||||
const tagsQuery = useTags({
|
||||
enabled: environmentsQuery.environments.length > 0,
|
||||
});
|
||||
|
||||
const environments: Array<DecoratedEnvironment> = useMemo(
|
||||
() =>
|
||||
environmentsQuery.environments
|
||||
.filter((e) => !hideEnvironmentIds.includes(e.Id))
|
||||
.map((env) => ({
|
||||
...env,
|
||||
Group:
|
||||
groupsQuery.data?.find((g) => g.Id === env.GroupId)?.Name || '',
|
||||
Tags: env.TagIds.map(
|
||||
(tagId) => tagsQuery.data?.find((t) => t.ID === tagId)?.Name || ''
|
||||
),
|
||||
})),
|
||||
[
|
||||
environmentsQuery.environments,
|
||||
groupsQuery.data,
|
||||
hideEnvironmentIds,
|
||||
tagsQuery.data,
|
||||
]
|
||||
);
|
||||
|
||||
const totalCount = environmentsQuery.totalCount - hideEnvironmentIds.length;
|
||||
|
||||
return (
|
||||
<Datatable<DecoratedEnvironment>
|
||||
title={title}
|
||||
columns={columns}
|
||||
settingsManager={tableState}
|
||||
dataset={environments}
|
||||
onPageChange={setPage}
|
||||
pageCount={Math.ceil(totalCount / tableState.pageSize)}
|
||||
renderRow={(row) => (
|
||||
<TableRow<DecoratedEnvironment>
|
||||
cells={row.getVisibleCells()}
|
||||
onClick={() => onClickRow(row.original)}
|
||||
/>
|
||||
)}
|
||||
emptyContentLabel={emptyContentLabel}
|
||||
data-cy={dataCy}
|
||||
disableSelect
|
||||
totalCount={totalCount}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -14,6 +14,7 @@ import {
|
|||
import { EnvironmentGroupId } from '@/react/portainer/environments/environment-groups/types';
|
||||
import {
|
||||
refetchIfAnyOffline,
|
||||
SortType,
|
||||
useEnvironmentList,
|
||||
} from '@/react/portainer/environments/queries/useEnvironmentList';
|
||||
import { useGroups } from '@/react/portainer/environments/environment-groups/queries';
|
||||
|
@ -68,7 +69,9 @@ export function EnvironmentList({ onClickBrowse, onRefresh }: Props) {
|
|||
'group',
|
||||
[]
|
||||
);
|
||||
const [sortByFilter, setSortByFilter] = useSearchBarState('sortBy');
|
||||
const [sortByFilter, setSortByFilter] = useHomePageFilter<
|
||||
SortType | undefined
|
||||
>('sortBy', 'Name');
|
||||
const [sortByDescending, setSortByDescending] = useHomePageFilter(
|
||||
'sortOrder',
|
||||
false
|
||||
|
@ -342,7 +345,7 @@ export function EnvironmentList({ onClickBrowse, onRefresh }: Props) {
|
|||
setConnectionTypes([]);
|
||||
}
|
||||
|
||||
function sortOnchange(value: string) {
|
||||
function sortOnchange(value?: 'Name' | 'Group' | 'Status') {
|
||||
setSortByFilter(value);
|
||||
setSortByButton(!!value);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,10 @@ import { useAgentVersionsList } from '../../environments/queries/useAgentVersion
|
|||
import { EnvironmentStatus, PlatformType } from '../../environments/types';
|
||||
import { isBE } from '../../feature-flags/feature-flags.service';
|
||||
import { useGroups } from '../../environments/environment-groups/queries';
|
||||
import {
|
||||
SortOptions,
|
||||
SortType,
|
||||
} from '../../environments/queries/useEnvironmentList';
|
||||
|
||||
import { HomepageFilter } from './HomepageFilter';
|
||||
import { SortbySelector } from './SortbySelector';
|
||||
|
@ -17,7 +21,7 @@ const status = [
|
|||
{ value: EnvironmentStatus.Down, label: 'Down' },
|
||||
];
|
||||
|
||||
const sortByOptions = ['Name', 'Group', 'Status'].map((v) => ({
|
||||
const sortByOptions = SortOptions.map((v) => ({
|
||||
value: v,
|
||||
label: v,
|
||||
}));
|
||||
|
@ -60,8 +64,8 @@ export function EnvironmentListFilters({
|
|||
setAgentVersions: (value: string[]) => void;
|
||||
agentVersions: string[];
|
||||
|
||||
sortByState: string;
|
||||
sortOnChange: (value: string) => void;
|
||||
sortByState?: SortType;
|
||||
sortOnChange: (value: SortType) => void;
|
||||
|
||||
sortOnDescending: () => void;
|
||||
sortByDescending: boolean;
|
||||
|
|
|
@ -3,16 +3,18 @@ import clsx from 'clsx';
|
|||
import { Option, PortainerSelect } from '@@/form-components/PortainerSelect';
|
||||
import { TableHeaderSortIcons } from '@@/datatables/TableHeaderSortIcons';
|
||||
|
||||
import { SortType } from '../../environments/queries/useEnvironmentList';
|
||||
|
||||
import styles from './SortbySelector.module.css';
|
||||
|
||||
interface Props {
|
||||
filterOptions: Option<string>[];
|
||||
onChange: (value: string) => void;
|
||||
filterOptions: Option<SortType>[];
|
||||
onChange: (value: SortType) => void;
|
||||
onDescending: () => void;
|
||||
placeHolder: string;
|
||||
sortByDescending: boolean;
|
||||
sortByButton: boolean;
|
||||
value: string;
|
||||
value?: SortType;
|
||||
}
|
||||
|
||||
export function SortbySelector({
|
||||
|
@ -30,7 +32,7 @@ export function SortbySelector({
|
|||
<PortainerSelect
|
||||
placeholder={placeHolder}
|
||||
options={filterOptions}
|
||||
onChange={(option) => onChange(option || '')}
|
||||
onChange={(option: SortType) => onChange(option || '')}
|
||||
isClearable
|
||||
value={value}
|
||||
/>
|
||||
|
|
|
@ -11,7 +11,7 @@ import { Link } from '@@/Link';
|
|||
import { useTableState } from '@@/datatables/useTableState';
|
||||
|
||||
import { isBE } from '../../feature-flags/feature-flags.service';
|
||||
import { refetchIfAnyOffline } from '../queries/useEnvironmentList';
|
||||
import { isSortType, refetchIfAnyOffline } from '../queries/useEnvironmentList';
|
||||
|
||||
import { columns } from './columns';
|
||||
import { EnvironmentListItem } from './types';
|
||||
|
@ -36,7 +36,7 @@ export function EnvironmentsDatatable({
|
|||
excludeSnapshots: true,
|
||||
page: page + 1,
|
||||
pageLimit: tableState.pageSize,
|
||||
sort: tableState.sortBy.id,
|
||||
sort: isSortType(tableState.sortBy.id) ? tableState.sortBy.id : undefined,
|
||||
order: tableState.sortBy.desc ? 'desc' : 'asc',
|
||||
},
|
||||
{ enabled: groupsQuery.isSuccess, refetchInterval: refetchIfAnyOffline }
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
import { EnvironmentId } from '../../types';
|
||||
|
||||
import { GroupAssociationTable } from './GroupAssociationTable';
|
||||
|
||||
export function AssociatedEnvironmentsSelector({
|
||||
onChange,
|
||||
value,
|
||||
}: {
|
||||
onChange: (
|
||||
value: EnvironmentId[],
|
||||
meta: { type: 'add' | 'remove'; value: EnvironmentId }
|
||||
) => void;
|
||||
value: EnvironmentId[];
|
||||
}) {
|
||||
return (
|
||||
<>
|
||||
<div className="col-sm-12 small text-muted">
|
||||
You can select which environment should be part of this group by moving
|
||||
them to the associated environments table. Simply click on any
|
||||
environment entry to move it from one table to the other.
|
||||
</div>
|
||||
|
||||
<div className="col-sm-12 mt-4">
|
||||
<div className="flex">
|
||||
<div className="w-1/2">
|
||||
<GroupAssociationTable
|
||||
title="Available environments"
|
||||
emptyContentLabel="No environment available"
|
||||
query={{
|
||||
groupIds: [1],
|
||||
}}
|
||||
onClickRow={(env) => {
|
||||
if (!value.includes(env.Id)) {
|
||||
onChange([...value, env.Id], { type: 'add', value: env.Id });
|
||||
}
|
||||
}}
|
||||
data-cy="edgeGroupCreate-availableEndpoints"
|
||||
/>
|
||||
</div>
|
||||
<div className="w-1/2">
|
||||
<GroupAssociationTable
|
||||
title="Associated environments"
|
||||
emptyContentLabel="No associated environment'"
|
||||
query={{
|
||||
endpointIds: value,
|
||||
}}
|
||||
onClickRow={(env) => {
|
||||
if (value.includes(env.Id)) {
|
||||
onChange(
|
||||
value.filter((id) => id !== env.Id),
|
||||
{ type: 'remove', value: env.Id }
|
||||
);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
import { createColumnHelper } from '@tanstack/react-table';
|
||||
import { truncate } from 'lodash';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { useEnvironmentList } from '@/react/portainer/environments/queries';
|
||||
import { Environment } from '@/react/portainer/environments/types';
|
||||
import { EnvironmentsQueryParams } from '@/react/portainer/environments/environment.service';
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
||||
import { useTableStateWithoutStorage } from '@@/datatables/useTableState';
|
||||
import { Datatable, TableRow } from '@@/datatables';
|
||||
|
||||
const columHelper = createColumnHelper<Environment>();
|
||||
|
||||
const columns = [
|
||||
columHelper.accessor('Name', {
|
||||
header: 'Name',
|
||||
id: 'Name',
|
||||
cell: ({ getValue }) => truncate(getValue(), { length: 64 }),
|
||||
}),
|
||||
];
|
||||
|
||||
export function GroupAssociationTable({
|
||||
title,
|
||||
query,
|
||||
emptyContentLabel,
|
||||
onClickRow,
|
||||
'data-cy': dataCy,
|
||||
}: {
|
||||
title: string;
|
||||
query: EnvironmentsQueryParams;
|
||||
emptyContentLabel: string;
|
||||
onClickRow?: (env: Environment) => void;
|
||||
} & AutomationTestingProps) {
|
||||
const tableState = useTableStateWithoutStorage('Name');
|
||||
const [page, setPage] = useState(1);
|
||||
const environmentsQuery = useEnvironmentList({
|
||||
pageLimit: tableState.pageSize,
|
||||
page,
|
||||
search: tableState.search,
|
||||
sort: tableState.sortBy.id as 'Name',
|
||||
order: tableState.sortBy.desc ? 'desc' : 'asc',
|
||||
...query,
|
||||
});
|
||||
|
||||
const { environments } = environmentsQuery;
|
||||
|
||||
return (
|
||||
<Datatable<Environment>
|
||||
title={title}
|
||||
columns={columns}
|
||||
settingsManager={tableState}
|
||||
dataset={environments}
|
||||
onPageChange={setPage}
|
||||
pageCount={Math.ceil(environmentsQuery.totalCount / tableState.pageSize)}
|
||||
renderRow={(row) => (
|
||||
<TableRow<Environment>
|
||||
cells={row.getVisibleCells()}
|
||||
onClick={onClickRow ? () => onClickRow(row.original) : undefined}
|
||||
/>
|
||||
)}
|
||||
emptyContentLabel={emptyContentLabel}
|
||||
data-cy={dataCy}
|
||||
disableSelect
|
||||
totalCount={environmentsQuery.totalCount}
|
||||
/>
|
||||
);
|
||||
}
|
|
@ -8,9 +8,11 @@ import { queryKeys } from './queries/query-keys';
|
|||
|
||||
export function useGroups<T = EnvironmentGroup[]>({
|
||||
select,
|
||||
}: { select?: (group: EnvironmentGroup[]) => T } = {}) {
|
||||
enabled = true,
|
||||
}: { select?: (group: EnvironmentGroup[]) => T; enabled?: boolean } = {}) {
|
||||
return useQuery(queryKeys.base(), getGroups, {
|
||||
select,
|
||||
enabled,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -60,7 +60,10 @@ export async function getEnvironments(
|
|||
query = {},
|
||||
}: GetEnvironmentsOptions = { query: {} }
|
||||
) {
|
||||
if (query.tagIds && query.tagIds.length === 0) {
|
||||
if (
|
||||
(query.tagIds && query.tagIds.length === 0) ||
|
||||
(query.endpointIds && query.endpointIds.length === 0)
|
||||
) {
|
||||
return {
|
||||
totalCount: 0,
|
||||
value: <Environment[]>[],
|
||||
|
|
|
@ -12,10 +12,16 @@ import { queryKeys } from './query-keys';
|
|||
|
||||
export const ENVIRONMENTS_POLLING_INTERVAL = 30000; // in ms
|
||||
|
||||
export const SortOptions = ['Name', 'Group', 'Status'] as const;
|
||||
export type SortType = (typeof SortOptions)[number];
|
||||
export function isSortType(value: string): value is SortType {
|
||||
return SortOptions.includes(value as SortType);
|
||||
}
|
||||
|
||||
export type Query = EnvironmentsQueryParams & {
|
||||
page?: number;
|
||||
pageLimit?: number;
|
||||
sort?: string;
|
||||
sort?: SortType;
|
||||
order?: 'asc' | 'desc';
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue