import {X} from 'lucide-react' import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query' import {useEffect, useState} from 'react' import {getFlatItem, setUserPoolUserGroups, updateUserPoolUser} from '@/api/cloudProxyClient' import {useAccountId} from '@/lib/accountStore' import {formatDateTime} from '@/lib/format' import type {UserPoolGroup, UserPoolUserAction} from '@/types/resource' type Props = { poolId: string username: string groups: UserPoolGroup[] usersKey: unknown[] runtimeReachable: boolean onClose: () => void } const ACTION_DONE: Record = { enable: 'User enabled.', disable: 'User disabled. New sign-ins are refused; tokens already issued last until they expire.', confirm: 'User confirmed.', resetPassword: 'Password reset. The user must use a reset code to sign in again.', signOut: 'Signed out everywhere. Refresh tokens no longer work.', setPassword: 'Password set.', } export function CognitoUserDetail({poolId, username, groups, usersKey, runtimeReachable, onClose}: Props) { const accountId = useAccountId() const queryClient = useQueryClient() const userKey = ['cognito-user', accountId, poolId, username] const [selectedGroups, setSelectedGroups] = useState([]) const [password, setPassword] = useState('') const [permanent, setPermanent] = useState(true) const [notice, setNotice] = useState('') const userQuery = useQuery({ queryKey: userKey, queryFn: ({signal}) => getFlatItem('aws', 'userpools', poolId, username, signal), enabled: runtimeReachable, }) const user = userQuery.data?.body const currentGroups = stringList(user?.groups) // Reset the checkboxes only when the stored membership changes, not on every render. const storedGroups = currentGroups.join('\n') useEffect(() => { setSelectedGroups(storedGroups ? storedGroups.split('\n') : []) }, [storedGroups]) const refresh = () => Promise.all([queryClient.invalidateQueries({queryKey: userKey}), queryClient.invalidateQueries({queryKey: usersKey})]) const actionMutation = useMutation({ mutationFn: (input: UserPoolUserAction) => updateUserPoolUser('aws', poolId, username, input), onSuccess: (_result, input) => { setNotice(ACTION_DONE[input.action]) if (input.action === 'setPassword') setPassword('') return refresh() }, }) const groupsMutation = useMutation({ mutationFn: () => setUserPoolUserGroups('aws', poolId, username, selectedGroups), onSuccess: () => { setNotice('Groups saved. They appear in tokens issued from the next sign-in.') return refresh() }, }) const pending = actionMutation.isPending || groupsMutation.isPending const error = userQuery.error ?? actionMutation.error ?? groupsMutation.error const run = (input: UserPoolUserAction) => { setNotice(''); actionMutation.mutate(input) } const groupsChanged = selectedGroups.slice().sort().join('\n') !== currentGroups.slice().sort().join('\n') const name = stringValue(user?.email) || username const attributes = Object.entries((user?.attributes ?? {}) as Record) const disabled = !runtimeReachable || pending return

User

{name}
{userQuery.isLoading &&

Loading user

} {user &&
Status
{stringValue(user.status) || '—'}
Enabled
{user.enabled === false ? 'No' : 'Yes'}
Email verified
{user.emailVerified ? 'Yes' : 'No'}
Created
{formatDateTime(userQuery.data?.timestamp ?? null) ?? '—'}
Username
{username}

Actions

{user.enabled === false ? : } {user.status === 'UNCONFIRMED' && }
{ event.preventDefault(); if (password) run({action: 'setPassword', password, permanent}) }}>

Groups

{groups.length === 0 &&

This pool has no groups. Create one in Groups above.

} {groups.map((group) => ( ))} {groups.length > 0 && }
{attributes.length > 0 &&

Attributes

{attributes.sort(([a], [b]) => a.localeCompare(b)).map(([key, value]) => ( ))}
{key}{value}
}
} {notice &&

{notice}

} {error instanceof Error &&

{error.message}

}
} function stringList(value: unknown): string[] { return Array.isArray(value) ? value.filter((entry): entry is string => typeof entry === 'string') : [] } function stringValue(value: unknown): string { return typeof value === 'string' ? value : '' }