Raktapulse

This commit is contained in:
Flatlogic Bot 2026-03-01 16:16:12 +00:00
parent 1c8a1cd56f
commit fb3e799193
8 changed files with 330 additions and 133 deletions

View File

@ -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 %}

View File

@ -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');

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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> &copy; 2026. All rights reserved.
<div class="col-md-6 text-center text-md-start text-muted">
<span class="fw-bold text-danger">RaktaPulse</span> &copy; 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>

View File

@ -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.")