Files
vibn-frontend/components/ai/extraction-review-checklist.tsx

130 lines
4.1 KiB
TypeScript

"use client";
import { CheckCircle2, Circle } from "lucide-react";
import { useEffect, useState } from "react";
import { db } from "@/lib/firebase/config";
import { doc, onSnapshot } from "firebase/firestore";
interface ExtractionReviewChecklistProps {
projectId: string;
className?: string;
}
export function ExtractionReviewChecklist({ projectId, className }: ExtractionReviewChecklistProps) {
const [hasProblems, setHasProblems] = useState(false);
const [hasUsers, setHasUsers] = useState(false);
const [hasFeatures, setHasFeatures] = useState(false);
const [reviewedWithAI, setReviewedWithAI] = useState(false);
const [readyForVision, setReadyForVision] = useState(false);
useEffect(() => {
if (!projectId) return;
const unsubscribe = onSnapshot(
doc(db, "projects", projectId),
(snapshot) => {
if (snapshot.exists()) {
const data = snapshot.data();
const extraction = data?.phaseData?.phaseHandoffs?.extraction;
if (extraction) {
// Check if we have key data
setHasProblems((extraction.confirmed?.problems?.length || 0) > 0);
setHasUsers((extraction.confirmed?.targetUsers?.length || 0) > 0);
setHasFeatures((extraction.confirmed?.features?.length || 0) > 0);
setReadyForVision(extraction.readyForNextPhase || false);
}
// Check if user has reviewed with AI (simplified: check if there are any messages in chat)
// This is a placeholder - you might track this differently
setReviewedWithAI(data?.lastChatAt ? true : false);
}
}
);
return () => unsubscribe();
}, [projectId]);
const checklist = [
{
id: "problems",
label: "Problems identified",
checked: hasProblems,
},
{
id: "users",
label: "Target users defined",
checked: hasUsers,
},
{
id: "features",
label: "Key features extracted",
checked: hasFeatures,
},
{
id: "reviewed",
label: "Reviewed with AI",
checked: reviewedWithAI,
},
];
const completedCount = checklist.filter((item) => item.checked).length;
const progress = (completedCount / checklist.length) * 100;
const isCompleted = completedCount === checklist.length || readyForVision;
return (
<div className={className}>
{/* Header */}
<div className="mb-3">
<h3 className="text-sm font-semibold">
{isCompleted ? '✓ Review Complete' : 'Extraction Review'}
</h3>
<div className="mt-2">
<div className="w-full bg-secondary h-2 rounded-full overflow-hidden">
<div
className="h-full bg-primary transition-all duration-300"
style={{ width: `${isCompleted ? 100 : progress}%` }}
/>
</div>
<p className="text-xs text-muted-foreground mt-1">
{isCompleted ? `${checklist.length} of ${checklist.length} complete` : `${completedCount} of ${checklist.length} complete`}
</p>
</div>
</div>
{/* Checklist Items */}
<div className="space-y-2">
{checklist.map((item) => (
<div
key={item.id}
className={`flex items-center gap-2 py-2 px-3 rounded-md border ${
(item.checked || isCompleted)
? "bg-green-50 border-green-200"
: "bg-muted/50 border-muted text-muted-foreground opacity-60"
}`}
>
{(item.checked || isCompleted) ? (
<CheckCircle2 className="h-4 w-4 text-green-600 flex-shrink-0" />
) : (
<Circle className="h-4 w-4 text-muted-foreground flex-shrink-0" />
)}
<div className="flex-1 min-w-0">
<p className="text-xs font-medium">{item.label}</p>
</div>
</div>
))}
</div>
{/* Ready indicator */}
{readyForVision && (
<div className="mt-4 pt-4 border-t">
<p className="text-xs text-green-600 font-medium">
Ready for Vision phase
</p>
</div>
)}
</div>
);
}