436 lines
12 KiB
TypeScript
436 lines
12 KiB
TypeScript
import { getWebhookUrl } from '@/lib/config';
|
||
|
||
export interface Event {
|
||
id?: string | number;
|
||
user_id?: number;
|
||
name?: string;
|
||
title?: string; // neuer Titel-Name vom Timeline-Endpoint
|
||
description?: string;
|
||
location?: string;
|
||
url?: string;
|
||
image?: string;
|
||
manager_name?: string;
|
||
manager_email?: string;
|
||
guests?: any;
|
||
begin_date?: string; // ISO date string (YYYY-MM-DD)
|
||
end_date?: string; // ISO date string (YYYY-MM-DD)
|
||
eventDate?: string; // ISO date string (YYYY-MM-DD) vom Timeline-Endpoint (camelCase)
|
||
event_date?: string; // ISO date string (YYYY-MM-DD) direkt aus SQL-Query
|
||
status?: 'PENDING' | 'ON_AIR'; // aggregierter Status für Timeline
|
||
overall_status?: 'PENDING' | 'ON_AIR'; // Alias-Name aus SQL-Query
|
||
creation_date?: number;
|
||
last_update?: number;
|
||
}
|
||
|
||
// Payload-Struktur für den neuen Event-Endpunkt
|
||
interface CreateEventPayload {
|
||
title: string;
|
||
description?: string;
|
||
eventDate: string; // YYYY-MM-DD
|
||
promotionStartDate?: string; // YYYY-MM-DD
|
||
coverMediaUrl?: string;
|
||
ticketUrl?: string;
|
||
admissionTimes?: string;
|
||
ticketPrice?: string;
|
||
promotionSlots: Array<{
|
||
slotIndex: number;
|
||
slotType: 'EVENT_MAIN' | 'ARTIST';
|
||
promoDate: string; // YYYY-MM-DD
|
||
artistName?: string;
|
||
artistInstagram?: string;
|
||
}>;
|
||
}
|
||
|
||
export interface EventDetail extends CreateEventPayload {
|
||
id: string | number;
|
||
}
|
||
|
||
export const createEvent = async (payload: CreateEventPayload): Promise<any> => {
|
||
try {
|
||
const { apiFetch } = await import('@/utils/apiFetch');
|
||
const response = await apiFetch(`${getWebhookUrl()}/events/new`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify(payload),
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! status: ${response.status}`);
|
||
}
|
||
|
||
const result = await response.json();
|
||
return result;
|
||
} catch (error) {
|
||
console.error('Error creating event manually:', error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
export interface ArtistCollabUpdateByOpPayload {
|
||
eventId: string | number;
|
||
slotId?: string | number;
|
||
slotIndex?: number;
|
||
artistDescription?: string;
|
||
artistInstagram?: string;
|
||
artistFacebook?: string;
|
||
artistLinkedin?: string;
|
||
artistTiktok?: string;
|
||
artistYoutube?: string;
|
||
}
|
||
|
||
export const updateArtistCollabDataByOp = async (
|
||
payload: ArtistCollabUpdateByOpPayload
|
||
): Promise<{ success: boolean; message?: string }> => {
|
||
try {
|
||
const { apiFetch } = await import('@/utils/apiFetch');
|
||
const response = await apiFetch(`${getWebhookUrl()}/events/update_artist_data_byop`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify(payload),
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! status: ${response.status}`);
|
||
}
|
||
|
||
const raw = await response.json();
|
||
const data = Array.isArray(raw) ? raw[0] ?? {} : raw ?? {};
|
||
|
||
return {
|
||
success: data.success !== false,
|
||
message: data.message,
|
||
};
|
||
} catch (error) {
|
||
console.error('Error updating artist collab data by op:', error);
|
||
return { success: false, message: error instanceof Error ? error.message : 'Unknown error' };
|
||
}
|
||
};
|
||
|
||
export const getEventInfoFromUrl = async (url: string): Promise<any> => {
|
||
try {
|
||
const { apiFetch } = await import('@/utils/apiFetch');
|
||
const response = await apiFetch(`${getWebhookUrl()}/events/getinfofromurl`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({ url }),
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! status: ${response.status}`);
|
||
}
|
||
|
||
const data = await response.json();
|
||
return data;
|
||
} catch (error) {
|
||
console.error('Error getting event info from URL:', error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
export const fetchCurrentEvents = async (apiFetch: Function): Promise<Event[]> => {
|
||
|
||
const webhookUrl = `${getWebhookUrl()}/events/get_current`;
|
||
|
||
try {
|
||
const response = await apiFetch(webhookUrl, {
|
||
method: 'GET',
|
||
headers: { 'Content-Type': 'application/json' }
|
||
});
|
||
|
||
if (!response.ok) throw new Error('Failed to fetch events');
|
||
|
||
const raw = await response.json();
|
||
|
||
// n8n kann die Liste ggf. als String-JSON zurückgeben
|
||
const data =
|
||
typeof raw === 'string'
|
||
? (() => {
|
||
try {
|
||
return JSON.parse(raw);
|
||
} catch {
|
||
return raw;
|
||
}
|
||
})()
|
||
: raw;
|
||
|
||
return Array.isArray(data) ? data : data ? [data] : [];
|
||
} catch (error) {
|
||
console.error('Error fetching events:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
export interface ArtistCollabUpdatePayload {
|
||
token: string;
|
||
slotId?: string | number;
|
||
eventId?: string | number;
|
||
artistDescription?: string;
|
||
artistInstagram?: string;
|
||
artistFacebook?: string;
|
||
artistLinkedin?: string;
|
||
artistTiktok?: string;
|
||
artistYoutube?: string;
|
||
}
|
||
|
||
export const updateArtistCollabData = async (
|
||
payload: ArtistCollabUpdatePayload
|
||
): Promise<{ success: boolean; message?: string }> => {
|
||
try {
|
||
const { apiFetch } = await import('@/utils/apiFetch');
|
||
const response = await apiFetch(`${getWebhookUrl()}/events/update_artist_data`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify(payload),
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! status: ${response.status}`);
|
||
}
|
||
|
||
const raw = await response.json();
|
||
const data = Array.isArray(raw) ? raw[0] ?? {} : raw ?? {};
|
||
|
||
return {
|
||
success: data.success !== false,
|
||
message: data.message,
|
||
};
|
||
} catch (error) {
|
||
console.error('Error updating artist collab data:', error);
|
||
return { success: false, message: error instanceof Error ? error.message : 'Unknown error' };
|
||
}
|
||
};
|
||
|
||
export const fetchEventById = async (
|
||
apiFetch: Function,
|
||
id: string | number
|
||
): Promise<EventDetail> => {
|
||
const webhookUrl = `${getWebhookUrl()}/events/getbyid?id=${id}`;
|
||
|
||
const response = await apiFetch(webhookUrl, {
|
||
method: 'GET',
|
||
headers: { 'Content-Type': 'application/json' },
|
||
});
|
||
|
||
if (!response.ok) throw new Error('Failed to fetch event');
|
||
|
||
const raw = await response.json();
|
||
|
||
// n8n kann entweder ein Objekt, ein Array mit einem Objekt
|
||
// oder einen JSON-String zurückgeben – hier alles abfangen.
|
||
const data =
|
||
typeof raw === 'string'
|
||
? (() => {
|
||
try {
|
||
return JSON.parse(raw);
|
||
} catch {
|
||
return raw;
|
||
}
|
||
})()
|
||
: raw;
|
||
|
||
if (Array.isArray(data)) {
|
||
return (data[0] || {}) as EventDetail;
|
||
}
|
||
|
||
return data as EventDetail;
|
||
};
|
||
|
||
export const updateEvent = async (
|
||
id: string | number,
|
||
payload: CreateEventPayload
|
||
): Promise<any> => {
|
||
try {
|
||
const { apiFetch } = await import('@/utils/apiFetch');
|
||
const response = await apiFetch(`${getWebhookUrl()}/events/update`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({ id, ...payload }),
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! status: ${response.status}`);
|
||
}
|
||
|
||
const result = await response.json();
|
||
return result;
|
||
} catch (error) {
|
||
console.error('Error updating event:', error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
export const fetchEvents = async (apiFetch: Function): Promise<Event[]> => {
|
||
const webhookUrl = `${getWebhookUrl()}/events`;
|
||
|
||
try {
|
||
const response = await apiFetch(webhookUrl, {
|
||
method: 'GET',
|
||
headers: { 'Content-Type': 'application/json' }
|
||
});
|
||
|
||
if (!response.ok) throw new Error('Failed to fetch events');
|
||
|
||
const data = await response.json();
|
||
return Array.isArray(data) ? data : data ? [data] : [];
|
||
} catch (error) {
|
||
console.error('Error fetching events:', error);
|
||
throw error;
|
||
}
|
||
}
|
||
|
||
export const createEventFromUrl = async (url: string): Promise<any> => {
|
||
try {
|
||
const { apiFetch } = await import('@/utils/apiFetch');
|
||
const response = await apiFetch(`${getWebhookUrl()}/event/new_fromurl`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({
|
||
url,
|
||
command: 'eventmodus_extract_from_url'
|
||
}),
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! status: ${response.status}`);
|
||
}
|
||
|
||
const data = await response.json();
|
||
return data.message_from_ai || data;
|
||
} catch (error) {
|
||
console.error('Error creating event from URL:', error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
// Helper function to format dates to YYYY-MM-DD
|
||
export const formatDateForInput = (dateString: string): string => {
|
||
if (!dateString) return '';
|
||
const date = new Date(dateString);
|
||
return date.toISOString().split('T')[0];
|
||
};
|
||
|
||
export interface CollabLinkResponse {
|
||
token: string;
|
||
url?: string;
|
||
[key: string]: any;
|
||
}
|
||
|
||
export const generateCollabLink = async (
|
||
eventId: string | number,
|
||
slotIndex: number,
|
||
artistName?: string,
|
||
artistInstagram?: string
|
||
): Promise<CollabLinkResponse> => {
|
||
try {
|
||
const { apiFetch } = await import('@/utils/apiFetch');
|
||
const response = await apiFetch(`${getWebhookUrl()}/events/generate_collab_link`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({ eventId, slotIndex, artistName, artistInstagram }),
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! status: ${response.status}`);
|
||
}
|
||
|
||
const data = (await response.json()) as CollabLinkResponse;
|
||
return data;
|
||
} catch (error) {
|
||
console.error('Error generating collaboration link:', error);
|
||
throw error;
|
||
}
|
||
};
|
||
|
||
export interface CollabMediaItem {
|
||
id: string;
|
||
url: string;
|
||
type?: string;
|
||
}
|
||
|
||
export interface CollabInfo {
|
||
eventTitle: string;
|
||
promoDate?: string;
|
||
artistName?: string;
|
||
artistDescription?: string;
|
||
artistMediaUrl?: string;
|
||
artistInstagram?: string;
|
||
slotId?: string | number;
|
||
eventId?: string | number;
|
||
media?: CollabMediaItem[];
|
||
}
|
||
|
||
export const fetchArtistSlotByToken = async (token: string): Promise<CollabInfo | null> => {
|
||
try {
|
||
const { apiFetch } = await import('@/utils/apiFetch');
|
||
const response = await apiFetch(`${getWebhookUrl()}/events/slots/getartistsslot`, {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify({ token }),
|
||
});
|
||
|
||
if (response.status === 401 || response.status === 403 || response.status === 404) {
|
||
// Token ungültig, abgelaufen oder Slot nicht gefunden
|
||
return null;
|
||
}
|
||
|
||
if (!response.ok) {
|
||
throw new Error(`HTTP error! status: ${response.status}`);
|
||
}
|
||
|
||
const raw = await response.json();
|
||
|
||
const data: any = (() => {
|
||
if (!raw) return null;
|
||
if (Array.isArray(raw)) return raw[0] ?? null;
|
||
if (typeof raw === 'string') {
|
||
try {
|
||
const parsed = JSON.parse(raw);
|
||
return Array.isArray(parsed) ? parsed[0] ?? null : parsed;
|
||
} catch {
|
||
return null;
|
||
}
|
||
}
|
||
return raw as any;
|
||
})();
|
||
|
||
if (!data) return null;
|
||
|
||
return {
|
||
eventTitle: data.eventTitle ?? data.event_title ?? data.title ?? '',
|
||
promoDate: data.promoDate ?? data.promo_date,
|
||
artistName: data.artistName ?? data.artist_name,
|
||
artistDescription: data.artistDescription ?? data.artist_description,
|
||
artistMediaUrl: data.artistMediaUrl ?? data.artist_media_url,
|
||
artistInstagram: data.artistInstagram ?? data.artist_instagram,
|
||
slotId: data.id ?? data.slot_id,
|
||
eventId: data.event_id ?? data.eventId,
|
||
media: Array.isArray(data.media)
|
||
? data.media.map((m: any) => ({
|
||
id: String(m.id),
|
||
url: m.url || m.file_url,
|
||
type: m.type || m.file_type,
|
||
}))
|
||
: [],
|
||
};
|
||
} catch (error) {
|
||
console.error('Error fetching artist slot by token:', error);
|
||
throw error;
|
||
}
|
||
};
|