feat(frontend): manage Cognito users and groups, and test sign-in
CI / checks (pull_request) Failing after 12m24s

The Cognito panel gains a test sign-in that shows the decoded ID and
access token claims, a groups section, and a per-user panel to enable or
disable, confirm, sign out everywhere, reset or set a password and change
group membership.

Delete confirmations (row and bulk) now say what else goes, driven by
the schema's deleteImpact, and the inspector follows list refreshes
instead of the click-time snapshot.
This commit is contained in:
Zakaria
2026-10-23 17:54:15 -04:00
parent 43f4cae9b5
commit bafe799d00
15 changed files with 773 additions and 16 deletions
@@ -0,0 +1,22 @@
import {expect, test} from 'vitest'
import {describeDeleteImpact} from './deleteImpact'
import type {CloudResource} from '@/types/resource'
const impact = [
{path: 'metadata.estimatedNumberOfUsers', singular: 'user', plural: 'users'},
{path: 'metadata.appClients', singular: 'app client', plural: 'app clients'},
]
function pool(users: unknown, clients: unknown[]): CloudResource {
return {id: 'p', name: 'p', cloud: 'aws', service: 'userpools', type: 'user-pool', region: null, createdAt: null, metadata: {estimatedNumberOfUsers: users, appClients: clients}}
}
test('sums numbers and list lengths across the selection', () => {
expect(describeDeleteImpact(impact, [pool(2, [{}]), pool(1, [])])).toBe('This also deletes 3 users and 1 app client.')
})
test('leaves out zero and unreadable counts', () => {
expect(describeDeleteImpact(impact, [pool(0, [{}, {}])])).toBe('This also deletes 2 app clients.')
expect(describeDeleteImpact(impact, [pool(null, [])])).toBeNull()
expect(describeDeleteImpact(undefined, [pool(5, [])])).toBeNull()
})
+25
View File
@@ -0,0 +1,25 @@
import {getPath} from '@/lib/resourcePath'
import type {CloudResource} from '@/types/resource'
import type {ServiceSchema} from '@/types/schema'
/**
* "This also deletes 2 users and 1 app client." for the delete confirmation,
* summed over the selected resources. A count is a number at the path, or the
* length of a list there; anything else, and zero counts, are left out.
*/
export function describeDeleteImpact(impact: ServiceSchema['deleteImpact'], resources: CloudResource[]): string | null {
if (!impact?.length) return null
const parts = impact.flatMap(({path, singular, plural}) => {
const total = resources.reduce((sum, resource) => {
const value = getPath(resource, path)
if (Array.isArray(value)) return sum + value.length
return typeof value === 'number' && Number.isFinite(value) ? sum + value : sum
}, 0)
return total > 0 ? [`${total} ${total === 1 ? singular : plural}`] : []
})
if (parts.length === 0) return null
const list = parts.length === 1 ? parts[0] : `${parts.slice(0, -1).join(', ')} and ${parts[parts.length - 1]}`
return `This also deletes ${list}.`
}