Add live show moderation queue

This commit is contained in:
Codex Agent
2026-01-05 14:04:05 +01:00
parent 7802bed394
commit e3b7271f69
16 changed files with 829 additions and 8 deletions

View File

@@ -132,6 +132,11 @@ export type TenantPhoto = {
url: string | null;
thumbnail_url: string | null;
status: string;
live_status?: string | null;
live_approved_at?: string | null;
live_reviewed_at?: string | null;
live_rejection_reason?: string | null;
live_priority?: number | null;
is_featured: boolean;
likes_count: number;
uploaded_at: string;
@@ -911,6 +916,11 @@ function normalizePhoto(photo: TenantPhoto): TenantPhoto {
url: photo.url,
thumbnail_url: photo.thumbnail_url ?? photo.url,
status: photo.status ?? 'approved',
live_status: photo.live_status ?? null,
live_approved_at: photo.live_approved_at ?? null,
live_reviewed_at: photo.live_reviewed_at ?? null,
live_rejection_reason: photo.live_rejection_reason ?? null,
live_priority: typeof photo.live_priority === 'number' ? photo.live_priority : null,
is_featured: Boolean(photo.is_featured),
likes_count: Number(photo.likes_count ?? 0),
uploaded_at: photo.uploaded_at,
@@ -1489,6 +1499,14 @@ export type GetEventPhotosOptions = {
visibility?: 'visible' | 'hidden' | 'all';
};
export type LiveShowQueueStatus = 'pending' | 'approved' | 'rejected' | 'none' | 'expired' | 'all';
export type GetLiveShowQueueOptions = {
page?: number;
perPage?: number;
liveStatus?: LiveShowQueueStatus;
};
export async function getEventPhotos(
slug: string,
options: GetEventPhotosOptions = {}
@@ -1526,6 +1544,67 @@ export async function getEventPhotos(
};
}
export async function getLiveShowQueue(
slug: string,
options: GetLiveShowQueueOptions = {}
): Promise<{ photos: TenantPhoto[]; meta: PaginationMeta }> {
const params = new URLSearchParams();
if (options.page) params.set('page', String(options.page));
if (options.perPage) params.set('per_page', String(options.perPage));
if (options.liveStatus) params.set('live_status', options.liveStatus);
const response = await authorizedFetch(
`${eventEndpoint(slug)}/live-show/photos${params.toString() ? `?${params.toString()}` : ''}`
);
const data = await jsonOrThrow<{
data?: TenantPhoto[];
meta?: Partial<PaginationMeta>;
current_page?: number;
last_page?: number;
per_page?: number;
total?: number;
}>(response, 'Failed to load live show queue');
const meta = buildPagination(data as unknown as JsonValue, options.perPage ?? 20);
return {
photos: (data.data ?? []).map(normalizePhoto),
meta,
};
}
export async function approveLiveShowPhoto(
slug: string,
id: number,
payload: { priority?: number } = {}
): Promise<TenantPhoto> {
const response = await authorizedFetch(`${eventEndpoint(slug)}/live-show/photos/${id}/approve`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(payload),
});
const data = await jsonOrThrow<PhotoResponse>(response, 'Failed to approve live show photo');
return normalizePhoto(data.data);
}
export async function rejectLiveShowPhoto(slug: string, id: number, reason?: string): Promise<TenantPhoto> {
const response = await authorizedFetch(`${eventEndpoint(slug)}/live-show/photos/${id}/reject`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ reason }),
});
const data = await jsonOrThrow<PhotoResponse>(response, 'Failed to reject live show photo');
return normalizePhoto(data.data);
}
export async function clearLiveShowPhoto(slug: string, id: number): Promise<TenantPhoto> {
const response = await authorizedFetch(`${eventEndpoint(slug)}/live-show/photos/${id}/clear`, {
method: 'POST',
});
const data = await jsonOrThrow<PhotoResponse>(response, 'Failed to clear live show photo');
return normalizePhoto(data.data);
}
export async function getEventPhoto(slug: string, id: number): Promise<TenantPhoto> {
const response = await authorizedFetch(`${eventEndpoint(slug)}/photos/${id}`);
const data = await jsonOrThrow<PhotoResponse>(response, 'Failed to load photo');