Files
Frank Schwenk 7cec3030c1 feat: PWA update strategy with hashed assets and cache control
Move frontend sources to frontend/, build content-hashed assets at deploy,
network-first SW for shell/JS, immutable cache for /assets/, version.json
update banner, and Cache-Control middleware. Docs in docs/PWA-STRATEGY.md.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-08 08:51:12 +02:00

105 lines
4.3 KiB
HTML

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="theme-color" content="#F12F12">
<meta name="description" content="Medikamenten-Erinnerung mit Sarkasmus">
<meta name="app-build" content="{{BUILD_HASH}}">
<link rel="manifest" href="/manifest.json">
<link rel="icon" href="/icon.png" type="image/png">
<link rel="apple-touch-icon" href="/icon-192x192.png">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Fredoka:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="{{STYLES_HREF}}">
<title>TakeYourMeds</title>
</head>
<body>
<div id="app">
<!-- Login -->
<section id="loginScreen" class="screen">
<div class="loginHero">
<img src="/icon-192x192.png" alt="TakeYourMeds" class="loginLogo" width="120" height="120">
<h1>TakeYourMeds</h1>
<p class="tagline">Dein Gehirn. Deine Medis. Unser Sarkasmus.</p>
</div>
<form id="pinForm" class="pinForm">
<input type="number" id="pinInput" class="pinInput" inputmode="numeric" min="0" max="999999" step="1" placeholder="PIN" autocomplete="off" required>
<button type="submit" class="btn btn-primary pinSubmit">Anmelden</button>
</form>
<p class="loginError" id="loginError" hidden></p>
</section>
<!-- Main -->
<section id="mainScreen" class="screen" hidden>
<header class="topBar">
<img src="/icon.png" alt="" class="topIcon" width="36" height="36">
<h1>Medis</h1>
<div class="streakBadge" id="streakBadge" title="Streak">🔥 0</div>
</header>
<nav class="tabNav">
<button type="button" class="tab active" data-tab="dashboard">Heute</button>
<button type="button" class="tab" data-tab="history">Verlauf</button>
<button type="button" class="tab" data-tab="settings"></button>
</nav>
<main class="tabContent">
<div id="tab-dashboard" class="tabPanel active">
<div class="motivationCard" id="motivationCard">
<div class="motivationLabel">ADHD live, laugh, toaster bath motivational</div>
<p id="motivationText"></p>
</div>
<div id="slotCards" class="slotCards"></div>
</div>
<div id="tab-history" class="tabPanel" hidden>
<div class="statsRow">
<div class="statBox">
<div class="statNum" id="statStreak">0</div>
<div class="statLabel">Streak</div>
</div>
<div class="statBox">
<div class="statNum" id="statCompliance">0%</div>
<div class="statLabel" id="statComplianceLabel">90 Tage</div>
</div>
<div class="statBox">
<div class="statNum" id="statTaken">0</div>
<div class="statLabel">Genommen</div>
</div>
</div>
<h2 class="sectionTitle">90-Tage Heatmap</h2>
<div id="heatmap" class="heatmap"></div>
</div>
<div id="tab-settings" class="tabPanel" hidden>
<div class="settingsGroup">
<h2>Benachrichtigungen</h2>
<p class="settingsHint">Push-Erinnerungen um 8:00 und 12:00 — dezent, kein Alarm.</p>
<button type="button" class="btn btn-accent" id="enablePush">Push aktivieren</button>
<p id="pushStatus" class="settingsStatus"></p>
</div>
<div class="settingsGroup" id="pwaInstallSection" hidden>
<h2>App installieren</h2>
<button type="button" class="btn btn-secondary" id="installPwa">Auf Homescreen</button>
</div>
<div class="settingsGroup">
<button type="button" class="btn btn-danger" id="logoutBtn">Abmelden</button>
</div>
</div>
</main>
</section>
</div>
<div id="toast" class="toast" hidden></div>
<div id="updateBanner" class="updateBanner" hidden>
<span>Neue Version verfügbar</span>
<button type="button" class="btn btn-primary" id="updateBannerBtn">Aktualisieren</button>
</div>
<div id="confetti" class="confetti" hidden></div>
<div id="easterEgg" class="easterEgg" hidden></div>
<script type="module" src="{{APP_SCRIPT_SRC}}"></script>
</body>
</html>