July 4 - Update Vendor Work-Order Workflow
This commit is contained in:
@@ -404,6 +404,53 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{# ── Vendor Work Orders (phase36) ───────────────────────────────────── #}
|
||||
{% if current_user.role in ['admin','director','project_manager'] %}
|
||||
<div class="card shadow-sm mt-3">
|
||||
<div class="card-header bg-light">
|
||||
<h6 class="mb-0"><i class="bi bi-send me-1"></i>Contractor Work Orders</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% set wos = issue.work_orders.all() %}
|
||||
{% if wos %}
|
||||
<ul class="list-unstyled small mb-3">
|
||||
{% for wo in wos %}
|
||||
{% set b = {'sent':'secondary','acknowledged':'info','completed':'success'}[wo.status] %}
|
||||
<li class="d-flex justify-content-between align-items-center border-bottom py-1">
|
||||
<span class="text-truncate me-2">{{ wo.vendor_name }}
|
||||
<span class="text-muted d-block" style="font-size:.75rem;">{{ wo.vendor_email }}</span>
|
||||
</span>
|
||||
<span class="badge bg-{{ b }}">{{ wo.status_label }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endif %}
|
||||
<form method="POST" action="{{ url_for('issues.dispatch_work_order', issue_id=issue.id) }}">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="mb-2">
|
||||
<label class="form-label small fw-semibold mb-1">Contractor name</label>
|
||||
<input type="text" name="vendor_name" class="form-control form-control-sm"
|
||||
value="{{ issue.vendor_name or '' }}" placeholder="e.g. Ace Plumbing" required>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label small fw-semibold mb-1">Contractor email</label>
|
||||
<input type="email" name="vendor_email" class="form-control form-control-sm"
|
||||
placeholder="name@contractor.com" required>
|
||||
</div>
|
||||
<div class="mb-2">
|
||||
<label class="form-label small fw-semibold mb-1">Message <span class="text-muted">(optional)</span></label>
|
||||
<textarea name="message" class="form-control form-control-sm" rows="2"
|
||||
placeholder="Any specific instructions…"></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-outline-primary btn-sm w-100">
|
||||
<i class="bi bi-envelope-paper me-1"></i> Send Work Order
|
||||
</button>
|
||||
<div class="form-text">Emails the contractor a private link to acknowledge & complete — no account needed.</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
</div>{# /col-lg-4 #}
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<body style="margin:0;padding:0;background:#f1f5f9;font-family:-apple-system,Segoe UI,Roboto,Arial,sans-serif;color:#1f2937;">
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="background:#f1f5f9;padding:24px 0;">
|
||||
<tr><td align="center">
|
||||
<table role="presentation" width="600" cellpadding="0" cellspacing="0" style="max-width:600px;width:100%;background:#ffffff;border-radius:12px;overflow:hidden;">
|
||||
<tr><td style="background:#1a56db;color:#ffffff;padding:20px 28px;font-size:18px;font-weight:700;">
|
||||
New Work Order
|
||||
</td></tr>
|
||||
<tr><td style="padding:24px 28px;">
|
||||
<p style="margin:0 0 12px;">Hello {{ work_order.vendor_name }},</p>
|
||||
<p style="margin:0 0 16px;">You've been assigned a work order at <strong>{{ facility_name }}</strong>.</p>
|
||||
|
||||
<table role="presentation" width="100%" cellpadding="0" cellspacing="0" style="border:1px solid #e5e7eb;border-radius:8px;margin-bottom:16px;">
|
||||
<tr><td style="padding:14px 16px;">
|
||||
<div style="font-size:12px;text-transform:uppercase;color:#6b7280;margin-bottom:4px;">What needs doing</div>
|
||||
<div style="margin-bottom:10px;">{{ issue.description }}</div>
|
||||
<div style="font-size:13px;color:#6b7280;">Severity: {{ issue.severity }}</div>
|
||||
{% if work_order.message %}
|
||||
<div style="margin-top:12px;padding-top:12px;border-top:1px solid #eee;font-size:14px;">
|
||||
<strong>Note:</strong> {{ work_order.message }}
|
||||
</div>
|
||||
{% endif %}
|
||||
</td></tr>
|
||||
</table>
|
||||
|
||||
<p style="margin:0 0 20px;">Open your work order to acknowledge it and mark it complete when done:</p>
|
||||
<p style="text-align:center;margin:0 0 8px;">
|
||||
<a href="{{ link }}" style="display:inline-block;background:#1a56db;color:#ffffff;text-decoration:none;padding:12px 28px;border-radius:8px;font-weight:600;">
|
||||
Open Work Order
|
||||
</a>
|
||||
</p>
|
||||
<p style="font-size:12px;color:#9aa3af;text-align:center;margin:12px 0 0;word-break:break-all;">{{ link }}</p>
|
||||
</td></tr>
|
||||
<tr><td style="padding:16px 28px;background:#f8fafc;color:#9aa3af;font-size:12px;">
|
||||
This link is private to you — please don't forward it.
|
||||
</td></tr>
|
||||
</table>
|
||||
</td></tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,100 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Work Order — {{ facility.name if facility else 'JQC' }}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
|
||||
<style>
|
||||
body { background:#f1f5f9; color:#1f2937; }
|
||||
.wo-wrap { max-width:640px; margin:2rem auto; padding:0 1rem; }
|
||||
.sev-critical{background:#dc2626}.sev-high{background:#ea580c}
|
||||
.sev-medium{background:#d97706}.sev-low{background:#64748b}
|
||||
@media (max-width:576px){ .wo-wrap{ margin:1rem auto; } }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="wo-wrap">
|
||||
|
||||
<div class="d-flex align-items-center gap-2 mb-3">
|
||||
<i class="bi bi-clipboard-check-fill fs-3 text-primary"></i>
|
||||
<div>
|
||||
<div class="fw-bold">Contractor Work Order</div>
|
||||
<div class="text-muted small">{{ facility.name if facility else 'Facility' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||
{% for category, message in messages %}
|
||||
<div class="alert alert-{{ 'success' if category == 'success' else 'warning' if category == 'warning' else 'info' }}">
|
||||
{{ message }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endwith %}
|
||||
|
||||
<div class="card shadow-sm mb-3">
|
||||
<div class="card-body">
|
||||
<div class="d-flex justify-content-between align-items-start mb-2">
|
||||
<span class="badge sev-{{ issue.severity }} text-white text-uppercase">{{ issue.severity }}</span>
|
||||
{% set badge = {'sent':'secondary','acknowledged':'info','completed':'success'}[wo.status] %}
|
||||
<span class="badge bg-{{ badge }}">{{ wo.status_label }}</span>
|
||||
</div>
|
||||
|
||||
<table class="table table-sm mb-3">
|
||||
<tr><th class="text-muted" style="width:120px;">Facility</th><td>{{ facility.name if facility else '—' }}</td></tr>
|
||||
{% if area %}<tr><th class="text-muted">Area</th><td>{{ area.name }}</td></tr>{% endif %}
|
||||
<tr><th class="text-muted">Reported</th><td>{{ issue.reported_at.strftime('%b %d, %Y') if issue.reported_at else '—' }}</td></tr>
|
||||
</table>
|
||||
|
||||
<div class="mb-2">
|
||||
<div class="text-muted small text-uppercase mb-1">What needs doing</div>
|
||||
<div>{{ issue.description }}</div>
|
||||
</div>
|
||||
|
||||
{% if wo.message %}
|
||||
<div class="mt-3 p-2 bg-light rounded">
|
||||
<div class="text-muted small text-uppercase mb-1">Note from the team</div>
|
||||
<div>{{ wo.message }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if wo.status == 'completed' %}
|
||||
<div class="alert alert-success">
|
||||
<i class="bi bi-check-circle-fill me-1"></i>
|
||||
You marked this complete on {{ wo.completed_at.strftime('%b %d, %Y') if wo.completed_at else 'file' }}.
|
||||
The team will verify the work. No further action is needed.
|
||||
</div>
|
||||
{% else %}
|
||||
{% if wo.status == 'sent' %}
|
||||
<form method="POST" action="{{ url_for('work_orders.update', token=wo.token) }}" class="mb-2">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="action" value="acknowledge">
|
||||
<button type="submit" class="btn btn-outline-primary w-100">
|
||||
<i class="bi bi-hand-thumbs-up me-1"></i> Acknowledge — I'll handle this
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
|
||||
<div class="card shadow-sm">
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ url_for('work_orders.update', token=wo.token) }}">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<input type="hidden" name="action" value="complete">
|
||||
<label class="form-label fw-semibold">Mark work complete</label>
|
||||
<textarea name="vendor_note" class="form-control mb-2" rows="3"
|
||||
placeholder="Optional: describe what you did…"></textarea>
|
||||
<button type="submit" class="btn btn-success w-100">
|
||||
<i class="bi bi-check2-circle me-1"></i> Mark as Completed
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<p class="text-center text-muted small mt-4">This link is private to you. Please don't share it.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user