mirror of
https://github.com/Dvorinka/MyClubServer.git
synced 2026-06-04 02:32:57 +00:00
dev day #75
This commit is contained in:
@@ -87,7 +87,24 @@ export const forwardMessage = async (id: string, toEmail: string) => {
|
||||
await api.post(`/admin/contact-messages/${id}/forward`, { to_email: toEmail });
|
||||
};
|
||||
|
||||
export const forwardAllMessages = async (toEmail: string) => {
|
||||
const response = await api.post('/admin/contact-messages/forward-all', { to_email: toEmail });
|
||||
export const forwardAllMessages = async (
|
||||
emails: string | string[],
|
||||
options?: { saveDefault?: boolean }
|
||||
) => {
|
||||
let payload: any = {};
|
||||
if (Array.isArray(emails)) {
|
||||
const arr = emails.map((e) => String(e || '').trim()).filter(Boolean);
|
||||
payload = arr.length > 1 ? { to_emails: arr } : { to_email: arr[0] || '' };
|
||||
} else {
|
||||
const parts = String(emails || '')
|
||||
.split(/[;\,\s]+/)
|
||||
.map((s) => s.trim())
|
||||
.filter(Boolean);
|
||||
payload = parts.length > 1 ? { to_emails: parts } : { to_email: parts[0] || '' };
|
||||
}
|
||||
if (options?.saveDefault) {
|
||||
payload.save_default = true;
|
||||
}
|
||||
const response = await api.post('/admin/contact-messages/forward-all', payload);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
import api from './api';
|
||||
|
||||
export interface Banner {
|
||||
id: number | string;
|
||||
name: string;
|
||||
image_url?: string;
|
||||
click_url?: string;
|
||||
placement?: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
is_active?: boolean;
|
||||
display_order?: number;
|
||||
}
|
||||
|
||||
export async function getBanners(params?: { active?: boolean; placement?: string }): Promise<Banner[]> {
|
||||
const res = await api.get<any>('/banners', { params });
|
||||
const body = res.data;
|
||||
const list = Array.isArray(body) ? body : (Array.isArray(body?.data) ? body.data : []);
|
||||
return (list || []).map((b: any) => ({
|
||||
...b,
|
||||
id: b.id ?? b.ID ?? b.Id ?? b.iD,
|
||||
}));
|
||||
}
|
||||
|
||||
export async function createBanner(payload: { name: string; image_url?: string; click_url?: string; placement?: string; width?: number; height?: number; is_active?: boolean; display_order?: number }) {
|
||||
const res = await api.post<Banner>('/banners', payload);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function updateBanner(id: number | string, payload: Partial<{ name: string; image_url?: string; click_url?: string; placement?: string; width?: number; height?: number; is_active?: boolean; display_order?: number }>) {
|
||||
const res = await api.put<Banner>(`/banners/${id}`, payload);
|
||||
return res.data;
|
||||
}
|
||||
|
||||
export async function deleteBanner(id: number | string) {
|
||||
const res = await api.delete<{ zprava: string }>(`/banners/${id}`);
|
||||
return res.data;
|
||||
}
|
||||
@@ -1,4 +1,5 @@
|
||||
import api from './api';
|
||||
import { assetUrl } from '../utils/url';
|
||||
|
||||
export interface ImageProcessRequest {
|
||||
image_url: string;
|
||||
@@ -44,7 +45,11 @@ export interface ImageProcessResponse {
|
||||
*/
|
||||
export const processImage = async (request: ImageProcessRequest): Promise<ImageProcessResponse> => {
|
||||
const response = await api.post('/image-processing/process', request);
|
||||
return response.data;
|
||||
const data = response.data || {};
|
||||
if (data && typeof data.url === 'string') {
|
||||
data.url = assetUrl(data.url) || data.url;
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -52,7 +57,11 @@ export const processImage = async (request: ImageProcessRequest): Promise<ImageP
|
||||
*/
|
||||
export const quickEditImage = async (request: QuickEditRequest): Promise<ImageProcessResponse> => {
|
||||
const response = await api.post('/image-processing/quick-edit', request);
|
||||
return response.data;
|
||||
const data = response.data || {};
|
||||
if (data && typeof data.url === 'string') {
|
||||
data.url = assetUrl(data.url) || data.url;
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -79,8 +88,11 @@ export const cropAndUpload = async (
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
});
|
||||
|
||||
return response.data;
|
||||
const data = response.data || {};
|
||||
if (data && typeof data.url === 'string') {
|
||||
data.url = assetUrl(data.url) || data.url;
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import axios from 'axios';
|
||||
import { API_URL as API_BASE_URL } from './api';
|
||||
import api, { API_URL as API_BASE_URL } from './api';
|
||||
|
||||
export interface NavigationItem {
|
||||
id?: number;
|
||||
@@ -30,86 +29,64 @@ export interface SocialLink {
|
||||
|
||||
// Public endpoints
|
||||
export const getNavigationItems = async (): Promise<NavigationItem[]> => {
|
||||
const response = await axios.get(`${API_BASE_URL}/navigation`);
|
||||
const response = await api.get(`/navigation`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const getSocialLinks = async (): Promise<SocialLink[]> => {
|
||||
const response = await axios.get(`${API_BASE_URL}/social-links`);
|
||||
const response = await api.get(`/social-links`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
// Admin endpoints
|
||||
export const getAllNavigationItems = async (): Promise<NavigationItem[]> => {
|
||||
const response = await axios.get(`${API_BASE_URL}/admin/navigation`, {
|
||||
withCredentials: true,
|
||||
});
|
||||
const response = await api.get(`/admin/navigation`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const createNavigationItem = async (item: Partial<NavigationItem>): Promise<NavigationItem> => {
|
||||
const response = await axios.post(`${API_BASE_URL}/admin/navigation`, item, {
|
||||
withCredentials: true,
|
||||
});
|
||||
const response = await api.post(`/admin/navigation`, item);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const updateNavigationItem = async (id: number, item: Partial<NavigationItem>): Promise<NavigationItem> => {
|
||||
const response = await axios.put(`${API_BASE_URL}/admin/navigation/${id}`, item, {
|
||||
withCredentials: true,
|
||||
});
|
||||
const response = await api.put(`/admin/navigation/${id}`, item);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const deleteNavigationItem = async (id: number): Promise<void> => {
|
||||
await axios.delete(`${API_BASE_URL}/admin/navigation/${id}`, {
|
||||
withCredentials: true,
|
||||
});
|
||||
await api.delete(`/admin/navigation/${id}`);
|
||||
};
|
||||
|
||||
export const reorderNavigationItems = async (orders: { id: number; display_order: number }[]): Promise<void> => {
|
||||
await axios.post(`${API_BASE_URL}/admin/navigation/reorder`, orders, {
|
||||
withCredentials: true,
|
||||
});
|
||||
await api.post(`/admin/navigation/reorder`, orders);
|
||||
};
|
||||
|
||||
// Social links admin endpoints
|
||||
export const getAllSocialLinks = async (): Promise<SocialLink[]> => {
|
||||
const response = await axios.get(`${API_BASE_URL}/admin/social-links`, {
|
||||
withCredentials: true,
|
||||
});
|
||||
const response = await api.get(`/admin/social-links`);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const createSocialLink = async (link: Partial<SocialLink>): Promise<SocialLink> => {
|
||||
const response = await axios.post(`${API_BASE_URL}/admin/social-links`, link, {
|
||||
withCredentials: true,
|
||||
});
|
||||
const response = await api.post(`/admin/social-links`, link);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const updateSocialLink = async (id: number, link: Partial<SocialLink>): Promise<SocialLink> => {
|
||||
const response = await axios.put(`${API_BASE_URL}/admin/social-links/${id}`, link, {
|
||||
withCredentials: true,
|
||||
});
|
||||
const response = await api.put(`/admin/social-links/${id}`, link);
|
||||
return response.data;
|
||||
};
|
||||
|
||||
export const deleteSocialLink = async (id: number): Promise<void> => {
|
||||
await axios.delete(`${API_BASE_URL}/admin/social-links/${id}`, {
|
||||
withCredentials: true,
|
||||
});
|
||||
await api.delete(`/admin/social-links/${id}`);
|
||||
};
|
||||
|
||||
export const reorderSocialLinks = async (orders: { id: number; display_order: number }[]): Promise<void> => {
|
||||
await axios.post(`${API_BASE_URL}/admin/social-links/reorder`, orders, {
|
||||
withCredentials: true,
|
||||
});
|
||||
await api.post(`/admin/social-links/reorder`, orders);
|
||||
};
|
||||
|
||||
export const seedDefaultNavigation = async (): Promise<{ message: string; count: number; seeded: boolean }> => {
|
||||
const response = await axios.post(`${API_BASE_URL}/admin/navigation/seed`, {}, {
|
||||
withCredentials: true,
|
||||
});
|
||||
const response = await api.post(`/admin/navigation/seed`, {});
|
||||
return response.data;
|
||||
};
|
||||
|
||||
@@ -116,14 +116,14 @@ export const PREDEFINED_ELEMENTS: PredefinedElement[] = [
|
||||
// Layout - Rozvržení
|
||||
{ name: 'style-pack', label: 'Styl balíček', description: 'Globální vizuální balíček pro celou stránku', icon: FaCube, category: 'layout', defaultVariant: 'default' },
|
||||
{ name: 'header', label: 'Hlavička', description: 'Hlavička stránky s logem a navigací', icon: FaRegClipboard, category: 'layout', defaultVariant: 'unified' },
|
||||
{ name: 'hero-topbar', label: 'Klub lišta nad hero', description: 'Pruh nad hero s logem klubu, názvem a akcemi', icon: FaCube, category: 'layout', defaultVariant: 'brand' },
|
||||
{ name: 'hero-topbar', label: 'Klub lišta nad hero', description: 'Pruh nad hero s logem klubu, názvem a akcemi', icon: FaCube, category: 'layout', defaultVariant: 'minimal' },
|
||||
{ name: 'hero', label: 'Hlavní Sekce', description: 'Hlavní obsahová oblast s úvodním obsahem', icon: FaBullseye, category: 'layout', defaultVariant: 'grid' },
|
||||
{ name: 'footer', label: 'Patička', description: 'Spodní část stránky s odkazy a kontakty', icon: FaMapSigns, category: 'layout', defaultVariant: 'standard' },
|
||||
{ name: 'sidebar', label: 'Boční Panel', description: 'Boční sloupec s doplňkovým obsahem', icon: FaColumns, category: 'layout', defaultVariant: 'right' },
|
||||
{ name: 'banner', label: 'Banner', description: 'Reklamní nebo informační banner', icon: FaFlag, category: 'layout', defaultVariant: 'top' },
|
||||
|
||||
// Content - Obsah
|
||||
{ name: 'news', label: 'Novinky', description: 'Nejnovější články a zprávy', icon: FaNewspaper, category: 'content', defaultVariant: 'grid' },
|
||||
{ name: 'news', label: 'Novinky', description: 'Nejnovější články a zprávy', icon: FaNewspaper, category: 'content', defaultVariant: 'grid_one' },
|
||||
{ name: 'matches', label: 'Zápasy', description: 'Nadcházející a poslední zápasy', icon: FaFutbol, category: 'content', defaultVariant: 'compact' },
|
||||
{ name: 'matches-slider', label: 'Zápasy (slider)', description: 'Přehled zápasů podle soutěže ve slideru', icon: FaFutbol, category: 'content', defaultVariant: 'carousel' },
|
||||
{ name: 'team', label: 'Tým', description: 'Hráči a realizační tým', icon: FaUsers, category: 'content', defaultVariant: 'grid' },
|
||||
@@ -190,6 +190,8 @@ export const ELEMENT_VARIANTS: Record<string, ElementVariant[]> = {
|
||||
{ value: 'sparta_featured_carousel', label: 'Sparta Featured Carousel', description: 'Hero header s pozadím, článek s kategoriemi, thumbnail navigace, auto-swap' },
|
||||
],
|
||||
news: [
|
||||
{ value: 'grid_one', label: 'Mřížka (1 sloupec)', description: 'Jednosloupcová mřížka bez tabulek vpravo (skryje sekci Tabulky)' },
|
||||
{ value: 'grid_two', label: 'Mřížka (2 sloupce)', description: 'Aktuality vlevo a Tabulky vpravo (pokud jsou k dispozici)' },
|
||||
{ value: 'grid', label: 'Mřížka', description: 'Rozložení karet v mřížce' },
|
||||
{ value: 'scroller', label: 'Posuvník', description: 'Horizontální posuvník' },
|
||||
{ value: 'hero_carousel', label: 'Hero Karusel', description: 'Jeden článek najednou. Tlačítko: ZJISTIT VÍCE (vlevo dole). Numerace: 01 02 03 (vpravo dole). Auto-swap' },
|
||||
|
||||
@@ -116,6 +116,8 @@ export type AdminSettings = PublicSettings & {
|
||||
location_longitude?: number;
|
||||
map_zoom_level?: number;
|
||||
show_map_on_homepage?: boolean;
|
||||
frontend_base_url?: string;
|
||||
api_base_url?: string;
|
||||
// Homepage matches display configuration
|
||||
finished_match_display_days?: number; // Number of days to show finished matches with scores on homepage
|
||||
};
|
||||
|
||||
@@ -15,6 +15,9 @@ export type SetupInitializePayload = {
|
||||
club_name?: string;
|
||||
club_logo_url?: string;
|
||||
club_url?: string;
|
||||
// deployment bases
|
||||
frontend_base_url?: string;
|
||||
api_base_url?: string;
|
||||
frontpage_style?: 'unified' | 'magazine' | 'pro' | 'edge';
|
||||
primary_color?: string;
|
||||
secondary_color?: string;
|
||||
|
||||
Reference in New Issue
Block a user