diff --git a/frontend/src/components/AccessListSelector.tsx b/frontend/src/components/AccessListSelector.tsx index 7c059f57..d359d87a 100644 --- a/frontend/src/components/AccessListSelector.tsx +++ b/frontend/src/components/AccessListSelector.tsx @@ -31,8 +31,8 @@ function resolveAccessListToken(value: number | string | null | undefined): stri return trimmed; } - const parsed = Number.parseInt(trimmed, 10); - if (!Number.isNaN(parsed)) { + if (/^\d+$/.test(trimmed)) { + const parsed = Number.parseInt(trimmed, 10); return `id:${parsed}`; } @@ -46,7 +46,7 @@ function getOptionToken(acl: { id?: number | string; uuid?: string }): string | if (typeof acl.id === 'string') { const trimmed = acl.id.trim(); - if (trimmed !== '') { + if (trimmed !== '' && /^\d+$/.test(trimmed)) { const parsed = Number.parseInt(trimmed, 10); if (!Number.isNaN(parsed)) { return `id:${parsed}`; @@ -89,8 +89,8 @@ export default function AccessListSelector({ value, onChange }: AccessListSelect return; } - const numericId = Number.parseInt(newValue, 10); - if (!Number.isNaN(numericId)) { + if (/^\d+$/.test(newValue)) { + const numericId = Number.parseInt(newValue, 10); onChange(numericId); return; } diff --git a/frontend/src/components/ProxyHostForm.tsx b/frontend/src/components/ProxyHostForm.tsx index 85afdd47..c0326ebf 100644 --- a/frontend/src/components/ProxyHostForm.tsx +++ b/frontend/src/components/ProxyHostForm.tsx @@ -148,6 +148,10 @@ function normalizeNullableID(value: unknown): number | null | undefined { return null } + if (!/^\d+$/.test(trimmed)) { + return undefined + } + const parsed = Number.parseInt(trimmed, 10) return Number.isNaN(parsed) ? undefined : parsed } @@ -173,8 +177,8 @@ function resolveSelectToken(value: number | string | null | undefined): string { return trimmed } - const parsed = Number.parseInt(trimmed, 10) - if (!Number.isNaN(parsed)) { + if (/^\d+$/.test(trimmed)) { + const parsed = Number.parseInt(trimmed, 10) return `id:${parsed}` } @@ -195,8 +199,12 @@ function resolveTokenToFormValue(value: string): number | string | null { return value.slice(5) } - const parsed = Number.parseInt(value, 10) - return Number.isNaN(parsed) ? value : parsed + if (/^\d+$/.test(value)) { + const parsed = Number.parseInt(value, 10) + return Number.isNaN(parsed) ? value : parsed + } + + return value } function getEntityToken(entity: { id?: number; uuid?: string }): string | null { diff --git a/frontend/src/components/__tests__/AccessListSelector.test.tsx b/frontend/src/components/__tests__/AccessListSelector.test.tsx index 90a69963..05f9f955 100644 --- a/frontend/src/components/__tests__/AccessListSelector.test.tsx +++ b/frontend/src/components/__tests__/AccessListSelector.test.tsx @@ -163,4 +163,38 @@ describe('AccessListSelector', () => { expect(mockOnChange).toHaveBeenCalledWith(7); }); + + it('keeps a UUID-leading-digit selection stable in the trigger', () => { + const uuid = '9f63b8c9-1d26-4b2f-a2c8-001122334455'; + const mockLists = [ + { + id: undefined, + uuid, + name: 'UUID Digit Prefix ACL', + description: 'UUID-only ACL payload', + type: 'whitelist', + ip_rules: '[]', + country_codes: '', + local_network_only: false, + enabled: true, + created_at: '2024-01-01', + updated_at: '2024-01-01', + }, + ]; + + vi.mocked(useAccessListsHook.useAccessLists).mockReturnValue({ + data: mockLists as unknown as AccessList[], + } as unknown as ReturnType); + + const mockOnChange = vi.fn(); + const Wrapper = createWrapper(); + + render( + + + + ); + + expect(screen.getByRole('combobox', { name: /Access Control List/i })).toHaveTextContent('UUID Digit Prefix ACL'); + }); }); diff --git a/frontend/src/components/__tests__/ProxyHostForm-dropdown-changes.test.tsx b/frontend/src/components/__tests__/ProxyHostForm-dropdown-changes.test.tsx index c30a7141..1662a29c 100644 --- a/frontend/src/components/__tests__/ProxyHostForm-dropdown-changes.test.tsx +++ b/frontend/src/components/__tests__/ProxyHostForm-dropdown-changes.test.tsx @@ -559,7 +559,7 @@ describe('ProxyHostForm Dropdown Change Bug Fix', () => { { ...mockAccessLists[0], id: undefined, - uuid: 'acl-uuid-only', + uuid: '9f63b8c9-1d26-4b2f-a2c8-001122334455', name: 'UUID Office Network', }, ]