Self-healing agent (supervisor, recovery, login fixes, operator mail) and Portal sage redesign

Snapshot of D:\Ecw Process a11c9ff (2026-10-30).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ManikandanandClaude Opus 5.5 committed 2026-10-30 10:58:35 +05:30
1 parent fafa5285eb
commit d500f750da
26 files changed
+1704 -357

No files matched your search

+82 -93
View File
@@ -1,110 +1,98 @@
{% extends "base.html" %}
{% block title %}Sign in{% endblock %}
{% block style %}
/* Operator, 2026-10-26: the Portal's front door — what it is on the
left, the form on the right; one column on a phone. */
/* A slow-moving navy-to-blue background, this page only. */
body { background: linear-gradient(120deg, #0f2a47, #12355b 35%,
#1d4f86 65%, #1565c0);
background-size: 220% 220%; animation: drift 18s ease infinite; }
@keyframes drift { 0%, 100% { background-position: 0% 50%; }
50% { background-position: 100% 50%; } }
footer.foot { background: rgba(255, 255, 255, .92); }
.auth { display: grid; grid-template-columns: 5fr 6fr; max-width: 860px;
margin: 40px auto; background: var(--panel); border-radius: 16px;
overflow: hidden; box-shadow: 0 24px 60px rgba(4, 20, 40, .35);
animation: rise .5s ease both; }
.auth-brand { background: var(--navy); color: #dbe5f1; padding: 40px 34px;
display: flex; flex-direction: column; gap: 22px;
position: relative; overflow: hidden; }
/* Two soft circles drifting behind the brand. */
.auth-brand::before, .auth-brand::after { content: ""; position: absolute;
border-radius: 50%; background: rgba(255, 255, 255, .07); }
.auth-brand::before { width: 220px; height: 220px; right: -70px; top: -60px;
animation: float 9s ease-in-out infinite; }
.auth-brand::after { width: 150px; height: 150px; left: -50px; bottom: -40px;
background: rgba(21, 101, 192, .35);
animation: float 11s ease-in-out infinite reverse; }
@keyframes float { 0%, 100% { transform: translate(0, 0); }
50% { transform: translate(-18px, 22px); } }
.auth-brand > * { position: relative; z-index: 1; }
.auth-brand .big { display: inline-grid; place-items: center; width: 56px;
height: 56px; border-radius: 16px; background: #fff;
color: var(--navy); font-size: 19px; font-weight: 800;
animation: ring 2.8s ease-out infinite; }
@keyframes ring { 0% { box-shadow: 0 0 0 0 rgba(255, 255, 255, .45); }
70%, 100% { box-shadow: 0 0 0 14px rgba(255, 255, 255, 0); } }
.auth-brand h1 { color: #fff; font-size: 28px; margin: 0; line-height: 1.2; }
.auth-brand ul { list-style: none; margin: 0; padding: 0;
display: grid; gap: 14px; }
.auth-brand li { display: flex; gap: 12px; align-items: center;
animation: slide .5s ease both; }
.auth-brand li:nth-child(1) { animation-delay: .15s; }
.auth-brand li:nth-child(2) { animation-delay: .3s; }
.auth-brand li:nth-child(3) { animation-delay: .45s; }
@keyframes slide { from { transform: translateX(-16px); }
to { transform: none; } }
.auth-brand li .dot { flex: none; display: inline-grid; place-items: center;
width: 30px; height: 30px; border-radius: 9px;
background: rgba(255, 255, 255, .12); color: #fff;
font-size: 14px; }
.auth-brand li b { color: #fff; }
.auth-brand .foot-note { margin-top: auto; font-size: 12px; color: #8fa3bb; }
.auth-form { padding: 40px 38px; display: flex; flex-direction: column;
justify-content: center; }
.auth-form h2 { font-size: 24px; margin: 0 0 22px; }
.field { margin-bottom: 16px; }
.field label { display: block; font-weight: 600; margin-bottom: 6px;
font-size: 13px; }
.field .box { display: flex; align-items: center; border: 1px solid #c9d2de;
border-radius: 8px; background: var(--panel);
/* The approved mockup (operator, 2026-10-30): a deep sage panel with
what the Portal is, the form in a white card; one column on a phone. */
footer.foot { display: none; }
main { padding: 0; }
nav.crumbs { display: none; }
.auth { display: flex; flex-wrap: wrap; gap: 20px;
min-height: calc(100vh - 40px); }
.auth-brand { flex: 1 1 520px; background: var(--navy); color: #f2f4f1;
border-radius: 28px; padding: 44px 52px; display: flex;
flex-direction: column; justify-content: space-between;
gap: 32px; }
.auth-brand .logo { display: flex; align-items: center; gap: 12px;
font-family: var(--display); font-weight: 500;
font-size: 18px; }
.auth-brand .big { display: inline-grid; place-items: center; width: 42px;
height: 42px; border-radius: 12px; background: var(--lime);
color: var(--navy); font-size: 14px; font-weight: 800; }
.auth-brand h1 { font-family: var(--display); font-weight: 200;
font-size: clamp(52px, 7vw, 88px); line-height: .95;
letter-spacing: -2px; margin: 0 0 18px; color: #f2f4f1; }
.auth-brand h1 span { color: var(--lime); font-weight: 300; }
.auth-brand p.lead { margin: 0; font-size: 17px; line-height: 1.55;
color: #c9d4cd; max-width: 520px; }
.auth-brand ul { list-style: none; margin: 0; padding: 0; display: grid;
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
gap: 14px; }
.auth-brand li { background: rgba(242, 244, 241, .08); border-radius: 18px;
padding: 16px; display: flex; flex-direction: column;
gap: 8px; animation: rise .5s ease both; }
.auth-brand li:nth-child(2) { animation-delay: .1s; }
.auth-brand li:nth-child(3) { animation-delay: .2s; }
.auth-brand li svg { color: var(--lime); }
.auth-brand li b { color: #f2f4f1; font-size: 15px; }
.auth-brand li small { color: #c9d4cd; font-size: 13px; line-height: 1.45; }
.auth-side { flex: 999 1 400px; display: flex; align-items: center;
justify-content: center; padding: 24px 8px; }
.auth-form { width: 100%; max-width: 420px; background: var(--panel);
border-radius: 28px; padding: 40px; box-shadow: var(--shadow);
animation: rise .5s ease both; }
.auth-form h2 { font-family: var(--display); font-weight: 500;
font-size: 32px; letter-spacing: -.5px; margin: 0 0 6px; }
.auth-form .intro { color: var(--muted); margin: 0 0 22px; font-size: 15px; }
.field { margin-bottom: 18px; }
.field label { display: block; font-weight: 500; margin-bottom: 8px;
font-size: 14px; }
.field .box { display: flex; align-items: center; height: 48px;
border: 1px solid #d5dbd6; border-radius: 12px;
background: var(--panel);
transition: border-color .15s, box-shadow .15s; }
.field .box:focus-within { border-color: var(--accent);
box-shadow: 0 0 0 3px rgba(21, 101, 192, .15); }
.field .ic { padding: 0 4px 0 12px; color: var(--muted); font-size: 15px; }
.field input { flex: 1; border: 0; padding: 11px 10px; min-width: 0;
background: transparent; outline: none; }
box-shadow: 0 0 0 3px rgba(74, 107, 88, .18); }
.field .ic { padding: 0 2px 0 14px; color: var(--muted); display: flex; }
.field input { flex: 1; border: 0; padding: 0 10px; height: 100%;
min-width: 0; background: transparent; outline: none; }
.field button.eye { background: transparent; border: 0; color: var(--accent);
font-weight: 600; font-size: 12px; padding: 0 12px; }
font-weight: 600; font-size: 13px; padding: 0 14px; }
.field button.eye:hover { background: transparent; text-decoration: underline; }
#caps { color: var(--review); font-size: 12px; margin-top: 6px; }
.auth-form button[type="submit"] { width: 100%; padding: 12px 14px;
font-size: 15px; border-radius: 8px;
margin-top: 4px; position: relative;
overflow: hidden;
.auth-form button[type="submit"] { width: 100%; height: 52px;
font-size: 16px; border-radius: 14px;
margin-top: 4px;
transition: transform .15s, box-shadow .15s; }
.auth-form button[type="submit"]:hover { transform: translateY(-1px);
box-shadow: 0 8px 18px rgba(21, 101, 192, .35); }
/* A light sweep across the button on hover. */
.auth-form button[type="submit"]::after { content: ""; position: absolute;
top: 0; left: -60%; width: 40%; height: 100%;
background: linear-gradient(90deg, transparent,
rgba(255, 255, 255, .35), transparent);
transform: skewX(-20deg); }
.auth-form button[type="submit"]:hover::after { left: 120%;
transition: left .6s ease; }
.auth-form .rules { margin-top: 18px; padding-top: 14px;
border-top: 1px solid var(--line); }
box-shadow: 0 8px 18px rgba(74, 107, 88, .3); }
.auth-form .rules { margin: 20px 0 0; padding: 12px 14px;
background: var(--bg); border-radius: 12px;
line-height: 1.5; }
@media (max-width: 760px) {
.auth { grid-template-columns: 1fr; margin: 8px 0; }
.auth-brand, .auth-form { padding: 24px 20px; }
.auth-brand { padding: 28px 24px; }
.auth-brand ul { display: none; }
.auth-form { padding: 28px 22px; }
}
{% endblock %}
{% block content %}
<div class="auth">
<section class="auth-brand">
<span class="big" aria-hidden="true">CV</span>
<h1>Charge Validation</h1>
<div class="logo"><span class="big" aria-hidden="true">CV</span>Ecare OTS</div>
<div>
<h1>Charge<br><span>Validation</span></h1>
<p class="lead">Agent run monitoring for the eCW Medcor Ready queue:
every claim checked against the Charge Condition Document.</p>
</div>
<ul>
<li><span class="dot" aria-hidden="true">✓</span><b>Charge conditions</b></li>
<li><span class="dot" aria-hidden="true">▦</span><b>Pass, Fail, Review</b></li>
<li><span class="dot" aria-hidden="true">◷</span><b>Every run recorded</b></li>
<li><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M3 12h4l3 8 4-16 3 8h4"></path></svg><b>Daily run status</b><small>Live progress of the agent and each run.</small></li>
<li><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="2"></rect><path d="M3 10h18M9 4v16"></path></svg><b>Claim results</b><small>Pass, Fail and Review with the reason.</small></li>
<li><svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 3l8 4v5c0 5-3.5 8-8 9-4.5-1-8-4-8-9V7z"></path></svg><b>{{ mode }} mode</b><small>{% if mode == "VERIFY" %}Nothing is saved to eCW claims.{% else %}Corrections are saved after a person confirms.{% endif %}</small></li>
</ul>
<p class="foot-note">Ecare India · {{ mode }} mode</p>
</section>
<section class="auth-form">
<h2>Sign in</h2>
<section class="auth-side">
<div class="auth-form">
<h2>Welcome back</h2>
<p class="intro">Sign in to the Charge Validation portal.</p>
{% if ecare_on %}<p class="note">Ecare staff: your Ecare office username and password.</p>{% endif %}
{% if error %}<div class="msg bad" role="alert">{{ error }}</div>{% endif %}
{% if no_users %}<div class="msg bad">No Portal Users yet. On this PC run
@@ -114,19 +102,20 @@
<input type="hidden" name="next" value="{{ next_url }}">
<div class="field">
<label for="username">Username</label>
<div class="box"><span class="ic" aria-hidden="true">👤</span><input id="username" name="username" autocomplete="username" required autofocus></div>
<div class="box"><span class="ic" aria-hidden="true"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><circle cx="12" cy="8" r="4"></circle><path d="M4 21c1-4 4-6 8-6s7 2 8 6"></path></svg></span><input id="username" name="username" autocomplete="username" required autofocus></div>
</div>
<div class="field">
<label for="pw">Password</label>
<div class="box"><span class="ic" aria-hidden="true">🔒</span><input id="pw" name="password" type="password" autocomplete="current-password" required><button type="button" class="eye" id="pw-toggle" aria-controls="pw" aria-pressed="false">Show</button></div>
<div class="box"><span class="ic" aria-hidden="true"><svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><rect x="4" y="11" width="16" height="10" rx="2"></rect><path d="M8 11V7a4 4 0 0 1 8 0v4"></path></svg></span><input id="pw" name="password" type="password" autocomplete="current-password" required><button type="button" class="eye" id="pw-toggle" aria-controls="pw" aria-pressed="false">Show</button></div>
<div id="caps" role="status" hidden>Caps Lock is on</div>
</div>
<button type="submit">Sign in</button>
</form>
<p class="note rules">Signed out after {{ idle_minutes }} minutes idle.
{{ lock_after }} wrong passwords lock the account for
{{ lock_minutes }} minutes. Forgot a Portal password? An Admin can
reset it.</p>
<p class="note rules">Ecare network or VPN only. Signed out after
{{ idle_minutes }} minutes idle. {{ lock_after }} wrong passwords lock
the account for {{ lock_minutes }} minutes. Forgot a Portal password?
An Admin can reset it.</p>
</div>
</section>
</div>
{% endblock %}