mirror of
https://github.com/hanzo-apps/hanzo.ai.git
synced 2026-10-30 14:58:08 +00:00
The product pages from /s3 to /zap, /university and its course data, /trust, /security, /sdks, /support, /tasks, /startups, /sms-opt-in, the buying specialist, Zen, Kai's guide, Studio, the settings pages, and the shared sales, solutions, zap and ui pieces. Client code draws with t(), server components with <T>, copy held as data is marked with phrase() and drawn through t() where it lands. A link or a figure inside a sentence is a value, so a translator reorders the whole sentence. Rebased on main: every page main moved onto Fold or Steps keeps main's layout, with its words wrapped (label, title, lede, points). Steps now draws a step's body through t, as it already drew the title. Settings keeps main's usage panel, refresh, rewards wallet and sign-in wallet rows, each label wrapped. Left English: the skills page's install line (a command and two URLs), and the solution eyebrow and sibling heading built from a kind label. Claude-Session: https://claude.ai/code/session_015XYKLjZtDTJEnMo4eK3AFH
960 lines
42 KiB
TypeScript
960 lines
42 KiB
TypeScript
'use client'
|
|
|
|
import React, { useState } from 'react'
|
|
import Link from 'next/link'
|
|
import { Box, Text, XStack, YStack } from '@/components/ui'
|
|
import { Band, Card, Head } from '@/components/marketing/band'
|
|
import { Action, Title, Lede, Line, Eyebrow, Chip } from '@hanzo/ui/marketing'
|
|
import { Grid } from '@hanzo/ui/grid'
|
|
import {
|
|
Terminal,
|
|
CheckCircle2,
|
|
Clock,
|
|
Play,
|
|
Award,
|
|
Cpu,
|
|
ShieldCheck,
|
|
Code2,
|
|
FileCode,
|
|
Layers,
|
|
ChevronRight,
|
|
ExternalLink,
|
|
RefreshCw,
|
|
Coins,
|
|
Check,
|
|
Copy,
|
|
Download,
|
|
Share2,
|
|
Sparkles,
|
|
BookOpen,
|
|
} from 'lucide-react'
|
|
import { UNIVERSITY_COURSES, type UniversityCourse } from '../courses-data'
|
|
import { phrase } from '@hanzo/i18n'
|
|
import { useI18n } from '@hanzo/i18n/react'
|
|
|
|
export default function StudentPortalPage() {
|
|
const { t } = useI18n()
|
|
const [selectedCourseSlug, setSelectedCourseSlug] = useState<string>('agentic-coding')
|
|
const [activeTab, setActiveTab] = useState<'terminal' | 'ast' | 'grader' | 'metering'>('terminal')
|
|
const [activeWeekIndex, setActiveWeekIndex] = useState<number>(2) // Week 3 in progress
|
|
const [terminalHistory, setTerminalHistory] = useState<string[]>([
|
|
'alex@hanzo-sandbox:~/swe-bench-agent$ hanzo dev status',
|
|
'[OK] gVisor runsc sandbox lease active (id: pod-gvs-uswest2-01)',
|
|
'[OK] Zen 6 (27.3B) inference endpoint connected via ZAP RPC (latency: 1.4ms)',
|
|
'[OK] Compute credit balance: $37.55 USD remaining of $50.00 deposit',
|
|
'alex@hanzo-sandbox:~/swe-bench-agent$ pytest -v tests/test_agent.py',
|
|
'============================= test session starts =============================',
|
|
'platform linux -- Python 3.12.3, pytest-8.1.1, pluggy-1.4.0',
|
|
'tests/test_agent.py::test_ast_symbol_resolution PASSED [ 25%]',
|
|
'tests/test_agent.py::test_zap_zerocopy_serialization PASSED [ 50%]',
|
|
'tests/test_agent.py::test_gvisor_sandboxed_reproduction PASSED [ 75%]',
|
|
'tests/test_agent.py::test_kai_zerotoken_decision_checkpoint PASSED [100%]',
|
|
'============================== 4 passed in 0.42s ===============================',
|
|
'alex@hanzo-sandbox:~/swe-bench-agent$ _',
|
|
])
|
|
const [isRunningCommand, setIsRunningCommand] = useState(false)
|
|
const [showCredentialModal, setShowCredentialModal] = useState(false)
|
|
|
|
const activeCourse: UniversityCourse =
|
|
UNIVERSITY_COURSES.find((c) => c.slug === selectedCourseSlug) || UNIVERSITY_COURSES[0]
|
|
|
|
const executeCommand = (cmd: string) => {
|
|
setIsRunningCommand(true)
|
|
setTerminalHistory((prev) => [...prev, `alex@hanzo-sandbox:~/swe-bench-agent$ ${cmd}`])
|
|
|
|
setTimeout(() => {
|
|
let output: string[] = []
|
|
if (cmd === 'zen eval --benchmark swe-bench') {
|
|
output = [
|
|
'[HANZO EVAL] Running SWE-bench Lite benchmark evaluation harness...',
|
|
' [1/5] django/django-14999: AST diff generated (cost: $0.038) -> TEST PASSED [100%]',
|
|
' [2/5] sympy/sympy-18057: Symbol extraction verified (cost: $0.041) -> TEST PASSED [100%]',
|
|
' [3/5] scikit-learn/scikit-learn-14092: Reproducing bug in gVisor -> TEST PASSED [100%]',
|
|
' [4/5] pytest-dev/pytest-7432: Zero syntax regressions -> TEST PASSED [100%]',
|
|
' [5/5] astropy/astropy-12907: Synthesizing patch -> TEST PASSED [100%]',
|
|
'--------------------------------------------------------------------------------',
|
|
'[SUMMARY] 5 of 5 SWE-bench benchmark issues resolved autonomously.',
|
|
'[ECONOMICS] Average compute cost per patch: $0.042 USD (Budget cap: $0.50).',
|
|
'[STATUS] 100% Test Pass Rate. Verification verified by Kai Decision Model.',
|
|
]
|
|
} else if (cmd === 'hanzo dev diff') {
|
|
output = [
|
|
'--- a/src/agent/synthesizer.py',
|
|
'+++ b/src/agent/synthesizer.py',
|
|
'@@ -42,7 +42,7 @@ class CodePatchSynthesizer:',
|
|
'- def repair_ast(self, tree: ast.AST) -> str:',
|
|
'+ def repair_ast(self, tree: ast.AST, budget_usd: float = 0.05) -> ast.AST:',
|
|
' """Generates syntax-safe diff with zero syntax regressions."""',
|
|
'+ kai_check = self.evaluator.verify_state(tree, ceiling=budget_usd)',
|
|
'+ assert kai_check.safe_to_execute, "Refusing execution: budget violation"',
|
|
]
|
|
} else {
|
|
output = [
|
|
'[COMMAND EXECUTED] Task processed in ephemeral gVisor container (exit code 0).',
|
|
]
|
|
}
|
|
setTerminalHistory((prev) => [...prev, ...output, 'alex@hanzo-sandbox:~/swe-bench-agent$ _'])
|
|
setIsRunningCommand(false)
|
|
}, 600)
|
|
}
|
|
|
|
return (
|
|
<Box minH="100vh" bg="$background" $platform-web={{ color: 'var(--foreground)' }}>
|
|
{/* ── Top Enrolled Student Navigation Bar ── */}
|
|
<Box
|
|
borderBottomWidth={1}
|
|
borderColor="var(--border)"
|
|
bg="var(--pure-black)"
|
|
py="$3"
|
|
px="$6"
|
|
position="sticky"
|
|
t={0}
|
|
$platform-web={{ zIndex: 'var(--z-sticky)' }}
|
|
>
|
|
<XStack items="center" justify="space-between" flexWrap="wrap" gap="$4" shrink={1} minW={0}>
|
|
<XStack items="center" gap="$3" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Link href="/university">
|
|
<XStack items="center" gap="$2" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Text fontSize="$2" fontWeight="700" color="var(--white)" fontFamily="$mono">
|
|
hanzo.university
|
|
</Text>
|
|
<Chip px={6} py={2} fontSize="$1" fontFamily="$mono" color="var(--state-success)">
|
|
{t('Student portal')}
|
|
</Chip>
|
|
</XStack>
|
|
</Link>
|
|
<Text color="var(--muted-foreground)">/</Text>
|
|
<Text fontSize="$2" color="var(--white)" fontWeight="600">
|
|
{activeCourse.code}: {t(activeCourse.title)}
|
|
</Text>
|
|
</XStack>
|
|
|
|
{/* Student Status Profile Badge */}
|
|
<XStack items="center" gap="$3" flexWrap="wrap" shrink={1} minW={0}>
|
|
<XStack
|
|
items="center"
|
|
gap="$2"
|
|
px="$3"
|
|
py="$1"
|
|
rounded="var(--radius-md)"
|
|
bg="var(--card)"
|
|
borderWidth={1}
|
|
borderColor="var(--border)"
|
|
>
|
|
<Coins size={14} color="var(--state-success)" />
|
|
<Text fontSize="$1" color="var(--muted-foreground)">
|
|
{t('Compute Credits:')}
|
|
</Text>
|
|
<Text fontSize="$1" fontWeight="700" color="var(--state-success)" fontFamily="$mono">
|
|
{t('${amount}.00 Active', { amount: activeCourse.rebateCredits })}
|
|
</Text>
|
|
</XStack>
|
|
|
|
<XStack
|
|
items="center"
|
|
gap="$2"
|
|
px="$3"
|
|
py="$1"
|
|
rounded="var(--radius-md)"
|
|
bg="var(--card)"
|
|
borderWidth={1}
|
|
borderColor="var(--border)"
|
|
>
|
|
<ShieldCheck size={14} color="var(--white)" />
|
|
<Text fontSize="$1" color="var(--muted-foreground)">
|
|
{t('Student ID:')}
|
|
</Text>
|
|
<Text fontSize="$1" fontWeight="600" color="var(--white)" fontFamily="$mono">
|
|
hz-stu-9821a
|
|
</Text>
|
|
</XStack>
|
|
|
|
<Action
|
|
render="button"
|
|
onClick={() => setShowCredentialModal(true)}
|
|
px={12}
|
|
py={6}
|
|
fontSize="$1"
|
|
>
|
|
<XStack items="center" gap="$1" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Award size={14} />
|
|
<Text fontSize="$1" fontWeight="600" color="inherit">
|
|
{t('View Credential')}
|
|
</Text>
|
|
</XStack>
|
|
</Action>
|
|
</XStack>
|
|
</XStack>
|
|
</Box>
|
|
|
|
{/* ── Active Course Selector Strip ── */}
|
|
<Box borderBottomWidth={1} borderColor="var(--border)" bg="var(--card)" px="$6" py="$2">
|
|
<XStack items="center" gap="$2" overflow="scroll" flexWrap="nowrap">
|
|
<Text fontSize="$1" color="var(--muted-foreground)" pr="$2" fontFamily="$mono">
|
|
{t('COURSES:')}
|
|
</Text>
|
|
{UNIVERSITY_COURSES.map((c) => (
|
|
<Box
|
|
key={c.slug}
|
|
render="button"
|
|
onClick={() => setSelectedCourseSlug(c.slug)}
|
|
px="$3"
|
|
py="$1"
|
|
rounded="var(--radius-md)"
|
|
bg={selectedCourseSlug === c.slug ? 'var(--white)' : 'transparent'}
|
|
borderWidth={1}
|
|
borderColor={selectedCourseSlug === c.slug ? 'var(--white)' : 'transparent'}
|
|
$platform-web={{
|
|
cursor: 'pointer',
|
|
border: 'none',
|
|
outline: 'none',
|
|
}}
|
|
>
|
|
<Text
|
|
fontSize="$1"
|
|
fontWeight="600"
|
|
fontFamily="$mono"
|
|
color={selectedCourseSlug === c.slug ? 'var(--pure-black)' : 'var(--muted-foreground)'}
|
|
>
|
|
{c.code} · {c.credential}
|
|
</Text>
|
|
</Box>
|
|
))}
|
|
</XStack>
|
|
</Box>
|
|
|
|
{/* ── Main Workspace Dashboard ── */}
|
|
<Band pad="$10" measure={1280}>
|
|
{/* Welcome Notification Banner */}
|
|
<Box
|
|
p="$4"
|
|
mb="$6"
|
|
rounded="var(--radius-lg)"
|
|
bg="var(--pure-black)"
|
|
borderWidth={1}
|
|
borderColor="var(--border-selected)"
|
|
>
|
|
<XStack items="center" justify="space-between" flexWrap="wrap" gap="$4" shrink={1} minW={0}>
|
|
<XStack items="center" gap="$3" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Box
|
|
p="$2"
|
|
rounded="var(--radius-md)"
|
|
bg="$panel"
|
|
$platform-web={{ backgroundColor: 'color-mix(in srgb, var(--state-success) 20%, transparent)' }}
|
|
>
|
|
<CheckCircle2 size={20} color="var(--state-success)" />
|
|
</Box>
|
|
<YStack gap="$1" shrink={1} minW={0}>
|
|
<XStack items="center" gap="$2" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Text fontSize="$2" fontWeight="700" color="var(--white)">
|
|
{t('Enrolled & Active: {code} — {title}', { code: activeCourse.code, title: t(activeCourse.title) })}
|
|
</Text>
|
|
<Chip px={8} py={2} fontSize="$1" fontFamily="$mono" color="var(--state-success)">
|
|
{t('Day 1 deposit active')}
|
|
</Chip>
|
|
</XStack>
|
|
<Text fontSize="$1" color="var(--muted-foreground)">
|
|
{t('Your 25% compute credit rebate (${amount}.00 USD) was deposited into your Hanzo Cloud account. All laboratory sandboxes are provisioned and accessible below.', { amount: activeCourse.rebateCredits })}
|
|
</Text>
|
|
</YStack>
|
|
</XStack>
|
|
|
|
<XStack items="center" gap="$2" flexWrap="wrap" shrink={1} minW={0}>
|
|
<XStack
|
|
render="a"
|
|
{...{ href: `/university/${activeCourse.slug}` }}
|
|
px={12}
|
|
py={6}
|
|
rounded="var(--radius-md)"
|
|
borderWidth={1}
|
|
borderColor="var(--border)"
|
|
items="center"
|
|
gap="$1.5"
|
|
>
|
|
<BookOpen size={13} />
|
|
<Text color="var(--white)" fontSize="$1">{t('Course Syllabus')}</Text>
|
|
</XStack>
|
|
<Action
|
|
render="button"
|
|
onClick={() => executeCommand('zen eval --benchmark swe-bench')}
|
|
disabled={isRunningCommand}
|
|
px={12}
|
|
py={6}
|
|
fontSize="$1"
|
|
>
|
|
{t('Run Autograder →')}
|
|
</Action>
|
|
</XStack>
|
|
</XStack>
|
|
</Box>
|
|
|
|
{/* ── Two-Column Layout: Left (Live Workspace / Terminal), Right (Curriculum & Tasks) ── */}
|
|
<Grid columns={{ min: 420, max: 2 }} gap={24}>
|
|
{/* LEFT: Live Laboratory Pod & Workstation */}
|
|
<YStack gap="$4" shrink={1} minW={0}>
|
|
<Card p="$0" overflow="hidden" bg="var(--pure-black)" borderWidth={1} borderColor="var(--border)">
|
|
{/* Terminal Header */}
|
|
<XStack
|
|
items="center"
|
|
justify="space-between"
|
|
p="$3"
|
|
px="$4"
|
|
borderBottomWidth={1}
|
|
borderColor="var(--border)"
|
|
bg="var(--card)"
|
|
>
|
|
<XStack items="center" gap="$3" flexWrap="wrap" shrink={1} minW={0}>
|
|
<XStack gap="$1" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Box width={10} height={10} rounded={9999} bg="var(--chrome-dot-red)" />
|
|
<Box width={10} height={10} rounded={9999} bg="var(--chrome-dot-yellow)" />
|
|
<Box width={10} height={10} rounded={9999} bg="var(--state-success)" />
|
|
</XStack>
|
|
<Text fontSize="$1" fontFamily="$mono" color="var(--white)" fontWeight="600">
|
|
{t('gVisor Sandbox: {pod}', { pod: 'pod-gvs-uswest2-01' })}
|
|
</Text>
|
|
<Chip px={6} py={2} fontSize="$1" fontFamily="$mono" color="var(--state-success)">
|
|
{t('Online')}
|
|
</Chip>
|
|
</XStack>
|
|
|
|
{/* Workspace Tabs */}
|
|
<XStack items="center" gap="$1" flexWrap="wrap" shrink={1} minW={0}>
|
|
{(
|
|
[
|
|
['terminal', phrase('Shell')],
|
|
['ast', phrase('AST Diff')],
|
|
['grader', phrase('Autograder')],
|
|
['metering', phrase('Spend')],
|
|
] as const
|
|
).map(([tab, label]) => (
|
|
<Text
|
|
key={tab}
|
|
render="button"
|
|
onClick={() => setActiveTab(tab)}
|
|
px="$2"
|
|
py="$1"
|
|
rounded="var(--radius-sm)"
|
|
bg={activeTab === tab ? 'var(--white-20)' : 'transparent'}
|
|
fontSize="$1"
|
|
$platform-web={{
|
|
cursor: 'pointer',
|
|
border: 'none',
|
|
color: activeTab === tab ? 'var(--white)' : 'var(--muted-foreground)',
|
|
fontFamily: 'monospace',
|
|
}}
|
|
>
|
|
{t(label)}
|
|
</Text>
|
|
))}
|
|
</XStack>
|
|
</XStack>
|
|
|
|
{/* Tab Content 1: Terminal Shell */}
|
|
{activeTab === 'terminal' && (
|
|
<YStack p="$4" bg="var(--pure-black)" minH={380} justify="space-between" shrink={1} minW={0}>
|
|
<Box
|
|
$platform-web={{
|
|
whiteSpace: 'pre-wrap',
|
|
maxHeight: '340px',
|
|
overflowY: 'auto',
|
|
}}
|
|
>
|
|
{terminalHistory.map((line, idx) => (
|
|
<Text
|
|
key={idx}
|
|
fontFamily="$mono"
|
|
fontSize="$1"
|
|
color={
|
|
line.startsWith('alex@')
|
|
? 'var(--state-success)'
|
|
: line.includes('PASSED')
|
|
? 'var(--state-success)'
|
|
: line.includes('[OK]')
|
|
? 'var(--white)'
|
|
: 'var(--muted-foreground)'
|
|
}
|
|
>
|
|
{line}
|
|
</Text>
|
|
))}
|
|
</Box>
|
|
|
|
{/* Quick Command Action Toolbar */}
|
|
<XStack
|
|
items="center"
|
|
gap="$2"
|
|
pt="$3"
|
|
borderTopWidth={1}
|
|
borderColor="var(--border)"
|
|
flexWrap="wrap"
|
|
>
|
|
<Text fontSize="$1" color="var(--muted-foreground)" fontFamily="$mono">
|
|
{t('Quick Run:')}
|
|
</Text>
|
|
{[
|
|
'pytest -v tests/test_agent.py',
|
|
'zen eval --benchmark swe-bench',
|
|
'hanzo dev diff',
|
|
].map((cmd) => (
|
|
<Text
|
|
key={cmd}
|
|
render="button"
|
|
onClick={() => !isRunningCommand && executeCommand(cmd)}
|
|
px="$2"
|
|
py="$1"
|
|
rounded="var(--radius-sm)"
|
|
bg="var(--card)"
|
|
borderWidth={1}
|
|
borderColor="var(--border)"
|
|
fontSize="$1"
|
|
$platform-web={{
|
|
cursor: 'pointer',
|
|
fontFamily: 'monospace',
|
|
color: 'var(--white-80)',
|
|
}}
|
|
hoverStyle={{ borderColor: 'var(--white)' }}
|
|
>
|
|
{cmd}
|
|
</Text>
|
|
))}
|
|
</XStack>
|
|
</YStack>
|
|
)}
|
|
|
|
{/* Tab Content 2: AST Diff Inspector */}
|
|
{activeTab === 'ast' && (
|
|
<YStack p="$4" bg="var(--pure-black)" minH={380} shrink={1} minW={0}>
|
|
<Text fontSize="$1" color="var(--muted-foreground)" mb="$2" fontFamily="$mono">
|
|
{t('AST Diff Inspector: Verified zero syntax regressions & budget bounds')}
|
|
</Text>
|
|
<Box
|
|
p="$3"
|
|
rounded="var(--radius-md)"
|
|
bg="var(--card)"
|
|
borderWidth={1}
|
|
borderColor="var(--border)"
|
|
$platform-web={{ whiteSpace: 'pre-wrap' }}
|
|
>
|
|
<Text color="var(--state-error)">- def repair_ast(self, tree: ast.AST) -> str:</Text>
|
|
<Text color="var(--state-success)">+ def repair_ast(self, tree: ast.AST, budget_usd: float = 0.05) -> ast.AST:</Text>
|
|
<Text color="var(--white-60)"> """Generates syntax-safe diff with zero syntax regressions."""</Text>
|
|
<Text color="var(--state-success)">+ kai_check = self.evaluator.verify_state(tree, ceiling=budget_usd)</Text>
|
|
<Text color="var(--state-success)">+ assert kai_check.safe_to_execute, "Refusing execution: budget violation"</Text>
|
|
</Box>
|
|
<XStack items="center" gap="$2" mt="$3" flexWrap="wrap" shrink={1} minW={0}>
|
|
<CheckCircle2 size={14} color="var(--state-success)" />
|
|
<Text fontSize="$1" color="var(--state-success)" fontWeight="600">
|
|
{t('AST Tree-Sitter validation: 0 errors · 0 unused symbols · Exit Code 0')}
|
|
</Text>
|
|
</XStack>
|
|
</YStack>
|
|
)}
|
|
|
|
{/* Tab Content 3: Autograder Logs */}
|
|
{activeTab === 'grader' && (
|
|
<YStack p="$4" bg="var(--pure-black)" minH={380} gap="$3" shrink={1} minW={0}>
|
|
<Text fontSize="$2" fontWeight="700" color="var(--white)">
|
|
{t('SWE-bench Automated Evaluation Harness')}
|
|
</Text>
|
|
{[
|
|
{ task: 'django/django-14999', cost: '$0.038', time: '1.2s', status: 'PASS' },
|
|
{ task: 'sympy/sympy-18057', cost: '$0.041', time: '1.4s', status: 'PASS' },
|
|
{ task: 'scikit-learn/scikit-learn-14092', cost: '$0.044', time: '1.8s', status: 'PASS' },
|
|
{ task: 'pytest-dev/pytest-7432', cost: '$0.029', time: '0.9s', status: 'PASS' },
|
|
{ task: 'astropy/astropy-12907', cost: '$0.052', time: '2.1s', status: 'PASS' },
|
|
].map((row) => (
|
|
<XStack
|
|
key={row.task}
|
|
items="center"
|
|
justify="space-between"
|
|
p="$2"
|
|
px="$3"
|
|
rounded="var(--radius-sm)"
|
|
bg="var(--card)"
|
|
borderWidth={1}
|
|
borderColor="var(--border)"
|
|
>
|
|
<XStack items="center" gap="$2" flexWrap="wrap" shrink={1} minW={0}>
|
|
<CheckCircle2 size={14} color="var(--state-success)" />
|
|
<Text fontSize="$1" fontFamily="$mono" color="var(--white)">
|
|
{row.task}
|
|
</Text>
|
|
</XStack>
|
|
<XStack items="center" gap="$3" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Text fontSize="$1" color="var(--muted-foreground)" fontFamily="$mono">
|
|
{row.time} · {row.cost}
|
|
</Text>
|
|
<Chip px={6} py={2} fontSize="$1" fontFamily="$mono" color="var(--state-success)">
|
|
{row.status}
|
|
</Chip>
|
|
</XStack>
|
|
</XStack>
|
|
))}
|
|
</YStack>
|
|
)}
|
|
|
|
{/* Tab Content 4: Compute Metering */}
|
|
{activeTab === 'metering' && (
|
|
<YStack p="$4" bg="var(--pure-black)" minH={380} gap="$4" shrink={1} minW={0}>
|
|
<Text fontSize="$2" fontWeight="700" color="var(--white)">
|
|
{t('Compute Credit Rebate Metering')}
|
|
</Text>
|
|
<Grid columns={{ min: 140, max: 2 }} gap={12}>
|
|
<YStack p="$3" rounded="var(--radius-md)" bg="var(--card)" gap="$1" shrink={1} minW={0}>
|
|
<Text fontSize="$1" color="var(--muted-foreground)">{t('Day 1 Deposit')}</Text>
|
|
<Text fontSize="$4" fontWeight="800" color="var(--white)">${activeCourse.rebateCredits}.00 USD</Text>
|
|
</YStack>
|
|
<YStack p="$3" rounded="var(--radius-md)" bg="var(--card)" gap="$1" shrink={1} minW={0}>
|
|
<Text fontSize="$1" color="var(--muted-foreground)">{t('Balance Remaining')}</Text>
|
|
<Text fontSize="$4" fontWeight="800" color="var(--state-success)">$37.55 USD</Text>
|
|
</YStack>
|
|
</Grid>
|
|
|
|
<YStack gap="$2" shrink={1} minW={0}>
|
|
<Text fontSize="$1" color="var(--muted-foreground)">{t('Resource Usage Breakdown:')}</Text>
|
|
<XStack justify="space-between" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Text fontSize="$1" color="var(--white)">{t('Zen 6 (27.3B) Inference (142k tokens)')}</Text>
|
|
<Text fontSize="$1" color="var(--muted-foreground)" fontFamily="$mono">$7.10</Text>
|
|
</XStack>
|
|
<XStack justify="space-between" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Text fontSize="$1" color="var(--white)">{t('gVisor Pod Execution (18.4 runtime hrs)')}</Text>
|
|
<Text fontSize="$1" color="var(--muted-foreground)" fontFamily="$mono">$4.60</Text>
|
|
</XStack>
|
|
<XStack justify="space-between" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Text fontSize="$1" color="var(--white)">{t('ZAP Zero-Copy RPC Channels')}</Text>
|
|
<Text fontSize="$1" color="var(--muted-foreground)" fontFamily="$mono">$0.75</Text>
|
|
</XStack>
|
|
</YStack>
|
|
</YStack>
|
|
)}
|
|
</Card>
|
|
|
|
{/* Sandbox Container Specifications */}
|
|
<Card p="$4" bg="$panel" borderWidth={1} borderColor="var(--border)">
|
|
<XStack items="center" justify="space-between" flexWrap="wrap" gap="$3" shrink={1} minW={0}>
|
|
<XStack items="center" gap="$3" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Cpu size={16} color="var(--white)" />
|
|
<YStack shrink={1} minW={0}>
|
|
<Text fontSize="$1" fontWeight="600" color="var(--white)">
|
|
{t('Hardware Leases: 4 vCPU · 16 GB RAM · Apple Silicon Metal / CUDA L4')}
|
|
</Text>
|
|
<Text fontSize="$1" color="var(--muted-foreground)">
|
|
{t('Sandboxed under Google gVisor runsc kernel virtualization in region us-west-2')}
|
|
</Text>
|
|
</YStack>
|
|
</XStack>
|
|
<XStack items="center" gap="$2" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Text
|
|
render="button"
|
|
onClick={() => executeCommand('hanzo dev restart')}
|
|
px={10}
|
|
py={5}
|
|
rounded="var(--radius-sm)"
|
|
bg="var(--card)"
|
|
borderWidth={1}
|
|
borderColor="var(--border)"
|
|
fontSize="$1"
|
|
$platform-web={{
|
|
cursor: 'pointer',
|
|
color: 'var(--white)',
|
|
}}
|
|
>
|
|
{t('Restart Pod')}
|
|
</Text>
|
|
</XStack>
|
|
</XStack>
|
|
</Card>
|
|
</YStack>
|
|
|
|
{/* RIGHT: Curriculum Modules, Lectures & Capstone Submission */}
|
|
<YStack gap="$4" shrink={1} minW={0}>
|
|
<Card p="$5" bg="$panel" borderWidth={1} borderColor="var(--border)">
|
|
<XStack items="center" justify="space-between" mb="$4" flexWrap="wrap" shrink={1} minW={0}>
|
|
<YStack gap="$1" shrink={1} minW={0}>
|
|
<Text fontSize="$3" fontWeight="700" color="var(--white)">
|
|
{t('Course Modules & Progress')}
|
|
</Text>
|
|
<Text fontSize="$1" color="var(--muted-foreground)">
|
|
{t('Complete all 4 laboratory modules to qualify for HACE Credential Defense')}
|
|
</Text>
|
|
</YStack>
|
|
<Chip px={10} py={3} fontSize="$1" fontFamily="$mono" color="var(--state-success)">
|
|
{t('75% COMPLETED')}
|
|
</Chip>
|
|
</XStack>
|
|
|
|
{/* Progress Bar */}
|
|
<Box height={6} rounded={9999} bg="var(--neutral-800)" overflow="hidden" mb="$4">
|
|
<Box height="100%" width="75%" bg="var(--state-success)" />
|
|
</Box>
|
|
|
|
{/* Week Modules List */}
|
|
<YStack gap="$3" shrink={1} minW={0}>
|
|
{activeCourse.syllabus.map((week, idx) => {
|
|
const isCompleted = idx < 2
|
|
const isActive = idx === 2
|
|
const isLocked = idx > 2
|
|
|
|
return (
|
|
<Box
|
|
key={week.week}
|
|
p="$4"
|
|
rounded="var(--radius-md)"
|
|
bg={isActive ? 'var(--pure-black)' : 'var(--card)'}
|
|
borderWidth={1}
|
|
borderColor={
|
|
isActive
|
|
? 'var(--border-selected)'
|
|
: isCompleted
|
|
? 'var(--neutral-700)'
|
|
: 'var(--border)'
|
|
}
|
|
$platform-web={{ cursor: 'pointer' }}
|
|
onClick={() => setActiveWeekIndex(idx)}
|
|
>
|
|
<XStack items="center" justify="space-between" flexWrap="wrap" shrink={1} minW={0}>
|
|
<XStack items="center" gap="$3" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Box
|
|
width={24}
|
|
height={24}
|
|
rounded={9999}
|
|
items="center"
|
|
justify="center"
|
|
bg={
|
|
isCompleted
|
|
? 'var(--state-success)'
|
|
: isActive
|
|
? 'var(--white)'
|
|
: 'var(--neutral-800)'
|
|
}
|
|
>
|
|
{isCompleted ? (
|
|
<Check size={14} color="var(--state-success)" />
|
|
) : (
|
|
<Text
|
|
fontSize="$1"
|
|
fontWeight="700"
|
|
color={isActive ? 'var(--pure-black)' : 'var(--muted-foreground)'}
|
|
>
|
|
{idx + 1}
|
|
</Text>
|
|
)}
|
|
</Box>
|
|
<YStack shrink={1} minW={0}>
|
|
<XStack items="center" gap="$2" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Text fontSize="$1" fontFamily="$mono" color="var(--muted-foreground)">
|
|
{week.code}
|
|
</Text>
|
|
<Text fontSize="$2" fontWeight="600" color="var(--white)">
|
|
{t(week.title)}
|
|
</Text>
|
|
</XStack>
|
|
<Text fontSize="$1" color="var(--muted-foreground)" numberOfLines={1}>
|
|
{t(week.lab)}
|
|
</Text>
|
|
</YStack>
|
|
</XStack>
|
|
|
|
<Chip
|
|
px={8}
|
|
py={2}
|
|
fontSize="$1"
|
|
fontFamily="$mono"
|
|
color={
|
|
isCompleted
|
|
? 'var(--state-success)'
|
|
: isActive
|
|
? 'var(--foreground)'
|
|
: 'var(--muted-foreground)'
|
|
}
|
|
>
|
|
{isCompleted ? t('COMPLETED') : isActive ? t('IN PROGRESS') : t('UP NEXT')}
|
|
</Chip>
|
|
</XStack>
|
|
|
|
{/* Expanded View for Active Module */}
|
|
{activeWeekIndex === idx && (
|
|
<YStack
|
|
mt="$3"
|
|
pt="$3"
|
|
borderTopWidth={1}
|
|
borderColor="var(--border)"
|
|
gap="$3"
|
|
>
|
|
<Text fontSize="$1" color="var(--white-80)" lineHeight="$2">
|
|
{t(week.summary)}
|
|
</Text>
|
|
|
|
<YStack gap="$1" shrink={1} minW={0}>
|
|
<Text fontSize="$1" fontWeight="600" color="var(--white)">
|
|
{t('Lectures & Topics:')}
|
|
</Text>
|
|
{week.lectures.map((lec, lIdx) => (
|
|
<XStack key={lIdx} items="center" gap="$2" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Play size={10} color="var(--state-success)" />
|
|
<Text fontSize="$1" color="var(--muted-foreground)">
|
|
{t(lec)}
|
|
</Text>
|
|
</XStack>
|
|
))}
|
|
</YStack>
|
|
|
|
<XStack items="center" justify="space-between" pt="$2" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Text fontSize="$1" color="var(--state-success)" fontWeight="600">
|
|
{t('Lab Assignment: Active in terminal above')}
|
|
</Text>
|
|
<Action
|
|
render="button"
|
|
onClick={() => executeCommand('pytest -v tests/test_agent.py')}
|
|
px={10}
|
|
py={5}
|
|
fontSize="$1"
|
|
>
|
|
{t('Submit Lab →')}
|
|
</Action>
|
|
</XStack>
|
|
</YStack>
|
|
)}
|
|
</Box>
|
|
)
|
|
})}
|
|
</YStack>
|
|
</Card>
|
|
|
|
{/* Capstone Project Defense Card */}
|
|
<Card
|
|
p="$5"
|
|
bg="var(--pure-black)"
|
|
borderColor="var(--border-selected)"
|
|
borderWidth={1}
|
|
>
|
|
<YStack gap="$3" shrink={1} minW={0}>
|
|
<XStack items="center" justify="space-between" flexWrap="wrap" shrink={1} minW={0}>
|
|
<XStack items="center" gap="$2" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Award size={18} color="var(--state-success)" />
|
|
<Text fontSize="$2" fontWeight="700" color="var(--white)">
|
|
{t('Capstone Defense: {credential}', { credential: activeCourse.credential })}
|
|
</Text>
|
|
</XStack>
|
|
<Chip px={8} py={2} fontSize="$1" fontFamily="$mono" color="var(--state-success)">
|
|
{t('Ready for defense')}
|
|
</Chip>
|
|
</XStack>
|
|
<Text fontSize="$1" color="var(--muted-foreground)" lineHeight="$2">
|
|
{t(activeCourse.capstone)} {t('Submit your repository for automated gVisor autograder verification.')}
|
|
</Text>
|
|
<XStack items="center" gap="$3" pt="$1" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Action
|
|
render="button"
|
|
onClick={() => setShowCredentialModal(true)}
|
|
fill
|
|
>
|
|
{t('Claim & Preview {credential} Credential →', { credential: activeCourse.credential })}
|
|
</Action>
|
|
</XStack>
|
|
</YStack>
|
|
</Card>
|
|
</YStack>
|
|
</Grid>
|
|
</Band>
|
|
|
|
{/* ── W3C Verifiable Credential Modal ── */}
|
|
{showCredentialModal && (
|
|
<Box
|
|
position="fixed"
|
|
t={0}
|
|
l={0}
|
|
width="100vw"
|
|
height="100vh"
|
|
bg="var(--surface-scrim)"
|
|
items="center"
|
|
justify="center"
|
|
p="$4"
|
|
$platform-web={{ backdropFilter: 'blur(8px)', zIndex: 'var(--z-modal)' }}
|
|
>
|
|
<Box
|
|
width="100%"
|
|
maxW={680}
|
|
bg="var(--pure-black)"
|
|
rounded="var(--radius-2xl)"
|
|
borderWidth={1}
|
|
borderColor="var(--neutral-700)"
|
|
p="$6"
|
|
position="relative"
|
|
>
|
|
{/* Modal Header */}
|
|
<XStack items="center" justify="space-between" mb="$4" flexWrap="wrap" shrink={1} minW={0}>
|
|
<XStack items="center" gap="$2" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Award size={20} color="var(--state-success)" />
|
|
<Text fontSize="$3" fontWeight="700" color="var(--white)">
|
|
{t('W3C Verifiable Credential Issued')}
|
|
</Text>
|
|
</XStack>
|
|
<Text
|
|
render="button"
|
|
onClick={() => setShowCredentialModal(false)}
|
|
p="$1"
|
|
rounded="var(--radius-sm)"
|
|
fontSize="$4"
|
|
$platform-web={{
|
|
background: 'transparent',
|
|
border: 'none',
|
|
color: 'var(--muted-foreground)',
|
|
cursor: 'pointer',
|
|
}}
|
|
>
|
|
✕
|
|
</Text>
|
|
</XStack>
|
|
|
|
{/* Official Certificate Card */}
|
|
<Box
|
|
p="$6"
|
|
rounded="var(--radius-xl)"
|
|
bg="var(--card)"
|
|
borderWidth={1}
|
|
borderColor="var(--border-selected)"
|
|
gap="$4"
|
|
position="relative"
|
|
overflow="hidden"
|
|
>
|
|
<Box
|
|
position="absolute"
|
|
t={-40}
|
|
r={-40}
|
|
width={120}
|
|
height={120}
|
|
rounded={9999}
|
|
$platform-web={{ backgroundColor: 'color-mix(in srgb, var(--state-success) 10%, transparent)' }}
|
|
pointerEvents="none"
|
|
/>
|
|
|
|
<XStack items="center" justify="space-between" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Text fontSize="$1" fontFamily="$mono" color="var(--muted-foreground)">
|
|
{t('HANZO UNIVERSITY · RESEARCH FOUNDATION')}
|
|
</Text>
|
|
<Chip px={8} py={2} fontSize="$1" fontFamily="$mono" color="var(--state-success)">
|
|
{t('Verified on-chain')}
|
|
</Chip>
|
|
</XStack>
|
|
|
|
<YStack gap="$1" my="$2" shrink={1} minW={0}>
|
|
<Text fontSize="$1" color="var(--muted-foreground)">
|
|
{t('This certifies that')}
|
|
</Text>
|
|
<Text fontSize="$5" fontWeight="800" color="var(--white)">
|
|
Alex Chen
|
|
</Text>
|
|
<Text fontSize="$1" color="var(--muted-foreground)">
|
|
{t('HAS SUCCESSFULLY MASTERED ALL Academic specifications & DEFENDED THE CAPSTONE FOR')}
|
|
</Text>
|
|
<Text fontSize="$3" fontWeight="700" color="var(--state-success)" mt="$1">
|
|
{t(activeCourse.credentialFull)} ({activeCourse.credential})
|
|
</Text>
|
|
<Text fontSize="$1" color="var(--white-80)">
|
|
{t('Course: {code} · Academic Units: {units}.0 · Level: {level}', { code: activeCourse.code, units: activeCourse.units, level: t(activeCourse.level) })}
|
|
</Text>
|
|
</YStack>
|
|
|
|
{/* Cryptographic Signature Box */}
|
|
<Box
|
|
p="$3"
|
|
rounded="var(--radius-md)"
|
|
bg="var(--pure-black)"
|
|
borderWidth={1}
|
|
borderColor="var(--border)"
|
|
gap="$1"
|
|
>
|
|
<XStack justify="space-between" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Text color="var(--muted-foreground)">{t('DID Subject:')}</Text>
|
|
<Text color="var(--white)">did:hanzo:user:hz-stu-9821a</Text>
|
|
</XStack>
|
|
<XStack justify="space-between" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Text color="var(--muted-foreground)">{t('Issuer DID:')}</Text>
|
|
<Text color="var(--white)">did:hanzo:university:accreditation</Text>
|
|
</XStack>
|
|
<XStack justify="space-between" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Text color="var(--muted-foreground)">{t('Ed25519 Proof:')}</Text>
|
|
<Text color="var(--state-success)">{t('{proof} (Verified)', { proof: '0x7f4a8b1c...99e2e89b' })}</Text>
|
|
</XStack>
|
|
</Box>
|
|
|
|
<XStack items="center" justify="space-between" pt="$2" flexWrap="wrap" shrink={1} minW={0}>
|
|
<YStack shrink={1} minW={0}>
|
|
<Text fontSize="$1" color="var(--muted-foreground)">{t('Accreditation Officer')}</Text>
|
|
<Text fontSize="$1" fontWeight="600" color="var(--white)">{activeCourse.instructor.name}</Text>
|
|
<Text fontSize="$1" color="var(--muted-foreground)">{t(activeCourse.instructor.role)}</Text>
|
|
</YStack>
|
|
|
|
<YStack items="flex-end" shrink={1} minW={0}>
|
|
<Text fontSize="$1" color="var(--muted-foreground)">{t('Issue Date')}</Text>
|
|
<Text fontSize="$1" fontWeight="600" color="var(--white)">October 2026</Text>
|
|
<Text fontSize="$1" color="var(--state-success)">{t('Tamper-Proof Proof v2.0')}</Text>
|
|
</YStack>
|
|
</XStack>
|
|
</Box>
|
|
|
|
{/* Modal Actions */}
|
|
<XStack items="center" justify="space-between" mt="$4" flexWrap="wrap" gap="$3" shrink={1} minW={0}>
|
|
<XStack items="center" gap="$2" flexWrap="wrap" shrink={1} minW={0}>
|
|
<Text
|
|
render="button"
|
|
onClick={() => alert(t('W3C JSON-LD Credential copied to clipboard!'))}
|
|
px="$3"
|
|
py="$2"
|
|
rounded="var(--radius-md)"
|
|
bg="var(--card)"
|
|
borderWidth={1}
|
|
borderColor="var(--border)"
|
|
fontSize="$1"
|
|
$platform-web={{
|
|
cursor: 'pointer',
|
|
color: 'var(--white)',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '6px',
|
|
}}
|
|
>
|
|
<Copy size={13} />
|
|
{t('Copy JSON-LD')}
|
|
</Text>
|
|
<Text
|
|
render="button"
|
|
onClick={() => alert(t('PDF Certificate downloaded.'))}
|
|
px="$3"
|
|
py="$2"
|
|
rounded="var(--radius-md)"
|
|
bg="var(--card)"
|
|
borderWidth={1}
|
|
borderColor="var(--border)"
|
|
fontSize="$1"
|
|
$platform-web={{
|
|
cursor: 'pointer',
|
|
color: 'var(--white)',
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
gap: '6px',
|
|
}}
|
|
>
|
|
<Download size={13} />
|
|
{t('Download PDF')}
|
|
</Text>
|
|
</XStack>
|
|
|
|
<Action
|
|
render="button"
|
|
onClick={() => setShowCredentialModal(false)}
|
|
px={16}
|
|
py={8}
|
|
fill
|
|
>
|
|
{t('Close & Return to Lab')}
|
|
</Action>
|
|
</XStack>
|
|
</Box>
|
|
</Box>
|
|
)}
|
|
</Box>
|
|
)
|
|
}
|