All checks were successful
Build & Deploy / deploy (push) Successful in 1m29s
Bildbearbeitung im Nachgang: Drehen in 90-Grad-Schritten, Ausrichten per
Schieberegler, Zuschneiden per Rahmen mit anfassbaren Ecken. Der Server
rechnet jede Korrektur vom unbearbeiteten Original, das beim ersten
Eingriff daneben abgelegt wird - sonst summieren sich die Verluste über
mehrere Korrekturen, und Zurücksetzen wäre nicht möglich. Zwei
Zuschnitte hintereinander werden ineinander verrechnet.
Der Ort ließ sich nicht übernehmen, weil beide Quellen versagen können:
iOS entfernt beim Weitergeben an eine Webseite oft die GPS-Daten aus dem
Foto, und der Browserstandort scheiterte bisher stumm. Jetzt meldet er
seinen Grund ("Standortfreigabe fehlt"), die Detailansicht zeigt die
Herkunft des Ortes, und der Scan-Ort ist von Hand nachtragbar.
E-Mail, Web, Telefon und Mobil sind in der Tabelle jetzt Links; Straße,
PLZ, Ort und Land stehen als eine Adresszeile, die die eingestellte
Navigations-App öffnet.
Einstellungen als vierter Reiter: Navigations-App (Apple Karten, Google
Maps, OpenStreetMap), Statusübersicht und Abmelden - der Knopf saß
vorher in der Kopfzeile.
Die Kopfzeile ist weg. Sie kostete auf dem Telefon eine Bildschirmzeile
und wiederholte nur, was die Tableiste schon sagt.
Kein full_name mehr, nur Vor- und Nachname; akademische Titel gehören in
den Vornamen. Bestehende Datensätze werden bei der Migration am letzten
Leerzeichen aufgeteilt. Die Spalte bleibt ungenutzt stehen: SQLite baut
zum Löschen die ganze Tabelle neu, der Gewinn wären ein paar Byte.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
157 lines
6.6 KiB
HTML
157 lines
6.6 KiB
HTML
<!doctype html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
||
<meta name="theme-color" content="#000000">
|
||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||
<meta name="apple-mobile-web-app-title" content="Karten">
|
||
<title>Visitenkarten Scanner</title>
|
||
<link rel="manifest" href="/manifest.webmanifest">
|
||
<link rel="apple-touch-icon" href="/static/icons/icon-192.png">
|
||
<link rel="icon" href="/static/icons/icon-192.png">
|
||
<link rel="stylesheet" href="/static/style.css">
|
||
</head>
|
||
<body>
|
||
|
||
<div class="app">
|
||
<main>
|
||
<!-- ------------------------------------------------------------- Scan -->
|
||
<section class="screen stack" id="screen-scan">
|
||
<label class="field">
|
||
<span class="label">Anlass · gilt für die nächsten Scans</span>
|
||
<input id="occasion-input" placeholder="z. B. Hannover Messe 2026"
|
||
autocomplete="off" enterkeyhint="done">
|
||
</label>
|
||
|
||
<label class="action" for="input-camera">
|
||
<span class="action-icon" aria-hidden="true">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8">
|
||
<path d="M3 7h3l1.5-2h9L18 7h3v12H3z"></path>
|
||
<circle cx="12" cy="13" r="3.6"></circle>
|
||
</svg>
|
||
</span>
|
||
<span class="action-body">
|
||
<span class="display d-lg">Foto aufnehmen</span>
|
||
<span class="action-hint">
|
||
Karten nebeneinander auf einen dunklen Untergrund legen.
|
||
Ein Foto reicht für den ganzen Tisch.
|
||
</span>
|
||
</span>
|
||
<input id="input-camera" type="file" accept="image/*" capture="environment" hidden>
|
||
</label>
|
||
|
||
<label class="btn btn-quiet" for="input-library">
|
||
Aus Fotos wählen <span class="arrow">↗</span>
|
||
<input id="input-library" type="file" accept="image/*" hidden>
|
||
</label>
|
||
|
||
<div id="scan-result"></div>
|
||
|
||
<div class="section-label"><span class="label">Zuletzt erfasst</span></div>
|
||
<div id="recent-list"></div>
|
||
</section>
|
||
|
||
<!-- ------------------------------------------------------------ Karten -->
|
||
<section class="screen stack" id="screen-cards" hidden>
|
||
<div class="search">
|
||
<input id="search-input" type="search" placeholder="Name, Firma, Anlass, Notiz …"
|
||
autocomplete="off" enterkeyhint="search">
|
||
<button id="search-clear" type="button" aria-label="Suche leeren">×</button>
|
||
</div>
|
||
<div class="chips" id="filter-chips">
|
||
<button class="chip" data-filter="alle" aria-pressed="true">Alle</button>
|
||
<button class="chip" data-filter="offen" aria-pressed="false">Nicht exportiert</button>
|
||
<button class="chip" data-filter="unvollstaendig" aria-pressed="false">Unvollständig</button>
|
||
<button class="chip" data-filter="erinnerung" aria-pressed="false">Mit Erinnerung</button>
|
||
</div>
|
||
<div id="cards-list"></div>
|
||
</section>
|
||
|
||
<!-- ------------------------------------------------------------ Detail -->
|
||
<section class="screen stack" id="screen-detail" hidden></section>
|
||
|
||
<!-- ------------------------------------------------------ Einstellungen -->
|
||
<section class="screen stack" id="screen-mehr" hidden></section>
|
||
|
||
<!-- ------------------------------------------------------ Erinnerungen -->
|
||
<section class="screen stack" id="screen-reminders" hidden>
|
||
<div class="section-label"><span class="label">Erinnerungen</span></div>
|
||
<div id="reminders-list"></div>
|
||
</section>
|
||
</main>
|
||
|
||
<nav class="tabs" role="tablist">
|
||
<button class="tab" role="tab" data-route="scan" aria-selected="true">
|
||
<span class="tab-icon" aria-hidden="true">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9">
|
||
<path d="M3 7h3l1.5-2h9L18 7h3v12H3z"></path>
|
||
<circle cx="12" cy="13" r="3.4"></circle>
|
||
</svg>
|
||
</span>
|
||
<span class="tab-label">Scannen</span>
|
||
</button>
|
||
<button class="tab" role="tab" data-route="karten" aria-selected="false">
|
||
<span class="tab-icon" aria-hidden="true">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9">
|
||
<rect x="3" y="6" width="18" height="13"></rect>
|
||
<path d="M7 11h5M7 15h8"></path>
|
||
</svg>
|
||
</span>
|
||
<span class="tab-label">Karten</span>
|
||
<span class="tab-badge mono-num" id="tab-count-cards" hidden>0</span>
|
||
</button>
|
||
<button class="tab" role="tab" data-route="erinnerungen" aria-selected="false">
|
||
<span class="tab-icon" aria-hidden="true">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9">
|
||
<circle cx="12" cy="12" r="8.5"></circle>
|
||
<path d="M12 7v5.2l3.4 2"></path>
|
||
</svg>
|
||
</span>
|
||
<span class="tab-label">Termine</span>
|
||
<span class="tab-badge mono-num" id="tab-count-reminders" hidden>0</span>
|
||
</button>
|
||
<button class="tab" role="tab" data-route="mehr" aria-selected="false">
|
||
<span class="tab-icon" aria-hidden="true">
|
||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.9">
|
||
<circle cx="12" cy="12" r="3.2"></circle>
|
||
<path d="M12 3v2.4M12 18.6V21M3 12h2.4M18.6 12H21M5.6 5.6l1.7 1.7M16.7 16.7l1.7 1.7M18.4 5.6l-1.7 1.7M7.3 16.7l-1.7 1.7"></path>
|
||
</svg>
|
||
</span>
|
||
<span class="tab-label">Mehr</span>
|
||
</button>
|
||
</nav>
|
||
</div>
|
||
|
||
<!-- ------------------------------------------------------------------ Login -->
|
||
<div class="overlay" id="login-overlay" hidden>
|
||
<form class="block stack" id="login-form">
|
||
<p class="label muted">Zugang</p>
|
||
<h2 class="display d-lg">Anmelden</h2>
|
||
<label class="field">
|
||
<span class="label">Passwort</span>
|
||
<input type="password" id="login-password" autocomplete="current-password" required>
|
||
</label>
|
||
<button class="btn btn-primary" type="submit">Weiter <span class="arrow">→</span></button>
|
||
<p class="hint" id="login-error" hidden></p>
|
||
</form>
|
||
</div>
|
||
|
||
<!-- -------------------------------------------------------------- Fortschritt -->
|
||
<div class="overlay" id="busy-overlay" hidden>
|
||
<div class="block-invert stack" style="text-align:center">
|
||
<div class="spinner" style="margin:0 auto"></div>
|
||
<p class="label" id="busy-text">Karten werden freigestellt</p>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- ---------------------------------------------------------- Bildbearbeitung -->
|
||
<div class="overlay" id="editor-overlay" hidden></div>
|
||
|
||
<div class="toast" id="toast" hidden></div>
|
||
|
||
<script src="/static/app.js"></script>
|
||
</body>
|
||
</html>
|