Raktapulse
This commit is contained in:
parent
1c8a1cd56f
commit
fb3e799193
Binary file not shown.
@ -11,10 +11,30 @@
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
.bg-critical { background: #FF4D4D; color: #fff; }
|
||||
.bg-critical { background: #FF4D4D; color: #fff; box-shadow: 0 0 10px rgba(255, 77, 77, 0.4); }
|
||||
.bg-urgent { background: #FFA500; color: #000; }
|
||||
.bg-normal { background: #4CAF50; color: #fff; }
|
||||
|
||||
.request-card {
|
||||
border-left: 5px solid transparent !important;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
.request-card.border-critical { border-left-color: #FF4D4D !important; }
|
||||
.request-card.border-urgent { border-left-color: #FFA500 !important; }
|
||||
.request-card.border-normal { border-left-color: #4CAF50 !important; }
|
||||
|
||||
@keyframes pulse-red {
|
||||
0% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.1); opacity: 0.8; }
|
||||
100% { transform: scale(1); opacity: 1; }
|
||||
}
|
||||
.pulse-icon {
|
||||
animation: pulse-red 2s infinite ease-in-out;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="container-fluid p-0">
|
||||
@ -32,10 +52,19 @@
|
||||
<div class="row">
|
||||
{% for req in requests %}
|
||||
<div class="col-md-6 col-xl-4 mb-4">
|
||||
<div class="p-3 border rounded h-100 bg-light d-flex flex-column">
|
||||
<div class="p-3 border rounded h-100 bg-light d-flex flex-column request-card border-{{ req.urgency|lower }}">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<div class="d-flex gap-2">
|
||||
<span class="urgency-badge bg-{{ req.urgency|lower }}">{{ req.urgency }}</span>
|
||||
<span class="urgency-badge bg-{{ req.urgency|lower }}">
|
||||
{% if req.urgency == 'CRITICAL' %}
|
||||
<i class="bi bi-exclamation-triangle-fill pulse-icon"></i>
|
||||
{% elif req.urgency == 'URGENT' %}
|
||||
<i class="bi bi-exclamation-circle-fill"></i>
|
||||
{% else %}
|
||||
<i class="bi bi-info-circle-fill"></i>
|
||||
{% endif %}
|
||||
{{ req.urgency }}
|
||||
</span>
|
||||
{% if req.status == 'Active' %}
|
||||
<span class="badge bg-success bg-opacity-10 text-success small" style="font-size: 0.7rem;">Active</span>
|
||||
{% else %}
|
||||
@ -76,11 +105,11 @@
|
||||
</a>
|
||||
{% endif %}
|
||||
{% if user.is_authenticated and user.donor_profile and req.user != user %}
|
||||
{% if can_volunteer %}
|
||||
{% if req.can_volunteer %}
|
||||
<a href="{% url 'volunteer_for_request' req.id %}" class="btn btn-sm btn-outline-danger px-3 rounded-pill">Volunteer</a>
|
||||
{% else %}
|
||||
<button class="btn btn-sm btn-outline-secondary px-3 rounded-pill" disabled title="Eligible to volunteer in {{ days_until_eligible }} days">
|
||||
Ineligible ({{ days_until_eligible }}d)
|
||||
<button class="btn btn-sm btn-outline-secondary px-3 rounded-pill" disabled title="{{ req.ineligibility_reason }}">
|
||||
{{ req.ineligibility_reason }}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
@ -77,11 +77,32 @@
|
||||
font-size: 0.7rem;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
.bg-critical { background: #FF4D4D; color: #fff; }
|
||||
.bg-critical { background: #FF4D4D; color: #fff; box-shadow: 0 0 10px rgba(255, 77, 77, 0.4); }
|
||||
.bg-urgent { background: #FFA500; color: #000; }
|
||||
.bg-normal { background: #4CAF50; color: #fff; }
|
||||
|
||||
.request-item {
|
||||
border-left: 4px solid transparent;
|
||||
transition: all 0.3s ease;
|
||||
padding-left: 12px;
|
||||
}
|
||||
.request-item.border-critical { border-left-color: #FF4D4D; background: rgba(255, 77, 77, 0.03); }
|
||||
.request-item.border-urgent { border-left-color: #FFA500; background: rgba(255, 165, 0, 0.03); }
|
||||
.request-item.border-normal { border-left-color: #4CAF50; background: rgba(76, 175, 80, 0.03); }
|
||||
|
||||
@keyframes pulse-red {
|
||||
0% { transform: scale(1); opacity: 1; }
|
||||
50% { transform: scale(1.1); opacity: 0.8; }
|
||||
100% { transform: scale(1); opacity: 1; }
|
||||
}
|
||||
.pulse-icon {
|
||||
animation: pulse-red 2s infinite ease-in-out;
|
||||
}
|
||||
|
||||
.progress {
|
||||
background-color: #2A2A2A;
|
||||
border-radius: 10px;
|
||||
@ -153,7 +174,13 @@
|
||||
<!-- Welcome Header -->
|
||||
<div class="row mb-4 align-items-center">
|
||||
<div class="col-md-7">
|
||||
<h2 class="brand-font mb-1">{% trans "RaktaPulse Community Dashboard" %}</h2>
|
||||
<div class="d-flex align-items-center gap-3 mb-1">
|
||||
<h2 class="brand-font mb-0">{% trans "RaktaPulse Community Dashboard" %}</h2>
|
||||
<span class="badge bg-success bg-opacity-10 text-success border border-success border-opacity-25 rounded-pill px-3 py-2 d-flex align-items-center gap-2" style="font-size: 0.7rem;">
|
||||
<span class="d-inline-block rounded-circle bg-success" style="width: 8px; height: 8px;"></span>
|
||||
SYSTEM ONLINE
|
||||
</span>
|
||||
</div>
|
||||
<p class="text-secondary mb-0">{% trans "Overview of blood donation activity and requirements in your area." %}</p>
|
||||
</div>
|
||||
{% if user.is_authenticated and user_badges %}
|
||||
@ -380,6 +407,14 @@
|
||||
|
||||
<!-- Sidebar Components -->
|
||||
<div class="col-lg-4">
|
||||
<!-- Urgency Insights (Pie Chart) -->
|
||||
<div class="glass-card mb-4 border-bottom border-danger border-3 shadow-sm">
|
||||
<h6 class="brand-font mb-3 text-center text-secondary uppercase small fw-bold">Urgency Distribution</h6>
|
||||
<div style="height: 180px; width: 100%; position: relative;">
|
||||
<canvas id="urgencyChart"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 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">
|
||||
@ -396,9 +431,18 @@
|
||||
|
||||
<div class="request-feed">
|
||||
{% for req in blood_requests %}
|
||||
<div class="mb-4 border-bottom border-light pb-3">
|
||||
<div class="mb-4 border-bottom border-light pb-3 request-item border-{{ req.urgency|lower }}">
|
||||
<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="urgency-badge bg-{{ req.urgency|lower }}">
|
||||
{% if req.urgency == 'CRITICAL' %}
|
||||
<i class="bi bi-exclamation-triangle-fill pulse-icon"></i>
|
||||
{% elif req.urgency == 'URGENT' %}
|
||||
<i class="bi bi-exclamation-circle-fill"></i>
|
||||
{% else %}
|
||||
<i class="bi bi-info-circle-fill"></i>
|
||||
{% endif %}
|
||||
{{ req.urgency }}
|
||||
</span>
|
||||
<span class="fw-bold text-danger">{{ req.blood_group }}</span>
|
||||
</div>
|
||||
<h6 class="mb-1 text-dark">{{ req.patient_name }}</h6>
|
||||
@ -473,7 +517,61 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<script>
|
||||
// Urgency Chart Implementation
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const urgencyData = JSON.parse('{{ urgency_counts|safe }}');
|
||||
const ctx = document.getElementById('urgencyChart').getContext('2d');
|
||||
|
||||
new Chart(ctx, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: ['Critical', 'Urgent', 'Normal'],
|
||||
datasets: [{
|
||||
data: [urgencyData.CRITICAL, urgencyData.URGENT, urgencyData.NORMAL],
|
||||
backgroundColor: ['#FF4D4D', '#FFA500', '#4CAF50'],
|
||||
borderWidth: 0,
|
||||
hoverOffset: 10
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
labels: {
|
||||
usePointStyle: true,
|
||||
padding: 15,
|
||||
font: {
|
||||
size: 10,
|
||||
family: "'Inter', sans-serif"
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function(context) {
|
||||
let label = context.label || '';
|
||||
if (label) {
|
||||
label += ': ';
|
||||
}
|
||||
if (context.parsed !== null) {
|
||||
const total = context.dataset.data.reduce((a, b) => a + b, 0);
|
||||
const percentage = Math.round((context.parsed / total) * 100);
|
||||
label += context.parsed + ' (' + percentage + '%)';
|
||||
}
|
||||
return label;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
cutout: '70%'
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const btnList = document.getElementById('btn-list');
|
||||
const btnMap = document.getElementById('btn-map');
|
||||
const mapContainer = document.getElementById('map-container');
|
||||
|
||||
@ -42,11 +42,17 @@
|
||||
<td>{{ req.hospital }}</td>
|
||||
<td>
|
||||
{% if req.urgency == 'CRITICAL' %}
|
||||
<span class="badge bg-dark">CRITICAL</span>
|
||||
<span class="badge bg-danger d-inline-flex align-items-center gap-1 pulse">
|
||||
<i class="bi bi-exclamation-triangle-fill"></i> CRITICAL
|
||||
</span>
|
||||
{% elif req.urgency == 'URGENT' %}
|
||||
<span class="badge bg-warning text-dark">URGENT</span>
|
||||
<span class="badge bg-warning text-dark d-inline-flex align-items-center gap-1">
|
||||
<i class="bi bi-exclamation-circle-fill"></i> URGENT
|
||||
</span>
|
||||
{% else %}
|
||||
<span class="badge bg-info">NORMAL</span>
|
||||
<span class="badge bg-info d-inline-flex align-items-center gap-1">
|
||||
<i class="bi bi-info-circle-fill"></i> NORMAL
|
||||
</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}Login - RaktaPulse{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center align-items-center py-5" style="min-height: 80vh;">
|
||||
<div class="col-md-5">
|
||||
<div class="glass-card shadow-lg border-danger border-opacity-10 p-4 p-md-5">
|
||||
<div class="glass-card shadow-lg border-0 p-4 p-md-5" style="border-top: 5px solid #E63946 !important;">
|
||||
<div class="text-center mb-5">
|
||||
<div class="bg-danger rounded-circle d-inline-flex align-items-center justify-content-center mb-4 blinking-logo shadow-danger" style="width: 70px; height: 70px; box-shadow: 0 0 20px rgba(230, 57, 70, 0.3);">
|
||||
<i class="bi bi-droplet-fill text-white fs-1"></i>
|
||||
<div class="d-inline-flex align-items-center justify-content-center mb-4" style="width: 60px; height: 60px; background: #fff5f5; border-radius: 12px;">
|
||||
<i class="bi bi-shield-lock text-danger fs-2"></i>
|
||||
</div>
|
||||
<h2 class="fw-bold text-danger brand-font mb-2">Welcome Back</h2>
|
||||
<p class="text-secondary">Enter your credentials to access RaktaPulse</p>
|
||||
<h2 class="fw-bold text-dark brand-font mb-2">{% trans "System Login" %}</h2>
|
||||
<p class="text-secondary small">{% trans "Secure access for registered members" %}</p>
|
||||
</div>
|
||||
|
||||
{% if form.non_field_errors %}
|
||||
@ -49,8 +50,8 @@
|
||||
<a href="#" class="text-danger small text-decoration-none fw-500">Forgot password?</a>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-danger w-100 py-3 fw-bold mt-2 shadow-sm">
|
||||
LOG IN <i class="bi bi-arrow-right-short ms-1"></i>
|
||||
<button type="submit" class="btn btn-danger w-100 py-3 fw-bold mt-2 shadow-sm" style="background-color: #E63946; border: none; border-radius: 8px;">
|
||||
{% trans "LOG IN" %} <i class="bi bi-arrow-right-short ms-1"></i>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
|
||||
@ -1,17 +1,18 @@
|
||||
{% extends 'base.html' %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}Create Account - RaktaPulse{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row justify-content-center align-items-center py-5" style="min-height: 80vh;">
|
||||
<div class="col-md-6 col-lg-5">
|
||||
<div class="glass-card shadow-lg border-danger border-opacity-10 p-4 p-md-5">
|
||||
<div class="glass-card shadow-lg border-0 p-4 p-md-5" style="border-top: 5px solid #E63946 !important;">
|
||||
<div class="text-center mb-5">
|
||||
<div class="bg-danger rounded-circle d-inline-flex align-items-center justify-content-center mb-4 blinking-logo shadow-danger" style="width: 70px; height: 70px; box-shadow: 0 0 20px rgba(230, 57, 70, 0.3);">
|
||||
<i class="bi bi-person-plus-fill text-white fs-1"></i>
|
||||
<div class="d-inline-flex align-items-center justify-content-center mb-4" style="width: 60px; height: 60px; background: #fff5f5; border-radius: 12px;">
|
||||
<i class="bi bi-person-plus text-danger fs-2"></i>
|
||||
</div>
|
||||
<h2 class="fw-bold text-danger brand-font mb-2">Join RaktaPulse</h2>
|
||||
<p class="text-secondary">Join our mission. Start saving lives today.</p>
|
||||
<h2 class="fw-bold text-dark brand-font mb-2">{% trans "Create Account" %}</h2>
|
||||
<p class="text-secondary small">{% trans "Join the next-gen management system" %}</p>
|
||||
</div>
|
||||
|
||||
<form method="post" class="needs-validation">
|
||||
@ -33,8 +34,8 @@
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<button type="submit" class="btn btn-danger w-100 py-3 fw-bold mt-4 shadow-sm">
|
||||
CREATE ACCOUNT <i class="bi bi-check-circle ms-1"></i>
|
||||
<button type="submit" class="btn btn-danger w-100 py-3 fw-bold mt-4 shadow-sm" style="background-color: #E63946; border: none; border-radius: 8px;">
|
||||
{% trans "CREATE ACCOUNT" %} <i class="bi bi-check-circle ms-1"></i>
|
||||
</button>
|
||||
</form>
|
||||
|
||||
|
||||
@ -6,34 +6,37 @@
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
:root {
|
||||
--primary-red: #e63946;
|
||||
--dark-red: #c1121f;
|
||||
--white: #f1faee;
|
||||
--primary-red: #E63946;
|
||||
--dark-red: #D62828;
|
||||
--white: #FFFFFF;
|
||||
--light-red: #FFF1F2;
|
||||
}
|
||||
|
||||
.welcome-body {
|
||||
background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
|
||||
background-color: var(--white);
|
||||
overflow-x: hidden;
|
||||
margin-left: -48px; /* Offset the parent padding */
|
||||
margin-right: -48px;
|
||||
margin-top: -48px;
|
||||
margin-bottom: -48px;
|
||||
font-family: 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
/* Hide Sidebar and Top Bar on Welcome Page */
|
||||
#sidebar { display: none !important; }
|
||||
#content { margin-left: 0 !important; width: 100% !important; }
|
||||
#content { margin-left: 0 !important; width: 100% !important; background: white !important; }
|
||||
.top-bar { display: none !important; }
|
||||
#sosButton { display: none !important; }
|
||||
.p-4.p-md-5 { padding: 0 !important; }
|
||||
|
||||
.hero-section {
|
||||
min-height: 80vh;
|
||||
min-height: 90vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
padding: 100px 0;
|
||||
padding: 60px 0;
|
||||
background: linear-gradient(180deg, var(--light-red) 0%, var(--white) 100%);
|
||||
}
|
||||
|
||||
.hero-content {
|
||||
@ -65,6 +68,7 @@
|
||||
.floating-icon {
|
||||
animation: float 3s ease-in-out infinite;
|
||||
color: var(--primary-red);
|
||||
filter: drop-shadow(0 10px 15px rgba(230, 57, 70, 0.2));
|
||||
}
|
||||
|
||||
@keyframes float {
|
||||
@ -74,50 +78,49 @@
|
||||
}
|
||||
|
||||
.btn-hero {
|
||||
padding: 15px 40px;
|
||||
font-size: 1.2rem;
|
||||
border-radius: 50px;
|
||||
transition: all 0.3s ease;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
font-weight: 700;
|
||||
padding: 16px 44px;
|
||||
font-size: 1.1rem;
|
||||
border-radius: 8px;
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||
text-transform: none;
|
||||
letter-spacing: 0.5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.btn-hero-primary {
|
||||
background-color: var(--primary-red);
|
||||
border: none;
|
||||
color: white;
|
||||
box-shadow: 0 10px 20px rgba(230, 57, 70, 0.3);
|
||||
box-shadow: 0 4px 6px -1px rgba(230, 57, 70, 0.2);
|
||||
}
|
||||
|
||||
.btn-hero-primary:hover {
|
||||
background-color: var(--dark-red);
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 15px 30px rgba(230, 57, 70, 0.4);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 10px 15px -3px rgba(230, 57, 70, 0.3);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
border: none;
|
||||
border-radius: 20px;
|
||||
transition: all 0.3s ease;
|
||||
border: 1px solid #F1F1F1;
|
||||
border-radius: 12px;
|
||||
transition: all 0.4s ease;
|
||||
background: white;
|
||||
padding: 30px;
|
||||
padding: 40px;
|
||||
height: 100%;
|
||||
box-shadow: 0 5px 15px rgba(0,0,0,0.05);
|
||||
}
|
||||
|
||||
.feature-card:hover {
|
||||
transform: translateY(-10px);
|
||||
box-shadow: 0 15px 35px rgba(0,0,0,0.1);
|
||||
border-color: var(--primary-red);
|
||||
transform: translateY(-8px);
|
||||
box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.feature-icon {
|
||||
font-size: 2.5rem;
|
||||
margin-bottom: 20px;
|
||||
background: linear-gradient(45deg, var(--primary-red), #ff4d6d);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
font-size: 3rem;
|
||||
margin-bottom: 24px;
|
||||
color: var(--primary-red);
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.pulse-animation {
|
||||
@ -125,20 +128,39 @@
|
||||
}
|
||||
|
||||
@keyframes pulse-red {
|
||||
0% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(230, 57, 70, 0.7); }
|
||||
70% { transform: scale(1); box-shadow: 0 0 0 10px rgba(230, 57, 70, 0); }
|
||||
100% { transform: scale(0.95); box-shadow: 0 0 0 0 rgba(230, 57, 70, 0); }
|
||||
0% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
100% { transform: scale(1); }
|
||||
}
|
||||
|
||||
.stats-section {
|
||||
background: var(--primary-red);
|
||||
color: white;
|
||||
padding: 60px 0;
|
||||
background: var(--white);
|
||||
border-top: 1px solid #F1F1F1;
|
||||
border-bottom: 1px solid #F1F1F1;
|
||||
padding: 80px 0;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 3rem;
|
||||
font-size: 3.5rem;
|
||||
font-weight: 800;
|
||||
color: var(--primary-red);
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: #6B7280;
|
||||
font-weight: 500;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.nav-link {
|
||||
color: #374151 !important;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
.nav-link:hover {
|
||||
color: var(--primary-red) !important;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
@ -146,21 +168,19 @@
|
||||
{% block content %}
|
||||
<div class="welcome-body">
|
||||
<!-- Navbar -->
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-white py-3 shadow-sm sticky-top">
|
||||
<nav class="navbar navbar-expand-lg navbar-light bg-white py-4 border-bottom sticky-top">
|
||||
<div class="container">
|
||||
<a class="navbar-brand d-flex align-items-center" href="#">
|
||||
<div class="bg-danger rounded-circle d-flex align-items-center justify-content-center me-2" style="width: 35px; height: 35px;">
|
||||
<i class="bi bi-droplet-fill text-white"></i>
|
||||
</div>
|
||||
<span class="fs-4 fw-bold text-danger">RaktaPulse</span>
|
||||
<i class="bi bi-droplet-fill text-danger fs-3 me-2"></i>
|
||||
<span class="fs-4 fw-bold text-dark">Rakta<span class="text-danger">Pulse</span></span>
|
||||
</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#welcomeNav">
|
||||
<button class="navbar-toggler border-0" type="button" data-bs-toggle="collapse" data-bs-target="#welcomeNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="welcomeNav">
|
||||
<ul class="navbar-nav ms-auto gap-2">
|
||||
<li class="nav-item"><a class="nav-link fw-bold" href="{% url 'login' %}">Login</a></li>
|
||||
<li class="nav-item"><a class="btn btn-danger rounded-pill px-4" href="{% url 'register' %}">Sign Up</a></li>
|
||||
<ul class="navbar-nav ms-auto align-items-center gap-4">
|
||||
<li class="nav-item"><a class="nav-link fw-semibold" href="{% url 'login' %}">Sign In</a></li>
|
||||
<li class="nav-item"><a class="btn btn-hero-primary px-4 py-2" href="{% url 'register' %}" style="border-radius: 6px;">Create Account</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@ -171,19 +191,19 @@
|
||||
<div class="container">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-lg-6 hero-content">
|
||||
<h1 class="display-3 fw-bold mb-4">Save Lives with <span style="color: var(--primary-red);">RaktaPulse</span></h1>
|
||||
<p class="lead mb-5 text-secondary">Every drop counts. Connect with donors, find blood banks, and request life-saving help in seconds. Join our mission to make blood donation accessible to everyone.</p>
|
||||
<div class="badge bg-danger bg-opacity-10 text-danger px-3 py-2 mb-4 fw-semibold rounded-pill">
|
||||
Next-Gen Blood Management
|
||||
</div>
|
||||
<h1 class="display-3 fw-bold mb-4" style="color: #111827; line-height: 1.1;">Streamlining <span class="text-danger">Life-Saving</span> Connections.</h1>
|
||||
<p class="lead mb-5 text-muted" style="max-width: 90%;">A high-efficiency platform for real-time blood donor matching, hospital coordination, and emergency response management.</p>
|
||||
<div class="d-flex gap-3 flex-wrap">
|
||||
<a href="{% url 'register' %}" class="btn btn-hero btn-hero-primary">Join as Donor</a>
|
||||
<a href="{% url 'login' %}" class="btn btn-hero btn-outline-danger border-2">Login to Portal</a>
|
||||
<a href="{% url 'register' %}" class="btn btn-hero btn-hero-primary">Get Started</a>
|
||||
<a href="{% url 'login' %}" class="btn btn-hero btn-outline-dark">System Login</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-6 text-center hero-image mt-5 mt-lg-0">
|
||||
<div class="position-relative">
|
||||
<i class="bi bi-droplet-fill floating-icon" style="font-size: 15rem;"></i>
|
||||
<div class="position-absolute top-50 start-50 translate-middle">
|
||||
<i class="bi bi-heart-fill text-white" style="font-size: 4rem;"></i>
|
||||
</div>
|
||||
<i class="bi bi-droplet-fill floating-icon" style="font-size: 18rem;"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -191,32 +211,34 @@
|
||||
</section>
|
||||
|
||||
<!-- Features Section -->
|
||||
<section class="py-5">
|
||||
<div class="container">
|
||||
<div class="text-center mb-5">
|
||||
<h2 class="fw-bold">Why Choose RaktaPulse?</h2>
|
||||
<div class="mx-auto bg-danger" style="width: 80px; height: 4px; border-radius: 2px;"></div>
|
||||
<section class="py-5" style="background: white;">
|
||||
<div class="container py-5">
|
||||
<div class="row mb-5 justify-content-center">
|
||||
<div class="col-lg-6 text-center">
|
||||
<h2 class="fw-bold mb-3" style="color: #111827;">Engineered for Impact</h2>
|
||||
<p class="text-muted">Our core modules provide the tools needed for rapid response and efficient data management.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4">
|
||||
<div class="feature-card text-center">
|
||||
<i class="bi bi-search feature-icon"></i>
|
||||
<h3>Find Donors</h3>
|
||||
<p class="text-muted">Locate blood donors in your vicinity using our advanced geolocation matching system.</p>
|
||||
<div class="feature-card">
|
||||
<i class="bi bi-cpu feature-icon"></i>
|
||||
<h4 class="fw-bold mb-3">Smart Matching</h4>
|
||||
<p class="text-muted mb-0">AI-driven geolocation algorithm to find the most compatible donors in the shortest time.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="feature-card text-center">
|
||||
<i class="bi bi-geo-alt feature-icon"></i>
|
||||
<h3>Nearby Hospitals</h3>
|
||||
<p class="text-muted">Instantly find hospitals and blood banks with live distance calculation and maps.</p>
|
||||
<div class="feature-card">
|
||||
<i class="bi bi-database-check feature-icon"></i>
|
||||
<h4 class="fw-bold mb-3">Live Inventory</h4>
|
||||
<p class="text-muted mb-0">Real-time tracking of blood stock levels across all partner hospitals and blood banks.</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="feature-card text-center">
|
||||
<i class="bi bi-chat-dots feature-icon"></i>
|
||||
<h3>Real-time Chat</h3>
|
||||
<p class="text-muted">Communicate directly with donors via our secure messaging and calling system.</p>
|
||||
<div class="feature-card">
|
||||
<i class="bi bi-shield-lock feature-icon"></i>
|
||||
<h4 class="fw-bold mb-3">Secure Channels</h4>
|
||||
<p class="text-muted mb-0">End-to-end encrypted communication for donor privacy and secure medical data handling.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -224,37 +246,32 @@
|
||||
</section>
|
||||
|
||||
<!-- Stats Section -->
|
||||
<section class="stats-section mt-5">
|
||||
<section class="stats-section">
|
||||
<div class="container">
|
||||
<div class="row text-center">
|
||||
<div class="col-md-4 mb-4 mb-md-0">
|
||||
<div class="stat-number">1000+</div>
|
||||
<div class="text-uppercase fw-bold">Active Donors</div>
|
||||
<div class="col-md-4 mb-5 mb-md-0">
|
||||
<div class="stat-number">10.4k</div>
|
||||
<div class="stat-label">Verified Donors</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-4 mb-md-0">
|
||||
<div class="stat-number">500+</div>
|
||||
<div class="text-uppercase fw-bold">Lives Saved</div>
|
||||
<div class="col-md-4 mb-5 mb-md-0">
|
||||
<div class="stat-number">85%</div>
|
||||
<div class="stat-label">Response Rate</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="stat-number">50+</div>
|
||||
<div class="text-uppercase fw-bold">Partner Hospitals</div>
|
||||
<div class="stat-number">120+</div>
|
||||
<div class="stat-label">Tech Partners</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Call to Action -->
|
||||
<section class="py-5 my-5">
|
||||
<div class="container">
|
||||
<div class="bg-dark text-white p-5 rounded-4 shadow-lg text-center position-relative overflow-hidden">
|
||||
<div class="position-relative z-1">
|
||||
<h2 class="display-5 fw-bold mb-4">Ready to make a difference?</h2>
|
||||
<p class="lead mb-4">Register today and start saving lives in your community.</p>
|
||||
<a href="{% url 'register' %}" class="btn btn-danger btn-lg px-5 py-3 pulse-animation">Get Started Now</a>
|
||||
</div>
|
||||
<div class="position-absolute bottom-0 end-0 p-3 opacity-25">
|
||||
<i class="bi bi-shield-check" style="font-size: 10rem;"></i>
|
||||
</div>
|
||||
<section class="py-5">
|
||||
<div class="container py-5">
|
||||
<div class="border border-danger border-2 p-5 rounded-4 text-center">
|
||||
<h2 class="display-5 fw-bold mb-4" style="color: #111827;">Ready to Integrate?</h2>
|
||||
<p class="lead mb-5 text-muted">Join the network of tech-driven healthcare providers saving lives every day.</p>
|
||||
<a href="{% url 'register' %}" class="btn btn-hero btn-hero-primary pulse-animation">Create Your Account</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@ -263,15 +280,14 @@
|
||||
<footer class="bg-white py-5 border-top">
|
||||
<div class="container">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-6 text-center text-md-start">
|
||||
<span class="fw-bold text-danger">RaktaPulse</span> © 2026. All rights reserved.
|
||||
<div class="col-md-6 text-center text-md-start text-muted">
|
||||
<span class="fw-bold text-danger">RaktaPulse</span> © 2026. <span class="ms-2">v2.1.0-stable</span>
|
||||
</div>
|
||||
<div class="col-md-6 text-center text-md-end mt-3 mt-md-0">
|
||||
<div class="d-flex justify-content-center justify-content-md-end gap-3">
|
||||
<a href="#" class="text-secondary fs-4"><i class="bi bi-facebook"></i></a>
|
||||
<a href="#" class="text-secondary fs-4"><i class="bi bi-twitter-x"></i></a>
|
||||
<a href="#" class="text-secondary fs-4"><i class="bi bi-instagram"></i></a>
|
||||
<a href="#" class="text-secondary fs-4"><i class="bi bi-linkedin"></i></a>
|
||||
<div class="d-flex justify-content-center justify-content-md-end gap-4">
|
||||
<a href="#" class="text-muted"><i class="bi bi-github"></i></a>
|
||||
<a href="#" class="text-muted"><i class="bi bi-twitter-x"></i></a>
|
||||
<a href="#" class="text-muted"><i class="bi bi-linkedin"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -25,6 +25,19 @@ from .models import (
|
||||
)
|
||||
from .forms import UserUpdateForm, ProfileUpdateForm, UserRegisterForm
|
||||
|
||||
# --- Medical Compatibility Constants ---
|
||||
|
||||
COMPATIBILITY_MATRIX = {
|
||||
'O-': ['O-', 'O+', 'A-', 'A+', 'B-', 'B+', 'AB-', 'AB+'], # Universal Donor
|
||||
'O+': ['O+', 'A+', 'B+', 'AB+'],
|
||||
'A-': ['A-', 'A+', 'AB-', 'AB+'],
|
||||
'A+': ['A+', 'AB+'],
|
||||
'B-': ['B-', 'B+', 'AB-', 'AB+'],
|
||||
'B+': ['B+', 'AB+'],
|
||||
'AB-': ['AB-', 'AB+'],
|
||||
'AB+': ['AB+'],
|
||||
}
|
||||
|
||||
# --- Emergency & Location Helpers ---
|
||||
|
||||
@login_required
|
||||
@ -347,6 +360,13 @@ def home(request):
|
||||
{"myth": "I can't donate because I have high BP.", "fact": "As long as it's within 180/100 at the time of donation, you're fine."},
|
||||
]
|
||||
|
||||
# Urgency Distribution for Pie Chart
|
||||
urgency_counts = {
|
||||
'CRITICAL': blood_requests.filter(urgency='CRITICAL').count(),
|
||||
'URGENT': blood_requests.filter(urgency='URGENT').count(),
|
||||
'NORMAL': blood_requests.filter(urgency='NORMAL').count(),
|
||||
}
|
||||
|
||||
context = {
|
||||
"donors": donor_list_data[:15], # Increased count for scrollability
|
||||
"blood_requests": blood_requests[:6],
|
||||
@ -357,6 +377,7 @@ def home(request):
|
||||
"project_name": "RaktaPulse",
|
||||
"current_time": timezone.now(),
|
||||
"myths_vs_facts": myths_vs_facts,
|
||||
"urgency_counts": json.dumps(urgency_counts),
|
||||
}
|
||||
|
||||
if request.user.is_authenticated:
|
||||
@ -427,21 +448,38 @@ def blood_request_list(request):
|
||||
|
||||
requests = requests.order_by('-created_at')
|
||||
|
||||
can_volunteer = True
|
||||
days_until_eligible = 0
|
||||
donor_profile = None
|
||||
if request.user.is_authenticated:
|
||||
donor_profile = getattr(request.user, 'donor_profile', None)
|
||||
if donor_profile and donor_profile.last_donation_date:
|
||||
days_since = (timezone.now().date() - donor_profile.last_donation_date).days
|
||||
if days_since < 90:
|
||||
can_volunteer = False
|
||||
days_until_eligible = 90 - days_since
|
||||
|
||||
for req in requests:
|
||||
req.can_volunteer = True
|
||||
req.ineligibility_reason = ""
|
||||
|
||||
if not donor_profile:
|
||||
req.can_volunteer = False
|
||||
req.ineligibility_reason = "Register as donor"
|
||||
else:
|
||||
# Check medical compatibility
|
||||
donor_group = donor_profile.blood_group
|
||||
compatible_groups = COMPATIBILITY_MATRIX.get(donor_group, [])
|
||||
|
||||
if req.blood_group not in compatible_groups:
|
||||
req.can_volunteer = False
|
||||
req.ineligibility_reason = "Incompatible"
|
||||
|
||||
# Check 90 days limit
|
||||
if donor_profile.last_donation_date:
|
||||
days_since = (timezone.now().date() - donor_profile.last_donation_date).days
|
||||
if days_since < 90:
|
||||
req.can_volunteer = False
|
||||
days_left = 90 - days_since
|
||||
req.ineligibility_reason = f"Wait {days_left}d"
|
||||
req.days_until_eligible = days_left
|
||||
|
||||
context = {
|
||||
'requests': requests,
|
||||
'current_status': status,
|
||||
'can_volunteer': can_volunteer,
|
||||
'days_until_eligible': days_until_eligible,
|
||||
}
|
||||
return render(request, 'core/blood_request_list.html', context)
|
||||
|
||||
@ -665,6 +703,14 @@ def volunteer_for_request(request, request_id):
|
||||
messages.error(request, "You cannot volunteer for your own blood request.")
|
||||
return redirect('blood_request_list')
|
||||
|
||||
# Check for medical compatibility
|
||||
donor_group = donor_profile.blood_group
|
||||
compatible_groups = COMPATIBILITY_MATRIX.get(donor_group, [])
|
||||
|
||||
if blood_request.blood_group not in compatible_groups:
|
||||
messages.error(request, f"Your blood group ({donor_group}) is not medically compatible with {blood_request.blood_group}. Only compatible donors can volunteer.")
|
||||
return redirect('blood_request_list')
|
||||
|
||||
# Check if already volunteered
|
||||
if DonationEvent.objects.filter(donor=donor_profile, request=blood_request).exists():
|
||||
messages.warning(request, "You have already volunteered for this request.")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user