841 lines
26 KiB
TypeScript
841 lines
26 KiB
TypeScript
/**
|
|
* Mock API Service
|
|
*
|
|
* Purpose: Simulates backend API for development and testing
|
|
* Provides realistic API responses with proper structure, error handling, and validation
|
|
*
|
|
* This replaces localStorage approach with a more realistic API simulation
|
|
*/
|
|
|
|
// API Response Types
|
|
interface ApiResponse<T = any> {
|
|
success: boolean;
|
|
data?: T;
|
|
message?: string;
|
|
error?: {
|
|
code: string;
|
|
message: string;
|
|
details?: any;
|
|
};
|
|
meta?: {
|
|
timestamp: string;
|
|
requestId?: string;
|
|
version?: string;
|
|
};
|
|
}
|
|
|
|
interface PaginatedResponse<T> extends ApiResponse<T[]> {
|
|
pagination?: {
|
|
page: number;
|
|
limit: number;
|
|
total: number;
|
|
totalPages: number;
|
|
};
|
|
}
|
|
|
|
// In-memory data store
|
|
const mockDatabase: {
|
|
requests: Map<string, any>;
|
|
approvalFlows: Map<string, any[]>;
|
|
documents: Map<string, any[]>;
|
|
activities: Map<string, any[]>;
|
|
ioBlocks: Map<string, any>;
|
|
} = {
|
|
requests: new Map(),
|
|
approvalFlows: new Map(),
|
|
documents: new Map(),
|
|
activities: new Map(),
|
|
ioBlocks: new Map(),
|
|
};
|
|
|
|
// Helper to simulate realistic API delay
|
|
const delay = (min: number = 300, max: number = 800): Promise<void> => {
|
|
const delayTime = Math.floor(Math.random() * (max - min + 1)) + min;
|
|
return new Promise(resolve => setTimeout(resolve, delayTime));
|
|
};
|
|
|
|
// Helper to create success response
|
|
function successResponse<T>(data: T, message?: string): ApiResponse<T> {
|
|
return {
|
|
success: true,
|
|
data,
|
|
message: message || 'Operation completed successfully',
|
|
meta: {
|
|
timestamp: new Date().toISOString(),
|
|
version: '1.0',
|
|
},
|
|
};
|
|
}
|
|
|
|
// Helper to create error response
|
|
function errorResponse(code: string, message: string, details?: any): ApiResponse {
|
|
return {
|
|
success: false,
|
|
error: {
|
|
code,
|
|
message,
|
|
details,
|
|
},
|
|
meta: {
|
|
timestamp: new Date().toISOString(),
|
|
version: '1.0',
|
|
},
|
|
};
|
|
}
|
|
|
|
// Helper to validate request ID
|
|
function validateRequestId(requestId: string | undefined | null): string {
|
|
if (!requestId) {
|
|
throw new Error('REQUEST_ID_REQUIRED');
|
|
}
|
|
return requestId;
|
|
}
|
|
|
|
/**
|
|
* Generate unique ID
|
|
*/
|
|
function generateId(prefix: string = 'req'): string {
|
|
const timestamp = Date.now();
|
|
const random = Math.random().toString(36).substring(2, 9);
|
|
return `${prefix}-${timestamp}-${random}`;
|
|
}
|
|
|
|
/**
|
|
* Mock API Service Class
|
|
*/
|
|
class MockApiService {
|
|
/**
|
|
* Create a new request
|
|
*/
|
|
async createRequest(requestData: any): Promise<ApiResponse<any>> {
|
|
try {
|
|
await delay(600, 1000);
|
|
|
|
// Validation
|
|
if (!requestData) {
|
|
return errorResponse('VALIDATION_ERROR', 'Request data is required');
|
|
}
|
|
|
|
const requestId = requestData.requestId || `RE-REQ-${new Date().getFullYear()}-CM-${String(Math.floor(Math.random() * 1000)).padStart(3, '0')}`;
|
|
const now = new Date().toISOString();
|
|
|
|
// Check if request ID already exists
|
|
if (mockDatabase.requests.has(requestId)) {
|
|
return errorResponse('DUPLICATE_REQUEST', `Request with ID ${requestId} already exists`);
|
|
}
|
|
|
|
const request = {
|
|
...requestData,
|
|
id: requestId,
|
|
requestId: requestId,
|
|
requestNumber: requestId,
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
version: 1,
|
|
};
|
|
|
|
mockDatabase.requests.set(requestId, request);
|
|
mockDatabase.approvalFlows.set(requestId, []);
|
|
mockDatabase.documents.set(requestId, []);
|
|
mockDatabase.activities.set(requestId, []);
|
|
|
|
console.log('[MockAPI] ✅ Request created:', requestId);
|
|
return successResponse(request, 'Request created successfully');
|
|
} catch (error: any) {
|
|
console.error('[MockAPI] ❌ Error creating request:', error);
|
|
return errorResponse('INTERNAL_ERROR', 'Failed to create request', error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get request by ID
|
|
*/
|
|
async getRequest(requestId: string): Promise<ApiResponse<any>> {
|
|
try {
|
|
await delay(200, 500);
|
|
|
|
const id = validateRequestId(requestId);
|
|
const request = mockDatabase.requests.get(id);
|
|
|
|
if (!request) {
|
|
return errorResponse('NOT_FOUND', `Request with ID ${id} not found`);
|
|
}
|
|
|
|
// Attach related data
|
|
const approvalFlow = (mockDatabase.approvalFlows.get(id) || []).sort((a: any, b: any) => a.step - b.step);
|
|
const documents = (mockDatabase.documents.get(id) || []).sort((a: any, b: any) =>
|
|
new Date(b.uploadedAt).getTime() - new Date(a.uploadedAt).getTime()
|
|
);
|
|
const activities = (mockDatabase.activities.get(id) || []).sort((a: any, b: any) =>
|
|
new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
|
|
);
|
|
const ioBlock = mockDatabase.ioBlocks.get(id) || null;
|
|
|
|
const enrichedRequest = {
|
|
...request,
|
|
approvalFlow,
|
|
documents,
|
|
activities,
|
|
auditTrail: activities,
|
|
ioBlock,
|
|
_meta: {
|
|
approvalFlowCount: approvalFlow.length,
|
|
documentCount: documents.length,
|
|
activityCount: activities.length,
|
|
hasIOBlock: !!ioBlock,
|
|
},
|
|
};
|
|
|
|
return successResponse(enrichedRequest);
|
|
} catch (error: any) {
|
|
console.error('[MockAPI] ❌ Error fetching request:', error);
|
|
return errorResponse('INTERNAL_ERROR', 'Failed to fetch request', error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update request
|
|
*/
|
|
async updateRequest(requestId: string, updates: any): Promise<ApiResponse<any>> {
|
|
try {
|
|
await delay(300, 600);
|
|
|
|
const id = validateRequestId(requestId);
|
|
const request = mockDatabase.requests.get(id);
|
|
|
|
if (!request) {
|
|
return errorResponse('NOT_FOUND', `Request with ID ${id} not found`);
|
|
}
|
|
|
|
// Validation: prevent invalid status transitions
|
|
if (updates.status && request.status === 'cancelled' && updates.status !== 'cancelled') {
|
|
return errorResponse('INVALID_TRANSITION', 'Cannot change status of a cancelled request');
|
|
}
|
|
|
|
const updated = {
|
|
...request,
|
|
...updates,
|
|
updatedAt: new Date().toISOString(),
|
|
version: (request.version || 1) + 1,
|
|
};
|
|
|
|
mockDatabase.requests.set(id, updated);
|
|
console.log('[MockAPI] ✅ Request updated:', id, Object.keys(updates));
|
|
|
|
return successResponse(updated, 'Request updated successfully');
|
|
} catch (error: any) {
|
|
console.error('[MockAPI] ❌ Error updating request:', error);
|
|
return errorResponse('INTERNAL_ERROR', 'Failed to update request', error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create approval flow step
|
|
*/
|
|
async createApprovalFlow(requestId: string, flowData: any): Promise<ApiResponse<any>> {
|
|
try {
|
|
await delay(200, 400);
|
|
|
|
const id = validateRequestId(requestId);
|
|
|
|
// Validate request exists
|
|
if (!mockDatabase.requests.has(id)) {
|
|
return errorResponse('NOT_FOUND', `Request with ID ${id} not found`);
|
|
}
|
|
|
|
// Validation
|
|
if (!flowData.step || !flowData.approver || !flowData.role) {
|
|
return errorResponse('VALIDATION_ERROR', 'Step, approver, and role are required');
|
|
}
|
|
|
|
const flows = mockDatabase.approvalFlows.get(id) || [];
|
|
|
|
// Check for duplicate step
|
|
if (flows.some((f: any) => f.step === flowData.step)) {
|
|
return errorResponse('DUPLICATE_STEP', `Step ${flowData.step} already exists for this request`);
|
|
}
|
|
|
|
const flow = {
|
|
...flowData,
|
|
id: flowData.id || generateId('flow'),
|
|
requestId: id,
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
};
|
|
|
|
flows.push(flow);
|
|
flows.sort((a: any, b: any) => a.step - b.step);
|
|
mockDatabase.approvalFlows.set(id, flows);
|
|
|
|
console.log('[MockAPI] ✅ Approval flow created:', flow.id, `Step ${flow.step}`);
|
|
return successResponse(flow, 'Approval flow step created successfully');
|
|
} catch (error: any) {
|
|
console.error('[MockAPI] ❌ Error creating approval flow:', error);
|
|
return errorResponse('INTERNAL_ERROR', 'Failed to create approval flow', error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update approval flow step
|
|
*/
|
|
async updateApprovalFlow(requestId: string, flowId: string, updates: any): Promise<ApiResponse<any>> {
|
|
try {
|
|
await delay(250, 500);
|
|
|
|
const id = validateRequestId(requestId);
|
|
const flows = mockDatabase.approvalFlows.get(id) || [];
|
|
const index = flows.findIndex((f: any) => f.id === flowId);
|
|
|
|
if (index === -1) {
|
|
return errorResponse('NOT_FOUND', `Approval flow with ID ${flowId} not found`);
|
|
}
|
|
|
|
const currentFlow = flows[index];
|
|
|
|
// Validation: prevent invalid status transitions
|
|
if (updates.status) {
|
|
const validTransitions: Record<string, string[]> = {
|
|
'waiting': ['pending', 'cancelled'],
|
|
'pending': ['approved', 'rejected', 'cancelled'],
|
|
'approved': [], // Final state
|
|
'rejected': [], // Final state
|
|
'cancelled': [], // Final state
|
|
};
|
|
|
|
const allowed = validTransitions[currentFlow.status] || [];
|
|
if (!allowed.includes(updates.status)) {
|
|
return errorResponse('INVALID_TRANSITION',
|
|
`Cannot transition from ${currentFlow.status} to ${updates.status}`);
|
|
}
|
|
}
|
|
|
|
flows[index] = {
|
|
...currentFlow,
|
|
...updates,
|
|
updatedAt: new Date().toISOString(),
|
|
};
|
|
|
|
mockDatabase.approvalFlows.set(id, flows);
|
|
console.log('[MockAPI] ✅ Approval flow updated:', flowId, updates.status || 'fields updated');
|
|
|
|
return successResponse(flows[index], 'Approval flow updated successfully');
|
|
} catch (error: any) {
|
|
console.error('[MockAPI] ❌ Error updating approval flow:', error);
|
|
return errorResponse('INTERNAL_ERROR', 'Failed to update approval flow', error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get approval flows for request
|
|
*/
|
|
async getApprovalFlows(requestId: string): Promise<ApiResponse<any[]>> {
|
|
try {
|
|
await delay(150, 300);
|
|
|
|
const id = validateRequestId(requestId);
|
|
const flows = (mockDatabase.approvalFlows.get(id) || []).sort((a: any, b: any) => a.step - b.step);
|
|
|
|
return successResponse(flows);
|
|
} catch (error: any) {
|
|
console.error('[MockAPI] ❌ Error fetching approval flows:', error);
|
|
return errorResponse('INTERNAL_ERROR', 'Failed to fetch approval flows', error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create document
|
|
*/
|
|
async createDocument(requestId: string, documentData: any): Promise<ApiResponse<any>> {
|
|
try {
|
|
await delay(400, 800);
|
|
|
|
const id = validateRequestId(requestId);
|
|
|
|
// Validate request exists
|
|
if (!mockDatabase.requests.has(id)) {
|
|
return errorResponse('NOT_FOUND', `Request with ID ${id} not found`);
|
|
}
|
|
|
|
// Validation
|
|
if (!documentData.name || !documentData.type) {
|
|
return errorResponse('VALIDATION_ERROR', 'Document name and type are required');
|
|
}
|
|
|
|
const documents = mockDatabase.documents.get(id) || [];
|
|
const document = {
|
|
...documentData,
|
|
id: documentData.id || generateId('doc'),
|
|
requestId: id,
|
|
uploadedAt: documentData.uploadedAt || new Date().toISOString(),
|
|
size: documentData.size || 0,
|
|
mimeType: documentData.mimeType || 'application/octet-stream',
|
|
};
|
|
|
|
documents.push(document);
|
|
mockDatabase.documents.set(id, documents);
|
|
|
|
console.log('[MockAPI] ✅ Document created:', document.id, document.name);
|
|
return successResponse(document, 'Document uploaded successfully');
|
|
} catch (error: any) {
|
|
console.error('[MockAPI] ❌ Error creating document:', error);
|
|
return errorResponse('INTERNAL_ERROR', 'Failed to upload document', error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get documents for request
|
|
*/
|
|
async getDocuments(requestId: string): Promise<ApiResponse<any[]>> {
|
|
try {
|
|
await delay(150, 300);
|
|
|
|
const id = validateRequestId(requestId);
|
|
const documents = (mockDatabase.documents.get(id) || []).sort((a: any, b: any) =>
|
|
new Date(b.uploadedAt).getTime() - new Date(a.uploadedAt).getTime()
|
|
);
|
|
|
|
return successResponse(documents);
|
|
} catch (error: any) {
|
|
console.error('[MockAPI] ❌ Error fetching documents:', error);
|
|
return errorResponse('INTERNAL_ERROR', 'Failed to fetch documents', error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create activity
|
|
*/
|
|
async createActivity(requestId: string, activityData: any): Promise<ApiResponse<any>> {
|
|
try {
|
|
await delay(150, 300);
|
|
|
|
const id = validateRequestId(requestId);
|
|
|
|
// Validate request exists
|
|
if (!mockDatabase.requests.has(id)) {
|
|
return errorResponse('NOT_FOUND', `Request with ID ${id} not found`);
|
|
}
|
|
|
|
// Validation
|
|
if (!activityData.type || !activityData.action) {
|
|
return errorResponse('VALIDATION_ERROR', 'Activity type and action are required');
|
|
}
|
|
|
|
const activities = mockDatabase.activities.get(id) || [];
|
|
const activity = {
|
|
...activityData,
|
|
id: activityData.id || generateId('act'),
|
|
requestId: id,
|
|
timestamp: activityData.timestamp || new Date().toISOString(),
|
|
};
|
|
|
|
activities.push(activity);
|
|
activities.sort((a: any, b: any) =>
|
|
new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
|
|
);
|
|
mockDatabase.activities.set(id, activities);
|
|
|
|
console.log('[MockAPI] ✅ Activity created:', activity.id, activity.action);
|
|
return successResponse(activity, 'Activity logged successfully');
|
|
} catch (error: any) {
|
|
console.error('[MockAPI] ❌ Error creating activity:', error);
|
|
return errorResponse('INTERNAL_ERROR', 'Failed to create activity', error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get activities for request
|
|
*/
|
|
async getActivities(requestId: string): Promise<ApiResponse<any[]>> {
|
|
try {
|
|
await delay(150, 300);
|
|
|
|
const id = validateRequestId(requestId);
|
|
const activities = (mockDatabase.activities.get(id) || []).sort((a: any, b: any) =>
|
|
new Date(b.timestamp).getTime() - new Date(a.timestamp).getTime()
|
|
);
|
|
|
|
return successResponse(activities);
|
|
} catch (error: any) {
|
|
console.error('[MockAPI] ❌ Error fetching activities:', error);
|
|
return errorResponse('INTERNAL_ERROR', 'Failed to fetch activities', error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Create IO block
|
|
*/
|
|
async createIOBlock(requestId: string, ioBlockData: any): Promise<ApiResponse<any>> {
|
|
try {
|
|
await delay(500, 1000); // Simulate SAP integration delay
|
|
|
|
const id = validateRequestId(requestId);
|
|
|
|
// Validate request exists
|
|
if (!mockDatabase.requests.has(id)) {
|
|
return errorResponse('NOT_FOUND', `Request with ID ${id} not found`);
|
|
}
|
|
|
|
// Validation
|
|
if (!ioBlockData.ioNumber || !ioBlockData.blockedAmount) {
|
|
return errorResponse('VALIDATION_ERROR', 'IO number and blocked amount are required');
|
|
}
|
|
|
|
// Check if IO block already exists
|
|
const existing = mockDatabase.ioBlocks.get(id);
|
|
if (existing) {
|
|
return errorResponse('DUPLICATE_IO_BLOCK', 'IO block already exists for this request');
|
|
}
|
|
|
|
const ioBlock = {
|
|
...ioBlockData,
|
|
id: ioBlockData.id || generateId('ioblock'),
|
|
requestId: id,
|
|
blockedDate: ioBlockData.blockedDate || new Date().toISOString(),
|
|
status: ioBlockData.status || 'blocked',
|
|
createdAt: new Date().toISOString(),
|
|
updatedAt: new Date().toISOString(),
|
|
};
|
|
|
|
mockDatabase.ioBlocks.set(id, ioBlock);
|
|
console.log('[MockAPI] ✅ IO block created:', ioBlock.id, ioBlock.ioNumber);
|
|
|
|
return successResponse(ioBlock, 'IO budget blocked successfully in SAP');
|
|
} catch (error: any) {
|
|
console.error('[MockAPI] ❌ Error creating IO block:', error);
|
|
return errorResponse('INTERNAL_ERROR', 'Failed to block IO budget', error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Update IO block
|
|
*/
|
|
async updateIOBlock(requestId: string, updates: any): Promise<ApiResponse<any>> {
|
|
try {
|
|
await delay(300, 600);
|
|
|
|
const id = validateRequestId(requestId);
|
|
const ioBlock = mockDatabase.ioBlocks.get(id);
|
|
|
|
if (!ioBlock) {
|
|
return errorResponse('NOT_FOUND', 'IO block not found for this request');
|
|
}
|
|
|
|
const updated = {
|
|
...ioBlock,
|
|
...updates,
|
|
updatedAt: new Date().toISOString(),
|
|
};
|
|
|
|
mockDatabase.ioBlocks.set(id, updated);
|
|
console.log('[MockAPI] ✅ IO block updated:', id);
|
|
|
|
return successResponse(updated, 'IO block updated successfully');
|
|
} catch (error: any) {
|
|
console.error('[MockAPI] ❌ Error updating IO block:', error);
|
|
return errorResponse('INTERNAL_ERROR', 'Failed to update IO block', error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get IO block for request
|
|
*/
|
|
async getIOBlock(requestId: string): Promise<ApiResponse<any>> {
|
|
try {
|
|
await delay(150, 300);
|
|
|
|
const id = validateRequestId(requestId);
|
|
const ioBlock = mockDatabase.ioBlocks.get(id) || null;
|
|
|
|
if (!ioBlock) {
|
|
return errorResponse('NOT_FOUND', 'IO block not found for this request');
|
|
}
|
|
|
|
return successResponse(ioBlock);
|
|
} catch (error: any) {
|
|
console.error('[MockAPI] ❌ Error fetching IO block:', error);
|
|
return errorResponse('INTERNAL_ERROR', 'Failed to fetch IO block', error.message);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Get all requests (for listing)
|
|
*/
|
|
async getAllRequests(filters?: any): Promise<PaginatedResponse<any>> {
|
|
try {
|
|
await delay(300, 600);
|
|
|
|
let requests = Array.from(mockDatabase.requests.values());
|
|
|
|
// Apply filters if provided
|
|
if (filters) {
|
|
if (filters.status) {
|
|
requests = requests.filter((r: any) => r.status === filters.status);
|
|
}
|
|
if (filters.category) {
|
|
requests = requests.filter((r: any) => r.category === filters.category);
|
|
}
|
|
if (filters.initiatorId) {
|
|
requests = requests.filter((r: any) => r.initiator?.userId === filters.initiatorId);
|
|
}
|
|
}
|
|
|
|
// Sort by updated date (newest first)
|
|
requests.sort((a: any, b: any) =>
|
|
new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()
|
|
);
|
|
|
|
const page = filters?.page || 1;
|
|
const limit = filters?.limit || 50;
|
|
const total = requests.length;
|
|
const totalPages = Math.ceil(total / limit);
|
|
const startIndex = (page - 1) * limit;
|
|
const endIndex = startIndex + limit;
|
|
const paginatedRequests = requests.slice(startIndex, endIndex);
|
|
|
|
return {
|
|
success: true,
|
|
data: paginatedRequests,
|
|
pagination: {
|
|
page,
|
|
limit,
|
|
total,
|
|
totalPages,
|
|
},
|
|
meta: {
|
|
timestamp: new Date().toISOString(),
|
|
version: '1.0',
|
|
},
|
|
};
|
|
} catch (error: any) {
|
|
console.error('[MockAPI] ❌ Error fetching requests:', error);
|
|
return errorResponse('INTERNAL_ERROR', 'Failed to fetch requests', error.message) as any;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Clear all data (for testing)
|
|
*/
|
|
clearAll(): void {
|
|
mockDatabase.requests.clear();
|
|
mockDatabase.approvalFlows.clear();
|
|
mockDatabase.documents.clear();
|
|
mockDatabase.activities.clear();
|
|
mockDatabase.ioBlocks.clear();
|
|
console.log('[MockAPI] 🗑️ All data cleared');
|
|
}
|
|
|
|
/**
|
|
* Initialize with dummy data
|
|
*/
|
|
initializeDummyData(): void {
|
|
// Create a sample request for testing
|
|
const sampleRequestId = 'RE-REQ-2024-CM-001';
|
|
const now = new Date().toISOString();
|
|
|
|
if (mockDatabase.requests.has(sampleRequestId)) {
|
|
return; // Already initialized
|
|
}
|
|
|
|
const sampleRequest = {
|
|
id: sampleRequestId,
|
|
requestId: sampleRequestId,
|
|
requestNumber: sampleRequestId,
|
|
title: 'Diwali Festival Campaign - Claim Request',
|
|
description: 'Claim request for dealer-led Diwali festival marketing campaign',
|
|
category: 'claim-management',
|
|
subcategory: 'Claim Management',
|
|
type: 'dealer-claim',
|
|
status: 'pending',
|
|
priority: 'standard',
|
|
amount: 245000,
|
|
claimAmount: 245000,
|
|
slaProgress: 35,
|
|
slaRemaining: '4 days 12 hours',
|
|
slaEndDate: new Date(Date.now() + 4 * 24 * 60 * 60 * 1000).toISOString(),
|
|
currentStep: 1,
|
|
totalSteps: 8,
|
|
template: 'claim-management',
|
|
templateName: 'Claim Management',
|
|
initiator: {
|
|
userId: 'user-123',
|
|
name: 'Sneha Patil',
|
|
email: 'sneha.patil@royalenfield.com',
|
|
role: 'Regional Marketing Coordinator',
|
|
department: 'Marketing - West Zone',
|
|
phone: '+91 98765 43250',
|
|
avatar: 'SP'
|
|
},
|
|
department: 'Marketing - West Zone',
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
dueDate: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000).toISOString(),
|
|
conclusionRemark: '',
|
|
claimDetails: {
|
|
activityName: 'Diwali Festival Campaign 2024',
|
|
activityType: 'Marketing Activity',
|
|
activityDate: now,
|
|
location: 'Mumbai, Maharashtra',
|
|
dealerCode: 'RE-MH-001',
|
|
dealerName: 'Royal Motors Mumbai',
|
|
dealerEmail: 'dealer@royalmotorsmumbai.com',
|
|
dealerPhone: '+91 98765 12345',
|
|
dealerAddress: '123 Main Street, Andheri West, Mumbai, Maharashtra 400053',
|
|
requestDescription: 'Marketing campaign for Diwali festival',
|
|
estimatedBudget: '₹2,45,000',
|
|
periodStart: now,
|
|
periodEnd: new Date(Date.now() + 10 * 24 * 60 * 60 * 1000).toISOString()
|
|
},
|
|
dealerInfo: {
|
|
name: 'Royal Motors Mumbai',
|
|
code: 'RE-MH-001',
|
|
email: 'dealer@royalmotorsmumbai.com',
|
|
phone: '+91 98765 12345',
|
|
address: '123 Main Street, Andheri West, Mumbai, Maharashtra 400053',
|
|
},
|
|
activityInfo: {
|
|
activityName: 'Diwali Festival Campaign 2024',
|
|
activityType: 'Marketing Activity',
|
|
activityDate: now,
|
|
location: 'Mumbai, Maharashtra',
|
|
},
|
|
tags: ['claim-management', 'marketing-activity'],
|
|
version: 1,
|
|
};
|
|
|
|
mockDatabase.requests.set(sampleRequestId, sampleRequest);
|
|
|
|
// Create approval flows
|
|
const approvalFlows = [
|
|
{
|
|
id: 'flow-1',
|
|
requestId: sampleRequestId,
|
|
step: 1,
|
|
levelId: 'level-1',
|
|
approver: 'Royal Motors Mumbai (Dealer)',
|
|
role: 'Dealer - Proposal Submission',
|
|
status: 'pending',
|
|
tatHours: 72,
|
|
assignedAt: now,
|
|
description: 'Dealer submits the proposal for the activity with comments including proposal document with requested details, cost break-up, timeline for closure, and other requests',
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
},
|
|
{
|
|
id: 'flow-2',
|
|
requestId: sampleRequestId,
|
|
step: 2,
|
|
levelId: 'level-2',
|
|
approver: 'Sneha Patil (Requestor)',
|
|
role: 'Requestor Evaluation & Confirmation',
|
|
status: 'waiting',
|
|
tatHours: 48,
|
|
description: 'Requestor evaluates the request and confirms with comments. Decision point: Confirms? (YES → Continue to Dept Lead / NO → Request is cancelled)',
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
},
|
|
{
|
|
id: 'flow-3',
|
|
requestId: sampleRequestId,
|
|
step: 3,
|
|
levelId: 'level-3',
|
|
approver: 'Department Lead',
|
|
role: 'Dept Lead Approval',
|
|
status: 'waiting',
|
|
tatHours: 72,
|
|
description: 'Department Lead approval. Decision point: Approved? (YES → Budget is blocked in the respective IO for the activity / NO → More clarification required → Request is cancelled)',
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
},
|
|
{
|
|
id: 'flow-4',
|
|
requestId: sampleRequestId,
|
|
step: 4,
|
|
levelId: 'level-4',
|
|
approver: 'System Auto-Process',
|
|
role: 'Activity Creation',
|
|
status: 'waiting',
|
|
tatHours: 1,
|
|
description: 'Activity is created. Activity confirmation email is auto-triggered to dealer / requestor / Lead. IO confirmation to be made.',
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
},
|
|
{
|
|
id: 'flow-5',
|
|
requestId: sampleRequestId,
|
|
step: 5,
|
|
levelId: 'level-5',
|
|
approver: 'Royal Motors Mumbai (Dealer)',
|
|
role: 'Dealer - Completion Documents',
|
|
status: 'waiting',
|
|
tatHours: 120,
|
|
description: 'Dealer submits the necessary documents upon completion of the activity including document attachments (Zip Folder) and brief description',
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
},
|
|
{
|
|
id: 'flow-6',
|
|
requestId: sampleRequestId,
|
|
step: 6,
|
|
levelId: 'level-6',
|
|
approver: 'Sneha Patil (Requestor)',
|
|
role: 'Requestor - Claim Approval',
|
|
status: 'waiting',
|
|
tatHours: 48,
|
|
description: 'Requestor approves the claim in full or can modify the amount. If more information is required, can request additional details from dealer.',
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
},
|
|
{
|
|
id: 'flow-7',
|
|
requestId: sampleRequestId,
|
|
step: 7,
|
|
levelId: 'level-7',
|
|
approver: 'System Auto-Process',
|
|
role: 'E-Invoice Generation',
|
|
status: 'waiting',
|
|
tatHours: 1,
|
|
description: 'E-invoice will be generated through DMS.',
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
},
|
|
{
|
|
id: 'flow-8',
|
|
requestId: sampleRequestId,
|
|
step: 8,
|
|
levelId: 'level-8',
|
|
approver: 'Finance Team',
|
|
role: 'Credit Note from SAP',
|
|
status: 'waiting',
|
|
tatHours: 48,
|
|
description: 'Got credit note from SAP. Review and send to dealer to complete the claim management process.',
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
},
|
|
];
|
|
|
|
mockDatabase.approvalFlows.set(sampleRequestId, approvalFlows);
|
|
|
|
// Create initial activity
|
|
mockDatabase.activities.set(sampleRequestId, [{
|
|
id: 'act-1',
|
|
requestId: sampleRequestId,
|
|
type: 'created',
|
|
action: 'Request Created',
|
|
details: 'Claim request for Diwali Festival Campaign 2024 created',
|
|
user: 'Sneha Patil',
|
|
timestamp: now,
|
|
message: 'Request created',
|
|
}]);
|
|
|
|
console.log('[MockAPI] 📦 Dummy data initialized');
|
|
}
|
|
}
|
|
|
|
// Export singleton instance
|
|
export const mockApi = new MockApiService();
|
|
|
|
// Initialize dummy data on import
|
|
if (typeof window !== 'undefined') {
|
|
mockApi.initializeDummyData();
|
|
}
|