Merge remote-tracking branch 'upstream/main'

This commit is contained in:
2025-11-25 22:02:23 +02:00
104 changed files with 3858 additions and 3874 deletions
+21
View File
@@ -47,6 +47,17 @@ export interface Activity {
createdAt: string;
}
export interface AdminProfile {
id: number;
username: string;
email: string;
mustChangePassword?: boolean;
last_login?: string | null;
last_login_ip?: string | null;
created_at?: string;
updated_at?: string;
}
export interface AnalyticsData {
chartData: Array<{
date: string;
@@ -130,5 +141,15 @@ export const adminService = {
// Change password
async changePassword(data: { currentPassword: string; newPassword: string }): Promise<void> {
await api.post('/admin/auth/change-password', data);
},
async getAdminProfile(): Promise<AdminProfile> {
const response = await api.get<AdminProfile>('/admin/auth/profile');
return response.data;
},
async updateAdminProfile(data: { username: string; email: string }): Promise<AdminProfile> {
const response = await api.put<{ user: AdminProfile }>('/admin/auth/profile', data);
return response.data.user;
}
};
+18 -6
View File
@@ -2,16 +2,27 @@ import { api } from '../config/api';
import type { Event } from '../types';
import { normalizeRequirePassword } from '../utils/accessControl';
const normalizeEvent = (event: Event): Event => ({
...event,
require_password: normalizeRequirePassword((event as any)?.require_password, true),
});
const normalizeEvent = (event: Event): Event => {
const legacyHostName = (event as any)?.host_name;
const legacyHostEmail = (event as any)?.host_email;
const customerName = event.customer_name ?? legacyHostName ?? undefined;
const customerEmail = event.customer_email ?? legacyHostEmail ?? '';
return {
...event,
customer_name: customerName,
customer_email: customerEmail,
require_password: normalizeRequirePassword((event as any)?.require_password, true),
};
};
interface CreateEventData {
event_type: string;
event_name: string;
event_date: string;
host_email: string;
customer_name?: string;
customer_email: string;
admin_email: string;
require_password?: boolean;
password?: string;
@@ -33,7 +44,8 @@ interface CreateEventData {
interface UpdateEventData {
event_name?: string;
event_date?: string;
host_email?: string;
customer_name?: string;
customer_email?: string;
admin_email?: string;
require_password?: boolean;
password?: string;
+6 -1
View File
@@ -1,5 +1,5 @@
import { api } from '../config/api';
import type { GalleryInfo, GalleryData, GalleryStats } from '../types';
import type { GalleryInfo, GalleryData, GalleryStats, ResolvedGalleryIdentifier } from '../types';
import { normalizeRequirePassword } from '../utils/accessControl';
export const galleryService = {
@@ -119,4 +119,9 @@ export const galleryService = {
const response = await api.get<GalleryStats>(`/gallery/${slug}/stats`);
return response.data;
},
async resolveIdentifier(identifier: string): Promise<ResolvedGalleryIdentifier> {
const response = await api.get<ResolvedGalleryIdentifier>(`/gallery/resolve/${identifier}`);
return response.data;
},
};
+13 -4
View File
@@ -18,6 +18,7 @@ export interface BrandingSettings {
logo_display_header?: boolean;
logo_display_hero?: boolean;
logo_display_mode?: 'logo_only' | 'text_only' | 'logo_and_text';
hide_powered_by?: boolean;
}
export interface ThemeSettings {
@@ -256,6 +257,13 @@ export const settingsService = {
return response.data;
},
// Helper to parse boolean values that might come as strings or actual booleans
_parseBoolean(value: any, defaultValue: boolean): boolean {
if (value === true || value === 'true') return true;
if (value === false || value === 'false') return false;
return defaultValue;
},
// Format branding settings from raw data
formatBrandingSettings(rawSettings: Record<string, any>): BrandingSettings {
return {
@@ -263,7 +271,7 @@ export const settingsService = {
company_tagline: rawSettings.branding_company_tagline || '',
support_email: rawSettings.branding_support_email || '',
footer_text: rawSettings.branding_footer_text || '',
watermark_enabled: rawSettings.branding_watermark_enabled || false,
watermark_enabled: this._parseBoolean(rawSettings.branding_watermark_enabled, false),
watermark_position: rawSettings.branding_watermark_position || 'bottom-right',
watermark_opacity: rawSettings.branding_watermark_opacity || 50,
watermark_size: rawSettings.branding_watermark_size || 15,
@@ -273,9 +281,10 @@ export const settingsService = {
logo_size: rawSettings.branding_logo_size || 'medium',
logo_max_height: rawSettings.branding_logo_max_height || 48,
logo_position: rawSettings.branding_logo_position || 'left',
logo_display_header: rawSettings.branding_logo_display_header !== false,
logo_display_hero: rawSettings.branding_logo_display_hero !== false,
logo_display_mode: rawSettings.branding_logo_display_mode || 'logo_and_text'
logo_display_header: this._parseBoolean(rawSettings.branding_logo_display_header, true),
logo_display_hero: this._parseBoolean(rawSettings.branding_logo_display_hero, true),
logo_display_mode: rawSettings.branding_logo_display_mode || 'logo_and_text',
hide_powered_by: this._parseBoolean(rawSettings.branding_hide_powered_by, false)
};
},