Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 74x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x | import { css } from '@emotion/react';
import { memo, type ComponentPropsWithRef } from 'react';
import { FormattedMessage } from 'react-intl';
import { Avatar, Skeleton, TextOverflow, Typography, useAvatarUser } from '@allshares/studio-design-system';
import { BorderRadiusVariant } from '@allshares/studio-design-system/meta';
import { formatUserFullName, type UserContract } from '@amalia/tenants/users/types';
import {
accentuatedLabel,
defaultLabel,
dimmedLabel,
IS_COMPUTING_CLASSNAME,
} from '../../FieldValuePrettyFormat.styles';
import { LabelDisplay, LabelSize, LabelVariant, TypographyVariantMap } from '../../types';
import {
EXTERNAL_ID_TYPOGRAPHY_VARIANT,
styles,
userPrettyFormatAvatarSize,
userPrettyFormatGap,
} from './UserPrettyFormat.styles';
export type UserPrettyFormatProps = ComponentPropsWithRef<'div'> & {
/** Whether the user info is loading. This will display a Skeleton over the avatar and the full name. */
readonly isLoading?: boolean;
/**
* What is displayed under the full name.
* For example, the user's external id or the email.
*/
readonly subLabel?: string;
/** The user's first name */
readonly firstName?: UserContract['firstName'];
/** The user's last name */
readonly lastName?: UserContract['lastName'];
/** The user's avatar url */
readonly pictureURL?: UserContract['pictureURL'];
/**
* How the label is displayed.
* BLOCK is multiline, INLINE is on a single line.
*/
readonly display?: LabelDisplay.BLOCK | LabelDisplay.INLINE;
/**
* The variant of the label.
* Accentuated is bolder and colored.
*/
readonly variant?: LabelVariant;
/**
* Size of the user pretty format.
*/
readonly size?: LabelSize;
/**
* Provides a different style when the current statement is computing.
*/
readonly isComputing?: boolean;
};
export const UserPrettyFormat = memo(function UserPrettyFormat({
isLoading = false,
firstName,
lastName,
subLabel,
pictureURL,
display = LabelDisplay.BLOCK,
variant = LabelVariant.DEFAULT,
size = LabelSize.MEDIUM,
isComputing,
ref,
...props
}: UserPrettyFormatProps) {
const avatarUser = useAvatarUser({
firstName,
lastName,
pictureURL,
});
return (
<div
{...props}
ref={ref}
css={[
css`
--column-gap: ${userPrettyFormatGap[display][size][`${!!subLabel}`]}px;
`,
styles.container,
display === LabelDisplay.BLOCK && styles.containerBlockDisplay,
display === LabelDisplay.INLINE && styles.containerInlineDisplay,
isComputing && IS_COMPUTING_CLASSNAME,
]}
>
<Skeleton
css={[styles.avatarSkeleton]}
data-testid="user-field-label-avatar-skeleton"
shape={BorderRadiusVariant.ROUND}
visible={isLoading}
>
<Avatar
size={userPrettyFormatAvatarSize[display][size][`${!!subLabel}`]}
user={avatarUser}
/>
</Skeleton>
<div css={[styles.userInfoContainer, display === LabelDisplay.INLINE && styles.userInfoContainerInlineDisplay]}>
<Skeleton
css={[styles.nameSkeleton, (theme) => theme.ds.typographies[TypographyVariantMap[variant][size]]]}
data-testid="user-field-label-name-skeleton"
visible={isLoading}
>
<Typography
as={TextOverflow}
variant={TypographyVariantMap[variant][size]}
css={[
variant === LabelVariant.DEFAULT && defaultLabel,
variant === LabelVariant.ACCENTUATED && accentuatedLabel,
variant === LabelVariant.DIMMED && dimmedLabel,
]}
>
{formatUserFullName({ firstName, lastName }) || <FormattedMessage defaultMessage="Unsynced user" />}
</Typography>
</Skeleton>
{!!subLabel && (
<div css={[dimmedLabel, styles.subLabel]}>
<Typography
as={TextOverflow}
variant={EXTERNAL_ID_TYPOGRAPHY_VARIANT}
>
{subLabel}
</Typography>
</div>
)}
</div>
</div>
);
});
/*
* There are multiple possible states for this component:
*
* - Loading: We show a loading indicator using a Skeleton.
* Only the subLabel (externalId) is shown as it is the only prop we have.
*
* - User Is Not Found: We show a message saying that the user
* was not synced (yet/anymore).
* We show the subLabel (externalId) and * "User not synced" as the fullName.
* Fallback avatar should be a random "default" avatar.
* (TODO): default avatar should be handled by Avatar component
*
* - Idle: We show the fullName, the subLabel (externalId) and the avatar.
*/
|