diff --git a/network-poc/frontend/src/pages/index.astro b/network-poc/frontend/src/pages/index.astro index 13e9d57..824a20f 100644 --- a/network-poc/frontend/src/pages/index.astro +++ b/network-poc/frontend/src/pages/index.astro @@ -239,6 +239,15 @@ OUTPUT FORMAT: const config = document.getElementById('agent-config'); if (!selectedAgent) { config.style.display = 'none'; return; } + // Tarkkailija-klikkaus: avaa raportti modalina jos se on generoitu + if (key === 'observer' && window._lastReport) { + config.style.display = 'none'; + showReportModal(window._lastReport); + selectedAgent = null; + renderAgentBar(); + return; + } + const a = agents[key]; config.style.display = 'block'; document.getElementById('config-avatar').src = a.avatar; @@ -830,25 +839,42 @@ OUTPUT FORMAT: if (dockerfile) files['Dockerfile'] = dockerfile; stepN++; - // Tarkkailija: yhteenveto + raportti + // Tarkkailija: yhteenveto + raportti + arvosana const obs = agents.observer || Object.values(agents)[5]; if (obs) { termLog(`\n[${stepN}] ${esc(obs.name)} — projektin yhteenveto`); highlightAgent('observer'); - explainStep('Raportti', `${obs.name} kokoaa yhteenvedon projektin rakenteesta, riskeistä ja käyttöönotosta.`); + explainStep('Raportti', `${obs.name} kokoaa yhteenvedon ja antaa arvosanan.`); const finalCode = Object.entries(files).map(([n,c]) => `--- ${n} ---\n${c}`).join('\n\n'); const obsPrompt = (obs.prompt ? obs.prompt+'\n\n' : '') + - `Write a project README.md report in markdown for this project: ${task}\n\n` + - `Include these sections:\n` + + `Write a project README.md report in markdown for: ${task}\n\n` + + `IMPORTANT: Start the FIRST LINE with exactly one of these verdicts:\n` + + `VERDICT: GREEN — project is production-ready, no issues\n` + + `VERDICT: ORANGE — project works but has warnings or improvements needed\n` + + `VERDICT: RED — project has critical issues that must be fixed\n\n` + + `Then include:\n` + `# Project: ${task}\n` + - `## Files (list each file and its purpose)\n` + - `## How to run (uv + Docker commands)\n` + - `## API Endpoints (table: method, path, description)\n` + - `## Architecture notes\n` + - `## Risk assessment (security, reliability)\n\n` + + `## Files\n## How to run\n## API Endpoints\n## Architecture\n## Risk assessment\n\n` + `Project code:\n${finalCode}`; const readme = await kpnRun(obs.model, obsPrompt); - if (readme) files['README.md'] = readme; + if (readme) { + files['README.md'] = readme; + // Tallennetaan raportti globaalisti jotta tarkkailija-klikkaus avaa sen + window._lastReport = readme; + // Parsitaan arvosana → tarkkailijan kehäväri + const firstLine = readme.split('\n')[0].toUpperCase(); + let verdictColor = '#3fb950'; // oletus: vihreä + let verdictText = 'OK'; + if (firstLine.includes('RED')) { verdictColor = '#f85149'; verdictText = 'KRIITTISTÄ'; } + else if (firstLine.includes('ORANGE')) { verdictColor = '#d29922'; verdictText = 'HUOMIOITA'; } + // Asetetaan tarkkailijan kehäväri + const obsAvatar = document.querySelector('.agent-avatar[data-agent="observer"] img'); + if (obsAvatar) { + obsAvatar.style.borderColor = verdictColor; + obsAvatar.style.boxShadow = `0 0 12px ${verdictColor}`; + } + termLog(` ● ${verdictText} — klikkaa Tarkkailijaa nähdäksesi raportin`); + } stepN++; } @@ -1118,6 +1144,24 @@ OUTPUT FORMAT: saveSettings(); initSettings(); }; + // === Raportti-modal === + window.showReportModal = function(markdown) { + let modal = document.getElementById('report-modal'); + if (!modal) { + modal = document.createElement('div'); + modal.id = 'report-modal'; + modal.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.7);z-index:1000;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(4px)'; + modal.innerHTML = `
+ +
+
`; + document.body.appendChild(modal); + } + document.getElementById('report-modal-content').innerHTML = renderMd(markdown); + document.getElementById('report-modal-content').querySelectorAll('pre code').forEach(b => { if (typeof hljs !== 'undefined') hljs.highlightElement(b); }); + modal.style.display = 'flex'; + modal.addEventListener('click', (e) => { if (e.target === modal) modal.style.display = 'none'; }); + };