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 107 108 109 110 111 112 | 1x 15x 15x 8x 7x 3x 7x 4x 1x | /**
* Speaker Status Service (Story 5.4)
*
* HTTP client for Speaker Status Management APIs
* Features:
* - Update speaker status with workflow validation
* - Get speaker status history with audit trail
* - Get status dashboard summary with acceptance metrics
* - JWT authentication via interceptors
* - Error handling with correlation IDs
*/
import apiClient from '@/services/api/apiClient';
import type { components } from '@/types/generated/speakers-api.types';
import type { SpeakerWorkflowState } from '@/types/speakerPool.types';
// Type aliases for generated types
type UpdateStatusRequest = Omit<components['schemas']['UpdateStatusRequest'], 'newStatus'> & {
newStatus: SpeakerWorkflowState;
};
type SpeakerStatusResponse = components['schemas']['SpeakerStatusResponse'];
type StatusHistoryItem = components['schemas']['StatusHistoryItem'];
type StatusSummaryResponse = components['schemas']['StatusSummaryResponse'];
// API base path
const EVENTS_API_PATH = '/events';
/**
* Speaker Status Service
*
* Handles all HTTP requests for speaker status management
*/
class SpeakerStatusService {
/**
* Update speaker status.
*
* Valid transitions are defined in ADR-009 ยง0.2 (target model: 8-state
* machine). Invalid transitions are rejected server-side; DECLINED is
* reachable from every non-terminal state.
*
* @param eventCode Event code (e.g., "BATbern56")
* @param speakerId Speaker pool UUID
* @param newStatus New workflow state
* @param reason Optional reason for status change (max 2000 characters)
* @returns Updated speaker status with change details
* @throws Error if validation fails (400), speaker not found (404), or invalid transition (422)
*/
async updateStatus(
eventCode: string,
speakerId: string,
newStatus: SpeakerWorkflowState,
reason?: string
): Promise<SpeakerStatusResponse> {
const request: UpdateStatusRequest = {
newStatus,
...(reason && { reason }),
};
const response = await apiClient.put<SpeakerStatusResponse>(
`${EVENTS_API_PATH}/${eventCode}/speakers/${speakerId}/status`,
request
);
return response.data;
}
/**
* Get speaker status history (AC3-4, AC15)
*
* Retrieves complete status change history with timestamps, organizers, and reasons
*
* @param eventCode Event code (e.g., "BATbern56")
* @param speakerId Speaker pool UUID
* @returns Array of status history items ordered by changedAt (newest first)
* @throws Error if speaker not found (404) or unauthorized (401, 403)
*/
async getStatusHistory(eventCode: string, speakerId: string): Promise<StatusHistoryItem[]> {
const response = await apiClient.get<StatusHistoryItem[]>(
`${EVENTS_API_PATH}/${eventCode}/speakers/${speakerId}/status/history`
);
return response.data;
}
/**
* Get status dashboard summary (AC5-6, AC13)
*
* Returns aggregated speaker status metrics including:
* - Status counts by workflow state
* - Acceptance rate percentage
* - Threshold status (min/max slots)
* - Overflow detection
*
* @param eventCode Event code (e.g., "BATbern56")
* @returns Status summary with counts, rates, and threshold indicators
* @throws Error if event not found (404) or unauthorized (401, 403)
*/
async getStatusSummary(eventCode: string): Promise<StatusSummaryResponse> {
const response = await apiClient.get<StatusSummaryResponse>(
`${EVENTS_API_PATH}/${eventCode}/speakers/status-summary`
);
return response.data;
}
}
// Export singleton instance
export const speakerStatusService = new SpeakerStatusService();
// Export class for testing
export default SpeakerStatusService;
|