Phase 2: initiate
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-8 offset-md-2">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h4 class="mb-0">{{ title }}</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
{{ form.username.label(class="form-label") }}
|
||||
{{ form.username(class="form-control") }}
|
||||
{% if form.username.errors %}
|
||||
<div class="text-danger small mt-1">
|
||||
{% for error in form.username.errors %}{{ error }}{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
{{ form.email.label(class="form-label") }}
|
||||
{{ form.email(class="form-control") }}
|
||||
{% if form.email.errors %}
|
||||
<div class="text-danger small mt-1">
|
||||
{% for error in form.email.errors %}{{ error }}{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
{{ form.password.label(class="form-label") }}
|
||||
{{ form.password(class="form-control", placeholder="Leave blank to keep current" if user else "") }}
|
||||
{% if form.password.errors %}
|
||||
<div class="text-danger small mt-1">
|
||||
{% for error in form.password.errors %}{{ error }}{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 mb-3">
|
||||
{{ form.confirm_password.label(class="form-label") }}
|
||||
{{ form.confirm_password(class="form-control") }}
|
||||
{% if form.confirm_password.errors %}
|
||||
<div class="text-danger small mt-1">
|
||||
{% for error in form.confirm_password.errors %}{{ error }}{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
{{ form.role.label(class="form-label") }}
|
||||
{{ form.role(class="form-select") }}
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-save"></i> Save User
|
||||
</button>
|
||||
<a href="{{ url_for('auth.list_users') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-x-circle"></i> Cancel
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,60 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}User Management{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<h2><i class="bi bi-people-fill"></i> User Management</h2>
|
||||
</div>
|
||||
<div class="col-md-6 text-end">
|
||||
<a href="{{ url_for('auth.create_user') }}" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> Add User
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Username</th>
|
||||
<th>Email</th>
|
||||
<th>Role</th>
|
||||
<th>Created</th>
|
||||
<th width="150">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in users %}
|
||||
<tr>
|
||||
<td><strong>{{ user.username }}</strong></td>
|
||||
<td>{{ user.email }}</td>
|
||||
<td>
|
||||
<span class="badge bg-{% if user.role == 'admin' %}danger{% elif user.role == 'supervisor' %}warning{% else %}info{% endif %}">
|
||||
{{ user.role|title }}
|
||||
</span>
|
||||
</td>
|
||||
<td>{{ user.created_at.strftime('%Y-%m-%d') }}</td>
|
||||
<td>
|
||||
<a href="{{ url_for('auth.edit_user', user_id=user.id) }}" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
{% if user.id != current_user.id %}
|
||||
<form method="POST" action="{{ url_for('auth.delete_user', user_id=user.id) }}" class="d-inline" onsubmit="return confirm('Delete this user?');">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -24,14 +24,22 @@
|
||||
<a class="nav-link" href="{{ url_for('dashboard.index') }}">Dashboard</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('inspections.index') }}">Inspections</a>
|
||||
<a class="nav-link" href="{{ url_for('facilities.list_facilities') }}">Facilities</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('templates.index') }}">Templates</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('inspections.index') }}">Inspections</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('reports.index') }}">Reports</a>
|
||||
</li>
|
||||
{% if current_user.role == 'admin' %}
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="{{ url_for('auth.list_users') }}">Users</a>
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<ul class="navbar-nav">
|
||||
<li class="nav-item dropdown">
|
||||
|
||||
+146
-21
@@ -1,47 +1,172 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Dashboard - Janitorial QC{% endblock %}
|
||||
{% block title %}Dashboard{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="row mb-4">
|
||||
<div class="col-12">
|
||||
<h2>Welcome, {{ current_user.username }}!</h2>
|
||||
<p class="text-muted">Role: {{ current_user.role|title }}</p>
|
||||
<p class="text-muted">
|
||||
<span class="badge bg-{% if current_user.role == 'admin' %}danger{% elif current_user.role == 'supervisor' %}warning{% else %}info{% endif %}">
|
||||
{{ current_user.role|title }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-primary mb-3">
|
||||
<div class="row">
|
||||
<div class="col-md-3 mb-4">
|
||||
<div class="card text-white bg-primary h-100">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="bi bi-clipboard-data"></i> Today's Inspections</h5>
|
||||
<p class="card-text display-6">0</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h6 class="card-title text-white-50">Today's Inspections</h6>
|
||||
<h2 class="mb-0">{{ today_inspections }}</h2>
|
||||
</div>
|
||||
<div>
|
||||
<i class="bi bi-clipboard-data" style="font-size: 3rem; opacity: 0.3;"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-success mb-3">
|
||||
|
||||
<div class="col-md-3 mb-4">
|
||||
<div class="card text-white bg-success h-100">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="bi bi-check-circle"></i> Completed</h5>
|
||||
<p class="card-text display-6">0</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h6 class="card-title text-white-50">Completed Today</h6>
|
||||
<h2 class="mb-0">{{ completed_today }}</h2>
|
||||
</div>
|
||||
<div>
|
||||
<i class="bi bi-check-circle" style="font-size: 3rem; opacity: 0.3;"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-warning mb-3">
|
||||
|
||||
<div class="col-md-3 mb-4">
|
||||
<div class="card text-white bg-warning h-100">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="bi bi-exclamation-triangle"></i> Issues Open</h5>
|
||||
<p class="card-text display-6">0</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h6 class="card-title text-white-50">Open Issues</h6>
|
||||
<h2 class="mb-0">{{ open_issues }}</h2>
|
||||
</div>
|
||||
<div>
|
||||
<i class="bi bi-exclamation-triangle" style="font-size: 3rem; opacity: 0.3;"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<div class="card text-white bg-info mb-3">
|
||||
|
||||
<div class="col-md-3 mb-4">
|
||||
<div class="card text-white bg-info h-100">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="bi bi-graph-up"></i> Avg Score</h5>
|
||||
<p class="card-text display-6">--</p>
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<h6 class="card-title text-white-50">Avg Score (30d)</h6>
|
||||
<h2 class="mb-0">{{ avg_score if avg_score else '--' }}%</h2>
|
||||
</div>
|
||||
<div>
|
||||
<i class="bi bi-graph-up" style="font-size: 3rem; opacity: 0.3;"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% if current_user.role in ['admin', 'supervisor'] %}
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body text-center">
|
||||
<i class="bi bi-building text-primary" style="font-size: 2.5rem;"></i>
|
||||
<h3 class="mt-2">{{ total_facilities }}</h3>
|
||||
<p class="text-muted mb-0">Active Facilities</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body text-center">
|
||||
<i class="bi bi-file-earmark-text text-success" style="font-size: 2.5rem;"></i>
|
||||
<h3 class="mt-2">{{ total_templates }}</h3>
|
||||
<p class="text-muted mb-0">Templates</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% if current_user.role == 'admin' %}
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body text-center">
|
||||
<i class="bi bi-people text-warning" style="font-size: 2.5rem;"></i>
|
||||
<h3 class="mt-2">{{ total_users }}</h3>
|
||||
<p class="text-muted mb-0">Users</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-light">
|
||||
<h5 class="mb-0"><i class="bi bi-clock-history"></i> Recent Activity</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if recent_inspections %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover mb-0">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Facility</th>
|
||||
<th>Area</th>
|
||||
<th>Inspector</th>
|
||||
<th>Score</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for inspection in recent_inspections %}
|
||||
<tr>
|
||||
<td>{{ inspection.inspection_date.strftime('%Y-%m-%d %H:%M') }}</td>
|
||||
<td>{{ inspection.facility.name }}</td>
|
||||
<td>{{ inspection.area.name if inspection.area else 'N/A' }}</td>
|
||||
<td>{{ inspection.inspector.username }}</td>
|
||||
<td>
|
||||
{% if inspection.overall_score %}
|
||||
<span class="badge bg-{% if inspection.overall_score >= 90 %}success{% elif inspection.overall_score >= 70 %}warning{% else %}danger{% endif %}">
|
||||
{{ inspection.overall_score }}%
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="text-muted">--</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<span class="badge bg-{% if inspection.status == 'completed' %}success{% elif inspection.status == 'flagged' %}danger{% else %}secondary{% endif %}">
|
||||
{{ inspection.status|title }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info mb-0">
|
||||
<i class="bi bi-info-circle"></i> No recent inspections to display.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,39 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-8 offset-md-2">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h4 class="mb-0">{{ title }} - {{ facility.name }}</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.name.label(class="form-label") }}
|
||||
{{ form.name(class="form-control") }}
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
{{ form.area_type.label(class="form-label") }}
|
||||
{{ form.area_type(class="form-select") }}
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-save"></i> Save Area
|
||||
</button>
|
||||
<a href="{{ url_for('facilities.view_facility', facility_id=facility.id) }}" class="btn btn-secondary">
|
||||
<i class="bi bi-x-circle"></i> Cancel
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,57 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-8 offset-md-2">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h4 class="mb-0">{{ title }}</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.name.label(class="form-label") }}
|
||||
{{ form.name(class="form-control") }}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.address.label(class="form-label") }}
|
||||
{{ form.address(class="form-control", rows=3) }}
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
{{ form.contact_person.label(class="form-label") }}
|
||||
{{ form.contact_person(class="form-control") }}
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
{{ form.contact_phone.label(class="form-label") }}
|
||||
{{ form.contact_phone(class="form-control") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="form-check">
|
||||
{{ form.active(class="form-check-input") }}
|
||||
{{ form.active.label(class="form-check-label") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-save"></i> Save Facility
|
||||
</button>
|
||||
<a href="{{ url_for('facilities.list_facilities') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-x-circle"></i> Cancel
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,60 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Facilities{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<h2><i class="bi bi-building"></i> Facilities</h2>
|
||||
</div>
|
||||
<div class="col-md-6 text-end">
|
||||
{% if current_user.role in ['admin', 'supervisor'] %}
|
||||
<a href="{{ url_for('facilities.create_facility') }}" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> Add Facility
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
{% for facility in facilities %}
|
||||
<div class="col-md-6 col-lg-4 mb-4">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
<a href="{{ url_for('facilities.view_facility', facility_id=facility.id) }}" class="text-decoration-none">
|
||||
{{ facility.name }}
|
||||
</a>
|
||||
{% if not facility.active %}
|
||||
<span class="badge bg-secondary">Inactive</span>
|
||||
{% endif %}
|
||||
</h5>
|
||||
|
||||
{% if facility.address %}
|
||||
<p class="card-text text-muted small">
|
||||
<i class="bi bi-geo-alt"></i> {{ facility.address }}
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<div class="mt-3">
|
||||
<small class="text-muted">
|
||||
<i class="bi bi-diagram-3"></i> {{ facility.areas.count() }} areas
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent">
|
||||
<a href="{{ url_for('facilities.view_facility', facility_id=facility.id) }}" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-eye"></i> View Details
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-12">
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i> No facilities configured yet.
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,124 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ facility.name }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-8">
|
||||
<h2><i class="bi bi-building"></i> {{ facility.name }}</h2>
|
||||
</div>
|
||||
<div class="col-md-4 text-end">
|
||||
{% if current_user.role in ['admin', 'supervisor'] %}
|
||||
<a href="{{ url_for('facilities.edit_facility', facility_id=facility.id) }}" class="btn btn-outline-primary">
|
||||
<i class="bi bi-pencil"></i> Edit
|
||||
</a>
|
||||
<a href="{{ url_for('facilities.create_area', facility_id=facility.id) }}" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> Add Area
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-light">
|
||||
<h5 class="mb-0">Facility Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-sm table-borderless">
|
||||
<tr>
|
||||
<th width="40%">Address:</th>
|
||||
<td>{{ facility.address or 'N/A' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Contact Person:</th>
|
||||
<td>{{ facility.contact_person or 'N/A' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Contact Phone:</th>
|
||||
<td>{{ facility.contact_phone or 'N/A' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Status:</th>
|
||||
<td>
|
||||
<span class="badge bg-{{ 'success' if facility.active else 'secondary' }}">
|
||||
{{ 'Active' if facility.active else 'Inactive' }}
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-light">
|
||||
<h5 class="mb-0">Statistics</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row text-center">
|
||||
<div class="col-6">
|
||||
<h3 class="text-primary">{{ areas|length }}</h3>
|
||||
<small class="text-muted">Areas</small>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
<h3 class="text-info">{{ facility.inspections.count() }}</h3>
|
||||
<small class="text-muted">Inspections</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-light">
|
||||
<h5 class="mb-0"><i class="bi bi-diagram-3"></i> Areas</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if areas %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead class="table-light">
|
||||
<tr>
|
||||
<th>Area Name</th>
|
||||
<th>Type</th>
|
||||
<th>Inspections</th>
|
||||
<th width="150">Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for area in areas %}
|
||||
<tr>
|
||||
<td><strong>{{ area.name }}</strong></td>
|
||||
<td>
|
||||
<span class="badge bg-secondary">{{ area.area_type|title if area.area_type else 'N/A' }}</span>
|
||||
</td>
|
||||
<td>{{ area.inspections.count() }}</td>
|
||||
<td>
|
||||
{% if current_user.role in ['admin', 'supervisor'] %}
|
||||
<a href="{{ url_for('facilities.edit_area', area_id=area.id) }}" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<form method="POST" action="{{ url_for('facilities.delete_area', area_id=area.id) }}" class="d-inline" onsubmit="return confirm('Delete this area?');">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info mb-0">
|
||||
<i class="bi bi-info-circle"></i> No areas defined for this facility yet.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
+32
-12
@@ -4,25 +4,45 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center mt-5">
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow">
|
||||
<div class="card-header bg-primary text-white text-center">
|
||||
<h4><i class="bi bi-clipboard-check"></i> Janitorial QC System</h4>
|
||||
<div class="col-md-5 col-lg-4">
|
||||
<div class="card shadow-lg">
|
||||
<div class="card-header bg-primary text-white text-center py-4">
|
||||
<h3><i class="bi bi-clipboard-check-fill"></i> Janitorial QC</h3>
|
||||
<p class="mb-0">Quality Control System</p>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="card-body p-4">
|
||||
<form method="POST" action="{{ url_for('auth.login') }}">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="username" class="form-label">Username</label>
|
||||
<input type="text" class="form-control" id="username" name="username" required autofocus>
|
||||
{{ form.username.label(class="form-label") }}
|
||||
{{ form.username(class="form-control form-control-lg", placeholder="Enter username") }}
|
||||
{% 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-3">
|
||||
<label for="password" class="form-label">Password</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required>
|
||||
|
||||
<div class="mb-4">
|
||||
{{ form.password.label(class="form-label") }}
|
||||
{{ form.password(class="form-control form-control-lg", 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>
|
||||
<button type="submit" class="btn btn-primary w-100">Login</button>
|
||||
|
||||
<button type="submit" class="btn btn-primary btn-lg w-100">
|
||||
<i class="bi bi-box-arrow-in-right"></i> Login
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-footer text-center text-muted small">
|
||||
© 2025 Janitorial QC System
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,132 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Edit Template - {{ template.name }}{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
.checklist-item { cursor: move; }
|
||||
.checklist-item:hover { background-color: #f8f9fa; }
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-8">
|
||||
<h2><i class="bi bi-file-earmark-text"></i> {{ template.name }}</h2>
|
||||
</div>
|
||||
<div class="col-md-4 text-end">
|
||||
<a href="{{ url_for('templates.create_checklist_item', template_id=template.id) }}" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> Add Item
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-4">
|
||||
<div class="card shadow-sm mb-4">
|
||||
<div class="card-header bg-light">
|
||||
<h5 class="mb-0">Template Settings</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.name.label(class="form-label") }}
|
||||
{{ form.name(class="form-control") }}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.description.label(class="form-label") }}
|
||||
{{ form.description(class="form-control", rows=3) }}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.frequency.label(class="form-label") }}
|
||||
{{ form.frequency(class="form-select") }}
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary w-100">
|
||||
<i class="bi bi-save"></i> Update Template
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-light">
|
||||
<h5 class="mb-0"><i class="bi bi-check2-square"></i> Checklist Items ({{ checklist_items|length }})</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if checklist_items %}
|
||||
<div id="checklist-items" class="list-group">
|
||||
{% for item in checklist_items %}
|
||||
<div class="list-group-item checklist-item" data-item-id="{{ item.id }}">
|
||||
<div class="d-flex justify-content-between align-items-start">
|
||||
<div class="flex-grow-1">
|
||||
<div class="d-flex align-items-center mb-1">
|
||||
<i class="bi bi-grip-vertical text-muted me-2"></i>
|
||||
<span class="badge bg-secondary me-2">{{ item.category }}</span>
|
||||
<span class="badge bg-info">{{ item.scoring_type.replace('_', ' ')|title }}</span>
|
||||
{% if item.requires_photo %}
|
||||
<span class="badge bg-warning ms-2"><i class="bi bi-camera"></i></span>
|
||||
{% endif %}
|
||||
</div>
|
||||
<p class="mb-1">{{ item.item_description }}</p>
|
||||
<small class="text-muted">Weight: {{ item.weight }}</small>
|
||||
</div>
|
||||
<div class="ms-3">
|
||||
<a href="{{ url_for('templates.edit_checklist_item', item_id=item.id) }}" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<form method="POST" action="{{ url_for('templates.delete_checklist_item', item_id=item.id) }}" class="d-inline" onsubmit="return confirm('Delete this item?');">
|
||||
<button type="submit" class="btn btn-sm btn-outline-danger">
|
||||
<i class="bi bi-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="alert alert-info mb-0">
|
||||
<i class="bi bi-info-circle"></i> No checklist items yet. Click "Add Item" to get started.
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.15.0/Sortable.min.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const el = document.getElementById('checklist-items');
|
||||
if (el) {
|
||||
new Sortable(el, {
|
||||
animation: 150,
|
||||
handle: '.bi-grip-vertical',
|
||||
onEnd: function(evt) {
|
||||
const itemOrder = [];
|
||||
el.querySelectorAll('.checklist-item').forEach(item => {
|
||||
itemOrder.push(item.getAttribute('data-item-id'));
|
||||
});
|
||||
|
||||
fetch('{{ url_for("templates.reorder_items", template_id=template.id) }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ item_order: itemOrder })
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,44 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-6 offset-md-3">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h4 class="mb-0">{{ title }}</h4>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.name.label(class="form-label") }}
|
||||
{{ form.name(class="form-control") }}
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.description.label(class="form-label") }}
|
||||
{{ form.description(class="form-control", rows=3) }}
|
||||
</div>
|
||||
|
||||
<div class="mb-4">
|
||||
{{ form.frequency.label(class="form-label") }}
|
||||
{{ form.frequency(class="form-select") }}
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-save"></i> Save Template
|
||||
</button>
|
||||
<a href="{{ url_for('templates.index') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-x-circle"></i> Cancel
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,61 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}{{ title }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row">
|
||||
<div class="col-md-8 offset-md-2">
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h4 class="mb-0">{{ title }}</h4>
|
||||
<small>Template: {{ template.name }}</small>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST">
|
||||
{{ form.hidden_tag() }}
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
{{ form.category.label(class="form-label") }}
|
||||
{{ form.category(class="form-control", placeholder="e.g., Restrooms, Floors, etc.") }}
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
{{ form.scoring_type.label(class="form-label") }}
|
||||
{{ form.scoring_type(class="form-select") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
{{ form.item_description.label(class="form-label") }}
|
||||
{{ form.item_description(class="form-control", rows=3) }}
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
{{ form.weight.label(class="form-label") }}
|
||||
{{ form.weight(class="form-control") }}
|
||||
<small class="text-muted">1.0 = standard weight</small>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label class="form-label"> </label>
|
||||
<div class="form-check">
|
||||
{{ form.requires_photo(class="form-check-input") }}
|
||||
{{ form.requires_photo.label(class="form-check-label") }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-save"></i> Save Item
|
||||
</button>
|
||||
<a href="{{ url_for('templates.edit_template', template_id=template.id) }}" class="btn btn-secondary">
|
||||
<i class="bi bi-x-circle"></i> Cancel
|
||||
</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,54 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block title %}Inspection Templates{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col-md-6">
|
||||
<h2><i class="bi bi-file-earmark-text"></i> Inspection Templates</h2>
|
||||
</div>
|
||||
<div class="col-md-6 text-end">
|
||||
{% if current_user.role in ['admin', 'supervisor'] %}
|
||||
<a href="{{ url_for('templates.create_template') }}" class="btn btn-primary">
|
||||
<i class="bi bi-plus-circle"></i> Create Template
|
||||
</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
{% for template in templates %}
|
||||
<div class="col-md-6 col-lg-4 mb-4">
|
||||
<div class="card shadow-sm h-100">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
<a href="{{ url_for('templates.view_template', template_id=template.id) }}" class="text-decoration-none">
|
||||
{{ template.name }}
|
||||
</a>
|
||||
</h5>
|
||||
|
||||
<p class="card-text text-muted small">{{ template.description or 'No description' }}</p>
|
||||
|
||||
<div class="mt-3">
|
||||
<span class="badge bg-info">{{ template.frequency|title }}</span>
|
||||
<small class="text-muted ms-2">
|
||||
<i class="bi bi-check2-square"></i> {{ template.checklist_items.count() }} items
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent">
|
||||
<a href="{{ url_for('templates.view_template', template_id=template.id) }}" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-eye"></i> View Template
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="col-12">
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle"></i> No templates created yet.
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user