feat: add kiosk frontend for guided intake flow
This commit is contained in:
parent
7d0bebf16d
commit
7807e32133
4 changed files with 144 additions and 2 deletions
42
bookmark/static/app.js
Normal file
42
bookmark/static/app.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
// bookmark/bookmark/static/app.js
|
||||
const statusEl = document.getElementById("status");
|
||||
const hintEl = document.getElementById("hint");
|
||||
const previewEl = document.getElementById("preview");
|
||||
const triggerButton = document.getElementById("trigger-button");
|
||||
|
||||
const STATE_COPY = {
|
||||
waiting_front: { status: "Waiting for book...", hint: "Center the front cover in the guide box." },
|
||||
waiting_back: { status: "Front captured — flip it over", hint: "Center the back cover in the guide box." },
|
||||
book_complete: { status: "Book complete!", hint: "Tap Next Book to continue." },
|
||||
};
|
||||
|
||||
async function refreshStatus() {
|
||||
const response = await fetch("/api/session-state");
|
||||
const body = await response.json();
|
||||
const copy = STATE_COPY[body.state] || { status: body.state, hint: "" };
|
||||
statusEl.textContent = copy.status;
|
||||
hintEl.textContent = copy.hint;
|
||||
triggerButton.textContent = body.state === "book_complete" ? "Next Book" : "Capture";
|
||||
}
|
||||
|
||||
function refreshPreview() {
|
||||
previewEl.src = `/api/preview.jpg?ts=${Date.now()}`;
|
||||
}
|
||||
|
||||
async function observe() {
|
||||
await fetch("/api/observe", { method: "POST" });
|
||||
await refreshStatus();
|
||||
}
|
||||
|
||||
triggerButton.addEventListener("click", async () => {
|
||||
if (statusEl.textContent === "Book complete!") {
|
||||
await fetch("/api/next-book", { method: "POST" });
|
||||
} else {
|
||||
await fetch("/api/manual-trigger", { method: "POST" });
|
||||
}
|
||||
await refreshStatus();
|
||||
});
|
||||
|
||||
setInterval(observe, 500);
|
||||
setInterval(refreshPreview, 500);
|
||||
refreshStatus();
|
||||
|
|
@ -1,5 +1,22 @@
|
|||
<!-- bookmark/bookmark/static/index.html -->
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head><meta charset="UTF-8" /><title>Books in Hand — Intake</title></head>
|
||||
<body><h1>Books in Hand — Intake</h1></body>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Books in Hand — Intake</title>
|
||||
<link rel="stylesheet" href="/theme.css" />
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<main class="kiosk">
|
||||
<div class="kiosk__preview">
|
||||
<img id="preview" alt="Camera preview" />
|
||||
</div>
|
||||
<div class="kiosk__status" id="status">Waiting for book...</div>
|
||||
<div class="kiosk__hint" id="hint">Center the front cover in the guide box.</div>
|
||||
<button class="kiosk__button" id="trigger-button">Capture</button>
|
||||
</main>
|
||||
<script src="/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
48
bookmark/static/style.css
Normal file
48
bookmark/static/style.css
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/* bookmark/bookmark/static/style.css */
|
||||
.kiosk {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 100vh;
|
||||
padding: var(--space-lg);
|
||||
gap: var(--space-md);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.kiosk__preview {
|
||||
width: min(90vw, 640px);
|
||||
aspect-ratio: 4 / 3;
|
||||
background: var(--color-surface);
|
||||
border-radius: var(--radius);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.kiosk__preview img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.kiosk__status {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.kiosk__hint {
|
||||
color: var(--color-muted);
|
||||
}
|
||||
|
||||
.kiosk__button {
|
||||
padding: var(--space-md) var(--space-lg);
|
||||
font-size: 1.25rem;
|
||||
border: none;
|
||||
border-radius: var(--radius);
|
||||
background: var(--color-accent);
|
||||
color: var(--color-bg);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.kiosk__button:active {
|
||||
opacity: 0.8;
|
||||
}
|
||||
35
bookmark/static/theme.css
Normal file
35
bookmark/static/theme.css
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/* bookmark/bookmark/static/theme.css */
|
||||
:root {
|
||||
--color-bg: #101418;
|
||||
--color-surface: #1b2128;
|
||||
--color-text: #f2f4f6;
|
||||
--color-muted: #9aa5b1;
|
||||
--color-accent: #4fb0ae;
|
||||
--color-success: #5cb85c;
|
||||
--color-warning: #e0a13a;
|
||||
--space-sm: 0.5rem;
|
||||
--space-md: 1rem;
|
||||
--space-lg: 2rem;
|
||||
--radius: 0.5rem;
|
||||
--font-base: system-ui, -apple-system, "Segoe UI", sans-serif;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: light) {
|
||||
:root {
|
||||
--color-bg: #f2f4f6;
|
||||
--color-surface: #ffffff;
|
||||
--color-text: #101418;
|
||||
--color-muted: #5a6472;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
background: var(--color-bg);
|
||||
color: var(--color-text);
|
||||
font-family: var(--font-base);
|
||||
}
|
||||
Loading…
Reference in a new issue