Flatlogic Bot c0bb59aeba RaktaPulse
2026-02-18 09:21:22 +00:00

477 lines
23 KiB
HTML

{% extends "base.html" %}
{% load static %}
{% load i18n %}
{% block title %}{% trans "RaktaPulse Dashboard" %} - {% trans "Lifeline of the Community" %}{% endblock %}
{% block head %}
<style>
.stat-card {
background: #ffffff;
border-radius: 20px;
padding: 24px;
border: 1px solid var(--border-color);
position: relative;
overflow: hidden;
box-shadow: 0 4px 15px rgba(0,0,0,0.03);
transition: all 0.3s ease;
text-decoration: none;
display: block;
color: inherit;
}
.stat-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(230, 57, 70, 0.1);
border-color: var(--pulse-red);
cursor: pointer;
}
.stat-card .icon-box {
width: 48px;
height: 48px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.5rem;
margin-bottom: 15px;
}
.stat-value {
font-size: 2rem;
font-weight: 700;
font-family: 'Outfit', sans-serif;
}
.stat-label {
color: var(--text-secondary);
font-size: 0.9rem;
font-weight: 500;
}
.blood-group-pill {
width: 40px;
height: 40px;
border-radius: 10px;
background: rgba(230, 57, 70, 0.1);
color: var(--pulse-red);
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
}
.donor-row {
background: #f8f9fa;
border-radius: 12px;
padding: 15px;
margin-bottom: 12px;
border: 1px solid #eee;
transition: all 0.3s;
}
.donor-row:hover {
border-color: var(--pulse-red);
background: var(--pulse-red-light);
}
.urgency-badge {
padding: 4px 10px;
border-radius: 6px;
font-size: 0.7rem;
font-weight: 700;
text-transform: uppercase;
}
.bg-critical { background: #FF4D4D; color: #fff; }
.bg-urgent { background: #FFA500; color: #000; }
.bg-normal { background: #4CAF50; color: #fff; }
.progress {
background-color: #2A2A2A;
border-radius: 10px;
}
.vaccination-card {
background: linear-gradient(135deg, #E63946 0%, #d62828 100%);
border-radius: 20px;
padding: 25px;
color: white !important;
}
.filter-btn {
background: #ffffff;
border: 1px solid var(--border-color);
color: var(--text-primary);
padding: 8px 16px;
border-radius: 10px;
font-size: 0.85rem;
}
.filter-btn.active {
background: var(--pulse-red);
border-color: var(--pulse-red);
}
</style>
{% endblock %}
{% block content %}
<div class="container-fluid p-0">
<!-- Welcome Header -->
<div class="row mb-4">
<div class="col-12">
<h2 class="brand-font mb-1">{% trans "RaktaPulse Community Dashboard" %}</h2>
<p class="text-secondary">{% trans "Overview of blood donation activity and requirements in your area." %}</p>
</div>
</div>
{% if involved_events %}
<!-- Active involvements / Message section -->
<div class="row mb-4">
<div class="col-12">
<div class="glass-card border-start border-primary border-4 bg-primary bg-opacity-10">
<h5 class="brand-font mb-3"><i class="bi bi-chat-dots-fill me-2"></i>{% trans "Action Required: Donations in Progress" %}</h5>
<div class="row g-3">
{% for event in involved_events %}
<div class="col-md-6 col-lg-4">
<div class="p-3 border rounded bg-white shadow-sm">
<p class="mb-2"><strong>{{ event.donor.name }}</strong> is helping <strong>{{ event.request.patient_name }}</strong></p>
<div class="d-flex justify-content-between">
<span class="small text-muted">{{ event.date|date:"M d, Y" }}</span>
<a href="{% url 'complete_donation' event.id %}" class="btn btn-sm btn-success px-3 rounded-pill">{% trans "Mark Completed" %}</a>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
{% endif %}
<!-- Quick Stats -->
<div class="row g-4 mb-5">
<div class="col-xl-3 col-md-6">
<a href="{% url 'donor_list' %}" class="stat-card">
<div class="icon-box bg-danger bg-opacity-10 text-danger">
<i class="bi bi-people"></i>
</div>
<div class="stat-value">{{ stats.total_donors }}</div>
<div class="stat-label">{% trans "Registered Donors" %}</div>
</a>
</div>
<div class="col-xl-3 col-md-6">
<a href="{% url 'blood_request_list' %}?status=Active" class="stat-card">
<div class="icon-box bg-warning bg-opacity-10 text-warning">
<i class="bi bi-activity"></i>
</div>
<div class="stat-value">{{ stats.active_requests }}</div>
<div class="stat-label">{% trans "Active Requests" %}</div>
</a>
</div>
<div class="col-xl-3 col-md-6">
<div class="stat-card">
<div class="icon-box bg-primary bg-opacity-10 text-primary">
<i class="bi bi-droplet"></i>
</div>
<div class="stat-value">{{ stats.total_stock }} <small class="fs-6 fw-normal">/ {{ stats.total_capacity }}</small></div>
<div class="stat-label">{% trans "Inventory vs Capacity" %}</div>
</div>
</div>
<div class="col-xl-3 col-md-6">
<div class="stat-card">
<div class="icon-box bg-success bg-opacity-10 text-success">
<i class="bi bi-shield-check"></i>
</div>
<div class="stat-value">{{ stats.vaccinated_percentage }}%</div>
<div class="stat-label">{% trans "Vaccinated Donors" %}</div>
</div>
</div>
</div>
<div class="row g-4">
<!-- Main Donor Search & Grid -->
<div class="col-lg-8">
<div class="glass-card mb-4">
<div class="d-flex justify-content-between align-items-center mb-4">
<h4 class="mb-0 brand-font">Available Donors</h4>
<div class="d-flex gap-2">
<button class="filter-btn active" id="btn-list">List View</button>
<button class="filter-btn" id="btn-map">Map View</button>
<button class="filter-btn" id="btn-nearest">Nearest First</button>
</div>
</div>
<div id="map-container" style="display: none; height: 400px; border-radius: 12px; margin-bottom: 20px; overflow: hidden; border: 1px solid var(--border-color);">
<div id="map" style="height: 100%;"></div>
</div>
<form method="GET" id="donorHomeFilterForm" class="row g-3 mb-4">
<div class="col-md-5">
<select name="blood_group" class="form-select bg-light border-secondary text-dark">
<option value="">All Blood Groups</option>
{% for group in blood_groups %}
<option value="{{ group }}" {% if request.GET.blood_group == group %}selected{% endif %}>{{ group }}</option>
{% endfor %}
</select>
</div>
<div class="col-md-5">
<input type="text" name="location" class="form-control bg-light border-secondary text-dark"
placeholder="Search location (e.g. Baluwatar)..." value="{{ request.GET.location }}">
</div>
<div class="col-md-2">
<button type="submit" class="btn btn-danger w-100 shadow-sm">Find</button>
</div>
<input type="hidden" name="lat" id="latInputHome" value="{{ request.GET.lat }}">
<input type="hidden" name="lng" id="lngInputHome" value="{{ request.GET.lng }}">
</form>
<div class="donor-list">
{% for donor in donors %}
<div class="donor-row d-flex align-items-center justify-content-between flex-wrap gap-3">
<div class="d-flex align-items-center gap-3">
<div class="position-relative">
{% if donor.user and donor.user.profile.profile_pic %}
<img src="{{ donor.user.profile.profile_pic.url }}" alt="{{ donor.name }}" class="rounded-circle" style="width: 45px; height: 45px; object-fit: cover;">
<span class="position-absolute bottom-0 end-0 badge rounded-pill bg-danger border border-white" style="font-size: 0.6rem; padding: 0.2rem 0.4rem;">
{{ donor.blood_group }}
</span>
{% else %}
<div class="blood-group-pill">{{ donor.blood_group }}</div>
{% endif %}
</div>
<div>
<h6 class="mb-0 fw-bold text-dark">
{{ donor.name }}
{% if donor.is_verified %}
<span class="ms-1" title="Verified Donor"><i class="bi bi-patch-check-fill text-success"></i></span>
{% endif %}
{% if donor.distance and donor.distance < 1000 %}
<span class="ms-2 badge bg-info bg-opacity-10 text-info" style="font-size: 0.65rem;">
{{ donor.distance|floatformat:1 }} km
</span>
{% endif %}
</h6>
<p class="mb-0 text-secondary small"><i class="bi bi-geo-alt me-1"></i> {{ donor.location }}, {{ donor.district }}</p>
</div>
</div>
<div class="d-flex align-items-center gap-4">
<div class="text-end d-none d-sm-block">
<span class="badge {% if donor.is_available %}bg-success{% else %}bg-secondary{% endif %} bg-opacity-10 text-{% if donor.is_available %}success{% else %}secondary{% endif %} mb-1">
{% if donor.is_available %}Available{% else %}Unavailable{% endif %}
</span>
<p class="mb-0 text-muted extra-small" style="font-size: 0.7rem;">Last Donated: {{ donor.last_donation_date|default:"Never" }}</p>
</div>
<div class="d-flex gap-2">
{% if donor.user %}
<a href="{% url 'chat' donor.user.username %}" class="btn btn-outline-danger btn-sm rounded-pill" title="Message">
<i class="bi bi-chat-dots-fill"></i>
</a>
{% endif %}
<a href="tel:{{ donor.phone }}" class="btn btn-danger btn-sm px-3 rounded-pill">Call</a>
</div>
</div>
</div>
{% empty %}
<div class="text-center py-5">
<i class="bi bi-search fs-1 text-secondary opacity-25"></i>
<p class="text-secondary mt-3">No donors match your search criteria.</p>
</div>
{% endfor %}
</div>
</div>
<!-- Vaccination Monitoring Section -->
<div class="vaccination-card">
<div class="row align-items-center">
<div class="col-md-7">
<h4 class="brand-font text-white mb-2">Vaccination & Eligibility</h4>
<p class="text-secondary small">We prioritize donors who are fully vaccinated to ensure maximum safety for recipients.</p>
<div class="mb-4">
<div class="d-flex justify-content-between small text-white mb-2">
<span>Community Immunity Level</span>
<span>{{ stats.vaccinated_percentage }}%</span>
</div>
<div class="progress" style="height: 10px;">
<div class="progress-bar bg-success" style="width: {{ stats.vaccinated_percentage }}%"></div>
</div>
</div>
<a href="{% url 'vaccination_dashboard' %}" class="btn btn-success btn-sm px-4">Update Status</a>
</div>
<div class="col-md-5 d-none d-md-block text-center">
<i class="bi bi-shield-plus text-success" style="font-size: 5rem; opacity: 0.2;"></i>
</div>
</div>
</div>
</div>
<!-- Sidebar Components -->
<div class="col-lg-4">
<!-- Urgent Requests -->
<div class="glass-card mb-4 border-start border-danger border-4">
<h5 class="brand-font mb-4 d-flex justify-content-between align-items-center">
Urgent Requests
<span class="badge bg-danger rounded-pill px-2" style="font-size: 0.6rem;">HOT</span>
</h5>
<div class="request-feed">
{% for req in blood_requests %}
<div class="mb-4 border-bottom border-light pb-3">
<div class="d-flex justify-content-between align-items-center mb-2">
<span class="urgency-badge bg-{{ req.urgency|lower }}">{{ req.urgency }}</span>
<span class="fw-bold text-danger">{{ req.blood_group }}</span>
</div>
<h6 class="mb-1 text-dark">{{ req.patient_name }}</h6>
<p class="text-secondary extra-small mb-2"><i class="bi bi-hospital me-1"></i> {{ req.hospital }}</p>
<div class="d-flex justify-content-between align-items-center">
<small class="text-muted" style="font-size: 0.7rem;">{{ req.created_at|timesince }} ago</small>
<div class="d-flex gap-2 align-items-center">
{% if req.user %}
<a href="{% url 'chat' req.user.username %}" class="text-secondary" title="Message">
<i class="bi bi-chat-dots-fill"></i>
</a>
{% endif %}
<a href="tel:{{ req.contact_number }}" class="btn btn-link text-danger p-0 text-decoration-none small">Help Now →</a>
</div>
</div>
</div>
{% empty %}
<p class="text-secondary text-center">No active requests found.</p>
{% endfor %}
</div>
</div>
<!-- Blood Bank Inventory -->
<div class="glass-card">
<h5 class="brand-font mb-4">Blood Bank Inventory</h5>
<div class="inventory-list">
{% for bank in blood_banks %}
<div class="mb-3">
<div class="d-flex justify-content-between mb-1">
<span class="small fw-bold text-dark">{{ bank.name }}</span>
<span class="small text-secondary">24/7 Available</span>
</div>
<div class="d-flex gap-1 flex-wrap">
<span class="badge bg-dark border border-secondary text-secondary extra-small" style="font-size: 0.65rem;">A+ : {{ bank.stock_a_plus }}</span>
<span class="badge bg-dark border border-secondary text-secondary extra-small" style="font-size: 0.65rem;">B+ : {{ bank.stock_b_plus }}</span>
<span class="badge bg-dark border border-secondary text-secondary extra-small" style="font-size: 0.65rem;">O+ : {{ bank.stock_o_plus }}</span>
<span class="badge bg-dark border border-secondary text-secondary extra-small" style="font-size: 0.65rem;">AB+ : {{ bank.stock_ab_plus }}</span>
</div>
</div>
{% empty %}
<p class="text-secondary text-center">No blood banks registered.</p>
{% endfor %}
</div>
<a href="/admin/core/bloodbank/" class="btn btn-outline-secondary w-100 btn-sm mt-3">Manage Banks</a>
</div>
<!-- Extra Feature: Community Tip -->
<div class="glass-card mt-4 bg-gradient" style="background: linear-gradient(135deg, #1e1e1e 0%, #2d1b1b 100%);">
<h6 class="text-danger mb-2"><i class="bi bi-lightbulb me-2"></i>Lifesaver Tip</h6>
<p class="small text-secondary mb-0">Donating once can save up to three lives. Make sure to stay hydrated and rest before your appointment!</p>
</div>
</div>
</div>
<!-- Blood & Vaccine Facts Section -->
<div class="row g-4 mt-2 mb-5">
<div class="col-12">
<div class="glass-card">
<h4 class="brand-font mb-4"><i class="bi bi-info-circle text-danger me-2"></i>Blood & Vaccine Facts</h4>
<div class="row g-4">
<div class="col-md-3">
<div class="p-3 border border-light rounded-4 h-100 bg-light bg-opacity-10">
<h6 class="fw-bold text-danger"><i class="bi bi-droplet-fill me-2"></i>Lifesaver</h6>
<p class="small text-secondary mb-0">A single blood donation can save up to three lives. Your contribution has a massive impact on the community.</p>
</div>
</div>
<div class="col-md-3">
<div class="p-3 border border-light rounded-4 h-100 bg-light bg-opacity-10">
<h6 class="fw-bold text-danger"><i class="bi bi-shield-check me-2"></i>Vaccines & Donation</h6>
<p class="small text-secondary mb-0">Most vaccines (like COVID-19 or Flu) don't stop you from donating if you feel well. Stay protected and keep saving lives!</p>
</div>
</div>
<div class="col-md-3">
<div class="p-3 border border-light rounded-4 h-100 bg-light bg-opacity-10">
<h6 class="fw-bold text-danger"><i class="bi bi-award me-2"></i>Universal Type</h6>
<p class="small text-secondary mb-0">O-Negative is the universal donor type. It's vital for emergency rooms and trauma centers during critical hours.</p>
</div>
</div>
<div class="col-md-3">
<div class="p-3 border border-light rounded-4 h-100 bg-light bg-opacity-10">
<h6 class="fw-bold text-danger"><i class="bi bi-lightning-charge me-2"></i>Quick Recovery</h6>
<p class="small text-secondary mb-0">Your body replaces blood plasma within 24-48 hours. Just stay hydrated and have a light snack after your donation!</p>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% endblock %}
{% block scripts %}
<script>
const btnList = document.getElementById('btn-list');
const btnMap = document.getElementById('btn-map');
const mapContainer = document.getElementById('map-container');
const donorList = document.querySelector('.donor-list');
let map;
btnMap.addEventListener('click', () => {
btnMap.classList.add('active');
btnList.classList.remove('active');
mapContainer.style.display = 'block';
donorList.style.display = 'none';
if (!map) {
initMap();
} else {
// Need to invalidate size if it was hidden when initialized
setTimeout(() => { map.invalidateSize(); }, 200);
}
});
btnList.addEventListener('click', () => {
btnList.classList.add('active');
btnMap.classList.remove('active');
mapContainer.style.display = 'none';
donorList.style.display = 'block';
});
const btnNearest = document.getElementById('btn-nearest');
btnNearest.addEventListener('click', () => {
const btn = btnNearest;
const originalContent = btn.innerHTML;
btn.disabled = true;
btn.innerHTML = '<span class="spinner-border spinner-border-sm"></span>';
if ("geolocation" in navigator) {
navigator.geolocation.getCurrentPosition(function(position) {
document.getElementById('latInputHome').value = position.coords.latitude;
document.getElementById('lngInputHome').value = position.coords.longitude;
document.getElementById('donorHomeFilterForm').submit();
}, function(error) {
alert("Error getting location: " + error.message);
btn.disabled = false;
btn.innerHTML = originalContent;
});
} else {
alert("Geolocation is not supported by this browser.");
btn.disabled = false;
btn.innerHTML = originalContent;
}
});
function initMap() {
map = L.map('map').setView([27.7226, 85.3312], 14); // Centered on Baluwatar, Kathmandu
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors'
}).addTo(map);
// Add dummy markers for donors
{% for donor in donors %}
// Randomly offset from center for demo
L.marker([27.7226 + (Math.random() - 0.5) * 0.05, 85.3312 + (Math.random() - 0.5) * 0.05])
.addTo(map)
.bindPopup("<b>{{ donor.name }}</b><br>{{ donor.blood_group }} - {{ donor.location }}");
{% endfor %}
}
</script>
{% endblock %}