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 | 1x 21x 21x 21x 21x 21x 21x 21x | import React from 'react';
import { Box, Typography, IconButton, Chip, Divider } from '@mui/material';
import { Close as CloseIcon } from '@mui/icons-material';
import { useTranslation } from 'react-i18next';
import { usePublicUser } from '@/hooks/useUserPortrait';
import type { SpeakerPoolEntry } from '@/types/speakerPool.types';
interface SpeakerDrawerHeaderProps {
speaker: SpeakerPoolEntry | null;
onClose: () => void;
}
export const SpeakerDrawerHeader: React.FC<SpeakerDrawerHeaderProps> = ({ speaker, onClose }) => {
const { t } = useTranslation('organizer');
// Epic 11 bug fix 2026-05-19 — once CONTACTED→READY promotes the speaker, the pool
// entry carries `username` (the linked User's meaningful ID). Display the real user's
// first+last name in the header instead of the brainstorm `speakerName`, so the
// organizer sees the actual identified speaker. Falls back to `speakerName` while
// the user fetch is in flight or when no username is set yet (pre-promote).
const { data: linkedUser } = usePublicUser(speaker?.username ?? undefined);
const displayName = (() => {
Iif (linkedUser?.firstName && linkedUser?.lastName) {
return `${linkedUser.firstName} ${linkedUser.lastName}`;
}
return speaker?.speakerName ?? '';
})();
// Surface the brainstorm name as a sub-caption ONLY when it differs from the real
// name (e.g. brainstorm placeholder like "Testreferent2" vs. real "Markus Gerber").
const showBrainstormCaption =
!!linkedUser?.firstName &&
!!linkedUser?.lastName &&
!!speaker?.speakerName &&
speaker.speakerName !== displayName;
return (
<>
<Box
sx={{ p: 2, display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}
>
<Box sx={{ flex: 1, mr: 1 }}>
{speaker && (
<>
<Typography variant="h6" gutterBottom>
{displayName}
</Typography>
{showBrainstormCaption && (
<Typography
variant="caption"
color="text.secondary"
sx={{ display: 'block', mt: -0.5, mb: 0.5 }}
data-testid="drawer-header-brainstorm-name"
>
{t('speakerDrawer.brainstormNameLabel', 'brainstormed as')} {speaker.speakerName}
</Typography>
)}
{speaker.company && (
<Typography variant="body2" color="text.secondary" gutterBottom>
{speaker.company}
</Typography>
)}
<Box sx={{ display: 'flex', gap: 1, flexWrap: 'wrap' }}>
<Chip
label={t(`speakerStatus.${speaker.status}`, speaker.status)}
size="small"
color={
speaker.status === 'ACCEPTED'
? 'success'
: speaker.status === 'DECLINED'
? 'error'
: speaker.status === 'INVITED'
? 'info'
: speaker.status === 'CONTACTED'
? 'warning'
: 'default'
}
/>
</Box>
</>
)}
</Box>
<IconButton onClick={onClose} size="small">
<CloseIcon />
</IconButton>
</Box>
<Divider />
</>
);
};
|