287 lines
13 KiB
HTML
287 lines
13 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}Login - Janitorial QC{% endblock %}
|
|
|
|
{% block extra_css %}
|
|
<style>
|
|
/* ── Login page background ─────────────────────────────────────────────── */
|
|
body.login-page {
|
|
min-height: 100vh;
|
|
position: relative;
|
|
overflow: hidden;
|
|
}
|
|
|
|
/* SVG illustration fills the entire viewport behind the login card */
|
|
body.login-page .login-bg-svg {
|
|
position: fixed;
|
|
inset: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
z-index: 0;
|
|
object-fit: cover;
|
|
}
|
|
|
|
body.login-page .login-wrapper {
|
|
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;
|
|
width: 100%;
|
|
max-width: 420px;
|
|
}
|
|
|
|
body.login-page .login-card .card-header {
|
|
background: #1a56db;
|
|
padding: 2rem 2rem 1.5rem;
|
|
border-bottom: none;
|
|
}
|
|
|
|
body.login-page .login-card .card-body {
|
|
padding: 2rem;
|
|
background: #fff;
|
|
}
|
|
|
|
body.login-page .login-card .card-footer {
|
|
background: #f8f9fa;
|
|
border-top: 1px solid #e9ecef;
|
|
padding: .75rem 2rem;
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|
|
{% block content %}
|
|
<script>document.body.classList.add('login-page');</script>
|
|
|
|
{# ── Background SVG illustration — inline, no external dependency ── #}
|
|
<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"/>
|
|
|
|
<!-- Grid -->
|
|
<g class="gd">
|
|
<line x1="0" y1="128" x2="1440" y2="128"/><line x1="0" y1="256" x2="1440" y2="256"/>
|
|
<line x1="0" y1="384" x2="1440" y2="384"/><line x1="0" y1="512" x2="1440" y2="512"/>
|
|
<line x1="0" y1="640" x2="1440" y2="640"/><line x1="0" y1="768" x2="1440" y2="768"/>
|
|
<line x1="170" y1="0" x2="170" y2="900"/><line x1="425" y1="0" x2="425" y2="900"/>
|
|
<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 class="card-body">
|
|
<form method="POST" action="{{ url_for('auth.login') }}">
|
|
{{ form.hidden_tag() }}
|
|
|
|
<div class="mb-3">
|
|
{{ form.username.label(class="form-label fw-semibold") }}
|
|
{{ form.username(class="form-control form-control-lg", placeholder="Enter username", autofocus=true) }}
|
|
{% if form.username.errors %}
|
|
<div class="text-danger small mt-1">
|
|
{% for error in form.username.errors %}{{ error }}{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<div class="d-flex justify-content-between align-items-baseline">
|
|
{{ form.password.label(class="form-label fw-semibold mb-0") }}
|
|
<a href="{{ url_for('auth.forgot_password') }}"
|
|
class="small text-muted text-decoration-none">Forgot password?</a>
|
|
</div>
|
|
{{ form.password(class="form-control form-control-lg mt-1", placeholder="Enter password") }}
|
|
{% if form.password.errors %}
|
|
<div class="text-danger small mt-1">
|
|
{% for error in form.password.errors %}{{ error }}{% endfor %}
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
|
|
<div class="mb-4 form-check">
|
|
{{ form.remember_me(class="form-check-input") }}
|
|
{{ form.remember_me.label(class="form-check-label text-muted") }}
|
|
</div>
|
|
|
|
<button type="submit" class="btn btn-primary btn-lg w-100">
|
|
<i class="bi bi-box-arrow-in-right me-1"></i> Log In
|
|
</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="card-footer text-center text-muted small">
|
|
© <span id="copy-year"></span> Janitorial QC System
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
{# Remove .login-page when navigating away (SPA-style guard) #}
|
|
<script>
|
|
document.getElementById('copy-year').textContent = new Date().getFullYear();
|
|
window.addEventListener('pagehide', function () {
|
|
document.body.classList.remove('login-page');
|
|
});
|
|
</script>
|
|
{% endblock %} |