"use client"; import Link from "next/link"; import { useMemo, useState } from "react"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader } from "@/components/ui/card"; import { Label } from "@/components/ui/label"; import { CONTRACT_TYPE_OPTIONS, ORGANIZATION_STRUCTURE_OPTIONS, STRATEGIC_EVIDENCE_CATEGORY_OPTIONS, TEAM_AVAILABILITY_OPTIONS, type StrategicDiagnosticData, type StrategicDiagnosticScores, type StrategicEvidenceDocumentView, type StrategicSectionKey, } from "@/lib/strategic-diagnostic/types"; type StrategicDiagnosticWizardProps = { initialData: StrategicDiagnosticData; initialScores: StrategicDiagnosticScores; initialEvidenceBySection: Record; initialCompletedAt: string | null; }; type TabKey = StrategicSectionKey | "results"; type StrategicAiInsights = { sectionGaps: { sectionKey: StrategicSectionKey; gap: string; impact: string; urgency: "alta" | "media" | "baja"; }[]; priorityActions: { title: string; description: string; priority: "alta" | "media" | "baja"; ownerSuggestion: string; targetDateSuggestion: string; }[]; suggestedEvidence: { sectionKey: StrategicSectionKey; category: string; reason: string; }[]; suggestedFieldValues: { sectionKey: StrategicSectionKey; fieldPath: string; suggestedValue: string; rationale: string; }[]; confidence: "low" | "medium" | "high"; }; const tabItems: { key: TabKey; label: string }[] = [ { key: "technical", label: "Capacidades Tecnicas" }, { key: "experience", label: "Experiencia" }, { key: "organization", label: "Organizacion" }, { key: "publicProcurement", label: "Contratacion Publica" }, { key: "results", label: "Resultados" }, ]; function getKnowledgeLabel(level: number) { if (level <= 1) { return "Muy bajo (1/5)"; } if (level === 2) { return "Bajo (2/5)"; } if (level === 3) { return "Medio (3/5)"; } if (level === 4) { return "Alto (4/5)"; } return "Muy alto (5/5)"; } function ToggleCard({ title, description, checked, onChange, }: { title: string; description?: string; checked: boolean; onChange: (checked: boolean) => void; }) { return (

{title}

{description ?

{description}

: null}
); } function Chip({ label, removable, onRemove, selected, onClick, }: { label: string; removable?: boolean; onRemove?: () => void; selected?: boolean; onClick?: () => void; }) { return ( {removable ? ( ) : null} ); } function SectionScoreBar({ title, subtitle, score }: { title: string; subtitle: string; score: number }) { return (

{title}

{Math.round(score)}%

{subtitle}

); } function setNestedValue(target: unknown, pathSegments: string[], value: string): unknown { if (!pathSegments.length || !target || typeof target !== "object" || Array.isArray(target)) { return target; } const [head, ...tail] = pathSegments; const record = target as Record; if (!(head in record)) { return target; } if (tail.length === 0) { if (typeof record[head] !== "string") { return target; } return { ...record, [head]: value, }; } const nextValue = setNestedValue(record[head], tail, value); if (nextValue === record[head]) { return target; } return { ...record, [head]: nextValue, }; } export function StrategicDiagnosticWizard({ initialData, initialScores, initialEvidenceBySection, initialCompletedAt, }: StrategicDiagnosticWizardProps) { const [activeTab, setActiveTab] = useState("technical"); const [data, setData] = useState(initialData); const [scores, setScores] = useState(initialScores); const [evidenceBySection, setEvidenceBySection] = useState(initialEvidenceBySection); const [completedAt, setCompletedAt] = useState(initialCompletedAt); const [isSaving, setIsSaving] = useState(false); const [savingSection, setSavingSection] = useState(null); const [uploadingSection, setUploadingSection] = useState(null); const [errorMessage, setErrorMessage] = useState(null); const [successMessage, setSuccessMessage] = useState(null); const [coverageInput, setCoverageInput] = useState(""); const [certificationInput, setCertificationInput] = useState(""); const [sectorsServedInput, setSectorsServedInput] = useState(""); const [selectedEvidenceCategory, setSelectedEvidenceCategory] = useState>({ technical: STRATEGIC_EVIDENCE_CATEGORY_OPTIONS.technical[0] ?? "Otro", experience: STRATEGIC_EVIDENCE_CATEGORY_OPTIONS.experience[0] ?? "Otro", organization: STRATEGIC_EVIDENCE_CATEGORY_OPTIONS.organization[0] ?? "Otro", publicProcurement: STRATEGIC_EVIDENCE_CATEGORY_OPTIONS.publicProcurement[0] ?? "Otro", }); const [aiInsights, setAiInsights] = useState(null); const [aiSuggestionId, setAiSuggestionId] = useState(null); const [isLoadingAiInsights, setIsLoadingAiInsights] = useState(false); const [isApplyingAiField, setIsApplyingAiField] = useState(null); const progressPercent = useMemo(() => { return Math.round((scores.completedSections / scores.totalSections) * 100); }, [scores.completedSections, scores.totalSections]); function clearFeedback() { setErrorMessage(null); setSuccessMessage(null); } function updateSection( section: K, updater: (value: StrategicDiagnosticData[K]) => StrategicDiagnosticData[K], ) { setData((previous) => ({ ...previous, [section]: updater(previous[section]), })); } function nextTab(current: TabKey) { const index = tabItems.findIndex((item) => item.key === current); return tabItems[index + 1]?.key ?? "results"; } async function setAiDecision(decision: "accept" | "dismiss") { if (!aiSuggestionId) { return; } await fetch(`/api/ai/suggestions/${encodeURIComponent(aiSuggestionId)}/decision`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ decision }), }); } async function persistData(section: TabKey, forceCompleted = false) { setIsSaving(true); setSavingSection(section); clearFeedback(); try { const response = await fetch("/api/strategic-diagnostic", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ data, forceCompleted, }), }); const payload = (await response.json().catch(() => ({}))) as { ok?: boolean; error?: string; payload?: { data: StrategicDiagnosticData; scores: StrategicDiagnosticScores; evidenceBySection: Record; completedAt: string | null; }; }; if (!response.ok || !payload.ok || !payload.payload) { setErrorMessage(payload.error ?? "No se pudo guardar esta seccion."); return false; } setData(payload.payload.data); setScores(payload.payload.scores); setEvidenceBySection(payload.payload.evidenceBySection); setCompletedAt(payload.payload.completedAt); setSuccessMessage("Seccion guardada correctamente."); return true; } catch { setErrorMessage("No se pudo guardar esta seccion."); return false; } finally { setIsSaving(false); setSavingSection(null); } } async function saveAndContinue(section: TabKey, forceCompleted = false) { const saved = await persistData(section, forceCompleted); if (saved) { setActiveTab(nextTab(section)); } } async function uploadEvidence(section: StrategicSectionKey, file: File) { setUploadingSection(section); clearFeedback(); try { const formData = new FormData(); formData.append("file", file); formData.append("section", section); formData.append("category", selectedEvidenceCategory[section]); const response = await fetch("/api/strategic-diagnostic/evidence", { method: "POST", body: formData, }); const payload = (await response.json().catch(() => ({}))) as { ok?: boolean; error?: string; payload?: { data: StrategicDiagnosticData; scores: StrategicDiagnosticScores; evidenceBySection: Record; completedAt: string | null; }; }; if (!response.ok || !payload.ok || !payload.payload) { setErrorMessage(payload.error ?? "No se pudo subir la evidencia."); return; } setData(payload.payload.data); setScores(payload.payload.scores); setEvidenceBySection(payload.payload.evidenceBySection); setCompletedAt(payload.payload.completedAt); setSuccessMessage("Evidencia subida correctamente."); } catch { setErrorMessage("No se pudo subir la evidencia."); } finally { setUploadingSection(null); } } async function generateAiInsights() { setIsLoadingAiInsights(true); clearFeedback(); try { const response = await fetch("/api/strategic-diagnostic/ai/insights", { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ data, evidenceMetadata: { technical: { count: evidenceBySection.technical.length, categories: Array.from(new Set(evidenceBySection.technical.map((item) => item.category))), }, experience: { count: evidenceBySection.experience.length, categories: Array.from(new Set(evidenceBySection.experience.map((item) => item.category))), }, organization: { count: evidenceBySection.organization.length, categories: Array.from(new Set(evidenceBySection.organization.map((item) => item.category))), }, publicProcurement: { count: evidenceBySection.publicProcurement.length, categories: Array.from(new Set(evidenceBySection.publicProcurement.map((item) => item.category))), }, }, }), }); const payload = (await response.json().catch(() => ({}))) as { ok?: boolean; error?: string; sectionGaps?: StrategicAiInsights["sectionGaps"]; priorityActions?: StrategicAiInsights["priorityActions"]; suggestedEvidence?: StrategicAiInsights["suggestedEvidence"]; suggestedFieldValues?: StrategicAiInsights["suggestedFieldValues"]; confidence?: StrategicAiInsights["confidence"]; suggestionId?: string; }; if (!response.ok || !payload.ok) { setErrorMessage(payload.error ?? "No fue posible generar plan IA."); return; } setAiInsights({ sectionGaps: payload.sectionGaps ?? [], priorityActions: payload.priorityActions ?? [], suggestedEvidence: payload.suggestedEvidence ?? [], suggestedFieldValues: payload.suggestedFieldValues ?? [], confidence: payload.confidence ?? "low", }); setAiSuggestionId(payload.suggestionId ?? null); setSuccessMessage("Plan sugerido por IA generado."); } catch { setErrorMessage("No fue posible generar plan IA."); } finally { setIsLoadingAiInsights(false); } } async function applySuggestedFieldValue(fieldPath: string, value: string) { const segments = fieldPath.split(".").map((segment) => segment.trim()).filter(Boolean); if (!segments.length) { return; } setIsApplyingAiField(fieldPath); clearFeedback(); try { let applied = false; setData((previous) => { const nextValue = setNestedValue(previous, segments, value); applied = nextValue !== previous; return (nextValue as StrategicDiagnosticData) ?? previous; }); if (!applied) { setErrorMessage("Este campo no acepta aplicacion directa desde IA. Aplica manualmente."); return; } await setAiDecision("accept"); setSuccessMessage(`Valor sugerido aplicado: ${fieldPath}.`); } catch { setErrorMessage("No fue posible aplicar el valor sugerido."); } finally { setIsApplyingAiField(null); } } function addListItem(section: "technical" | "experience", field: "coverageRegions" | "certifications" | "sectorsServed", rawValue: string) { const value = rawValue.trim(); if (!value) { return; } if (section === "technical") { updateSection("technical", (previous) => { const current = previous[field as "coverageRegions" | "certifications"]; return current.includes(value) ? previous : { ...previous, [field]: [...current, value] }; }); return; } updateSection("experience", (previous) => { const current = previous[field as "sectorsServed"]; return current.includes(value) ? previous : { ...previous, sectorsServed: [...current, value] }; }); } return (

Progreso del diagnostico

{scores.completedSections} de {scores.totalSections} secciones completadas

{tabItems.map((tab) => ( ))}
{activeTab === "technical" ? (

Productos y Servicios

Describe detalladamente que ofrece tu empresa