leekumar commited on
Commit
8e0202b
·
verified ·
1 Parent(s): ca344dc

undefined - Initial Deployment

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +698 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Lee
3
- emoji: 🐠
4
- colorFrom: yellow
5
- colorTo: red
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: lee
3
+ emoji: 🐳
4
+ colorFrom: blue
5
+ colorTo: purple
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,698 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Blood Bank Management System</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ .gradient-bg {
11
+ background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
12
+ }
13
+ .blood-card {
14
+ transition: all 0.3s ease;
15
+ }
16
+ .blood-card:hover {
17
+ transform: translateY(-5px);
18
+ box-shadow: 0 10px 20px rgba(0,0,0,0.1);
19
+ }
20
+ .blood-A { background-color: #FFEBEE; border-left: 4px solid #EF5350; }
21
+ .blood-B { background-color: #E8F5E9; border-left: 4px solid #66BB6A; }
22
+ .blood-AB { background-color: #E3F2FD; border-left: 4px solid #42A5F5; }
23
+ .blood-O { background-color: #FFF8E1; border-left: 4px solid #FFCA28; }
24
+ .due-soon { animation: pulse 2s infinite; }
25
+ @keyframes pulse {
26
+ 0% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0.4); }
27
+ 70% { box-shadow: 0 0 0 10px rgba(255, 193, 7, 0); }
28
+ 100% { box-shadow: 0 0 0 0 rgba(255, 193, 7, 0); }
29
+ }
30
+ </style>
31
+ </head>
32
+ <body class="gradient-bg min-h-screen">
33
+ <div class="container mx-auto px-4 py-8">
34
+ <!-- Header -->
35
+ <header class="flex flex-col md:flex-row justify-between items-center mb-8">
36
+ <div class="flex items-center mb-4 md:mb-0">
37
+ <i class="fas fa-tint text-red-600 text-4xl mr-3"></i>
38
+ <h1 class="text-3xl font-bold text-gray-800">Blood Bank Manager</h1>
39
+ </div>
40
+ <div class="flex space-x-2">
41
+ <button onclick="openAddDonorModal()" class="bg-red-600 hover:bg-red-700 text-white px-4 py-2 rounded-lg flex items-center">
42
+ <i class="fas fa-plus mr-2"></i> Add Donor
43
+ </button>
44
+ <button onclick="showReminders()" class="bg-amber-500 hover:bg-amber-600 text-white px-4 py-2 rounded-lg flex items-center">
45
+ <i class="fas fa-bell mr-2"></i> Reminders
46
+ </button>
47
+ </div>
48
+ </header>
49
+
50
+ <!-- Stats Cards -->
51
+ <div class="grid grid-cols-1 md:grid-cols-4 gap-4 mb-8">
52
+ <div class="bg-white rounded-lg shadow p-4 flex items-center">
53
+ <div class="p-3 rounded-full bg-red-100 text-red-600 mr-4">
54
+ <i class="fas fa-tint text-xl"></i>
55
+ </div>
56
+ <div>
57
+ <p class="text-gray-500">Total Donors</p>
58
+ <h3 class="text-2xl font-bold" id="totalDonors">0</h3>
59
+ </div>
60
+ </div>
61
+ <div class="bg-white rounded-lg shadow p-4 flex items-center">
62
+ <div class="p-3 rounded-full bg-green-100 text-green-600 mr-4">
63
+ <i class="fas fa-calendar-check text-xl"></i>
64
+ </div>
65
+ <div>
66
+ <p class="text-gray-500">Eligible Now</p>
67
+ <h3 class="text-2xl font-bold" id="eligibleDonors">0</h3>
68
+ </div>
69
+ </div>
70
+ <div class="bg-white rounded-lg shadow p-4 flex items-center">
71
+ <div class="p-3 rounded-full bg-amber-100 text-amber-600 mr-4">
72
+ <i class="fas fa-exclamation-circle text-xl"></i>
73
+ </div>
74
+ <div>
75
+ <p class="text-gray-500">Due Soon</p>
76
+ <h3 class="text-2xl font-bold" id="dueSoonDonors">0</h3>
77
+ </div>
78
+ </div>
79
+ <div class="bg-white rounded-lg shadow p-4 flex items-center">
80
+ <div class="p-3 rounded-full bg-blue-100 text-blue-600 mr-4">
81
+ <i class="fas fa-filter text-xl"></i>
82
+ </div>
83
+ <div>
84
+ <p class="text-gray-500">Blood Groups</p>
85
+ <h3 class="text-2xl font-bold">8 Types</h3>
86
+ </div>
87
+ </div>
88
+ </div>
89
+
90
+ <!-- Filter Section -->
91
+ <div class="bg-white rounded-lg shadow p-4 mb-8">
92
+ <div class="flex flex-col md:flex-row md:items-center md:justify-between space-y-4 md:space-y-0">
93
+ <div class="flex-1">
94
+ <label class="block text-gray-700 mb-2">Search Donors</label>
95
+ <div class="relative">
96
+ <input type="text" id="searchInput" placeholder="Search by name or phone..." class="w-full px-4 py-2 border rounded-lg pl-10">
97
+ <i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
98
+ </div>
99
+ </div>
100
+ <div class="md:ml-4">
101
+ <label class="block text-gray-700 mb-2">Blood Group</label>
102
+ <select id="bloodGroupFilter" class="w-full px-4 py-2 border rounded-lg">
103
+ <option value="all">All Blood Groups</option>
104
+ <option value="A+">A+</option>
105
+ <option value="A-">A-</option>
106
+ <option value="B+">B+</option>
107
+ <option value="B-">B-</option>
108
+ <option value="AB+">AB+</option>
109
+ <option value="AB-">AB-</option>
110
+ <option value="O+">O+</option>
111
+ <option value="O-">O-</option>
112
+ </select>
113
+ </div>
114
+ <div class="md:ml-4">
115
+ <label class="block text-gray-700 mb-2">Eligibility</label>
116
+ <select id="eligibilityFilter" class="w-full px-4 py-2 border rounded-lg">
117
+ <option value="all">All Donors</option>
118
+ <option value="eligible">Eligible Now</option>
119
+ <option value="dueSoon">Due Soon</option>
120
+ <option value="notEligible">Not Eligible</option>
121
+ </select>
122
+ </div>
123
+ </div>
124
+ </div>
125
+
126
+ <!-- Donors Table -->
127
+ <div class="bg-white rounded-lg shadow overflow-hidden">
128
+ <div class="overflow-x-auto">
129
+ <table class="min-w-full divide-y divide-gray-200">
130
+ <thead class="bg-gray-50">
131
+ <tr>
132
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Donor</th>
133
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Blood Group</th>
134
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Last Donation</th>
135
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Next Eligible</th>
136
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
137
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
138
+ </tr>
139
+ </thead>
140
+ <tbody class="bg-white divide-y divide-gray-200" id="donorsTableBody">
141
+ <!-- Donors will be added here dynamically -->
142
+ </tbody>
143
+ </table>
144
+ </div>
145
+ <div class="bg-gray-50 px-6 py-3 flex items-center justify-between border-t border-gray-200">
146
+ <div class="text-sm text-gray-500" id="tableInfo">
147
+ Showing 0 to 0 of 0 entries
148
+ </div>
149
+ <div class="flex space-x-2">
150
+ <button onclick="prevPage()" class="px-3 py-1 border rounded text-gray-700 disabled:opacity-50" id="prevBtn" disabled>
151
+ Previous
152
+ </button>
153
+ <button onclick="nextPage()" class="px-3 py-1 border rounded text-gray-700 disabled:opacity-50" id="nextBtn" disabled>
154
+ Next
155
+ </button>
156
+ </div>
157
+ </div>
158
+ </div>
159
+ </div>
160
+
161
+ <!-- Add Donor Modal -->
162
+ <div id="addDonorModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
163
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-md">
164
+ <div class="flex justify-between items-center border-b px-6 py-4">
165
+ <h3 class="text-lg font-semibold">Add New Donor</h3>
166
+ <button onclick="closeAddDonorModal()" class="text-gray-500 hover:text-gray-700">
167
+ <i class="fas fa-times"></i>
168
+ </button>
169
+ </div>
170
+ <div class="p-6">
171
+ <form id="donorForm">
172
+ <input type="hidden" id="editDonorId" value="">
173
+ <div class="mb-4">
174
+ <label class="block text-gray-700 mb-2">Full Name</label>
175
+ <input type="text" id="donorName" class="w-full px-4 py-2 border rounded-lg" required>
176
+ </div>
177
+ <div class="mb-4">
178
+ <label class="block text-gray-700 mb-2">Phone Number</label>
179
+ <input type="tel" id="donorPhone" class="w-full px-4 py-2 border rounded-lg" required>
180
+ </div>
181
+ <div class="mb-4">
182
+ <label class="block text-gray-700 mb-2">Email</label>
183
+ <input type="email" id="donorEmail" class="w-full px-4 py-2 border rounded-lg">
184
+ </div>
185
+ <div class="mb-4">
186
+ <label class="block text-gray-700 mb-2">Blood Group</label>
187
+ <select id="donorBloodGroup" class="w-full px-4 py-2 border rounded-lg" required>
188
+ <option value="">Select Blood Group</option>
189
+ <option value="A+">A+</option>
190
+ <option value="A-">A-</option>
191
+ <option value="B+">B+</option>
192
+ <option value="B-">B-</option>
193
+ <option value="AB+">AB+</option>
194
+ <option value="AB-">AB-</option>
195
+ <option value="O+">O+</option>
196
+ <option value="O-">O-</option>
197
+ </select>
198
+ </div>
199
+ <div class="mb-4">
200
+ <label class="block text-gray-700 mb-2">Last Donation Date</label>
201
+ <input type="date" id="donorLastDonation" class="w-full px-4 py-2 border rounded-lg" required>
202
+ </div>
203
+ <div class="flex justify-end space-x-3 mt-6">
204
+ <button type="button" onclick="closeAddDonorModal()" class="px-4 py-2 border rounded-lg text-gray-700 hover:bg-gray-100">
205
+ Cancel
206
+ </button>
207
+ <button type="submit" class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700">
208
+ Save Donor
209
+ </button>
210
+ </div>
211
+ </form>
212
+ </div>
213
+ </div>
214
+ </div>
215
+
216
+ <!-- Reminders Modal -->
217
+ <div id="remindersModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
218
+ <div class="bg-white rounded-lg shadow-xl w-full max-w-2xl">
219
+ <div class="flex justify-between items-center border-b px-6 py-4">
220
+ <h3 class="text-lg font-semibold">Donation Reminders</h3>
221
+ <button onclick="closeRemindersModal()" class="text-gray-500 hover:text-gray-700">
222
+ <i class="fas fa-times"></i>
223
+ </button>
224
+ </div>
225
+ <div class="p-6 max-h-96 overflow-y-auto">
226
+ <div class="mb-4">
227
+ <h4 class="font-medium text-gray-700 mb-2">Donors Eligible Now (90+ days since last donation)</h4>
228
+ <div id="eligibleNowList" class="space-y-2">
229
+ <!-- Eligible donors will be added here -->
230
+ </div>
231
+ </div>
232
+ <div>
233
+ <h4 class="font-medium text-gray-700 mb-2">Donors Due Soon (80-89 days since last donation)</h4>
234
+ <div id="dueSoonList" class="space-y-2">
235
+ <!-- Due soon donors will be added here -->
236
+ </div>
237
+ </div>
238
+ </div>
239
+ <div class="border-t px-6 py-3 flex justify-end">
240
+ <button onclick="closeRemindersModal()" class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700">
241
+ Close
242
+ </button>
243
+ </div>
244
+ </div>
245
+ </div>
246
+
247
+ <script>
248
+ // Sample data - in a real app, this would come from a database
249
+ let donors = [
250
+ {
251
+ id: 1,
252
+ name: "John Doe",
253
+ phone: "555-123-4567",
254
+ email: "john.doe@example.com",
255
+ bloodGroup: "A+",
256
+ lastDonation: "2023-05-15",
257
+ status: "eligible"
258
+ },
259
+ {
260
+ id: 2,
261
+ name: "Jane Smith",
262
+ phone: "555-987-6543",
263
+ email: "jane.smith@example.com",
264
+ bloodGroup: "B-",
265
+ lastDonation: "2023-06-20",
266
+ status: "eligible"
267
+ },
268
+ {
269
+ id: 3,
270
+ name: "Robert Johnson",
271
+ phone: "555-456-7890",
272
+ email: "robert.j@example.com",
273
+ bloodGroup: "O+",
274
+ lastDonation: "2023-08-10",
275
+ status: "dueSoon"
276
+ },
277
+ {
278
+ id: 4,
279
+ name: "Emily Davis",
280
+ phone: "555-789-1234",
281
+ email: "emily.d@example.com",
282
+ bloodGroup: "AB+",
283
+ lastDonation: "2023-07-05",
284
+ status: "notEligible"
285
+ },
286
+ {
287
+ id: 5,
288
+ name: "Michael Brown",
289
+ phone: "555-321-6547",
290
+ email: "michael.b@example.com",
291
+ bloodGroup: "A-",
292
+ lastDonation: "2023-04-30",
293
+ status: "eligible"
294
+ },
295
+ {
296
+ id: 6,
297
+ name: "Sarah Wilson",
298
+ phone: "555-654-3210",
299
+ email: "sarah.w@example.com",
300
+ bloodGroup: "B+",
301
+ lastDonation: "2023-08-01",
302
+ status: "dueSoon"
303
+ },
304
+ {
305
+ id: 7,
306
+ name: "David Taylor",
307
+ phone: "555-147-2583",
308
+ email: "david.t@example.com",
309
+ bloodGroup: "O-",
310
+ lastDonation: "2023-05-25",
311
+ status: "eligible"
312
+ },
313
+ {
314
+ id: 8,
315
+ name: "Jessica Martinez",
316
+ phone: "555-369-2581",
317
+ email: "jessica.m@example.com",
318
+ bloodGroup: "AB-",
319
+ lastDonation: "2023-07-15",
320
+ status: "notEligible"
321
+ }
322
+ ];
323
+
324
+ // Current page for pagination
325
+ let currentPage = 1;
326
+ const donorsPerPage = 5;
327
+
328
+ // DOM elements
329
+ const donorsTableBody = document.getElementById('donorsTableBody');
330
+ const searchInput = document.getElementById('searchInput');
331
+ const bloodGroupFilter = document.getElementById('bloodGroupFilter');
332
+ const eligibilityFilter = document.getElementById('eligibilityFilter');
333
+ const totalDonorsElement = document.getElementById('totalDonors');
334
+ const eligibleDonorsElement = document.getElementById('eligibleDonors');
335
+ const dueSoonDonorsElement = document.getElementById('dueSoonDonors');
336
+ const tableInfoElement = document.getElementById('tableInfo');
337
+ const prevBtn = document.getElementById('prevBtn');
338
+ const nextBtn = document.getElementById('nextBtn');
339
+
340
+ // Initialize the app
341
+ document.addEventListener('DOMContentLoaded', function() {
342
+ updateDonorStatuses();
343
+ renderDonors();
344
+ updateStats();
345
+
346
+ // Event listeners for filters
347
+ searchInput.addEventListener('input', function() {
348
+ currentPage = 1;
349
+ renderDonors();
350
+ });
351
+
352
+ bloodGroupFilter.addEventListener('change', function() {
353
+ currentPage = 1;
354
+ renderDonors();
355
+ });
356
+
357
+ eligibilityFilter.addEventListener('change', function() {
358
+ currentPage = 1;
359
+ renderDonors();
360
+ });
361
+
362
+ // Form submission
363
+ document.getElementById('donorForm').addEventListener('submit', function(e) {
364
+ e.preventDefault();
365
+ saveDonor();
366
+ });
367
+ });
368
+
369
+ // Update donor statuses based on last donation date
370
+ function updateDonorStatuses() {
371
+ const today = new Date();
372
+ donors.forEach(donor => {
373
+ const lastDonationDate = new Date(donor.lastDonation);
374
+ const daysSinceDonation = Math.floor((today - lastDonationDate) / (1000 * 60 * 60 * 24));
375
+
376
+ if (daysSinceDonation >= 90) {
377
+ donor.status = "eligible";
378
+ } else if (daysSinceDonation >= 80) {
379
+ donor.status = "dueSoon";
380
+ } else {
381
+ donor.status = "notEligible";
382
+ }
383
+ });
384
+ }
385
+
386
+ // Render donors table based on filters and pagination
387
+ function renderDonors() {
388
+ const searchTerm = searchInput.value.toLowerCase();
389
+ const selectedBloodGroup = bloodGroupFilter.value;
390
+ const selectedEligibility = eligibilityFilter.value;
391
+
392
+ // Filter donors
393
+ let filteredDonors = donors.filter(donor => {
394
+ const matchesSearch = donor.name.toLowerCase().includes(searchTerm) ||
395
+ donor.phone.includes(searchTerm);
396
+ const matchesBloodGroup = selectedBloodGroup === 'all' ||
397
+ donor.bloodGroup === selectedBloodGroup;
398
+ const matchesEligibility = selectedEligibility === 'all' ||
399
+ donor.status === selectedEligibility;
400
+
401
+ return matchesSearch && matchesBloodGroup && matchesEligibility;
402
+ });
403
+
404
+ // Update pagination info
405
+ const totalPages = Math.ceil(filteredDonors.length / donorsPerPage);
406
+ const startIndex = (currentPage - 1) * donorsPerPage;
407
+ const endIndex = Math.min(startIndex + donorsPerPage, filteredDonors.length);
408
+ const paginatedDonors = filteredDonors.slice(startIndex, endIndex);
409
+
410
+ // Update table info
411
+ tableInfoElement.textContent = `Showing ${startIndex + 1} to ${endIndex} of ${filteredDonors.length} entries`;
412
+
413
+ // Update pagination buttons
414
+ prevBtn.disabled = currentPage === 1;
415
+ nextBtn.disabled = currentPage === totalPages || filteredDonors.length === 0;
416
+
417
+ // Clear table
418
+ donorsTableBody.innerHTML = '';
419
+
420
+ // Add rows for each donor
421
+ paginatedDonors.forEach(donor => {
422
+ const lastDonationDate = new Date(donor.lastDonation);
423
+ const nextEligibleDate = new Date(lastDonationDate);
424
+ nextEligibleDate.setDate(lastDonationDate.getDate() + 90);
425
+
426
+ const today = new Date();
427
+ const daysUntilEligible = Math.ceil((nextEligibleDate - today) / (1000 * 60 * 60 * 24));
428
+
429
+ const row = document.createElement('tr');
430
+ row.className = donor.status === 'dueSoon' ? 'due-soon' : '';
431
+
432
+ // Determine blood group class for styling
433
+ let bloodClass = '';
434
+ if (donor.bloodGroup.includes('A')) bloodClass = 'blood-A';
435
+ else if (donor.bloodGroup.includes('B')) bloodClass = 'blood-B';
436
+ else if (donor.bloodGroup.includes('AB')) bloodClass = 'blood-AB';
437
+ else if (donor.bloodGroup.includes('O')) bloodClass = 'blood-O';
438
+
439
+ row.innerHTML = `
440
+ <td class="px-6 py-4 whitespace-nowrap">
441
+ <div class="flex items-center">
442
+ <div class="flex-shrink-0 h-10 w-10 rounded-full bg-red-100 flex items-center justify-center">
443
+ <i class="fas fa-user text-red-600"></i>
444
+ </div>
445
+ <div class="ml-4">
446
+ <div class="text-sm font-medium text-gray-900">${donor.name}</div>
447
+ <div class="text-sm text-gray-500">${donor.phone}</div>
448
+ </div>
449
+ </div>
450
+ </td>
451
+ <td class="px-6 py-4 whitespace-nowrap">
452
+ <div class="text-sm font-medium ${bloodClass} px-3 py-1 rounded-full text-center">${donor.bloodGroup}</div>
453
+ </td>
454
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
455
+ ${formatDate(donor.lastDonation)}
456
+ </td>
457
+ <td class="px-6 py-4 whitespace-nowrap">
458
+ <div class="text-sm ${daysUntilEligible <= 0 ? 'text-green-600' : 'text-gray-500'}">
459
+ ${formatDate(nextEligibleDate.toISOString().split('T')[0])}
460
+ ${daysUntilEligible > 0 ? `<span class="text-xs ml-1">(${daysUntilEligible} days)</span>` : ''}
461
+ </div>
462
+ </td>
463
+ <td class="px-6 py-4 whitespace-nowrap">
464
+ ${getStatusBadge(donor.status)}
465
+ </td>
466
+ <td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
467
+ <button onclick="editDonor(${donor.id})" class="text-blue-600 hover:text-blue-900 mr-3">
468
+ <i class="fas fa-edit"></i>
469
+ </button>
470
+ <button onclick="deleteDonor(${donor.id})" class="text-red-600 hover:text-red-900">
471
+ <i class="fas fa-trash"></i>
472
+ </button>
473
+ </td>
474
+ `;
475
+
476
+ donorsTableBody.appendChild(row);
477
+ });
478
+
479
+ // If no donors match the filters
480
+ if (filteredDonors.length === 0) {
481
+ const row = document.createElement('tr');
482
+ row.innerHTML = `
483
+ <td colspan="6" class="px-6 py-4 text-center text-gray-500">
484
+ No donors found matching your criteria.
485
+ </td>
486
+ `;
487
+ donorsTableBody.appendChild(row);
488
+ }
489
+ }
490
+
491
+ // Format date for display
492
+ function formatDate(dateString) {
493
+ const options = { year: 'numeric', month: 'short', day: 'numeric' };
494
+ return new Date(dateString).toLocaleDateString(undefined, options);
495
+ }
496
+
497
+ // Get status badge HTML
498
+ function getStatusBadge(status) {
499
+ const badges = {
500
+ eligible: '<span class="px-2 py-1 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">Eligible</span>',
501
+ dueSoon: '<span class="px-2 py-1 inline-flex text-xs leading-5 font-semibold rounded-full bg-amber-100 text-amber-800">Due Soon</span>',
502
+ notEligible: '<span class="px-2 py-1 inline-flex text-xs leading-5 font-semibold rounded-full bg-gray-100 text-gray-800">Not Eligible</span>'
503
+ };
504
+ return badges[status] || '';
505
+ }
506
+
507
+ // Update statistics
508
+ function updateStats() {
509
+ totalDonorsElement.textContent = donors.length;
510
+
511
+ const eligibleCount = donors.filter(d => d.status === 'eligible').length;
512
+ const dueSoonCount = donors.filter(d => d.status === 'dueSoon').length;
513
+
514
+ eligibleDonorsElement.textContent = eligibleCount;
515
+ dueSoonDonorsElement.textContent = dueSoonCount;
516
+ }
517
+
518
+ // Pagination functions
519
+ function prevPage() {
520
+ if (currentPage > 1) {
521
+ currentPage--;
522
+ renderDonors();
523
+ }
524
+ }
525
+
526
+ function nextPage() {
527
+ const filteredDonors = getFilteredDonors();
528
+ const totalPages = Math.ceil(filteredDonors.length / donorsPerPage);
529
+
530
+ if (currentPage < totalPages) {
531
+ currentPage++;
532
+ renderDonors();
533
+ }
534
+ }
535
+
536
+ // Get filtered donors based on current filters
537
+ function getFilteredDonors() {
538
+ const searchTerm = searchInput.value.toLowerCase();
539
+ const selectedBloodGroup = bloodGroupFilter.value;
540
+ const selectedEligibility = eligibilityFilter.value;
541
+
542
+ return donors.filter(donor => {
543
+ const matchesSearch = donor.name.toLowerCase().includes(searchTerm) ||
544
+ donor.phone.includes(searchTerm);
545
+ const matchesBloodGroup = selectedBloodGroup === 'all' ||
546
+ donor.bloodGroup === selectedBloodGroup;
547
+ const matchesEligibility = selectedEligibility === 'all' ||
548
+ donor.status === selectedEligibility;
549
+
550
+ return matchesSearch && matchesBloodGroup && matchesEligibility;
551
+ });
552
+ }
553
+
554
+ // Modal functions
555
+ function openAddDonorModal() {
556
+ document.getElementById('addDonorModal').classList.remove('hidden');
557
+ document.getElementById('donorForm').reset();
558
+ document.getElementById('editDonorId').value = '';
559
+ document.getElementById('donorLastDonation').valueAsDate = new Date();
560
+ }
561
+
562
+ function closeAddDonorModal() {
563
+ document.getElementById('addDonorModal').classList.add('hidden');
564
+ }
565
+
566
+ function showReminders() {
567
+ const eligibleNow = donors.filter(d => d.status === 'eligible');
568
+ const dueSoon = donors.filter(d => d.status === 'dueSoon');
569
+
570
+ const eligibleNowList = document.getElementById('eligibleNowList');
571
+ const dueSoonList = document.getElementById('dueSoonList');
572
+
573
+ eligibleNowList.innerHTML = '';
574
+ dueSoonList.innerHTML = '';
575
+
576
+ if (eligibleNow.length === 0) {
577
+ eligibleNowList.innerHTML = '<p class="text-gray-500">No donors eligible for donation right now.</p>';
578
+ } else {
579
+ eligibleNow.forEach(donor => {
580
+ const lastDonationDate = new Date(donor.lastDonation);
581
+ const nextEligibleDate = new Date(lastDonationDate);
582
+ nextEligibleDate.setDate(lastDonationDate.getDate() + 90);
583
+
584
+ const item = document.createElement('div');
585
+ item.className = 'bg-white p-3 rounded-lg shadow-sm flex justify-between items-center';
586
+ item.innerHTML = `
587
+ <div>
588
+ <h5 class="font-medium">${donor.name}</h5>
589
+ <p class="text-sm text-gray-600">${donor.bloodGroup} • Last donated ${formatDate(donor.lastDonation)}</p>
590
+ </div>
591
+ <div class="text-right">
592
+ <span class="text-xs bg-green-100 text-green-800 px-2 py-1 rounded-full">Eligible since ${formatDate(nextEligibleDate.toISOString().split('T')[0])}</span>
593
+ </div>
594
+ `;
595
+ eligibleNowList.appendChild(item);
596
+ });
597
+ }
598
+
599
+ if (dueSoon.length === 0) {
600
+ dueSoonList.innerHTML = '<p class="text-gray-500">No donors due for donation soon.</p>';
601
+ } else {
602
+ dueSoon.forEach(donor => {
603
+ const lastDonationDate = new Date(donor.lastDonation);
604
+ const nextEligibleDate = new Date(lastDonationDate);
605
+ nextEligibleDate.setDate(lastDonationDate.getDate() + 90);
606
+
607
+ const today = new Date();
608
+ const daysUntilEligible = Math.ceil((nextEligibleDate - today) / (1000 * 60 * 60 * 24));
609
+
610
+ const item = document.createElement('div');
611
+ item.className = 'bg-white p-3 rounded-lg shadow-sm flex justify-between items-center';
612
+ item.innerHTML = `
613
+ <div>
614
+ <h5 class="font-medium">${donor.name}</h5>
615
+ <p class="text-sm text-gray-600">${donor.bloodGroup} • Last donated ${formatDate(donor.lastDonation)}</p>
616
+ </div>
617
+ <div class="text-right">
618
+ <span class="text-xs bg-amber-100 text-amber-800 px-2 py-1 rounded-full">Eligible in ${daysUntilEligible} days</span>
619
+ </div>
620
+ `;
621
+ dueSoonList.appendChild(item);
622
+ });
623
+ }
624
+
625
+ document.getElementById('remindersModal').classList.remove('hidden');
626
+ }
627
+
628
+ function closeRemindersModal() {
629
+ document.getElementById('remindersModal').classList.add('hidden');
630
+ }
631
+
632
+ // CRUD operations
633
+ function saveDonor() {
634
+ const id = document.getElementById('editDonorId').value;
635
+ const name = document.getElementById('donorName').value;
636
+ const phone = document.getElementById('donorPhone').value;
637
+ const email = document.getElementById('donorEmail').value;
638
+ const bloodGroup = document.getElementById('donorBloodGroup').value;
639
+ const lastDonation = document.getElementById('donorLastDonation').value;
640
+
641
+ if (id) {
642
+ // Update existing donor
643
+ const index = donors.findIndex(d => d.id == id);
644
+ if (index !== -1) {
645
+ donors[index] = {
646
+ ...donors[index],
647
+ name,
648
+ phone,
649
+ email,
650
+ bloodGroup,
651
+ lastDonation
652
+ };
653
+ }
654
+ } else {
655
+ // Add new donor
656
+ const newId = donors.length > 0 ? Math.max(...donors.map(d => d.id)) + 1 : 1;
657
+ donors.push({
658
+ id: newId,
659
+ name,
660
+ phone,
661
+ email,
662
+ bloodGroup,
663
+ lastDonation,
664
+ status: "notEligible"
665
+ });
666
+ }
667
+
668
+ updateDonorStatuses();
669
+ renderDonors();
670
+ updateStats();
671
+ closeAddDonorModal();
672
+ }
673
+
674
+ function editDonor(id) {
675
+ const donor = donors.find(d => d.id == id);
676
+ if (donor) {
677
+ document.getElementById('editDonorId').value = donor.id;
678
+ document.getElementById('donorName').value = donor.name;
679
+ document.getElementById('donorPhone').value = donor.phone;
680
+ document.getElementById('donorEmail').value = donor.email || '';
681
+ document.getElementById('donorBloodGroup').value = donor.bloodGroup;
682
+ document.getElementById('donorLastDonation').value = donor.lastDonation;
683
+
684
+ document.getElementById('addDonorModal').classList.remove('hidden');
685
+ }
686
+ }
687
+
688
+ function deleteDonor(id) {
689
+ if (confirm('Are you sure you want to delete this donor?')) {
690
+ donors = donors.filter(d => d.id != id);
691
+ updateDonorStatuses();
692
+ renderDonors();
693
+ updateStats();
694
+ }
695
+ }
696
+ </script>
697
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=leekumar/lee" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
698
+ </html>