From 314975ac65335763ed8335a8cc3fe859bb241afd Mon Sep 17 00:00:00 2001 From: a2nr Date: Fri, 10 Apr 2026 14:15:16 +0700 Subject: [PATCH] feat: add actions snippet to OutputPanel and implement run-all evaluation logic for lessons --- .../src/lib/components/OutputPanel.svelte | 34 +++++++++--- .../src/routes/lesson/[slug]/+page.svelte | 52 +++++++++++++++---- 2 files changed, 68 insertions(+), 18 deletions(-) diff --git a/frontend/src/lib/components/OutputPanel.svelte b/frontend/src/lib/components/OutputPanel.svelte index bcc29d9..d357720 100644 --- a/frontend/src/lib/components/OutputPanel.svelte +++ b/frontend/src/lib/components/OutputPanel.svelte @@ -18,9 +18,10 @@ interface Props { sections?: OutputEntry[]; + actions?: import('svelte').Snippet; } - let { sections = [] }: Props = $props(); + let { sections = [], actions }: Props = $props(); let showDebug = $state>({}); @@ -36,13 +37,20 @@
- Output - {#if anyLoading} - Compiling... - {:else if overallSuccess === true} - Berhasil - {:else if overallSuccess === false} - Error +
+ Output + {#if anyLoading} + Compiling... + {:else if overallSuccess === true} + Berhasil + {:else if overallSuccess === false} + Error + {/if} +
+ {#if actions} +
+ {@render actions()} +
{/if}
@@ -94,6 +102,16 @@ font-size: 0.8rem; font-family: system-ui, sans-serif; } + .header-info { + display: flex; + align-items: center; + gap: 0.5rem; + } + .header-actions { + display: flex; + align-items: center; + gap: 0.4rem; + } .output-title { font-weight: 600; } diff --git a/frontend/src/routes/lesson/[slug]/+page.svelte b/frontend/src/routes/lesson/[slug]/+page.svelte index d383c30..55edc6a 100644 --- a/frontend/src/routes/lesson/[slug]/+page.svelte +++ b/frontend/src/routes/lesson/[slug]/+page.svelte @@ -212,6 +212,7 @@ /** Mark lesson as complete: track progress + celebration. Called when ALL exercises pass. */ async function completeLesson() { + if (lessonCompleted) return; showCelebration = true; if (auth.isLoggedIn) { const lessonName = slug.replace('.md', ''); @@ -298,17 +299,16 @@ } } - async function handleRun() { - if (activeTab === 'circuit') { await evaluateCircuit(); return; } + async function evaluateLanguage(lang: 'c' | 'python') { if (!data) return; - const out = getCodeOut(); + const out = lang === 'c' ? cOut : pyOut; Object.assign(out, { loading: true, output: '', error: '', success: null }); activeTab = 'output'; try { - const code = editor?.getCode() ?? currentCode; - const res = await compileCode({ code, language: currentLanguage }); + const code = (currentLanguage === lang) ? (editor?.getCode() ?? currentCode) : (lang === 'c' ? cCode : pythonCode); + const res = await compileCode({ code, language: lang }); if (!res.success) { Object.assign(out, { error: res.error || 'Compilation failed', success: false }); @@ -319,13 +319,13 @@ out.success = true; if (data.expected_output) { - const currentCCode = currentLanguage === 'c' ? code : cCode; - const currentPythonCode = currentLanguage === 'python' ? code : pythonCode; + const currentCCode = (currentLanguage === 'c') ? code : cCode; + const currentPythonCode = (currentLanguage === 'python') ? code : pythonCode; const mergedCode = currentCCode + '\n' + currentPythonCode; const passed = res.output.trim() === data.expected_output.trim() && checkKeyText(mergedCode, data.key_text ?? ''); if (passed) { - if (currentLanguage === 'c') cPassed = true; - else if (currentLanguage === 'python') pythonPassed = true; + if (lang === 'c') cPassed = true; + else if (lang === 'python') pythonPassed = true; if (!checkAllPassed()) { out.output += '\n✅ Kode benar!\n⏳ Selesaikan juga tantangan di tab lainnya untuk menyelesaikan pelajaran ini.'; @@ -352,6 +352,28 @@ } } + async function handleRun() { + if (activeTab === 'circuit') { await evaluateCircuit(); return; } + if (!data) return; + + activeTab = 'output'; + await evaluateLanguage(currentLanguage as 'c' | 'python'); + } + + async function handleRunAll() { + if (!data) return; + activeTab = 'output'; + + const tabs = data.active_tabs ?? []; + const hasC = tabs.includes('c') || (!tabs.length && !tabs.includes('python')); + const hasPython = tabs.includes('python'); + + if (hasC) await evaluateLanguage('c'); + if (hasPython) await evaluateLanguage('python'); + if (tabs.includes('circuit')) await evaluateCircuit(); + if (tabs.includes('velxio')) await handleVelxioSubmit(); + } + function handleReset() { if (!data) return; if (activeTab === 'circuit') { @@ -922,7 +944,13 @@
- + + {#snippet actions()} + + {/snippet} +
@@ -1039,6 +1067,10 @@ .btn-secondary:hover { background: var(--color-border); } + .btn-run-all { + padding: 0.3rem 0.6rem; + font-size: 0.8rem; + } .lang-label { margin-left: auto; font-size: 0.75rem;