fixed event join token handling in the event admin. created new seeders with new tenants and package purchases. added new playwright test scenarios.
This commit is contained in:
@@ -18,16 +18,37 @@ export type EventJoinTokenLayout = {
|
||||
download_urls: Record<string, string>;
|
||||
};
|
||||
|
||||
export type TenantEventType = {
|
||||
id: number;
|
||||
slug: string;
|
||||
name: string;
|
||||
name_translations: Record<string, string>;
|
||||
icon: string | null;
|
||||
settings: Record<string, unknown>;
|
||||
created_at?: string | null;
|
||||
updated_at?: string | null;
|
||||
};
|
||||
|
||||
export type TenantEvent = {
|
||||
id: number;
|
||||
name: string | Record<string, string>;
|
||||
slug: string;
|
||||
event_date: string | null;
|
||||
event_type_id: number | null;
|
||||
event_type: TenantEventType | null;
|
||||
status: 'draft' | 'published' | 'archived';
|
||||
is_active?: boolean;
|
||||
description?: string | null;
|
||||
photo_count?: number;
|
||||
like_count?: number;
|
||||
package?: {
|
||||
id: number | string | null;
|
||||
name: string | null;
|
||||
price: number | null;
|
||||
purchased_at: string | null;
|
||||
expires_at: string | null;
|
||||
} | null;
|
||||
[key: string]: unknown;
|
||||
};
|
||||
|
||||
export type TenantPhoto = {
|
||||
@@ -208,8 +229,8 @@ export type EventMember = {
|
||||
avatar_url?: string | null;
|
||||
};
|
||||
|
||||
type EventListResponse = { data?: TenantEvent[] };
|
||||
type EventResponse = { data: TenantEvent };
|
||||
type EventListResponse = { data?: JsonValue[] };
|
||||
type EventResponse = { data: JsonValue };
|
||||
|
||||
export type EventJoinToken = {
|
||||
id: number;
|
||||
@@ -226,13 +247,14 @@ export type EventJoinToken = {
|
||||
layouts: EventJoinTokenLayout[];
|
||||
layouts_url: string | null;
|
||||
};
|
||||
type CreatedEventResponse = { message: string; data: TenantEvent; balance: number };
|
||||
type CreatedEventResponse = { message: string; data: JsonValue; balance: number };
|
||||
type PhotoResponse = { message: string; data: TenantPhoto };
|
||||
|
||||
type EventSavePayload = {
|
||||
name: string;
|
||||
slug: string;
|
||||
date?: string;
|
||||
event_type_id: number;
|
||||
event_date?: string;
|
||||
status?: 'draft' | 'published' | 'archived';
|
||||
is_active?: boolean;
|
||||
package_id?: number;
|
||||
@@ -322,13 +344,51 @@ function pickTranslatedText(translations: Record<string, string>, fallback: stri
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function normalizeEvent(event: TenantEvent): TenantEvent {
|
||||
function normalizeEventType(raw: JsonValue | TenantEventType | null): TenantEventType | null {
|
||||
if (!raw) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const translations = normalizeTranslationMap((raw as JsonValue).name ?? {}, undefined, true);
|
||||
const fallback = typeof (raw as JsonValue).name === 'string' ? (raw as JsonValue).name : 'Event';
|
||||
|
||||
return {
|
||||
...event,
|
||||
is_active: typeof event.is_active === 'boolean' ? event.is_active : undefined,
|
||||
id: Number((raw as JsonValue).id ?? 0),
|
||||
slug: String((raw as JsonValue).slug ?? ''),
|
||||
name: pickTranslatedText(translations, fallback ?? 'Event'),
|
||||
name_translations: translations,
|
||||
icon: ((raw as JsonValue).icon ?? null) as string | null,
|
||||
settings: ((raw as JsonValue).settings ?? {}) as Record<string, unknown>,
|
||||
created_at: (raw as JsonValue).created_at ?? null,
|
||||
updated_at: (raw as JsonValue).updated_at ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeEvent(event: JsonValue): TenantEvent {
|
||||
const normalizedType = normalizeEventType(event.event_type ?? event.eventType ?? null);
|
||||
const normalized: TenantEvent = {
|
||||
...(event as Record<string, unknown>),
|
||||
id: Number(event.id ?? 0),
|
||||
name: event.name ?? '',
|
||||
slug: String(event.slug ?? ''),
|
||||
event_date: typeof event.event_date === 'string'
|
||||
? event.event_date
|
||||
: (typeof event.date === 'string' ? event.date : null),
|
||||
event_type_id: event.event_type_id !== undefined && event.event_type_id !== null
|
||||
? Number(event.event_type_id)
|
||||
: null,
|
||||
event_type: normalizedType,
|
||||
status: (event.status ?? 'draft') as TenantEvent['status'],
|
||||
is_active: typeof event.is_active === 'boolean' ? event.is_active : undefined,
|
||||
description: event.description ?? null,
|
||||
photo_count: event.photo_count !== undefined ? Number(event.photo_count ?? 0) : undefined,
|
||||
like_count: event.like_count !== undefined ? Number(event.like_count ?? 0) : undefined,
|
||||
package: event.package ?? null,
|
||||
};
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function normalizePhoto(photo: TenantPhoto): TenantPhoto {
|
||||
return {
|
||||
id: photo.id,
|
||||
@@ -574,6 +634,15 @@ export async function getEvent(slug: string): Promise<TenantEvent> {
|
||||
return normalizeEvent(data.data);
|
||||
}
|
||||
|
||||
export async function getEventTypes(): Promise<TenantEventType[]> {
|
||||
const response = await authorizedFetch('/api/v1/tenant/event-types');
|
||||
const data = await jsonOrThrow<{ data?: JsonValue[] }>(response, 'Failed to load event types');
|
||||
const rows = Array.isArray(data.data) ? data.data : [];
|
||||
return rows
|
||||
.map((row) => normalizeEventType(row))
|
||||
.filter((row): row is TenantEventType => Boolean(row));
|
||||
}
|
||||
|
||||
export async function getEventPhotos(slug: string): Promise<TenantPhoto[]> {
|
||||
const response = await authorizedFetch(`${eventEndpoint(slug)}/photos`);
|
||||
const data = await jsonOrThrow<{ data?: TenantPhoto[] }>(response, 'Failed to load photos');
|
||||
@@ -602,7 +671,7 @@ export async function deletePhoto(slug: string, id: number): Promise<void> {
|
||||
|
||||
export async function toggleEvent(slug: string): Promise<TenantEvent> {
|
||||
const response = await authorizedFetch(`${eventEndpoint(slug)}/toggle`, { method: 'POST' });
|
||||
const data = await jsonOrThrow<{ message: string; data: TenantEvent }>(response, 'Failed to toggle event');
|
||||
const data = await jsonOrThrow<{ message: string; data: JsonValue }>(response, 'Failed to toggle event');
|
||||
return normalizeEvent(data.data);
|
||||
}
|
||||
|
||||
@@ -621,7 +690,7 @@ export async function getEventStats(slug: string): Promise<EventStats> {
|
||||
|
||||
export async function getEventJoinTokens(slug: string): Promise<EventJoinToken[]> {
|
||||
const response = await authorizedFetch(`${eventEndpoint(slug)}/join-tokens`);
|
||||
const payload = await jsonOrThrow<{ data: JsonValue[] }>(response, 'Failed to load join tokens');
|
||||
const payload = await jsonOrThrow<{ data: JsonValue[] }>(response, 'Failed to load invitations');
|
||||
const list = Array.isArray(payload.data) ? payload.data : [];
|
||||
return list.map(normalizeJoinToken);
|
||||
}
|
||||
@@ -636,7 +705,7 @@ export async function createInviteLink(
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body,
|
||||
});
|
||||
const data = await jsonOrThrow<{ data: JsonValue }>(response, 'Failed to create invite');
|
||||
const data = await jsonOrThrow<{ data: JsonValue }>(response, 'Failed to create invitation');
|
||||
return normalizeJoinToken(data.data ?? {});
|
||||
}
|
||||
|
||||
@@ -651,7 +720,7 @@ export async function revokeEventJoinToken(
|
||||
options.body = JSON.stringify({ reason });
|
||||
}
|
||||
const response = await authorizedFetch(`${eventEndpoint(slug)}/join-tokens/${tokenId}`, options);
|
||||
const data = await jsonOrThrow<{ data: JsonValue }>(response, 'Failed to revoke join token');
|
||||
const data = await jsonOrThrow<{ data: JsonValue }>(response, 'Failed to revoke invitation');
|
||||
return normalizeJoinToken(data.data ?? {});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user