User feedback + expanded privacy-respecting admin stats
Feedback: - feedback table; POST /api/feedback (anonymous-ok, optional category/email, honeypot + per-day flood cap) stores + emails the admin; GET /api/admin/feedback. - Shared feedback store + FeedbackModal; a speech-bubble opens it from the desktop header, the mobile top bar (logo moves left), the footer, and /account. Feedback section in /admin. Stats (additive, same privacy model — no IP/UA/referrer/raw terms): - Event vocab: summary_viewed (fired on /a load), full_story (card → source), not_today/less_like_this/hide_topic, replace_used/replace_none, paywall_replace, paywalled_source_open. Card title/image opens /a (no double-count); history records via keepalive so it survives the nav. - Dashboard: Accounts card (counts only), reading funnel (summary→source rate), emotional-mix & friction, paywall, returning-visitor buckets. (Health metrics deferred to a future monitoring dashboard.) 131 tests pass. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,18 @@
|
||||
<script>
|
||||
import '../app.css';
|
||||
import FeedbackModal from '$lib/components/FeedbackModal.svelte';
|
||||
import { fb, openFeedback, closeFeedback } from '$lib/feedback.svelte.js';
|
||||
let { children } = $props();
|
||||
</script>
|
||||
|
||||
{@render children()}
|
||||
|
||||
{#if fb.open}<FeedbackModal onclose={closeFeedback} />{/if}
|
||||
|
||||
<footer class="site">
|
||||
<div class="container">
|
||||
<button class="fb" onclick={openFeedback}>Send feedback</button>
|
||||
<span class="dot">·</span>
|
||||
Upbeat Bytes · metadata & links only, no stored articles · <a href="/docs">API</a>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -20,6 +26,8 @@
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
footer.site a { color: var(--accent-deep); }
|
||||
footer.site .fb { background: none; border: none; color: var(--accent-deep); font: inherit; font-size: 0.82rem; cursor: pointer; text-decoration: underline; padding: 0; }
|
||||
footer.site .dot { margin: 0 4px; }
|
||||
/* room for the mobile bottom tab bar */
|
||||
@media (max-width: 720px) {
|
||||
footer.site { padding-bottom: calc(34px + 64px + env(safe-area-inset-bottom)); }
|
||||
|
||||
Reference in New Issue
Block a user