import type { User, Asset, Category, BlogPost, Announcement } from '../types'; import { initializeStorage } from './mock-data'; // Ensure data is seeded in localStorage initializeStorage(); // Helper delay function to simulate network response const delay = (ms: number = 300) => new Promise(resolve => setTimeout(resolve, ms)); const getStored = (key: string): T => { const data = localStorage.getItem(key); return data ? JSON.parse(data) : [] as unknown as T; }; const setStored = (key: string, data: T): void => { localStorage.setItem(key, JSON.stringify(data)); }; export interface ApiResponse { data: T; status: number; } // Custom mock API client resembling Axios export const apiClient = { get: async (url: string, config?: { params?: any }): Promise> => { await delay(); // Clean up url by removing query string if any const cleanUrl = url.split('?')[0]; // Auth endpoints if (cleanUrl === '/auth/session') { const activeUserEmail = sessionStorage.getItem('t4b_session_email'); if (!activeUserEmail) { throw { status: 401, message: 'Unauthorized' }; } const users = getStored('t4b_users'); const user = users.find(u => u.email === activeUserEmail); if (!user) { throw { status: 401, message: 'User not found' }; } return { data: user as unknown as T, status: 200 }; } // Asset endpoints if (cleanUrl === '/assets') { const assets = getStored('t4b_assets'); const params = config?.params || {}; let filtered = [...assets]; if (params.categoryId && params.categoryId !== 'all') { filtered = filtered.filter(a => a.categoryId === params.categoryId); } if (params.subcategory && params.subcategory !== 'all') { filtered = filtered.filter(a => a.subcategory === params.subcategory); } if (params.search) { const q = params.search.toLowerCase(); filtered = filtered.filter(a => a.title.toLowerCase().includes(q) || a.description.toLowerCase().includes(q) || a.tags.some(t => t.toLowerCase().includes(q)) ); } // If client is logged in and not admin, only show published assets const activeUserEmail = sessionStorage.getItem('t4b_session_email'); const users = getStored('t4b_users'); const activeUser = users.find(u => u.email === activeUserEmail); if (!activeUser || activeUser.role !== 'ADMIN') { filtered = filtered.filter(a => a.status === 'published'); } return { data: filtered as unknown as T, status: 200 }; } if (cleanUrl.startsWith('/assets/')) { const id = cleanUrl.split('/assets/')[1]; const assets = getStored('t4b_assets'); const asset = assets.find(a => a.id === id); if (!asset) { throw { status: 404, message: 'Asset not found' }; } return { data: asset as unknown as T, status: 200 }; } // Categories if (cleanUrl === '/categories') { const categories = getStored('t4b_categories'); return { data: categories as unknown as T, status: 200 }; } // Blog posts if (cleanUrl === '/blog') { const posts = getStored('t4b_blog_posts'); // Filter out drafts for client roles const activeUserEmail = sessionStorage.getItem('t4b_session_email'); const users = getStored('t4b_users'); const activeUser = users.find(u => u.email === activeUserEmail); let filtered = [...posts]; if (!activeUser || activeUser.role !== 'ADMIN') { filtered = filtered.filter(p => p.status === 'published'); } return { data: filtered as unknown as T, status: 200 }; } // Announcements if (cleanUrl === '/announcements') { const announcements = getStored('t4b_announcements'); return { data: announcements as unknown as T, status: 200 }; } // Admin Client Queue if (cleanUrl === '/admin/clients') { const users = getStored('t4b_users'); // Filter out admins, return only clients const clients = users.filter(u => u.role === 'CLIENT'); return { data: clients as unknown as T, status: 200 }; } throw { status: 404, message: 'Route not found' }; }, post: async (url: string, payload: any): Promise> => { await delay(); if (url === '/auth/login') { const { email, password } = payload; const users = getStored('t4b_users'); const user = users.find(u => u.email === email); if (!user || password !== 'password') { throw { status: 400, message: 'Invalid email or password' }; } // Login success, return user (MFA verification will occur if required) return { data: user as unknown as T, status: 200 }; } if (url === '/auth/register') { const { email, companyName, website, sector, companySize } = payload; const users = getStored('t4b_users'); if (users.some(u => u.email === email)) { throw { status: 400, message: 'Email already registered' }; } const newUser: User = { id: `user-${Date.now()}`, email, role: 'CLIENT', companyName: companyName || '', website: website || '', sector: sector || 'Technology', companySize: companySize || '1-10', onboardingStatus: 'NOT_STARTED', mfaVerified: false, createdAt: new Date().toISOString() }; users.push(newUser); setStored('t4b_users', users); return { data: newUser as unknown as T, status: 201 }; } if (url === '/auth/mfa-verify') { const { email, code } = payload; const users = getStored('t4b_users'); const index = users.findIndex(u => u.email === email); if (index === -1) { throw { status: 404, message: 'User not found' }; } if (code !== '123456') { throw { status: 400, message: 'Invalid OTP code. Try "123456"' }; } users[index].mfaVerified = true; setStored('t4b_users', users); sessionStorage.setItem('t4b_session_email', email); return { data: users[index] as unknown as T, status: 200 }; } if (url === '/client/onboarding') { const activeUserEmail = sessionStorage.getItem('t4b_session_email'); const users = getStored('t4b_users'); const index = users.findIndex(u => u.email === activeUserEmail); if (index === -1) { throw { status: 401, message: 'Unauthorized' }; } users[index] = { ...users[index], ...payload, onboardingStatus: 'FORM_COMPLETED' }; setStored('t4b_users', users); return { data: users[index] as unknown as T, status: 200 }; } if (url === '/client/nda') { const activeUserEmail = sessionStorage.getItem('t4b_session_email'); const users = getStored('t4b_users'); const index = users.findIndex(u => u.email === activeUserEmail); if (index === -1) { throw { status: 401, message: 'Unauthorized' }; } users[index].ndaSignature = { type: payload.type, dataUrl: payload.dataUrl, date: new Date().toISOString() }; users[index].onboardingStatus = 'NDA_SIGNED'; setStored('t4b_users', users); return { data: users[index] as unknown as T, status: 200 }; } if (url === '/client/msa') { const activeUserEmail = sessionStorage.getItem('t4b_session_email'); const users = getStored('t4b_users'); const index = users.findIndex(u => u.email === activeUserEmail); if (index === -1) { throw { status: 401, message: 'Unauthorized' }; } users[index].msaSignature = { type: payload.type, dataUrl: payload.dataUrl, date: new Date().toISOString() }; users[index].onboardingStatus = 'PENDING_APPROVAL'; setStored('t4b_users', users); return { data: users[index] as unknown as T, status: 200 }; } if (url === '/assets/new') { const assets = getStored('t4b_assets'); const newAsset: Asset = { id: `asset-${Date.now()}`, title: payload.title, description: payload.description, categoryId: payload.categoryId, subcategory: payload.subcategory, tags: payload.tags || [], author: payload.author || 'Admin Staff', publishDate: new Date().toISOString().split('T')[0], thumbnailUrl: payload.thumbnailUrl || 'https://images.unsplash.com/photo-1488590528505-98d2b5aba04b?w=500&auto=format&fit=crop&q=60', downloadUrl: payload.downloadUrl || '#', githubUrl: payload.githubUrl, status: payload.status || 'draft', downloadsCount: 0 }; assets.push(newAsset); setStored('t4b_assets', assets); return { data: newAsset as unknown as T, status: 201 }; } if (url === '/blog/new') { const posts = getStored('t4b_blog_posts'); const newPost: BlogPost = { id: `blog-${Date.now()}`, title: payload.title, content: payload.content, author: payload.author || 'Admin Staff', publishDate: new Date().toISOString().split('T')[0], thumbnailUrl: payload.thumbnailUrl || 'https://images.unsplash.com/photo-1555066931-4365d14bab8c?w=500&auto=format&fit=crop&q=60', readTime: `${Math.ceil(payload.content.split(' ').length / 200)} min read`, tags: payload.tags || [], status: payload.status || 'draft' }; posts.push(newPost); setStored('t4b_blog_posts', posts); return { data: newPost as unknown as T, status: 201 }; } throw { status: 404, message: 'Route not found' }; }, put: async (url: string, payload: any): Promise> => { await delay(); if (url.startsWith('/admin/clients/')) { const clientId = url.split('/admin/clients/')[1]; const users = getStored('t4b_users'); const index = users.findIndex(u => u.id === clientId); if (index === -1) { throw { status: 404, message: 'Client not found' }; } users[index].onboardingStatus = payload.status; // 'APPROVED' or 'REJECTED' setStored('t4b_users', users); return { data: users[index] as unknown as T, status: 200 }; } if (url.startsWith('/assets/')) { const id = url.split('/assets/')[1]; const assets = getStored('t4b_assets'); const index = assets.findIndex(a => a.id === id); if (index === -1) { throw { status: 404, message: 'Asset not found' }; } assets[index] = { ...assets[index], ...payload }; setStored('t4b_assets', assets); return { data: assets[index] as unknown as T, status: 200 }; } throw { status: 404, message: 'Route not found' }; }, delete: async (url: string): Promise> => { await delay(); if (url.startsWith('/assets/')) { const id = url.split('/assets/')[1]; let assets = getStored('t4b_assets'); const exists = assets.some(a => a.id === id); if (!exists) { throw { status: 404, message: 'Asset not found' }; } assets = assets.filter(a => a.id !== id); setStored('t4b_assets', assets); return { data: { success: true } as unknown as T, status: 200 }; } throw { status: 404, message: 'Route not found' }; } };