06/10 Redesign login page

This commit is contained in:
2026-06-10 13:27:47 -04:00
parent a2d465b3d2
commit a594eec173
+261 -214
View File
@@ -4,56 +4,150 @@
{% block extra_css %} {% block extra_css %}
<style> <style>
/* ── Login page background ─────────────────────────────────────────────── */ /* ════════════════════════════════════════════════════════════════════
body.login-page { Login page — split-screen layout
min-height: 100vh; Theme colors preserved: brand #1a56db / #1a4ab5 / #1551c7,
position: relative; quality-green #16a34a, amber #d97706 / #f59e0b, light-blue surfaces.
overflow: hidden; ════════════════════════════════════════════════════════════════════ */
body.login-page { min-height: 100vh; background: #fff; }
/* Let the split fill the viewport: neutralize base.html content wrapper */
body.login-page .container-fluid.mt-4 {
margin-top: 0 !important;
padding: 0;
max-width: none;
} }
/* SVG illustration fills the entire viewport behind the login card */ /* Flash messages float as top-center toasts instead of pushing layout */
body.login-page .login-bg-svg { body.login-page .container-fluid.mt-4 > .alert {
position: fixed; position: fixed;
inset: 0; top: 1rem; left: 50%;
width: 100%; transform: translateX(-50%);
height: 100%; z-index: 1080;
z-index: 0; min-width: 320px; max-width: 92vw;
object-fit: cover; box-shadow: 0 8px 28px rgba(0,0,0,.18);
} }
body.login-page .login-wrapper { .login-split { display: flex; min-height: 100vh; }
/* ── LEFT: brand / QC showcase ──────────────────────────────────────── */
.login-brand {
flex: 1 1 55%;
position: relative; position: relative;
z-index: 1;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
body.login-page .login-card {
border: none;
border-radius: 14px;
box-shadow: 0 8px 40px rgba(26, 86, 219, 0.18), 0 2px 12px rgba(0,0,0,0.10);
overflow: hidden; overflow: hidden;
width: 100%; background: linear-gradient(135deg, #1a56db 0%, #1a4ab5 55%, #1551c7 100%);
max-width: 420px; color: #fff;
padding: 3rem 3.5rem;
display: flex;
flex-direction: column;
justify-content: center;
gap: 1.75rem;
}
.login-brand::before { /* dotted texture */
content: "";
position: absolute; inset: 0;
background-image: radial-gradient(rgba(255,255,255,.12) 1.5px, transparent 1.5px);
background-size: 26px 26px;
opacity: .55; pointer-events: none;
}
.login-brand::after { /* soft glow */
content: "";
position: absolute; width: 460px; height: 460px;
right: -140px; top: -120px;
background: radial-gradient(circle, rgba(255,255,255,.14), transparent 70%);
border-radius: 50%; pointer-events: none;
}
.login-brand > * { position: relative; z-index: 2; max-width: 500px; }
.brand-logo { display: flex; align-items: center; gap: .8rem; }
.brand-logo .logo-badge {
width: 50px; height: 50px; border-radius: 13px;
background: rgba(255,255,255,.16);
border: 1px solid rgba(255,255,255,.22);
display: flex; align-items: center; justify-content: center;
font-size: 1.6rem;
}
.brand-logo .name { font-weight: 700; font-size: 1.3rem; line-height: 1.1; }
.brand-logo .sub { font-size: .8rem; opacity: .82; }
.brand-headline { font-size: 2.05rem; font-weight: 700; line-height: 1.22; }
.brand-sub { opacity: .86; font-size: 1.02rem; line-height: 1.55; }
.feature-list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 1.05rem; }
.feature-list li { display: flex; align-items: flex-start; gap: .85rem; }
.feature-list .fi {
flex: 0 0 auto; width: 32px; height: 32px; border-radius: 50%;
background: #16a34a; color: #fff;
display: flex; align-items: center; justify-content: center;
font-size: .95rem; box-shadow: 0 0 0 4px rgba(22,163,74,.22);
}
.feature-list .ft { font-weight: 600; line-height: 1.2; }
.feature-list .fd { font-size: .85rem; opacity: .82; }
.stat-row { display: flex; gap: .75rem; flex-wrap: wrap; }
.stat-chip {
background: rgba(255,255,255,.12);
border: 1px solid rgba(255,255,255,.20);
border-radius: 12px; padding: .6rem 1.05rem; min-width: 102px;
}
.stat-chip .v { font-size: 1.4rem; font-weight: 700; line-height: 1; }
.stat-chip .l { font-size: .7rem; opacity: .82; text-transform: uppercase; letter-spacing: .6px; margin-top: .25rem; }
.stat-chip.g .v { color: #86efac; }
.stat-chip.a .v { color: #fcd34d; }
/* Decorative janitorial / QC illustration anchored bottom-right */
.brand-illustration {
position: absolute; right: -8px; bottom: -10px;
width: 340px; max-width: 42%; height: auto;
z-index: 1; opacity: .95; pointer-events: none;
} }
body.login-page .login-card .card-header { /* ── RIGHT: sign-in form ────────────────────────────────────────────── */
background: #1a56db; .login-form-side {
padding: 2rem 2rem 1.5rem; flex: 1 1 45%;
border-bottom: none; display: flex; align-items: center; justify-content: center;
padding: 2.5rem 1.5rem; background: #fff;
} }
.login-form-inner { width: 100%; max-width: 400px; }
body.login-page .login-card .card-body { .form-mobile-brand { display: none; } /* shown only on small screens */
padding: 2rem; .form-mobile-brand .logo-badge {
background: #fff; width: 56px; height: 56px; border-radius: 15px;
background: #1a56db; color: #fff;
display: flex; align-items: center; justify-content: center; font-size: 1.8rem;
} }
.form-mobile-brand .name { font-weight: 700; color: #1a56db; font-size: 1.2rem; margin-top: .55rem; }
.form-mobile-brand .sub { color: #6b7280; font-size: .8rem; }
body.login-page .login-card .card-footer { .login-form-inner h2 { font-weight: 700; color: #16264a; margin-bottom: .25rem; }
background: #f8f9fa; .login-form-inner .lead-sub { color: #6b7280; margin-bottom: 1.75rem; }
border-top: 1px solid #e9ecef;
padding: .75rem 2rem; .login-form-side .input-group-text {
background: #f0f7ff; border-color: #d4e1f4; color: #1a56db;
}
.login-form-side .form-control:focus {
border-color: #1a56db; box-shadow: 0 0 0 .2rem rgba(26,86,219,.18);
}
.login-form-side .pw-toggle {
border-color: #d4e1f4; color: #6b8fc7; background: #fff;
}
.login-form-side .pw-toggle:hover { color: #1a56db; }
.btn-login {
background: #1a56db; border-color: #1a56db; color: #fff; font-weight: 600;
}
.btn-login:hover, .btn-login:focus { background: #1648b8; border-color: #1648b8; color: #fff; }
.login-foot { margin-top: 2rem; text-align: center; color: #9aa3af; font-size: .8rem; }
/* ── Responsive: collapse to single column ──────────────────────────── */
@media (max-width: 991.98px) {
.login-brand { display: none; }
.form-mobile-brand {
display: flex; flex-direction: column; align-items: center;
text-align: center; margin-bottom: 1.75rem;
}
.login-form-inner h2, .login-form-inner .lead-sub { text-align: center; }
} }
</style> </style>
{% endblock %} {% endblock %}
@@ -61,183 +155,113 @@
{% block content %} {% block content %}
<script>document.body.classList.add('login-page');</script> <script>document.body.classList.add('login-page');</script>
{# ── Background SVG illustration — inline, no external dependency ── #} <div class="login-split">
<svg class="login-bg-svg" viewBox="0 0 1440 900" preserveAspectRatio="xMidYMid slice"
xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<style>
.bg{fill:#e8f2ff}
.card-bg{fill:#ffffff;stroke:#c2d9f8;stroke-width:1}
.ch1{fill:#1a56db}.ch2{fill:#1a4ab5}.ch3{fill:#1551c7}
.ib{fill:#dceeff}
.dl{stroke:#c2d9f8;stroke-width:1.5}
.cl{stroke:#16a34a;stroke-width:2.5;stroke-linecap:round;stroke-linejoin:round;fill:none}
.bl{fill:#dbeafe;stroke:#1a56db;stroke-width:1.5}
.gl{stroke:#93c5fd;stroke-width:1.2}
.mt{font-family:Arial,sans-serif;font-size:26px;fill:#ffffff;font-weight:700}
.lb{font-family:Arial,sans-serif;font-size:22px;fill:#1a56db;font-weight:600}
.sb{font-family:Arial,sans-serif;font-size:18px;fill:#6b8fc7}
.mv-g{font-family:Arial,sans-serif;font-size:28px;fill:#16a34a;font-weight:700}
.mv-b{font-family:Arial,sans-serif;font-size:28px;fill:#1a56db;font-weight:700}
.mv-a{font-family:Arial,sans-serif;font-size:28px;fill:#d97706;font-weight:700}
.br{font-family:Arial,sans-serif;font-size:22px;fill:#1a56db;font-weight:700;letter-spacing:4px}
.tg{font-family:Arial,sans-serif;font-size:18px;font-weight:600}
.gd{stroke:#c8ddf5;stroke-width:0.8;opacity:0.7}
</style>
<rect width="1440" height="900" class="bg"/> {# ══ LEFT: brand / quality-control showcase ══ #}
<section class="login-brand">
<!-- Grid --> <div class="brand-logo">
<g class="gd"> <span class="logo-badge"><i class="bi bi-clipboard-check-fill"></i></span>
<line x1="0" y1="128" x2="1440" y2="128"/><line x1="0" y1="256" x2="1440" y2="256"/> <span>
<line x1="0" y1="384" x2="1440" y2="384"/><line x1="0" y1="512" x2="1440" y2="512"/> <span class="name d-block">Janitorial QC</span>
<line x1="0" y1="640" x2="1440" y2="640"/><line x1="0" y1="768" x2="1440" y2="768"/> <span class="sub">Quality Control System</span>
<line x1="170" y1="0" x2="170" y2="900"/><line x1="425" y1="0" x2="425" y2="900"/> </span>
<line x1="680" y1="0" x2="680" y2="900"/><line x1="935" y1="0" x2="935" y2="900"/>
<line x1="1190" y1="0" x2="1190" y2="900"/>
</g>
<!-- ══ CARD 1: Inspection Report (left) ══ -->
<rect x="80" y="95" width="370" height="500" rx="24" class="card-bg"/>
<rect x="80" y="95" width="370" height="90" rx="24" class="ch1"/>
<rect x="80" y="155" width="370" height="30" class="ch1"/>
<text x="265" y="152" class="mt" text-anchor="middle">Inspection Report</text>
<!-- Clipboard -->
<rect x="160" y="210" width="210" height="220" rx="10" fill="#f0f7ff" stroke="#c2d9f8" stroke-width="2"/>
<rect x="205" y="195" width="120" height="30" rx="12" fill="#dceeff" stroke="#93c5fd" stroke-width="2"/>
<!-- Row 1 -->
<circle cx="182" cy="255" r="12" fill="#dcfce7" stroke="#86efac" stroke-width="2"/>
<polyline points="177,255 182,261 190,248" class="cl" stroke-width="3"/>
<line x1="202" y1="255" x2="356" y2="255" stroke="#93c5fd" stroke-width="2.5"/>
<line x1="160" y1="274" x2="370" y2="274" class="dl"/>
<!-- Row 2 -->
<circle cx="182" cy="294" r="12" fill="#dcfce7" stroke="#86efac" stroke-width="2"/>
<polyline points="177,294 182,300 190,287" class="cl" stroke-width="3"/>
<line x1="202" y1="294" x2="356" y2="294" stroke="#93c5fd" stroke-width="2.5"/>
<line x1="160" y1="313" x2="370" y2="313" class="dl"/>
<!-- Row 3 -->
<circle cx="182" cy="333" r="12" fill="#fef9c3" stroke="#fde047" stroke-width="2"/>
<polyline points="177,333 182,339 190,326" stroke="#d97706" stroke-width="3" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
<line x1="202" y1="333" x2="340" y2="333" stroke="#93c5fd" stroke-width="2.5"/>
<line x1="160" y1="352" x2="370" y2="352" class="dl"/>
<!-- Row 4 -->
<circle cx="182" cy="371" r="12" fill="#f0f7ff" stroke="#c2d9f8" stroke-width="2"/>
<line x1="202" y1="371" x2="356" y2="371" stroke="#e0ecff" stroke-width="2.5"/>
<!-- Stars -->
<g transform="translate(148,460) scale(1.15)">
<polygon points="12,2 14.5,9 22,9 16,14 18,21 12,17 6,21 8,14 2,9 9.5,9" fill="#f59e0b"/>
<polygon points="12,2 14.5,9 22,9 16,14 18,21 12,17 6,21 8,14 2,9 9.5,9" fill="#f59e0b" transform="translate(28,0)"/>
<polygon points="12,2 14.5,9 22,9 16,14 18,21 12,17 6,21 8,14 2,9 9.5,9" fill="#f59e0b" transform="translate(56,0)"/>
<polygon points="12,2 14.5,9 22,9 16,14 18,21 12,17 6,21 8,14 2,9 9.5,9" fill="#f59e0b" transform="translate(84,0)"/>
<polygon points="12,2 14.5,9 22,9 16,14 18,21 12,17 6,21 8,14 2,9 9.5,9" fill="#e2e8f0" transform="translate(112,0)"/>
</g>
<text x="265" y="520" class="sb" text-anchor="middle">Score: 4.0 / 5.0</text>
<text x="265" y="572" class="lb" text-anchor="middle">Facility checklist</text>
<!-- ══ CARD 2: Quality Control (centre-left) ══ -->
<rect x="535" y="55" width="370" height="580" rx="24" class="card-bg"/>
<rect x="535" y="55" width="370" height="90" rx="24" class="ch2"/>
<rect x="535" y="115" width="370" height="30" class="ch2"/>
<text x="720" y="112" class="mt" text-anchor="middle">Quality Control</text>
<!-- Magnifying glass -->
<circle cx="720" cy="265" r="78" fill="#f0f7ff" stroke="#bfdbfe" stroke-width="2"/>
<circle cx="708" cy="252" r="48" fill="#dbeafe" stroke="#1a56db" stroke-width="5"/>
<line x1="744" y1="288" x2="772" y2="316" stroke="#1a56db" stroke-width="9" stroke-linecap="round"/>
<text x="708" y="263" font-family="Arial" font-size="28" fill="#1a56db" font-weight="800" text-anchor="middle">QC</text>
<!-- Shield -->
<path d="M720 358 L768 374 L768 420 Q768 458 720 475 Q672 458 672 420 L672 374 Z" fill="#dbeafe" stroke="#1a56db" stroke-width="3"/>
<polyline points="696,420 712,438 748,400" stroke="#16a34a" stroke-width="5" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
<!-- Metric boxes -->
<rect x="550" y="500" width="100" height="82" rx="12" fill="#f0fdf4" stroke="#bbf7d0" stroke-width="1.5"/>
<text x="600" y="538" class="mv-g" text-anchor="middle">98%</text>
<text x="600" y="560" class="sb" text-anchor="middle">Pass rate</text>
<rect x="670" y="500" width="100" height="82" rx="12" fill="#eff6ff" stroke="#bfdbfe" stroke-width="1.5"/>
<text x="720" y="538" class="mv-b" text-anchor="middle">247</text>
<text x="720" y="560" class="sb" text-anchor="middle">Inspections</text>
<rect x="790" y="500" width="100" height="82" rx="12" fill="#fffbeb" stroke="#fde68a" stroke-width="1.5"/>
<text x="840" y="538" class="mv-a" text-anchor="middle">4.8</text>
<text x="840" y="560" class="sb" text-anchor="middle">Avg score</text>
<text x="720" y="614" class="lb" text-anchor="middle">Performance overview</text>
<!-- ══ CARD 3: Janitorial Services (right) ══ -->
<rect x="990" y="95" width="370" height="500" rx="24" class="card-bg"/>
<rect x="990" y="95" width="370" height="90" rx="24" class="ch3"/>
<rect x="990" y="155" width="370" height="30" class="ch3"/>
<text x="1175" y="152" class="mt" text-anchor="middle">Janitorial Services</text>
<!-- Mop handle -->
<line x1="1175" y1="205" x2="1175" y2="345" stroke="#1a56db" stroke-width="7" stroke-linecap="round"/>
<!-- Mop head -->
<ellipse cx="1175" cy="352" rx="50" ry="18" fill="#dbeafe" stroke="#1a56db" stroke-width="3"/>
<!-- Mop strings -->
<g stroke="#60a5fa" stroke-width="3" stroke-linecap="round">
<line x1="1127" y1="352" x2="1116" y2="395"/>
<line x1="1138" y1="358" x2="1131" y2="400"/>
<line x1="1152" y1="363" x2="1150" y2="406"/>
<line x1="1166" y1="365" x2="1166" y2="408"/>
<line x1="1180" y1="365" x2="1182" y2="408"/>
<line x1="1194" y1="363" x2="1198" y2="406"/>
<line x1="1208" y1="358" x2="1215" y2="400"/>
<line x1="1220" y1="352" x2="1232" y2="394"/>
</g>
<!-- Bucket -->
<path d="M1110 415 L1124 472 L1226 472 L1240 415 Z" fill="#dbeafe" stroke="#1a56db" stroke-width="3"/>
<path d="M1110 415 Q1175 400 1240 415" fill="none" stroke="#1a56db" stroke-width="3"/>
<ellipse cx="1175" cy="458" rx="44" ry="9" fill="#bfdbfe" opacity="0.7"/>
<line x1="1143" y1="456" x2="1152" y2="460" stroke="#93c5fd" stroke-width="2.5" stroke-linecap="round"/>
<line x1="1188" y1="453" x2="1200" y2="457" stroke="#93c5fd" stroke-width="2.5" stroke-linecap="round"/>
<!-- Status tags -->
<rect x="1078" y="490" width="120" height="38" rx="19" fill="#dcfce7"/>
<text x="1138" y="514" class="tg" fill="#16a34a" text-anchor="middle">● Active</text>
<rect x="1210" y="490" width="140" height="38" rx="19" fill="#dbeafe"/>
<text x="1280" y="514" class="tg" fill="#1a56db" text-anchor="middle">● Scheduled</text>
<text x="1175" y="572" class="lb" text-anchor="middle">Cleaning operations</text>
<!-- ══ Brand bar ══ -->
<rect x="0" y="820" width="1440" height="80" fill="#1a56db" opacity="0.08"/>
<text x="720" y="868" class="br" text-anchor="middle">JANITORIAL QUALITY CONTROL SYSTEM</text>
<!-- Decorative dots -->
<g fill="#bfdbfe">
<circle cx="1280" cy="38" r="7"/><circle cx="1308" cy="38" r="7"/>
<circle cx="1336" cy="38" r="7"/><circle cx="1364" cy="38" r="7"/>
<circle cx="1280" cy="70" r="7"/><circle cx="1308" cy="70" r="7"/>
<circle cx="1336" cy="70" r="7"/><circle cx="1364" cy="70" r="7"/>
<circle cx="58" cy="845" r="7"/><circle cx="86" cy="845" r="7"/><circle cx="114" cy="845" r="7"/>
<circle cx="58" cy="873" r="7"/><circle cx="86" cy="873" r="7"/><circle cx="114" cy="873" r="7"/>
</g>
</svg>
<div class="login-wrapper">
<div class="login-card card shadow-lg">
<div class="card-header text-white text-center">
<i class="bi bi-clipboard-check-fill fs-1 d-block mb-2"></i>
<h3 class="mb-0 fw-bold">Janitorial QC</h3>
<p class="mb-0 opacity-75 small">Quality Control System</p>
</div> </div>
<div class="card-body"> <div>
<h1 class="brand-headline mb-2">Quality control for spotless facilities.</h1>
<p class="brand-sub mb-0">Standardized inspections, real-time issue tracking, and
clear performance reporting &mdash; keeping every facility audit-ready.</p>
</div>
<ul class="feature-list">
<li>
<span class="fi"><i class="bi bi-clipboard-check"></i></span>
<span><span class="ft d-block">Standardized facility inspections</span>
<span class="fd">Consistent checklists across every site.</span></span>
</li>
<li>
<span class="fi"><i class="bi bi-search"></i></span>
<span><span class="ft d-block">Real-time issue tracking</span>
<span class="fd">Log, assign, and resolve quality issues fast.</span></span>
</li>
<li>
<span class="fi"><i class="bi bi-bar-chart-line"></i></span>
<span><span class="ft d-block">Automated quality reporting</span>
<span class="fd">Scores and trends, ready to share.</span></span>
</li>
</ul>
<div class="stat-row">
<div class="stat-chip g"><div class="v">98%</div><div class="l">Pass rate</div></div>
<div class="stat-chip"> <div class="v">247</div><div class="l">Inspections</div></div>
<div class="stat-chip a"><div class="v">4.8</div><div class="l">Avg score</div></div>
</div>
{# ── Decorative inspection / janitorial illustration ── #}
<svg class="brand-illustration" viewBox="0 0 360 340" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
<!-- clipboard card -->
<rect x="48" y="36" width="210" height="252" rx="16" fill="#ffffff" opacity="0.97"/>
<rect x="48" y="36" width="210" height="52" rx="16" fill="#1a56db"/>
<rect x="48" y="64" width="210" height="24" fill="#1a56db"/>
<rect x="118" y="26" width="70" height="24" rx="10" fill="#dceeff" stroke="#93c5fd" stroke-width="2"/>
<text x="153" y="70" font-family="Arial,sans-serif" font-size="17" font-weight="700"
fill="#ffffff" text-anchor="middle">Inspection</text>
<!-- row 1 (pass) -->
<circle cx="78" cy="124" r="11" fill="#dcfce7" stroke="#86efac" stroke-width="2"/>
<polyline points="73,124 78,130 85,118" fill="none" stroke="#16a34a" stroke-width="3"
stroke-linecap="round" stroke-linejoin="round"/>
<line x1="98" y1="124" x2="236" y2="124" stroke="#cfe0f5" stroke-width="5" stroke-linecap="round"/>
<!-- row 2 (pass) -->
<circle cx="78" cy="160" r="11" fill="#dcfce7" stroke="#86efac" stroke-width="2"/>
<polyline points="73,160 78,166 85,154" fill="none" stroke="#16a34a" stroke-width="3"
stroke-linecap="round" stroke-linejoin="round"/>
<line x1="98" y1="160" x2="236" y2="160" stroke="#cfe0f5" stroke-width="5" stroke-linecap="round"/>
<!-- row 3 (flag / amber) -->
<circle cx="78" cy="196" r="11" fill="#fef9c3" stroke="#fde047" stroke-width="2"/>
<line x1="78" y1="190" x2="78" y2="197" stroke="#d97706" stroke-width="3" stroke-linecap="round"/>
<circle cx="78" cy="201" r="1.6" fill="#d97706"/>
<line x1="98" y1="196" x2="206" y2="196" stroke="#cfe0f5" stroke-width="5" stroke-linecap="round"/>
<!-- row 4 -->
<circle cx="78" cy="232" r="11" fill="#eef4fc" stroke="#c2d9f8" stroke-width="2"/>
<line x1="98" y1="232" x2="236" y2="232" stroke="#e4edf9" stroke-width="5" stroke-linecap="round"/>
<!-- shield-check badge -->
<g transform="translate(214,210)">
<path d="M30 0 L58 10 L58 38 Q58 62 30 74 Q2 62 2 38 L2 10 Z"
fill="#16a34a"/>
<polyline points="16,38 26,49 46,25" fill="none" stroke="#ffffff" stroke-width="5"
stroke-linecap="round" stroke-linejoin="round"/>
</g>
<!-- magnifying glass -->
<circle cx="118" cy="206" r="56" fill="none" stroke="#1a56db" stroke-width="0" />
<circle cx="118" cy="206" r="46" fill="#dbeafe" fill-opacity="0.55" stroke="#1a56db" stroke-width="7"/>
<line x1="151" y1="239" x2="182" y2="270" stroke="#1a56db" stroke-width="11" stroke-linecap="round"/>
<text x="118" y="214" font-family="Arial,sans-serif" font-size="26" font-weight="800"
fill="#1a56db" text-anchor="middle">QC</text>
</svg>
</section>
{# ══ RIGHT: sign-in form ══ #}
<section class="login-form-side">
<div class="login-form-inner">
<div class="form-mobile-brand">
<span class="logo-badge"><i class="bi bi-clipboard-check-fill"></i></span>
<span class="name">Janitorial Quality Control System</span>
<span class="sub">By L.T Services, Inc</span>
</div>
<h2>Welcome</h2>
<p class="lead-sub">Sign in to access your inspections and reports.</p>
<form method="POST" action="{{ url_for('auth.login') }}"> <form method="POST" action="{{ url_for('auth.login') }}">
{{ form.hidden_tag() }} {{ form.hidden_tag() }}
<div class="mb-3"> <div class="mb-3">
{{ form.username.label(class="form-label fw-semibold") }} {{ form.username.label(class="form-label fw-semibold") }}
{{ form.username(class="form-control form-control-lg", placeholder="Enter username", autofocus=true) }} <div class="input-group input-group-lg">
<span class="input-group-text"><i class="bi bi-person"></i></span>
{{ form.username(class="form-control", placeholder="Enter username", autofocus=true) }}
</div>
{% if form.username.errors %} {% if form.username.errors %}
<div class="text-danger small mt-1"> <div class="text-danger small mt-1">
{% for error in form.username.errors %}{{ error }}{% endfor %} {% for error in form.username.errors %}{{ error }}{% endfor %}
@@ -251,7 +275,14 @@
<a href="{{ url_for('auth.forgot_password') }}" <a href="{{ url_for('auth.forgot_password') }}"
class="small text-muted text-decoration-none">Forgot password?</a> class="small text-muted text-decoration-none">Forgot password?</a>
</div> </div>
{{ form.password(class="form-control form-control-lg mt-1", placeholder="Enter password") }} <div class="input-group input-group-lg mt-1">
<span class="input-group-text"><i class="bi bi-lock"></i></span>
{{ form.password(class="form-control", placeholder="Enter password") }}
<button class="btn pw-toggle" type="button" id="pw-toggle" tabindex="-1"
aria-label="Show password">
<i class="bi bi-eye" id="pw-toggle-icon"></i>
</button>
</div>
{% if form.password.errors %} {% if form.password.errors %}
<div class="text-danger small mt-1"> <div class="text-danger small mt-1">
{% for error in form.password.errors %}{{ error }}{% endfor %} {% for error in form.password.errors %}{{ error }}{% endfor %}
@@ -264,22 +295,38 @@
{{ form.remember_me.label(class="form-check-label text-muted") }} {{ form.remember_me.label(class="form-check-label text-muted") }}
</div> </div>
<button type="submit" class="btn btn-primary btn-lg w-100"> <button type="submit" class="btn btn-login btn-lg w-100">
<i class="bi bi-box-arrow-in-right me-1"></i> Log In <i class="bi bi-box-arrow-in-right me-1"></i> Log In
</button> </button>
</form> </form>
</div>
<div class="card-footer text-center text-muted small"> <div class="login-foot">
&copy; <span id="copy-year"></span> Janitorial QC System &copy; <span id="copy-year"></span> Janitorial QC System
</div>
</div> </div>
</section>
</div>
</div> </div>
{# Remove .login-page when navigating away (SPA-style guard) #}
<script> <script>
document.getElementById('copy-year').textContent = new Date().getFullYear(); document.getElementById('copy-year').textContent = new Date().getFullYear();
// Password visibility toggle
(function () {
var btn = document.getElementById('pw-toggle');
var icon = document.getElementById('pw-toggle-icon');
var input = document.getElementById('password');
if (btn && input) {
btn.addEventListener('click', function () {
var show = input.type === 'password';
input.type = show ? 'text' : 'password';
icon.classList.toggle('bi-eye', !show);
icon.classList.toggle('bi-eye-slash', show);
btn.setAttribute('aria-label', show ? 'Hide password' : 'Show password');
});
}
})();
// Remove .login-page when navigating away (SPA-style guard)
window.addEventListener('pagehide', function () { window.addEventListener('pagehide', function () {
document.body.classList.remove('login-page'); document.body.classList.remove('login-page');
}); });