die tenant admin oauth authentifizierung wurde implementiert und funktioniert jetzt. Zudem wurde das marketing frontend dashboard implementiert.

This commit is contained in:
Codex Agent
2025-11-04 16:14:17 +01:00
parent 92e64c361a
commit fe380689fb
63 changed files with 4239 additions and 1142 deletions

View File

@@ -5,6 +5,24 @@ import i18n from './i18n';
type JsonValue = Record<string, unknown>;
export type TenantAccountProfile = {
id: number;
name: string;
email: string;
preferred_locale: string | null;
email_verified: boolean;
email_verified_at: string | null;
};
export type UpdateTenantProfilePayload = {
name: string;
email: string;
preferred_locale?: string | null;
current_password?: string;
password?: string;
password_confirmation?: string;
};
export type EventQrInviteLayout = {
id: string;
name: string;
@@ -353,10 +371,16 @@ async function jsonOrThrow<T>(response: Response, message: string, options: Json
const errorCode = errorPayload && typeof errorPayload === 'object' && typeof errorPayload.code === 'string'
? errorPayload.code
: undefined;
const errorMeta = errorPayload && typeof errorPayload === 'object' && typeof errorPayload.meta === 'object'
let errorMeta = errorPayload && typeof errorPayload === 'object' && typeof errorPayload.meta === 'object'
? errorPayload.meta as Record<string, unknown>
: undefined;
if (!errorMeta && body && typeof body === 'object' && 'errors' in body && typeof body.errors === 'object') {
errorMeta = {
errors: body.errors as Record<string, unknown>,
};
}
if (!options.suppressToast) {
emitApiErrorEvent({ message: errorMessage, status, code: errorCode, meta: errorMeta });
}
@@ -1102,6 +1126,49 @@ export async function getNotificationPreferences(): Promise<NotificationPreferen
};
}
type ProfileResponse = {
data?: TenantAccountProfile;
message?: string;
};
export async function fetchTenantProfile(): Promise<TenantAccountProfile> {
const response = await authorizedFetch('/api/v1/tenant/profile');
const payload = await jsonOrThrow<ProfileResponse>(
response,
i18n.t('settings.profile.errors.load', 'Profil konnte nicht geladen werden.'),
{ suppressToast: true }
);
if (!payload.data) {
throw new Error('Profilantwort war leer.');
}
return payload.data;
}
export async function updateTenantProfile(payload: UpdateTenantProfilePayload): Promise<TenantAccountProfile> {
const response = await authorizedFetch('/api/v1/tenant/profile', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json',
},
body: JSON.stringify(payload),
});
const json = await jsonOrThrow<ProfileResponse>(
response,
i18n.t('settings.profile.errors.update', 'Profil konnte nicht aktualisiert werden.'),
{ suppressToast: true }
);
if (!json.data) {
throw new Error('Profilantwort war leer.');
}
return json.data;
}
export async function updateNotificationPreferences(
preferences: NotificationPreferences
): Promise<NotificationPreferenceResponse> {