mirror of
https://github.com/portainer/portainer.git
synced 2025-08-08 23:35:31 +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
|
@ -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