Files
tgex-frontend/lib/demo/api.ts
2025-12-29 10:12:13 +03:00

197 lines
5.7 KiB
TypeScript

// ============================================================================
// Demo API - Mock responses for demo mode
// ============================================================================
import type {
PaginatedResponse,
Workspace,
Project,
Channel,
Creative,
Placement,
PurchasePlanChannel,
WorkspaceMember,
SpendingAnalytics,
CreativeAnalyticsItem,
ChannelAnalyticsItem,
} from "@/lib/types/api";
import {
demoWorkspace,
demoMember,
demoProjects,
demoChannels,
demoCreatives,
demoPlacements,
demoPurchasePlanChannels,
demoSpendingAnalytics,
demoCreativeAnalytics,
demoChannelAnalytics,
} from "./mock-data";
import { DEMO_USER } from "./constants";
// ============================================================================
// Helper to create paginated response
// ============================================================================
const paginate = <T>(items: T[], page = 1, size = 50): PaginatedResponse<T> => ({
items: items.slice((page - 1) * size, page * size),
total: items.length,
page,
size,
pages: Math.ceil(items.length / size),
});
// ============================================================================
// Demo API Methods
// ============================================================================
export const demoApi = {
// Auth
auth: {
me: () => Promise.resolve(DEMO_USER),
},
// Workspaces
workspaces: {
list: () => Promise.resolve(paginate([demoWorkspace])),
get: () => Promise.resolve(demoWorkspace),
},
// Members
members: {
list: () => Promise.resolve(paginate([demoMember])),
},
// Projects
projects: {
list: (params?: { project_id?: string }) => {
let items = demoProjects;
if (params?.project_id) {
items = items.filter((p) => p.id === params.project_id);
}
return Promise.resolve(paginate(items));
},
},
// Channels
channels: {
list: () => Promise.resolve(paginate(demoChannels)),
},
// Creatives
creatives: {
list: (params?: { project_id?: string; include_archived?: boolean }) => {
let items = demoCreatives;
if (params?.project_id) {
items = items.filter((c) => c.project_id === params.project_id);
}
if (!params?.include_archived) {
items = items.filter((c) => c.status === "active");
}
return Promise.resolve(paginate(items));
},
get: (id: string) => {
const creative = demoCreatives.find((c) => c.id === id);
if (!creative) throw new Error("Creative not found");
return Promise.resolve(creative);
},
},
// Placements
placements: {
list: (params?: {
project_id?: string;
placement_channel_id?: string;
creative_id?: string;
include_archived?: boolean;
}) => {
let items = demoPlacements;
if (params?.project_id) {
items = items.filter((p) => p.project_id === params.project_id);
}
if (params?.placement_channel_id) {
items = items.filter(
(p) => p.placement_channel_id === params.placement_channel_id
);
}
if (params?.creative_id) {
items = items.filter((p) => p.creative_id === params.creative_id);
}
if (!params?.include_archived) {
items = items.filter((p) => p.status === "active");
}
return Promise.resolve(paginate(items));
},
get: (id: string) => {
const placement = demoPlacements.find((p) => p.id === id);
if (!placement) throw new Error("Placement not found");
return Promise.resolve(placement);
},
},
// Purchase Plan
purchasePlan: {
list: (projectId: string) => {
const items = demoPurchasePlanChannels[projectId] || [];
return Promise.resolve(paginate(items));
},
},
// Analytics
analytics: {
spending: (params?: { project_id?: string; grouping?: string }) => {
// Filter by project if specified
if (params?.project_id) {
const projectPlacements = demoPlacements.filter(
(p) => p.project_id === params.project_id && p.status === "active"
);
const totalCost = projectPlacements.reduce(
(sum, p) => sum + (p.cost || 0),
0
);
const totalSubs = projectPlacements.reduce(
(sum, p) => sum + (p.subscriptions_count || 0),
0
);
const totalViews = projectPlacements.reduce(
(sum, p) => sum + (p.views_count || 0),
0
);
return Promise.resolve({
total_cost: totalCost,
total_subscriptions: totalSubs,
total_views: totalViews,
avg_cpf: totalSubs > 0 ? totalCost / totalSubs : null,
avg_cpm: totalViews > 0 ? (totalCost / totalViews) * 1000 : null,
chart_data: demoSpendingAnalytics.chart_data.slice(0, 3),
} as SpendingAnalytics);
}
return Promise.resolve(demoSpendingAnalytics);
},
creatives: (params?: { project_id?: string }) => {
let items = demoCreativeAnalytics;
if (params?.project_id) {
const projectCreativeIds = demoCreatives
.filter((c) => c.project_id === params.project_id)
.map((c) => c.id);
items = items.filter((a) => projectCreativeIds.includes(a.id));
}
return Promise.resolve(paginate(items));
},
channels: (params?: { project_id?: string }) => {
let items = demoChannelAnalytics;
if (params?.project_id) {
const projectPlacementChannelIds = new Set(
demoPlacements
.filter((p) => p.project_id === params.project_id)
.map((p) => p.placement_channel_id)
);
items = items.filter((a) => projectPlacementChannelIds.has(a.channel_id));
}
return Promise.resolve(paginate(items));
},
},
};