feat: enhance session management by resolving collaborators to users and integrating CollaboratorDisplay component across motivators and sessions pages

This commit is contained in:
Julien Froidefond
2025-11-28 11:04:58 +01:00
parent 941151553f
commit eaeb1335fa
10 changed files with 237 additions and 33 deletions

View 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>
);
}

View File

@@ -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';