667 lines
No EOL
23 KiB
TypeScript
667 lines
No EOL
23 KiB
TypeScript
import axios from 'axios';
|
|
|
|
// Base URL for API requests
|
|
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || '/semblance_back/api';
|
|
|
|
|
|
// Create axios instance with baseURL
|
|
const api = axios.create({
|
|
baseURL: API_BASE_URL,
|
|
headers: {
|
|
'Content-Type': 'application/json'
|
|
},
|
|
// Default timeout - will be overridden for specific AI endpoints
|
|
timeout: 600000 // 10 minutes default timeout for AI operations
|
|
});
|
|
|
|
// Request interceptor to add JWT token
|
|
api.interceptors.request.use(
|
|
(config) => {
|
|
const token = localStorage.getItem('auth_token');
|
|
if (token) {
|
|
config.headers.Authorization = `Bearer ${token}`;
|
|
}
|
|
|
|
// Debug logging for focus group updates
|
|
if (config.method === 'put' && config.url?.includes('/focus-groups/')) {
|
|
console.log('🌐 API Request:', {
|
|
method: config.method,
|
|
url: config.url,
|
|
baseURL: config.baseURL,
|
|
fullURL: `${config.baseURL}${config.url}`,
|
|
data: config.data
|
|
});
|
|
}
|
|
|
|
// Debug logging for folder operations
|
|
if (config.url?.includes('/folders/')) {
|
|
console.log('🌐 API Folder Request:', {
|
|
method: config.method,
|
|
url: config.url,
|
|
baseURL: config.baseURL,
|
|
fullURL: `${config.baseURL}${config.url}`,
|
|
data: config.data
|
|
});
|
|
}
|
|
|
|
return config;
|
|
},
|
|
(error) => Promise.reject(error)
|
|
);
|
|
|
|
// Create a custom event for auth errors
|
|
export const AUTH_ERROR_EVENT = 'auth_error';
|
|
|
|
// Custom event with details
|
|
export interface AuthErrorDetail {
|
|
source?: string;
|
|
isPersonaCreation?: boolean;
|
|
}
|
|
|
|
// Function to dispatch auth error event
|
|
export const dispatchAuthError = (details?: AuthErrorDetail) => {
|
|
// Only remove auth data if not a persona creation request
|
|
if (!details?.isPersonaCreation) {
|
|
// Clean up local storage auth data
|
|
localStorage.removeItem('auth_token');
|
|
localStorage.removeItem('user');
|
|
}
|
|
|
|
// Dispatch event for handling in auth context
|
|
const event = new CustomEvent(AUTH_ERROR_EVENT, {
|
|
detail: details || {}
|
|
});
|
|
window.dispatchEvent(event);
|
|
};
|
|
|
|
// Response interceptor to handle common errors
|
|
api.interceptors.response.use(
|
|
(response) => response,
|
|
(error) => {
|
|
// Handle 401 (Unauthorized) - dispatch event instead of redirect
|
|
if (error.response && error.response.status === 401) {
|
|
// Check if this is a persona-related request - these are handled separately
|
|
const isPersonaRequest =
|
|
error.config &&
|
|
(error.config.url?.includes('/personas') ||
|
|
error.config.url?.includes('/personas/batch') ||
|
|
(error.config.method && error.config.url?.startsWith('/personas')));
|
|
|
|
console.log('API Error:', {
|
|
url: error.config?.url,
|
|
method: error.config?.method,
|
|
isPersonaRequest: isPersonaRequest
|
|
});
|
|
|
|
// For persona-related requests, don't automatically log out
|
|
// Let the component handle the auth error
|
|
if (!isPersonaRequest) {
|
|
dispatchAuthError({
|
|
source: error.config?.url,
|
|
isPersonaCreation: false
|
|
});
|
|
} else {
|
|
console.warn('Authentication error in persona request, letting component handle it');
|
|
}
|
|
}
|
|
|
|
return Promise.reject(error);
|
|
}
|
|
);
|
|
|
|
// Auth endpoints
|
|
export const authApi = {
|
|
login: (username: string, password: string) =>
|
|
api.post('/auth/login', { username, password }),
|
|
|
|
loginWithMicrosoft: (idToken: string) =>
|
|
api.post('/auth/microsoft', { id_token: idToken }),
|
|
|
|
register: (username: string, email: string, password: string) =>
|
|
api.post('/auth/register', { username, email, password }),
|
|
|
|
getProfile: () =>
|
|
api.get('/auth/me')
|
|
};
|
|
|
|
// Personas endpoints
|
|
export const personasApi = {
|
|
getAll: () =>
|
|
api.get('/personas/all'),
|
|
|
|
getById: (id: string) =>
|
|
api.get(`/personas/${id}`),
|
|
|
|
create: (personaData: any) =>
|
|
api.post('/personas', personaData),
|
|
|
|
update: (id: string, personaData: any) => {
|
|
// Don't try to update with local-prefixed IDs - they won't work
|
|
if (id && id.startsWith('local-')) {
|
|
console.log('Cannot update with local ID, creating new instead:', id);
|
|
return api.post('/personas', personaData);
|
|
}
|
|
return api.put(`/personas/${id}`, personaData);
|
|
},
|
|
|
|
modifyWithAI: (id: string, modificationData: any) => {
|
|
const personaId = typeof id === 'object' && id !== null ? (id as any)._id || id : id;
|
|
console.log(`Modifying persona with AI, ID: ${personaId}`);
|
|
return api.post(`/personas/${personaId}/modify-with-ai`, modificationData);
|
|
},
|
|
|
|
delete: (id: string) => {
|
|
// Handle different ID formats
|
|
// If the ID is an object with an _id property, use that
|
|
// Otherwise, use the provided ID string
|
|
const personaId = typeof id === 'object' && id !== null ? (id as any)._id || id : id;
|
|
console.log(`Deleting persona with ID: ${personaId}`);
|
|
return api.delete(`/personas/${personaId}`);
|
|
},
|
|
|
|
createBatch: (personasData: any[]) =>
|
|
api.post('/personas/batch', personasData),
|
|
|
|
// Export individual persona profile as markdown
|
|
exportProfile: (id: string, options?: { llm_model?: string; temperature?: number }) =>
|
|
api.post(`/personas/${id}/export-profile`, options || {}, {
|
|
timeout: 300000 // 5 minutes for profile export
|
|
})
|
|
};
|
|
|
|
// AI Persona Generation endpoints
|
|
export const aiPersonasApi = {
|
|
// Legacy endpoints
|
|
// Generate a persona without saving
|
|
generate: (options?: any) =>
|
|
api.post('/ai-personas/generate', options || {}, {
|
|
timeout: 600000 // 10 minutes for single persona generation
|
|
}),
|
|
|
|
// Generate and immediately save to database
|
|
generateAndSave: (options?: any) =>
|
|
api.post('/ai-personas/generate-and-save', options || {}, {
|
|
timeout: 600000 // 10 minutes for single persona generation
|
|
}),
|
|
|
|
// Generate multiple personas without saving
|
|
batchGenerate: (options: { count: number, customizations?: any[], temperature?: number }) =>
|
|
api.post('/ai-personas/batch-generate', options, {
|
|
timeout: 600000 // 10 minutes for batch generation
|
|
}),
|
|
|
|
// Generate multiple personas and save to database
|
|
batchGenerateAndSave: (options: { count: number, customizations?: any[], temperature?: number }) =>
|
|
api.post('/ai-personas/batch-generate-and-save', options, {
|
|
timeout: 600000 // 10 minutes for batch generation and save
|
|
}),
|
|
|
|
// Two-stage generation endpoints
|
|
// First stage: Generate basic profiles
|
|
generateBasicProfiles: (
|
|
audienceBrief: string,
|
|
count: number = 5,
|
|
temperature: number = 0.8
|
|
) =>
|
|
api.post('/ai-personas/generate-basic-profiles', {
|
|
audience_brief: audienceBrief,
|
|
count,
|
|
temperature
|
|
}, {
|
|
timeout: 600000 // 10 minutes for basic profile generation
|
|
}),
|
|
|
|
// Second stage: Complete a single persona without saving
|
|
completePersona: (basicProfile: any, temperature: number = 0.7) =>
|
|
api.post('/ai-personas/complete-persona', {
|
|
basic_profile: basicProfile,
|
|
temperature
|
|
}, {
|
|
timeout: 600000 // 10 minutes for detailed persona generation
|
|
}),
|
|
|
|
// Second stage: Complete a single persona and save to database
|
|
completeAndSavePersona: (
|
|
basicProfile: any,
|
|
temperature: number = 0.7,
|
|
audienceBrief?: string,
|
|
researchObjective?: string,
|
|
customerDataSessionId?: string,
|
|
llmModel?: string
|
|
) =>
|
|
api.post('/ai-personas/complete-and-save-persona', {
|
|
basic_profile: basicProfile,
|
|
temperature,
|
|
audience_brief: audienceBrief,
|
|
research_objective: researchObjective,
|
|
customer_data_session_id: customerDataSessionId,
|
|
llm_model: llmModel
|
|
}, {
|
|
timeout: 600000 // 10 minutes for detailed persona generation and saving
|
|
}),
|
|
|
|
// Generate summary for an existing persona
|
|
generatePersonaSummary: (personaData: any, temperature: number = 0.7) =>
|
|
api.post('/ai-personas/generate-persona-summary', {
|
|
persona_data: personaData,
|
|
temperature
|
|
}, {
|
|
timeout: 600000 // 10 minutes for summary generation
|
|
}),
|
|
|
|
// Helper method for two-stage batch generation and save
|
|
batchGenerateWithStages: async (
|
|
audienceBrief: string,
|
|
researchObjective?: string,
|
|
count: number = 5,
|
|
temperature: number = 0.7,
|
|
customerDataSessionId?: string,
|
|
llmModel?: string
|
|
) => {
|
|
try {
|
|
// Log the API call with model information
|
|
console.log(`📡 API call to generate-basic-profiles with model: ${llmModel || 'gemini-2.5-pro'}`);
|
|
|
|
// First stage: Generate basic profiles
|
|
const basicProfilesResponse = await api.post('/ai-personas/generate-basic-profiles', {
|
|
audience_brief: audienceBrief,
|
|
research_objective: researchObjective,
|
|
count,
|
|
temperature: 0.7, // Use 0.7 temperature for basic profiles
|
|
customer_data_session_id: customerDataSessionId,
|
|
llm_model: llmModel || 'gemini-2.5-pro'
|
|
}, {
|
|
timeout: 600000 // 10 minutes for basic profile generation
|
|
});
|
|
|
|
const basicProfiles = basicProfilesResponse.data.profiles;
|
|
const personas = [];
|
|
const personaIds = [];
|
|
const errors = [];
|
|
|
|
// Log the second stage API calls with model information
|
|
console.log(`📡 API call to complete-and-save-persona with model: ${llmModel || 'gemini-2.5-pro'}`);
|
|
|
|
// Second stage: Complete each profile in parallel
|
|
const completeRequests = basicProfiles.map(profile =>
|
|
api.post('/ai-personas/complete-and-save-persona', {
|
|
basic_profile: profile,
|
|
temperature,
|
|
audience_brief: audienceBrief,
|
|
research_objective: researchObjective,
|
|
customer_data_session_id: customerDataSessionId,
|
|
llm_model: llmModel || 'gemini-2.5-pro'
|
|
}, {
|
|
timeout: 600000 // 10 minutes for each persona completion
|
|
})
|
|
);
|
|
|
|
// Wait for all requests to complete
|
|
const results = await Promise.allSettled(completeRequests);
|
|
|
|
// Process results
|
|
results.forEach((result, index) => {
|
|
if (result.status === 'fulfilled') {
|
|
personas.push(result.value.data.persona);
|
|
personaIds.push(result.value.data.persona_id);
|
|
} else {
|
|
const basicProfile = basicProfiles[index];
|
|
const errorInfo = {
|
|
index,
|
|
name: basicProfile.name || `Persona ${index + 1}`,
|
|
error: result.reason
|
|
};
|
|
errors.push(errorInfo);
|
|
console.error(`Failed to complete persona ${index + 1} (${basicProfile.name || 'unnamed'}):`, result.reason);
|
|
}
|
|
});
|
|
|
|
// Only fail completely if ALL personas failed
|
|
if (personas.length === 0 && errors.length > 0) {
|
|
throw new Error(`Failed to generate any personas. ${errors.length} profile(s) failed.`);
|
|
}
|
|
|
|
return {
|
|
data: {
|
|
message: `Generated and saved ${personas.length} personas${errors.length > 0 ? ` (${errors.length} failed)` : ''}`,
|
|
personas,
|
|
persona_ids: personaIds,
|
|
errors: errors.length > 0 ? errors : undefined,
|
|
partial_success: errors.length > 0 && personas.length > 0
|
|
}
|
|
};
|
|
} catch (error) {
|
|
// For errors in the first stage
|
|
if (error.response?.status === 504 || error.code === "ECONNABORTED") {
|
|
throw new Error(`Timeout error: The server took too long to generate personas. Please try with fewer personas or try again later.`);
|
|
}
|
|
throw error;
|
|
}
|
|
},
|
|
|
|
// Enhance audience brief endpoint
|
|
enhanceAudienceBrief: (audienceBrief: string, researchObjective: string, temperature: number = 0.7) =>
|
|
api.post('/ai-personas/enhance-audience-brief', {
|
|
audience_brief: audienceBrief,
|
|
research_objective: researchObjective,
|
|
temperature
|
|
}, {
|
|
timeout: 600000 // 10 minutes timeout for AI enhancement
|
|
}),
|
|
|
|
// Batch generate summaries for download
|
|
batchGenerateSummaries: (personaIds: string[], temperature: number = 0.7, llmModel?: string) => {
|
|
// Log the API call with model information
|
|
console.log(`📡 Frontend: API call to batch-generate-summaries with model: ${llmModel || 'gemini-2.5-pro'}`);
|
|
|
|
return api.post('/ai-personas/batch-generate-summaries', {
|
|
persona_ids: personaIds,
|
|
temperature,
|
|
llm_model: llmModel || 'gemini-2.5-pro'
|
|
}, {
|
|
timeout: 900000 // 15 minutes timeout for batch processing
|
|
});
|
|
},
|
|
|
|
// Upload customer data files for parsing
|
|
uploadCustomerData: (files: FileList) => {
|
|
const formData = new FormData();
|
|
for (let i = 0; i < files.length; i++) {
|
|
formData.append('files', files[i]);
|
|
}
|
|
|
|
return api.post('/ai-personas/upload-customer-data', formData, {
|
|
headers: {
|
|
'Content-Type': 'multipart/form-data',
|
|
},
|
|
timeout: 300000 // 5 minutes for file upload and parsing
|
|
});
|
|
},
|
|
|
|
// Clean up customer data files for a session
|
|
cleanupCustomerData: (sessionId: string) =>
|
|
api.delete(`/ai-personas/cleanup-customer-data/${sessionId}`)
|
|
};
|
|
|
|
// Focus Groups endpoints
|
|
export const focusGroupsApi = {
|
|
getAll: () =>
|
|
api.get('/focus-groups'),
|
|
|
|
getById: (id: string) =>
|
|
api.get(`/focus-groups/${id}`),
|
|
|
|
create: (focusGroupData: any) =>
|
|
api.post('/focus-groups', focusGroupData),
|
|
|
|
update: (id: string, focusGroupData: any) =>
|
|
api.put(`/focus-groups/${id}`, focusGroupData),
|
|
|
|
delete: (id: string) =>
|
|
api.delete(`/focus-groups/${id}`),
|
|
|
|
addParticipant: (focusGroupId: string, personaId: string) =>
|
|
api.post(`/focus-groups/${focusGroupId}/participants`, { persona_id: personaId }),
|
|
|
|
removeParticipant: (focusGroupId: string, personaId: string) =>
|
|
api.delete(`/focus-groups/${focusGroupId}/participants/${personaId}`),
|
|
|
|
sendMessage: (focusGroupId: string, messageData: any) =>
|
|
api.post(`/focus-groups/${focusGroupId}/messages`, messageData),
|
|
|
|
getMessages: (focusGroupId: string) =>
|
|
api.get(`/focus-groups/${focusGroupId}/messages`),
|
|
|
|
updateMessageHighlight: (focusGroupId: string, messageId: string, highlighted: boolean) =>
|
|
api.patch(`/focus-groups/${focusGroupId}/messages/${messageId}`, { highlighted }),
|
|
|
|
describeAsset: (focusGroupId: string, assetFilename: string) =>
|
|
api.post(`/focus-groups/${focusGroupId}/describe-asset`, { asset_filename: assetFilename }, {
|
|
timeout: 120000 // 2 minutes timeout for AI description generation
|
|
}),
|
|
|
|
generateDiscussionGuide: (data: any) =>
|
|
api.post('/focus-groups/generate-discussion-guide', data, {
|
|
timeout: 600000 // 10 minutes timeout for AI generation
|
|
}),
|
|
|
|
generateDiscussionGuideForGroup: (focusGroupId: string, data: any) =>
|
|
api.post(`/focus-groups/${focusGroupId}/generate-discussion-guide`, data, {
|
|
timeout: 600000 // 10 minutes timeout for AI generation
|
|
}),
|
|
|
|
downloadDiscussionGuide: async (focusGroupId: string) => {
|
|
try {
|
|
const response = await api.get(`/focus-groups/${focusGroupId}/discussion-guide/download`, {
|
|
responseType: 'blob', // Important for file download
|
|
timeout: 30000 // 30 seconds timeout for download
|
|
});
|
|
|
|
// Extract filename from Content-Disposition header
|
|
const contentDisposition = response.headers['content-disposition'];
|
|
let filename = 'discussion-guide.md';
|
|
|
|
if (contentDisposition) {
|
|
const filenameMatch = contentDisposition.match(/filename="([^"]+)"/);
|
|
if (filenameMatch) {
|
|
filename = filenameMatch[1];
|
|
}
|
|
}
|
|
|
|
// Create blob URL and trigger download
|
|
const blob = new Blob([response.data], { type: 'text/markdown' });
|
|
const url = URL.createObjectURL(blob);
|
|
|
|
// Create temporary anchor element for download
|
|
const anchor = document.createElement('a');
|
|
anchor.href = url;
|
|
anchor.download = filename;
|
|
anchor.style.display = 'none';
|
|
|
|
// Trigger download
|
|
document.body.appendChild(anchor);
|
|
anchor.click();
|
|
document.body.removeChild(anchor);
|
|
|
|
// Clean up
|
|
URL.revokeObjectURL(url);
|
|
|
|
return { success: true, filename };
|
|
} catch (error) {
|
|
console.error('Error downloading discussion guide:', error);
|
|
throw new Error('Failed to download discussion guide');
|
|
}
|
|
},
|
|
|
|
// Notes endpoints
|
|
createNote: (focusGroupId: string, noteData: any) =>
|
|
api.post(`/focus-groups/${focusGroupId}/notes`, noteData),
|
|
|
|
getNotes: (focusGroupId: string) =>
|
|
api.get(`/focus-groups/${focusGroupId}/notes`),
|
|
|
|
deleteNote: (focusGroupId: string, noteId: string) =>
|
|
api.delete(`/focus-groups/${focusGroupId}/notes/${noteId}`),
|
|
|
|
// Asset management endpoints
|
|
uploadAssets: (focusGroupId: string, formData: FormData, replace?: boolean) => {
|
|
// Add replace flag to form data if specified
|
|
if (replace === true) {
|
|
formData.append('replace', 'true');
|
|
}
|
|
|
|
return api.post(`/focus-groups/${focusGroupId}/assets`, formData, {
|
|
headers: {
|
|
'Content-Type': 'multipart/form-data'
|
|
},
|
|
timeout: 120000 // 2 minutes for file upload
|
|
});
|
|
},
|
|
|
|
getAssets: (focusGroupId: string) =>
|
|
api.get(`/focus-groups/${focusGroupId}/assets`),
|
|
|
|
getAssetUrl: (focusGroupId: string, filename: string) =>
|
|
`${API_BASE_URL}/focus-groups/${focusGroupId}/assets/${filename}`,
|
|
|
|
updateAssetName: (focusGroupId: string, filename: string, userAssignedName: string) =>
|
|
api.patch(`/focus-groups/${focusGroupId}/assets/${filename}`, {
|
|
user_assigned_name: userAssignedName
|
|
}),
|
|
|
|
deleteAsset: (focusGroupId: string, filename: string) =>
|
|
api.delete(`/focus-groups/${focusGroupId}/assets/${filename}`)
|
|
};
|
|
|
|
// Focus Group AI endpoints
|
|
export const focusGroupAiApi = {
|
|
generateResponse: (
|
|
focusGroupId: string,
|
|
personaId: string,
|
|
currentTopic: string,
|
|
temperature: number = 0.7
|
|
) =>
|
|
api.post('/focus-group-ai/generate-response', {
|
|
focus_group_id: focusGroupId,
|
|
persona_id: personaId,
|
|
current_topic: currentTopic,
|
|
temperature: temperature
|
|
}, {
|
|
timeout: 600000 // 10 minutes for AI response generation
|
|
}),
|
|
|
|
generateKeyThemes: (
|
|
focusGroupId: string,
|
|
temperature: number = 0.7
|
|
) =>
|
|
api.post('/focus-group-ai/generate-key-themes', {
|
|
focus_group_id: focusGroupId,
|
|
temperature: temperature
|
|
}, {
|
|
timeout: 600000 // 10 minutes for key theme generation
|
|
}),
|
|
|
|
getKeyThemes: (focusGroupId: string) =>
|
|
api.get(`/focus-group-ai/key-themes/${focusGroupId}`),
|
|
|
|
deleteKeyTheme: (focusGroupId: string, themeId: string) =>
|
|
api.delete(`/focus-group-ai/key-themes/${focusGroupId}/${themeId}`),
|
|
|
|
// AI Moderator endpoints
|
|
getModeratorStatus: (focusGroupId: string) =>
|
|
api.get(`/focus-group-ai/moderator/status/${focusGroupId}`),
|
|
|
|
advanceModeratorDiscussion: (focusGroupId: string) =>
|
|
api.post(`/focus-group-ai/moderator/advance/${focusGroupId}`, {}, {
|
|
timeout: 600000 // 10 minutes for AI moderator response
|
|
}),
|
|
|
|
setModeratorPosition: (focusGroupId: string, sectionId: string, itemId?: string) =>
|
|
api.put(`/focus-group-ai/moderator/position/${focusGroupId}`, {
|
|
section_id: sectionId,
|
|
item_id: itemId
|
|
}),
|
|
|
|
// Autonomous Conversation endpoints
|
|
startAutonomousConversation: (focusGroupId: string, initialPrompt?: string) =>
|
|
api.post(`/focus-group-ai/autonomous/start/${focusGroupId}`, {
|
|
initial_prompt: initialPrompt
|
|
}, {
|
|
timeout: 600000 // 10 minutes for autonomous conversation startup
|
|
}),
|
|
|
|
stopAutonomousConversation: (focusGroupId: string, reason?: string) =>
|
|
api.post(`/focus-group-ai/autonomous/stop/${focusGroupId}`, {
|
|
reason: reason
|
|
}),
|
|
|
|
getAutonomousConversationStatus: (focusGroupId: string) =>
|
|
api.get(`/focus-group-ai/autonomous/status/${focusGroupId}`),
|
|
|
|
// Conversation State and Analytics endpoints
|
|
getConversationState: (focusGroupId: string) =>
|
|
api.get(`/focus-group-ai/conversation/state/${focusGroupId}`),
|
|
|
|
getConversationAnalytics: (focusGroupId: string) =>
|
|
api.get(`/focus-group-ai/conversation/analytics/${focusGroupId}`),
|
|
|
|
makeConversationDecision: (focusGroupId: string, temperature: number = 0.7, mode: string = 'ai') =>
|
|
api.post(`/focus-group-ai/conversation/decision/${focusGroupId}`, {
|
|
temperature: temperature,
|
|
mode: mode
|
|
}, {
|
|
timeout: 600000 // 10 minutes for LLM decision making
|
|
}),
|
|
|
|
getConversationInsights: (focusGroupId: string) =>
|
|
api.get(`/focus-group-ai/conversation/insights/${focusGroupId}`, {
|
|
timeout: 600000 // 10 minutes for LLM insight generation
|
|
}),
|
|
|
|
manualIntervention: (focusGroupId: string, action: string, message?: string, participantId?: string) =>
|
|
api.post(`/focus-group-ai/conversation/intervene/${focusGroupId}`, {
|
|
action: action,
|
|
message: message,
|
|
participant_id: participantId
|
|
}),
|
|
|
|
getReasoningHistory: (focusGroupId: string) =>
|
|
api.get(`/focus-group-ai/conversation/reasoning-history/${focusGroupId}`),
|
|
|
|
// Session ending endpoint
|
|
endSession: (focusGroupId: string, reason?: string) =>
|
|
api.post(`/focus-group-ai/moderator/end-session/${focusGroupId}`, {
|
|
reason: reason || 'session_ended'
|
|
})
|
|
};
|
|
|
|
// Folders endpoints
|
|
export const foldersApi = {
|
|
getAll: () =>
|
|
api.get('/folders'),
|
|
|
|
getById: (id: string) =>
|
|
api.get(`/folders/${id}`),
|
|
|
|
create: (folderData: any) =>
|
|
api.post('/folders', folderData),
|
|
|
|
update: (id: string, folderData: any) =>
|
|
api.put(`/folders/${id}`, folderData),
|
|
|
|
delete: (id: string) =>
|
|
api.delete(`/folders/${id}`),
|
|
|
|
addPersona: (folderId: string, personaId: string) =>
|
|
api.post(`/folders/${folderId}/personas`, { persona_id: personaId }),
|
|
|
|
removePersona: (folderId: string, personaId: string) =>
|
|
api.delete(`/folders/${folderId}/personas/${personaId}`),
|
|
|
|
addPersonasBatch: (folderId: string, personaIds: string[]) =>
|
|
api.post(`/folders/${folderId}/personas/batch`, { persona_ids: personaIds }),
|
|
|
|
removePersonasBatch: (folderId: string, personaIds: string[]) => {
|
|
console.log(`🌐 API removePersonasBatch: Sending POST to /folders/${folderId}/personas/remove-batch with persona_ids:`, personaIds);
|
|
return api.post(`/folders/${folderId}/personas/remove-batch`, {
|
|
persona_ids: personaIds
|
|
});
|
|
},
|
|
|
|
// New endpoints for multiple folder management
|
|
addPersonaToMultipleFolders: (personaId: string, folderIds: string[]) => {
|
|
// Add persona to multiple folders
|
|
const promises = folderIds.map(folderId =>
|
|
api.post(`/folders/${folderId}/personas`, { persona_id: personaId })
|
|
);
|
|
return Promise.all(promises);
|
|
},
|
|
|
|
removePersonaFromAllFolders: (personaId: string) => {
|
|
// This would require getting all folders first, then removing from each
|
|
// For now, we'll handle this on a per-folder basis in the UI
|
|
throw new Error("Use removePersona for specific folders");
|
|
}
|
|
};
|
|
|
|
export default api; |