a1c5fecd83
- Switch Prisma schema from SQLite to PostgreSQL - Add netlify.toml for Netlify deployment config - Add @netlify/plugin-nextjs for serverless Next.js - Remove Docker build files (Dockerfile, docker-compose, start.sh) - Remove auto-healer and scripts directories - Update next.config.ts (remove standalone output) - Database: falah_mobile_v1 on Contabo Postgres
724 lines
25 KiB
HTML
724 lines
25 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Falah Mobile — Micro Learning Handover</title>
|
||
<style>
|
||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800;900&family=JetBrains+Mono:wght@400;500&display=swap');
|
||
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
||
body {
|
||
background: #07090C;
|
||
color: #E8E0D0;
|
||
font-family: 'Inter', -apple-system, sans-serif;
|
||
min-height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 40px 24px;
|
||
}
|
||
|
||
.container {
|
||
max-width: 1200px;
|
||
width: 100%;
|
||
}
|
||
|
||
/* Header */
|
||
.header {
|
||
text-align: center;
|
||
margin-bottom: 48px;
|
||
position: relative;
|
||
}
|
||
|
||
.header::after {
|
||
content: '';
|
||
position: absolute;
|
||
bottom: -24px;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: 120px;
|
||
height: 2px;
|
||
background: linear-gradient(90deg, transparent, #C9A84C, transparent);
|
||
}
|
||
|
||
.badge {
|
||
display: inline-block;
|
||
padding: 4px 16px;
|
||
border-radius: 20px;
|
||
background: rgba(201, 168, 76, 0.1);
|
||
border: 1px solid rgba(201, 168, 76, 0.25);
|
||
color: #C9A84C;
|
||
font-size: 12px;
|
||
font-weight: 600;
|
||
letter-spacing: 1.5px;
|
||
text-transform: uppercase;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.header h1 {
|
||
font-size: 42px;
|
||
font-weight: 800;
|
||
letter-spacing: -1px;
|
||
line-height: 1.15;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.header h1 span.gold { color: #C9A84C; }
|
||
.header h1 span.green { color: #00C48C; }
|
||
|
||
.header p {
|
||
color: #8A8478;
|
||
font-size: 16px;
|
||
max-width: 600px;
|
||
margin: 12px auto 0;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
/* Repo Bar */
|
||
.repo-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
justify-content: center;
|
||
flex-wrap: wrap;
|
||
margin-top: 20px;
|
||
padding: 12px 24px;
|
||
background: #0F1219;
|
||
border: 1px solid #1A1F2E;
|
||
border-radius: 12px;
|
||
font-size: 13px;
|
||
color: #8A8478;
|
||
}
|
||
.repo-bar .label {
|
||
color: #C9A84C;
|
||
font-weight: 600;
|
||
font-size: 11px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 1px;
|
||
}
|
||
.repo-bar code {
|
||
font-family: 'JetBrains Mono', monospace;
|
||
color: #E8E0D0;
|
||
background: #1A1F2E;
|
||
padding: 2px 8px;
|
||
border-radius: 4px;
|
||
font-size: 12px;
|
||
}
|
||
.repo-bar .dot {
|
||
color: #1A1F2E;
|
||
}
|
||
|
||
/* Bento Grid */
|
||
.bento {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr 1fr;
|
||
gap: 16px;
|
||
margin-top: 32px;
|
||
}
|
||
|
||
.card {
|
||
background: #0F1219;
|
||
border: 1px solid #1A1F2E;
|
||
border-radius: 16px;
|
||
padding: 24px;
|
||
position: relative;
|
||
overflow: hidden;
|
||
transition: border-color 0.2s, transform 0.15s;
|
||
}
|
||
|
||
.card:hover {
|
||
border-color: rgba(201, 168, 76, 0.3);
|
||
transform: translateY(-2px);
|
||
}
|
||
|
||
.card::before {
|
||
content: '';
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
height: 3px;
|
||
border-radius: 16px 16px 0 0;
|
||
}
|
||
|
||
.card.gold::before { background: linear-gradient(90deg, #C9A84C, #E8C84C); }
|
||
.card.green::before { background: linear-gradient(90deg, #00C48C, #00E8A0); }
|
||
.card.blue::before { background: linear-gradient(90deg, #4A90D9, #6BB5FF); }
|
||
.card.purple::before { background: linear-gradient(90deg, #9B59B6, #C084FC); }
|
||
.card.orange::before { background: linear-gradient(90deg, #E67E22, #F0A050); }
|
||
.card.teal::before { background: linear-gradient(90deg, #1ABC9C, #48D1B0); }
|
||
|
||
/* Hero card — spans 2 columns */
|
||
.card.hero {
|
||
grid-column: span 2;
|
||
background: linear-gradient(135deg, #0F1219 0%, #151A28 100%);
|
||
border-color: rgba(201, 168, 76, 0.15);
|
||
}
|
||
.card.hero::before { background: linear-gradient(90deg, #C9A84C, #00C48C, #4A90D9); }
|
||
|
||
/* Side card — right column */
|
||
.card.side {
|
||
grid-column: span 1;
|
||
}
|
||
|
||
/* Full width */
|
||
.card.full {
|
||
grid-column: span 3;
|
||
}
|
||
|
||
/* Double width */
|
||
.card.double {
|
||
grid-column: span 2;
|
||
}
|
||
|
||
.card-icon {
|
||
width: 36px;
|
||
height: 36px;
|
||
border-radius: 10px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 18px;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.card-icon.gold { background: rgba(201, 168, 76, 0.12); }
|
||
.card-icon.green { background: rgba(0, 196, 140, 0.12); }
|
||
.card-icon.blue { background: rgba(74, 144, 217, 0.12); }
|
||
.card-icon.purple { background: rgba(155, 89, 182, 0.12); }
|
||
.card-icon.orange { background: rgba(230, 126, 34, 0.12); }
|
||
.card-icon.teal { background: rgba(26, 188, 156, 0.12); }
|
||
|
||
.card h2 {
|
||
font-size: 16px;
|
||
font-weight: 700;
|
||
margin-bottom: 6px;
|
||
letter-spacing: -0.3px;
|
||
}
|
||
|
||
.card h3 {
|
||
font-size: 13px;
|
||
font-weight: 600;
|
||
color: #8A8478;
|
||
margin-bottom: 14px;
|
||
letter-spacing: 0.3px;
|
||
text-transform: uppercase;
|
||
}
|
||
|
||
.card p {
|
||
font-size: 13px;
|
||
line-height: 1.6;
|
||
color: #A8A090;
|
||
}
|
||
|
||
.card ul {
|
||
list-style: none;
|
||
margin-top: 8px;
|
||
}
|
||
.card ul li {
|
||
font-size: 13px;
|
||
line-height: 1.5;
|
||
color: #A8A090;
|
||
padding: 6px 0;
|
||
border-bottom: 1px solid rgba(26, 31, 46, 0.6);
|
||
display: flex;
|
||
align-items: flex-start;
|
||
gap: 8px;
|
||
}
|
||
.card ul li:last-child { border-bottom: none; }
|
||
.card ul li .bullet {
|
||
color: #C9A84C;
|
||
flex-shrink: 0;
|
||
font-size: 14px;
|
||
line-height: 1.5;
|
||
}
|
||
.card ul li strong { color: #E8E0D0; font-weight: 600; }
|
||
|
||
.stat-grid {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 10px;
|
||
margin-top: 8px;
|
||
}
|
||
|
||
.stat-item {
|
||
background: rgba(0,0,0,0.2);
|
||
border-radius: 10px;
|
||
padding: 12px 14px;
|
||
text-align: center;
|
||
}
|
||
|
||
.stat-number {
|
||
font-size: 28px;
|
||
font-weight: 800;
|
||
letter-spacing: -1px;
|
||
line-height: 1;
|
||
}
|
||
|
||
.stat-number.gold { color: #C9A84C; }
|
||
.stat-number.green { color: #00C48C; }
|
||
.stat-number.blue { color: #4A90D9; }
|
||
|
||
.stat-label {
|
||
font-size: 11px;
|
||
color: #8A8478;
|
||
margin-top: 4px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.tech-pill {
|
||
display: inline-block;
|
||
padding: 3px 10px;
|
||
border-radius: 6px;
|
||
font-size: 11px;
|
||
font-weight: 600;
|
||
margin: 2px 3px;
|
||
background: rgba(201, 168, 76, 0.08);
|
||
color: #C9A84C;
|
||
border: 1px solid rgba(201, 168, 76, 0.15);
|
||
}
|
||
|
||
.tech-pill.green {
|
||
background: rgba(0, 196, 140, 0.08);
|
||
color: #00C48C;
|
||
border-color: rgba(0, 196, 140, 0.15);
|
||
}
|
||
.tech-pill.blue {
|
||
background: rgba(74, 144, 217, 0.08);
|
||
color: #6BB5FF;
|
||
border-color: rgba(74, 144, 217, 0.15);
|
||
}
|
||
.tech-pill.purple {
|
||
background: rgba(155, 89, 182, 0.08);
|
||
color: #C084FC;
|
||
border-color: rgba(155, 89, 182, 0.15);
|
||
}
|
||
.tech-pill.orange {
|
||
background: rgba(230, 126, 34, 0.08);
|
||
color: #F0A050;
|
||
border-color: rgba(230, 126, 34, 0.15);
|
||
}
|
||
|
||
.status-dot {
|
||
display: inline-block;
|
||
width: 8px;
|
||
height: 8px;
|
||
border-radius: 50%;
|
||
margin-right: 6px;
|
||
}
|
||
.status-dot.green { background: #00C48C; box-shadow: 0 0 8px rgba(0,196,140,0.4); }
|
||
.status-dot.yellow { background: #C9A84C; box-shadow: 0 0 8px rgba(201,168,76,0.4); }
|
||
.status-dot.red { background: #E74C3C; box-shadow: 0 0 8px rgba(231,76,60,0.4); }
|
||
|
||
.route-block {
|
||
font-family: 'JetBrains Mono', monospace;
|
||
font-size: 11px;
|
||
line-height: 1.8;
|
||
color: #6BB5FF;
|
||
background: rgba(0,0,0,0.3);
|
||
border-radius: 8px;
|
||
padding: 12px 14px;
|
||
margin-top: 8px;
|
||
}
|
||
.route-block .comment { color: #5A5A5A; }
|
||
.route-block .method { color: #00C48C; }
|
||
|
||
.two-col {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 16px;
|
||
margin-top: 8px;
|
||
}
|
||
|
||
.cmd-box {
|
||
background: rgba(0,0,0,0.3);
|
||
border-radius: 8px;
|
||
padding: 10px 14px;
|
||
font-family: 'JetBrains Mono', monospace;
|
||
font-size: 11px;
|
||
color: #8A8478;
|
||
margin-top: 6px;
|
||
line-height: 1.8;
|
||
}
|
||
.cmd-box .prompt { color: #00C48C; }
|
||
.cmd-box .cmd { color: #E8E0D0; }
|
||
|
||
.issue-list li {
|
||
font-size: 12px;
|
||
padding: 7px 0 !important;
|
||
}
|
||
.issue-list li .issue-id {
|
||
color: #C9A84C;
|
||
font-weight: 600;
|
||
font-size: 11px;
|
||
}
|
||
|
||
.hero-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 14px;
|
||
}
|
||
.hero-content .row {
|
||
display: flex;
|
||
gap: 20px;
|
||
flex-wrap: wrap;
|
||
}
|
||
.hero-content .info-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 8px;
|
||
font-size: 13px;
|
||
}
|
||
.hero-content .info-item .key {
|
||
color: #8A8478;
|
||
font-weight: 500;
|
||
}
|
||
.hero-content .info-item .val {
|
||
color: #E8E0D0;
|
||
font-weight: 600;
|
||
}
|
||
.hero-content .info-item .val.code {
|
||
font-family: 'JetBrains Mono', monospace;
|
||
font-size: 12px;
|
||
background: #1A1F2E;
|
||
padding: 2px 8px;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.section-tag {
|
||
font-size: 10px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 1.5px;
|
||
font-weight: 700;
|
||
margin-bottom: 10px;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
.progress-bar {
|
||
height: 4px;
|
||
background: #1A1F2E;
|
||
border-radius: 2px;
|
||
margin: 10px 0 4px;
|
||
overflow: hidden;
|
||
}
|
||
.progress-bar .fill {
|
||
height: 100%;
|
||
border-radius: 2px;
|
||
background: linear-gradient(90deg, #C9A84C, #00C48C);
|
||
}
|
||
.progress-label {
|
||
font-size: 11px;
|
||
color: #8A8478;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.price-badge {
|
||
display: inline-flex;
|
||
align-items: center;
|
||
gap: 6px;
|
||
padding: 4px 10px;
|
||
background: rgba(0, 196, 140, 0.1);
|
||
border: 1px solid rgba(0, 196, 140, 0.2);
|
||
border-radius: 6px;
|
||
font-size: 12px;
|
||
color: #00C48C;
|
||
font-weight: 600;
|
||
}
|
||
.price-badge.warn {
|
||
background: rgba(201, 168, 76, 0.08);
|
||
border-color: rgba(201, 168, 76, 0.2);
|
||
color: #C9A84C;
|
||
}
|
||
.price-badge.danger {
|
||
background: rgba(231, 76, 60, 0.08);
|
||
border-color: rgba(231, 76, 60, 0.2);
|
||
color: #E74C3C;
|
||
}
|
||
|
||
/* Footer */
|
||
.footer {
|
||
text-align: center;
|
||
margin-top: 48px;
|
||
padding-top: 24px;
|
||
border-top: 1px solid #1A1F2E;
|
||
color: #5A5A5A;
|
||
font-size: 12px;
|
||
letter-spacing: 0.3px;
|
||
}
|
||
|
||
/* Responsive */
|
||
@media (max-width: 900px) {
|
||
.bento { grid-template-columns: 1fr 1fr; }
|
||
.card.hero { grid-column: span 2; }
|
||
.card.full { grid-column: span 2; }
|
||
.card.double { grid-column: span 1; }
|
||
.two-col { grid-template-columns: 1fr; }
|
||
}
|
||
@media (max-width: 640px) {
|
||
.bento { grid-template-columns: 1fr; }
|
||
.card.hero, .card.full, .card.double { grid-column: span 1; }
|
||
.header h1 { font-size: 28px; }
|
||
body { padding: 20px 12px; }
|
||
.stat-grid { grid-template-columns: 1fr; }
|
||
.repo-bar { flex-direction: column; text-align: center; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="container">
|
||
|
||
<!-- Header -->
|
||
<div class="header">
|
||
<div class="badge">📚 Micro Learning Platform</div>
|
||
<h1><span class="gold">Falah Mobile</span> <span style="color:#5A5A5A;font-weight:300;">—</span> <span class="green">Handover</span></h1>
|
||
<p>Complete E2E system for browsing, purchasing, consuming, and certifying bite-sized (5 min) courses derived from books.</p>
|
||
|
||
<div class="repo-bar">
|
||
<span class="label">Repository</span>
|
||
<code>git.falahos.my/wmj/falah-mobile.git</code>
|
||
<span class="dot">·</span>
|
||
<span class="label">Source</span>
|
||
<code>/root/falah-mobile</code>
|
||
<span class="dot">·</span>
|
||
<span class="label">Branch</span>
|
||
<code>main</code>
|
||
<span class="dot">·</span>
|
||
<span class="label">Commit</span>
|
||
<code>bfd3509</code>
|
||
<span class="dot">·</span>
|
||
<span class="label">GitHub</span>
|
||
<code>maifors/falah-mobile</code>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Bento Grid -->
|
||
<div class="bento">
|
||
|
||
<!-- Hero Card: Overview -->
|
||
<div class="card hero">
|
||
<div class="hero-content">
|
||
<div style="display:flex;align-items:center;gap:12px;margin-bottom:4px;">
|
||
<div class="card-icon gold" style="margin-bottom:0;">📊</div>
|
||
<div>
|
||
<h2 style="font-size:20px;">Phase 1 Complete — Live in Production</h2>
|
||
<p style="color:#8A8478;font-size:13px;">Production: <a href="https://falahos.my/mobile" style="color:#00C48C;text-decoration:none;">falahos.my/mobile</a> · Staging: <a href="https://falahos.my:4014/mobile" style="color:#C9A84C;text-decoration:none;">falahos.my:4014/mobile</a></p>
|
||
</div>
|
||
</div>
|
||
<div class="row">
|
||
<div class="info-item"><span class="key">Deployment</span> <span class="val code">Docker + Traefik</span></div>
|
||
<div class="info-item"><span class="key">Host</span> <span class="val code">Contabo VPS (Ubuntu 24.04)</span></div>
|
||
<div class="info-item"><span class="key">Auth</span> <span class="val code">Ummah ID (JWT)</span></div>
|
||
<div class="info-item"><span class="key">Payments</span> <span class="val code">Polar.sh</span></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Cell: ✅ Complete — Courses -->
|
||
<div class="card gold">
|
||
<div class="card-icon gold">✅</div>
|
||
<h2>Courses Built</h2>
|
||
<div class="stat-grid">
|
||
<div class="stat-item">
|
||
<div class="stat-number gold">4</div>
|
||
<div class="stat-label">Courses Live</div>
|
||
</div>
|
||
<div class="stat-item">
|
||
<div class="stat-number green">21</div>
|
||
<div class="stat-label">Modules</div>
|
||
</div>
|
||
<div class="stat-item">
|
||
<div class="stat-number blue">6</div>
|
||
<div class="stat-label">Categories</div>
|
||
</div>
|
||
<div class="stat-item">
|
||
<div class="stat-number" style="color:#F0A050;">35</div>
|
||
<div class="stat-label">Courses Left</div>
|
||
</div>
|
||
</div>
|
||
<div style="margin-top:12px;">
|
||
<div style="display:flex;gap:6px;flex-wrap:wrap;">
|
||
<span class="tech-pill">Atomic Habits</span>
|
||
<span class="tech-pill green">Defining Decade</span>
|
||
<span class="tech-pill blue">Simplicity Parenting</span>
|
||
<span class="tech-pill orange">Tao Te Ching</span>
|
||
</div>
|
||
<div class="progress-bar">
|
||
<div class="fill" style="width:10%"></div>
|
||
</div>
|
||
<div class="progress-label">
|
||
<span>Library completion</span>
|
||
<span>4 / 39 courses (10%)</span>
|
||
</div>
|
||
</div>
|
||
<p style="margin-top:10px;font-size:12px;">All 4 in <strong>Self Improvement</strong>. Quran, Hadith, Prayer, History, Finance categories empty — ready for content.</p>
|
||
</div>
|
||
|
||
<!-- Cell: ✅ Complete — Features -->
|
||
<div class="card green">
|
||
<div class="card-icon green">🎓</div>
|
||
<h2>Delivery Features</h2>
|
||
<ul>
|
||
<li><span class="bullet">✦</span> <strong>Course catalog</strong> — search bar + category filter tabs</li>
|
||
<li><span class="bullet">✦</span> <strong>Module reader</strong> — markdown content + progress bar + quiz badges</li>
|
||
<li><span class="bullet">✦</span> <strong>TTS audio</strong> — Web Speech API, Google UK English Female (preferred)</li>
|
||
<li><span class="bullet">✦</span> <strong>Markdown stripping</strong> — 11 patterns clean text for TTS</li>
|
||
<li><span class="bullet">✦</span> <strong>Quiz system</strong> — 3–5 MCQs per module, submit/score/retry</li>
|
||
<li><span class="bullet">✦</span> <strong>Certificate issuance</strong> — auto-issued on 100% completion, PDF + serial</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<!-- Cell: 💳 Payments -->
|
||
<div class="card purple">
|
||
<div class="card-icon purple">💳</div>
|
||
<h2>Payments & Subscription</h2>
|
||
<ul>
|
||
<li>
|
||
<span class="bullet">✦</span>
|
||
<span class="price-badge">$9.99/mo</span> <strong>Learn Pass Monthly</span></strong> <span style="color:#00C48C;">✅ Live</span>
|
||
</li>
|
||
<li>
|
||
<span class="bullet">✦</span>
|
||
<span class="price-badge">$79.99/yr</span> <strong>Learn Pass Annual</span></strong> <span style="color:#00C48C;">✅ Live</span>
|
||
</li>
|
||
<li>
|
||
<span class="bullet">✦</span>
|
||
<span class="price-badge danger">$4.99 each</span> <strong>One-time course purchases</strong> <span style="color:#E74C3C;">❌ Not mapped</span>
|
||
</li>
|
||
<li style="font-size:12px;color:#C9A84C;padding-top:8px;">
|
||
⚠ <strong>Action:</strong> Add Polar price IDs for 4 new courses to <code style="font-size:11px;">checkout/route.ts</code> PRODUCTS map
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<!-- Cell: 📋 Pending -->
|
||
<div class="card orange">
|
||
<div class="card-icon orange">📋</div>
|
||
<h2>Priority Backlog</h2>
|
||
<ul>
|
||
<li><span class="bullet">✦</span> <strong>35 more courses</strong> — build & seed across 6 categories</li>
|
||
<li><span class="bullet">✦</span> <strong>Checkout mapping</strong> — Polar price IDs for each new course</li>
|
||
<li><span class="bullet">✦</span> <strong>Course thumbnails</strong> — replace emoji/gradient with real cover art</li>
|
||
<li><span class="bullet">✦</span> <strong>Gitea sync</strong> — test course sync script</li>
|
||
<li><span class="bullet">✦</span> <strong>Server-side TTS</strong> — Azure / ElevenLabs for higher quality</li>
|
||
<li><span class="bullet">✦</span> <strong>Admin dashboard</strong> — course management UI</li>
|
||
<li><span class="bullet">✦</span> <strong>Analytics</strong> — completion rates, quiz scores</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<!-- Cell: 📦 Tech Stack (double width) -->
|
||
<div class="card blue double">
|
||
<div class="card-icon blue">📦</div>
|
||
<h2>Tech Stack</h2>
|
||
<div style="margin-top:8px;">
|
||
<div style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:12px;">
|
||
<span class="tech-pill green">Next.js 16</span>
|
||
<span class="tech-pill">TypeScript</span>
|
||
<span class="tech-pill blue">SQLite</span>
|
||
<span class="tech-pill purple">Prisma v5.22</span>
|
||
<span class="tech-pill orange">Tailwind CSS</span>
|
||
<span class="tech-pill green">Polar.sh</span>
|
||
<span class="tech-pill blue">Docker</span>
|
||
<span class="tech-pill">Traefik</span>
|
||
<span class="tech-pill green">Web Speech API</span>
|
||
<span class="tech-pill">Gitea</span>
|
||
</div>
|
||
<div class="two-col">
|
||
<div>
|
||
<p style="color:#8A8478;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:4px;">Database</p>
|
||
<p style="font-size:12px;">SQLite (<code style="font-family:'JetBrains Mono',monospace;font-size:11px;">/app/data/dev.db</code>) via Prisma ORM</p>
|
||
<p style="font-size:12px;margin-top:6px;">Schema: <code style="font-family:'JetBrains Mono',monospace;font-size:11px;color:#C9A84C;">prisma/schema.prisma</code></p>
|
||
</div>
|
||
<div>
|
||
<p style="color:#8A8478;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:4px;">Deployment</p>
|
||
<p style="font-size:12px;">Source → <code style="font-family:'JetBrains Mono',monospace;font-size:11px;">npm run build</code> → Docker → Traefik reverse proxy</p>
|
||
<p style="font-size:12px;margin-top:6px;">Host: Contabo VPS</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Cell: 🏗 Architecture (full width) -->
|
||
<div class="card full">
|
||
<div style="display:flex;align-items:center;gap:12px;margin-bottom:8px;">
|
||
<div class="card-icon teal" style="margin-bottom:0;">🏗</div>
|
||
<h2>Route Architecture</h2>
|
||
</div>
|
||
<div class="route-block">
|
||
<span class="comment"># Next.js App Router — basePath: "/mobile"</span><br>
|
||
<span class="method">GET</span> /mobile/learn <span class="comment">← Course catalog (search + category tabs)</span><br>
|
||
<span class="method">GET</span> /mobile/learn/<span style="color:#E8E0D0;">[slug]</span> <span class="comment">← Course detail + module timeline</span><br>
|
||
<span class="method">GET</span> /mobile/learn/<span style="color:#E8E0D0;">[slug]/[moduleId]</span> <span class="comment">← Module reader + TTS + Quiz</span><br>
|
||
<span class="method">GET</span> /mobile/api/learn/courses <span class="comment">← ?category=&search=</span><br>
|
||
<span class="method">GET</span> /mobile/api/learn/courses/<span style="color:#E8E0D0;">[slug]</span> <span class="comment">← Course detail API</span><br>
|
||
<span class="method">GET</span> /mobile/api/learn/categories <span class="comment">← List categories</span><br>
|
||
<span class="method">POST</span> /mobile/api/learn/checkout <span class="comment">← Polar.sh checkout</span><br>
|
||
<span class="method">POST</span> /mobile/api/learn/progress <span class="comment">← Mark module complete + auto-certificate</span><br>
|
||
<span class="method">GET</span> /mobile/api/learn/certificates <span class="comment">← User certificates</span><br>
|
||
<span class="method">POST</span> /mobile/api/learn/sync <span class="comment">← Gitea sync</span>
|
||
</div>
|
||
|
||
<div style="margin-top:16px;">
|
||
<p style="color:#8A8478;font-size:11px;font-weight:600;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:6px;">Key Source Files (26 files)</p>
|
||
<div style="display:flex;flex-wrap:wrap;gap:4px;">
|
||
<span class="tech-pill" style="font-family:'JetBrains Mono',monospace;">prisma/schema.prisma</span>
|
||
<span class="tech-pill green" style="font-family:'JetBrains Mono',monospace;">learn/page.tsx</span>
|
||
<span class="tech-pill blue" style="font-family:'JetBrains Mono',monospace;">[slug]/page.tsx</span>
|
||
<span class="tech-pill orange" style="font-family:'JetBrains Mono',monospace;">checkout/route.ts</span>
|
||
<span class="tech-pill" style="font-family:'JetBrains Mono',monospace;">progress/route.ts</span>
|
||
<span class="tech-pill green" style="font-family:'JetBrains Mono',monospace;">courses/route.ts</span>
|
||
<span class="tech-pill purple" style="font-family:'JetBrains Mono',monospace;">lib/learn.ts</span>
|
||
<span class="tech-pill" style="font-family:'JetBrains Mono',monospace;">Dockerfile</span>
|
||
<span class="tech-pill blue" style="font-family:'JetBrains Mono',monospace;">LearnPassCard.tsx</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Cell: ⚙️ Commands -->
|
||
<div class="card gold">
|
||
<div class="card-icon gold">⚙️</div>
|
||
<h2>Common Commands</h2>
|
||
<div class="cmd-box">
|
||
<span class="prompt">$</span> <span class="cmd">cd /root/falah-mobile</span><br>
|
||
<span class="prompt">$</span> <span class="cmd">npm run build</span><br>
|
||
<span class="prompt">$</span> <span class="cmd">docker build -t falah-mobile:latest .</span><br>
|
||
<span class="prompt">$</span> <span class="cmd">docker compose up -d --no-deps falah-mobile</span><br>
|
||
<span class="prompt">$</span> <span class="cmd">npx prisma db push</span>
|
||
</div>
|
||
<p style="margin-top:10px;font-size:12px;">DB ops via <code style="font-size:11px;">docker cp</code> + <code style="font-size:11px;">sqlite3</code> for quick data fixes</p>
|
||
</div>
|
||
|
||
<!-- Cell: 🐛 Known Issues -->
|
||
<div class="card double" style="border-color:rgba(231,76,60,0.2);">
|
||
<div class="card-icon orange" style="background:rgba(231,76,60,0.12);">🐛</div>
|
||
<h2 style="color:#E74C3C;">Known Issues</h2>
|
||
<ul class="issue-list">
|
||
<li><span class="issue-id">#1</span> <span class="bullet">🟡</span> <strong>Health check slow</strong> — 30s+ but API works immediately</li>
|
||
<li><span class="issue-id">#2</span> <span class="bullet">🟡</span> <strong>.env permission error</strong> — <code style="font-size:11px;">EACCES</code> in logs (env still loads correctly)</li>
|
||
<li><span class="issue-id">#3</span> <span class="bullet">🔴</span> <strong>Checkout missing courses</strong> — Only 3 old courses in PRODUCTS map</li>
|
||
<li><span class="issue-id">#4</span> <span class="bullet">🟡</span> <strong>Staging DB sharing</strong> — prod & staging share same volume</li>
|
||
<li><span class="issue-id">#5</span> <span class="bullet">🟢</span> <strong>Missing thumbnails</strong> — emoji/gradient placeholders</li>
|
||
<li><span class="issue-id">#6</span> <span class="bullet">🟢</span> <strong>Course card navigation</strong> — <code style="font-size:11px;">stopPropagation</code> edge case</li>
|
||
</ul>
|
||
<div style="margin-top:10px;display:flex;gap:12px;font-size:12px;">
|
||
<span><span class="status-dot green"></span> Low</span>
|
||
<span><span class="status-dot yellow"></span> Medium</span>
|
||
<span><span class="status-dot red"></span> High</span>
|
||
</div>
|
||
</div>
|
||
|
||
</div>
|
||
|
||
<!-- Footer -->
|
||
<div class="footer">
|
||
FalahOS · Generated 2026 · falahos.my/mobile · <span style="color:#C9A84C;">Next: Build 35 more courses →</span>
|
||
</div>
|
||
|
||
</div>
|
||
</body>
|
||
</html>
|