import apiClient from './apiClient'; /** * Banner API Service * * NOTE: To avoid hammering the API, we add a very lightweight * in-memory + optional localStorage cache for read endpoints. */ export interface Banner { id: number; title: string; image_url: string; link?: string; position: string; display_order: number; is_active: boolean; start_date?: string; end_date?: string; created_at: string; updated_at: string; } export interface BannerListResponse { success: boolean; status?: string; data: { banners: Banner[]; }; message?: string; } // ---------- Simple client-side cache ---------- type CacheEntry = { timestamp: number; response: BannerListResponse; }; const BANNER_CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes const memoryCache = new Map(); const getCacheKey = (position?: string) => position ? `banners:position:${position}` : 'banners:active'; const isCacheValid = (entry: CacheEntry | undefined) => { if (!entry) return false; return Date.now() - entry.timestamp < BANNER_CACHE_TTL_MS; }; const loadFromStorage = (key: string): CacheEntry | undefined => { if (typeof window === 'undefined') return undefined; try { const raw = window.localStorage.getItem(key); if (!raw) return undefined; const parsed = JSON.parse(raw) as CacheEntry; return isCacheValid(parsed) ? parsed : undefined; } catch { return undefined; } }; const saveToStorage = (key: string, entry: CacheEntry) => { if (typeof window === 'undefined') return; try { window.localStorage.setItem(key, JSON.stringify(entry)); } catch { // ignore storage errors (quota, disabled, etc.) } }; const getCachedOrFetch = async ( key: string, fetcher: () => Promise ): Promise => { // 1) Check in-memory cache const inMemory = memoryCache.get(key); if (isCacheValid(inMemory)) { return inMemory!.response; } // 2) Check localStorage cache (fills memory cache if valid) const fromStorage = loadFromStorage(key); if (fromStorage) { memoryCache.set(key, fromStorage); return fromStorage.response; } // 3) Fetch from API and cache result const response = await fetcher(); const entry: CacheEntry = { timestamp: Date.now(), response, }; memoryCache.set(key, entry); saveToStorage(key, entry); return response; }; /** * Get banners by position * * Cached per-position on the client for a short TTL. */ export const getBannersByPosition = async ( position: string = 'home' ): Promise => { const key = getCacheKey(position); return getCachedOrFetch(key, async () => { const response = await apiClient.get('/banners', { params: { position }, }); return response.data; }); }; /** * Get all active banners * * Cached on the client for a short TTL. */ export const getActiveBanners = async (): Promise => { const key = getCacheKey(); return getCachedOrFetch(key, async () => { const response = await apiClient.get('/banners'); return response.data; }); }; /** * Get all banners (admin) */ export const getAllBanners = async ( params?: { position?: string; page?: number; limit?: number } ): Promise => { const response = await apiClient.get('/banners', { params }); return response.data; }; /** * Get banner by ID */ export const getBannerById = async ( id: number ): Promise<{ success: boolean; data: { banner: Banner } }> => { const response = await apiClient.get(`/banners/${id}`); return response.data; }; /** * Create banner */ export const createBanner = async ( data: { title: string; description?: string; image_url: string; link?: string; position?: string; display_order?: number; start_date?: string; end_date?: string; } ): Promise<{ success: boolean; data: { banner: Banner }; message: string }> => { const response = await apiClient.post('/banners', data); return response.data; }; /** * Update banner */ export const updateBanner = async ( id: number, data: { title?: string; description?: string; image_url?: string; link?: string; position?: string; display_order?: number; is_active?: boolean; start_date?: string; end_date?: string; } ): Promise<{ success: boolean; data: { banner: Banner }; message: string }> => { const response = await apiClient.put(`/banners/${id}`, data); return response.data; }; /** * Delete banner */ export const deleteBanner = async ( id: number ): Promise<{ success: boolean; message: string }> => { const response = await apiClient.delete(`/banners/${id}`); return response.data; }; /** * Upload banner image */ export const uploadBannerImage = async ( file: File ): Promise<{ success: boolean; data: { image_url: string; full_url: string }; message: string }> => { const formData = new FormData(); formData.append('image', file); const response = await apiClient.post('/banners/upload', formData, { headers: { 'Content-Type': 'multipart/form-data', }, }); return response.data; }; export interface BannerService { getBannersByPosition: typeof getBannersByPosition; getActiveBanners: typeof getActiveBanners; getAllBanners: typeof getAllBanners; getBannerById: typeof getBannerById; createBanner: typeof createBanner; updateBanner: typeof updateBanner; deleteBanner: typeof deleteBanner; uploadBannerImage: typeof uploadBannerImage; } const bannerService: BannerService = { getBannersByPosition, getActiveBanners, getAllBanners, getBannerById, createBanner, updateBanner, deleteBanner, uploadBannerImage, }; export default bannerService as BannerService;