Fix language setting not being saved to database on admin settings page
- Added default_language field to general settings state in SettingsPage - Replaced LanguageSelector component with simple select dropdown on settings page - Fixed public settings endpoint to read general_default_language from database - Language setting now properly saved when clicking Save Settings button - Setting is correctly used by gallery login page and legal pages 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
import { api } from '../config/api';
|
||||
|
||||
export interface PhotoCategory {
|
||||
id: number;
|
||||
name: string;
|
||||
slug: string;
|
||||
is_global: boolean;
|
||||
event_id: number | null;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export interface CreateCategoryData {
|
||||
name: string;
|
||||
slug?: string;
|
||||
is_global?: boolean;
|
||||
event_id?: number;
|
||||
}
|
||||
|
||||
export const categoriesService = {
|
||||
// Get all global categories
|
||||
async getGlobalCategories(): Promise<PhotoCategory[]> {
|
||||
const response = await api.get<PhotoCategory[]>('/api/admin/categories/global');
|
||||
return response.data;
|
||||
},
|
||||
|
||||
// Get categories for a specific event (global + event-specific)
|
||||
async getEventCategories(eventId: number): Promise<PhotoCategory[]> {
|
||||
const response = await api.get<PhotoCategory[]>(`/api/admin/categories/event/${eventId}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
// Create a new category
|
||||
async createCategory(data: CreateCategoryData): Promise<PhotoCategory> {
|
||||
const response = await api.post<PhotoCategory>('/api/admin/categories', data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
// Update a category
|
||||
async updateCategory(id: number, name: string): Promise<PhotoCategory> {
|
||||
const response = await api.put<PhotoCategory>(`/api/admin/categories/${id}`, { name });
|
||||
return response.data;
|
||||
},
|
||||
|
||||
// Delete a category
|
||||
async deleteCategory(id: number): Promise<void> {
|
||||
await api.delete(`/api/admin/categories/${id}`);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,39 @@
|
||||
import { api } from '../config/api';
|
||||
|
||||
export interface CMSPage {
|
||||
id: number;
|
||||
slug: string;
|
||||
title_en: string;
|
||||
title_de: string;
|
||||
content_en: string;
|
||||
content_de: string;
|
||||
updated_at: string;
|
||||
}
|
||||
|
||||
export const cmsService = {
|
||||
// Get all CMS pages
|
||||
async getPages(): Promise<CMSPage[]> {
|
||||
const response = await api.get<CMSPage[]>('/api/admin/cms/pages');
|
||||
return response.data;
|
||||
},
|
||||
|
||||
// Get a single CMS page
|
||||
async getPage(slug: string): Promise<CMSPage> {
|
||||
const response = await api.get<CMSPage>(`/api/admin/cms/pages/${slug}`);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
// Update a CMS page
|
||||
async updatePage(slug: string, data: Partial<CMSPage>): Promise<CMSPage> {
|
||||
const response = await api.put<CMSPage>(`/api/admin/cms/pages/${slug}`, data);
|
||||
return response.data;
|
||||
},
|
||||
|
||||
// Get public CMS page (no auth required)
|
||||
async getPublicPage(slug: string, lang: string = 'en'): Promise<{ title: string; content: string }> {
|
||||
const response = await api.get<{ title: string; content: string }>(`/api/public/pages/${slug}`, {
|
||||
params: { lang }
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
};
|
||||
@@ -5,4 +5,5 @@ export { adminService } from './admin.service';
|
||||
export { analyticsService } from './analytics.service';
|
||||
export { archiveService } from './archive.service';
|
||||
export { emailService } from './email.service';
|
||||
export { settingsService } from './settings.service';
|
||||
export { settingsService } from './settings.service';
|
||||
export { cmsService } from './cms.service';
|
||||
@@ -6,7 +6,12 @@ export interface BrandingSettings {
|
||||
support_email: string;
|
||||
footer_text: string;
|
||||
watermark_enabled: boolean;
|
||||
watermark_position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' | 'center';
|
||||
watermark_opacity?: number;
|
||||
watermark_size?: number;
|
||||
watermark_logo_url?: string;
|
||||
logo_url?: string;
|
||||
favicon_url?: string;
|
||||
}
|
||||
|
||||
export interface ThemeSettings {
|
||||
@@ -50,11 +55,11 @@ export const settingsService = {
|
||||
},
|
||||
|
||||
// Upload logo
|
||||
async uploadLogo(file: File): Promise<{ logo_url: string }> {
|
||||
async uploadLogo(file: File): Promise<string> {
|
||||
const formData = new FormData();
|
||||
formData.append('logo', file);
|
||||
|
||||
const response = await api.post<{ message: string; logo_url: string }>(
|
||||
const response = await api.post<{ logoUrl: string }>(
|
||||
'/api/admin/settings/logo',
|
||||
formData,
|
||||
{
|
||||
@@ -64,7 +69,43 @@ export const settingsService = {
|
||||
}
|
||||
);
|
||||
|
||||
return { logo_url: response.data.logo_url };
|
||||
return response.data.logoUrl;
|
||||
},
|
||||
|
||||
// Upload favicon
|
||||
async uploadFavicon(file: File): Promise<string> {
|
||||
const formData = new FormData();
|
||||
formData.append('favicon', file);
|
||||
|
||||
const response = await api.post<{ faviconUrl: string }>(
|
||||
'/api/admin/settings/favicon',
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return response.data.faviconUrl;
|
||||
},
|
||||
|
||||
// Upload watermark logo
|
||||
async uploadWatermarkLogo(file: File): Promise<string> {
|
||||
const formData = new FormData();
|
||||
formData.append('watermarkLogo', file);
|
||||
|
||||
const response = await api.post<{ watermarkLogoUrl: string }>(
|
||||
'/api/admin/settings/branding/watermark-logo',
|
||||
formData,
|
||||
{
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data'
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return response.data.watermarkLogoUrl;
|
||||
},
|
||||
|
||||
// Update theme settings
|
||||
@@ -91,7 +132,12 @@ export const settingsService = {
|
||||
support_email: rawSettings.branding_support_email || '',
|
||||
footer_text: rawSettings.branding_footer_text || '',
|
||||
watermark_enabled: rawSettings.branding_watermark_enabled || false,
|
||||
logo_url: rawSettings.branding_logo_url || undefined
|
||||
watermark_position: rawSettings.branding_watermark_position || 'bottom-right',
|
||||
watermark_opacity: rawSettings.branding_watermark_opacity || 50,
|
||||
watermark_size: rawSettings.branding_watermark_size || 15,
|
||||
watermark_logo_url: rawSettings.branding_watermark_logo_url || undefined,
|
||||
logo_url: rawSettings.branding_logo_url || undefined,
|
||||
favicon_url: rawSettings.branding_favicon_url || undefined
|
||||
};
|
||||
},
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { toast as toastify } from 'react-toastify';
|
||||
import i18n from '../i18n/config';
|
||||
|
||||
export const toast = {
|
||||
success: (messageKey: string, interpolations?: Record<string, any>) => {
|
||||
const message = i18n.t(messageKey, interpolations);
|
||||
toastify.success(message);
|
||||
},
|
||||
|
||||
error: (messageKey: string, interpolations?: Record<string, any>) => {
|
||||
const message = i18n.t(messageKey, interpolations);
|
||||
toastify.error(message);
|
||||
},
|
||||
|
||||
info: (messageKey: string, interpolations?: Record<string, any>) => {
|
||||
const message = i18n.t(messageKey, interpolations);
|
||||
toastify.info(message);
|
||||
},
|
||||
|
||||
warning: (messageKey: string, interpolations?: Record<string, any>) => {
|
||||
const message = i18n.t(messageKey, interpolations);
|
||||
toastify.warning(message);
|
||||
},
|
||||
|
||||
// For direct messages (not translation keys)
|
||||
successDirect: (message: string) => toastify.success(message),
|
||||
errorDirect: (message: string) => toastify.error(message),
|
||||
infoDirect: (message: string) => toastify.info(message),
|
||||
warningDirect: (message: string) => toastify.warning(message),
|
||||
};
|
||||
Reference in New Issue
Block a user