All files / src/components/organizer/SpeakerDrawer PrimaryActionSurface.tsx

81.81% Statements 9/11
50% Branches 6/12
100% Functions 1/1
90% Lines 9/10

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                                                            1x         21x 21x   21x   21x                                     21x                               21x 21x                           21x                              
/**
 * Primary action surface for the speaker detail drawer (Story 11.D.4 โ€” AC7.1).
 *
 * Renders the same primary-action button (or "Publishable" chip) that the kanban card
 * surfaces along its bottom edge โ€” but at `size="large"` and as a full-width
 * `variant="contained"` button atop the drawer body. Re-uses `getPrimaryAction.ts`
 * verbatim so the label, click handler, and disabled/tooltip state stay in sync between
 * card and drawer (per plan ยง8.5 "Leads with the same primary-action button at the top,
 * big and prominent").
 */
import React from 'react';
import { Box, Button, Chip, Stack, Tooltip } from '@mui/material';
import {
  CheckCircleOutline as CheckCircleOutlineIcon,
  Schedule as ScheduleIcon,
} from '@mui/icons-material';
import { useTranslation } from 'react-i18next';
import {
  getPrimaryAction,
  type PrimaryActionCallbacks,
  type SlotCapacityState,
} from '@/components/organizer/SpeakerStatus/getPrimaryAction';
import type { SpeakerPoolEntry } from '@/types/speakerPool.types';
 
export interface PrimaryActionSurfaceProps {
  speaker: SpeakerPoolEntry;
  callbacks: PrimaryActionCallbacks;
  slotCapacity: SlotCapacityState;
}
 
export const PrimaryActionSurface: React.FC<PrimaryActionSurfaceProps> = ({
  speaker,
  callbacks,
  slotCapacity,
}) => {
  const { t } = useTranslation(['organizer']);
  const action = getPrimaryAction(speaker, callbacks, slotCapacity, t);
 
  Iif (action.kind === 'none') return null;
 
  Iif (action.kind === 'chip') {
    return (
      <Box sx={{ px: 3, py: 2, borderBottom: 1, borderColor: 'divider' }}>
        <Tooltip title={action.tooltip ?? ''}>
          <Chip
            label={action.label}
            icon={<CheckCircleOutlineIcon />}
            color="success"
            variant="outlined"
            sx={{ width: '100%', height: 40, fontSize: '1rem' }}
            data-testid={`drawer-primary-action-chip-${speaker.id}`}
            data-action={action.testIdSuffix}
          />
        </Tooltip>
      </Box>
    );
  }
 
  const button = (
    <Button
      variant="contained"
      size="large"
      fullWidth
      disabled={action.disabled}
      onClick={action.onClick}
      data-testid={`drawer-primary-action-button-${speaker.id}`}
      data-action={action.testIdSuffix}
    >
      {action.label}
    </Button>
  );
 
  // Review patch โ€” surface the invitation response deadline alongside the primary
  // action for INVITED speakers. Restores the deadline visibility that the old
  // Overview tab carried (information regression noted by the code-review).
  const showDeadlinePill = speaker.status === 'INVITED' && Boolean(speaker.responseDeadline);
  const deadlinePill = showDeadlinePill ? (
    <Chip
      size="small"
      icon={<ScheduleIcon fontSize="small" />}
      label={t('organizer:speakerDrawer.responseDeadline', {
        date: speaker.responseDeadline,
        defaultValue: 'Response due {{date}}',
      })}
      color="default"
      variant="outlined"
      data-testid={`drawer-primary-action-deadline-${speaker.id}`}
    />
  ) : null;
 
  return (
    <Box sx={{ px: 3, py: 2, borderBottom: 1, borderColor: 'divider' }}>
      <Stack spacing={1}>
        {action.tooltip ? (
          <Tooltip title={action.tooltip}>
            <span data-testid={`drawer-primary-action-tooltip-${speaker.id}`}>{button}</span>
          </Tooltip>
        ) : (
          button
        )}
        {deadlinePill}
      </Stack>
    </Box>
  );
};