244 lines
12 KiB
HTML
244 lines
12 KiB
HTML
{% extends 'base.html' %}
|
|
{% load static %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid py-4">
|
|
{% if not selected_tenant %}
|
|
<div class="row">
|
|
<div class="col-12 text-center py-5">
|
|
<h1 class="display-4 fw-bold">Welcome to Campaign Manager</h1>
|
|
<p class="lead text-muted mb-4">Select a campaign to view the dashboard.</p>
|
|
<div class="row justify-content-center">
|
|
{% for tenant in tenants %}
|
|
<div class="col-md-4 mb-3">
|
|
<div class="card shadow-sm border-0 h-100">
|
|
<div class="card-body">
|
|
<h5 class="card-title fw-bold">{{ tenant.name }}</h5>
|
|
<a href="{% url 'select_campaign' tenant.id %}" class="btn btn-primary mt-3">Manage Campaign</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% else %}
|
|
<div class="row mb-3 align-items-center">
|
|
<div class="col-12">
|
|
<h1 class="display-5 fw-bold text-dark">{{ selected_tenant.name }} Dashboard</h1>
|
|
<p class="lead text-muted mb-0">Overview of voter engagement and field operations.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row mb-4 align-items-center">
|
|
<div class="col-12 col-md-8 col-lg-6">
|
|
<form action="{% url 'voter_list' %}" method="GET" class="w-100">
|
|
<div class="input-group shadow-sm">
|
|
<span class="input-group-text bg-white border-end-0">
|
|
<i class="bi bi-search text-muted"></i>
|
|
</span>
|
|
<input type="text" name="q" class="form-control border-start-0 ps-0" placeholder="Search voters by name..." aria-label="Search voters">
|
|
<button class="btn btn-primary px-4" type="submit">Search</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="col-12 col-md-4 col-lg-3 mt-2 mt-md-0">
|
|
<a href="{% url 'voter_advanced_search' %}" class="btn btn-outline-primary shadow-sm w-100">
|
|
<i class="bi bi-sliders me-2"></i>Advanced Search
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Main Stats Row -->
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-md-3">
|
|
<div class="card border-0 shadow-sm h-100 bg-primary text-white">
|
|
<div class="card-body p-4">
|
|
<h6 class="text-uppercase fw-bold small opacity-75">Active Voters</h6>
|
|
<h2 class="mb-0 fw-bold">{{ metrics.total_registered_voters }}</h2>
|
|
<a href="{% url 'voter_list' %}" class="text-white small text-decoration-none mt-2 d-inline-block">View All →</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body p-4">
|
|
<h6 class="text-uppercase fw-bold small text-muted">Target Voters</h6>
|
|
<h2 class="mb-0 fw-bold text-dark">{{ metrics.total_target_voters }}</h2>
|
|
<a href="{% url 'voter_list' %}?is_targeted=true" class="small text-decoration-none mt-2 d-inline-block text-primary">View Targets →</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body p-4">
|
|
<h6 class="text-uppercase fw-bold small text-muted">Supporting</h6>
|
|
<h2 class="mb-0 fw-bold text-success">{{ metrics.total_supporting }}</h2>
|
|
<a href="{% url 'voter_list' %}?support=supporting" class="small text-decoration-none mt-2 d-inline-block text-primary">View Supporters →</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body p-4">
|
|
<h6 class="text-uppercase fw-bold small text-muted">Target Households</h6>
|
|
<h2 class="mb-0 fw-bold text-info">{{ metrics.total_target_households }}</h2>
|
|
<a href="{% url 'voter_list' %}?is_targeted=true&has_address=true" class="small text-decoration-none mt-2 d-inline-block text-primary">View Map →</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Secondary Stats Row -->
|
|
<div class="row g-3 mb-4">
|
|
<div class="col-md-3">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body p-4 text-center">
|
|
<div class="bg-warning bg-opacity-10 p-3 rounded-circle d-inline-block mb-3">
|
|
<i class="bi bi-door-open-fill text-warning fs-3"></i>
|
|
</div>
|
|
<h6 class="text-uppercase fw-bold small text-muted mb-1">Door Visits</h6>
|
|
<h3 class="mb-0 fw-bold">{{ metrics.total_door_visits }}</h3>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body p-4 text-center">
|
|
<div class="bg-danger bg-opacity-10 p-3 rounded-circle d-inline-block mb-3">
|
|
<i class="bi bi-signpost-2-fill text-danger fs-3"></i>
|
|
</div>
|
|
<h6 class="text-uppercase fw-bold small text-muted mb-1">Signs</h6>
|
|
<h3 class="mb-0 fw-bold">{{ metrics.total_signs }}</h3>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body p-4 text-center">
|
|
<div class="bg-primary bg-opacity-10 p-3 rounded-circle d-inline-block mb-3">
|
|
<i class="bi bi-window-sidebar text-primary fs-3"></i>
|
|
</div>
|
|
<h6 class="text-uppercase fw-bold small text-muted mb-1">Window Stickers</h6>
|
|
<h3 class="mb-0 fw-bold">{{ metrics.total_window_stickers }}</h3>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="col-md-3">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body p-4">
|
|
<h6 class="text-uppercase fw-bold small text-muted mb-1">Donation Goal</h6>
|
|
<div class="d-flex justify-content-between align-items-end mb-2">
|
|
<h4 class="mb-0 fw-bold text-success">{{ metrics.donation_percentage }}%</h4>
|
|
</div>
|
|
<div class="progress" style="height: 10px;">
|
|
<div class="progress-bar bg-success" role="progressbar" style="width: {{ metrics.donation_percentage }}%" aria-valuenow="{{ metrics.donation_percentage }}" aria-valuemin="0" aria-valuemax="100"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Bottom Row: Other Metrics & Lists -->
|
|
<div class="row g-4">
|
|
<div class="col-lg-8">
|
|
<div class="card border-0 shadow-sm mb-4">
|
|
<div class="card-header bg-white border-0 py-3 d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0 fw-bold">Recent Interactions</h5>
|
|
<a href="/admin/core/interaction/" class="btn btn-sm btn-outline-primary rounded-pill px-3">View All</a>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div class="table-responsive">
|
|
<table class="table table-hover align-middle mb-0">
|
|
<thead class="bg-light text-muted small text-uppercase">
|
|
<tr>
|
|
<th class="px-4 py-3 border-0">Voter</th>
|
|
<th class="py-3 border-0">Type</th>
|
|
<th class="py-3 border-0">Date</th>
|
|
<th class="py-3 border-0">Notes</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for interaction in recent_interactions %}
|
|
<tr>
|
|
<td class="px-4 py-3 border-0">
|
|
<div class="fw-bold">{{ interaction.voter }}</div>
|
|
<div class="small text-muted">{{ interaction.volunteer|default:"Staff" }}</div>
|
|
</td>
|
|
<td class="py-3 border-0">
|
|
<span class="badge rounded-pill bg-light text-dark fw-normal border">
|
|
{{ interaction.type }}
|
|
</span>
|
|
</td>
|
|
<td class="py-3 border-0 text-muted small">
|
|
{{ interaction.date|date:"M d, Y" }}
|
|
</td>
|
|
<td class="py-3 border-0 text-muted small">
|
|
{{ interaction.description|truncatechars:50 }}
|
|
</td>
|
|
</tr>
|
|
{% empty %}
|
|
<tr>
|
|
<td colspan="4" class="text-center py-5 text-muted">No recent interactions found.</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-4">
|
|
<div class="card border-0 shadow-sm mb-4">
|
|
<div class="card-header bg-white border-0 py-3">
|
|
<h5 class="mb-0 fw-bold">Upcoming Events</h5>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<div class="list-group list-group-flush">
|
|
{% for event in upcoming_events %}
|
|
<div class="list-group-item px-4 py-3 border-0 border-bottom">
|
|
<div class="d-flex justify-content-between align-items-center mb-1">
|
|
<h6 class="mb-0 fw-bold text-dark">{{ event.name|default:event.event_type }}</h6>
|
|
<span class="badge rounded-pill bg-primary bg-opacity-10 text-primary small">
|
|
{{ event.event_type }}
|
|
</span>
|
|
</div>
|
|
<div class="small fw-medium text-dark">
|
|
<i class="bi bi-calendar-event me-1 text-muted"></i>{{ event.date|date:"M d, Y" }}
|
|
</div>
|
|
</div>
|
|
{% empty %}
|
|
<div class="text-center py-5 text-muted">
|
|
No upcoming events.
|
|
</div>
|
|
{% endfor %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Campaign Overview -->
|
|
<div class="card border-0 shadow-sm">
|
|
<div class="card-header bg-white border-0 py-3">
|
|
<h5 class="mb-0 fw-bold">Field Operations</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between mb-3">
|
|
<span class="text-muted">Total Volunteers</span>
|
|
<span class="fw-bold">{{ metrics.volunteers_count }}</span>
|
|
</div>
|
|
<div class="d-flex justify-content-between mb-3">
|
|
<span class="text-muted">Total Interactions</span>
|
|
<span class="fw-bold">{{ metrics.interactions_count }}</span>
|
|
</div>
|
|
<div class="d-flex justify-content-between mb-0">
|
|
<span class="text-muted">Total Events</span>
|
|
<span class="fw-bold">{{ metrics.events_count }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
{% endblock %} |