feat: unify tenant admin ui and add photo moderation

This commit is contained in:
Codex Agent
2025-11-07 13:50:55 +01:00
parent 9cc9950b0c
commit 253239455b
14 changed files with 995 additions and 583 deletions

View File

@@ -137,6 +137,11 @@ export type DashboardSummary = {
expires_at?: string | null;
remaining_events?: number | null;
} | null;
engagement_totals?: {
tasks?: number;
collections?: number;
emotions?: number;
};
};
export type TenantOnboardingStatus = {
@@ -620,8 +625,31 @@ function normalizeDashboard(payload: JsonValue | null): DashboardSummary | null
name: String(payload.active_package.name ?? 'Aktives Package'),
expires_at: payload.active_package.expires_at ?? null,
remaining_events: payload.active_package.remaining_events ?? payload.active_package.remainingEvents ?? null,
}
}
: null,
engagement_totals: {
tasks:
Number(
payload.tasks?.summary?.total ??
payload.tasks_total ??
payload.engagement?.tasks ??
0,
),
collections:
Number(
payload.task_collections?.summary?.total ??
payload.collections_total ??
payload.engagement?.collections ??
0,
),
emotions:
Number(
payload.emotions?.summary?.total ??
payload.emotions_total ??
payload.engagement?.emotions ??
0,
),
},
};
}
@@ -923,6 +951,18 @@ export async function deletePhoto(slug: string, id: number): Promise<void> {
}
}
export async function updatePhotoVisibility(slug: string, id: number, visible: boolean): Promise<TenantPhoto> {
const response = await authorizedFetch(`${eventEndpoint(slug)}/photos/${id}/visibility`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ visible }),
});
const data = await jsonOrThrow<PhotoResponse>(response, 'Failed to update photo visibility');
return normalizePhoto(data.data);
}
export async function toggleEvent(slug: string): Promise<TenantEvent> {
const response = await authorizedFetch(`${eventEndpoint(slug)}/toggle`, { method: 'POST' });
const data = await jsonOrThrow<{ message: string; data: JsonValue }>(response, 'Failed to toggle event');
@@ -1026,22 +1066,22 @@ export async function getEventToolkit(slug: string): Promise<EventToolkit> {
active_invites: Number((metrics as JsonValue).active_invites ?? 0),
engagement_mode: ((metrics as JsonValue).engagement_mode as 'tasks' | 'photo_only') ?? 'tasks',
},
tasks: {
summary: {
total: Number((tasks as JsonValue)?.summary?.total ?? 0),
completed: Number((tasks as JsonValue)?.summary?.completed ?? 0),
pending: Number((tasks as JsonValue)?.summary?.pending ?? 0),
},
items: Array.isArray((tasks as JsonValue)?.items)
? ((tasks as JsonValue).items as JsonValue[]).map((item) => ({
id: Number(item?.id ?? 0),
title: String(item?.title ?? ''),
description: item?.description !== undefined && item?.description !== null ? String(item.description) : null,
is_completed: Boolean(item?.is_completed ?? false),
priority: item?.priority !== undefined ? String(item.priority) : null,
}))
: [],
tasks: {
summary: {
total: Number((tasks as JsonValue)?.summary?.total ?? 0),
completed: Number((tasks as JsonValue)?.summary?.completed ?? 0),
pending: Number((tasks as JsonValue)?.summary?.pending ?? 0),
},
items: Array.isArray((tasks as JsonValue)?.items)
? ((tasks as JsonValue).items as JsonValue[]).map((item) => ({
id: Number(item?.id ?? 0),
title: String(item?.title ?? ''),
description: item?.description !== undefined && item?.description !== null ? String(item.description) : null,
is_completed: Boolean(item?.is_completed ?? false),
priority: item?.priority !== undefined ? String(item.priority) : null,
}))
: [],
},
photos: {
pending: pendingPhotosRaw.map((photo: JsonValue) => normalizePhoto(photo as TenantPhoto)),
recent: recentPhotosRaw.map((photo: JsonValue) => normalizePhoto(photo as TenantPhoto)),