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 | 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 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 2x 6x 8x 8x 8x 8x 8x 8x 8x 1x 7x 7x 7x 7x 7x 7x 7x 7x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x 8x | // Types
import { BaseVariant, FeedbackAnonymousName, FeedbackRating } from '@/types';
// Utils
import { combineClasses } from '@shared/utils';
// Components
import { ImageFallback } from '@/components/common';
import { ProfileCircleIcon } from '@/components/icons/reacts';
import { ActionMenu } from '@/components/ActionMenu';
// Constants
import { FALLBACK_SRC } from '@shared/constants';
import { RATING_PENDING_LABEL } from '@/constants/dailyFeedback';
// Styles
import './styles/user-feedback.css';
import { formatRelativeTime } from '@/utils';
const ratingClass = {
[FeedbackRating.GOOD]: 'user-feedback-rating-good',
[FeedbackRating.BAD]: 'user-feedback-rating-bad',
};
const variantTheme = {
[BaseVariant.PRIMARY]: {
root: 'user-feedback-root',
header: 'user-feedback-header',
avatarContainer: 'user-feedback-avatar-container',
avatarFull: 'user-feedback-avatar-full',
avatarIcon: 'user-feedback-avatar-icon',
userInfo: 'user-feedback-user-info',
name: 'user-feedback-name',
time: 'user-feedback-time',
ratingBadge: 'user-feedback-rating-badge',
content: 'user-feedback-content',
nameContainer: 'user-feedback-name-container',
},
};
export interface UserFeedbackProps {
name?: string;
avatar: string;
isAnonymous?: boolean;
time: string;
rating: FeedbackRating | null;
content: string;
onEdit?: () => void;
onDelete?: () => void;
className?: string;
variant?: BaseVariant;
}
export const UserFeedback = ({
name = '',
avatar,
isAnonymous = false,
time,
rating,
content,
onEdit,
onDelete,
className = '',
variant = BaseVariant.PRIMARY,
}: UserFeedbackProps) => {
const theme = variantTheme[variant];
const resolvedName = name.trim();
const displayName =
isAnonymous || !resolvedName
? FeedbackAnonymousName.ANONYMOUS
: resolvedName;
return (
<div
data-variant={variant}
className={combineClasses(theme.root, className)}
>
<div className={theme.header}>
<div className={theme.avatarContainer}>
{isAnonymous ? (
<ProfileCircleIcon className={theme.avatarIcon} />
) : (
<ImageFallback
src={avatar}
alt={resolvedName || FeedbackAnonymousName.ANONYMOUS}
width={30}
height={30}
className={theme.avatarFull}
fallbackSrc={FALLBACK_SRC.AVATAR}
/>
)}
</div>
<div className={theme.userInfo}>
<div className={theme.nameContainer}>
<span className={theme.name}>{displayName}</span>
<ActionMenu onEdit={onEdit} onDelete={onDelete} />
</div>
<span className={theme.time}>{formatRelativeTime(time)}</span>
</div>
</div>
<span
className={combineClasses(
theme.ratingBadge,
rating ? ratingClass[rating] : 'user-feedback-rating-pending',
)}
>
{rating ?? RATING_PENDING_LABEL}
</span>
<p className={theme.content}>{content}</p>
</div>
);
};
|