docs(prd): learner-first mobile app PRD and interactive prototypes (#916)
* docs(prd): add learner-first mobile app PRD and interactive prototypes Draft the ClassroomIO iOS/Android companion as a learner-first product (attention home, compliance wallet, offline content, push on top of prd/notification-system) with clickable HTML prototypes as the UX source of truth. Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> * style(prd): quiet mobile app prototype colours to match @cio/ui Replace filled red/orange/green surfaces with the dashboard Badge and Alert recipes: 10% tint chips, card-background alerts, and default rows. Keep destructive fills only for actual destroy actions. Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com> --------- Co-authored-by: Cursor Agent <cursoragent@cursor.com> Co-authored-by: Rotimi Best <rotimi-best@users.noreply.github.com>
This commit is contained in:
co-authored by
Rotimi Best
Cursor Agent
parent
368a7fe5ae
commit
967e81d33f
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,203 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Sign in — ClassroomIO Mobile</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300..800&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="app-theme.css" />
|
||||
<script>
|
||||
if (localStorage.cioTheme === 'dark' || (!localStorage.cioTheme && matchMedia('(prefers-color-scheme: dark)').matches))
|
||||
document.documentElement.classList.add('dark');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="proto-head">
|
||||
<a class="back" href="index.html"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="14" height="14"><path d="m15 18-6-6 6-6"/></svg> All screens</a>
|
||||
<span class="eyebrow">Learner · Entry</span>
|
||||
<h1>Sign in & organization</h1>
|
||||
<p>
|
||||
Email/password, Google, and enterprise SSO all run through Better Auth's Expo client — the session cookie lives in
|
||||
<code>expo-secure-store</code> and OAuth returns through the <code>classroomio://</code> deep link. Multi-org accounts
|
||||
pick an organization, which sets the <code>cio-org-id</code> header for every later request and re-themes the app.
|
||||
Self-hosted installs point the same binary at their own API and skip the picker.
|
||||
</p>
|
||||
<div class="proto-links">
|
||||
<a href="home.html">Next: Home →</a>
|
||||
<a href="profile.html">Org switcher (Profile)</a>
|
||||
<a href="downloads.html">Offline entry</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="stage">
|
||||
<!-- 1. SIGN IN -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:41"></div>
|
||||
|
||||
<div class="screen centered">
|
||||
<div style="text-align: center; margin-bottom: 26px">
|
||||
<div class="avatar brand lg" style="margin: 0 auto 14px; width: 60px; height: 60px; border-radius: 16px; font-size: 24px">C</div>
|
||||
<div style="font-size: 21px; font-weight: 600; letter-spacing: -0.02em">Welcome back</div>
|
||||
<div class="text-muted" style="font-size: 13.5px; margin-top: 4px">Sign in to continue your training</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Work email</label>
|
||||
<input class="input" type="email" value="amara.okafor@northwind.co" readonly />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Password</label>
|
||||
<input class="input" type="password" value="············" readonly />
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary btn-lg btn-block" style="margin-top: 4px">Sign in</button>
|
||||
|
||||
<div class="row" style="margin: 18px 0 16px">
|
||||
<div class="divider" style="flex: 1; margin: 0"></div>
|
||||
<span class="text-muted" style="font-size: 11.5px">OR</span>
|
||||
<div class="divider" style="flex: 1; margin: 0"></div>
|
||||
</div>
|
||||
|
||||
<div class="stack">
|
||||
<button class="btn btn-outline btn-lg btn-block">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 8v8M8 12h8"/></svg>
|
||||
Continue with Google
|
||||
</button>
|
||||
<button class="btn btn-outline btn-lg btn-block">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
|
||||
Sign in with SSO
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center; margin-top: 22px">
|
||||
<a class="text-primary" style="font-size: 13px; font-weight: 500">Forgot password?</a>
|
||||
</div>
|
||||
<div style="text-align: center; margin-top: 14px">
|
||||
<a class="text-muted" style="font-size: 12px">Use a custom server</a>
|
||||
<div class="hint" style="margin-top: 3px">Self-hosted ClassroomIO</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Sign in</b>Three auth paths in one screen. "Use a custom server" is how a self-hosted deployment points the same app at its own API.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 2. ORG PICKER -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:42"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<div>
|
||||
<div class="title">Choose organization</div>
|
||||
<div class="sub">You're a member of 3 academies</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="stack">
|
||||
<a class="lrow current" href="home.html">
|
||||
<div class="avatar brand" style="background: oklch(0.488 0.243 264.376)">NL</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Northwind Logistics</div>
|
||||
<div class="lmeta"><span class="badge badge-primary-soft">Learner</span><span>4 courses in progress</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
|
||||
<a class="lrow" href="tutor-grading.html">
|
||||
<div class="avatar brand" style="background: oklch(0.424 0.199 265.638)">AH</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Acme Health Academy</div>
|
||||
<div class="lmeta"><span class="badge badge-success-soft">Tutor</span><span>12 submissions to grade</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
|
||||
<a class="lrow" href="home.html">
|
||||
<div class="avatar brand" style="background: oklch(0.546 0.245 262.881)">MP</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Meridian Partner Network</div>
|
||||
<div class="lmeta"><span class="badge badge-primary-soft">Learner</span><span>Certification expires in 21 days</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="banner banner-muted" style="margin-top: 18px">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></svg>
|
||||
<div>
|
||||
<div class="bd" style="color: var(--muted-foreground)">
|
||||
Each organization keeps its own courses, branding, and records. You can switch any time from your profile.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Organization picker</b>Only for multi-org accounts. The choice sets <code>cio-org-id</code> and re-themes the app; single-org and self-hosted users never see it.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 3. BIOMETRIC + OFFLINE LAUNCH -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="7:02" data-offline></div>
|
||||
|
||||
<div class="offline-strip">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M2 2 22 22M8.5 16.5a5 5 0 0 1 7 0M5 13a10 10 0 0 1 4-2.4M12 20h.01"/></svg>
|
||||
You're offline — showing downloaded content
|
||||
</div>
|
||||
|
||||
<div class="screen centered">
|
||||
<div style="text-align: center">
|
||||
<div
|
||||
style="width: 88px; height: 88px; border-radius: 999px; margin: 0 auto 20px; display: grid; place-items: center; background: color-mix(in oklab, var(--primary) 12%, transparent); color: var(--primary)"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="40" height="40" stroke-width="1.6">
|
||||
<path d="M12 11c0 3-1 5.5-2 7"/><path d="M5 11a7 7 0 0 1 12-5"/><path d="M5 15v-4"/>
|
||||
<path d="M8.5 20a12 12 0 0 0 1.5-6 2 2 0 1 1 4 0 12 12 0 0 0 .5 3.5"/><path d="M19 11a7 7 0 0 0-.5-2.6"/>
|
||||
<path d="M17.5 19a20 20 0 0 0 .5-5"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div style="font-size: 20px; font-weight: 600; letter-spacing: -0.02em">Unlock ClassroomIO</div>
|
||||
<div class="text-muted" style="font-size: 13.5px; margin-top: 6px; line-height: 1.5; padding: 0 12px">
|
||||
Use Face ID to open your saved session. Your training records stay on this device until you reconnect.
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary btn-lg btn-block" style="margin-top: 26px">Unlock with Face ID</button>
|
||||
<button class="btn btn-ghost btn-block" style="margin-top: 8px">Use password instead</button>
|
||||
</div>
|
||||
|
||||
<a class="card card-pad row" href="downloads.html" style="margin-top: 30px; text-align: left">
|
||||
<div class="media is-current">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 15V3M7 10l5 5 5-5"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/></svg>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">3 courses available offline</div>
|
||||
<div class="lsub">Forklift Safety, Fire Safety, GDPR Essentials</div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Biometric re-entry, offline launch</b>Face ID unlocks an existing stored session — it never replaces sign-in. Launching with no network goes straight to downloaded content instead of an error.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<button class="theme-toggle" title="Toggle theme" aria-label="Toggle theme">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
|
||||
</button>
|
||||
<script src="proto.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,199 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Certificates — ClassroomIO Mobile</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300..800&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="app-theme.css" />
|
||||
<script>
|
||||
if (localStorage.cioTheme === 'dark' || (!localStorage.cioTheme && matchMedia('(prefers-color-scheme: dark)').matches))
|
||||
document.documentElement.classList.add('dark');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="proto-head">
|
||||
<a class="back" href="index.html"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="14" height="14"><path d="m15 18-6-6 6-6"/></svg> All screens</a>
|
||||
<span class="eyebrow">Learner · Proof</span>
|
||||
<h1>Certificate wallet</h1>
|
||||
<p>
|
||||
Proof of training in the learner's pocket — the thing a supervisor, an auditor, or a client site actually asks for.
|
||||
Reads the existing <code>packages/certificates</code> records, stays viewable offline once downloaded, and shares as
|
||||
a PDF with a verification link and QR.
|
||||
</p>
|
||||
<div class="proto-links">
|
||||
<a href="compliance.html">← Compliance</a>
|
||||
<a href="course.html">Earn another →</a>
|
||||
<a href="profile.html">Profile →</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="stage">
|
||||
<!-- 1. WALLET -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:41"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<a class="icon-btn plain" href="compliance.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
||||
<div class="grow"><div class="title">Certificates</div><div class="sub">6 issued · 1 expiring soon</div></div>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="chips" data-select style="margin: 10px 0 14px">
|
||||
<button class="chip on" data-option>All</button>
|
||||
<button class="chip" data-option>Valid</button>
|
||||
<button class="chip" data-option>Expiring</button>
|
||||
<button class="chip" data-option>Expired</button>
|
||||
</div>
|
||||
|
||||
<div class="stack">
|
||||
<div class="card" style="overflow: hidden">
|
||||
<div class="card-pad">
|
||||
<div class="row row-top">
|
||||
<div class="media lg is-warning"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="8" r="6"/><path d="M15.5 13.5 17 22l-5-3-5 3 1.5-8.5"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle" style="font-size: 15px">Hazardous Materials Handling</div>
|
||||
<div class="lsub">Issued 8 Sep 2025 · Northwind Logistics</div>
|
||||
<div class="lmeta"><span class="badge badge-warning-soft">Expires in 21 days</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-pad row" style="gap: 8px; padding-top: 0">
|
||||
<button class="btn btn-outline btn-sm" style="flex: 1">View</button>
|
||||
<button class="btn btn-outline btn-sm" style="flex: 1">Share</button>
|
||||
<button class="btn btn-primary btn-sm" style="flex: 1.2">Renew</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a class="lrow" href="certificates.html">
|
||||
<div class="media lg is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="8" r="6"/><path d="M15.5 13.5 17 22l-5-3-5 3 1.5-8.5"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Data Protection & GDPR Essentials</div>
|
||||
<div class="lmeta"><span>Issued 16 Aug 2026</span><span class="sep">·</span><span class="tnum">CIO-GDPR-4F91</span></div>
|
||||
<div class="lmeta"><span class="badge badge-success-soft">Valid until 16 Aug 2027</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
|
||||
<a class="lrow" href="certificates.html">
|
||||
<div class="media lg is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="8" r="6"/><path d="M15.5 13.5 17 22l-5-3-5 3 1.5-8.5"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Manual Handling & Ergonomics</div>
|
||||
<div class="lmeta"><span>Issued 2 Jun 2026</span><span class="sep">·</span><span class="tnum">CIO-MHE-8C22</span></div>
|
||||
<div class="lmeta"><span class="badge badge-success-soft">No expiry</span><span class="badge badge-outline">Saved offline</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
|
||||
<a class="lrow" href="certificates.html">
|
||||
<div class="media lg is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="8" r="6"/><path d="M15.5 13.5 17 22l-5-3-5 3 1.5-8.5"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Site Induction — Meridian Depot</div>
|
||||
<div class="lmeta"><span>Issued 4 Mar 2026</span><span class="sep">·</span><span class="tnum">CIO-SI-1A70</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
|
||||
<div class="lrow" style="opacity: 0.65">
|
||||
<div class="media lg"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="8" r="6"/><path d="M15.5 13.5 17 22l-5-3-5 3 1.5-8.5"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Forklift Safety Recertification 2025</div>
|
||||
<div class="lmeta"><span class="badge badge-danger-soft">Expired 17 Aug 2026</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="tabbar" data-tabbar="compliance"></nav>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Wallet</b>Sorted by what needs attention. Expiring certificates get a Renew action that jumps straight into the refresher course.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 2. DETAIL -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:43"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<a class="icon-btn plain" href="certificates.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
||||
<div class="grow"><div class="title-sm">Certificate</div></div>
|
||||
<button class="icon-btn" aria-label="Share"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/><path d="m16 6-4-4-4 4M12 2v13"/></svg></button>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<!-- rendered certificate -->
|
||||
<div
|
||||
class="card"
|
||||
style="margin: 12px 0 14px; padding: 22px 18px; text-align: center; border-width: 2px; border-color: color-mix(in oklab, var(--primary) 30%, transparent)"
|
||||
>
|
||||
<div class="avatar brand" style="margin: 0 auto 12px; width: 38px; height: 38px">NL</div>
|
||||
<div class="eyebrow">Northwind Logistics Academy</div>
|
||||
<div style="font-size: 12.5px; color: var(--muted-foreground); margin: 12px 0 4px">This certifies that</div>
|
||||
<div style="font-size: 19px; font-weight: 600; letter-spacing: -0.02em">Amara Okafor</div>
|
||||
<div style="font-size: 12.5px; color: var(--muted-foreground); margin: 10px 0 4px">has successfully completed</div>
|
||||
<div style="font-size: 15.5px; font-weight: 600; letter-spacing: -0.01em; line-height: 1.35">
|
||||
Data Protection & GDPR Essentials
|
||||
</div>
|
||||
<div class="lsub" style="margin-top: 8px">16 August 2026 · Final score 92%</div>
|
||||
|
||||
<div class="divider" style="margin: 16px 0 14px"></div>
|
||||
|
||||
<div class="row" style="justify-content: center; gap: 14px">
|
||||
<div style="width: 62px; height: 62px; border-radius: 8px; border: 1px solid var(--border); display: grid; place-items: center; background: var(--muted)">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="30" height="30" stroke-width="1.7">
|
||||
<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/>
|
||||
<path d="M14 14h3v3h-3zM18 18h3v3h-3z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div style="text-align: left">
|
||||
<div class="eyebrow">Verify</div>
|
||||
<div class="tnum" style="font-size: 12px; font-weight: 600; margin-top: 3px">CIO-GDPR-4F91-2026</div>
|
||||
<div class="lsub" style="margin-top: 2px">verify.classroomio.com</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" style="gap: 8px; margin-bottom: 14px">
|
||||
<button class="btn btn-primary btn-lg" style="flex: 1">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/><path d="m16 6-4-4-4 4M12 2v13"/></svg>
|
||||
Share PDF
|
||||
</button>
|
||||
<button class="btn btn-outline btn-lg" style="flex: 1">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 15V3M7 10l5 5 5-5"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/></svg>
|
||||
Save
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="card card-pad">
|
||||
<div class="kv"><span class="k">Issued to</span><span class="v">Amara Okafor</span></div>
|
||||
<div class="kv"><span class="k">Issued by</span><span class="v">Northwind Logistics</span></div>
|
||||
<div class="kv"><span class="k">Issue date</span><span class="v">16 Aug 2026</span></div>
|
||||
<div class="kv"><span class="k">Valid until</span><span class="v">16 Aug 2027</span></div>
|
||||
<div class="kv"><span class="k">Certificate ID</span><span class="v tnum" style="font-size: 12.5px">CIO-GDPR-4F91-2026</span></div>
|
||||
<div class="kv"><span class="k">Available offline</span><span class="v"><span class="badge badge-success-soft">Yes</span></span></div>
|
||||
</div>
|
||||
|
||||
<div class="hint" style="text-align: center; margin-top: 12px">
|
||||
Anyone with the ID can confirm this certificate is genuine without you sending a file.
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Detail & verification</b>The rendered certificate, share sheet, and a verification ID/QR — so a site supervisor can check it on the spot.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<button class="theme-toggle" title="Toggle theme" aria-label="Toggle theme">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
|
||||
</button>
|
||||
<script src="proto.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,321 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Community — ClassroomIO Mobile</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300..800&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="app-theme.css" />
|
||||
<script>
|
||||
if (localStorage.cioTheme === 'dark' || (!localStorage.cioTheme && matchMedia('(prefers-color-scheme: dark)').matches))
|
||||
document.documentElement.classList.add('dark');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="proto-head">
|
||||
<a class="back" href="index.html"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="14" height="14"><path d="m15 18-6-6 6-6"/></svg> All screens</a>
|
||||
<span class="eyebrow">Learner · Tab 4</span>
|
||||
<h1>Community & newsfeed</h1>
|
||||
<p>
|
||||
Org and course Q&A plus cohort announcements, backed by the existing <code>/community</code> and
|
||||
<code>/course/:id/newsfeed</code> routes. There is no direct messaging in ClassroomIO, so threaded Q&A is the
|
||||
discussion surface — and offline it degrades to read-only with the compose box explicitly disabled.
|
||||
</p>
|
||||
<div class="proto-links">
|
||||
<a href="notifications.html">Notifications →</a>
|
||||
<a href="tutor-course.html">Tutor moderation →</a>
|
||||
<a href="home.html">← Home</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="stage">
|
||||
<!-- 1. FEED -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="12:20"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<div class="grow"><div class="title">Community</div></div>
|
||||
<button class="icon-btn" aria-label="Search"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg></button>
|
||||
<button class="icon-btn" aria-label="New post"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 5v14M5 12h14"/></svg></button>
|
||||
</div>
|
||||
|
||||
<div class="tabs" data-select>
|
||||
<button class="on" data-option>For you</button>
|
||||
<button data-option>Q&A</button>
|
||||
<button data-option>Announcements</button>
|
||||
<button data-option>My threads</button>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="card card-pad" style="margin: 12px 0 10px; border-color: color-mix(in oklab, var(--primary) 30%, transparent)">
|
||||
<div class="row row-top">
|
||||
<div class="avatar">DM</div>
|
||||
<div class="grow">
|
||||
<div class="row between">
|
||||
<div class="ltitle" style="font-size: 13.5px">Daniel Mensah <span class="badge badge-primary-soft" style="margin-left: 4px">Tutor</span></div>
|
||||
<span class="text-muted" style="font-size: 11px">2h</span>
|
||||
</div>
|
||||
<div class="lmeta" style="margin-top: 1px"><span>Announcement · Q3 Frontline Onboarding</span></div>
|
||||
<div style="font-size: 13.5px; line-height: 1.55; margin-top: 7px">
|
||||
The practical forklift assessment moves to <strong>Bay 4</strong> this Thursday. Bring your completion code
|
||||
from Lesson 12 — we can't sign anyone off without it.
|
||||
</div>
|
||||
<div class="row" style="margin-top: 10px; gap: 14px">
|
||||
<span class="lmeta" style="margin: 0"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M7 10v12M15 5.9 14 10h5.8a2 2 0 0 1 2 2.3l-1.4 8a2 2 0 0 1-2 1.7H7V10l4-8a2.7 2.7 0 0 1 4 2z"/></svg> 12</span>
|
||||
<span class="lmeta" style="margin: 0"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22z"/></svg> 4 replies</span>
|
||||
<span class="badge badge-primary-soft" style="margin-left: auto">Mentions you</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head" style="margin-top: 14px"><h2>Questions you follow</h2></div>
|
||||
<div class="stack">
|
||||
<div class="card card-pad">
|
||||
<div class="row row-top">
|
||||
<div class="avatar">TA</div>
|
||||
<div class="grow">
|
||||
<div class="row between">
|
||||
<div class="ltitle" style="font-size: 13.5px">Tomás Alvarez</div>
|
||||
<span class="text-muted" style="font-size: 11px">5h</span>
|
||||
</div>
|
||||
<div class="lmeta" style="margin-top: 1px"><span>Forklift Safety Recertification · Lesson 4</span></div>
|
||||
<div style="font-size: 14px; font-weight: 500; margin-top: 6px; line-height: 1.4">
|
||||
Does a hairline crack at the fork heel always mean tag-out?
|
||||
</div>
|
||||
<div class="lrow" style="margin-top: 10px; background: var(--muted); border: none">
|
||||
<div class="avatar sm">DM</div>
|
||||
<div class="grow">
|
||||
<div style="font-size: 12.5px; line-height: 1.45">
|
||||
Yes — any crack at the heel is a tag-out. Photograph it, log it, and tell your supervisor before the
|
||||
next shift.
|
||||
</div>
|
||||
<div class="lmeta"><span class="badge badge-success-soft">Answered by tutor</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="margin-top: 10px; gap: 14px">
|
||||
<span class="lmeta" style="margin: 0"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M7 10v12M15 5.9 14 10h5.8a2 2 0 0 1 2 2.3l-1.4 8a2 2 0 0 1-2 1.7H7V10l4-8a2.7 2.7 0 0 1 4 2z"/></svg> 8</span>
|
||||
<span class="lmeta" style="margin: 0"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22z"/></svg> 3</span>
|
||||
<span class="lmeta" style="margin: 0 0 0 auto"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg> Following</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-pad">
|
||||
<div class="row row-top">
|
||||
<div class="avatar">PK</div>
|
||||
<div class="grow">
|
||||
<div class="row between">
|
||||
<div class="ltitle" style="font-size: 13.5px">Priya Kaur</div>
|
||||
<span class="text-muted" style="font-size: 11px">1d</span>
|
||||
</div>
|
||||
<div class="lmeta" style="margin-top: 1px"><span>GDPR Essentials · General</span></div>
|
||||
<div style="font-size: 14px; font-weight: 500; margin-top: 6px; line-height: 1.4">
|
||||
If a customer asks for their data verbally at the counter, does the 30-day clock start?
|
||||
</div>
|
||||
<div class="row" style="margin-top: 10px; gap: 14px">
|
||||
<span class="lmeta" style="margin: 0"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M7 10v12M15 5.9 14 10h5.8a2 2 0 0 1 2 2.3l-1.4 8a2 2 0 0 1-2 1.7H7V10l4-8a2.7 2.7 0 0 1 4 2z"/></svg> 5</span>
|
||||
<span class="lmeta" style="margin: 0"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22z"/></svg> 2</span>
|
||||
<span class="badge badge-warning-soft" style="margin-left: auto">Unanswered</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Ask your own</h2></div>
|
||||
<div class="card card-pad">
|
||||
<div class="row">
|
||||
<div class="avatar">AO</div>
|
||||
<div class="grow"><div class="text-muted" style="font-size: 13.5px">Ask a question about a lesson…</div></div>
|
||||
<button class="icon-btn" aria-label="Attach"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m21.4 11.1-9.2 9.2a5 5 0 0 1-7-7l9.2-9.2a3.3 3.3 0 1 1 4.7 4.7l-9.2 9.2a1.7 1.7 0 0 1-2.4-2.4l8.5-8.5"/></svg></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="tabbar" data-tabbar="community" data-badges="community:3"></nav>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>For you</b>Mentions, followed threads, and announcements in one stream. Tutor answers are marked so learners can trust them at a glance.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 2. THREAD -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="12:24"></div>
|
||||
|
||||
<div class="navbar bordered">
|
||||
<a class="icon-btn plain" href="community.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
||||
<div class="grow"><div class="title-sm">Question</div><div class="sub">Forklift Safety · Lesson 4</div></div>
|
||||
<button class="icon-btn" aria-label="More"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/></svg></button>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div style="padding: 14px 0 10px">
|
||||
<div class="row row-top">
|
||||
<div class="avatar">TA</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle" style="font-size: 13.5px">Tomás Alvarez</div>
|
||||
<div class="lmeta" style="margin-top: 0"><span>5h ago</span></div>
|
||||
</div>
|
||||
<button class="btn btn-outline btn-sm">Following</button>
|
||||
</div>
|
||||
<div style="font-size: 16.5px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.35; margin: 12px 0 8px">
|
||||
Does a hairline crack at the fork heel always mean tag-out?
|
||||
</div>
|
||||
<div class="prose" style="font-size: 14px">
|
||||
<p>
|
||||
The lesson says cracks are a fault but doesn't distinguish severity. On our older trucks there are surface
|
||||
marks that have been there for years. Do I tag out every time?
|
||||
</p>
|
||||
</div>
|
||||
<div class="row" style="gap: 8px">
|
||||
<button class="btn btn-outline btn-sm"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M7 10v12M15 5.9 14 10h5.8a2 2 0 0 1 2 2.3l-1.4 8a2 2 0 0 1-2 1.7H7V10l4-8a2.7 2.7 0 0 1 4 2z"/></svg> 8</button>
|
||||
<button class="btn btn-ghost btn-sm">Reply</button>
|
||||
<button class="btn btn-ghost btn-sm" style="margin-left: auto">Share</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="card card-pad" style="margin-bottom: 12px">
|
||||
<div class="row row-top">
|
||||
<div class="avatar">DM</div>
|
||||
<div class="grow">
|
||||
<div class="row between">
|
||||
<div class="ltitle" style="font-size: 13.5px">Daniel Mensah <span class="badge badge-success-soft" style="margin-left: 4px">Tutor answer</span></div>
|
||||
<span class="text-muted" style="font-size: 11px">4h</span>
|
||||
</div>
|
||||
<div style="font-size: 13.5px; line-height: 1.55; margin-top: 7px">
|
||||
Yes — any crack at the heel is a tag-out, no severity judgement. Surface scoring from wear is different:
|
||||
it's smooth and doesn't catch a fingernail. If you can feel an edge, tag it, photograph it, and log it.
|
||||
</div>
|
||||
<div class="row" style="margin-top: 10px; gap: 14px">
|
||||
<span class="lmeta" style="margin: 0"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M7 10v12M15 5.9 14 10h5.8a2 2 0 0 1 2 2.3l-1.4 8a2 2 0 0 1-2 1.7H7V10l4-8a2.7 2.7 0 0 1 4 2z"/></svg> 14</span>
|
||||
<span class="lmeta" style="margin: 0">Reply</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stack">
|
||||
<div class="lrow" style="align-items: flex-start">
|
||||
<div class="avatar sm">PK</div>
|
||||
<div class="grow">
|
||||
<div class="row between">
|
||||
<div class="ltitle" style="font-size: 13px">Priya Kaur</div>
|
||||
<span class="text-muted" style="font-size: 11px">3h</span>
|
||||
</div>
|
||||
<div style="font-size: 13px; line-height: 1.5; margin-top: 4px">
|
||||
The fingernail test is a good rule of thumb — that's what our depot uses too.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lrow" style="align-items: flex-start">
|
||||
<div class="avatar sm">AO</div>
|
||||
<div class="grow">
|
||||
<div class="row between">
|
||||
<div class="ltitle" style="font-size: 13px">Amara Okafor</div>
|
||||
<span class="text-muted" style="font-size: 11px">2h</span>
|
||||
</div>
|
||||
<div style="font-size: 13px; line-height: 1.5; margin-top: 4px">
|
||||
Thanks — I tagged out FLT-07 this morning on exactly this.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" style="gap: 8px; margin-top: 16px">
|
||||
<input class="input" placeholder="Write a reply…" />
|
||||
<button class="icon-btn" style="width: 40px; height: 40px" aria-label="Send"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m22 2-7 20-4-9-9-4z"/></svg></button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Thread</b>The tutor answer is visually promoted above learner replies — the same author/team distinction the API already enforces.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 3. OFFLINE READ-ONLY -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="19:05" data-offline></div>
|
||||
|
||||
<div class="offline-strip">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M2 2 22 22M8.5 16.5a5 5 0 0 1 7 0M5 13a10 10 0 0 1 4-2.4M12 20h.01"/></svg>
|
||||
Offline — showing cached posts
|
||||
</div>
|
||||
|
||||
<div class="navbar">
|
||||
<div class="grow"><div class="title">Community</div></div>
|
||||
<button class="icon-btn" disabled style="opacity: 0.45" aria-label="New post"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 5v14M5 12h14"/></svg></button>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="banner banner-muted" style="margin: 12px 0 14px">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M2 2 22 22M8.5 16.5a5 5 0 0 1 7 0M5 13a10 10 0 0 1 4-2.4M12 20h.01"/></svg>
|
||||
<div class="grow">
|
||||
<div class="bt">Posting is off while you're offline</div>
|
||||
<div class="bd">You can read the last 50 posts. Replies need a connection so they don't post out of order.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stack" style="opacity: 0.85">
|
||||
<div class="card card-pad">
|
||||
<div class="row row-top">
|
||||
<div class="avatar">DM</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle" style="font-size: 13.5px">Daniel Mensah</div>
|
||||
<div class="lmeta" style="margin-top: 1px"><span>Cached 2h ago</span></div>
|
||||
<div style="font-size: 13.5px; line-height: 1.55; margin-top: 7px">
|
||||
The practical forklift assessment moves to Bay 4 this Thursday.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card card-pad">
|
||||
<div class="row row-top">
|
||||
<div class="avatar">TA</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle" style="font-size: 13.5px">Tomás Alvarez</div>
|
||||
<div class="lmeta" style="margin-top: 1px"><span>Cached 5h ago</span></div>
|
||||
<div style="font-size: 13.5px; line-height: 1.55; margin-top: 7px">
|
||||
Does a hairline crack at the fork heel always mean tag-out?
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lrow locked" style="margin-top: 14px">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Reply box disabled</div>
|
||||
<div class="lsub">Reconnect to post — nothing you type would be saved</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="tabbar" data-tabbar="community"></nav>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Offline read-only</b>The compose affordance is disabled and explained rather than accepting text that would be lost — no fake optimistic posting.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<button class="theme-toggle" title="Toggle theme" aria-label="Toggle theme">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
|
||||
</button>
|
||||
<script src="proto.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,206 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Compliance — ClassroomIO Mobile</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300..800&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="app-theme.css" />
|
||||
<script>
|
||||
if (localStorage.cioTheme === 'dark' || (!localStorage.cioTheme && matchMedia('(prefers-color-scheme: dark)').matches))
|
||||
document.documentElement.classList.add('dark');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="proto-head">
|
||||
<a class="back" href="index.html"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="14" height="14"><path d="m15 18-6-6 6-6"/></svg> All screens</a>
|
||||
<span class="eyebrow">Learner · Tab 3</span>
|
||||
<h1>Compliance — the differentiator</h1>
|
||||
<p>
|
||||
ClassroomIO's core value is knowing where you stand, so it gets a tab rather than a settings sub-page. Deadlines,
|
||||
grace periods, renewals, and waivers all already exist server-side (<code>/course/:courseId/compliance</code>);
|
||||
mobile needs one learner-scoped aggregate, <code>GET /account/compliance</code>, to render them together.
|
||||
</p>
|
||||
<div class="proto-links">
|
||||
<a href="certificates.html">Certificate wallet →</a>
|
||||
<a href="course.html">Open a required course →</a>
|
||||
<a href="home.html">← Home</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="stage">
|
||||
<!-- 1. AT RISK -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:41"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<div class="grow"><div class="title">Compliance</div><div class="sub">Northwind Logistics · updated just now</div></div>
|
||||
<a class="icon-btn" href="certificates.html" aria-label="Certificates"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="8" r="6"/><path d="M15.5 13.5 17 22l-5-3-5 3 1.5-8.5"/></svg></a>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="card card-pad" style="margin: 8px 0 14px">
|
||||
<div class="row">
|
||||
<div class="media lg is-danger">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 9v4M12 17h.01"/><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0"/></svg>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Action required</div>
|
||||
<div class="lmeta">
|
||||
<span class="badge badge-danger-soft">1 overdue</span>
|
||||
<span class="badge badge-warning-soft">1 expiring</span>
|
||||
<span class="badge badge-outline">4 compliant</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head" style="margin-top: 0"><h2>Overdue</h2></div>
|
||||
<a class="card card-pad" href="course.html" style="display: block">
|
||||
<div class="row row-top">
|
||||
<div class="media is-danger"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 9v4M12 17h.01"/><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Forklift Safety Recertification 2026</div>
|
||||
<div class="lmeta"><span class="badge badge-danger-soft">Overdue 3 days</span><span class="badge badge-warning-soft">Grace ends 24 Aug</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider" style="margin: 12px 0"></div>
|
||||
<div class="kv"><span class="k">Deadline</span><span class="v">17 Aug 2026</span></div>
|
||||
<div class="kv"><span class="k">Progress</span><span class="v tnum">4 of 12 lessons</span></div>
|
||||
<div class="kv"><span class="k">If missed</span><span class="v">Authorization suspended</span></div>
|
||||
<button class="btn btn-primary btn-block" style="margin-top: 12px">Resume now · 28 min left</button>
|
||||
</a>
|
||||
|
||||
<div class="sec-head"><h2>Expiring soon</h2></div>
|
||||
<div class="card card-pad">
|
||||
<div class="row row-top">
|
||||
<div class="media is-warning"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Hazardous Materials Handling</div>
|
||||
<div class="lmeta"><span class="badge badge-warning-soft">Expires in 21 days</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider" style="margin: 12px 0"></div>
|
||||
<div class="row" style="margin-bottom: 6px">
|
||||
<div class="progress"><span style="width: 82%"></span></div>
|
||||
<span class="text-muted tnum" style="font-size: 11.5px">21d</span>
|
||||
</div>
|
||||
<div class="kv"><span class="k">Certified</span><span class="v">8 Sep 2025</span></div>
|
||||
<div class="kv"><span class="k">Renewal opens</span><span class="v">8 Sep 2026</span></div>
|
||||
<a class="btn btn-outline btn-block" href="certificates.html" style="margin-top: 12px">View current certificate</a>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Compliant</h2><span class="count">4</span></div>
|
||||
<div class="stack stack-sm">
|
||||
<a class="lrow flat" href="certificates.html">
|
||||
<div class="media is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Data Protection & GDPR</div><div class="lmeta"><span>Completed 16 Aug 2026</span><span class="sep">·</span><span>Renews annually</span></div></div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
<a class="lrow flat" href="certificates.html">
|
||||
<div class="media is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Manual Handling</div><div class="lmeta"><span>Completed 2 Jun 2026</span></div></div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
<a class="lrow flat" href="certificates.html">
|
||||
<div class="media is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Site Induction</div><div class="lmeta"><span>Completed 4 Mar 2026</span></div></div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
<div class="lrow flat">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6M9 15l2 2 4-4"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Working at Height</div>
|
||||
<div class="lmeta"><span class="badge badge-muted">Waived</span><span>Not required for your role</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="tabbar" data-tabbar="compliance" data-badges="compliance:dot"></nav>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>At risk</b>Overdue first, with the consequence spelled out ("Authorization suspended") and grace-period dates visible. Waivers are shown, not hidden.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 2. ALL CLEAR -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:41"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<div class="grow"><div class="title">Compliance</div><div class="sub">Northwind Logistics · updated just now</div></div>
|
||||
<a class="icon-btn" href="certificates.html" aria-label="Certificates"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="8" r="6"/><path d="M15.5 13.5 17 22l-5-3-5 3 1.5-8.5"/></svg></a>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="card card-pad" style="margin: 8px 0 14px">
|
||||
<div class="row">
|
||||
<div class="media lg is-done">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/><path d="m9 12 2 2 4-4"/></svg>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Fully compliant</div>
|
||||
<div class="lmeta"><span class="badge badge-success-soft">All 6 requirements up to date</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head" style="margin-top: 0"><h2>Renewal timeline</h2></div>
|
||||
<div class="card card-pad">
|
||||
<div class="lrow flat">
|
||||
<div class="media is-warning">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>
|
||||
</div>
|
||||
<div class="grow"><div class="ltitle">Hazardous Materials Handling</div><div class="lmeta"><span>Renews 15 Feb 2027</span></div></div>
|
||||
<span class="badge badge-warning-soft tnum">183d</span>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Forklift Safety Recertification</div><div class="lmeta"><span>Renews 17 Aug 2027</span></div></div>
|
||||
<span class="badge badge-outline tnum">366d</span>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Data Protection & GDPR</div><div class="lmeta"><span>Renews 16 Aug 2027</span></div></div>
|
||||
<span class="badge badge-outline tnum">365d</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Your record</h2></div>
|
||||
<div class="card card-pad">
|
||||
<div class="kv"><span class="k">Requirements met</span><span class="v tnum">6 of 6</span></div>
|
||||
<div class="kv"><span class="k">On-time completion rate</span><span class="v tnum">83%</span></div>
|
||||
<div class="kv"><span class="k">Certificates held</span><span class="v tnum">6</span></div>
|
||||
<div class="kv"><span class="k">Last audit export</span><span class="v">1 Aug 2026</span></div>
|
||||
</div>
|
||||
|
||||
<a class="btn btn-outline btn-block" href="certificates.html" style="margin-top: 14px">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="8" r="6"/><path d="M15.5 13.5 17 22l-5-3-5 3 1.5-8.5"/></svg>
|
||||
Open certificate wallet
|
||||
</a>
|
||||
<div class="hint" style="text-align: center">
|
||||
Your organization can verify any certificate from its ID without you sending a file.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="tabbar" data-tabbar="compliance"></nav>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>All clear</b>The same screen when nothing is wrong: status, what renews next, and the record an auditor would ask for.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<button class="theme-toggle" title="Toggle theme" aria-label="Toggle theme">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
|
||||
</button>
|
||||
<script src="proto.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,284 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Course detail — ClassroomIO Mobile</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300..800&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="app-theme.css" />
|
||||
<script>
|
||||
if (localStorage.cioTheme === 'dark' || (!localStorage.cioTheme && matchMedia('(prefers-color-scheme: dark)').matches))
|
||||
document.documentElement.classList.add('dark');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="proto-head">
|
||||
<a class="back" href="index.html"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="14" height="14"><path d="m15 18-6-6 6-6"/></svg> All screens</a>
|
||||
<span class="eyebrow">Learner · Course</span>
|
||||
<h1>Course detail & outline</h1>
|
||||
<p>
|
||||
Sections and lessons with an explicit state on every row: complete, current, locked (with the reason spelled out),
|
||||
or downloaded. Compliance courses carry a deadline banner. Download is a whole-course action from the header,
|
||||
sized before it starts and Wi-Fi-only by default.
|
||||
</p>
|
||||
<div class="proto-links">
|
||||
<a href="lesson.html">Open lesson →</a>
|
||||
<a href="exercise.html">Knowledge check →</a>
|
||||
<a href="certificates.html">Certificate →</a>
|
||||
<a href="learn.html">← Learn</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="stage">
|
||||
<!-- 1. OUTLINE -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:44"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<a class="icon-btn plain" href="learn.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
||||
<div class="grow"><div class="title-sm">Forklift Safety Recertification</div></div>
|
||||
<button class="icon-btn" data-sheet-open="dl" aria-label="Download course"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 15V3M7 10l5 5 5-5"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/></svg></button>
|
||||
<button class="icon-btn" aria-label="More"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="1"/><circle cx="19" cy="12" r="1"/><circle cx="5" cy="12" r="1"/></svg></button>
|
||||
</div>
|
||||
|
||||
<div class="tabs" data-select>
|
||||
<button class="on" data-option>Content</button>
|
||||
<button data-option>Compliance</button>
|
||||
<button data-option>Newsfeed</button>
|
||||
<button data-option>Community</button>
|
||||
<button data-option>Certificate</button>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="banner banner-danger" style="margin: 12px 0 12px">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 9v4M12 17h.01"/><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0"/></svg>
|
||||
<div class="grow">
|
||||
<div class="bt">Overdue by 3 days</div>
|
||||
<div class="bd">Grace period ends 24 Aug. After that your forklift authorization is suspended.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-pad" style="margin-bottom: 6px">
|
||||
<div class="row">
|
||||
<div class="ring lg">
|
||||
<svg viewBox="0 0 36 36"><circle class="track" cx="18" cy="18" r="15.5" fill="none" stroke-width="3"/><circle class="arc primary" cx="18" cy="18" r="15.5" fill="none" stroke-width="3" stroke-linecap="round" stroke-dasharray="97.4" stroke-dashoffset="65.3"/></svg>
|
||||
<span class="lbl tnum">33%</span>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="lmeta" style="margin: 0"><span>4 of 12 lessons</span></div>
|
||||
<div class="lmeta"><span>~28 min remaining</span></div>
|
||||
<div class="lmeta"><span class="badge badge-success-soft">Downloaded · 84 MB</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="btn btn-primary btn-block" href="lesson.html" style="margin-top: 12px">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M8 5v14l11-7z"/></svg>
|
||||
Continue lesson 4
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Section 1 · Before you drive</h2><span class="count">3/3</span></div>
|
||||
<div class="stack stack-sm">
|
||||
<a class="lrow flat done" href="lesson.html">
|
||||
<div class="media is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Why recertification matters</div><div class="lmeta"><span>Video · 4 min</span></div></div>
|
||||
</a>
|
||||
<a class="lrow flat done" href="lesson.html">
|
||||
<div class="media is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Site rules and exclusion zones</div><div class="lmeta"><span>Reading · 5 min</span></div></div>
|
||||
</a>
|
||||
<a class="lrow flat done" href="exercise.html">
|
||||
<div class="media is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Section 1 check</div><div class="lmeta"><span class="badge badge-success-soft">9/10</span></div></div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Section 2 · Inspection</h2><span class="count">1/4</span></div>
|
||||
<div class="stack stack-sm">
|
||||
<a class="lrow current" href="lesson.html" style="padding: 12px 14px">
|
||||
<div class="media is-current"><svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M8 5v14l11-7z"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Pre-operation inspection checklist</div>
|
||||
<div class="lmeta"><span>Video · 9 min</span><span class="sep">·</span><span class="text-primary" style="font-weight: 600">6 min left</span></div>
|
||||
</div>
|
||||
<span class="dl-dot"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></span>
|
||||
</a>
|
||||
<a class="lrow flat" href="lesson.html">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M8 5v14l11-7z"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Load stability and centre of gravity</div><div class="lmeta"><span>Video · 7 min</span></div></div>
|
||||
<span class="dl-dot"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></span>
|
||||
</a>
|
||||
<div class="lrow flat locked">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Hydraulics walkthrough</div>
|
||||
<div class="lmeta"><span>Complete "Load stability" to unlock</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lrow flat locked">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Section 2 check</div>
|
||||
<div class="lmeta"><span>Complete all lessons in this section</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Section 3 · Assessment</h2><span class="count">0/5</span></div>
|
||||
<div class="lrow flat locked">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">5 items locked</div>
|
||||
<div class="lmeta"><span>Finish Section 2 to unlock the assessment</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Outline</b>Every row states its own condition. Locked items name the prerequisite instead of just greying out — and the API enforces the same rule.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 2. DOWNLOAD SHEET -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:45"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<a class="icon-btn plain" href="learn.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
||||
<div class="grow"><div class="title-sm">Warehouse Fire Safety</div></div>
|
||||
<button class="icon-btn" aria-label="Download course"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 15V3M7 10l5 5 5-5"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/></svg></button>
|
||||
</div>
|
||||
|
||||
<div class="tabs">
|
||||
<button class="on">Content</button>
|
||||
<button>Compliance</button>
|
||||
<button>Newsfeed</button>
|
||||
<button>Community</button>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="banner banner-warning" style="margin: 12px 0">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>
|
||||
<div class="grow"><div class="bt">Due in 6 days</div><div class="bd">Required for all warehouse staff</div></div>
|
||||
</div>
|
||||
<div class="stack stack-sm" style="opacity: 0.5">
|
||||
<div class="lrow flat done"><div class="media is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></div><div class="grow"><div class="ltitle">Fire triangle basics</div></div></div>
|
||||
<div class="lrow flat done"><div class="media is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></div><div class="grow"><div class="ltitle">Extinguisher classes</div></div></div>
|
||||
<div class="lrow flat"><div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M8 5v14l11-7z"/></svg></div><div class="grow"><div class="ltitle">Evacuation routes</div></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scrim"></div>
|
||||
<div class="sheet" style="display: flex">
|
||||
<div class="grabber"></div>
|
||||
<div class="sheet-head">
|
||||
<div class="st">Download this course</div>
|
||||
<button class="icon-btn plain" style="margin-left: auto" aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
||||
</div>
|
||||
<div class="sheet-body">
|
||||
<div class="card card-pad" style="margin-bottom: 12px">
|
||||
<div class="kv"><span class="k">8 lessons · video, PDFs, transcripts</span><span class="v tnum">112 MB</span></div>
|
||||
<div class="kv"><span class="k">Free space on this device</span><span class="v tnum">3.4 GB</span></div>
|
||||
<div class="kv"><span class="k">Connection</span><span class="v">Wi-Fi</span></div>
|
||||
</div>
|
||||
|
||||
<div class="lrow" style="margin-bottom: 8px">
|
||||
<div class="grow">
|
||||
<div class="ltitle">Download over Wi-Fi only</div>
|
||||
<div class="lsub">Avoids using your mobile data</div>
|
||||
</div>
|
||||
<label class="switch"><input type="checkbox" checked /><span class="track"></span><span class="knob"></span></label>
|
||||
</div>
|
||||
|
||||
<div class="banner banner-muted" style="margin-bottom: 14px">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></svg>
|
||||
<div class="bd" style="color: var(--muted-foreground)">
|
||||
Lessons, video, and transcripts work offline. The knowledge check still needs a connection to submit.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary btn-lg btn-block">Download 112 MB</button>
|
||||
<button class="btn btn-ghost btn-block" style="margin-top: 6px">Not now</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Download sheet</b>Size, free space, and connection shown before committing, with the offline limits stated up front rather than discovered on a plane.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 3. COMPLETED -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="10:02"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<a class="icon-btn plain" href="learn.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
||||
<div class="grow"><div class="title-sm">Data Protection & GDPR Essentials</div></div>
|
||||
<button class="icon-btn" aria-label="Share"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"/><path d="m16 6-4-4-4 4M12 2v13"/></svg></button>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="card card-pad" style="margin: 14px 0 12px">
|
||||
<div class="row row-top">
|
||||
<div class="media lg is-done">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Course complete</div>
|
||||
<div class="lmeta"><span class="badge badge-success-soft">Finished 16 Aug 2026</span><span>Final score 92%</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<a class="btn btn-primary btn-block" href="certificates.html" style="margin-top: 14px">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="8" r="6"/><path d="M15.5 13.5 17 22l-5-3-5 3 1.5-8.5"/></svg>
|
||||
View your certificate
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="card card-pad" style="margin-bottom: 12px">
|
||||
<div class="kv"><span class="k">Lessons completed</span><span class="v tnum">10 / 10</span></div>
|
||||
<div class="kv"><span class="k">Exercises passed</span><span class="v tnum">3 / 3</span></div>
|
||||
<div class="kv"><span class="k">Time spent</span><span class="v">1h 12m</span></div>
|
||||
<div class="kv"><span class="k">Certificate ID</span><span class="v tnum" style="font-size: 12.5px">CIO-GDPR-4F91-2026</span></div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Keep the content</h2></div>
|
||||
<div class="lrow">
|
||||
<div class="media is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 15V3M7 10l5 5 5-5"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Downloaded · 96 MB</div>
|
||||
<div class="lsub">Review offline any time, or free the space</div>
|
||||
</div>
|
||||
<button class="btn btn-outline btn-sm">Remove</button>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Recommended next</h2></div>
|
||||
<a class="lrow" href="course.html">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Handling a Data Subject Request</div>
|
||||
<div class="lmeta"><span>4 lessons</span><span class="sep">·</span><span>~18 min</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Completed</b>Certificate issuance is the payoff, and the download stays until the learner removes it.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<button class="theme-toggle" title="Toggle theme" aria-label="Toggle theme">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
|
||||
</button>
|
||||
<script src="proto.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,212 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Downloads & sync — ClassroomIO Mobile</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300..800&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="app-theme.css" />
|
||||
<script>
|
||||
if (localStorage.cioTheme === 'dark' || (!localStorage.cioTheme && matchMedia('(prefers-color-scheme: dark)').matches))
|
||||
document.documentElement.classList.add('dark');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="proto-head">
|
||||
<a class="back" href="index.html"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="14" height="14"><path d="m15 18-6-6 6-6"/></svg> All screens</a>
|
||||
<span class="eyebrow">Learner · Platform</span>
|
||||
<h1>Downloads, storage & sync queue</h1>
|
||||
<p>
|
||||
Content is cached in SQLite plus the file system from one <code>GET /course/:courseId/offline-manifest</code> call.
|
||||
Writes go the other way: completion and playback position queue locally with an idempotency key and replay through
|
||||
<code>POST /course/:courseId/sync</code>. Completion is monotonic, so a retry can never corrupt a record — which is
|
||||
exactly why offline exercises are deferred to v2.
|
||||
</p>
|
||||
<div class="proto-links">
|
||||
<a href="lesson.html">← Offline lesson</a>
|
||||
<a href="course.html">Download a course →</a>
|
||||
<a href="profile.html">Profile →</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="stage">
|
||||
<!-- 1. DOWNLOADS -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:48"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<a class="icon-btn plain" href="profile.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
||||
<div class="grow"><div class="title">Downloads</div><div class="sub">3 courses · 284 MB used</div></div>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="card card-pad" style="margin: 10px 0 14px">
|
||||
<div class="row between" style="margin-bottom: 8px">
|
||||
<span class="eyebrow">Storage on this device</span>
|
||||
<span class="tnum" style="font-size: 12px; font-weight: 600">284 MB of 3.7 GB free</span>
|
||||
</div>
|
||||
<div style="display: flex; height: 8px; border-radius: 999px; overflow: hidden; background: var(--muted)">
|
||||
<span style="width: 30%; background: var(--primary)"></span>
|
||||
<span style="width: 22%; background: color-mix(in oklab, var(--primary) 55%, var(--muted))"></span>
|
||||
<span style="width: 12%; background: color-mix(in oklab, var(--primary) 30%, var(--muted))"></span>
|
||||
</div>
|
||||
<div class="row" style="gap: 12px; margin-top: 10px; flex-wrap: wrap">
|
||||
<span class="lmeta" style="margin: 0"><span style="width: 8px; height: 8px; border-radius: 2px; background: var(--primary); display: inline-block"></span> Forklift 84 MB</span>
|
||||
<span class="lmeta" style="margin: 0"><span style="width: 8px; height: 8px; border-radius: 2px; background: color-mix(in oklab, var(--primary) 55%, var(--muted)); display: inline-block"></span> Fire Safety 112 MB</span>
|
||||
<span class="lmeta" style="margin: 0"><span style="width: 8px; height: 8px; border-radius: 2px; background: color-mix(in oklab, var(--primary) 30%, var(--muted)); display: inline-block"></span> GDPR 88 MB</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head" style="margin-top: 0"><h2>Downloading</h2></div>
|
||||
<div class="card card-pad" style="margin-bottom: 4px">
|
||||
<div class="row row-top">
|
||||
<div class="media lg is-current"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 15V3M7 10l5 5 5-5"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Hazardous Materials Handling</div>
|
||||
<div class="lmeta"><span>Lesson 5 of 8</span><span class="sep">·</span><span class="tnum">48 MB of 96 MB</span></div>
|
||||
<div class="row" style="margin-top: 8px">
|
||||
<div class="progress"><span style="width: 50%"></span></div>
|
||||
<span class="text-muted tnum" style="font-size: 11px">50%</span>
|
||||
</div>
|
||||
</div>
|
||||
<button class="icon-btn plain" aria-label="Pause"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M6 4h4v16H6zM14 4h4v16h-4z"/></svg></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>On this device</h2><span class="count">3</span></div>
|
||||
<div class="stack">
|
||||
<div class="lrow">
|
||||
<div class="media lg is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Forklift Safety Recertification</div>
|
||||
<div class="lmeta"><span>12 lessons</span><span class="sep">·</span><span class="tnum">84 MB</span><span class="sep">·</span><span>Synced 2h ago</span></div>
|
||||
</div>
|
||||
<button class="icon-btn" aria-label="Remove download"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M3 6h18M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button>
|
||||
</div>
|
||||
<div class="lrow">
|
||||
<div class="media lg is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Warehouse Fire Safety</div>
|
||||
<div class="lmeta"><span>8 lessons</span><span class="sep">·</span><span class="tnum">112 MB</span><span class="sep">·</span><span>Synced 2h ago</span></div>
|
||||
</div>
|
||||
<button class="icon-btn" aria-label="Remove download"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M3 6h18M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button>
|
||||
</div>
|
||||
<div class="lrow">
|
||||
<div class="media lg is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Data Protection & GDPR Essentials</div>
|
||||
<div class="lmeta"><span>Completed</span><span class="sep">·</span><span class="tnum">88 MB</span></div>
|
||||
</div>
|
||||
<button class="icon-btn" aria-label="Remove download"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M3 6h18M8 6V4a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1v2M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"/></svg></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Settings</h2></div>
|
||||
<div class="card card-pad">
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Download over Wi-Fi only</div><div class="lsub">Protects your mobile data</div></div>
|
||||
<label class="switch"><input type="checkbox" checked /><span class="track"></span><span class="knob"></span></label>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Auto-download required training</div><div class="lsub">When new compliance courses are assigned</div></div>
|
||||
<label class="switch"><input type="checkbox" /><span class="track"></span><span class="knob"></span></label>
|
||||
</div>
|
||||
<div class="kv"><span class="k">Video quality offline</span><span class="v">Standard (480p)</span></div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-outline btn-block" style="margin-top: 12px; color: var(--destructive); border-color: color-mix(in oklab, var(--destructive) 35%, transparent)">
|
||||
Remove all downloads · 284 MB
|
||||
</button>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Downloads & storage</b>Per-course breakdown, resumable progress, and Wi-Fi-only by default. Nothing is silently evicted — the learner chose to download it.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 2. SYNC QUEUE -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="19:22"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<a class="icon-btn plain" href="downloads.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
||||
<div class="grow"><div class="title-sm">Sync queue</div><div class="sub">Back online · syncing</div></div>
|
||||
<button class="icon-btn" aria-label="Sync now"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M21 12a9 9 0 1 1-6.2-8.6"/><path d="M21 3v6h-6"/></svg></button>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="banner banner-info" style="margin: 12px 0 14px">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M21 12a9 9 0 1 1-6.2-8.6"/><path d="M21 3v6h-6"/></svg>
|
||||
<div class="grow">
|
||||
<div class="bt">Syncing 4 changes</div>
|
||||
<div class="bd">Recorded while you were offline. Your dashboard will match in a few seconds.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="stack">
|
||||
<div class="lrow">
|
||||
<div class="media is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Lesson 4 completed</div>
|
||||
<div class="lmeta"><span>Forklift Safety</span><span class="sep">·</span><span>Queued 18:31</span></div>
|
||||
</div>
|
||||
<span class="badge badge-success-soft">Synced</span>
|
||||
</div>
|
||||
<div class="lrow">
|
||||
<div class="media is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Lesson 5 completed</div>
|
||||
<div class="lmeta"><span>Forklift Safety</span><span class="sep">·</span><span>Queued 18:40</span></div>
|
||||
</div>
|
||||
<span class="badge badge-success-soft">Synced</span>
|
||||
</div>
|
||||
<div class="lrow current">
|
||||
<div class="media is-current"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M21 12a9 9 0 1 1-6.2-8.6"/><path d="M21 3v6h-6"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Playback position 7:02</div>
|
||||
<div class="lmeta"><span>Lesson 5</span><span class="sep">·</span><span>Queued 18:47</span></div>
|
||||
</div>
|
||||
<span class="badge badge-primary-soft">Syncing</span>
|
||||
</div>
|
||||
<div class="lrow">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 9v4M12 17h.01"/><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Lesson 6 completed</div>
|
||||
<div class="lmeta"><span>Retrying in 8s · attempt 2 of 5</span></div>
|
||||
</div>
|
||||
<button class="btn btn-outline btn-sm">Retry</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>What syncs offline</h2></div>
|
||||
<div class="card card-pad">
|
||||
<div class="kv"><span class="k">Lesson completion</span><span class="v"><span class="badge badge-success-soft">Queued offline</span></span></div>
|
||||
<div class="kv"><span class="k">Playback position</span><span class="v"><span class="badge badge-success-soft">Queued offline</span></span></div>
|
||||
<div class="kv"><span class="k">Exercise answers</span><span class="v"><span class="badge badge-muted">Online only</span></span></div>
|
||||
<div class="kv"><span class="k">Community posts</span><span class="v"><span class="badge badge-muted">Online only</span></span></div>
|
||||
</div>
|
||||
|
||||
<div class="hint" style="margin-top: 10px">
|
||||
Completion only ever moves forward, so replaying a queued change twice is harmless. Grading and submissions
|
||||
stay online to keep assessment records unambiguous.
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Sync queue</b>Every queued intent is visible with its state and retry count — the learner can see their work is safe rather than hoping.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<button class="theme-toggle" title="Toggle theme" aria-label="Toggle theme">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
|
||||
</button>
|
||||
<script src="proto.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,254 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Exercise — ClassroomIO Mobile</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300..800&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="app-theme.css" />
|
||||
<script>
|
||||
if (localStorage.cioTheme === 'dark' || (!localStorage.cioTheme && matchMedia('(prefers-color-scheme: dark)').matches))
|
||||
document.documentElement.classList.add('dark');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="proto-head">
|
||||
<a class="back" href="index.html"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="14" height="14"><path d="m15 18-6-6 6-6"/></svg> All screens</a>
|
||||
<span class="eyebrow">Learner · Assessment</span>
|
||||
<h1>Exercises</h1>
|
||||
<p>
|
||||
Touch renderers for all 16 question types in <code>packages/question-types</code> — including the ones the web
|
||||
takes for granted: <code>MATCHING</code> and <code>ORDERING</code> as drag, <code>HOTSPOT</code> as tap,
|
||||
<code>FILE_UPLOAD</code> through the camera, and <code>VIDEO_RECORDING</code> in-app. Answers autosave locally so a
|
||||
phone call never costs an attempt; media goes up through the existing presigned-upload route.
|
||||
</p>
|
||||
<div class="proto-links">
|
||||
<a href="lesson.html">← Lesson</a>
|
||||
<a href="tutor-grading.html">How it's graded →</a>
|
||||
<a href="certificates.html">Certificate →</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="stage">
|
||||
<!-- 1. IN PROGRESS -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:52"></div>
|
||||
|
||||
<div class="navbar bordered">
|
||||
<button class="icon-btn plain" aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
||||
<div class="grow">
|
||||
<div class="title-sm">Section 2 check</div>
|
||||
<div class="sub">Question 3 of 8 · no time limit</div>
|
||||
</div>
|
||||
<span class="badge badge-muted">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg>
|
||||
Saved
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="row" style="margin: 12px 0 18px">
|
||||
<div class="segs">
|
||||
<span class="seg done"></span><span class="seg done"></span><span class="seg now"></span><span class="seg"></span>
|
||||
<span class="seg"></span><span class="seg"></span><span class="seg"></span><span class="seg"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="eyebrow">Single choice · 2 points</div>
|
||||
<div style="font-size: 17px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.4; margin: 8px 0 16px">
|
||||
You find a wet, shiny film on a hydraulic ram that reappears after wiping. What do you do?
|
||||
</div>
|
||||
|
||||
<div class="stack" data-select style="margin-bottom: 18px">
|
||||
<label class="opt" data-option>
|
||||
<span class="mark"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></span>
|
||||
<span>Top up the hydraulic fluid and continue the shift</span>
|
||||
</label>
|
||||
<label class="opt on" data-option>
|
||||
<span class="mark"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></span>
|
||||
<span>Tag the truck out of service and report the fault</span>
|
||||
</label>
|
||||
<label class="opt" data-option>
|
||||
<span class="mark"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></span>
|
||||
<span>Note it on the log and check again at the end of the shift</span>
|
||||
</label>
|
||||
<label class="opt" data-option>
|
||||
<span class="mark"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></span>
|
||||
<span>Wipe it down and only escalate if the load feels unstable</span>
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="banner banner-muted">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></svg>
|
||||
<div class="bd" style="color: var(--muted-foreground)">
|
||||
Your answers save on this device as you go. If you get interrupted, you'll pick up exactly here.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" style="gap: 8px; margin-top: 18px">
|
||||
<button class="btn btn-outline btn-lg" style="flex: 1">Back</button>
|
||||
<button class="btn btn-primary btn-lg" style="flex: 2">Next question</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>In progress</b>One question per screen with a segment bar and a persistent "Saved" chip — the autosave promise made visible.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 2. RICH TYPES -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:58"></div>
|
||||
|
||||
<div class="navbar bordered">
|
||||
<button class="icon-btn plain" aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
||||
<div class="grow">
|
||||
<div class="title-sm">Section 2 check</div>
|
||||
<div class="sub">Question 7 of 8</div>
|
||||
</div>
|
||||
<span class="badge badge-muted"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg> Saved</span>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="row" style="margin: 12px 0 18px">
|
||||
<div class="segs">
|
||||
<span class="seg done"></span><span class="seg done"></span><span class="seg done"></span><span class="seg done"></span>
|
||||
<span class="seg done"></span><span class="seg done"></span><span class="seg now"></span><span class="seg"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="eyebrow">Photo evidence · 5 points</div>
|
||||
<div style="font-size: 17px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.4; margin: 8px 0 6px">
|
||||
Photograph the data plate on the truck you're authorized to drive.
|
||||
</div>
|
||||
<div class="lsub" style="margin-bottom: 14px">Your tutor checks that the rated capacity matches your authorization.</div>
|
||||
|
||||
<div class="card card-pad" style="margin-bottom: 10px">
|
||||
<div class="row">
|
||||
<div class="thumb" style="background: linear-gradient(135deg, oklch(0.556 0 0), oklch(0.3 0 0)); width: 64px; height: 48px">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="3" y="6" width="18" height="14" rx="2"/><circle cx="12" cy="13" r="3.5"/><path d="M8 6l1.5-2h5L16 6"/></svg>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">IMG_4192.jpg</div>
|
||||
<div class="lmeta"><span>2.4 MB</span><span class="sep">·</span><span>Uploading…</span></div>
|
||||
<div class="row" style="margin-top: 6px"><div class="progress"><span style="width: 62%"></span></div><span class="text-muted tnum" style="font-size: 11px">62%</span></div>
|
||||
</div>
|
||||
<button class="icon-btn plain" aria-label="Cancel upload"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" style="gap: 8px; margin-bottom: 18px">
|
||||
<button class="btn btn-outline" style="flex: 1">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="3" y="6" width="18" height="14" rx="2"/><circle cx="12" cy="13" r="3.5"/><path d="M8 6l1.5-2h5L16 6"/></svg>
|
||||
Camera
|
||||
</button>
|
||||
<button class="btn btn-outline" style="flex: 1">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/></svg>
|
||||
Files
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="eyebrow">Also in this exercise</div>
|
||||
<div class="stack stack-sm" style="margin-top: 8px">
|
||||
<div class="lrow flat">
|
||||
<div class="media is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Match the hazard to its control</div><div class="lmeta"><span>Drag to match · answered</span></div></div>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="media is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Tap the blind spot on the diagram</div><div class="lmeta"><span>Hotspot · answered</span></div></div>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m22 8-6 4 6 4V8z"/><rect x="2" y="6" width="14" height="12" rx="2"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Record your 60-second handover</div><div class="lmeta"><span>Video · not started</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary btn-lg btn-block" style="margin-top: 18px">Next question</button>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Media & rich question types</b>Camera capture with visible upload progress and retry, plus drag, hotspot, and in-app video recording — the types a phone does better than a laptop.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 3. GRADED -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="14:07"></div>
|
||||
|
||||
<div class="navbar bordered">
|
||||
<a class="icon-btn plain" href="course.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
||||
<div class="grow"><div class="title-sm">Section 2 check · Result</div></div>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="card card-pad" style="margin: 14px 0 12px; text-align: center">
|
||||
<div class="ring lg" style="margin: 0 auto 12px; width: 92px; height: 92px">
|
||||
<svg viewBox="0 0 36 36"><circle class="track" cx="18" cy="18" r="15.5" fill="none" stroke-width="3"/><circle class="arc" cx="18" cy="18" r="15.5" fill="none" stroke-width="3" stroke-linecap="round" stroke-dasharray="97.4" stroke-dashoffset="12.7"/></svg>
|
||||
<span class="lbl tnum" style="font-size: 20px">87%</span>
|
||||
</div>
|
||||
<div style="font-size: 17px; font-weight: 600; letter-spacing: -0.02em">Passed</div>
|
||||
<div class="lsub" style="margin-top: 3px">26 of 30 points · pass mark 70%</div>
|
||||
<div class="row" style="justify-content: center; gap: 6px; margin-top: 10px">
|
||||
<span class="badge badge-success-soft">Graded by Daniel Mensah</span>
|
||||
<span class="badge badge-outline">2h ago</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-pad" style="margin-bottom: 12px">
|
||||
<div class="eyebrow" style="margin-bottom: 8px">Tutor feedback</div>
|
||||
<div style="font-size: 13.5px; line-height: 1.55">
|
||||
Solid inspection sequence and the tag-out call was right. Your handover recording skipped the battery state —
|
||||
mention it next time, it's the one item the incoming driver can't check quickly.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Question breakdown</h2></div>
|
||||
<div class="stack stack-sm">
|
||||
<div class="lrow flat">
|
||||
<div class="media is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Q3 · Hydraulic fault response</div><div class="lmeta"><span>2 / 2</span></div></div>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="media is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Q5 · Match hazard to control</div><div class="lmeta"><span>4 / 4</span></div></div>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="media is-warning"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 9v4M12 17h.01"/><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Q8 · Handover recording</div>
|
||||
<div class="lmeta"><span>4 / 8</span><span class="sep">·</span><span>Missing battery state</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<a class="btn btn-primary btn-lg btn-block" href="course.html">Continue to Section 3</a>
|
||||
<button class="btn btn-outline btn-block" style="margin-top: 8px">Retake for a higher score</button>
|
||||
<div class="hint" style="text-align: center">1 retake remaining · your highest score counts</div>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Graded</b>Score, tutor comment, and a per-question breakdown that names what was missing — feedback the learner can act on without opening a laptop.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<button class="theme-toggle" title="Toggle theme" aria-label="Toggle theme">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
|
||||
</button>
|
||||
<script src="proto.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,353 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Home — ClassroomIO Mobile</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300..800&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="app-theme.css" />
|
||||
<script>
|
||||
if (localStorage.cioTheme === 'dark' || (!localStorage.cioTheme && matchMedia('(prefers-color-scheme: dark)').matches))
|
||||
document.documentElement.classList.add('dark');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="proto-head">
|
||||
<a class="back" href="index.html"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="14" height="14"><path d="m15 18-6-6 6-6"/></svg> All screens</a>
|
||||
<span class="eyebrow">Learner · Tab 1</span>
|
||||
<h1>Home — the attention surface</h1>
|
||||
<p>
|
||||
Ordered by urgency, not recency: compliance alerts, then continue-learning, due-soon, newly assigned, announcements,
|
||||
and feedback. The whole screen is one <code>GET /account/home-digest</code> call so a cold launch on a weak
|
||||
connection costs a single round trip — and the cached copy still renders offline with an explicit staleness chip.
|
||||
</p>
|
||||
<div class="proto-links">
|
||||
<a href="compliance.html">Compliance →</a>
|
||||
<a href="lesson.html">Continue lesson →</a>
|
||||
<a href="exercise.html">Due exercise →</a>
|
||||
<a href="notifications.html">Notifications →</a>
|
||||
<a href="learn.html">Learn tab →</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="stage">
|
||||
<!-- 1. DEFAULT — WORK TO DO -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:41"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<div class="avatar brand">NL</div>
|
||||
<div class="grow">
|
||||
<div class="sub">Northwind Logistics</div>
|
||||
<div class="title-sm">Good morning, Amara</div>
|
||||
</div>
|
||||
<a class="icon-btn plain" href="notifications.html" aria-label="Notifications">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/><path d="M18 8a6 6 0 1 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/></svg>
|
||||
<span class="ping"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<a class="banner banner-danger" href="compliance.html">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 9v4M12 17h.01"/><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0"/></svg>
|
||||
<div class="grow">
|
||||
<div class="bt">1 training overdue</div>
|
||||
<div class="bd">Forklift Safety Recertification was due 3 days ago · 4 days of grace left</div>
|
||||
</div>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" style="width: 15px; height: 15px; margin-top: 3px"><path d="m9 18 6-6-6-6"/></svg>
|
||||
</a>
|
||||
|
||||
<!-- continue -->
|
||||
<div class="sec-head"><h2>Continue learning</h2></div>
|
||||
<a class="card" href="lesson.html" style="display: block; overflow: hidden">
|
||||
<div style="display: flex; gap: 12px; padding: 12px">
|
||||
<div class="thumb" style="background: linear-gradient(135deg, oklch(0.488 0.243 264.376), oklch(0.546 0.245 262.881))">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M8 5v14l11-7z"/></svg>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="lsub" style="margin: 0">Forklift Safety Recertification 2026</div>
|
||||
<div class="ltitle" style="margin-top: 2px">Pre-operation inspection checklist</div>
|
||||
<div class="lmeta"><span>Lesson 4 of 12</span><span class="sep">·</span><span>6 min left</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 0 12px 12px">
|
||||
<div class="row">
|
||||
<div class="progress"><span style="width: 33%"></span></div>
|
||||
<span class="text-muted tnum" style="font-size: 11.5px">33%</span>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<!-- due soon -->
|
||||
<div class="sec-head"><h2>Due soon</h2><span class="count">4</span><a class="more" href="learn.html">See all</a></div>
|
||||
<div class="stack">
|
||||
<a class="lrow" href="exercise.html">
|
||||
<div class="media is-danger">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 9v4M12 17h.01"/><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0"/></svg>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Forklift Safety Recertification</div>
|
||||
<div class="lmeta"><span class="badge badge-danger-soft">Overdue 3 days</span><span class="badge badge-outline">Required</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
<a class="lrow" href="certificates.html">
|
||||
<div class="media is-warning"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Hazardous Materials Handling</div>
|
||||
<div class="lmeta"><span class="badge badge-warning-soft">Expires in 21 days</span><span>Renewal opens 8 Sep</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
<a class="lrow" href="exercise.html">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Module 3 knowledge check</div>
|
||||
<div class="lmeta"><span>Data Protection & GDPR Essentials</span><span class="sep">·</span><span>Due in 2 days</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
<a class="lrow" href="course.html">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Warehouse Fire Safety</div>
|
||||
<div class="lmeta"><span>Due in 6 days</span><span class="sep">·</span><span>2 of 8 lessons done</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- newly assigned -->
|
||||
<div class="sec-head"><h2>Newly assigned</h2></div>
|
||||
<div class="card card-pad">
|
||||
<div class="row row-top">
|
||||
<div class="media lg is-current"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Hazardous Materials Handling</div>
|
||||
<div class="lsub">Assigned by Daniel Mensah · 8 lessons · ~45 min</div>
|
||||
<div class="lmeta"><span class="badge badge-outline">Required</span><span class="badge badge-outline">Deadline 30 Sep</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn btn-primary btn-block btn-sm" style="margin-top: 12px; height: 36px">Start course</button>
|
||||
</div>
|
||||
|
||||
<!-- announcements -->
|
||||
<div class="sec-head"><h2>Cohort announcements</h2><a class="more" href="community.html">See all</a></div>
|
||||
<a class="card card-pad" href="community.html" style="display: block">
|
||||
<div class="row row-top">
|
||||
<div class="avatar">DM</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle" style="font-size: 13.5px">Daniel Mensah</div>
|
||||
<div class="lsub" style="margin-top: 3px">
|
||||
Reminder: the practical forklift assessment moves to Bay 4 this Thursday. Bring your completion code from
|
||||
Lesson 12.
|
||||
</div>
|
||||
<div class="lmeta"><span>Q3 Frontline Onboarding</span><span class="sep">·</span><span>2h ago</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<!-- feedback -->
|
||||
<div class="sec-head"><h2>Recent feedback</h2></div>
|
||||
<a class="lrow" href="exercise.html">
|
||||
<div class="media is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Incident reporting scenario</div>
|
||||
<div class="lsub">"Clear escalation path — nice work on step 3."</div>
|
||||
<div class="lmeta"><span class="badge badge-success-soft">18 / 20 · Passed</span><span>Graded yesterday</span></div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<nav class="tabbar" data-tabbar="home" data-badges="compliance:dot"></nav>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Default — work to do</b>Overdue compliance outranks everything. Every card is a deep-link target for a push notification.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 2. CAUGHT UP -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:41"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<div class="avatar brand">NL</div>
|
||||
<div class="grow">
|
||||
<div class="sub">Northwind Logistics</div>
|
||||
<div class="title-sm">Good morning, Amara</div>
|
||||
</div>
|
||||
<a class="icon-btn plain" href="notifications.html" aria-label="Notifications">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/><path d="M18 8a6 6 0 1 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/></svg>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<a class="banner banner-muted" href="compliance.html">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/><path d="m9 12 2 2 4-4"/></svg>
|
||||
<div class="grow">
|
||||
<div class="bt">All 6 requirements compliant</div>
|
||||
<div class="bd">Next renewal: Hazmat Handling on 15 Feb 2027</div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<div class="empty" style="padding: 34px 22px 26px">
|
||||
<div class="ei">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg>
|
||||
</div>
|
||||
<div class="et">Nothing due — you're all caught up</div>
|
||||
<div class="ed">No overdue training, no deadlines this week. We'll notify you when something new is assigned.</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-pad" style="margin-bottom: 12px">
|
||||
<div class="row" style="align-items: stretch">
|
||||
<div class="stat"><div class="v tnum">9</div><div class="k">Completed</div></div>
|
||||
<div style="width: 1px; background: var(--border)"></div>
|
||||
<div class="stat"><div class="v tnum">6</div><div class="k">Certificates</div></div>
|
||||
<div style="width: 1px; background: var(--border)"></div>
|
||||
<div class="stat"><div class="v tnum">14</div><div class="k">Day streak</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Keep going</h2><a class="more" href="learn.html">Explore</a></div>
|
||||
<div class="stack">
|
||||
<a class="lrow" href="course.html">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Customer Service Excellence</div>
|
||||
<div class="lmeta"><span class="badge badge-outline">Optional</span><span>6 lessons · ~30 min</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
<a class="lrow" href="certificates.html">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="8" r="6"/><path d="M15.5 13.5 17 22l-5-3-5 3 1.5-8.5"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Your certificate wallet</div>
|
||||
<div class="lmeta"><span>6 certificates · 1 expiring soon</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="tabbar" data-tabbar="home"></nav>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Caught up</b>The reward state. A muted status line replaces the overdue alert, the urgency stack collapses to an empty state, and the screen shifts to optional learning.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 3. OFFLINE + PUSH -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="18:12" data-offline></div>
|
||||
|
||||
<!-- push notification preview -->
|
||||
<div class="push">
|
||||
<div class="pi">C</div>
|
||||
<div class="grow">
|
||||
<div class="row between">
|
||||
<div class="pt">Deadline reminder</div>
|
||||
<div class="pw">now</div>
|
||||
</div>
|
||||
<div class="pb">Forklift Safety Recertification is due tomorrow. 8 lessons left — about 20 minutes.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="offline-strip">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M2 2 22 22M8.5 16.5a5 5 0 0 1 7 0M5 13a10 10 0 0 1 4-2.4M12 20h.01"/></svg>
|
||||
Offline — showing your last sync
|
||||
</div>
|
||||
|
||||
<div class="navbar" style="margin-top: 54px">
|
||||
<div class="avatar brand">NL</div>
|
||||
<div class="grow">
|
||||
<div class="sub">Northwind Logistics</div>
|
||||
<div class="title-sm">Good evening, Amara</div>
|
||||
</div>
|
||||
<a class="icon-btn plain" href="notifications.html" aria-label="Notifications">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/><path d="M18 8a6 6 0 1 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/></svg>
|
||||
<span class="ping"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="row" style="margin-bottom: 10px">
|
||||
<span class="badge badge-warning-soft">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>
|
||||
Last updated 2h ago
|
||||
</span>
|
||||
<a class="badge badge-outline" href="downloads.html">2 changes waiting to sync</a>
|
||||
</div>
|
||||
|
||||
<div class="sec-head" style="margin-top: 6px"><h2>Available offline</h2></div>
|
||||
<div class="stack">
|
||||
<a class="card" href="lesson.html" style="display: block; overflow: hidden">
|
||||
<div style="display: flex; gap: 12px; padding: 12px">
|
||||
<div class="thumb" style="background: linear-gradient(135deg, oklch(0.488 0.243 264.376), oklch(0.546 0.245 262.881))">
|
||||
<svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M8 5v14l11-7z"/></svg>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="lsub" style="margin: 0">Forklift Safety Recertification 2026</div>
|
||||
<div class="ltitle" style="margin-top: 2px">Load stability and centre of gravity</div>
|
||||
<div class="lmeta">
|
||||
<span class="dl-dot"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></span>
|
||||
<span>Downloaded</span><span class="sep">·</span><span>Lesson 5 of 12</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 0 12px 12px">
|
||||
<div class="row"><div class="progress"><span style="width: 42%"></span></div><span class="text-muted tnum" style="font-size: 11.5px">42%</span></div>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
<a class="lrow" href="course.html">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 15V3M7 10l5 5 5-5"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Warehouse Fire Safety</div>
|
||||
<div class="lmeta"><span>8 lessons downloaded</span><span class="sep">·</span><span>112 MB</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Needs a connection</h2></div>
|
||||
<div class="stack">
|
||||
<div class="lrow locked">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Module 3 knowledge check</div>
|
||||
<div class="lsub">Exercises submit online only — we'll remind you when you reconnect</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lrow locked">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22z"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Community & announcements</div>
|
||||
<div class="lsub">Reading cached posts · posting disabled offline</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="tabbar" data-tabbar="home"></nav>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Offline + incoming push</b>The cached digest with an explicit staleness chip. Anything that can't work offline is listed and explained rather than silently failing.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<button class="theme-toggle" title="Toggle theme" aria-label="Toggle theme">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
|
||||
</button>
|
||||
<script src="proto.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,345 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>ClassroomIO Mobile App — Prototype Map</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300..800&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="app-theme.css" />
|
||||
<script>
|
||||
if (localStorage.cioTheme === 'dark' || (!localStorage.cioTheme && matchMedia('(prefers-color-scheme: dark)').matches))
|
||||
document.documentElement.classList.add('dark');
|
||||
</script>
|
||||
<style>
|
||||
.page {
|
||||
max-width: 1040px;
|
||||
margin: 0 auto;
|
||||
padding: 56px 24px 80px;
|
||||
}
|
||||
h1 {
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.03em;
|
||||
margin: 8px 0 10px;
|
||||
}
|
||||
.lead {
|
||||
color: var(--muted-foreground);
|
||||
font-size: 15px;
|
||||
max-width: 74ch;
|
||||
margin: 0 0 12px;
|
||||
line-height: 1.65;
|
||||
}
|
||||
.lead b {
|
||||
color: var(--foreground);
|
||||
font-weight: 500;
|
||||
}
|
||||
.lead code {
|
||||
background: var(--muted);
|
||||
color: var(--foreground);
|
||||
padding: 1px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
.sec {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted-foreground);
|
||||
margin: 38px 0 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
.sec::after {
|
||||
content: '';
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: var(--border);
|
||||
}
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 14px;
|
||||
}
|
||||
.shot {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
transition: border-color 0.15s, box-shadow 0.15s;
|
||||
}
|
||||
.shot:hover {
|
||||
border-color: var(--ring);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
.shot .cover {
|
||||
height: 72px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
color: #fff;
|
||||
}
|
||||
.shot .cover svg {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
stroke-width: 1.8;
|
||||
}
|
||||
.shot .b {
|
||||
padding: 13px 15px 15px;
|
||||
}
|
||||
.shot .n {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: var(--muted-foreground);
|
||||
letter-spacing: 0.05em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.shot .t {
|
||||
font-size: 14.5px;
|
||||
font-weight: 600;
|
||||
letter-spacing: -0.01em;
|
||||
margin: 3px 0 4px;
|
||||
}
|
||||
.shot .d {
|
||||
font-size: 12.5px;
|
||||
color: var(--muted-foreground);
|
||||
margin: 0;
|
||||
line-height: 1.5;
|
||||
}
|
||||
.foot {
|
||||
margin-top: 46px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid var(--border);
|
||||
color: var(--muted-foreground);
|
||||
font-size: 13.5px;
|
||||
line-height: 1.75;
|
||||
}
|
||||
.foot b {
|
||||
color: var(--foreground);
|
||||
font-weight: 500;
|
||||
}
|
||||
.foot code {
|
||||
background: var(--muted);
|
||||
padding: 1px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
@media (max-width: 900px) {
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="page">
|
||||
<span class="eyebrow" style="font-size: 12px; letter-spacing: 0.1em">ClassroomIO · Prototype + PRD handoff</span>
|
||||
<h1>Mobile App</h1>
|
||||
<p class="lead">
|
||||
A <b>learner-first</b> iOS and Android app for the people companies actually train — frontline staff, field
|
||||
technicians, and distributed employees who often have no work laptop and unreliable connectivity. It opens on
|
||||
<b>what needs your attention</b>, works <b>offline</b>, and finally gives ClassroomIO a <b>push channel</b> for
|
||||
deadlines and renewals. A small tutor mode handles grading and attendance on the go; authoring and org
|
||||
administration stay on the web.
|
||||
</p>
|
||||
<p class="lead" style="font-size: 13.5px">
|
||||
Built on the real <code>packages/ui</code> tokens, rendered in a 390 × 844 device frame. Every page has a theme
|
||||
toggle and cross-links so you can click through each journey end to end. The engineering PRD lives at
|
||||
<code>prd/mobile-app/README.md</code>.
|
||||
</p>
|
||||
|
||||
<!-- ENTRY -->
|
||||
<div class="sec">1 · Entry — sign in & organization</div>
|
||||
<div class="grid">
|
||||
<a class="card shot" href="auth.html">
|
||||
<div class="cover" style="background: linear-gradient(135deg, oklch(0.488 0.243 264.376), oklch(0.623 0.214 259.815))">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
|
||||
</div>
|
||||
<div class="b">
|
||||
<div class="n">Auth · 3 states</div>
|
||||
<div class="t">Sign in, org picker, biometric</div>
|
||||
<p class="d">Password, Google, and enterprise SSO through Better Auth's Expo client; custom server URL for self-hosted; Face ID re-entry and an offline launch path.</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="card shot" href="home.html">
|
||||
<div class="cover" style="background: linear-gradient(135deg, oklch(0.546 0.245 262.881), oklch(0.488 0.243 264.376))">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m3 10 9-7 9 7v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/></svg>
|
||||
</div>
|
||||
<div class="b">
|
||||
<div class="n">Tab 1 · 3 states</div>
|
||||
<div class="t">Home — attention surface</div>
|
||||
<p class="d">Compliance alerts, continue-learning, due-soon, assignments, announcements, feedback — one <code>/account/home-digest</code> call. Plus caught-up and offline states.</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="card shot" href="notifications.html">
|
||||
<div class="cover" style="background: linear-gradient(135deg, oklch(0.623 0.214 259.815), oklch(0.546 0.245 262.881))">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/><path d="M18 8a6 6 0 1 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/></svg>
|
||||
</div>
|
||||
<div class="b">
|
||||
<div class="n">Retention · 3 states</div>
|
||||
<div class="t">Notifications</div>
|
||||
<p class="d">Permission pre-prompt, the notification center with deep links, and per-category preferences with quiet hours.</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- LEARNING -->
|
||||
<div class="sec">2 · Learner — the consumption path</div>
|
||||
<div class="grid">
|
||||
<a class="card shot" href="learn.html">
|
||||
<div class="cover" style="background: linear-gradient(135deg, oklch(0.424 0.199 265.638), oklch(0.488 0.243 264.376))">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg>
|
||||
</div>
|
||||
<div class="b">
|
||||
<div class="n">Tab 2 · 3 states</div>
|
||||
<div class="t">Learn</div>
|
||||
<p class="d">My learning with progress rings and download markers, the org catalog with enroll-in-place, and cohorts with goal progress.</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="card shot" href="course.html">
|
||||
<div class="cover" style="background: linear-gradient(135deg, oklch(0.685 0.169 237.323), oklch(0.488 0.243 264.376))">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>
|
||||
</div>
|
||||
<div class="b">
|
||||
<div class="n">Detail · 3 states</div>
|
||||
<div class="t">Course outline</div>
|
||||
<p class="d">Complete / current / locked / downloaded on every row, a deadline banner for compliance courses, the download sheet, and the completed state.</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="card shot" href="lesson.html">
|
||||
<div class="cover" style="background: linear-gradient(135deg, oklch(0.424 0.199 265.638), oklch(0.546 0.245 262.881))">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m22 8-6 4 6 4V8z"/><rect x="2" y="6" width="14" height="12" rx="2"/></svg>
|
||||
</div>
|
||||
<div class="b">
|
||||
<div class="n">Player · 3 states</div>
|
||||
<div class="t">Lesson</div>
|
||||
<p class="d">HLS video with a playback token, transcript and AI-tutor sheets, the fair-use cap state, and offline playback with a queued completion.</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="card shot" href="exercise.html">
|
||||
<div class="cover" style="background: linear-gradient(135deg, oklch(0.488 0.243 264.376), oklch(0.623 0.214 259.815))">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>
|
||||
</div>
|
||||
<div class="b">
|
||||
<div class="n">Assessment · 3 states</div>
|
||||
<div class="t">Exercises</div>
|
||||
<p class="d">Autosaving single-choice, camera capture with upload progress, drag/hotspot/video types, and the graded result with a per-question breakdown.</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="card shot" href="compliance.html">
|
||||
<div class="cover" style="background: linear-gradient(135deg, oklch(0.37 0.044 257.287), oklch(0.488 0.243 264.376))">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/><path d="m9 12 2 2 4-4"/></svg>
|
||||
</div>
|
||||
<div class="b">
|
||||
<div class="n">Tab 3 · 2 states</div>
|
||||
<div class="t">Compliance</div>
|
||||
<p class="d">The differentiator as a tab: overdue with grace dates and consequences, expiring certifications, waivers, and the all-clear renewal timeline.</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="card shot" href="certificates.html">
|
||||
<div class="cover" style="background: linear-gradient(135deg, oklch(0.685 0.169 237.323), oklch(0.424 0.199 265.638))">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="8" r="6"/><path d="M15.5 13.5 17 22l-5-3-5 3 1.5-8.5"/></svg>
|
||||
</div>
|
||||
<div class="b">
|
||||
<div class="n">Proof · 2 states</div>
|
||||
<div class="t">Certificate wallet</div>
|
||||
<p class="d">Issued certificates sorted by what needs attention, plus the rendered certificate with share, save, and a verification ID/QR.</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- PLATFORM -->
|
||||
<div class="sec">3 · Learner — engagement & platform</div>
|
||||
<div class="grid">
|
||||
<a class="card shot" href="community.html">
|
||||
<div class="cover" style="background: linear-gradient(135deg, oklch(0.488 0.243 264.376), oklch(0.685 0.169 237.323))">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22z"/></svg>
|
||||
</div>
|
||||
<div class="b">
|
||||
<div class="n">Tab 4 · 3 states</div>
|
||||
<div class="t">Community & newsfeed</div>
|
||||
<p class="d">Mentions and followed threads, a Q&A thread with the tutor answer promoted, and the offline read-only state with compose disabled.</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="card shot" href="downloads.html">
|
||||
<div class="cover" style="background: linear-gradient(135deg, oklch(0.556 0 0), oklch(0.488 0.243 264.376))">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 15V3M7 10l5 5 5-5"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/></svg>
|
||||
</div>
|
||||
<div class="b">
|
||||
<div class="n">Offline · 2 states</div>
|
||||
<div class="t">Downloads & sync queue</div>
|
||||
<p class="d">Storage breakdown, resumable downloads, Wi-Fi-only defaults, and every queued write with its sync state and retry count.</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="card shot" href="profile.html">
|
||||
<div class="cover" style="background: linear-gradient(135deg, oklch(0.546 0.245 262.881), oklch(0.685 0.169 237.323))">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/></svg>
|
||||
</div>
|
||||
<div class="b">
|
||||
<div class="n">Tab 5 · 2 states</div>
|
||||
<div class="t">Profile & settings</div>
|
||||
<p class="d">Learner record, org switcher sheet, app settings and biometric lock — plus frontline mode, QR launch, and the accessibility commitments.</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- TUTOR -->
|
||||
<div class="sec">4 · Tutor companion — role-aware, same binary</div>
|
||||
<div class="grid">
|
||||
<a class="card shot" href="tutor-grading.html">
|
||||
<div class="cover" style="background: linear-gradient(135deg, oklch(0.424 0.199 265.638), oklch(0.546 0.245 262.881))">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6M9 15l2 2 4-4"/></svg>
|
||||
</div>
|
||||
<div class="b">
|
||||
<div class="n">Teach · 2 states</div>
|
||||
<div class="t">Grading queue</div>
|
||||
<p class="d">Submissions across all courses, oldest first, and a review screen with answer, video, rubric, score, and recorded audio feedback.</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="card shot" href="tutor-attendance.html">
|
||||
<div class="cover" style="background: linear-gradient(135deg, oklch(0.488 0.243 264.376), oklch(0.546 0.245 262.881))">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18M9 16l2 2 4-4"/></svg>
|
||||
</div>
|
||||
<div class="b">
|
||||
<div class="n">Teach · 2 states</div>
|
||||
<div class="t">Attendance</div>
|
||||
<p class="d">One-tap Present / Late / Absent on the roster, plus a rotating QR check-in code so learners mark themselves.</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="card shot" href="tutor-course.html">
|
||||
<div class="cover" style="background: linear-gradient(135deg, oklch(0.685 0.169 237.323), oklch(0.488 0.243 264.376))">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M3 3v16a2 2 0 0 0 2 2h16"/><path d="m7 15 4-4 3 3 5-6"/></svg>
|
||||
</div>
|
||||
<div class="b">
|
||||
<div class="n">Teach · 2 states</div>
|
||||
<div class="t">Course snapshot & moderation</div>
|
||||
<p class="d">Completion, overdue learners, and where people get stuck; post an announcement; handle unanswered and reported posts.</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="foot">
|
||||
<p style="margin: 0 0 10px">
|
||||
<b>Explicitly not in the app.</b> Course, lesson, and exercise authoring; the media manager and AI course builder;
|
||||
org settings, team management, billing, SSO configuration, custom domains, and API keys; live-class video hosting;
|
||||
SCORM playback; and <b>any purchase flow at all</b> — no checkout, no plan upgrade, no AI token packs, on any
|
||||
storefront.
|
||||
</p>
|
||||
<p style="margin: 0 0 10px">
|
||||
<b>Hard dependency.</b> Push notifications ride on <code>prd/notification-system</code>. Mobile contributes a
|
||||
device-token registry and an Expo Push transport in the existing <code>apps/jobs</code> worker — never a second
|
||||
notification pipeline.
|
||||
</p>
|
||||
<p style="margin: 0">
|
||||
<b>Offline scope.</b> v1 caches content and queues two monotonic writes (lesson completion, playback position).
|
||||
Exercises, submissions, and grading stay online so no assessment record can be corrupted by a replay — the same
|
||||
line Canvas and Blackboard draw.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="theme-toggle" title="Toggle theme" aria-label="Toggle theme">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
|
||||
</button>
|
||||
<script src="proto.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,329 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Learn — ClassroomIO Mobile</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300..800&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="app-theme.css" />
|
||||
<script>
|
||||
if (localStorage.cioTheme === 'dark' || (!localStorage.cioTheme && matchMedia('(prefers-color-scheme: dark)').matches))
|
||||
document.documentElement.classList.add('dark');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="proto-head">
|
||||
<a class="back" href="index.html"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="14" height="14"><path d="m15 18-6-6 6-6"/></svg> All screens</a>
|
||||
<span class="eyebrow">Learner · Tab 2</span>
|
||||
<h1>Learn — my learning, explore, cohorts</h1>
|
||||
<p>
|
||||
Everything the learner is enrolled in, everything the organization has published, and the cohorts they belong to.
|
||||
Course rows carry a progress ring, a type chip (Compliance / Cohort / Self-paced), and a download marker.
|
||||
Explore enrolls in place for assigned and free courses — there is deliberately no checkout anywhere in the app.
|
||||
</p>
|
||||
<div class="proto-links">
|
||||
<a href="course.html">Course detail →</a>
|
||||
<a href="downloads.html">Downloads →</a>
|
||||
<a href="home.html">← Home</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="stage">
|
||||
<!-- 1. MY LEARNING -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:41"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<div class="title">Learn</div>
|
||||
<div class="actions">
|
||||
<button class="icon-btn" aria-label="Search"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg></button>
|
||||
<a class="icon-btn" href="downloads.html" aria-label="Downloads"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 15V3M7 10l5 5 5-5"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/></svg></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tabs" data-select>
|
||||
<button class="on" data-option>My learning</button>
|
||||
<button data-option>Explore</button>
|
||||
<button data-option>Cohorts</button>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="segmented" data-select style="margin: 12px 0 14px">
|
||||
<button class="on" data-option>In progress</button>
|
||||
<button data-option>Not started</button>
|
||||
<button data-option>Completed</button>
|
||||
</div>
|
||||
|
||||
<div class="stack">
|
||||
<a class="lrow current" href="course.html">
|
||||
<div class="ring">
|
||||
<svg viewBox="0 0 36 36"><circle class="track" cx="18" cy="18" r="15.5" fill="none" stroke-width="3.4"/><circle class="arc primary" cx="18" cy="18" r="15.5" fill="none" stroke-width="3.4" stroke-linecap="round" stroke-dasharray="97.4" stroke-dashoffset="65.3"/></svg>
|
||||
<span class="lbl tnum">33%</span>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Forklift Safety Recertification 2026</div>
|
||||
<div class="lmeta">
|
||||
<span class="badge badge-danger-soft">Overdue</span>
|
||||
<span class="badge badge-outline">Compliance</span>
|
||||
</div>
|
||||
<div class="lmeta">
|
||||
<span class="dl-dot"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></span>
|
||||
<span>Downloaded</span><span class="sep">·</span><span>Lesson 4 of 12</span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
|
||||
<a class="lrow" href="course.html">
|
||||
<div class="ring">
|
||||
<svg viewBox="0 0 36 36"><circle class="track" cx="18" cy="18" r="15.5" fill="none" stroke-width="3.4"/><circle class="arc primary" cx="18" cy="18" r="15.5" fill="none" stroke-width="3.4" stroke-linecap="round" stroke-dasharray="97.4" stroke-dashoffset="73.1"/></svg>
|
||||
<span class="lbl tnum">25%</span>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Warehouse Fire Safety</div>
|
||||
<div class="lmeta"><span class="badge badge-warning-soft">Due in 6 days</span><span class="badge badge-outline">Compliance</span></div>
|
||||
<div class="lmeta">
|
||||
<span class="dl-dot"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></span>
|
||||
<span>Downloaded</span><span class="sep">·</span><span>2 of 8 lessons</span>
|
||||
</div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
|
||||
<a class="lrow" href="course.html">
|
||||
<div class="ring">
|
||||
<svg viewBox="0 0 36 36"><circle class="track" cx="18" cy="18" r="15.5" fill="none" stroke-width="3.4"/><circle class="arc primary" cx="18" cy="18" r="15.5" fill="none" stroke-width="3.4" stroke-linecap="round" stroke-dasharray="97.4" stroke-dashoffset="29.2"/></svg>
|
||||
<span class="lbl tnum">70%</span>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Data Protection & GDPR Essentials</div>
|
||||
<div class="lmeta"><span class="badge badge-outline">Cohort</span><span>Q3 Frontline Onboarding</span></div>
|
||||
<div class="lmeta"><span>Knowledge check due in 2 days</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
|
||||
<a class="lrow" href="course.html">
|
||||
<div class="ring">
|
||||
<svg viewBox="0 0 36 36"><circle class="track" cx="18" cy="18" r="15.5" fill="none" stroke-width="3.4"/><circle class="arc primary" cx="18" cy="18" r="15.5" fill="none" stroke-width="3.4" stroke-linecap="round" stroke-dasharray="97.4" stroke-dashoffset="88.6"/></svg>
|
||||
<span class="lbl tnum">9%</span>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Customer Service Excellence</div>
|
||||
<div class="lmeta"><span class="badge badge-outline">Self-paced</span><span>Optional</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="tabbar" data-tabbar="learn"></nav>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>My learning</b>Progress ring, urgency badge, course type, and download state on every row — the four things a learner scans for.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 2. EXPLORE -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:41"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<div class="title">Learn</div>
|
||||
<div class="actions">
|
||||
<button class="icon-btn" aria-label="Search"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg></button>
|
||||
<a class="icon-btn" href="downloads.html" aria-label="Downloads"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 15V3M7 10l5 5 5-5"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/></svg></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tabs" data-select>
|
||||
<button data-option>My learning</button>
|
||||
<button class="on" data-option>Explore</button>
|
||||
<button data-option>Cohorts</button>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="search" style="margin: 12px 0 12px">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>
|
||||
Search Northwind Academy
|
||||
</div>
|
||||
|
||||
<div class="chips" data-select style="margin-bottom: 14px">
|
||||
<button class="chip on" data-option>All</button>
|
||||
<button class="chip" data-option>Safety</button>
|
||||
<button class="chip" data-option>Compliance</button>
|
||||
<button class="chip" data-option>Operations</button>
|
||||
<button class="chip" data-option>Soft skills</button>
|
||||
</div>
|
||||
|
||||
<div class="stack">
|
||||
<div class="card" style="overflow: hidden">
|
||||
<div style="height: 96px; background: linear-gradient(135deg, oklch(0.488 0.243 264.376), oklch(0.424 0.199 265.638)); position: relative">
|
||||
<span class="badge badge-secondary" style="position: absolute; top: 10px; left: 10px">Safety</span>
|
||||
</div>
|
||||
<div class="card-pad">
|
||||
<div class="ltitle" style="font-size: 15px">Hazardous Materials Handling</div>
|
||||
<div class="lsub">Segregation, spill response, and documentation for warehouse teams.</div>
|
||||
<div class="lmeta"><span>8 lessons</span><span class="sep">·</span><span>~45 min</span><span class="sep">·</span><span>Certificate</span></div>
|
||||
<div class="row" style="margin-top: 12px">
|
||||
<span class="badge badge-primary-soft">Assigned to you</span>
|
||||
<button class="btn btn-primary btn-sm" style="margin-left: auto">Start</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a class="lrow" href="course.html">
|
||||
<div class="media sq" style="background: linear-gradient(135deg, oklch(0.488 0.243 264.376), oklch(0.424 0.199 265.638)); border: none; color: #fff">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Manual Handling & Ergonomics</div>
|
||||
<div class="lmeta"><span>6 lessons</span><span class="sep">·</span><span>~25 min</span></div>
|
||||
</div>
|
||||
<button class="btn btn-outline btn-sm">Enroll</button>
|
||||
</a>
|
||||
|
||||
<a class="lrow" href="course.html">
|
||||
<div class="media sq" style="background: linear-gradient(135deg, oklch(0.546 0.245 262.881), oklch(0.488 0.243 264.376)); border: none; color: #fff">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Working at Height</div>
|
||||
<div class="lmeta"><span>5 lessons</span><span class="sep">·</span><span>~20 min</span></div>
|
||||
</div>
|
||||
<button class="btn btn-outline btn-sm">Enroll</button>
|
||||
</a>
|
||||
|
||||
<a class="lrow" href="course.html">
|
||||
<div class="media sq" style="background: linear-gradient(135deg, oklch(0.685 0.169 237.323), oklch(0.424 0.199 265.638)); border: none; color: #fff">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg>
|
||||
</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Difficult Conversations</div>
|
||||
<div class="lmeta"><span>4 lessons</span><span class="sep">·</span><span>~18 min</span></div>
|
||||
</div>
|
||||
<button class="btn btn-outline btn-sm">Enroll</button>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="banner banner-muted" style="margin-top: 16px">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></svg>
|
||||
<div class="bd" style="color: var(--muted-foreground)">
|
||||
Paid enrollment is handled by your organization on the web — the app never asks for payment.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="tabbar" data-tabbar="learn"></nav>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Explore</b>The org catalog with enroll-in-place. No checkout, no upgrade prompt, no external purchase link — Decision 9 in the PRD.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 3. COHORTS -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:41"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<div class="title">Learn</div>
|
||||
<div class="actions">
|
||||
<button class="icon-btn" aria-label="Search"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg></button>
|
||||
<a class="icon-btn" href="downloads.html" aria-label="Downloads"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 15V3M7 10l5 5 5-5"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/></svg></a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tabs" data-select>
|
||||
<button data-option>My learning</button>
|
||||
<button data-option>Explore</button>
|
||||
<button class="on" data-option>Cohorts</button>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="card card-pad" style="margin: 12px 0 10px">
|
||||
<div class="row between">
|
||||
<div>
|
||||
<div class="ltitle" style="font-size: 15px">Q3 Frontline Onboarding</div>
|
||||
<div class="lsub">18 members · Daniel Mensah</div>
|
||||
</div>
|
||||
<span class="badge badge-success-soft">Active</span>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="eyebrow" style="margin-bottom: 8px">Cohort goal</div>
|
||||
<div class="row">
|
||||
<div class="progress tall success"><span style="width: 72%"></span></div>
|
||||
<span class="tnum" style="font-size: 12px; font-weight: 600">13/18</span>
|
||||
</div>
|
||||
<div class="lmeta"><span>Everyone completes GDPR Essentials by 30 Aug · 9 days left</span></div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<div class="eyebrow" style="margin-bottom: 8px">Your progress</div>
|
||||
<div class="stack-sm stack">
|
||||
<div class="row">
|
||||
<span class="dl-dot"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></span>
|
||||
<span style="font-size: 13px">Welcome & expectations</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="dl-dot"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></span>
|
||||
<span style="font-size: 13px">Site induction</span>
|
||||
</div>
|
||||
<div class="row">
|
||||
<span class="media" style="width: 16px; height: 16px; border-radius: 999px"></span>
|
||||
<span style="font-size: 13px" class="text-muted">GDPR Essentials — knowledge check</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a class="btn btn-outline btn-block btn-sm" href="course.html" style="margin-top: 14px; height: 36px">Open cohort course</a>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Upcoming sessions</h2></div>
|
||||
<div class="stack">
|
||||
<div class="lrow">
|
||||
<div class="media is-current"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Practical forklift assessment</div>
|
||||
<div class="lmeta"><span>Thu 21 Aug · 14:00</span><span class="sep">·</span><span>Bay 4</span></div>
|
||||
</div>
|
||||
<span class="badge badge-outline">In person</span>
|
||||
</div>
|
||||
<div class="lrow">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m22 8-6 4 6 4V8z"/><rect x="2" y="6" width="14" height="12" rx="2"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">GDPR Q&A with Daniel</div>
|
||||
<div class="lmeta"><span>Fri 22 Aug · 10:00</span><span class="sep">·</span><span>Live class</span></div>
|
||||
</div>
|
||||
<button class="btn btn-outline btn-sm">Join</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hint" style="margin-top: 12px">
|
||||
Live classes open in your organization's meeting provider — the app doesn't host video conferencing.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="tabbar" data-tabbar="learn"></nav>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Cohorts</b>Goal progress, the learner's own checklist, and upcoming sessions. Live classes hand off to the org's existing provider.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<button class="theme-toggle" title="Toggle theme" aria-label="Toggle theme">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
|
||||
</button>
|
||||
<script src="proto.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,324 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Lesson player — ClassroomIO Mobile</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300..800&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="app-theme.css" />
|
||||
<script>
|
||||
if (localStorage.cioTheme === 'dark' || (!localStorage.cioTheme && matchMedia('(prefers-color-scheme: dark)').matches))
|
||||
document.documentElement.classList.add('dark');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="proto-head">
|
||||
<a class="back" href="index.html"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="14" height="14"><path d="m15 18-6-6 6-6"/></svg> All screens</a>
|
||||
<span class="eyebrow">Learner · Player</span>
|
||||
<h1>Lesson player</h1>
|
||||
<p>
|
||||
HLS video with captions, speed, PiP, background audio, and lock-screen controls, authorized by the new
|
||||
<code>GET /hls/:assetId/playback-token</code> because a native player can't carry the web's per-asset cookie.
|
||||
Transcript and the AI Lesson Tutor are bottom sheets over the same screen; mark-complete queues locally when offline.
|
||||
</p>
|
||||
<div class="proto-links">
|
||||
<a href="course.html">← Course outline</a>
|
||||
<a href="exercise.html">Next: knowledge check →</a>
|
||||
<a href="downloads.html">Sync queue →</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="stage">
|
||||
<!-- 1. ONLINE PLAYER -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:46"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<a class="icon-btn plain" href="course.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
||||
<div class="grow">
|
||||
<div class="sub">Section 2 · Lesson 4 of 12</div>
|
||||
<div class="title-sm">Pre-operation inspection</div>
|
||||
</div>
|
||||
<button class="icon-btn" aria-label="Lesson language"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M2 12h20M12 2a15 15 0 0 1 0 20 15 15 0 0 1 0-20"/></svg></button>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="player">
|
||||
<span class="pill"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg> Downloaded</span>
|
||||
<div class="play"><svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M8 5v14l11-7z"/></svg></div>
|
||||
<div class="ctrl">
|
||||
<div class="bar"><span style="width: 34%"></span></div>
|
||||
<div class="meta">
|
||||
<span>3:04</span>
|
||||
<span style="opacity: 0.7">9:12</span>
|
||||
<span class="right">
|
||||
<span style="font-weight: 600">1.25x</span>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="2" y="5" width="20" height="14" rx="2"/><path d="M7 15h4M14 15h3"/></svg>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M5 17H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v3"/><rect x="12" y="13" width="10" height="7" rx="2"/></svg>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M8 3H5a2 2 0 0 0-2 2v3M16 3h3a2 2 0 0 1 2 2v3M8 21H5a2 2 0 0 1-2-2v-3M16 21h3a2 2 0 0 0 2-2v-3"/></svg>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row" style="margin: 12px 0 6px">
|
||||
<div class="segs">
|
||||
<span class="seg done"></span><span class="seg done"></span><span class="seg done"></span><span class="seg now"></span>
|
||||
<span class="seg"></span><span class="seg"></span><span class="seg"></span><span class="seg"></span>
|
||||
<span class="seg"></span><span class="seg"></span><span class="seg"></span><span class="seg"></span>
|
||||
</div>
|
||||
<span class="text-muted tnum" style="font-size: 11.5px">4/12</span>
|
||||
</div>
|
||||
|
||||
<h2 style="font-size: 18px; font-weight: 600; letter-spacing: -0.02em; margin: 10px 0 8px">
|
||||
Pre-operation inspection checklist
|
||||
</h2>
|
||||
<div class="row" style="margin-bottom: 12px; gap: 6px; flex-wrap: wrap">
|
||||
<span class="badge badge-outline">Video · 9 min</span>
|
||||
<span class="badge badge-outline">English</span>
|
||||
<span class="badge badge-success-soft">Available offline</span>
|
||||
</div>
|
||||
|
||||
<div class="prose">
|
||||
<p>
|
||||
A pre-operation inspection takes about four minutes and catches most of the faults that cause workplace
|
||||
incidents. Work through it in the same order every time so nothing gets skipped when you're in a hurry.
|
||||
</p>
|
||||
<h3>The seven checks</h3>
|
||||
<ul>
|
||||
<li><strong>Tyres and wheels</strong> — cuts, embedded debris, correct pressure.</li>
|
||||
<li><strong>Forks</strong> — cracks at the heel, bent tips, locking pins seated.</li>
|
||||
<li><strong>Hydraulic hoses</strong> — chafing, weeping seals, pooled fluid underneath.</li>
|
||||
<li><strong>Mast and chains</strong> — even tension, no kinked links.</li>
|
||||
<li><strong>Horn, lights, and alarm</strong> — audible and visible from 10 metres.</li>
|
||||
<li><strong>Data plate</strong> — legible, and the rated capacity matches today's load.</li>
|
||||
<li><strong>Seatbelt</strong> — retracts cleanly and latches.</li>
|
||||
</ul>
|
||||
<p>Log the result before you move the truck. An unlogged inspection counts as no inspection.</p>
|
||||
</div>
|
||||
|
||||
<div class="stack" style="margin: 16px 0 12px">
|
||||
<button class="lrow" data-sheet-open="transcript" style="width: 100%; text-align: left; cursor: pointer">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6M8 13h8M8 17h5"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Transcript</div><div class="lsub">Tap a line to jump to that moment</div></div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</button>
|
||||
<button class="lrow" data-sheet-open="tutor" style="width: 100%; text-align: left; cursor: pointer">
|
||||
<div class="media is-current"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m12 3 1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9z"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Ask the AI tutor</div><div class="lsub">Answers from this lesson only · 8 of 20 questions left today</div></div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<a class="btn btn-primary btn-lg btn-block" href="lesson.html">
|
||||
Mark complete & continue
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg>
|
||||
</a>
|
||||
<div class="hint" style="text-align: center">Next: Load stability and centre of gravity · 7 min</div>
|
||||
</div>
|
||||
|
||||
<div class="scrim" data-scrim-for="transcript" style="display: none"></div>
|
||||
<div class="sheet" id="transcript" style="display: none">
|
||||
<div class="grabber"></div>
|
||||
<div class="sheet-head">
|
||||
<div class="st">Transcript</div>
|
||||
<button class="icon-btn plain" data-sheet-close="transcript" style="margin-left: auto" aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
||||
</div>
|
||||
<div class="sheet-body">
|
||||
<div class="lrow flat"><span class="text-muted tnum" style="font-size: 11.5px; width: 34px">0:12</span><div class="grow"><div style="font-size: 13.5px">Before you touch the controls, walk the truck. Start at the front-left tyre.</div></div></div>
|
||||
<div class="lrow flat" style="background: color-mix(in oklab, var(--primary) 8%, transparent); border-radius: var(--radius-md); padding: 11px 8px">
|
||||
<span class="text-primary tnum" style="font-size: 11.5px; width: 34px; font-weight: 600">3:04</span>
|
||||
<div class="grow"><div style="font-size: 13.5px; font-weight: 500">Look for weeping seals — a wet film on the ram is a fault, not a maybe.</div></div>
|
||||
</div>
|
||||
<div class="lrow flat"><span class="text-muted tnum" style="font-size: 11.5px; width: 34px">3:41</span><div class="grow"><div style="font-size: 13.5px">Chains should have even tension. If one side sags, tag the truck out.</div></div></div>
|
||||
<div class="lrow flat"><span class="text-muted tnum" style="font-size: 11.5px; width: 34px">4:18</span><div class="grow"><div style="font-size: 13.5px">The data plate tells you the rated capacity at a given load centre.</div></div></div>
|
||||
<div class="lrow flat"><span class="text-muted tnum" style="font-size: 11.5px; width: 34px">5:02</span><div class="grow"><div style="font-size: 13.5px">Finally, the seatbelt. It has to retract cleanly and latch first time.</div></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scrim" data-scrim-for="tutor" style="display: none"></div>
|
||||
<div class="sheet" id="tutor" style="display: none">
|
||||
<div class="grabber"></div>
|
||||
<div class="sheet-head">
|
||||
<div class="media is-current" style="width: 30px; height: 30px"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m12 3 1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9z"/></svg></div>
|
||||
<div><div class="st">AI lesson tutor</div><div class="lsub" style="margin: 0">Scoped to this lesson</div></div>
|
||||
<button class="icon-btn plain" data-sheet-close="tutor" style="margin-left: auto" aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
||||
</div>
|
||||
<div class="sheet-body">
|
||||
<div style="display: flex; justify-content: flex-end; margin-bottom: 10px">
|
||||
<div style="max-width: 78%; background: var(--primary); color: var(--primary-foreground); padding: 9px 12px; border-radius: 16px 16px 4px 16px; font-size: 13.5px">
|
||||
What counts as a "weeping" seal versus normal residue?
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; gap: 8px; margin-bottom: 12px">
|
||||
<div class="media is-current" style="width: 26px; height: 26px"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m12 3 1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9z"/></svg></div>
|
||||
<div style="max-width: 82%; background: var(--muted); padding: 9px 12px; border-radius: 16px 16px 16px 4px; font-size: 13.5px; line-height: 1.5">
|
||||
Residue is dry and dull — old dust bonded to grease. Weeping is a wet, shiny film that reappears after you
|
||||
wipe it. This lesson's rule at 3:04: if it comes back, tag the truck out.
|
||||
<div class="lmeta" style="margin-top: 7px"><span class="badge badge-outline">From 3:04</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="gap: 8px">
|
||||
<input class="input" placeholder="Ask about this lesson…" />
|
||||
<button class="icon-btn" style="width: 40px; height: 40px" aria-label="Send"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m22 2-7 20-4-9-9-4z"/></svg></button>
|
||||
</div>
|
||||
<div class="hint">8 of 20 questions left today · resets at midnight</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Online player</b>Video, native prose, and two sheets (transcript, AI tutor) — tap either row in the live prototype to open it.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 2. AI TUTOR CAP -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="21:18"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<a class="icon-btn plain" href="course.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
||||
<div class="grow">
|
||||
<div class="sub">Section 2 · Lesson 5 of 12</div>
|
||||
<div class="title-sm">Load stability</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="player">
|
||||
<div class="play"><svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M8 5v14l11-7z"/></svg></div>
|
||||
<div class="ctrl">
|
||||
<div class="bar"><span style="width: 88%"></span></div>
|
||||
<div class="meta"><span>6:11</span><span style="opacity: 0.7">7:02</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="prose" style="margin-top: 14px">
|
||||
<p>
|
||||
The load centre is the horizontal distance from the fork face to the load's centre of gravity. Every truck's
|
||||
data plate is rated at a specific load centre — usually 500 mm.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scrim"></div>
|
||||
<div class="sheet" style="display: flex">
|
||||
<div class="grabber"></div>
|
||||
<div class="sheet-head">
|
||||
<div class="media is-current" style="width: 30px; height: 30px"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m12 3 1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9z"/></svg></div>
|
||||
<div><div class="st">AI lesson tutor</div><div class="lsub" style="margin: 0">Scoped to this lesson</div></div>
|
||||
<button class="icon-btn plain" style="margin-left: auto" aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
||||
</div>
|
||||
<div class="sheet-body">
|
||||
<div class="empty" style="padding: 22px 12px 16px">
|
||||
<div class="ei">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>
|
||||
</div>
|
||||
<div class="et">You've used today's 20 questions</div>
|
||||
<div class="ed">Your allowance resets at midnight. The transcript and lesson notes are still available.</div>
|
||||
</div>
|
||||
<button class="btn btn-outline btn-block">Open transcript instead</button>
|
||||
<button class="btn btn-ghost btn-block" style="margin-top: 6px">Ask in Community</button>
|
||||
<div class="hint" style="text-align: center; margin-top: 10px">
|
||||
Fair-use caps are set by your organization.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>AI tutor, cap reached</b>The fair-use limit is stated plainly with two real alternatives, not a paywall — the app never sells token packs.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 3. OFFLINE / QUEUED -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="18:40" data-offline></div>
|
||||
|
||||
<div class="offline-strip">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M2 2 22 22M8.5 16.5a5 5 0 0 1 7 0M5 13a10 10 0 0 1 4-2.4M12 20h.01"/></svg>
|
||||
Offline — progress will sync automatically
|
||||
</div>
|
||||
|
||||
<div class="navbar">
|
||||
<a class="icon-btn plain" href="course.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
||||
<div class="grow">
|
||||
<div class="sub">Section 2 · Lesson 5 of 12</div>
|
||||
<div class="title-sm">Load stability</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="player">
|
||||
<span class="pill"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg> Playing from device</span>
|
||||
<div class="play"><svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><path d="M6 4h4v16H6zM14 4h4v16h-4z"/></svg></div>
|
||||
<div class="ctrl">
|
||||
<div class="bar"><span style="width: 100%"></span></div>
|
||||
<div class="meta"><span>7:02</span><span style="opacity: 0.7">7:02</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="banner banner-muted" style="margin: 14px 0 12px">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg>
|
||||
<div class="grow">
|
||||
<div class="bt">Marked complete</div>
|
||||
<div class="bd">Saved on this device · will sync when you're back online</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head" style="margin-top: 6px"><h2>Waiting to sync</h2><span class="count">2</span></div>
|
||||
<div class="stack">
|
||||
<div class="lrow">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M21 12a9 9 0 1 1-6.2-8.6"/><path d="M21 3v6h-6"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Lesson 5 completed</div>
|
||||
<div class="lmeta"><span>Queued 18:40</span></div>
|
||||
</div>
|
||||
<span class="badge badge-muted">Queued</span>
|
||||
</div>
|
||||
<div class="lrow">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M21 12a9 9 0 1 1-6.2-8.6"/><path d="M21 3v6h-6"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Lesson 4 position 9:12</div>
|
||||
<div class="lmeta"><span>Queued 18:31</span></div>
|
||||
</div>
|
||||
<span class="badge badge-muted">Queued</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="divider"></div>
|
||||
|
||||
<a class="btn btn-primary btn-lg btn-block" href="lesson.html">
|
||||
Next lesson
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg>
|
||||
</a>
|
||||
|
||||
<div class="lrow locked" style="margin-top: 10px">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="3" y="11" width="18" height="11" rx="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Section 2 check</div>
|
||||
<div class="lsub">Exercises need a connection — we'll nudge you when you're back online</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Offline & queued</b>Completion is a monotonic, idempotent intent held on-device — the reason v1 offline covers content but not assessments.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<button class="theme-toggle" title="Toggle theme" aria-label="Toggle theme">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
|
||||
</button>
|
||||
<script src="proto.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,263 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Notifications — ClassroomIO Mobile</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300..800&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="app-theme.css" />
|
||||
<script>
|
||||
if (localStorage.cioTheme === 'dark' || (!localStorage.cioTheme && matchMedia('(prefers-color-scheme: dark)').matches))
|
||||
document.documentElement.classList.add('dark');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="proto-head">
|
||||
<a class="back" href="index.html"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="14" height="14"><path d="m15 18-6-6 6-6"/></svg> All screens</a>
|
||||
<span class="eyebrow">Learner · Retention</span>
|
||||
<h1>Notifications, permission & preferences</h1>
|
||||
<p>
|
||||
The single biggest reason to ship an app: ClassroomIO has no push channel today, only email. This rides on
|
||||
<code>prd/notification-system</code> — mobile adds a device-token registry and an Expo Push transport in the existing
|
||||
<code>apps/jobs</code> worker, never a second notification pipeline. Every category is independently switchable and
|
||||
quiet hours defer rather than drop.
|
||||
</p>
|
||||
<div class="proto-links">
|
||||
<a href="home.html">Deep-link target: Home →</a>
|
||||
<a href="exercise.html">Deep-link target: Exercise →</a>
|
||||
<a href="profile.html">← Profile</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="stage">
|
||||
<!-- 1. PRE-PROMPT -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:43"></div>
|
||||
|
||||
<div class="screen centered">
|
||||
<div style="text-align: center">
|
||||
<div style="width: 76px; height: 76px; border-radius: 22px; background: color-mix(in oklab, var(--primary) 12%, transparent); color: var(--primary); display: grid; place-items: center; margin: 0 auto 18px">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="34" height="34" stroke-width="1.7"><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/><path d="M18 8a6 6 0 1 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/></svg>
|
||||
</div>
|
||||
<div style="font-size: 20px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.3">
|
||||
Never miss a deadline
|
||||
</div>
|
||||
<div class="text-muted" style="font-size: 14px; margin-top: 8px; line-height: 1.55">
|
||||
Northwind Logistics assigns required training with real deadlines. Turn on notifications and we'll remind you
|
||||
before something goes overdue — not after.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-pad" style="margin: 22px 0 20px; text-align: left">
|
||||
<div class="lrow flat">
|
||||
<div class="media is-warning"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Deadline & renewal reminders</div></div>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="media is-current"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">New assignments and grades</div></div>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22z"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Replies to your questions</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary btn-lg btn-block">Turn on notifications</button>
|
||||
<button class="btn btn-ghost btn-block" style="margin-top: 6px">Not now</button>
|
||||
<div class="hint" style="text-align: center; margin-top: 12px">
|
||||
You choose exactly which categories reach you — nothing is marketing.
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Permission pre-prompt</b>Asks in context with the value stated, so a "no" from the cold OS dialog doesn't permanently kill the channel.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 2. CENTER -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:41"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<a class="icon-btn plain" href="home.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
||||
<div class="grow"><div class="title">Notifications</div></div>
|
||||
<button class="btn btn-ghost btn-sm">Mark all read</button>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="sec-head" style="margin-top: 10px"><h2>Today</h2></div>
|
||||
<div class="stack">
|
||||
<a class="lrow" href="compliance.html">
|
||||
<div class="media is-danger"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 9v4M12 17h.01"/><path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Forklift Safety is now overdue</div>
|
||||
<div class="lsub">Grace period ends 24 Aug · 8 lessons left</div>
|
||||
<div class="lmeta"><span>08:00</span></div>
|
||||
</div>
|
||||
<span style="width: 8px; height: 8px; border-radius: 999px; background: var(--primary)"></span>
|
||||
</a>
|
||||
|
||||
<a class="lrow" href="exercise.html">
|
||||
<div class="media is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Your Section 2 check was graded</div>
|
||||
<div class="lsub">87% · Passed · Feedback from Daniel Mensah</div>
|
||||
<div class="lmeta"><span>07:12</span></div>
|
||||
</div>
|
||||
<span style="width: 8px; height: 8px; border-radius: 999px; background: var(--primary)"></span>
|
||||
</a>
|
||||
|
||||
<a class="lrow" href="community.html">
|
||||
<div class="media is-current"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M7.9 20A9 9 0 1 0 4 16.1L2 22z"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Daniel replied to your question</div>
|
||||
<div class="lsub">"Yes — any crack at the heel is a tag-out…"</div>
|
||||
<div class="lmeta"><span>06:40</span></div>
|
||||
</div>
|
||||
<span style="width: 8px; height: 8px; border-radius: 999px; background: var(--primary)"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Earlier</h2></div>
|
||||
<div class="stack">
|
||||
<a class="lrow" href="course.html">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">New course assigned: Hazardous Materials Handling</div>
|
||||
<div class="lsub">Assigned by Daniel Mensah · due 30 Sep</div>
|
||||
<div class="lmeta"><span>Yesterday 16:20</span></div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="lrow" href="certificates.html">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="8" r="6"/><path d="M15.5 13.5 17 22l-5-3-5 3 1.5-8.5"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Certificate issued: GDPR Essentials</div>
|
||||
<div class="lsub">Valid until 16 Aug 2027</div>
|
||||
<div class="lmeta"><span>Sat 16 Aug</span></div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="lrow" href="compliance.html">
|
||||
<div class="media is-warning"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Hazmat certification expires in 30 days</div>
|
||||
<div class="lsub">Renewal opens 8 Sep</div>
|
||||
<div class="lmeta"><span>Fri 15 Aug</span></div>
|
||||
</div>
|
||||
</a>
|
||||
<a class="lrow" href="learn.html">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="3" y="4" width="18" height="18" rx="2"/><path d="M16 2v4M8 2v4M3 10h18"/></svg></div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Cohort goal: 9 days left</div>
|
||||
<div class="lsub">Q3 Frontline Onboarding · 13 of 18 done</div>
|
||||
<div class="lmeta"><span>Thu 14 Aug</span></div>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="tabbar" data-tabbar="home"></nav>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Center</b>Grouped Today / Earlier with unread dots. Every row deep-links to the exact screen its push notification would open.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 3. PREFERENCES -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:47"></div>
|
||||
|
||||
<div class="navbar bordered">
|
||||
<a class="icon-btn plain" href="profile.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
||||
<div class="grow"><div class="title-sm">Notification preferences</div></div>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="sec-head" style="margin-top: 12px"><h2>Training & deadlines</h2></div>
|
||||
<div class="card card-pad">
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">New assignment</div><div class="lsub">When a course is assigned to you</div></div>
|
||||
<label class="switch"><input type="checkbox" checked /><span class="track"></span><span class="knob"></span></label>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Deadline reminder</div><div class="lsub">7 days, 1 day, and morning-of</div></div>
|
||||
<label class="switch"><input type="checkbox" checked /><span class="track"></span><span class="knob"></span></label>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Overdue alert</div><div class="lsub">Always on for required training</div></div>
|
||||
<label class="switch"><input type="checkbox" checked disabled /><span class="track"></span><span class="knob"></span></label>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Certification renewal</div><div class="lsub">30 and 7 days before expiry</div></div>
|
||||
<label class="switch"><input type="checkbox" checked /><span class="track"></span><span class="knob"></span></label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Progress & feedback</h2></div>
|
||||
<div class="card card-pad">
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Exercise graded</div></div>
|
||||
<label class="switch"><input type="checkbox" checked /><span class="track"></span><span class="knob"></span></label>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Certificate issued</div></div>
|
||||
<label class="switch"><input type="checkbox" checked /><span class="track"></span><span class="knob"></span></label>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Cohort goal approaching</div></div>
|
||||
<label class="switch"><input type="checkbox" /><span class="track"></span><span class="knob"></span></label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Community</h2></div>
|
||||
<div class="card card-pad">
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Mentions & replies</div></div>
|
||||
<label class="switch"><input type="checkbox" checked /><span class="track"></span><span class="knob"></span></label>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">New lesson or newsfeed post</div></div>
|
||||
<label class="switch"><input type="checkbox" /><span class="track"></span><span class="knob"></span></label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Quiet hours</h2></div>
|
||||
<div class="card card-pad">
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Pause notifications</div><div class="lsub">Deferred, not dropped — they arrive after</div></div>
|
||||
<label class="switch"><input type="checkbox" checked /><span class="track"></span><span class="knob"></span></label>
|
||||
</div>
|
||||
<div class="kv"><span class="k">From</span><span class="v tnum">21:00</span></div>
|
||||
<div class="kv"><span class="k">Until</span><span class="v tnum">07:00</span></div>
|
||||
<div class="kv"><span class="k">Time zone</span><span class="v">Europe/London</span></div>
|
||||
</div>
|
||||
|
||||
<div class="banner banner-muted" style="margin-top: 14px">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></svg>
|
||||
<div class="bd" style="color: var(--muted-foreground)">
|
||||
These settings apply to push and in-app alike, and stay in sync with your email preferences on the web.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Preferences</b>Per-category switches writing to the same preference store the web uses. Overdue compliance alerts are deliberately not switchable.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<button class="theme-toggle" title="Toggle theme" aria-label="Toggle theme">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
|
||||
</button>
|
||||
<script src="proto.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,242 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Profile — ClassroomIO Mobile</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300..800&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="app-theme.css" />
|
||||
<script>
|
||||
if (localStorage.cioTheme === 'dark' || (!localStorage.cioTheme && matchMedia('(prefers-color-scheme: dark)').matches))
|
||||
document.documentElement.classList.add('dark');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="proto-head">
|
||||
<a class="back" href="index.html"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="14" height="14"><path d="m15 18-6-6 6-6"/></svg> All screens</a>
|
||||
<span class="eyebrow">Learner · Tab 5</span>
|
||||
<h1>Profile & settings</h1>
|
||||
<p>
|
||||
The learner's own record plus the app's controls. Switching organization re-themes the app and swaps every list
|
||||
behind it. Anything that belongs to org administration — billing, team management, SSO, domains — is deliberately a
|
||||
link to the web, not a screen here.
|
||||
</p>
|
||||
<div class="proto-links">
|
||||
<a href="downloads.html">Downloads →</a>
|
||||
<a href="notifications.html">Notification preferences →</a>
|
||||
<a href="certificates.html">Certificates →</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="stage">
|
||||
<!-- 1. PROFILE -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="9:50"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<div class="grow"><div class="title">Profile</div></div>
|
||||
<button class="icon-btn" data-sheet-open="orgs" aria-label="Switch organization"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m18 8 4 4-4 4M2 12h20M6 16l-4-4 4-4"/></svg></button>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="card card-pad" style="margin: 10px 0 12px">
|
||||
<div class="row">
|
||||
<div class="avatar lg">AO</div>
|
||||
<div class="grow">
|
||||
<div style="font-size: 16px; font-weight: 600; letter-spacing: -0.02em">Amara Okafor</div>
|
||||
<div class="lsub">amara.okafor@northwind.co</div>
|
||||
<div class="lmeta"><span class="badge badge-primary-soft">Learner</span><span>Warehouse Operations</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="row" style="align-items: stretch">
|
||||
<div class="stat"><div class="v tnum">9</div><div class="k">Completed</div></div>
|
||||
<div style="width: 1px; background: var(--border)"></div>
|
||||
<div class="stat"><div class="v tnum">6</div><div class="k">Certificates</div></div>
|
||||
<div style="width: 1px; background: var(--border)"></div>
|
||||
<div class="stat"><div class="v tnum">83%</div><div class="k">On time</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="lrow" data-sheet-open="orgs" style="width: 100%; text-align: left; cursor: pointer; margin-bottom: 14px">
|
||||
<div class="avatar brand">NL</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Northwind Logistics</div>
|
||||
<div class="lsub">Tap to switch organization · 3 available</div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</button>
|
||||
|
||||
<div class="sec-head" style="margin-top: 0"><h2>Your learning</h2></div>
|
||||
<div class="card card-pad">
|
||||
<a class="lrow flat" href="certificates.html">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="8" r="6"/><path d="M15.5 13.5 17 22l-5-3-5 3 1.5-8.5"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Certificates</div></div>
|
||||
<span class="badge badge-outline">6</span>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
<a class="lrow flat" href="compliance.html">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/><path d="m9 12 2 2 4-4"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Compliance record</div></div>
|
||||
<span class="badge badge-danger-soft">1 overdue</span>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
<a class="lrow flat" href="downloads.html">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 15V3M7 10l5 5 5-5"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Downloads</div></div>
|
||||
<span class="badge badge-outline tnum">284 MB</span>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>App</h2></div>
|
||||
<div class="card card-pad">
|
||||
<a class="lrow flat" href="notifications.html">
|
||||
<div class="media"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/><path d="M18 8a6 6 0 1 0-12 0c0 7-3 9-3 9h18s-3-2-3-9"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Notifications</div></div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
<div class="kv"><span class="k">Language</span><span class="v">English (UK)</span></div>
|
||||
<div class="kv"><span class="k">Theme</span><span class="v">Match system</span></div>
|
||||
<div class="kv"><span class="k">Video quality</span><span class="v">Auto</span></div>
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Unlock with Face ID</div><div class="lsub">Re-lock the app after 5 minutes</div></div>
|
||||
<label class="switch"><input type="checkbox" checked /><span class="track"></span><span class="knob"></span></label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Account</h2></div>
|
||||
<div class="card card-pad">
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Change password</div><div class="lsub">Opens the web dashboard</div></div>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="15" height="15" class="text-muted"><path d="M15 3h6v6M10 14 21 3M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Privacy & data</div></div>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="15" height="15" class="text-muted"><path d="M15 3h6v6M10 14 21 3M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/></svg>
|
||||
</div>
|
||||
<a class="lrow flat" href="auth.html">
|
||||
<div class="grow"><div class="ltitle" style="color: var(--destructive)">Sign out</div></div>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="hint" style="text-align: center; margin-top: 14px">
|
||||
ClassroomIO 1.0.0 (build 118) · Connected to app.classroomio.com
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scrim" data-scrim-for="orgs" style="display: none"></div>
|
||||
<div class="sheet" id="orgs" style="display: none">
|
||||
<div class="grabber"></div>
|
||||
<div class="sheet-head">
|
||||
<div class="st">Switch organization</div>
|
||||
<button class="icon-btn plain" data-sheet-close="orgs" style="margin-left: auto" aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
||||
</div>
|
||||
<div class="sheet-body">
|
||||
<div class="stack">
|
||||
<div class="lrow current">
|
||||
<div class="avatar brand">NL</div>
|
||||
<div class="grow"><div class="ltitle">Northwind Logistics</div><div class="lmeta"><span class="badge badge-primary-soft">Learner</span><span>Current</span></div></div>
|
||||
<span class="dl-dot"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></span>
|
||||
</div>
|
||||
<a class="lrow" href="tutor-grading.html">
|
||||
<div class="avatar brand" style="background: oklch(0.424 0.199 265.638)">AH</div>
|
||||
<div class="grow"><div class="ltitle">Acme Health Academy</div><div class="lmeta"><span class="badge badge-success-soft">Tutor</span><span>12 to grade</span></div></div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
<a class="lrow" href="home.html">
|
||||
<div class="avatar brand" style="background: oklch(0.546 0.245 262.881)">MP</div>
|
||||
<div class="grow"><div class="ltitle">Meridian Partner Network</div><div class="lmeta"><span class="badge badge-primary-soft">Learner</span></div></div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
</div>
|
||||
<div class="hint" style="margin-top: 12px">
|
||||
Switching changes the theme, the courses, and your records. Downloads stay on the device per organization.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="tabbar" data-tabbar="profile"></nav>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Profile</b>Learner record, app settings, and the org switcher (tap the header row in the live prototype). Admin concerns link out to the web on purpose.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 2. FRONTLINE MODE -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="6:05"></div>
|
||||
|
||||
<div class="navbar bordered">
|
||||
<a class="icon-btn plain" href="profile.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
||||
<div class="grow"><div class="title-sm">Frontline mode</div></div>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="banner banner-info" style="margin: 12px 0 14px">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></svg>
|
||||
<div class="grow">
|
||||
<div class="bt">Built for shop floors and shared devices</div>
|
||||
<div class="bd">Bigger targets, lighter media, and QR launch for staff without a work laptop.</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-pad" style="margin-bottom: 14px">
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Large touch targets</div><div class="lsub">Gloves-friendly rows and buttons</div></div>
|
||||
<label class="switch"><input type="checkbox" checked /><span class="track"></span><span class="knob"></span></label>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Low-bandwidth media</div><div class="lsub">Audio-first playback on cellular</div></div>
|
||||
<label class="switch"><input type="checkbox" checked /><span class="track"></span><span class="knob"></span></label>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Shared device</div><div class="lsub">Sign out automatically after each session</div></div>
|
||||
<label class="switch"><input type="checkbox" /><span class="track"></span><span class="knob"></span></label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-pad" style="text-align: center">
|
||||
<div class="eyebrow">Scan to start a course</div>
|
||||
<div
|
||||
style="width: 150px; height: 150px; margin: 14px auto; border-radius: var(--radius-lg); border: 1px solid var(--border); background: var(--muted); display: grid; place-items: center; color: var(--muted-foreground)"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="66" height="66" stroke-width="1.5">
|
||||
<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/>
|
||||
<path d="M14 14h3v3h-3zM18 18h3v3h-3zM14 21h3M21 14v3"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="lsub">Point the camera at the poster in the break room, or a QR on the machine itself.</div>
|
||||
<button class="btn btn-primary btn-lg btn-block" style="margin-top: 14px">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="3" y="6" width="18" height="14" rx="2"/><circle cx="12" cy="13" r="3.5"/><path d="M8 6l1.5-2h5L16 6"/></svg>
|
||||
Open scanner
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Accessibility</h2></div>
|
||||
<div class="card card-pad">
|
||||
<div class="kv"><span class="k">Text size</span><span class="v">Follows system (Dynamic Type)</span></div>
|
||||
<div class="kv"><span class="k">Screen reader</span><span class="v">Full labels on every control</span></div>
|
||||
<div class="kv"><span class="k">Status colours</span><span class="v">Always paired with an icon</span></div>
|
||||
<div class="kv"><span class="k">Minimum target</span><span class="v tnum">44 pt</span></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Frontline mode & accessibility</b>The settings that make the app usable in a warehouse: large targets, low-bandwidth media, shared-device sign-out, and QR launch.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<button class="theme-toggle" title="Toggle theme" aria-label="Toggle theme">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
|
||||
</button>
|
||||
<script src="proto.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* proto.js — shared behaviour for the mobile-app prototypes.
|
||||
*
|
||||
* Deliberately tiny: a theme toggle, plus opt-in interactions (segmented
|
||||
* controls, in-screen tabs, chips, bottom sheets, switches) that pages get
|
||||
* for free by using the right data attribute. No framework, no build step.
|
||||
*/
|
||||
|
||||
(function () {
|
||||
// ---- device chrome ------------------------------------------------------
|
||||
// <div class="statusbar" data-status="9:41" data-offline></div>
|
||||
const SIGNAL =
|
||||
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M2 20h.01M7 20v-4M12 20v-8M17 20V8"/></svg>';
|
||||
const WIFI =
|
||||
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M5 13a10 10 0 0 1 14 0M8.5 16.5a5 5 0 0 1 7 0M12 20h.01"/></svg>';
|
||||
const WIFI_OFF =
|
||||
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M2 2 22 22M8.5 16.5a5 5 0 0 1 7 0M5 13a10 10 0 0 1 4-2.4M12 20h.01"/></svg>';
|
||||
const BATTERY =
|
||||
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="2" y="7" width="18" height="10" rx="2"/><path d="M22 11v2"/></svg>';
|
||||
|
||||
document.querySelectorAll('[data-status]').forEach((bar) => {
|
||||
const offline = bar.hasAttribute('data-offline');
|
||||
bar.innerHTML = `<span>${bar.dataset.status}</span><span class="icons">${offline ? '' : SIGNAL}${
|
||||
offline ? WIFI_OFF : WIFI
|
||||
}${BATTERY}</span>`;
|
||||
});
|
||||
|
||||
// <nav class="tabbar" data-tabbar="home" data-role="tutor"></nav>
|
||||
const TAB_ICONS = {
|
||||
home: '<path d="m3 10 9-7 9 7v10a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><path d="M9 22V12h6v10"/>',
|
||||
learn:
|
||||
'<path d="M12 7v14"/><path d="M3 18a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h5a4 4 0 0 1 4 4 4 4 0 0 1 4-4h5a1 1 0 0 1 1 1v13a1 1 0 0 1-1 1h-6a3 3 0 0 0-3 3 3 3 0 0 0-3-3z"/>',
|
||||
compliance:
|
||||
'<path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z"/><path d="m9 12 2 2 4-4"/>',
|
||||
teach:
|
||||
'<path d="M21.4 10.9a1 1 0 0 0 0-1.8L12.8 5.2a2 2 0 0 0-1.6 0L2.6 9.1a1 1 0 0 0 0 1.8l8.6 3.9a2 2 0 0 0 1.6 0z"/><path d="M22 10v6"/><path d="M6 12.5V16a6 3 0 0 0 12 0v-3.5"/>',
|
||||
community: '<path d="M7.9 20A9 9 0 1 0 4 16.1L2 22z"/>',
|
||||
profile: '<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2"/><circle cx="12" cy="7" r="4"/>'
|
||||
};
|
||||
const LEARNER_TABS = [
|
||||
{ id: 'home', label: 'Home', href: 'home.html' },
|
||||
{ id: 'learn', label: 'Learn', href: 'learn.html' },
|
||||
{ id: 'compliance', label: 'Compliance', href: 'compliance.html' },
|
||||
{ id: 'community', label: 'Community', href: 'community.html' },
|
||||
{ id: 'profile', label: 'Profile', href: 'profile.html' }
|
||||
];
|
||||
const TUTOR_TABS = [
|
||||
{ id: 'home', label: 'Home', href: 'home.html' },
|
||||
{ id: 'learn', label: 'Learn', href: 'learn.html' },
|
||||
{ id: 'teach', label: 'Teach', href: 'tutor-grading.html' },
|
||||
{ id: 'community', label: 'Community', href: 'community.html' },
|
||||
{ id: 'profile', label: 'Profile', href: 'profile.html' }
|
||||
];
|
||||
|
||||
document.querySelectorAll('[data-tabbar]').forEach((bar) => {
|
||||
const active = bar.dataset.tabbar;
|
||||
const badges = (bar.dataset.badges || '').split(',').filter(Boolean);
|
||||
const tabs = bar.dataset.role === 'tutor' ? TUTOR_TABS : LEARNER_TABS;
|
||||
|
||||
bar.innerHTML = tabs
|
||||
.map((tab) => {
|
||||
const badge = badges.find((entry) => entry.startsWith(`${tab.id}:`));
|
||||
const value = badge ? badge.split(':')[1] : '';
|
||||
const marker =
|
||||
value === 'dot' ? '<span class="dot"></span>' : value ? `<span class="count">${value}</span>` : '';
|
||||
return `<a class="tab${tab.id === active ? ' on' : ''}" href="${tab.href}"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor">${
|
||||
TAB_ICONS[tab.id]
|
||||
}</svg>${tab.label}${marker}</a>`;
|
||||
})
|
||||
.join('');
|
||||
});
|
||||
|
||||
// ---- theme toggle -------------------------------------------------------
|
||||
const toggle = document.querySelector('.theme-toggle');
|
||||
if (toggle) {
|
||||
toggle.addEventListener('click', () => {
|
||||
const dark = document.documentElement.classList.toggle('dark');
|
||||
localStorage.cioTheme = dark ? 'dark' : 'light';
|
||||
});
|
||||
}
|
||||
|
||||
// ---- exclusive selection groups ----------------------------------------
|
||||
// Any container with [data-select] makes its direct children mutually
|
||||
// exclusive on click: .segmented, .tabs, .chips, option lists.
|
||||
document.querySelectorAll('[data-select]').forEach((group) => {
|
||||
const activeClass = group.dataset.select || 'on';
|
||||
group.addEventListener('click', (event) => {
|
||||
const target = event.target.closest('[data-option]');
|
||||
if (!target || !group.contains(target)) return;
|
||||
|
||||
group.querySelectorAll('[data-option]').forEach((option) => option.classList.remove(activeClass));
|
||||
target.classList.add(activeClass);
|
||||
});
|
||||
});
|
||||
|
||||
// ---- bottom sheets ------------------------------------------------------
|
||||
// [data-sheet-open="id"] shows #id and its scrim; [data-sheet-close] hides.
|
||||
function setSheet(id, open) {
|
||||
const sheet = document.getElementById(id);
|
||||
if (!sheet) return;
|
||||
|
||||
const scrim = sheet.parentElement.querySelector(`[data-scrim-for="${id}"]`);
|
||||
sheet.style.display = open ? 'flex' : 'none';
|
||||
if (scrim) scrim.style.display = open ? 'block' : 'none';
|
||||
}
|
||||
|
||||
document.querySelectorAll('[data-sheet-open]').forEach((trigger) => {
|
||||
trigger.addEventListener('click', () => setSheet(trigger.dataset.sheetOpen, true));
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-sheet-close]').forEach((trigger) => {
|
||||
trigger.addEventListener('click', () => setSheet(trigger.dataset.sheetClose, false));
|
||||
});
|
||||
|
||||
document.querySelectorAll('[data-scrim-for]').forEach((scrim) => {
|
||||
scrim.addEventListener('click', () => setSheet(scrim.dataset.scrimFor, false));
|
||||
});
|
||||
})();
|
||||
@@ -0,0 +1,192 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Tutor attendance — ClassroomIO Mobile</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300..800&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="app-theme.css" />
|
||||
<script>
|
||||
if (localStorage.cioTheme === 'dark' || (!localStorage.cioTheme && matchMedia('(prefers-color-scheme: dark)').matches))
|
||||
document.documentElement.classList.add('dark');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="proto-head">
|
||||
<a class="back" href="index.html"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="14" height="14"><path d="m15 18-6-6 6-6"/></svg> All screens</a>
|
||||
<span class="eyebrow">Tutor · Release 3</span>
|
||||
<h1>Attendance</h1>
|
||||
<p>
|
||||
The clearest case for a phone over a laptop: marking a roster while standing in the room. Backed by the existing
|
||||
<code>/course/:courseId/attendance</code> route, with a QR/code check-in so learners mark themselves and the tutor
|
||||
only handles exceptions.
|
||||
</p>
|
||||
<div class="proto-links">
|
||||
<a href="tutor-grading.html">← Grading queue</a>
|
||||
<a href="tutor-course.html">Course snapshot →</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="stage">
|
||||
<!-- 1. ROSTER -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="14:03"></div>
|
||||
|
||||
<div class="navbar bordered">
|
||||
<a class="icon-btn plain" href="tutor-grading.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
||||
<div class="grow">
|
||||
<div class="title-sm">Practical assessment</div>
|
||||
<div class="sub">Thu 21 Aug · 14:00 · Bay 4</div>
|
||||
</div>
|
||||
<button class="icon-btn" aria-label="Check-in code"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><path d="M14 14h3v3h-3zM18 18h3v3h-3z"/></svg></button>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="card card-pad" style="margin: 12px 0 12px">
|
||||
<div class="row" style="align-items: stretch">
|
||||
<div class="stat"><div class="v tnum">14</div><div class="k">Present</div></div>
|
||||
<div style="width: 1px; background: var(--border)"></div>
|
||||
<div class="stat"><div class="v tnum">2</div><div class="k">Late</div></div>
|
||||
<div style="width: 1px; background: var(--border)"></div>
|
||||
<div class="stat"><div class="v tnum">2</div><div class="k">Absent</div></div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="row" style="gap: 8px">
|
||||
<button class="btn btn-outline btn-sm" style="flex: 1">Mark all present</button>
|
||||
<button class="btn btn-outline btn-sm" style="flex: 1">Reset</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="search" style="margin-bottom: 12px">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3"/></svg>
|
||||
Find a learner
|
||||
</div>
|
||||
|
||||
<div class="stack stack-sm">
|
||||
<div class="lrow">
|
||||
<div class="avatar">AO</div>
|
||||
<div class="grow"><div class="ltitle">Amara Okafor</div><div class="lmeta"><span>Checked in 13:52 via QR</span></div></div>
|
||||
<div class="segmented" style="width: 128px; padding: 2px">
|
||||
<button class="on" style="padding: 4px 6px; font-size: 11px">P</button>
|
||||
<button style="padding: 4px 6px; font-size: 11px">L</button>
|
||||
<button style="padding: 4px 6px; font-size: 11px">A</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lrow">
|
||||
<div class="avatar">TA</div>
|
||||
<div class="grow"><div class="ltitle">Tomás Alvarez</div><div class="lmeta"><span>Checked in 13:58 via QR</span></div></div>
|
||||
<div class="segmented" style="width: 128px; padding: 2px">
|
||||
<button class="on" style="padding: 4px 6px; font-size: 11px">P</button>
|
||||
<button style="padding: 4px 6px; font-size: 11px">L</button>
|
||||
<button style="padding: 4px 6px; font-size: 11px">A</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lrow">
|
||||
<div class="avatar">PK</div>
|
||||
<div class="grow"><div class="ltitle">Priya Kaur</div><div class="lmeta"><span>Arrived 14:09</span><span class="badge badge-warning-soft">Late</span></div></div>
|
||||
<div class="segmented" style="width: 128px; padding: 2px">
|
||||
<button style="padding: 4px 6px; font-size: 11px">P</button>
|
||||
<button class="on" style="padding: 4px 6px; font-size: 11px">L</button>
|
||||
<button style="padding: 4px 6px; font-size: 11px">A</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lrow">
|
||||
<div class="avatar">MB</div>
|
||||
<div class="grow"><div class="ltitle">Marcus Bell</div><div class="lmeta"><span>No check-in</span><span class="badge badge-danger-soft">Absent</span></div></div>
|
||||
<div class="segmented" style="width: 128px; padding: 2px">
|
||||
<button style="padding: 4px 6px; font-size: 11px">P</button>
|
||||
<button style="padding: 4px 6px; font-size: 11px">L</button>
|
||||
<button class="on" style="padding: 4px 6px; font-size: 11px">A</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lrow">
|
||||
<div class="avatar">EN</div>
|
||||
<div class="grow"><div class="ltitle">Esther Nwosu</div><div class="lmeta"><span>Checked in 13:47 via QR</span></div></div>
|
||||
<div class="segmented" style="width: 128px; padding: 2px">
|
||||
<button class="on" style="padding: 4px 6px; font-size: 11px">P</button>
|
||||
<button style="padding: 4px 6px; font-size: 11px">L</button>
|
||||
<button style="padding: 4px 6px; font-size: 11px">A</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="lrow">
|
||||
<div class="avatar">LC</div>
|
||||
<div class="grow"><div class="ltitle">Liam Chen</div><div class="lmeta"><span>Checked in 13:55 via QR</span></div></div>
|
||||
<div class="segmented" style="width: 128px; padding: 2px">
|
||||
<button class="on" style="padding: 4px 6px; font-size: 11px">P</button>
|
||||
<button style="padding: 4px 6px; font-size: 11px">L</button>
|
||||
<button style="padding: 4px 6px; font-size: 11px">A</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary btn-lg btn-block" style="margin-top: 16px">Save attendance · 18 learners</button>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Roster</b>Present / Late / Absent as one-tap segments, with the QR check-in time shown so the tutor only corrects exceptions.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 2. CHECK-IN CODE -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="13:45"></div>
|
||||
|
||||
<div class="navbar bordered">
|
||||
<a class="icon-btn plain" href="tutor-attendance.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
||||
<div class="grow"><div class="title-sm">Check-in code</div><div class="sub">Practical assessment · Bay 4</div></div>
|
||||
</div>
|
||||
|
||||
<div class="screen centered">
|
||||
<div class="card card-pad" style="text-align: center; padding: 24px 18px">
|
||||
<div class="eyebrow">Learners scan or type this</div>
|
||||
<div
|
||||
style="width: 190px; height: 190px; margin: 16px auto 14px; border-radius: var(--radius-lg); border: 1px solid var(--border); background: var(--muted); display: grid; place-items: center; color: var(--foreground)"
|
||||
>
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="92" height="92" stroke-width="1.4">
|
||||
<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/>
|
||||
<path d="M14 14h3v3h-3zM18 18h3v3h-3zM14 21h3M21 14v3"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="tnum" style="font-size: 30px; font-weight: 600; letter-spacing: 0.14em">4Q7 812</div>
|
||||
<div class="lsub" style="margin-top: 6px">Expires in 14:52 · rotates automatically</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-pad" style="margin-top: 14px">
|
||||
<div class="row between" style="margin-bottom: 10px">
|
||||
<span class="eyebrow">Checked in so far</span>
|
||||
<span class="tnum" style="font-size: 12px; font-weight: 600">14 of 18</span>
|
||||
</div>
|
||||
<div class="progress tall success" style="margin-bottom: 12px"><span style="width: 78%"></span></div>
|
||||
<div class="row" style="gap: -8px">
|
||||
<div class="avatar sm">AO</div>
|
||||
<div class="avatar sm" style="margin-left: -8px">TA</div>
|
||||
<div class="avatar sm" style="margin-left: -8px">EN</div>
|
||||
<div class="avatar sm" style="margin-left: -8px">LC</div>
|
||||
<div class="avatar sm" style="margin-left: -8px">RS</div>
|
||||
<div class="avatar sm" style="margin-left: -8px; background: var(--muted); color: var(--muted-foreground)">+9</div>
|
||||
<span class="lmeta" style="margin: 0 0 0 auto">Updating live</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-outline btn-block" style="margin-top: 14px">Show on the room display</button>
|
||||
<a class="btn btn-ghost btn-block" href="tutor-attendance.html" style="margin-top: 6px">Back to roster</a>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>QR check-in</b>A rotating code the room can scan. The roster fills itself and the tutor's job shrinks to the four people it missed.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<button class="theme-toggle" title="Toggle theme" aria-label="Toggle theme">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
|
||||
</button>
|
||||
<script src="proto.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,248 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Tutor course snapshot — ClassroomIO Mobile</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300..800&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="app-theme.css" />
|
||||
<script>
|
||||
if (localStorage.cioTheme === 'dark' || (!localStorage.cioTheme && matchMedia('(prefers-color-scheme: dark)').matches))
|
||||
document.documentElement.classList.add('dark');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="proto-head">
|
||||
<a class="back" href="index.html"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="14" height="14"><path d="m15 18-6-6 6-6"/></svg> All screens</a>
|
||||
<span class="eyebrow">Tutor · Release 3</span>
|
||||
<h1>Course snapshot, announcements & moderation</h1>
|
||||
<p>
|
||||
Read-only insight plus the two write actions a tutor genuinely needs away from a desk: post an announcement and
|
||||
moderate a thread. Authoring, analytics configuration, enrollment management, and org settings are deliberately
|
||||
absent — they stay on the web.
|
||||
</p>
|
||||
<div class="proto-links">
|
||||
<a href="tutor-grading.html">← Grading queue</a>
|
||||
<a href="tutor-attendance.html">Attendance →</a>
|
||||
<a href="community.html">Learner's community view →</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="stage">
|
||||
<!-- 1. SNAPSHOT -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="11:32"></div>
|
||||
|
||||
<div class="navbar bordered">
|
||||
<a class="icon-btn plain" href="tutor-grading.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
||||
<div class="grow"><div class="title-sm">HIPAA Awareness 2026</div><div class="sub">Acme Health Academy</div></div>
|
||||
<button class="icon-btn" data-sheet-open="announce" aria-label="Post announcement"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m3 11 18-5v12L3 13z"/><path d="M11.6 16.8a3 3 0 1 1-5.8-1.6"/></svg></button>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="card card-pad" style="margin: 12px 0 12px">
|
||||
<div class="row" style="align-items: stretch">
|
||||
<div class="stat"><div class="v tnum">64</div><div class="k">Enrolled</div></div>
|
||||
<div style="width: 1px; background: var(--border)"></div>
|
||||
<div class="stat"><div class="v tnum">71%</div><div class="k">Completed</div></div>
|
||||
<div style="width: 1px; background: var(--border)"></div>
|
||||
<div class="stat"><div class="v tnum" style="color: var(--destructive)">7</div><div class="k">Overdue</div></div>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="row" style="margin-bottom: 6px">
|
||||
<div class="progress tall success"><span style="width: 71%"></span></div>
|
||||
<span class="tnum" style="font-size: 12px; font-weight: 600">45/64</span>
|
||||
</div>
|
||||
<div class="lmeta"><span>Deadline 30 Sep 2026 · 45 days left</span></div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head" style="margin-top: 0"><h2>Needs a nudge</h2><span class="count">7</span></div>
|
||||
<div class="stack">
|
||||
<div class="lrow">
|
||||
<div class="avatar">MB</div>
|
||||
<div class="grow"><div class="ltitle">Marcus Bell</div><div class="lmeta"><span>No activity in 21 days</span><span class="sep">·</span><span>1 of 9 lessons</span></div></div>
|
||||
<button class="btn btn-outline btn-sm">Remind</button>
|
||||
</div>
|
||||
<div class="lrow">
|
||||
<div class="avatar">RS</div>
|
||||
<div class="grow"><div class="ltitle">Rina Sato</div><div class="lmeta"><span>Stalled at Lesson 4</span><span class="sep">·</span><span>9 days</span></div></div>
|
||||
<button class="btn btn-outline btn-sm">Remind</button>
|
||||
</div>
|
||||
<div class="lrow">
|
||||
<div class="avatar">JW</div>
|
||||
<div class="grow"><div class="ltitle">Joseph Wanjiru</div><div class="lmeta"><span>Waiting on your grading</span></div></div>
|
||||
<a class="btn btn-primary btn-sm" href="tutor-grading.html">Grade</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-outline btn-block" style="margin-top: 12px">Remind all 7 learners</button>
|
||||
|
||||
<div class="sec-head"><h2>Where learners get stuck</h2></div>
|
||||
<div class="card card-pad">
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Lesson 4 · Minimum necessary rule</div><div class="lmeta"><span>11 learners stalled here</span></div></div>
|
||||
<span class="badge badge-warning-soft">17%</span>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Scenario response</div><div class="lmeta"><span>Average score 6.8 / 10</span></div></div>
|
||||
<span class="badge badge-warning-soft">Low</span>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Lesson 2 · Covered entities</div><div class="lmeta"><span>4 unanswered questions</span></div></div>
|
||||
<span class="badge badge-outline">Q&A</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hint" style="margin-top: 14px">
|
||||
Full analytics, exports, and course editing live in the web dashboard.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="scrim" data-scrim-for="announce" style="display: none"></div>
|
||||
<div class="sheet" id="announce" style="display: none">
|
||||
<div class="grabber"></div>
|
||||
<div class="sheet-head">
|
||||
<div class="st">Post an announcement</div>
|
||||
<button class="icon-btn plain" data-sheet-close="announce" style="margin-left: auto" aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M18 6 6 18M6 6l12 12"/></svg></button>
|
||||
</div>
|
||||
<div class="sheet-body">
|
||||
<div class="field">
|
||||
<label class="label">Audience</label>
|
||||
<div class="segmented" data-select>
|
||||
<button class="on" data-option>Everyone</button>
|
||||
<button data-option>Overdue only</button>
|
||||
<button data-option>One cohort</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Message</label>
|
||||
<textarea class="textarea">Reminder: the HIPAA scenario response is the last item before certification. If you're stuck on the "minimum necessary" rule, Lesson 4 has a worked example at 2:15.</textarea>
|
||||
</div>
|
||||
<div class="lrow" style="margin-bottom: 12px">
|
||||
<div class="grow"><div class="ltitle">Also send as a push notification</div><div class="lsub">Respects each learner's quiet hours</div></div>
|
||||
<label class="switch"><input type="checkbox" checked /><span class="track"></span><span class="knob"></span></label>
|
||||
</div>
|
||||
<button class="btn btn-primary btn-lg btn-block">Post to 64 learners</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Snapshot & announcement</b>Enough insight to act — who's stalled and where — plus a one-tap nudge. Open the announcement sheet from the header in the live prototype.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 2. MODERATION -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="11:40"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<div class="avatar brand" style="background: oklch(0.424 0.199 265.638)">AH</div>
|
||||
<div class="grow"><div class="sub">Acme Health Academy</div><div class="title-sm">Teach</div></div>
|
||||
</div>
|
||||
|
||||
<div class="tabs" data-select>
|
||||
<button data-option>To grade</button>
|
||||
<button data-option>Attendance</button>
|
||||
<button data-option>Courses</button>
|
||||
<button class="on" data-option>Moderation</button>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="sec-head" style="margin-top: 12px"><h2>Unanswered questions</h2><span class="count">4</span></div>
|
||||
<div class="stack">
|
||||
<div class="card card-pad">
|
||||
<div class="row row-top">
|
||||
<div class="avatar">FA</div>
|
||||
<div class="grow">
|
||||
<div class="row between">
|
||||
<div class="ltitle" style="font-size: 13.5px">Fatima Ali</div>
|
||||
<span class="text-muted" style="font-size: 11px">2d</span>
|
||||
</div>
|
||||
<div class="lmeta" style="margin-top: 1px"><span>HIPAA Awareness · Lesson 2</span></div>
|
||||
<div style="font-size: 13.5px; line-height: 1.5; margin-top: 6px">
|
||||
Is a contracted cleaning company a business associate if they're never shown records?
|
||||
</div>
|
||||
<div class="row" style="gap: 8px; margin-top: 10px">
|
||||
<button class="btn btn-primary btn-sm">Answer</button>
|
||||
<button class="btn btn-outline btn-sm">Assign to tutor</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-pad">
|
||||
<div class="row row-top">
|
||||
<div class="avatar">LC</div>
|
||||
<div class="grow">
|
||||
<div class="row between">
|
||||
<div class="ltitle" style="font-size: 13.5px">Liam Chen</div>
|
||||
<span class="text-muted" style="font-size: 11px">3d</span>
|
||||
</div>
|
||||
<div class="lmeta" style="margin-top: 1px"><span>HIPAA Awareness · General</span></div>
|
||||
<div style="font-size: 13.5px; line-height: 1.5; margin-top: 6px">
|
||||
Does the training reset if I change department within the same hospital?
|
||||
</div>
|
||||
<div class="row" style="gap: 8px; margin-top: 10px">
|
||||
<button class="btn btn-primary btn-sm">Answer</button>
|
||||
<button class="btn btn-outline btn-sm">Assign to tutor</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Reported</h2><span class="count">1</span></div>
|
||||
<div class="card card-pad">
|
||||
<div class="row row-top">
|
||||
<div class="avatar">?</div>
|
||||
<div class="grow">
|
||||
<div class="row between">
|
||||
<div class="ltitle" style="font-size: 13.5px">Anonymous learner</div>
|
||||
<span class="badge badge-danger-soft">Reported ×2</span>
|
||||
</div>
|
||||
<div class="lmeta" style="margin-top: 1px"><span>SOC 2 Security Basics · Q&A</span></div>
|
||||
<div style="font-size: 13.5px; line-height: 1.5; margin-top: 6px; padding: 8px 10px; background: var(--muted); border-radius: var(--radius-md)">
|
||||
"Here's the answer key from last year's exam, DM me…"
|
||||
</div>
|
||||
<div class="row" style="gap: 8px; margin-top: 10px">
|
||||
<button class="btn btn-destructive btn-sm">Remove post</button>
|
||||
<button class="btn btn-outline btn-sm">Keep</button>
|
||||
<button class="btn btn-ghost btn-sm">View thread</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Recently handled</h2></div>
|
||||
<div class="stack stack-sm">
|
||||
<div class="lrow flat">
|
||||
<div class="media is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Marked "audit trail" answer as accepted</div><div class="lmeta"><span>Yesterday</span></div></div>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="media is-done"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M20 6 9 17l-5-5"/></svg></div>
|
||||
<div class="grow"><div class="ltitle">Removed duplicate thread</div><div class="lmeta"><span>Mon 18 Aug</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="tabbar" data-tabbar="teach" data-role="tutor" data-badges="teach:12"></nav>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Moderation</b>Unanswered questions and reported posts in one queue, using the same author/team permission rules the API enforces.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<button class="theme-toggle" title="Toggle theme" aria-label="Toggle theme">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
|
||||
</button>
|
||||
<script src="proto.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,226 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Tutor grading — ClassroomIO Mobile</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Geist:wght@300..800&display=swap" rel="stylesheet" />
|
||||
<link rel="stylesheet" href="app-theme.css" />
|
||||
<script>
|
||||
if (localStorage.cioTheme === 'dark' || (!localStorage.cioTheme && matchMedia('(prefers-color-scheme: dark)').matches))
|
||||
document.documentElement.classList.add('dark');
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<header class="proto-head">
|
||||
<a class="back" href="index.html"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" width="14" height="14"><path d="m15 18-6-6 6-6"/></svg> All screens</a>
|
||||
<span class="eyebrow">Tutor · Release 3</span>
|
||||
<h1>Grading queue</h1>
|
||||
<p>
|
||||
The Teach tab appears only when <code>orgRoles[activeOrg]</code> is ADMIN or TUTOR — one binary, a role-aware tab bar.
|
||||
A single new endpoint, <code>GET /account/grading-queue</code>, gathers submissions awaiting grading across every
|
||||
course the tutor owns, oldest first, so a backlog can be cleared between meetings.
|
||||
</p>
|
||||
<div class="proto-links">
|
||||
<a href="tutor-attendance.html">Attendance →</a>
|
||||
<a href="tutor-course.html">Course snapshot →</a>
|
||||
<a href="exercise.html">Learner's view →</a>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="stage">
|
||||
<!-- 1. QUEUE -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="11:15"></div>
|
||||
|
||||
<div class="navbar">
|
||||
<div class="avatar brand" style="background: oklch(0.424 0.199 265.638)">AH</div>
|
||||
<div class="grow"><div class="sub">Acme Health Academy</div><div class="title-sm">Teach</div></div>
|
||||
<button class="icon-btn" aria-label="Filter"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M3 5h18l-7 8v6l-4 2v-8z"/></svg></button>
|
||||
</div>
|
||||
|
||||
<div class="tabs" data-select>
|
||||
<button class="on" data-option>To grade</button>
|
||||
<button data-option>Attendance</button>
|
||||
<button data-option>Courses</button>
|
||||
<button data-option>Moderation</button>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="card card-pad" style="margin: 12px 0 14px">
|
||||
<div class="row" style="align-items: stretch">
|
||||
<div class="stat"><div class="v tnum" style="color: var(--destructive)">3</div><div class="k">Over 3 days</div></div>
|
||||
<div style="width: 1px; background: var(--border)"></div>
|
||||
<div class="stat"><div class="v tnum">12</div><div class="k">Awaiting</div></div>
|
||||
<div style="width: 1px; background: var(--border)"></div>
|
||||
<div class="stat"><div class="v tnum">28</div><div class="k">Graded this week</div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head" style="margin-top: 0"><h2>Waiting longest</h2></div>
|
||||
<div class="stack">
|
||||
<a class="lrow" href="tutor-grading.html">
|
||||
<div class="avatar">JW</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Joseph Wanjiru</div>
|
||||
<div class="lsub">HIPAA Awareness · Scenario response</div>
|
||||
<div class="lmeta"><span class="badge badge-danger-soft">Waiting 4 days</span><span class="badge badge-outline">Written + video</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
<a class="lrow" href="tutor-grading.html">
|
||||
<div class="avatar">RS</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Rina Sato</div>
|
||||
<div class="lsub">SOC 2 Security Basics · Incident write-up</div>
|
||||
<div class="lmeta"><span class="badge badge-warning-soft">Waiting 3 days</span><span class="badge badge-outline">Written</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
<a class="lrow" href="tutor-grading.html">
|
||||
<div class="avatar">MB</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Marcus Bell</div>
|
||||
<div class="lsub">HIPAA Awareness · Scenario response</div>
|
||||
<div class="lmeta"><span class="badge badge-warning-soft">Waiting 3 days</span><span class="badge badge-outline">Photo evidence</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="sec-head"><h2>Recent</h2><span class="count">9</span></div>
|
||||
<div class="stack">
|
||||
<a class="lrow" href="tutor-grading.html">
|
||||
<div class="avatar">EN</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Esther Nwosu</div>
|
||||
<div class="lsub">SOC 2 Security Basics · Incident write-up</div>
|
||||
<div class="lmeta"><span>Submitted 6h ago</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
<a class="lrow" href="tutor-grading.html">
|
||||
<div class="avatar">LC</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Liam Chen</div>
|
||||
<div class="lsub">HIPAA Awareness · Scenario response</div>
|
||||
<div class="lmeta"><span>Submitted 9h ago</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
<a class="lrow" href="tutor-grading.html">
|
||||
<div class="avatar">FA</div>
|
||||
<div class="grow">
|
||||
<div class="ltitle">Fatima Ali</div>
|
||||
<div class="lsub">SOC 2 Security Basics · Incident write-up</div>
|
||||
<div class="lmeta"><span>Submitted yesterday</span></div>
|
||||
</div>
|
||||
<span class="chev"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="hint" style="margin-top: 14px">
|
||||
Authoring, org settings, and analytics configuration stay on the web — this tab is only the work that can't wait.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<nav class="tabbar" data-tabbar="teach" data-role="tutor" data-badges="teach:12"></nav>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Queue</b>Oldest first, with waiting time as the primary signal and the submission type visible before you open it.</figcaption>
|
||||
</figure>
|
||||
|
||||
<!-- 2. REVIEW -->
|
||||
<figure class="device">
|
||||
<div class="phone">
|
||||
<div class="phone-inner">
|
||||
<div class="notch"></div>
|
||||
<div class="statusbar" data-status="11:18"></div>
|
||||
|
||||
<div class="navbar bordered">
|
||||
<a class="icon-btn plain" href="tutor-grading.html" aria-label="Back"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m15 18-6-6 6-6"/></svg></a>
|
||||
<div class="grow"><div class="title-sm">Joseph Wanjiru</div><div class="sub">1 of 12 · HIPAA Awareness</div></div>
|
||||
<button class="icon-btn" aria-label="Next submission"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="m9 18 6-6-6-6"/></svg></button>
|
||||
</div>
|
||||
|
||||
<div class="screen">
|
||||
<div class="banner banner-danger" style="margin: 12px 0 14px">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="10"/><path d="M12 6v6l4 2"/></svg>
|
||||
<div class="grow"><div class="bt">Waiting 4 days</div><div class="bd">Learner's certification is blocked until this is graded</div></div>
|
||||
</div>
|
||||
|
||||
<div class="eyebrow">Question · 10 points</div>
|
||||
<div style="font-size: 15px; font-weight: 600; line-height: 1.4; margin: 6px 0 12px">
|
||||
A colleague asks you to look up a patient record for a friend. Describe your response and the escalation path.
|
||||
</div>
|
||||
|
||||
<div class="card card-pad" style="margin-bottom: 10px">
|
||||
<div class="eyebrow" style="margin-bottom: 8px">Written answer</div>
|
||||
<div style="font-size: 13.5px; line-height: 1.6">
|
||||
I would decline and explain that access has to be tied to a care relationship. I'd remind them that the audit
|
||||
log records every lookup by user, so an access without a clinical reason is traceable. If they pressed, I'd
|
||||
report it to the privacy officer the same day using the internal form.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card card-pad" style="margin-bottom: 14px">
|
||||
<div class="eyebrow" style="margin-bottom: 8px">Video response · 0:48</div>
|
||||
<div class="player" style="margin: 0; border-radius: var(--radius-md); aspect-ratio: 16/10">
|
||||
<div class="play" style="width: 44px; height: 44px"><svg viewBox="0 0 24 24" fill="currentColor" stroke="none" width="18" height="18"><path d="M8 5v14l11-7z"/></svg></div>
|
||||
<div class="ctrl" style="padding: 14px 10px 8px"><div class="bar"><span style="width: 0%"></span></div><div class="meta"><span>0:00</span><span style="opacity: 0.7">0:48</span></div></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sec-head" style="margin-top: 0"><h2>Rubric</h2></div>
|
||||
<div class="card card-pad" style="margin-bottom: 12px">
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Declines the request</div></div>
|
||||
<span class="badge badge-success-soft">3 / 3</span>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Cites the audit trail</div></div>
|
||||
<span class="badge badge-success-soft">3 / 3</span>
|
||||
</div>
|
||||
<div class="lrow flat">
|
||||
<div class="grow"><div class="ltitle">Names the escalation path</div></div>
|
||||
<span class="badge badge-warning-soft">2 / 4</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Score</label>
|
||||
<div class="row" style="gap: 8px">
|
||||
<input class="input tnum" value="8" style="width: 76px; text-align: center; font-size: 17px; font-weight: 600" />
|
||||
<span class="text-muted" style="font-size: 14px">/ 10 · pass mark 7</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field">
|
||||
<label class="label">Feedback</label>
|
||||
<textarea class="textarea">Good instinct on the audit trail. Name the privacy officer's 24-hour SLA next time — that's the part auditors check.</textarea>
|
||||
<div class="row" style="gap: 8px; margin-top: 8px">
|
||||
<button class="btn btn-outline btn-sm"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><path d="M12 2a3 3 0 0 0-3 3v7a3 3 0 0 0 6 0V5a3 3 0 0 0-3-3"/><path d="M19 10v2a7 7 0 0 1-14 0v-2M12 19v3"/></svg> Record audio</button>
|
||||
<span class="hint" style="margin: 0">Faster than typing on a phone</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary btn-lg btn-block">Submit grade & next</button>
|
||||
<button class="btn btn-outline btn-block" style="margin-top: 8px">Request resubmission</button>
|
||||
</div>
|
||||
<div class="home-bar"></div>
|
||||
</div>
|
||||
</div>
|
||||
<figcaption><b>Review</b>Answer, video, and rubric on one screen, with recorded audio feedback because typing a paragraph on a phone is the real blocker.</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
|
||||
<button class="theme-toggle" title="Toggle theme" aria-label="Toggle theme">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg>
|
||||
</button>
|
||||
<script src="proto.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user