155 lines
7.6 KiB
HTML
155 lines
7.6 KiB
HTML
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="utf-8" />
|
|
<meta name="viewport" content="width=device-width,initial-scale=1" />
|
|
<title>Survey Responses</title>
|
|
<script src="https://cdn.tailwindcss.com"></script>
|
|
<style>
|
|
.nice-scrollbar::-webkit-scrollbar{height:8px;width:8px}
|
|
.nice-scrollbar::-webkit-scrollbar-thumb{background:rgba(255,255,255,.15);border-radius:9999px}
|
|
</style>
|
|
</head>
|
|
<body class="min-h-screen bg-slate-950 text-slate-100">
|
|
<!-- gradient header -->
|
|
<div class="bg-gradient-to-b from-slate-900 via-slate-900/70 to-transparent">
|
|
<header class="mx-auto max-w-7xl px-6 pt-8 pb-6">
|
|
<h1 class="text-2xl md:text-3xl font-bold tracking-tight">Survey Responses</h1>
|
|
<p class="mt-1 text-sm text-slate-400">Review and export all captured entries.</p>
|
|
<div class="mt-4 flex flex-wrap items-center gap-2 text-sm text-slate-300">
|
|
<span>Total <span class="font-semibold text-white">{{ total }}</span></span>
|
|
<span class="opacity-50">•</span>
|
|
{% set pages = (total // per_page) + (1 if total % per_page else 0) %}
|
|
<span>Page <span class="font-semibold text-white">{{ page }}</span> / {{ pages }}</span>
|
|
</div>
|
|
</header>
|
|
</div>
|
|
|
|
<main class="mx-auto max-w-7xl px-6 pb-12">
|
|
<!-- sticky tools -->
|
|
<div class="sticky top-0 z-20 -mx-6 px-6 py-3 border-y border-slate-800/60 backdrop-blur bg-slate-950/65">
|
|
<div class="flex items-center gap-2">
|
|
<input id="search" type="search" placeholder="Search name, persona, device, email…"
|
|
class="w-72 max-w-[70vw] rounded-lg bg-slate-900/80 border border-slate-800 px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-emerald-500" />
|
|
<a href="/host/responses.csv"
|
|
class="ml-auto inline-flex items-center gap-2 rounded-lg bg-emerald-500 text-black font-semibold px-3 py-2 text-sm hover:bg-emerald-400 transition">
|
|
Export CSV
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- table card -->
|
|
<div class="mt-4 overflow-hidden rounded-xl border border-slate-800 shadow-[0_0_0_1px_rgba(255,255,255,0.03)_inset]">
|
|
<div class="overflow-x-auto nice-scrollbar">
|
|
<table class="min-w-[1200px] w-full text-sm">
|
|
<thead class="bg-slate-900/90">
|
|
<tr class="text-slate-300">
|
|
<th class="p-3 text-left font-semibold">ID</th>
|
|
<th class="p-3 text-left font-semibold">PID</th>
|
|
<th class="p-3 text-left font-semibold">Name</th>
|
|
<th class="p-3 text-left font-semibold">Persona</th>
|
|
<th class="p-3 text-left font-semibold">Online Freq</th>
|
|
<th class="p-3 text-left font-semibold">Fulfillment</th>
|
|
<th class="p-3 text-left font-semibold">Coupons Use</th>
|
|
<th class="p-3 text-left font-semibold">Pref</th>
|
|
<th class="p-3 text-left font-semibold">Matters</th>
|
|
<th class="p-3 text-left font-semibold">Barriers</th>
|
|
<th class="p-3 text-left font-semibold">Device</th>
|
|
<th class="p-3 text-left font-semibold">Email</th>
|
|
<th class="p-3 text-left font-semibold whitespace-nowrap">Created</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody id="rows" class="[&_tr:nth-child(even)]:bg-slate-900/30">
|
|
{% for r in rows %}
|
|
{% set a = r.answers or {} %}
|
|
<tr class="border-t border-slate-800 hover:bg-slate-900/60 transition">
|
|
<td class="p-3 text-slate-300">{{ r.id }}</td>
|
|
<td class="p-3 text-slate-300">{{ r.pid }}</td>
|
|
<td class="p-3">
|
|
<div class="font-medium">{{ r.name }}</div>
|
|
{% if a.device %}<div class="text-xs text-slate-400">{{ a.device }}</div>{% endif %}
|
|
</td>
|
|
<td class="p-3">
|
|
{% set persona = r.persona or '-' %}
|
|
<span class="inline-flex items-center rounded-full px-2 py-0.5 text-xs font-semibold
|
|
{% if persona == 'Sharpshooter' %} bg-emerald-500/15 text-emerald-300 border border-emerald-600/40
|
|
{% elif persona == 'Speed Demon' %} bg-fuchsia-500/15 text-fuchsia-300 border border-fuchsia-600/40
|
|
{% elif persona == 'Tank' %} bg-sky-500/15 text-sky-300 border border-sky-600/40
|
|
{% else %} bg-slate-700/40 text-slate-200 border border-slate-600/40 {% endif %}">
|
|
{{ persona }}
|
|
</span>
|
|
</td>
|
|
<td class="p-3 text-slate-200">{{ a.shop_online_freq or '-' }}</td>
|
|
<td class="p-3 text-slate-200">{{ a.fulfillment_preference or '-' }}</td>
|
|
<td class="p-3 text-slate-200">{{ a.digital_coupons_use or '-' }}</td>
|
|
<td class="p-3 text-slate-200">{{ a.coupons_preference or '-' }}</td>
|
|
<td class="p-3">
|
|
{% set wm = (a.what_matters or []) %}
|
|
{% if wm %}
|
|
<div class="flex flex-wrap gap-1">
|
|
{% for item in wm %}
|
|
<span class="rounded-full bg-slate-800 px-2 py-0.5 text-xs text-slate-300">{{ item }}</span>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}-{% endif %}
|
|
</td>
|
|
<td class="p-3">
|
|
{% set br = (a.barriers or []) %}
|
|
{% if br %}
|
|
<div class="flex flex-wrap gap-1">
|
|
{% for item in br %}
|
|
<span class="rounded-full bg-slate-800 px-2 py-0.5 text-xs text-slate-300">{{ item }}</span>
|
|
{% endfor %}
|
|
</div>
|
|
{% else %}-{% endif %}
|
|
</td>
|
|
<td class="p-3 text-slate-200">{{ a.device or '-' }}</td>
|
|
<td class="p-3">
|
|
{% if a.email_consent and a.email %}
|
|
<a class="underline decoration-slate-600 hover:decoration-emerald-400" href="mailto:{{
|
|
a.email }}">{{ a.email }}</a>
|
|
{% else %}-{% endif %}
|
|
</td>
|
|
<td class="p-3 whitespace-nowrap text-slate-300">
|
|
{% if r.created_at %}{{ r.created_at.strftime('%Y-%m-%d %H:%M') }}{% else %}-{% endif %}
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<!-- pagination -->
|
|
<div class="flex items-center gap-2 p-3 bg-slate-900/70 border-t border-slate-800">
|
|
{% if page > 1 %}
|
|
<a class="px-3 py-1.5 bg-slate-800 rounded-lg hover:bg-slate-700 transition" href="?page={{ page - 1 }}">Prev</a>
|
|
{% else %}
|
|
<span class="px-3 py-1.5 bg-slate-900 rounded-lg opacity-40">Prev</span>
|
|
{% endif %}
|
|
<span class="text-sm text-slate-300">Page <span class="text-white font-semibold">{{ page }}</span> / {{ pages }}</span>
|
|
{% if page < pages %}
|
|
<a class="px-3 py-1.5 bg-slate-800 rounded-lg hover:bg-slate-700 transition" href="?page={{ page + 1 }}">Next</a>
|
|
{% else %}
|
|
<span class="px-3 py-1.5 bg-slate-900 rounded-lg opacity-40">Next</span>
|
|
{% endif %}
|
|
<div class="ml-auto text-xs text-slate-400">Showing {{ rows|length }} of {{ total }}</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<script>
|
|
// simple client-side filter
|
|
const q = document.getElementById('search');
|
|
const rows = Array.from(document.querySelectorAll('#rows tr'));
|
|
q?.addEventListener('input', () => {
|
|
const needle = q.value.trim().toLowerCase();
|
|
rows.forEach(tr => {
|
|
if (!needle) { tr.style.display = ''; return; }
|
|
tr.style.display = tr.innerText.toLowerCase().includes(needle) ? '' : 'none';
|
|
});
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|
|
|