155 lines
No EOL
5.2 KiB
TypeScript
155 lines
No EOL
5.2 KiB
TypeScript
import React, { useState, useRef, useCallback } from 'react';
|
|
import {
|
|
ChevronDown,
|
|
ChevronUp,
|
|
ClipboardList,
|
|
Download,
|
|
Loader2
|
|
} from 'lucide-react';
|
|
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible';
|
|
import { Card, CardContent } from '@/components/ui/card';
|
|
import { Button } from '@/components/ui/button';
|
|
import DiscussionGuideViewer from './DiscussionGuideViewer';
|
|
import { cn } from '@/lib/utils';
|
|
import { toast } from 'sonner';
|
|
import { focusGroupsApi } from '@/lib/api';
|
|
|
|
interface CollapsibleDiscussionGuideProps {
|
|
discussionGuide: any;
|
|
moderatorStatus?: any;
|
|
onSectionSelect?: (sectionId: string, itemId?: string) => void;
|
|
onSetPosition?: (sectionId: string, itemId: string, content: string, sectionTitle: string, itemTitle?: string, itemType?: string) => void;
|
|
onSave?: (updatedGuide: any) => void;
|
|
focusGroupId: string;
|
|
isOpen: boolean;
|
|
onToggle: () => void;
|
|
className?: string;
|
|
onEditingChange?: (isEditing: boolean) => void;
|
|
}
|
|
|
|
const CollapsibleDiscussionGuide: React.FC<CollapsibleDiscussionGuideProps> = ({
|
|
discussionGuide,
|
|
moderatorStatus,
|
|
onSectionSelect,
|
|
onSetPosition,
|
|
onSave,
|
|
focusGroupId,
|
|
isOpen,
|
|
onToggle,
|
|
className,
|
|
onEditingChange
|
|
}) => {
|
|
|
|
// Track editing state to prevent re-renders
|
|
const isEditingRef = useRef(false);
|
|
|
|
// Wrapper for onEditingChange to track editing state
|
|
const handleEditingChange = useCallback((editing: boolean) => {
|
|
isEditingRef.current = editing;
|
|
onEditingChange?.(editing);
|
|
}, [onEditingChange]);
|
|
|
|
// Download state
|
|
const [isDownloadingGuide, setIsDownloadingGuide] = useState(false);
|
|
|
|
// Function to download discussion guide
|
|
const handleDownloadDiscussionGuide = async () => {
|
|
if (!discussionGuide) {
|
|
toast.error("No discussion guide available", {
|
|
description: "The discussion guide is not available for download"
|
|
});
|
|
return;
|
|
}
|
|
|
|
setIsDownloadingGuide(true);
|
|
|
|
try {
|
|
await focusGroupsApi.downloadDiscussionGuide(focusGroupId);
|
|
|
|
toast.success("Discussion guide downloaded", {
|
|
description: "The guide has been saved to your downloads folder"
|
|
});
|
|
} catch (error) {
|
|
console.error('Error downloading discussion guide:', error);
|
|
toast.error("Download failed", {
|
|
description: "Unable to download the discussion guide. Please try again."
|
|
});
|
|
} finally {
|
|
setIsDownloadingGuide(false);
|
|
}
|
|
};
|
|
|
|
// Check if we have a valid structured discussion guide
|
|
const hasStructuredGuide = discussionGuide && typeof discussionGuide === 'object' && discussionGuide.sections;
|
|
|
|
return (
|
|
<div className={cn("w-full border-b bg-white shadow-sm", className)}>
|
|
<Collapsible open={isOpen} onOpenChange={onToggle}>
|
|
<CollapsibleTrigger asChild>
|
|
<div className="w-full px-4 py-3 flex items-center justify-between hover:bg-slate-50 transition-colors cursor-pointer">
|
|
<div className="flex items-center gap-3">
|
|
<ClipboardList className="h-5 w-5 text-slate-600" />
|
|
<div>
|
|
<h2 className="font-semibold text-slate-900">Discussion Guide</h2>
|
|
{hasStructuredGuide && (
|
|
<p className="text-xs text-slate-500">
|
|
{discussionGuide.title} • {discussionGuide.total_duration} minutes
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-2">
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
handleDownloadDiscussionGuide();
|
|
}}
|
|
disabled={!discussionGuide || isDownloadingGuide}
|
|
className="h-8"
|
|
>
|
|
{isDownloadingGuide ? (
|
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
) : (
|
|
<Download className="h-4 w-4" />
|
|
)}
|
|
</Button>
|
|
{isOpen ? (
|
|
<ChevronUp className="h-4 w-4 text-slate-500" />
|
|
) : (
|
|
<ChevronDown className="h-4 w-4 text-slate-500" />
|
|
)}
|
|
</div>
|
|
</div>
|
|
</CollapsibleTrigger>
|
|
|
|
<CollapsibleContent>
|
|
<div className="border-t bg-slate-50">
|
|
<Card className="mx-4 mb-4 mt-2">
|
|
<CardContent className="p-4">
|
|
<div className="max-h-[70vh] overflow-y-auto">
|
|
<DiscussionGuideViewer
|
|
discussionGuide={discussionGuide}
|
|
moderatorStatus={moderatorStatus}
|
|
onSectionSelect={onSectionSelect}
|
|
onSetPosition={onSetPosition}
|
|
onSave={onSave}
|
|
showProgress={true}
|
|
collapsible={true}
|
|
defaultExpanded={true}
|
|
focusGroupId={focusGroupId}
|
|
onEditingChange={handleEditingChange}
|
|
/>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</CollapsibleContent>
|
|
</Collapsible>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default CollapsibleDiscussionGuide; |