37769-vm/core/templates/core/voter_detail.html
2026-02-01 21:17:19 +00:00

1153 lines
63 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% block content %}
<!-- Google Maps JS -->
<script src="https://maps.googleapis.com/maps/api/js?key={{ GOOGLE_MAPS_API_KEY }}"></script>
<div class="container py-5">
<!-- Breadcrumb -->
<nav aria-label="breadcrumb" class="mb-4">
<ol class="breadcrumb">
<li class="breadcrumb-item"><a href="{% url 'index' %}" class="text-decoration-none">Dashboard</a></li>
<li class="breadcrumb-item"><a href="{% url 'voter_list' %}" class="text-decoration-none">Voters</a></li>
<li class="breadcrumb-item active" aria-current="page">{% if voter.nickname %}{{ voter.nickname }}{% else %}{{ voter.first_name }}{% endif %} {{ voter.last_name }}</li>
</ol>
</nav>
<!-- Header / Demographics Card -->
<div class="card border-0 shadow-sm mb-4">
<div class="card-body p-4 p-md-5">
<div class="row align-items-center">
<div class="col-md-auto mb-3 mb-md-0">
<div class="bg-primary text-white rounded-circle d-flex align-items-center justify-content-center shadow-sm" style="width: 80px; height: 80px; font-size: 2rem;">
{% if voter.nickname %}{{ voter.nickname|first }}{% else %}{{ voter.first_name|first }}{% endif %}{{ voter.last_name|first }}
</div>
</div>
<div class="col-md">
<div class="d-flex align-items-center">
<h1 class="h3 mb-1 me-3">{% if voter.nickname %}{{ voter.nickname }}{% else %}{{ voter.first_name }}{% endif %} {{ voter.last_name }}</h1>
{% if can_edit_voter %}
<button class="btn btn-sm btn-outline-primary" data-bs-toggle="modal" data-bs-target="#editVoterModal">
<i class="bi bi-pencil me-1"></i>Edit
</button>
{% endif %}
</div>
<p class="text-muted mb-0">
<i class="bi bi-geo-alt me-1"></i> {{ voter.address|default:"No address on file" }}
</p>
<div class="mt-2">
<span class="badge bg-light text-dark border me-1">Voter ID: {{ voter.voter_id|default:"N/A" }}</span>
<span class="badge bg-light text-dark border me-1">District: {{ voter.district|default:"-" }}</span>
<span class="badge bg-light text-dark border me-1">Neighborhood: {{ voter.neighborhood|default:"-" }}</span>
<span class="badge bg-light text-dark border">Precinct: {{ voter.precinct|default:"-" }}</span>
</div>
</div>
<div class="col-md-auto text-md-end mt-3 mt-md-0">
{% if voter.is_targeted %}
<div class="badge bg-primary mb-2 px-3 py-2 text-uppercase fw-bold" style="font-size: 0.75rem;"><i class="bi bi-bullseye me-2"></i>Targeted Voter</div>
{% endif %}
{% if voter.candidate_support == 'supporting' %}
<div class="h4 text-success mb-0"><i class="bi bi-check-circle-fill me-2"></i>Supporting</div>
{% elif voter.candidate_support == 'not_supporting' %}
<div class="h4 text-danger mb-0"><i class="bi bi-x-circle-fill me-2"></i>Not Supporting</div>
{% else %}
<div class="h4 text-secondary mb-0"><i class="bi bi-question-circle-fill me-2"></i>Unknown Support</div>
{% endif %}
</div>
</div>
</div>
</div>
<div class="row">
<!-- Left Column: Quick Stats & Likelihood -->
<div class="col-lg-4">
<!-- Map Card -->
<div class="card border-0 shadow-sm mb-4 overflow-hidden">
<div class="card-header bg-white py-3">
<h5 class="card-title mb-0">Location Map</h5>
</div>
<div id="voterMap" style="height: 250px; background-color: #f8f9fa;">
{% if not voter.latitude or not voter.longitude %}
<div class="d-flex align-items-center justify-content-center h-100 flex-column text-muted p-4 text-center">
<i class="bi bi-map mb-2" style="font-size: 2rem;"></i>
<p class="small mb-0">No coordinates available for this address.</p>
<p class="small mb-0">Edit profile to trigger auto-geocoding.</p>
</div>
{% endif %}
</div>
</div>
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-white py-3">
<h5 class="card-title mb-0">Contact Information</h5>
</div>
<div class="card-body">
<ul class="list-unstyled mb-0">
<li class="mb-3">
<label class="small text-muted d-block">Email</label>
<span class="fw-semibold">{{ voter.email|default:"N/A" }}</span>
</li>
<li class="mb-3">
<label class="small text-muted d-block">Phone</label>
{% if voter.phone %}
<div class="d-flex align-items-center">
<span class="fw-semibold me-2">{{ voter.phone }}</span>
<a href="tel:{{ voter.phone }}" class="btn btn-sm btn-outline-primary py-0 px-2 me-1" title="Call">
<i class="bi bi-telephone"></i>
</a>
<a href="sms:{{ voter.phone }}" class="btn btn-sm btn-outline-secondary py-0 px-2 me-2" title="Text">
<i class="bi bi-chat-dots"></i>
</a>
<span class="badge bg-light text-dark border">{{ voter.get_phone_type_display }}</span>
</div>
{% else %}
<span class="fw-semibold">N/A</span>
{% endif %}
</li>
{% if voter.secondary_phone %}
<li class="mb-3">
<label class="small text-muted d-block">Secondary Phone</label>
<div class="d-flex align-items-center">
<span class="fw-semibold me-2">{{ voter.secondary_phone }}</span>
<a href="tel:{{ voter.secondary_phone }}" class="btn btn-sm btn-outline-primary py-0 px-2 me-1" title="Call">
<i class="bi bi-telephone"></i>
</a>
<a href="sms:{{ voter.secondary_phone }}" class="btn btn-sm btn-outline-secondary py-0 px-2 me-2" title="Text">
<i class="bi bi-chat-dots"></i>
</a>
<span class="badge bg-light text-dark border">{{ voter.get_secondary_phone_type_display }}</span>
</div>
</li>
{% endif %}
<label class="small text-muted d-block">Birthdate</label>
<span class="fw-semibold">{{ voter.birthdate|date:"M d, Y"|default:"N/A" }}</span>
</li>
<li class="mb-3">
<label class="small text-muted d-block">Prior State</label>
<span class="fw-semibold">{{ voter.prior_state|default:"N/A" }}</span>
</li>
<li>
<label class="small text-muted d-block">Registration Date</label>
<span class="fw-semibold">{{ voter.registration_date|date:"M d, Y"|default:"Unknown" }}</span>
</li>
</ul>
</div>
</div>
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-white py-3 d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">Likelihood to Vote</h5>
{% if can_edit_voter %}
<button class="btn btn-sm btn-link text-primary p-0" data-bs-toggle="modal" data-bs-target="#addLikelihoodModal">
<i class="bi bi-plus-circle"></i>
</button>
{% endif %}
</div>
<div class="card-body">
{% for likelihood in likelihoods %}
<div class="d-flex justify-content-between align-items-center mb-3 last-child-mb-0">
<div>
<span class="text-muted">{{ likelihood.election_type.name }}</span>
{% if likelihood.likelihood == 'very_likely' %}
<span class="badge bg-success ms-2">Very Likely</span>
{% elif likelihood.likelihood == 'somewhat_likely' %}
<span class="badge bg-info ms-2">Somewhat Likely</span>
{% else %}
<span class="badge bg-secondary ms-2">Not Likely</span>
{% endif %}
</div>
{% if can_edit_voter %}
<div class="text-nowrap ms-2">
<button class="btn btn-sm btn-link text-primary p-0 me-2" data-bs-toggle="modal" data-bs-target="#editLikelihoodModal{{ likelihood.id }}">
<i class="bi bi-pencil small"></i>
</button>
<button class="btn btn-sm btn-link text-danger p-0" data-bs-toggle="modal" data-bs-target="#deleteLikelihoodModal{{ likelihood.id }}">
<i class="bi bi-trash small"></i>
</button>
</div>
{% endif %}
</div>
{% empty %}
<p class="text-muted mb-0 italic">No likelihood data available.</p>
{% endfor %}
</div>
</div>
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-white py-3">
<h5 class="card-title mb-0">Campaign Assets</h5>
</div>
<div class="card-body">
<div class="d-flex align-items-center mb-3">
<div class="flex-grow-1">
<span class="text-muted">Yard Sign Status</span>
</div>
<div>
{% if voter.yard_sign == 'has' %}
<span class="badge bg-warning text-dark">Has Sign</span>
{% elif voter.yard_sign == 'wants' %}
<span class="badge bg-info">Wants Sign</span>
{% else %}
<span class="badge bg-light text-dark border">None</span>
{% endif %}
</div>
</div>
<div class="d-flex align-items-center">
<div class="flex-grow-1">
<span class="text-muted">Window Sticker Status</span>
</div>
<div>
{% if voter.window_sticker == 'has' %}
<span class="badge bg-warning text-dark">Has Sticker</span>
{% elif voter.window_sticker == 'wants' %}
<span class="badge bg-info">Wants Sticker</span>
{% else %}
<span class="badge bg-light text-dark border">None</span>
{% endif %}
</div>
</div>
</div>
</div>
<div class="card border-0 shadow-sm mb-4">
<div class="card-header bg-white py-3">
<h5 class="card-title mb-0">Notes</h5>
</div>
<div class="card-body">
<p class="mb-0 text-muted" style="white-space: pre-wrap;">{{ voter.notes|default:"No notes available." }}</p>
</div>
</div>
</div>
<!-- Right Column: Detailed Records -->
<div class="col-lg-8">
<!-- Nav Tabs -->
<ul class="nav nav-tabs border-0 mb-4" id="voterTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active border-0 py-3 px-4" id="interactions-tab" data-bs-toggle="tab" data-bs-target="#interactions" type="button" role="tab">Interactions</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link border-0 py-3 px-4" id="events-tab" data-bs-toggle="tab" data-bs-target="#events" type="button" role="tab">Events</button>
</li>
{% if can_view_donations %}
<li class="nav-item" role="presentation">
<button class="nav-link border-0 py-3 px-4" id="donations-tab" data-bs-toggle="tab" data-bs-target="#donations" type="button" role="tab">Donations</button>
</li>
{% endif %}
<li class="nav-item" role="presentation">
<button class="nav-link border-0 py-3 px-4" id="voting-tab" data-bs-toggle="tab" data-bs-target="#voting" type="button" role="tab">Voting History</button>
</li>
</ul>
<div class="tab-content" id="voterTabsContent">
<!-- Interactions Tab -->
<div class="tab-pane fade show active" id="interactions" role="tabpanel">
<div class="card border-0 shadow-sm">
<div class="card-header bg-white py-3 d-flex justify-content-between align-items-center">
<h6 class="mb-0">Interaction History</h6>
<button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#addInteractionModal">
<i class="bi bi-plus-lg me-1"></i>New Interaction
</button>
</div>
<div class="table-responsive">
<table class="table align-middle mb-0">
<thead class="bg-light">
<tr>
<th class="ps-4">Date</th>
<th>Type</th>
<th>Volunteer</th>
<th>Description</th>
<th>Notes</th>
{% if can_edit_voter %}
<th class="pe-4 text-end">Actions</th>
{% endif %}
</tr>
</thead>
<tbody>
{% for interaction in interactions %}
<tr>
<td class="ps-4 text-nowrap">{{ interaction.date|date:"M d, Y H:i" }}</td>
<td><span class="badge bg-light text-dark border">{{ interaction.type.name }}</span></td>
<td>{% if interaction.volunteer %}{{ interaction.volunteer }}{% else %}<span class="text-muted small">-</span>{% endif %}</td>
<td>{{ interaction.description }}</td>
<td class="small text-muted">{{ interaction.notes|truncatechars:30 }}</td>
{% if can_edit_voter %}
<td class="pe-4 text-end">
<button class="btn btn-sm btn-link text-primary p-0 me-2" data-bs-toggle="modal" data-bs-target="#editInteractionModal{{ interaction.id }}">
<i class="bi bi-pencil"></i>
</button>
<button class="btn btn-sm btn-link text-danger p-0" data-bs-toggle="modal" data-bs-target="#deleteInteractionModal{{ interaction.id }}">
<i class="bi bi-trash"></i>
</button>
</td>
{% endif %}
</tr>
{% empty %}
<tr><td colspan="6" class="text-center py-4 text-muted">No interactions recorded.</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<!-- Events Tab -->
<div class="tab-pane fade" id="events" role="tabpanel">
<div class="card border-0 shadow-sm">
<div class="card-header bg-white py-3 d-flex justify-content-between align-items-center">
<h6 class="mb-0">Event Participation</h6>
<button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#addEventParticipationModal">
<i class="bi bi-plus-lg me-1"></i>Add Participation
</button>
</div>
<div class="table-responsive">
<table class="table align-middle mb-0">
<thead class="bg-light">
<tr>
<th class="ps-4">Date</th>
<th>Event Name</th>
<th>Event Type</th>
<th>Status</th>
<th>Description</th>
{% if can_edit_voter %}
<th class="pe-4 text-end">Actions</th>
{% endif %}
</tr>
</thead>
<tbody>
{% for participation in event_participations %}
<tr>
<td class="ps-4 text-nowrap">{{ participation.event.date|date:"M d, Y" }}</td>
<td><strong>{{ participation.event.name|default:"(No Name)" }}</strong></td>
<td><span class="badge bg-light text-dark border">{{ participation.event.event_type.name }}</span></td>
<td>
{% if participation.participation_status.name|lower == 'attended' %}
<span class="badge bg-success-subtle text-success border border-success-subtle">Attended</span>
{% elif participation.participation_status.name|lower == "invited but didn't attend" or participation.participation_status.name|lower == "invited but didn't attend" %}
<span class="badge bg-danger-subtle text-danger border border-danger-subtle">Did Not Attend</span>
{% else %}
<span class="badge bg-info-subtle text-info border border-info-subtle">{{ participation.participation_status.name }}</span>
{% endif %}
</td>
<td class="small text-muted">{{ participation.event.description|truncatechars:60 }}</td>
{% if can_edit_voter %}
<td class="pe-4 text-end">
<button class="btn btn-sm btn-link text-primary p-0 me-2" data-bs-toggle="modal" data-bs-target="#editEventParticipationModal{{ participation.id }}">
<i class="bi bi-pencil"></i>
</button>
<button class="btn btn-sm btn-link text-danger p-0" data-bs-toggle="modal" data-bs-target="#deleteEventParticipationModal{{ participation.id }}">
<i class="bi bi-trash"></i>
</button>
</td>
{% endif %}
</tr>
{% empty %}
<tr><td colspan="6" class="text-center py-4 text-muted">No event participations found.</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
<!-- Donations Tab -->
{% if can_view_donations %}
<div class="tab-pane fade" id="donations" role="tabpanel">
<div class="card border-0 shadow-sm">
<div class="card-header bg-white py-3 d-flex justify-content-between align-items-center">
<h6 class="mb-0">Donation History</h6>
<button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#addDonationModal">
<i class="bi bi-plus-lg me-1"></i>Add Donation
</button>
</div>
<div class="table-responsive">
<table class="table align-middle mb-0">
<thead class="bg-light">
<tr>
<th class="ps-4">Date</th>
<th>Method</th>
<th class="text-end">Amount</th>
<th class="pe-4 text-end">Actions</th>
</tr>
</thead>
<tbody>
{% for donation in donations %}
<tr>
<td class="ps-4 text-nowrap">{{ donation.date|date:"M d, Y" }}</td>
<td>{{ donation.method.name }}</td>
<td class="text-end fw-semibold text-success">${{ donation.amount }}</td>
<td class="pe-4 text-end">
<button class="btn btn-sm btn-link text-primary p-0 me-2" data-bs-toggle="modal" data-bs-target="#editDonationModal{{ donation.id }}">
<i class="bi bi-pencil"></i>
</button>
<button class="btn btn-sm btn-link text-danger p-0" data-bs-toggle="modal" data-bs-target="#deleteDonationModal{{ donation.id }}">
<i class="bi bi-trash"></i>
</button>
</td>
</tr>
{% empty %}
<tr><td colspan="4" class="text-center py-4 text-muted">No donations recorded.</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endif %}
<!-- Voting History Tab -->
<div class="tab-pane fade" id="voting" role="tabpanel">
<div class="card border-0 shadow-sm">
<div class="table-responsive">
<table class="table align-middle mb-0">
<thead class="bg-light">
<tr>
<th class="ps-4">Election Date</th>
<th>Description</th>
<th class="pe-4">Party</th>
</tr>
</thead>
<tbody>
{% for record in voting_records %}
<tr>
<td class="ps-4 text-nowrap">{{ record.election_date|date:"M d, Y" }}</td>
<td>{{ record.election_description }}</td>
<td class="pe-4">{{ record.primary_party|default:"-" }}</td>
</tr>
{% empty %}
<tr><td colspan="3" class="text-center py-4 text-muted">No voting records found.</td></tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% if can_edit_voter %}
<!-- Edit Voter Modal -->
<div class="modal fade" id="editVoterModal" tabindex="-1" aria-labelledby="editVoterModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content border-0">
<div class="modal-header border-0 bg-light">
<h5 class="modal-title" id="editVoterModalLabel">Edit Voter Profile</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form id="editVoterForm" action="{% url 'voter_edit' voter.id %}" method="POST">
{% csrf_token %}
<div class="modal-body p-4">
<div class="row">
<div class="col-md-4 mb-3">
<label class="form-label fw-medium">{{ voter_form.first_name.label }}</label>
{{ voter_form.first_name }}
</div>
<div class="col-md-4 mb-3">
<label class="form-label fw-medium">{{ voter_form.last_name.label }}</label>
{{ voter_form.last_name }}
</div>
<div class="col-md-4 mb-3">
<label class="form-label fw-medium">{{ voter_form.nickname.label }}</label>
{{ voter_form.nickname }}
</div>
<div class="col-md-12 mb-3">
<label class="form-label fw-medium">Address Street</label>
{{ voter_form.address_street }}
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-medium">City</label>
{{ voter_form.city }}
</div>
<div class="col-md-2 mb-3">
<label class="form-label fw-medium">State</label>
{{ voter_form.state }}
</div>
<div class="col-md-2 mb-3">
<label class="form-label fw-medium">Prior State</label>
{{ voter_form.prior_state }}
</div>
<div class="col-md-2 mb-3">
<label class="form-label fw-medium">Zip Code</label>
{{ voter_form.zip_code }}
</div>
<div class="col-12 mb-4">
<button type="button" id="manualGeocodeBtn" class="btn btn-outline-primary w-100 fw-bold py-2 shadow-sm">
<i class="bi bi-geo-alt-fill me-2"></i>Manual Geocode Address
</button>
<div id="geocodeStatus" class="small mt-2 text-center" style="display: none;"></div>
</div>
<div class="col-md-12 mb-3">
<label class="form-label fw-medium">County</label>
{{ voter_form.county }}
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-medium">Latitude</label>
{{ voter_form.latitude }}
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-medium">Longitude</label>
{{ voter_form.longitude }}
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-medium">{{ voter_form.phone.label }}</label>
{{ voter_form.phone }}
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-medium">{{ voter_form.phone_type.label }}</label>
{{ voter_form.phone_type }}
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-medium">{{ voter_form.secondary_phone.label }}</label>
{{ voter_form.secondary_phone }}
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-medium">{{ voter_form.secondary_phone_type.label }}</label>
{{ voter_form.secondary_phone_type }}
</div>
<div class="col-md-12 mb-3">
<label class="form-label fw-medium">{{ voter_form.email.label }}</label>
{{ voter_form.email }}
</div>
<div class="col-md-4 mb-3">
<label class="form-label fw-medium">{{ voter_form.voter_id.label }}</label>
{{ voter_form.voter_id }}
</div>
<div class="col-md-4 mb-3">
<label class="form-label fw-medium">{{ voter_form.neighborhood.label }}</label>
{{ voter_form.neighborhood }}
</div>
<div class="col-md-4 mb-3">
<label class="form-label fw-medium">{{ voter_form.district.label }}</label>
{{ voter_form.district }}
</div>
<div class="col-md-4 mb-3">
<label class="form-label fw-medium">{{ voter_form.precinct.label }}</label>
{{ voter_form.precinct }}
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-medium">{{ voter_form.birthdate.label }}</label>
{{ voter_form.birthdate }}
</div>
<div class="col-md-6 mb-3">
<label class="form-label fw-medium">{{ voter_form.registration_date.label }}</label>
{{ voter_form.registration_date }}
</div>
<div class="col-md-12 mb-3 d-flex align-items-center">
<div class="form-check">
{{ voter_form.is_targeted }}
<label class="form-check-label fw-medium" for="{{ voter_form.is_targeted.id_for_label }}">
Targeted Voter
</label>
</div>
</div>
<div class="col-md-4 mb-3">
<label class="form-label fw-medium">{{ voter_form.candidate_support.label }}</label>
{{ voter_form.candidate_support }}
</div>
<div class="col-md-4 mb-3">
<label class="form-label fw-medium">{{ voter_form.yard_sign.label }}</label>
{{ voter_form.yard_sign }}
</div>
<div class="col-md-4 mb-3">
<label class="form-label fw-medium">{{ voter_form.window_sticker.label }}</label>
{{ voter_form.window_sticker }}
</div>
<div class="col-md-12 mb-3">
<label class="form-label fw-medium">Notes</label>
{{ voter_form.notes }}
</div>
</div>
</div>
<div class="modal-footer border-0 p-4 pt-0 justify-content-between">
<button type="button" class="btn btn-outline-danger" data-bs-toggle="modal" data-bs-target="#deleteVoterModal">
<i class="bi bi-trash me-1"></i>Delete Profile
</button>
<div>
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary px-4">Save Changes</button>
</div>
</div>
</form>
</div>
</div>
</div>
<!-- Delete Voter Confirmation Modal -->
<div class="modal fade" id="deleteVoterModal" tabindex="-1" aria-labelledby="deleteVoterModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content border-0">
<div class="modal-header border-0 bg-danger text-white">
<h5 class="modal-title" id="deleteVoterModalLabel">Delete Voter Profile</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body p-4 text-center">
<i class="bi bi-exclamation-triangle text-danger mb-3" style="font-size: 3rem;"></i>
<h4 class="mb-3">Are you sure?</h4>
<p class="text-muted">This action will permanently delete <strong>{% if voter.nickname %}{{ voter.nickname }}{% else %}{{ voter.first_name }}{% endif %} {{ voter.last_name }}</strong> and all associated data. This cannot be undone.</p>
</div>
<div class="modal-footer border-0 p-4 pt-0 justify-content-center">
<button type="button" class="btn btn-light px-4" data-bs-dismiss="modal">No, Keep Profile</button>
<form action="{% url 'voter_delete' voter.id %}" method="POST" class="ms-2">
{% csrf_token %}
<button type="submit" class="btn btn-danger px-4">Yes, Delete Permanently</button>
</form>
</div>
</div>
</div>
</div>
{% endif %}
<!-- Add Interaction Modal -->
<div class="modal fade" id="addInteractionModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content border-0">
<div class="modal-header border-0 bg-light">
<h5 class="modal-title">New Interaction</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form action="{% url 'add_interaction' voter.id %}" method="POST">
{% csrf_token %}
<div class="modal-body p-4">
<div class="mb-3">
<label class="form-label fw-medium">{{ interaction_form.type.label }}</label>
{{ interaction_form.type }}
</div>
<div class="mb-3">
<label class="form-label fw-medium">{{ interaction_form.volunteer.label }}</label>
{{ interaction_form.volunteer }}
</div>
<div class="mb-3">
<label class="form-label fw-medium">{{ interaction_form.date.label }}</label>
{{ interaction_form.date }}
</div>
<div class="mb-3">
<label class="form-label fw-medium">{{ interaction_form.description.label }}</label>
{{ interaction_form.description }}
</div>
<div class="mb-0">
<label class="form-label fw-medium">{{ interaction_form.notes.label }}</label>
{{ interaction_form.notes }}
</div>
</div>
<div class="modal-footer border-0 p-4 pt-0">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary px-4">Log Interaction</button>
</div>
</form>
</div>
</div>
</div>
{% if can_edit_voter %}
<!-- Edit Interaction Modals -->
{% for interaction in interactions %}
<div class="modal fade" id="editInteractionModal{{ interaction.id }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content border-0">
<div class="modal-header border-0 bg-light">
<h5 class="modal-title">Edit Interaction</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form action="{% url 'edit_interaction' interaction.id %}" method="POST">
{% csrf_token %}
<div class="modal-body p-4">
<div class="mb-3">
<label class="form-label fw-medium">Type</label>
<select name="type" class="form-select">
{% for type in interaction_form.fields.type.queryset %}
<option value="{{ type.id }}" {% if type.id == interaction.type.id %}selected{% endif %}>{{ type.name }}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label class="form-label fw-medium">Volunteer</label>
<select name="volunteer" class="form-select">
<option value="">---------</option>
{% for vol in interaction_form.fields.volunteer.queryset %}
<option value="{{ vol.id }}" {% if vol.id == interaction.volunteer.id %}selected{% endif %}>{{ vol }}</option>
{% endfor %}
</select>
</div>
<div class="mb-3">
<label class="form-label fw-medium">Date</label>
<input type="datetime-local" name="date" class="form-control" value="{{ interaction.date|date:'Y-m-d\TH:i' }}">
</div>
<div class="mb-3">
<label class="form-label fw-medium">Description</label>
<input type="text" name="description" class="form-control" value="{{ interaction.description }}">
</div>
<div class="mb-0">
<label class="form-label fw-medium">Notes</label>
<textarea name="notes" class="form-control" rows="2">{{ interaction.notes }}</textarea>
</div>
</div>
<div class="modal-footer border-0 p-4 pt-0 justify-content-between">
<button type="button" class="btn btn-outline-danger" data-bs-toggle="modal" data-bs-target="#deleteVoterModal">
<i class="bi bi-trash me-1"></i>Delete Profile
</button>
<div>
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary px-4">Save Changes</button>
</div>
</div>
</form>
</div>
</div>
</div>
<!-- Delete Interaction Modal -->
<div class="modal fade" id="deleteInteractionModal{{ interaction.id }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content border-0">
<div class="modal-header border-0 bg-light">
<h5 class="modal-title">Delete Interaction?</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body p-4 text-center">
<p class="text-muted mb-0">Are you sure you want to delete this interaction?</p>
</div>
<div class="modal-footer border-0 p-4 pt-0 justify-content-center">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">No, Keep</button>
<form action="{% url 'delete_interaction' interaction.id %}" method="POST">
{% csrf_token %}
<button type="submit" class="btn btn-danger">Yes, Delete</button>
</form>
</div>
</div>
</div>
</div>
{% endfor %}
{% endif %}
{% if can_view_donations %}
<!-- Add Donation Modal -->
<div class="modal fade" id="addDonationModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content border-0">
<div class="modal-header border-0 bg-light">
<h5 class="modal-title">Add Donation</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form action="{% url 'add_donation' voter.id %}" method="POST">
{% csrf_token %}
<div class="modal-body p-4">
<div class="mb-3">
<label class="form-label fw-medium">{{ donation_form.amount.label }}</label>
<div class="input-group">
<span class="input-group-text">$</span>
{{ donation_form.amount }}
</div>
</div>
<div class="mb-3">
<label class="form-label fw-medium">{{ donation_form.date.label }}</label>
{{ donation_form.date }}
</div>
<div class="mb-0">
<label class="form-label fw-medium">{{ donation_form.method.label }}</label>
{{ donation_form.method }}
</div>
</div>
<div class="modal-footer border-0 p-4 pt-0">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary px-4">Record Donation</button>
</div>
</form>
</div>
</div>
</div>
<!-- Edit Donation Modals -->
{% for donation in donations %}
<div class="modal fade" id="editDonationModal{{ donation.id }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content border-0">
<div class="modal-header border-0 bg-light">
<h5 class="modal-title">Edit Donation</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form action="{% url 'edit_donation' donation.id %}" method="POST">
{% csrf_token %}
<div class="modal-body p-4">
<div class="mb-3">
<label class="form-label fw-medium">Amount</label>
<div class="input-group">
<span class="input-group-text">$</span>
<input type="number" name="amount" class="form-control" step="0.01" value="{{ donation.amount }}">
</div>
</div>
<div class="mb-3">
<label class="form-label fw-medium">Date</label>
<input type="date" name="date" class="form-control" value="{{ donation.date|date:'Y-m-d' }}">
</div>
<div class="mb-0">
<label class="form-label fw-medium">Method</label>
<select name="method" class="form-select">
{% for method in donation_form.fields.method.queryset %}
<option value="{{ method.id }}" {% if method.id == donation.method.id %}selected{% endif %}>{{ method.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="modal-footer border-0 p-4 pt-0 justify-content-between">
<button type="button" class="btn btn-outline-danger" data-bs-toggle="modal" data-bs-target="#deleteVoterModal">
<i class="bi bi-trash me-1"></i>Delete Profile
</button>
<div>
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary px-4">Save Changes</button>
</div>
</div>
</form>
</div>
</div>
</div>
<!-- Delete Donation Modal -->
<div class="modal fade" id="deleteDonationModal{{ donation.id }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content border-0">
<div class="modal-header border-0 bg-light">
<h5 class="modal-title">Delete Donation?</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body p-4 text-center">
<p class="text-muted mb-0">Are you sure you want to delete this donation record?</p>
</div>
<div class="modal-footer border-0 p-4 pt-0 justify-content-center">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">No, Keep</button>
<form action="{% url 'delete_donation' donation.id %}" method="POST">
{% csrf_token %}
<button type="submit" class="btn btn-danger">Yes, Delete</button>
</form>
</div>
</div>
</div>
</div>
{% endfor %}
{% endif %}
<!-- Add Likelihood Modal -->
<div class="modal fade" id="addLikelihoodModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content border-0">
<div class="modal-header border-0 bg-light">
<h5 class="modal-title">Update Voter Likelihood</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form action="{% url 'add_likelihood' voter.id %}" method="POST">
{% csrf_token %}
<div class="modal-body p-4">
<div class="mb-3">
<label class="form-label fw-medium">{{ likelihood_form.election_type.label }}</label>
{{ likelihood_form.election_type }}
</div>
<div class="mb-0">
<label class="form-label fw-medium">{{ likelihood_form.likelihood.label }}</label>
{{ likelihood_form.likelihood }}
</div>
</div>
<div class="modal-footer border-0 p-4 pt-0">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary px-4">Update</button>
</div>
</form>
</div>
</div>
</div>
{% if can_edit_voter %}
<!-- Edit Likelihood Modals -->
{% for likelihood in likelihoods %}
<div class="modal fade" id="editLikelihoodModal{{ likelihood.id }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content border-0">
<div class="modal-header border-0 bg-light">
<h5 class="modal-title">Edit Likelihood</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form action="{% url 'edit_likelihood' likelihood.id %}" method="POST">
{% csrf_token %}
<div class="modal-body p-4">
<div class="mb-3">
<label class="form-label fw-medium">Election Type</label>
<select name="election_type" class="form-select">
{% for et in likelihood_form.fields.election_type.queryset %}
<option value="{{ et.id }}" {% if et.id == likelihood.election_type.id %}selected{% endif %}>{{ et.name }}</option>
{% endfor %}
</select>
</div>
<div class="mb-0">
<label class="form-label fw-medium">Likelihood</label>
<select name="likelihood" class="form-select">
<option value="not_likely" {% if likelihood.likelihood == 'not_likely' %}selected{% endif %}>Not Likely</option>
<option value="somewhat_likely" {% if likelihood.likelihood == 'somewhat_likely' %}selected{% endif %}>Somewhat Likely</option>
<option value="very_likely" {% if likelihood.likelihood == 'very_likely' %}selected{% endif %}>Very Likely</option>
</select>
</div>
</div>
<div class="modal-footer border-0 p-4 pt-0 justify-content-between">
<button type="button" class="btn btn-outline-danger" data-bs-toggle="modal" data-bs-target="#deleteVoterModal">
<i class="bi bi-trash me-1"></i>Delete Profile
</button>
<div>
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary px-4">Save Changes</button>
</div>
</div>
</form>
</div>
</div>
</div>
<!-- Delete Likelihood Modal -->
<div class="modal fade" id="deleteLikelihoodModal{{ likelihood.id }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content border-0">
<div class="modal-header border-0 bg-light">
<h5 class="modal-title">Delete Likelihood?</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body p-4 text-center">
<p class="text-muted mb-0">Delete likelihood for <strong>{{ likelihood.election_type.name }}</strong>?</p>
</div>
<div class="modal-footer border-0 p-4 pt-0 justify-content-center">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">No</button>
<form action="{% url 'delete_likelihood' likelihood.id %}" method="POST">
{% csrf_token %}
<button type="submit" class="btn btn-danger">Yes, Delete</button>
</form>
</div>
</div>
</div>
</div>
{% endfor %}
{% endif %}
<!-- Add Event Participation Modal -->
<div class="modal fade" id="addEventParticipationModal" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content border-0">
<div class="modal-header border-0 bg-light">
<h5 class="modal-title">Add Event Participation</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form action="{% url 'add_event_participation' voter.id %}" method="POST">
{% csrf_token %}
<div class="modal-body p-4">
<div class="mb-3">
<label class="form-label fw-medium">Select Event</label>
{{ event_participation_form.event }}
</div>
<div class="mb-0">
<label class="form-label fw-medium">Participation Status</label>
{{ event_participation_form.participation_status }}
</div>
</div>
<div class="modal-footer border-0 p-4 pt-0">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary px-4">Add Participation</button>
</div>
</form>
</div>
</div>
</div>
{% if can_edit_voter %}
<!-- Edit Event Participation Modals -->
{% for participation in event_participations %}
<div class="modal fade" id="editEventParticipationModal{{ participation.id }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content border-0">
<div class="modal-header border-0 bg-light">
<h5 class="modal-title">Edit Participation</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<form action="{% url 'edit_event_participation' participation.id %}" method="POST">
{% csrf_token %}
<div class="modal-body p-4">
<div class="mb-3">
<label class="form-label fw-medium">Event</label>
<select name="event" class="form-select">
{% for ev in event_participation_form.fields.event.queryset %}
<option value="{{ ev.id }}" {% if ev.id == participation.event.id %}selected{% endif %}>{{ ev.event_type.name }} on {{ ev.date }}</option>
{% endfor %}
</select>
</div>
<div class="mb-0">
<label class="form-label fw-medium">Participation Status</label>
<select name="participation_status" class="form-select">
{% for status in event_participation_form.fields.participation_status.queryset %}
<option value="{{ status.id }}" {% if status.id == participation.participation_status.id %}selected{% endif %}>{{ status.name }}</option>
{% endfor %}
</select>
</div>
</div>
<div class="modal-footer border-0 p-4 pt-0 justify-content-between">
<button type="button" class="btn btn-outline-danger" data-bs-toggle="modal" data-bs-target="#deleteVoterModal">
<i class="bi bi-trash me-1"></i>Delete Profile
</button>
<div>
<button type="button" class="btn btn-light" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-primary px-4">Save Changes</button>
</div>
</div>
</form>
</div>
</div>
</div>
<!-- Delete Event Participation Modal -->
<div class="modal fade" id="deleteEventParticipationModal{{ participation.id }}" tabindex="-1" aria-hidden="true">
<div class="modal-dialog modal-sm">
<div class="modal-content border-0">
<div class="modal-header border-0 bg-light">
<h5 class="modal-title">Remove Participation?</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body p-4 text-center">
<p class="text-muted mb-0">Are you sure you want to remove this event participation?</p>
</div>
<div class="modal-footer border-0 p-4 pt-0 justify-content-center">
<button type="button" class="btn btn-light" data-bs-dismiss="modal">No</button>
<form action="{% url 'delete_event_participation' participation.id %}" method="POST">
{% csrf_token %}
<button type="submit" class="btn btn-danger">Yes, Remove</button>
</form>
</div>
</div>
</div>
</div>
{% endfor %}
{% endif %}
<style>
.nav-tabs .nav-link {
color: #6c757d;
font-weight: 500;
border-radius: 0;
}
.nav-tabs .nav-link.active {
color: #059669;
background: transparent;
border-bottom: 2px solid #059669 !important;
}
.last-child-mb-0:last-child {
margin-bottom: 0 !important;
}
.modal-content {
border-radius: 1rem;
overflow: hidden;
}
.form-control, .form-select {
border-color: #e5e7eb;
padding: 0.6rem 0.8rem;
}
.form-control:focus, .form-select:focus {
border-color: #059669;
box-shadow: 0 0 0 0.25rem rgba(5, 150, 105, 0.1);
}
.bg-success-subtle { background-color: #d1fae5; }
.bg-danger-subtle { background-color: #fee2e2; }
.bg-info-subtle { background-color: #e0f2fe; }
</style>
<script>
document.addEventListener('DOMContentLoaded', function() {
const urlParams = new URLSearchParams(window.location.search);
const activeTab = urlParams.get('active_tab');
if (activeTab) {
const tabButton = document.getElementById(activeTab + '-tab');
if (tabButton) {
const tab = new bootstrap.Tab(tabButton);
tab.show();
}
}
{% if voter.latitude and voter.longitude %}
// Initialize Map
try {
const position = { lat: {{ voter.latitude }}, lng: {{ voter.longitude }} };
const map = new google.maps.Map(document.getElementById("voterMap"), {
zoom: 15,
center: position,
});
const marker = new google.maps.Marker({
position: position,
map: map,
title: "{% if voter.nickname %}{{ voter.nickname }}{% else %}{{ voter.first_name }}{% endif %} {{ voter.last_name }}",
});
const infowindow = new google.maps.InfoWindow({
content: "<strong>{% if voter.nickname %}{{ voter.nickname }}{% else %}{{ voter.first_name }}{% endif %} {{ voter.last_name }}</strong><br>{{ voter.address_street }}",
});
marker.addListener("click", () => {
infowindow.open(map, marker);
});
infowindow.open(map, marker);
} catch (e) {
console.error("Google Maps initialization failed:", e);
}
{% endif %}
// Manual Geocode Logic
const geocodeBtn = document.getElementById('manualGeocodeBtn');
const statusDiv = document.getElementById('geocodeStatus');
if (geocodeBtn) {
geocodeBtn.addEventListener('click', function() {
const street = document.querySelector('[name="address_street"]').value;
const city = document.querySelector('[name="city"]').value;
const state = document.querySelector('[name="state"]').value;
const zip = document.querySelector('[name="zip_code"]').value;
if (!street && !city) {
statusDiv.innerHTML = '<span class="text-danger">Please enter at least a street or city.</span>';
statusDiv.style.display = 'block';
return;
}
geocodeBtn.disabled = true;
geocodeBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span>Geocoding...';
statusDiv.style.display = 'none';
const formData = new FormData();
formData.append('address_street', street);
formData.append('city', city);
formData.append('state', state);
formData.append('zip_code', zip);
formData.append('csrfmiddlewaretoken', '{{ csrf_token }}');
fetch('{% url "voter_geocode" voter.id %}', {
method: 'POST',
body: formData,
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
})
.then(response => response.json())
.then(data => {
if (data.success) {
document.querySelector('[name="latitude"]').value = data.latitude;
document.querySelector('[name="longitude"]').value = String(data.longitude).substring(0, 12);
statusDiv.innerHTML = '<span class="text-success fw-bold"><i class="bi bi-check-circle me-1"></i>Coordinates updated!</span>';
} else {
statusDiv.innerHTML = '<span class="text-danger">' + (data.error || 'Geocoding failed.') + '</span>';
}
})
.catch(error => {
console.error('Error:', error);
statusDiv.innerHTML = '<span class="text-danger">An error occurred during geocoding.</span>';
})
.finally(() => {
geocodeBtn.disabled = false;
geocodeBtn.innerHTML = '<i class="bi bi-geo-alt-fill me-2"></i>Manual Geocode Address';
statusDiv.style.display = 'block';
});
});
}
});
</script>
{% endblock %}