import React, { useState, useEffect, useRef } from 'react'; import { useNavigate, useLocation } from 'react-router-dom'; import Navigation from '@/components/Navigation'; import FocusGroupModerator from '@/components/FocusGroupModerator'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Checkbox } from '@/components/ui/checkbox'; import { Search, Filter, MessageSquare, Users, Calendar, PlayCircle, ChevronRight, Clock, Loader2, Trash2 } from 'lucide-react'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; import { cn } from '@/lib/utils'; import { toastService } from '@/lib/toast'; import { focusGroupsApi } from '@/lib/api'; // Sample focus group data for fallback const sampleFocusGroups = [ { id: '1', name: 'Mobile App UX Evaluation', status: 'completed', participants: 6, date: '2023-06-10T14:00:00Z', duration: 60, topic: 'user-experience', }, { id: '2', name: 'Product Feature Feedback', status: 'scheduled', participants: 8, date: '2023-06-15T10:00:00Z', duration: 90, topic: 'product-feedback', }, { id: '3', name: 'Marketing Campaign Testing', status: 'in-progress', participants: 5, date: '2023-06-12T15:30:00Z', duration: 45, topic: 'creative-testing', }, { id: '4', name: 'Website Navigation Study', status: 'scheduled', participants: 7, date: '2023-06-18T13:00:00Z', duration: 60, topic: 'user-experience', }, ]; const statusColors = { 'completed': 'bg-green-100 text-green-800 border-green-200', 'scheduled': 'bg-blue-100 text-blue-800 border-blue-200', 'in-progress': 'bg-amber-100 text-amber-800 border-amber-200', 'active': 'bg-amber-100 text-amber-800 border-amber-200', 'paused': 'bg-purple-100 text-purple-800 border-purple-200', 'new': 'bg-slate-100 text-slate-800 border-slate-200', 'ai_mode': 'bg-amber-100 text-amber-800 border-amber-200', 'draft': 'bg-gray-100 text-gray-800 border-gray-200', }; interface FocusGroup { id?: string; _id?: string; name: string; status: string; participants?: string[] | any[]; participants_count?: number; date: string; duration: number; topic: string; discussionGuide?: string; created_at?: string; created_by?: string; updated_at?: string; } const FocusGroups = () => { console.log('FocusGroups component rendering'); const [mode, setMode] = useState<'view' | 'create'>('view'); const [searchTerm, setSearchTerm] = useState(''); const [focusGroups, setFocusGroups] = useState([]); const [isLoading, setIsLoading] = useState(true); const [selectedGroups, setSelectedGroups] = useState([]); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [isDeletingGroups, setIsDeletingGroups] = useState(false); const [draftToEdit, setDraftToEdit] = useState(null); const navigate = useNavigate(); const location = useLocation(); const [preSelectedParticipants, setPreSelectedParticipants] = useState([]); // Use a ref to track component mounted state const isMounted = useRef(true); // Create a function to fetch focus groups that can be called when needed const fetchFocusGroups = async (isMountedCheck = true) => { console.log('fetchFocusGroups called with isMountedCheck:', isMountedCheck); console.log('isMounted.current:', isMounted.current); // Skip if component is not mounted (when called from useEffect cleanup) if (isMountedCheck && !isMounted.current) { console.log('Exiting early: component not mounted'); return; } console.log('Setting loading to true and making API call'); setIsLoading(true); try { console.log('Calling focusGroupsApi.getAll()'); const response = await focusGroupsApi.getAll(); console.log('API response received:', response); if (!isMountedCheck || isMounted.current) { // Process the data to ensure consistent format const processedGroups = response.data.map((group: FocusGroup) => ({ ...group, // Ensure id is set for compatibility id: group.id || group._id, // Fix participants display participants_count: Array.isArray(group.participants) ? group.participants.length : (typeof group.participants === 'number' ? group.participants : 0) })); setFocusGroups(processedGroups); } } catch (error) { console.error("Error fetching focus groups:", error); if (!isMountedCheck || isMounted.current) { toastService.error("Failed to load focus groups"); // Fallback to sample data setFocusGroups(sampleFocusGroups); } } finally { if (!isMountedCheck || isMounted.current) { setIsLoading(false); } } }; // Fetch a specific focus group for editing const fetchFocusGroupForEdit = async (focusGroupId: string) => { try { const response = await focusGroupsApi.getById(focusGroupId); if (response && response.data) { setDraftToEdit(response.data); setMode('create'); // Use create mode for editing } } catch (error) { console.error('Error fetching focus group for edit:', error); toastService.error("Failed to load focus group for editing"); } }; useEffect(() => { console.log('useEffect running - about to fetch focus groups'); // Fetch focus groups on initial load fetchFocusGroups(); // Cleanup function to prevent memory leaks return () => { console.log('useEffect cleanup - setting isMounted to false'); isMounted.current = false; }; }, []); // Refetch when mode changes from create back to view useEffect(() => { console.log('Mode change useEffect running, mode:', mode); if (mode === 'view') { console.log('Mode is view, calling fetchFocusGroups'); fetchFocusGroups(); } }, [mode]); // Handle navigation state for pre-selected participants useEffect(() => { const state = location.state as { mode?: string; preSelectedParticipants?: string[] } | null; if (state?.mode === 'create' && state?.preSelectedParticipants) { setPreSelectedParticipants(state.preSelectedParticipants); setMode('create'); // Clear the navigation state to prevent re-triggering navigate(location.pathname, { replace: true, state: null }); } }, [location.state, location.pathname, navigate]); // Handle URL query parameters for navigation from persona details useEffect(() => { const searchParams = new URLSearchParams(location.search); const urlMode = searchParams.get('mode'); const focusGroupId = searchParams.get('id'); const tab = searchParams.get('tab'); if (urlMode === 'create') { // For new focus group creation, switch to create mode setMode('create'); setDraftToEdit(null); } else if (urlMode === 'edit' && focusGroupId) { // For editing existing focus group, find and load the draft const foundGroup = focusGroups.find(group => (group._id || group.id) === focusGroupId ); if (foundGroup) { setDraftToEdit(foundGroup); setMode('create'); // Use create mode for editing } else { // If group not found, try to fetch it from API fetchFocusGroupForEdit(focusGroupId); } } // Clear URL parameters after processing to avoid re-triggering if (urlMode || focusGroupId || tab) { const newUrl = location.pathname; navigate(newUrl, { replace: true }); } }, [location.search, focusGroups, navigate, location.pathname]); const filteredGroups = focusGroups.filter(group => group.name.toLowerCase().includes(searchTerm.toLowerCase()) || group.topic.toLowerCase().includes(searchTerm.toLowerCase()) ); const formatDate = (dateString: string) => { const date = new Date(dateString); return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); }; const formatTime = (dateString: string) => { const date = new Date(dateString); return date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit', hour12: true }); }; // Handle checkbox selection const toggleGroupSelection = (groupId: string) => { setSelectedGroups(current => { if (current.includes(groupId)) { return current.filter(id => id !== groupId); } else { return [...current, groupId]; } }); }; // Delete selected focus groups const deleteSelectedGroups = async () => { if (selectedGroups.length === 0) return; setIsDeletingGroups(true); try { // Delete each selected focus group const deletePromises = selectedGroups.map(groupId => focusGroupsApi.delete(groupId) ); await Promise.all(deletePromises); // Update the UI by removing the deleted groups setFocusGroups(current => current.filter(group => !selectedGroups.includes(group.id || group._id || '')) ); // Reset selection setSelectedGroups([]); toastService.success(`${selectedGroups.length} focus group${selectedGroups.length > 1 ? 's' : ''} deleted successfully`); } catch (error) { console.error("Error deleting focus groups:", error); toastService.error("Failed to delete focus groups"); } finally { setIsDeletingGroups(false); setDeleteDialogOpen(false); } }; return (

Focus Groups

Set up and manage AI-moderated research sessions

{mode === 'view' ? ( <>
setSearchTerm(e.target.value)} />

Your Focus Groups

{selectedGroups.length > 0 && ( )}
{isLoading ? (
) : filteredGroups.length > 0 ? (
{filteredGroups.map((group) => (
toggleGroupSelection(group.id || group._id || '')} className="mt-1" />

{group.name}

{formatDate(group.date)}
{formatTime(group.date)}
{/* Use participants_count if available, otherwise calculate from participants array */} {group.participants_count || (Array.isArray(group.participants) ? group.participants.length : 0)} participant{ (group.participants_count > 1 || (Array.isArray(group.participants) && group.participants.length > 1)) ? 's' : ''}
{group.duration} min
{group.status === 'completed' && 'Completed'} {group.status === 'scheduled' && 'Scheduled'} {group.status === 'in-progress' && 'In Progress'} {group.status === 'active' && 'In Progress'} {group.status === 'ai_mode' && 'In Progress'} {group.status === 'paused' && 'Paused'} {group.status === 'new' && 'Not Started'} {group.status === 'draft' && 'Draft'} {!['completed', 'scheduled', 'in-progress', 'active', 'ai_mode', 'paused', 'new', 'draft'].includes(group.status) && group.status}
{group.topic === 'user-experience' && 'User Experience'} {group.topic === 'product-feedback' && 'Product Feedback'} {group.topic === 'creative-testing' && 'Creative Testing'} {group.topic === 'messaging-evaluation' && 'Messaging Evaluation'} {group.topic && !['user-experience', 'product-feedback', 'creative-testing', 'messaging-evaluation'].includes(group.topic) && group.topic.charAt(0).toUpperCase() + group.topic.slice(1).replace(/-/g, ' ')}
AI Moderated
))}
) : (

No focus groups found matching your search criteria.

)}
) : ( { setDraftToEdit(null); setMode('view'); setPreSelectedParticipants([]); fetchFocusGroups(); }} /> )}
{/* Confirmation Dialog for Delete */} Delete {selectedGroups.length} Focus Group{selectedGroups.length !== 1 ? 's' : ''}? This action cannot be undone. This will permanently delete the selected focus group{selectedGroups.length !== 1 ? 's' : ''} and remove all data associated with {selectedGroups.length !== 1 ? 'them' : 'it'}. Cancel { e.preventDefault(); deleteSelectedGroups(); }} disabled={isDeletingGroups} className="bg-destructive text-destructive-foreground hover:bg-destructive/90" > {isDeletingGroups ? ( <> Deleting... ) : ( <>Delete )}
); }; export default FocusGroups;