Files
ecw-charge-validation/web/templates/base.html
T
ManikandanandClaude Opus 5.5 120fd6808a Portal look: aligned header and footer, 7-day chart, today's reasons, pass rate
Header, breadcrumb, page and footer share one 1180px column; the header
marks the current page; a footer sits at the bottom of every page.
Dashboard: pass rate on Checked today, a Last 7 days Pass / Fail / Review
chart, today's Fail and Review reasons grouped with counts, and three
equal cards (Claim status, Runs, Find a claim). Motion is movement only
and off under prefers-reduced-motion. Context.md brought up to date.

Snapshot of local 9d5ac63 (tree scanned: no real claim ids, no patient
data).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 13:06:52 +05:30

243 lines
13 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="run-token" content="{{ run_token }}">
<title>{% block title %}Charge Validation{% endblock %} · eCW</title>
{% block head %}{% endblock %}
<style>
/* Navy & blue (operator, 2026-10-19): navy header, white cards on light
grey, blue buttons; Pass green, Fail red, Review amber everywhere. */
:root {
--navy: #12355b; --navy-2: #0f2a47; --bg: #f4f6fa; --panel: #ffffff;
--text: #1f2a37; --muted: #6b7785; --line: #dde3ec; --head: #e8edf4;
--accent: #1565c0; --accent-dark: #0d4f9e;
--pass: #2e7d32; --pass-bg: #e8f5e9;
--fail: #c62828; --fail-bg: #fdecea;
--review: #b45309; --review-bg: #fff3e0; --review-bar: #ef6c00;
--stripe: #f9fafc; --hilite: #eef4ff; --sel: #dbe7fb;
--shadow: 0 1px 2px rgba(18, 53, 91, .06), 0 2px 8px rgba(18, 53, 91, .05);
}
* { box-sizing: border-box; }
body { margin: 0; background: var(--bg); color: var(--text);
font: 14px/1.45 "Segoe UI", system-ui, sans-serif;
display: flex; flex-direction: column; min-height: 100vh; }
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
/* Header, breadcrumb, page and footer share one column (1180px), so
their edges line up (operator, 2026-10-26). */
.wrap { max-width: 1180px; margin: 0 auto; padding: 0 24px; width: 100%; }
header.top { background: var(--navy); color: #fff; }
header.top .wrap { display: flex; align-items: center; gap: 14px;
min-height: 58px; }
header.top .brand { color: #fff; font-size: 17px; font-weight: 600;
letter-spacing: .2px; display: flex; align-items: center; }
header.top .brand:hover { text-decoration: none; }
header.top .mark { display: inline-grid; place-items: center; width: 30px;
height: 30px; border-radius: 8px; background: #fff;
color: var(--navy); font-size: 12px; font-weight: 800;
margin-right: 10px; letter-spacing: .5px; }
.pill { border-radius: 12px; padding: 2px 10px; font-size: 11px;
font-weight: 700; letter-spacing: .4px; background: var(--pass);
color: #fff; }
.pill.LIVE-WRITE { background: var(--fail); }
header.top nav.links { margin-left: auto; display: flex; gap: 4px;
align-items: center; }
header.top nav.links a { color: #e3ecf7; font-weight: 600; padding: 6px 11px;
border-radius: 6px; transition: background .15s; }
header.top nav.links a:hover { background: rgba(255, 255, 255, .08);
text-decoration: none; }
header.top nav.links a.on { background: rgba(255, 255, 255, .16); color: #fff; }
header.top .who { color: #b9c6d6; font-size: 13px; display: flex;
align-items: center; gap: 7px; margin-left: 10px; }
header.top .who b { color: #fff; }
header.top .avatar { display: inline-grid; place-items: center; width: 26px;
height: 26px; border-radius: 50%; background: var(--accent);
color: #fff; font-size: 12px; font-weight: 700; }
header.top form.out { margin: 0; }
header.top form.out button { background: transparent; border-color: #6f86a3;
font-weight: 400; padding: 4px 10px; }
.msg { border-radius: 8px; padding: 10px 14px; margin-bottom: 14px; }
.msg.bad { background: var(--fail-bg); color: var(--fail); }
.msg.good { background: var(--pass-bg); color: var(--pass); }
nav.crumbs { background: var(--navy-2); color: #b9c6d6; padding: 7px 0;
font-size: 13px; }
nav.crumbs a { color: #e3ecf7; }
nav.crumbs .sep { margin: 0 8px; color: #7d8ea3; }
nav.crumbs .here { color: #fff; font-weight: 600; }
main { flex: 1; width: 100%; max-width: 1180px; margin: 0 auto;
padding: 22px 24px 40px; }
footer.foot { background: var(--panel); border-top: 1px solid var(--line);
color: var(--muted); font-size: 12px; }
footer.foot .wrap { display: flex; gap: 16px; align-items: center;
justify-content: space-between; flex-wrap: wrap;
padding-top: 12px; padding-bottom: 12px; }
footer.foot b { color: var(--text); font-weight: 600; }
/* Motion: short, never in the way, off when Windows asks for less. */
/* Movement only, never opacity: a tab the browser holds in the
background must not leave the page faded. */
@keyframes rise { from { transform: translateY(8px); }
to { transform: none; } }
.card, .tile { animation: rise .35s ease both; }
.tiles > :nth-child(2) { animation-delay: .06s; }
.tiles > :nth-child(3) { animation-delay: .12s; }
.tiles > :nth-child(4) { animation-delay: .18s; }
a.tile, a.card { transition: transform .15s, box-shadow .15s,
border-color .15s; }
a.tile:hover, a.card:hover { transform: translateY(-2px);
box-shadow: 0 6px 18px rgba(18, 53, 91, .12); }
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { animation: none !important;
transition: none !important; }
}
h1 { font-size: 20px; margin: 0 0 4px; }
.sub { color: var(--muted); margin: 0 0 16px; }
.card { background: var(--panel); border: 1px solid var(--line);
border-radius: 10px; padding: 16px 18px; box-shadow: var(--shadow);
margin-bottom: 18px; }
.card h2 { font-size: 15px; margin: 0 0 10px; }
.tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
gap: 12px; margin-bottom: 18px; }
.tile { background: var(--panel); border: 1px solid var(--line);
border-radius: 10px; padding: 12px 16px; box-shadow: var(--shadow);
color: var(--text); display: block; }
.tile .n { font-size: 26px; font-weight: 700; display: block; }
.tile .l { color: var(--muted); font-size: 12px; text-transform: uppercase;
letter-spacing: .5px; }
.tile.Pass .n { color: var(--pass); }
.tile.Fail .n { color: var(--fail); }
.tile.Review .n { color: var(--review); }
a.tile:hover { text-decoration: none; border-color: var(--accent); }
a.tile.on { border: 2px solid var(--accent); padding: 11px 15px; }
select, input, button { font: inherit; padding: 7px 10px;
border: 1px solid #c9d2de; border-radius: 6px;
background: var(--panel); color: var(--text); }
button, .btn { background: var(--accent); color: #fff;
border: 1px solid var(--accent); border-radius: 6px;
padding: 7px 14px; cursor: pointer; font-weight: 600;
display: inline-block; }
button:hover, .btn:hover { background: var(--accent-dark);
text-decoration: none; }
button.plain { background: var(--panel); color: var(--text);
border-color: #c9d2de; font-weight: 400; }
button.plain:hover { background: var(--hilite); }
button[disabled] { opacity: .5; cursor: default; }
.cat { display: inline-block; padding: 2px 9px; border-radius: 10px;
font-weight: 600; font-size: 12px; }
.cat.Pass { background: var(--pass-bg); color: var(--pass); }
.cat.Fail { background: var(--fail-bg); color: var(--fail); }
.cat.Review { background: var(--review-bg); color: var(--review); }
.empty { background: var(--panel); border: 1px dashed #c9d2de;
border-radius: 10px; padding: 26px; color: var(--muted);
text-align: center; }
.note { color: var(--muted); font-size: 12px; }
/* Tables: short, no sideways scroll; long text cut on one line. */
table.list { width: 100%; border-collapse: separate; border-spacing: 0;
background: var(--panel); border: 1px solid var(--line);
border-radius: 10px; overflow: hidden; table-layout: fixed; }
table.list th, table.list td { padding: 9px 12px; text-align: left;
border-bottom: 1px solid var(--line);
overflow: hidden; text-overflow: ellipsis;
white-space: nowrap; }
table.list th { background: var(--head); font-size: 12px; color: #3b4757;
font-weight: 600; text-transform: uppercase;
letter-spacing: .4px; }
table.list tbody tr { cursor: pointer; }
table.list tbody tr:nth-child(even) td { background: var(--stripe); }
table.list tbody tr:hover td { background: var(--hilite); }
table.list tbody tr.sel td { background: var(--sel); }
table.list tbody tr:last-child td { border-bottom: 0; }
td.claim { font-weight: 600; }
.pager { display: flex; gap: 6px; align-items: center; flex-wrap: wrap;
margin: 12px 0; }
.pager button { padding: 4px 10px; background: var(--panel);
color: var(--text); border-color: #c9d2de; font-weight: 400; }
.pager button.cur { background: var(--accent); color: #fff;
border-color: var(--accent); }
.pager button[disabled]:not(.cur) { opacity: .4; }
.pager .info { color: var(--muted); margin-left: 8px; }
form.search { display: flex; gap: 8px; align-items: center;
flex-wrap: wrap; }
form.search input { min-width: 220px; }
/* Side panels. The page under an open panel does not scroll: one scroll
bar, the panel's (operator, 2026-10-15; review 2026-10-19). */
body.drawer-open { overflow: hidden; }
.drawer { position: fixed; top: 0; right: 0; height: 100vh;
width: min(480px, 100vw); background: var(--panel);
border-left: 1px solid var(--line); z-index: 20;
box-shadow: -8px 0 24px rgba(18, 53, 91, .12);
padding: 0 0 30px; overflow-y: auto; }
.drawer[hidden] { display: none; }
.drawer .top { display: flex; align-items: center; gap: 10px;
background: var(--navy); color: #fff; padding: 12px 18px;
position: sticky; top: 0; }
.drawer .top h2 { font-size: 16px; margin: 0; }
.drawer .top .close { margin-left: auto; background: transparent;
border-color: #6f86a3; font-weight: 400; }
.drawer .body { padding: 14px 18px; }
.drawer dl { display: grid; grid-template-columns: 140px 1fr;
gap: 6px 12px; margin: 0; font-size: 13px; }
.drawer dt { color: var(--muted); }
.drawer dd { margin: 0; white-space: pre-wrap; word-break: break-word; }
.drawer h3 { font-size: 14px; margin: 16px 0 6px; }
.drawer pre { background: var(--head); padding: 10px; border-radius: 6px;
white-space: pre-wrap; font-size: 12px; margin: 0; }
@media (max-width: 760px) {
.tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
main { padding: 16px; }
.wrap { padding-left: 16px; padding-right: 16px; }
header.top .wrap { flex-wrap: wrap; padding-top: 8px; padding-bottom: 8px; }
header.top nav.links { margin-left: 0; flex-wrap: wrap; }
}
{% block style %}{% endblock %}
</style>
<script>
// Shared by every page, defined before any page script runs: one side
// panel open at a time; Escape and every Close button shut it.
const $ = id => document.getElementById(id);
const CATEGORIES = {{ categories | list | tojson }};
function openPanel(id) {
document.querySelectorAll('.drawer').forEach(p => p.hidden = p.id !== id);
document.body.classList.toggle('drawer-open', id !== null);
}
const closePanels = () => openPanel(null);
document.addEventListener('click', e => {
if (e.target.closest('.drawer .close')) closePanels();
});
document.addEventListener('keydown', e => {
if (e.key === 'Escape') closePanels();
});
</script>
</head>
<body>
{% set section = crumbs[1][0] if crumbs | length > 1 else crumbs[0][0] %}
<header class="top">
<div class="wrap">
<a class="brand" href="/"><span class="mark" aria-hidden="true">CV</span>eCW Charge Validation</a>
<span class="pill {{ mode }}" title="SAVE_CORRECTIONS mode">{{ mode }}</span>
{% if user %}<nav class="links">
{% for label, link, admin in [("Dashboard", "/", False), ("Claim status", "/days", False), ("Runs", "/runs", False), ("Users", "/admin/users", True), ("Sign-ins", "/admin/signins", True)] %}{% if not admin or is_admin %}{% if label == section %}<a href="{{ link }}" class="on" aria-current="page">{{ label }}</a>{% else %}<a href="{{ link }}">{{ label }}</a>{% endif %}{% endif %}{% endfor %}
<a class="who" href="/account/password" title="Change password"><span class="avatar" aria-hidden="true">{{ (user.display_name or "?")[:1] | upper }}</span><b>{{ user.display_name }}</b> · {{ user.role }}</a>
<form class="out" method="post" action="/signout"><input type="hidden" name="token" value="{{ run_token }}"><button type="submit">Sign out</button></form>
</nav>{% endif %}
</div>
</header>
<nav class="crumbs">
<div class="wrap">{% for label, link in crumbs %}{% if not loop.first %}<span class="sep">›</span>{% endif %}{% if link %}<a href="{{ link }}">{{ label }}</a>{% else %}<span class="here">{{ label }}</span>{% endif %}{% endfor %}</div>
</nav>
<main>
{% block content %}{% endblock %}
</main>
<footer class="foot">
<div class="wrap">
<span><b>eCW Charge Validation</b> · Ecare India</span>
<span class="phi">Local page — contains patient data; do not share
screenshots outside the team.</span>
<span>Mode <b>{{ mode }}</b></span>
</div>
</footer>
{% block scripts %}{% endblock %}
</body>
</html>