From 20b971dc1fa0293dd5cbbdb9ee9cf2f2c76e1adc Mon Sep 17 00:00:00 2001 From: LP B Date: Fri, 6 Mar 2026 15:00:01 +0100 Subject: [PATCH] fix(app/stack): virtual grouping in EnvSelector for non admins (#2001) --- .../StackDuplicationForm/EnvSelector.test.tsx | 25 ++++++++----------- .../StackDuplicationForm/EnvSelector.tsx | 14 ++++++----- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/app/react/docker/stacks/ItemView/StackInfoTab/StackDuplicationForm/EnvSelector.test.tsx b/app/react/docker/stacks/ItemView/StackInfoTab/StackDuplicationForm/EnvSelector.test.tsx index 9335f2f69..c7ec3f734 100644 --- a/app/react/docker/stacks/ItemView/StackInfoTab/StackDuplicationForm/EnvSelector.test.tsx +++ b/app/react/docker/stacks/ItemView/StackInfoTab/StackDuplicationForm/EnvSelector.test.tsx @@ -173,24 +173,19 @@ describe('getEnvironmentOptions', () => { }); }); - it('should create "Unassigned" group for GroupId = 1', () => { + it('should auto create an Others group if group is missing', () => { const environments: Environment[] = [ { Id: 1, Name: 'Env 1', GroupId: 1 } as Environment, + { Id: 2, Name: 'Env 2', GroupId: 2 } as Environment, ]; + const groups: EnvironmentGroup[] = []; - const result = getEnvironmentOptions([], environments); - - expect(result[0].label).toBe('Unassigned'); - expect(result[0].options[0]).toEqual({ label: 'Env 1', value: 1 }); - }); - - it('should throw error if group is missing for non-unassigned GroupId', () => { - const environments: Environment[] = [ - { Id: 1, Name: 'Env 1', GroupId: 2 } as Environment, - ]; - - expect(() => getEnvironmentOptions([], environments)).toThrow( - 'Missing group with id 2' - ); + const result = getEnvironmentOptions(groups, environments); + expect(result.length).toBe(1); + expect(result[0].label).toBe('Others'); + expect(result[0].options).toEqual([ + { label: 'Env 1', value: 1 }, + { label: 'Env 2', value: 2 }, + ]); }); }); diff --git a/app/react/docker/stacks/ItemView/StackInfoTab/StackDuplicationForm/EnvSelector.tsx b/app/react/docker/stacks/ItemView/StackInfoTab/StackDuplicationForm/EnvSelector.tsx index b8c46a20c..bbb39e182 100644 --- a/app/react/docker/stacks/ItemView/StackInfoTab/StackDuplicationForm/EnvSelector.tsx +++ b/app/react/docker/stacks/ItemView/StackInfoTab/StackDuplicationForm/EnvSelector.tsx @@ -1,4 +1,5 @@ import { useMemo } from 'react'; +import { sortBy } from 'lodash'; import { useEnvironmentList } from '@/react/portainer/environments/queries'; import { useGroups } from '@/react/portainer/environments/environment-groups/queries'; @@ -73,7 +74,11 @@ export function getEnvironmentOptions( return acc; } - const groupId = environment.GroupId; + let groupId = environment.GroupId; + if (!groups.some((g) => g.Id === groupId)) { + groupId = -1; + } + if (!acc[groupId]) { acc[groupId] = []; } @@ -87,13 +92,10 @@ export function getEnvironmentOptions( return Object.entries(groupedEnvironments).map(([groupId, envOptions]) => { const parsedGroupId = parseInt(groupId, 10); const group = groups.find((g) => g.Id === parsedGroupId); - if (!group && parsedGroupId !== 1) { - throw new Error(`Missing group with id ${groupId}`); - } return { - label: group?.Name || 'Unassigned', - options: envOptions, + label: group?.Name || 'Others', + options: sortBy(envOptions, 'label'), }; }); }