| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101 |
- import { RoleImpersonationSelector } from '.'
- import { ChevronDown, User as IconUser } from 'lucide-react'
- import { useState } from 'react'
- import { Button, cn, Popover, PopoverContent, PopoverTrigger } from 'ui'
- import { getAvatarUrl, getDisplayName } from '../Auth/Users/Users.utils'
- import type { User } from '@/data/auth/users-infinite-query'
- import { useRoleImpersonationStateSnapshot } from '@/state/role-impersonation-state'
- export interface RoleImpersonationPopoverProps {
- header?: string
- serviceRoleLabel?: string
- variant?: 'regular' | 'connected-on-right' | 'connected-on-left' | 'connected-on-both'
- align?: 'center' | 'start' | 'end'
- disallowAuthenticatedOption?: boolean
- }
- export const RoleImpersonationPopover = ({
- header,
- serviceRoleLabel,
- variant = 'regular',
- align = 'end',
- disallowAuthenticatedOption = false,
- }: RoleImpersonationPopoverProps) => {
- const state = useRoleImpersonationStateSnapshot()
- const [isOpen, setIsOpen] = useState(false)
- const currentRole = state.role?.role ?? serviceRoleLabel ?? 'postgres'
- return (
- <Popover open={isOpen} onOpenChange={setIsOpen}>
- <PopoverTrigger asChild>
- <Button
- size="tiny"
- type="default"
- className={cn(
- 'h-[26px] pr-3 gap-0',
- variant === 'connected-on-right' && 'rounded-r-none border-r-0',
- variant === 'connected-on-left' && 'rounded-l-none border-l-0',
- variant === 'connected-on-both' && 'rounded-none border-x-0'
- )}
- >
- <div className="flex items-center gap-1">
- <span className="text-foreground-muted">Role</span>
- <span>{currentRole}</span>
- {state.role?.type === 'postgrest' && state.role.role === 'authenticated' && (
- <>
- {state.role.userType === 'native' && state.role.user ? (
- <UserRoleButtonSection user={state.role.user} />
- ) : state.role.userType === 'external' && state.role.externalAuth ? (
- <ExternalAuthButtonSection sub={state.role.externalAuth.sub} />
- ) : null}
- <span className="text-xs text-foreground-lighter font-light">
- {state.role.aal === 'aal2' ? 'AAL2' : 'AAL1'}
- </span>
- </>
- )}
- <ChevronDown className="text-muted" strokeWidth={1} size={12} />
- </div>
- </Button>
- </PopoverTrigger>
- <PopoverContent className="p-0 overflow-hidden w-min" side="bottom" align={align}>
- <RoleImpersonationSelector
- header={header}
- serviceRoleLabel={serviceRoleLabel}
- disallowAuthenticatedOption={disallowAuthenticatedOption}
- />
- </PopoverContent>
- </Popover>
- )
- }
- const UserRoleButtonSection = ({ user }: { user: User }) => {
- const avatarUrl = getAvatarUrl(user)
- const displayName = getDisplayName(user, user.email ?? user.phone ?? user.id ?? 'Unknown')
- return (
- <div className="flex gap-1 items-center pl-0.5 pr-1.5 h-[21px] bg-surface-200 rounded-full overflow-hidden">
- {avatarUrl ? (
- <img className="rounded-full w-[18px] h-[18px]" src={avatarUrl} alt={displayName} />
- ) : (
- <div className="rounded-full w-[18px] h-[18px] bg-surface-100 border flex items-center justify-center text-light">
- <IconUser size={12} strokeWidth={2} />
- </div>
- )}
- <span className="truncate max-w-[84px]">{displayName}</span>
- </div>
- )
- }
- const ExternalAuthButtonSection = ({ sub }: { sub: string }) => {
- return (
- <div className="flex gap-1 items-center pl-0.5 pr-1.5 h-[21px] bg-surface-200 rounded-full overflow-hidden">
- <div className="rounded-full w-[18px] h-[18px] bg-surface-100 border flex items-center justify-center text-light">
- <IconUser size={12} strokeWidth={2} />
- </div>
- <span className="truncate max-w-[84px]">{sub}</span>
- </div>
- )
- }
|