All files / src/services qnaService.ts

0% Statements 0/8
0% Branches 0/2
0% Functions 0/5
0% Lines 0/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 62 63 64 65 66 67 68 69 70 71 72 73 74                                                                                                                                                   
/**
 * Q&A Service (Story 7.5 — "The Apéro Continues")
 *
 * HTTP client for per-session Q&A. GET is public; POST is authenticated (JWT attached by the
 * apiClient interceptor); PATCH/DELETE are organizer-only.
 */
 
import apiClient from '@/services/api/apiClient';
import type { components } from '@/types/generated/event-sessions-api.types';
 
export type QnaWindowResponse = components['schemas']['QnaWindowResponse'];
export type QnaPostResponse = components['schemas']['QnaPostResponse'];
 
function base(eventCode: string, sessionSlug: string): string {
  return `/events/${encodeURIComponent(eventCode)}/sessions/${encodeURIComponent(sessionSlug)}/qna`;
}
 
/** Public read of a session's Q&A thread. Throws 404 if no window exists yet. */
export async function getThread(
  eventCode: string,
  sessionSlug: string
): Promise<QnaWindowResponse> {
  // Public, optional read on (public) archive pages: a 401 must not force-logout the visitor —
  // the caller renders empty on error. See apiClient's skipAuthRedirect handling.
  const response = await apiClient.get<QnaWindowResponse>(base(eventCode, sessionSlug), {
    skipAuthRedirect: true,
  });
  return response.data;
}
 
/** Post a question (no parent) or an answer (parentPostId set). Authenticated. */
export async function addPost(
  eventCode: string,
  sessionSlug: string,
  body: string,
  parentPostId?: string | null
): Promise<QnaPostResponse> {
  const response = await apiClient.post<QnaPostResponse>(`${base(eventCode, sessionSlug)}/posts`, {
    body,
    ...(parentPostId ? { parentPostId } : {}),
  });
  return response.data;
}
 
/**
 * Organizer: extend (new closesAt) or close early (close=true) ALL of the event's Q&A windows
 * at once (Story 7.5 rework — event-level control replaces the old per-session PATCH).
 */
export interface AdjustEventQnaResult {
  eventCode: string;
  windowsAdjusted: number;
  status: 'OPEN' | 'FROZEN';
}
 
export async function adjustEventQna(
  eventCode: string,
  payload: { open?: boolean; closesAt?: string; close?: boolean }
): Promise<AdjustEventQnaResult> {
  const response = await apiClient.patch<AdjustEventQnaResult>(
    `/events/${encodeURIComponent(eventCode)}/qna`,
    payload
  );
  return response.data;
}
 
/** Organizer: take down a post (soft-delete tombstone). */
export async function removePost(
  eventCode: string,
  sessionSlug: string,
  postId: string
): Promise<void> {
  await apiClient.delete(`${base(eventCode, sessionSlug)}/posts/${encodeURIComponent(postId)}`);
}