345 lines
11 KiB
TypeScript
345 lines
11 KiB
TypeScript
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 = <T>(key: string): T => {
|
|
const data = localStorage.getItem(key);
|
|
return data ? JSON.parse(data) : [] as unknown as T;
|
|
};
|
|
|
|
const setStored = <T>(key: string, data: T): void => {
|
|
localStorage.setItem(key, JSON.stringify(data));
|
|
};
|
|
|
|
export interface ApiResponse<T> {
|
|
data: T;
|
|
status: number;
|
|
}
|
|
|
|
// Custom mock API client resembling Axios
|
|
export const apiClient = {
|
|
get: async <T>(url: string, config?: { params?: any }): Promise<ApiResponse<T>> => {
|
|
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<User[]>('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<Asset[]>('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<User[]>('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<Asset[]>('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<Category[]>('t4b_categories');
|
|
return { data: categories as unknown as T, status: 200 };
|
|
}
|
|
|
|
// Blog posts
|
|
if (cleanUrl === '/blog') {
|
|
const posts = getStored<BlogPost[]>('t4b_blog_posts');
|
|
// Filter out drafts for client roles
|
|
const activeUserEmail = sessionStorage.getItem('t4b_session_email');
|
|
const users = getStored<User[]>('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<Announcement[]>('t4b_announcements');
|
|
return { data: announcements as unknown as T, status: 200 };
|
|
}
|
|
|
|
// Admin Client Queue
|
|
if (cleanUrl === '/admin/clients') {
|
|
const users = getStored<User[]>('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 <T>(url: string, payload: any): Promise<ApiResponse<T>> => {
|
|
await delay();
|
|
|
|
if (url === '/auth/login') {
|
|
const { email, password } = payload;
|
|
const users = getStored<User[]>('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<User[]>('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<User[]>('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<User[]>('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<User[]>('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<User[]>('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<Asset[]>('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<BlogPost[]>('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 <T>(url: string, payload: any): Promise<ApiResponse<T>> => {
|
|
await delay();
|
|
|
|
if (url.startsWith('/admin/clients/')) {
|
|
const clientId = url.split('/admin/clients/')[1];
|
|
const users = getStored<User[]>('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<Asset[]>('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 <T>(url: string): Promise<ApiResponse<T>> => {
|
|
await delay();
|
|
|
|
if (url.startsWith('/assets/')) {
|
|
const id = url.split('/assets/')[1];
|
|
let assets = getStored<Asset[]>('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' };
|
|
}
|
|
};
|