All files / src/components/shared OrganizerChip.tsx

100% Statements 10/10
76.92% Branches 10/13
100% Functions 3/3
100% Lines 8/8

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                                                            6x 6x     7x             65x 65x   6x 65x   65x                          
/**
 * OrganizerChip — the canonical "assigned organizer" display used across the app
 * (speaker kanban, Cockpit attention cards, task cards/lists).
 *
 * Renders an outlined chip with an initials avatar + the organizer's "First Last"
 * display name, resolved from the organizer directory where `id === username`
 * (see OrganizerSelect). Falls back to the raw username when the directory has no
 * match, and renders nothing when no username is given.
 *
 * Self-resolving by default (calls `useOrganizers`); pass an `organizers` array to
 * reuse a directory the parent already fetched.
 */
 
import React from 'react';
import { Avatar, Chip } from '@mui/material';
import type { ChipProps } from '@mui/material';
import { useOrganizers, type Organizer } from '@/components/shared/OrganizerSelect';
 
export interface OrganizerChipProps {
  /** Organizer username (id === username in the directory). */
  username?: string | null;
  /** Optional pre-fetched directory to avoid a duplicate fetch. */
  organizers?: Organizer[];
  size?: ChipProps['size'];
  sx?: ChipProps['sx'];
  'data-testid'?: string;
}
 
/** First-letter-of-up-to-two name parts; handles "First Last" and "first.last". */
function initials(text: string): string {
  const parts = text.split(/[.\s_-]+/).filter(Boolean);
  return (parts[0]?.[0] ?? '').concat(parts[1]?.[0] ?? '').toUpperCase() || '?';
}
 
export const OrganizerChip: React.FC<OrganizerChipProps> = ({
  username,
  organizers,
  size = 'small',
  sx,
  'data-testid': testId,
}) => {
  const { organizers: fetched } = useOrganizers();
  if (!username) return null;
 
  const directory = organizers ?? fetched;
  const name = directory.find((o) => o.id === username)?.name ?? username;
 
  return (
    <Chip
      size={size}
      variant="outlined"
      label={name}
      avatar={<Avatar sx={{ width: 18, height: 18, fontSize: '0.6rem' }}>{initials(name)}</Avatar>}
      data-testid={testId}
      sx={{ height: 22, '& .MuiChip-label': { fontSize: '0.7rem', px: 0.75 }, ...sx }}
    />
  );
};
 
export default OrganizerChip;