1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-05 05:45:22 +02:00

feat(edge): associate edge env to meta fields [EE-3209] (#8551)

* refactor(edge/groups): load edge groups in selector

fix(edge/stacks): remove double groups title

* feat(edge): supply meta fields to edge script [EE-5043]

* feat(edge): auto assign aeec envs to groups and tags [EE-5043]

fix [EE-5043]

fix(envs): fix global key test

* fix(edge/groups): save group type

* refactor(edge/devices): move loading of devices to table

* refactor(tags): select paramter for query

* feat(edge/devices): show meta fields

* refactor(home): simplify filter

* feat(edge/devices): filter by meta fields

* refactor(edge/devices): break filter and loading hook
This commit is contained in:
Chaim Lev-Ari 2023-03-06 22:25:04 +02:00 committed by GitHub
parent 03712966e4
commit 70710cfeb7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
32 changed files with 554 additions and 263 deletions

View file

@ -12,27 +12,24 @@ import { useSearchBarState } from '@@/datatables/SearchBar';
import { useAssociateDeviceMutation, useLicenseOverused } from '../queries';
import { columns } from './columns';
import { Filter } from './Filter';
import { useEnvironments } from './useEnvironments';
const storageKey = 'edge-devices-waiting-room';
const settingsStore = createPersistedStore(storageKey, 'Name');
interface Props {
devices: Environment[];
isLoading: boolean;
totalCount: number;
}
export function Datatable({ devices, isLoading, totalCount }: Props) {
export function Datatable() {
const associateMutation = useAssociateDeviceMutation();
const licenseOverused = useLicenseOverused();
const settings = useStore(settingsStore);
const [search, setSearch] = useSearchBarState(storageKey);
const { data: environments, totalCount, isLoading } = useEnvironments();
return (
<GenericDatatable
columns={columns}
dataset={devices}
dataset={environments}
initialPageSize={settings.pageSize}
onPageSizeChange={settings.setPageSize}
initialSortBy={settings.sortBy}
@ -62,6 +59,7 @@ export function Datatable({ devices, isLoading, totalCount }: Props) {
)}
isLoading={isLoading}
totalCount={totalCount}
description={<Filter />}
/>
);

View file

@ -0,0 +1,50 @@
import { HomepageFilter } from '@/react/portainer/HomeView/EnvironmentList/HomepageFilter';
import { useGroups } from '@/react/portainer/environments/environment-groups/queries';
import { useEdgeGroups } from '@/react/edge/edge-groups/queries/useEdgeGroups';
import { useTags } from '@/portainer/tags/queries';
import { useFilterStore } from './filter-store';
export function Filter() {
const edgeGroupsQuery = useEdgeGroups();
const groupsQuery = useGroups();
const tagsQuery = useTags();
const filterStore = useFilterStore();
if (!edgeGroupsQuery.data || !groupsQuery.data || !tagsQuery.data) {
return null;
}
return (
<div className="flex w-full gap-5 [&>*]:w-1/5">
<HomepageFilter
onChange={(f) => filterStore.setEdgeGroups(f)}
placeHolder="Edge groups"
value={filterStore.edgeGroups}
filterOptions={edgeGroupsQuery.data.map((g) => ({
label: g.Name,
value: g.Id,
}))}
/>
<HomepageFilter
onChange={(f) => filterStore.setGroups(f)}
placeHolder="Group"
value={filterStore.groups}
filterOptions={groupsQuery.data.map((g) => ({
label: g.Name,
value: g.Id,
}))}
/>
<HomepageFilter
onChange={(f) => filterStore.setTags(f)}
placeHolder="Tags"
value={filterStore.tags}
filterOptions={tagsQuery.data.map((g) => ({
label: g.Name,
value: g.ID,
}))}
/>
</div>
);
}

View file

@ -1,8 +1,8 @@
import { Column } from 'react-table';
import { CellProps, Column } from 'react-table';
import { Environment } from '@/react/portainer/environments/types';
import { WaitingRoomEnvironment } from '../types';
export const columns: readonly Column<Environment>[] = [
export const columns: readonly Column<WaitingRoomEnvironment>[] = [
{
Header: 'Name',
accessor: (row) => row.Name,
@ -21,4 +21,35 @@ export const columns: readonly Column<Environment>[] = [
canHide: false,
sortType: 'string',
},
{
Header: 'Edge Groups',
accessor: (row) => row.EdgeGroups || [],
Cell: ({ value }: CellProps<WaitingRoomEnvironment, string[]>) =>
value.join(', ') || '-',
id: 'edge-groups',
disableFilters: true,
Filter: () => null,
canHide: false,
sortType: 'string',
},
{
Header: 'Group',
accessor: (row) => row.Group || '-',
id: 'group',
disableFilters: true,
Filter: () => null,
canHide: false,
sortType: 'string',
},
{
Header: 'Tags',
accessor: (row) => row.Tags || [],
Cell: ({ value }: CellProps<WaitingRoomEnvironment, string[]>) =>
value.join(', ') || '-',
id: 'tags',
disableFilters: true,
Filter: () => null,
canHide: false,
sortType: 'string',
},
] as const;

View file

@ -0,0 +1,35 @@
import createStore from 'zustand';
import { persist } from 'zustand/middleware';
import { keyBuilder } from '@/react/hooks/useLocalStorage';
interface TableFiltersStore {
groups: number[];
setGroups(value: number[]): void;
edgeGroups: number[];
setEdgeGroups(value: number[]): void;
tags: number[];
setTags(value: number[]): void;
}
export const useFilterStore = createStore<TableFiltersStore>()(
persist(
(set) => ({
edgeGroups: [],
setEdgeGroups(edgeGroups: number[]) {
set({ edgeGroups });
},
groups: [],
setGroups(groups: number[]) {
set({ groups });
},
tags: [],
setTags(tags: number[]) {
set({ tags });
},
}),
{
name: keyBuilder('edge-devices-meta-filters'),
}
)
);

View file

@ -0,0 +1,74 @@
import _ from 'lodash';
import { useTags } from '@/portainer/tags/queries';
import { useEdgeGroups } from '@/react/edge/edge-groups/queries/useEdgeGroups';
import { useGroups } from '@/react/portainer/environments/environment-groups/queries';
import { useEnvironmentList } from '@/react/portainer/environments/queries';
import { EdgeTypes } from '@/react/portainer/environments/types';
import { WaitingRoomEnvironment } from '../types';
import { useFilterStore } from './filter-store';
export function useEnvironments() {
const filterStore = useFilterStore();
const edgeGroupsQuery = useEdgeGroups();
const filterByEnvironmentsIds = filterStore.edgeGroups.length
? _.compact(
filterStore.edgeGroups.flatMap(
(groupId) =>
edgeGroupsQuery.data?.find((g) => g.Id === groupId)?.Endpoints
)
)
: undefined;
const environmentsQuery = useEnvironmentList({
edgeDeviceUntrusted: true,
excludeSnapshots: true,
types: EdgeTypes,
tagIds: filterStore.tags.length ? filterStore.tags : undefined,
groupIds: filterStore.groups.length ? filterStore.groups : undefined,
endpointIds: filterByEnvironmentsIds,
});
const groupsQuery = useGroups({
select: (groups) =>
Object.fromEntries(groups.map((g) => [g.Id, g.Name] as const)),
});
const environmentEdgeGroupsQuery = useEdgeGroups({
select: (groups) =>
_.groupBy(
groups.flatMap((group) => {
const envs = group.Endpoints;
return envs.map((id) => ({ id, group: group.Name }));
}),
(env) => env.id
),
});
const tagsQuery = useTags({
select: (tags) =>
Object.fromEntries(tags.map((tag) => [tag.ID, tag.Name] as const)),
});
const envs: Array<WaitingRoomEnvironment> =
environmentsQuery.environments.map((env) => ({
...env,
Group: groupsQuery.data?.[env.GroupId] || '',
EdgeGroups:
environmentEdgeGroupsQuery.data?.[env.Id]?.map((env) => env.group) ||
[],
Tags:
_.compact(env.TagIds?.map((tagId) => tagsQuery.data?.[tagId])) || [],
}));
return {
data: envs,
isLoading:
environmentsQuery.isLoading ||
groupsQuery.isLoading ||
environmentEdgeGroupsQuery.isLoading ||
tagsQuery.isLoading,
totalCount: environmentsQuery.totalCount,
};
}