All files / src/hooks useEventTeaserImages.ts

90.9% Statements 20/22
20% Branches 1/5
83.33% Functions 10/12
90.9% Lines 20/22

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                                      3x     6x           3x                     3x 40x   40x   3x 2x     2x     2x         3x 40x   40x 3x   2x         3x 40x   40x   3x   2x        
/**
 * Hooks for teaser image management (event-specific or global).
 *
 * Pass eventCode for event-specific images, or null/undefined for global images
 * (shown on all event presentations).
 *
 * 3-phase presigned PUT upload pattern:
 * 1. POST .../upload-url → { uploadUrl, s3Key }
 * 2. Client PUT to S3 directly (no backend involvement)
 * 3. POST .../confirm → TeaserImageItem
 */
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import axios from 'axios';
import { eventApiClient } from '@/services/eventApiClient';
import type { components } from '@/types/generated/events-core-api.types';
 
type TeaserImageUploadUrlRequest = components['schemas']['TeaserImageUploadUrlRequest'];
type TeaserImageUpdateRequest = components['schemas']['TeaserImageUpdateRequest'];
 
const GLOBAL_KEY = ['global-teaser-images'] as const;
 
function cacheKey(eventCode?: string | null) {
  return eventCode ? ['event', eventCode] : [...GLOBAL_KEY];
}
 
/**
 * Fetch teaser images. For event-specific pass eventCode; for global pass null.
 */
export const useTeaserImages = (eventCode?: string | null) => {
  return useQuery({
    queryKey: cacheKey(eventCode),
    queryFn: () => eventApiClient.listTeaserImages(eventCode),
    enabled: eventCode === null || eventCode === undefined || eventCode.length > 0,
  });
};
 
/**
 * 3-phase upload mutation.
 */
export const useUploadTeaserImage = (eventCode?: string | null) => {
  const queryClient = useQueryClient();
 
  return useMutation({
    mutationFn: async ({ file, request }: { file: File; request: TeaserImageUploadUrlRequest }) => {
      const urlResponse = await eventApiClient.requestTeaserImageUploadUrl(eventCode, request);
      await axios.put(urlResponse.uploadUrl, file, {
        headers: { 'Content-Type': file.type },
      });
      return eventApiClient.confirmTeaserImageUpload(eventCode, { s3Key: urlResponse.s3Key });
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: cacheKey(eventCode) });
    },
  });
};
 
export const useDeleteTeaserImage = (eventCode?: string | null) => {
  const queryClient = useQueryClient();
 
  return useMutation({
    mutationFn: (imageId: string) => eventApiClient.deleteTeaserImage(eventCode, imageId),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: cacheKey(eventCode) });
    },
  });
};
 
export const useUpdateTeaserImagePosition = (eventCode?: string | null) => {
  const queryClient = useQueryClient();
 
  return useMutation({
    mutationFn: ({ imageId, request }: { imageId: string; request: TeaserImageUpdateRequest }) =>
      eventApiClient.updateTeaserImage(eventCode, imageId, request),
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: cacheKey(eventCode) });
    },
  });
};