feat: enhance session management by resolving collaborators to users and integrating CollaboratorDisplay component across motivators and sessions pages
This commit is contained in:
82
src/components/ui/CollaboratorDisplay.tsx
Normal file
82
src/components/ui/CollaboratorDisplay.tsx
Normal file
@@ -0,0 +1,82 @@
|
||||
import { getGravatarUrl } from '@/lib/gravatar';
|
||||
|
||||
interface CollaboratorDisplayProps {
|
||||
collaborator: {
|
||||
raw: string;
|
||||
matchedUser: {
|
||||
id: string;
|
||||
email: string;
|
||||
name: string | null;
|
||||
} | null;
|
||||
};
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
showEmail?: boolean;
|
||||
}
|
||||
|
||||
const sizeConfig = {
|
||||
sm: { avatar: 24, text: 'text-sm', gap: 'gap-1.5' },
|
||||
md: { avatar: 32, text: 'text-base', gap: 'gap-2' },
|
||||
lg: { avatar: 40, text: 'text-lg', gap: 'gap-3' },
|
||||
};
|
||||
|
||||
export function CollaboratorDisplay({
|
||||
collaborator,
|
||||
size = 'md',
|
||||
showEmail = false,
|
||||
}: CollaboratorDisplayProps) {
|
||||
const { raw, matchedUser } = collaborator;
|
||||
const config = sizeConfig[size];
|
||||
|
||||
// If we have a matched user, show their avatar and name
|
||||
if (matchedUser) {
|
||||
const displayName = matchedUser.name || matchedUser.email.split('@')[0];
|
||||
|
||||
return (
|
||||
<div className={`flex items-center ${config.gap}`}>
|
||||
{/* eslint-disable-next-line @next/next/no-img-element */}
|
||||
<img
|
||||
src={getGravatarUrl(matchedUser.email, config.avatar * 2)}
|
||||
alt={displayName}
|
||||
width={config.avatar}
|
||||
height={config.avatar}
|
||||
className="rounded-full border border-border"
|
||||
/>
|
||||
<div className="min-w-0">
|
||||
<span className={`${config.text} font-medium text-foreground truncate block`}>
|
||||
{displayName}
|
||||
</span>
|
||||
{showEmail && matchedUser.name && (
|
||||
<span className="text-xs text-muted truncate block">{matchedUser.email}</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// No match - just show the raw name with a default person icon
|
||||
return (
|
||||
<div className={`flex items-center ${config.gap}`}>
|
||||
<div
|
||||
className="flex items-center justify-center rounded-full border border-border bg-card-hover"
|
||||
style={{ width: config.avatar, height: config.avatar }}
|
||||
>
|
||||
<svg
|
||||
className="text-muted"
|
||||
style={{ width: config.avatar * 0.5, height: config.avatar * 0.5 }}
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
viewBox="0 0 24 24"
|
||||
>
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<span className={`${config.text} text-muted truncate`}>{raw}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ export { Avatar } from './Avatar';
|
||||
export { Badge } from './Badge';
|
||||
export { Button } from './Button';
|
||||
export { Card, CardHeader, CardTitle, CardDescription, CardContent, CardFooter } from './Card';
|
||||
export { CollaboratorDisplay } from './CollaboratorDisplay';
|
||||
export { Input } from './Input';
|
||||
export { Modal, ModalFooter } from './Modal';
|
||||
export { Textarea } from './Textarea';
|
||||
|
||||
Reference in New Issue
Block a user