feat: add guest notification center
This commit is contained in:
@@ -85,6 +85,31 @@ export type TenantEvent = {
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
export type GuestNotificationSummary = {
|
||||
id: number;
|
||||
type: string;
|
||||
title: string;
|
||||
body: string | null;
|
||||
status: 'draft' | 'active' | 'archived';
|
||||
audience_scope: 'all' | 'guest';
|
||||
target_identifier?: string | null;
|
||||
payload?: Record<string, unknown> | null;
|
||||
priority: number;
|
||||
created_at: string | null;
|
||||
expires_at: string | null;
|
||||
};
|
||||
|
||||
export type SendGuestNotificationPayload = {
|
||||
title: string;
|
||||
message: string;
|
||||
type?: string;
|
||||
audience?: 'all' | 'guest';
|
||||
guest_identifier?: string | null;
|
||||
cta?: { label: string; url: string } | null;
|
||||
expires_in_minutes?: number | null;
|
||||
priority?: number | null;
|
||||
};
|
||||
|
||||
export type TenantPhoto = {
|
||||
id: number;
|
||||
filename: string;
|
||||
@@ -968,10 +993,36 @@ function normalizeQrInvite(raw: JsonValue): EventQrInvite {
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeGuestNotification(raw: JsonValue): GuestNotificationSummary | null {
|
||||
if (!raw || typeof raw !== 'object') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const record = raw as Record<string, JsonValue>;
|
||||
|
||||
return {
|
||||
id: Number(record.id ?? 0),
|
||||
type: typeof record.type === 'string' ? record.type : 'broadcast',
|
||||
title: typeof record.title === 'string' ? record.title : '',
|
||||
body: typeof record.body === 'string' ? record.body : null,
|
||||
status: (record.status as GuestNotificationSummary['status']) ?? 'active',
|
||||
audience_scope: (record.audience_scope as GuestNotificationSummary['audience_scope']) ?? 'all',
|
||||
target_identifier: typeof record.target_identifier === 'string' ? record.target_identifier : null,
|
||||
payload: (record.payload as Record<string, unknown>) ?? null,
|
||||
priority: Number(record.priority ?? 0),
|
||||
created_at: typeof record.created_at === 'string' ? record.created_at : null,
|
||||
expires_at: typeof record.expires_at === 'string' ? record.expires_at : null,
|
||||
};
|
||||
}
|
||||
|
||||
function eventEndpoint(slug: string): string {
|
||||
return `/api/v1/tenant/events/${encodeURIComponent(slug)}`;
|
||||
}
|
||||
|
||||
function guestNotificationsEndpoint(slug: string): string {
|
||||
return `${eventEndpoint(slug)}/guest-notifications`;
|
||||
}
|
||||
|
||||
function photoboothEndpoint(slug: string): string {
|
||||
return `${eventEndpoint(slug)}/photobooth`;
|
||||
}
|
||||
@@ -1239,6 +1290,41 @@ export async function getEventToolkit(slug: string): Promise<EventToolkit> {
|
||||
return toolkit;
|
||||
}
|
||||
|
||||
export async function listGuestNotifications(slug: string): Promise<GuestNotificationSummary[]> {
|
||||
const response = await authorizedFetch(guestNotificationsEndpoint(slug));
|
||||
const data = await jsonOrThrow<{ data?: JsonValue[] }>(response, 'Failed to load guest notifications');
|
||||
const rows = Array.isArray(data.data) ? data.data : [];
|
||||
|
||||
return rows
|
||||
.map((row) => normalizeGuestNotification(row))
|
||||
.filter((row): row is GuestNotificationSummary => Boolean(row));
|
||||
}
|
||||
|
||||
export async function sendGuestNotification(
|
||||
slug: string,
|
||||
payload: SendGuestNotificationPayload
|
||||
): Promise<GuestNotificationSummary> {
|
||||
const response = await authorizedFetch(guestNotificationsEndpoint(slug), {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(payload),
|
||||
});
|
||||
const data = await jsonOrThrow<{ data?: JsonValue }>(response, 'Failed to send guest notification');
|
||||
return normalizeGuestNotification(data.data ?? {}) ?? normalizeGuestNotification({
|
||||
id: 0,
|
||||
type: payload.type ?? 'broadcast',
|
||||
title: payload.title,
|
||||
body: payload.message,
|
||||
status: 'active',
|
||||
audience_scope: payload.audience ?? 'all',
|
||||
target_identifier: payload.guest_identifier ?? null,
|
||||
payload: payload.cta ? { cta: payload.cta } : null,
|
||||
priority: payload.priority ?? 0,
|
||||
created_at: new Date().toISOString(),
|
||||
expires_at: null,
|
||||
});
|
||||
}
|
||||
|
||||
export async function getEventPhotoboothStatus(slug: string): Promise<PhotoboothStatus> {
|
||||
return requestPhotoboothStatus(slug, '', {}, 'Failed to load photobooth status');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user