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>
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
<!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>
|
||||
Reference in New Issue
Block a user