arena-learning / studyArena /components /generation /generating-progress.tsx
NitishStark's picture
Upload folder using huggingface_hub
c20f20c verified
'use client';
import { useEffect, useState } from 'react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Loader2, CheckCircle2, XCircle, Circle } from 'lucide-react';
import { useI18n } from '@/lib/hooks/use-i18n';
interface GeneratingProgressProps {
outlineReady: boolean; // Is outline generation complete?
firstPageReady: boolean; // Is first page generated?
statusMessage: string;
error?: string | null;
}
// Status item component - declared outside main component
function StatusItem({
completed,
inProgress,
hasError,
label,
}: {
completed: boolean;
inProgress: boolean;
hasError: boolean;
label: string;
}) {
return (
<div className="flex items-center gap-3 py-3">
<div className="flex-shrink-0">
{hasError ? (
<XCircle className="size-6 text-destructive" />
) : completed ? (
<CheckCircle2 className="size-6 text-green-500" />
) : inProgress ? (
<Loader2 className="size-6 text-primary animate-spin" />
) : (
<Circle className="size-6 text-muted-foreground" />
)}
</div>
<span
className={`text-base ${
hasError
? 'text-destructive'
: completed
? 'text-green-600 font-medium'
: inProgress
? 'text-primary font-medium'
: 'text-muted-foreground'
}`}
>
{label}
</span>
</div>
);
}
export function GeneratingProgress({
outlineReady,
firstPageReady,
statusMessage,
error,
}: GeneratingProgressProps) {
const { t } = useI18n();
const [dots, setDots] = useState('');
// Animated dots for loading state
useEffect(() => {
if (!error && !firstPageReady) {
const interval = setInterval(() => {
setDots((prev) => (prev.length >= 3 ? '' : prev + '.'));
}, 500);
return () => clearInterval(interval);
}
}, [error, firstPageReady]);
return (
<div className="space-y-6">
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
{error ? (
<>
<XCircle className="size-5 text-destructive" />
{t('generation.generationFailed')}
</>
) : firstPageReady ? (
<>
<CheckCircle2 className="size-5 text-green-500" />
{t('generation.openingClassroom')}
</>
) : (
<>
<Loader2 className="size-5 animate-spin" />
{t('generation.generatingCourse')}
{dots}
</>
)}
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
{/* Two milestone status items */}
<div className="divide-y">
<StatusItem
completed={outlineReady}
inProgress={!outlineReady && !error}
hasError={!outlineReady && !!error}
label={
outlineReady ? t('generation.outlineReady') : t('generation.generatingOutlines')
}
/>
<StatusItem
completed={firstPageReady}
inProgress={outlineReady && !firstPageReady && !error}
hasError={outlineReady && !firstPageReady && !!error}
label={
firstPageReady
? t('generation.firstPageReady')
: t('generation.generatingFirstPage')
}
/>
</div>
{/* Status message */}
{statusMessage && !error && (
<div className="pt-2 border-t">
<p className="text-sm text-muted-foreground">{statusMessage}</p>
</div>
)}
{/* Error message */}
{error && (
<div className="p-4 bg-destructive/10 border border-destructive/20 rounded-lg">
<p className="text-sm text-destructive">{error}</p>
</div>
)}
</CardContent>
</Card>
</div>
);
}