offerpk3 commited on
Commit
295cbc6
·
verified ·
1 Parent(s): f82577a

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +518 -499
index.html CHANGED
@@ -3,45 +3,45 @@
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Network Device Monitoring Dashboard</title>
7
  <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
10
  <style>
11
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
12
 
 
 
 
 
 
 
 
 
13
  body {
14
  font-family: 'Inter', sans-serif;
15
- background: linear-gradient(135deg, #f5f7fa 0%, #e4e7f1 100%);
16
  min-height: 100vh;
17
  }
18
 
19
  .card {
20
- background: rgba(255, 255, 255, 0.85);
21
- backdrop-filter: blur(10px);
22
  border-radius: 16px;
23
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.05);
24
  transition: all 0.3s ease;
 
25
  }
26
 
27
  .card:hover {
28
- box-shadow: 0 15px 40px rgba(0, 0, 0, 0.1);
29
- transform: translateY(-5px);
30
- }
31
-
32
- .device-table tr {
33
- transition: background 0.2s;
34
- }
35
-
36
- .device-table tr:hover {
37
- background-color: #f8f9fc;
38
  }
39
 
40
  .status-badge {
41
- padding: 4px 10px;
42
  border-radius: 20px;
43
  font-size: 12px;
44
  font-weight: 500;
 
45
  }
46
 
47
  .active-badge {
@@ -54,23 +54,35 @@
54
  color: #d93025;
55
  }
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  .form-input {
58
  border: 1px solid #e2e8f0;
59
  border-radius: 10px;
60
  padding: 12px 16px;
61
  transition: all 0.3s;
 
62
  }
63
 
64
  .form-input:focus {
65
- border-color: #6366f1;
66
- box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.15);
67
  outline: none;
68
  }
69
 
70
- .form-input.error {
71
- border-color: #ef4444;
72
- }
73
-
74
  .toast {
75
  position: fixed;
76
  bottom: 30px;
@@ -92,43 +104,53 @@
92
  }
93
 
94
  .toast.success {
95
- background: linear-gradient(135deg, #0f9d58 0%, #1db954 100%);
96
  }
97
 
98
  .toast.error {
99
- background: linear-gradient(135deg, #d93025 0%, #ea4335 100%);
100
  }
101
 
102
  .toast.info {
103
- background: linear-gradient(135deg, #1a73e8 0%, #4285f4 100%);
104
  }
105
 
106
- .btn-primary {
107
- background: linear-gradient(135deg, #6366f1 0%, #818cf8 100%);
108
- color: white;
109
  border-radius: 10px;
110
  padding: 12px 24px;
111
  font-weight: 500;
112
  transition: all 0.3s;
113
  border: none;
114
  cursor: pointer;
 
 
 
 
 
 
 
 
115
  }
116
 
117
  .btn-primary:hover {
 
118
  transform: translateY(-2px);
119
- box-shadow: 0 10px 20px rgba(99, 102, 241, 0.3);
120
  }
121
 
122
- .btn-primary:active {
123
- transform: translateY(0);
 
124
  }
125
 
126
- .btn-blocked {
127
- background: linear-gradient(135deg, #d93025 0%, #ea4335 100%);
 
128
  }
129
 
130
- .btn-success {
131
- background: linear-gradient(135deg, #0f9d58 0%, #1db954 100%);
 
132
  }
133
 
134
  .action-btn {
@@ -141,25 +163,31 @@
141
  cursor: pointer;
142
  transition: all 0.2s;
143
  color: #4b5563;
 
144
  }
145
 
146
  .action-btn:hover {
147
  transform: scale(1.1);
148
- color: #6366f1;
149
  }
150
 
151
  .edit-btn:hover {
152
  background-color: #eef2ff;
153
  }
154
 
 
 
 
 
 
155
  .block-btn:hover {
156
  background-color: #fef2f2;
157
- color: #ef4444;
158
  }
159
 
160
  .delete-btn:hover {
161
  background-color: #fef2f2;
162
- color: #ef4444;
163
  }
164
 
165
  .mac-address {
@@ -170,11 +198,17 @@
170
  font-weight: 500;
171
  }
172
 
173
- .chart-container {
174
- background: white;
175
- border-radius: 16px;
176
- padding: 20px;
177
- box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
 
 
 
 
 
 
178
  }
179
 
180
  .modal-overlay {
@@ -203,6 +237,8 @@
203
  border-radius: 16px;
204
  width: 90%;
205
  max-width: 500px;
 
 
206
  transform: translateY(20px);
207
  opacity: 0;
208
  transition: all 0.3s;
@@ -213,53 +249,40 @@
213
  opacity: 1;
214
  }
215
 
216
- .pagination-btn {
217
- width: 36px;
218
- height: 36px;
219
- border-radius: 10px;
220
- display: inline-flex;
221
- align-items: center;
222
- justify-content: center;
223
- background: #f1f5f9;
224
- color: #4b5563;
225
- font-weight: 500;
226
- cursor: pointer;
227
- transition: all 0.2s;
228
- }
229
-
230
- .pagination-btn.active {
231
- background: #6366f1;
232
- color: white;
233
- }
234
-
235
- .pagination-btn:hover:not(.active) {
236
- background: #e2e8f0;
237
  }
238
 
239
- .error-text {
240
- display: none;
241
- color: #ef4444;
242
- font-size: 0.875rem;
243
- margin-top: 0.25rem;
244
  }
245
 
246
- .form-group {
247
- margin-bottom: 1.5rem;
 
 
 
 
248
  }
249
 
250
- .validation-icon {
251
- position: absolute;
252
- right: 15px;
253
- top: 40px;
254
- display: none;
255
  }
256
 
257
- .validation-icon.valid {
258
- color: #10B981;
 
259
  }
260
 
261
- .validation-icon.invalid {
262
- color: #EF4444;
 
 
 
263
  }
264
  </style>
265
  </head>
@@ -267,20 +290,77 @@
267
  <!-- Toast Container -->
268
  <div id="toast-container"></div>
269
 
270
- <!-- Modal -->
271
- <div class="modal-overlay" id="block-modal">
272
  <div class="modal-content">
273
  <div class="p-6">
274
  <div class="flex justify-between items-center mb-4">
275
- <h3 class="text-xl font-bold text-gray-800">Confirm Block Action</h3>
276
- <button class="action-btn close-modal">
277
  <i class="fas fa-times"></i>
278
  </button>
279
  </div>
280
- <p class="text-gray-600 mb-6">Are you sure you want to block this device? The device will lose network access until unblocked.</p>
281
- <div class="flex justify-between">
282
- <button class="btn-primary close-modal">Cancel</button>
283
- <button class="btn-primary btn-blocked" id="confirm-block">Block Device</button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
284
  </div>
285
  </div>
286
  </div>
@@ -294,12 +374,6 @@
294
  <p class="text-gray-600 mt-2">Track, manage, and monitor all network devices</p>
295
  </div>
296
  <div class="flex items-center space-x-4 mt-4 md:mt-0">
297
- <div class="relative">
298
- <div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
299
- <i class="fas fa-search text-gray-400"></i>
300
- </div>
301
- <input type="text" id="global-search" placeholder="Search devices..." class="pl-10 pr-4 py-2 form-input w-full md:w-64">
302
- </div>
303
  <div class="flex items-center bg-white rounded-xl shadow py-2 px-4">
304
  <div class="bg-indigo-100 p-2 rounded-lg mr-3">
305
  <i class="fas fa-user text-indigo-600"></i>
@@ -312,151 +386,106 @@
312
  </div>
313
  </div>
314
 
315
- <!-- Stats Cards -->
316
- <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
317
- <div class="card p-6">
318
- <div class="flex justify-between">
319
- <div>
320
- <p class="text-gray-500">Total Devices</p>
321
- <p class="text-2xl font-bold text-gray-800" id="total-devices">0</p>
322
- </div>
323
- <div class="bg-blue-100 p-3 rounded-lg">
324
- <i class="fas fa-network-wired text-blue-600 text-xl"></i>
325
- </div>
326
- </div>
327
- <div class="mt-4">
328
- <div class="flex items-center text-sm text-green-600">
329
- <i class="fas fa-arrow-up mr-1"></i>
330
- <span>12% from last month</span>
331
- </div>
332
- </div>
333
- </div>
334
-
335
- <div class="card p-6">
336
- <div class="flex justify-between">
337
- <div>
338
- <p class="text-gray-500">Active Devices</p>
339
- <p class="text-2xl font-bold text-gray-800" id="active-devices">0</p>
340
- </div>
341
- <div class="bg-green-100 p-3 rounded-lg">
342
- <i class="fas fa-check-circle text-green-600 text-xl"></i>
343
- </div>
344
- </div>
345
- <div class="mt-4">
346
- <div class="flex items-center text-sm text-green-600">
347
- <i class="fas fa-arrow-up mr-1"></i>
348
- <span>8% from last month</span>
349
- </div>
350
- </div>
351
- </div>
352
-
353
- <div class="card p-6">
354
- <div class="flex justify-between">
355
- <div>
356
- <p class="text-gray-500">Blocked Devices</p>
357
- <p class="text-2xl font-bold text-gray-800" id="blocked-devices">0</p>
358
- </div>
359
- <div class="bg-red-100 p-3 rounded-lg">
360
- <i class="fas fa-ban text-red-600 text-xl"></i>
361
- </div>
362
- </div>
363
- <div class="mt-4">
364
- <div class="flex items-center text-sm text-red-600">
365
- <i class="fas fa-arrow-down mr-1"></i>
366
- <span>3% from last month</span>
367
- </div>
368
- </div>
369
- </div>
370
-
371
- <div class="card p-6">
372
- <div class="flex justify-between">
373
- <div>
374
- <p class="text-gray-500">Total Revenue</p>
375
- <p class="text-2xl font-bold text-gray-800" id="total-revenue">₹0</p>
376
- </div>
377
- <div class="bg-purple-100 p-3 rounded-lg">
378
- <i class="fas fa-rupee-sign text-purple-600 text-xl"></i>
379
- </div>
380
- </div>
381
- <div class="mt-4">
382
- <div class="flex items-center text-sm text-green-600">
383
- <i class="fas fa-arrow-up mr-1"></i>
384
- <span>15% from last month</span>
385
- </div>
386
- </div>
387
- </div>
388
- </div>
389
-
390
  <div class="flex flex-col lg:flex-row gap-8">
391
- <!-- Left Column - Form and Charts -->
392
- <div class="lg:w-2/5">
393
  <!-- Add Device Form -->
394
  <div class="card p-6 mb-8">
395
  <h2 class="text-xl font-bold text-gray-800 mb-4">Register New Device</h2>
396
  <form id="device-form" class="space-y-4">
397
- <div class="form-group">
398
  <label class="block text-gray-700 mb-2 font-medium">MAC Address</label>
399
- <div class="relative">
400
- <input type="text" id="mac-address" class="w-full form-input" placeholder="AA:BB:CC:DD:EE:FF">
401
- <i class="fas fa-check-circle validation-icon valid" id="mac-valid-icon"></i>
402
- <i class="fas fa-times-circle validation-icon invalid" id="mac-invalid-icon"></i>
403
- </div>
404
- <div class="error-text" id="mac-error">Valid MAC address required (e.g., AA:BB:CC:DD:EE:FF)</div>
405
  </div>
406
 
407
- <div class="form-group">
408
  <label class="block text-gray-700 mb-2 font-medium">Device Name</label>
409
- <div class="relative">
410
- <input type="text" id="device-name" class="w-full form-input" placeholder="e.g. John's iPhone">
411
- <i class="fas fa-check-circle validation-icon valid" id="device-valid-icon"></i>
412
- <i class="fas fa-times-circle validation-icon invalid" id="device-invalid-icon"></i>
413
- </div>
414
- <div class="error-text" id="device-error">Device name is required</div>
415
  </div>
416
 
417
- <div class="form-group">
418
  <label class="block text-gray-700 mb-2 font-medium">User Name</label>
419
- <div class="relative">
420
- <input type="text" id="user-name" class="w-full form-input" placeholder="e.g. John Doe">
421
- <i class="fas fa-check-circle validation-icon valid" id="user-valid-icon"></i>
422
- <i class="fas fa-times-circle validation-icon invalid" id="user-invalid-icon"></i>
423
- </div>
424
- <div class="error-text" id="user-error">User name is required</div>
425
  </div>
426
 
427
- <div class="form-group">
428
- <label class="block text-gray-700 mb-2 font-medium">Paid Amount (₹)</label>
429
- <div class="relative">
430
- <input type="number" id="paid-amount" class="w-full form-input" placeholder="e.g. 500" min="0">
431
- <i class="fas fa-check-circle validation-icon valid" id="amount-valid-icon"></i>
432
- <i class="fas fa-times-circle validation-icon invalid" id="amount-invalid-icon"></i>
 
 
433
  </div>
434
- <div class="error-text" id="amount-error">Please enter a valid amount (minimum ₹0)</div>
435
  </div>
436
 
437
- <button type="submit" class="btn-primary w-full flex items-center justify-center">
438
  <i class="fas fa-plus-circle mr-2"></i> Add New Device
439
  </button>
440
  </form>
441
  </div>
442
 
443
- <!-- Payment Chart -->
444
  <div class="card p-6">
445
- <h2 class="text-xl font-bold text-gray-800 mb-4">Payment Statistics</h2>
446
- <div class="chart-container">
447
- <canvas id="payment-chart"></canvas>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
448
  </div>
449
  </div>
450
  </div>
451
 
452
  <!-- Right Column - Device Table -->
453
- <div class="lg:w-3/5">
454
  <div class="card p-6">
455
  <div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-6">
456
  <h2 class="text-xl font-bold text-gray-800">Registered Devices</h2>
457
  <div class="flex space-x-3 mt-4 md:mt-0 w-full md:w-auto">
458
- <button id="export-btn" class="btn-primary flex items-center">
459
- <i class="fas fa-file-export mr-2"></i> Export CSV
 
 
 
 
460
  </button>
461
  </div>
462
  </div>
@@ -467,10 +496,11 @@
467
  <tr class="text-left text-gray-500 text-sm font-medium border-b">
468
  <th class="pb-3">#</th>
469
  <th class="pb-3">MAC Address</th>
470
- <th class="pb-3">Device Name</th>
471
- <th class="pb-3">User Name</th>
472
- <th class="pb-3">Paid (₹)</th>
473
- <th class="pb-3">Added Time</th>
 
474
  <th class="pb-3">Status</th>
475
  <th class="pb-3 text-right">Actions</th>
476
  </tr>
@@ -488,22 +518,6 @@
488
  <p class="text-gray-500 mt-1">Add a new device to start monitoring</p>
489
  </div>
490
  </div>
491
-
492
- <div class="mt-6 flex flex-col sm:flex-row justify-between items-center border-t pt-4">
493
- <div class="mb-4 sm:mb-0">
494
- <p class="text-gray-700 font-medium">Total Paid Amount: <span class="text-indigo-600" id="total-paid">₹0</span></p>
495
- </div>
496
- <div class="flex space-x-2">
497
- <div class="pagination-btn">
498
- <i class="fas fa-chevron-left"></i>
499
- </div>
500
- <div class="pagination-btn active">1</div>
501
- <div class="pagination-btn">2</div>
502
- <div class="pagination-btn">
503
- <i class="fas fa-chevron-right"></i>
504
- </div>
505
- </div>
506
- </div>
507
  </div>
508
  </div>
509
  </div>
@@ -512,113 +526,28 @@
512
  <script>
513
  // Initialize device data from localStorage or empty array
514
  let devices = JSON.parse(localStorage.getItem('networkDevices')) || [];
515
- let currentBlockDevice = null;
516
 
517
  // DOM elements
518
  const deviceForm = document.getElementById('device-form');
519
  const deviceTable = document.getElementById('device-table');
520
  const emptyState = document.getElementById('empty-state');
521
- const blockModal = document.getElementById('block-modal');
522
- const searchInput = document.getElementById('global-search');
523
  const exportBtn = document.getElementById('export-btn');
524
 
525
- // Form elements
526
- const macInput = document.getElementById('mac-address');
527
- const deviceNameInput = document.getElementById('device-name');
528
- const userNameInput = document.getElementById('user-name');
529
- const paidAmountInput = document.getElementById('paid-amount');
530
-
531
  // Initialize the app
532
  document.addEventListener('DOMContentLoaded', () => {
533
  renderDeviceTable();
534
  updateSummary();
535
- setupEventListeners();
536
- setupCharts();
537
- setupFormValidation();
538
  });
539
 
540
- // Setup form validation
541
- function setupFormValidation() {
542
- // Add input event listeners for real-time validation
543
- macInput.addEventListener('input', validateMac);
544
- deviceNameInput.addEventListener('input', validateDeviceName);
545
- userNameInput.addEventListener('input', validateUserName);
546
- paidAmountInput.addEventListener('input', validatePaidAmount);
547
- }
548
-
549
- // Validate MAC address
550
- function validateMac() {
551
- const value = macInput.value.trim();
552
- const isValid = value && isValidMAC(value) && !isDuplicateMAC(value);
553
-
554
- updateValidationState(macInput, 'mac', isValid,
555
- isValid ? '' : 'Valid MAC address required (e.g., AA:BB:CC:DD:EE:FF)');
556
-
557
- return isValid;
558
- }
559
-
560
- // Validate device name
561
- function validateDeviceName() {
562
- const value = deviceNameInput.value.trim();
563
- const isValid = value.length > 0;
564
-
565
- updateValidationState(deviceNameInput, 'device', isValid,
566
- isValid ? '' : 'Device name is required');
567
-
568
- return isValid;
569
- }
570
-
571
- // Validate user name
572
- function validateUserName() {
573
- const value = userNameInput.value.trim();
574
- const isValid = value.length > 0;
575
-
576
- updateValidationState(userNameInput, 'user', isValid,
577
- isValid ? '' : 'User name is required');
578
-
579
- return isValid;
580
- }
581
-
582
- // Validate paid amount
583
- function validatePaidAmount() {
584
- const value = parseFloat(paidAmountInput.value);
585
- const isValid = !isNaN(value) && value >= 0;
586
-
587
- updateValidationState(paidAmountInput, 'amount', isValid,
588
- isValid ? '' : 'Please enter a valid amount (minimum ₹0)');
589
-
590
- return isValid;
591
- }
592
-
593
- // Update validation state
594
- function updateValidationState(input, field, isValid, errorMessage) {
595
- const errorElement = document.getElementById(`${field}-error`);
596
- const validIcon = document.getElementById(`${field}-valid-icon`);
597
- const invalidIcon = document.getElementById(`${field}-invalid-icon`);
598
-
599
- if (isValid) {
600
- input.classList.remove('error');
601
- errorElement.style.display = 'none';
602
- validIcon.style.display = 'block';
603
- invalidIcon.style.display = 'none';
604
- } else {
605
- input.classList.add('error');
606
- errorElement.textContent = errorMessage;
607
- errorElement.style.display = 'block';
608
- validIcon.style.display = 'none';
609
- invalidIcon.style.display = 'block';
610
- }
611
- }
612
-
613
- // Setup event listeners
614
- function setupEventListeners() {
615
  deviceForm.addEventListener('submit', addNewDevice);
616
- document.querySelectorAll('.close-modal').forEach(btn => {
617
- btn.addEventListener('click', () => {
618
- blockModal.classList.remove('active');
619
- });
620
- });
621
- document.getElementById('confirm-block').addEventListener('click', blockDevice);
622
  searchInput.addEventListener('input', filterDevices);
623
  exportBtn.addEventListener('click', exportToCSV);
624
  }
@@ -627,21 +556,27 @@
627
  function addNewDevice(e) {
628
  e.preventDefault();
629
 
630
- // Validate all fields
631
- const isMacValid = validateMac();
632
- const isDeviceNameValid = validateDeviceName();
633
- const isUserNameValid = validateUserName();
634
- const isPaidAmountValid = validatePaidAmount();
 
 
 
 
 
 
635
 
636
- if (!isMacValid || !isDeviceNameValid || !isUserNameValid || !isPaidAmountValid) {
637
- showToast('Please fix the errors in the form', 'error');
638
  return;
639
  }
640
 
641
- const macAddress = macInput.value.trim();
642
- const deviceName = deviceNameInput.value.trim();
643
- const userName = userNameInput.value.trim();
644
- const paidAmount = parseFloat(paidAmountInput.value);
645
 
646
  // Create new device object
647
  const newDevice = {
@@ -649,37 +584,46 @@
649
  mac: formatMAC(macAddress),
650
  deviceName,
651
  userName,
652
- paidAmount,
 
653
  addedTime: new Date().toLocaleString(),
654
  status: 'Active'
655
  };
656
 
 
 
 
 
 
 
 
 
 
657
  // Add to devices array and update storage
658
  devices.push(newDevice);
659
  localStorage.setItem('networkDevices', JSON.stringify(devices));
660
 
661
  // Reset form and update UI
662
  deviceForm.reset();
663
- resetValidationIcons();
664
  renderDeviceTable();
665
  updateSummary();
666
- setupCharts();
667
 
668
  // Show success message
669
  showToast('Device added successfully!', 'success');
670
  }
671
 
672
- // Reset validation icons
673
- function resetValidationIcons() {
674
- document.querySelectorAll('.validation-icon').forEach(icon => {
675
- icon.style.display = 'none';
676
- });
677
- document.querySelectorAll('.error-text').forEach(error => {
678
- error.style.display = 'none';
679
- });
680
- document.querySelectorAll('.form-input').forEach(input => {
681
- input.classList.remove('error');
682
- });
 
683
  }
684
 
685
  // Render device table
@@ -694,8 +638,24 @@
694
  emptyState.classList.add('hidden');
695
 
696
  devices.forEach((device, index) => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
697
  const row = document.createElement('tr');
698
- row.className = 'py-3 border-b';
699
  row.innerHTML = `
700
  <td class="py-4">${index + 1}</td>
701
  <td class="py-4">
@@ -703,22 +663,25 @@
703
  </td>
704
  <td class="py-4 font-medium">${device.deviceName}</td>
705
  <td class="py-4">${device.userName}</td>
706
- <td class="py-4 font-medium ${device.paidAmount > 0 ? 'text-green-600' : 'text-red-600'}">₹${device.paidAmount}</td>
707
- <td class="py-4 text-sm text-gray-500">${device.addedTime}</td>
 
 
 
708
  <td class="py-4">
709
- <span class="status-badge ${device.status === 'Active' ? 'active-badge' : 'blocked-badge'}">
710
- ${device.status}
711
  </span>
712
  </td>
713
  <td class="py-4 text-right">
714
- <div class="inline-flex space-x-2">
715
- <button class="action-btn edit-btn" data-id="${device.id}">
716
- <i class="fas fa-edit"></i>
717
  </button>
718
- <button class="action-btn block-btn" data-id="${device.id}">
719
  <i class="fas fa-ban"></i>
720
  </button>
721
- <button class="action-btn delete-btn" data-id="${device.id}">
722
  <i class="fas fa-trash"></i>
723
  </button>
724
  </div>
@@ -728,12 +691,12 @@
728
  });
729
 
730
  // Attach event listeners to action buttons
731
- document.querySelectorAll('.edit-btn').forEach(btn => {
732
- btn.addEventListener('click', () => editDevice(btn.dataset.id));
733
  });
734
 
735
  document.querySelectorAll('.block-btn').forEach(btn => {
736
- btn.addEventListener('click', () => openBlockModal(btn.dataset.id));
737
  });
738
 
739
  document.querySelectorAll('.delete-btn').forEach(btn => {
@@ -743,104 +706,145 @@
743
 
744
  // Update summary stats
745
  function updateSummary() {
746
- const totalDevices = devices.length;
747
- const activeDevices = devices.filter(d => d.status === 'Active').length;
748
- const blockedDevices = devices.filter(d => d.status === 'Blocked').length;
749
- const totalRevenue = devices.reduce((sum, device) => sum + device.paidAmount, 0);
 
 
 
 
 
 
 
 
 
750
 
751
- document.getElementById('total-devices').textContent = totalDevices;
752
- document.getElementById('active-devices').textContent = activeDevices;
753
- document.getElementById('blocked-devices').textContent = blockedDevices;
 
 
 
 
 
 
 
 
 
 
 
754
  document.getElementById('total-revenue').textContent = `₹${totalRevenue.toFixed(2)}`;
755
- document.getElementById('total-paid').textContent = `₹${totalRevenue.toFixed(2)}`;
 
 
 
 
 
 
 
 
 
 
 
 
 
756
  }
757
 
758
- // Setup charts
759
- function setupCharts() {
760
- const ctx = document.getElementById('payment-chart').getContext('2d');
 
 
 
 
 
 
 
 
761
 
762
- // Payment status data
763
- const paidDevices = devices.filter(d => d.paidAmount > 0).length;
764
- const unpaidDevices = devices.length - paidDevices;
 
 
 
 
 
 
765
 
766
- // Device status data
767
- const activeDevices = devices.filter(d => d.status === 'Active').length;
768
- const blockedDevices = devices.filter(d => d.status === 'Blocked').length;
769
 
770
- // Destroy existing chart if it exists
771
- if (window.paymentChart) {
772
- window.paymentChart.destroy();
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
773
  }
774
 
775
- window.paymentChart = new Chart(ctx, {
776
- type: 'doughnut',
777
- data: {
778
- labels: ['Paid Devices', 'Unpaid Devices'],
779
- datasets: [{
780
- data: [paidDevices, unpaidDevices],
781
- backgroundColor: ['#10B981', '#EF4444'],
782
- borderWidth: 0,
783
- }]
784
- },
785
- options: {
786
- responsive: true,
787
- maintainAspectRatio: false,
788
- plugins: {
789
- legend: {
790
- position: 'bottom',
791
- labels: {
792
- usePointStyle: true,
793
- padding: 20
794
- }
795
- }
796
- },
797
- cutout: '65%'
798
- }
799
- });
800
  }
801
 
802
- // Edit device
803
- function editDevice(deviceId) {
804
- const device = devices.find(d => d.id == deviceId);
805
- if (!device) return;
806
-
807
- // For simplicity, we'll use prompts
808
- const newDeviceName = prompt('Enter new device name:', device.deviceName);
809
- if (!newDeviceName) return;
 
810
 
811
- const newUserName = prompt('Enter new user name:', device.userName);
812
- if (!newUserName) return;
813
 
814
- const newPaidAmount = parseFloat(prompt('Enter new paid amount:', device.paidAmount));
815
- if (isNaN(newPaidAmount)) {
816
- showToast('Invalid amount entered', 'error');
817
  return;
818
  }
819
 
820
- device.deviceName = newDeviceName;
821
- device.userName = newUserName;
822
- device.paidAmount = newPaidAmount;
 
 
 
823
 
824
  // Update storage and UI
825
  localStorage.setItem('networkDevices', JSON.stringify(devices));
 
826
  renderDeviceTable();
827
  updateSummary();
828
- setupCharts();
829
 
830
- showToast('Device updated successfully!', 'success');
831
- }
832
-
833
- // Open block modal
834
- function openBlockModal(deviceId) {
835
- currentBlockDevice = deviceId;
836
- blockModal.classList.add('active');
837
  }
838
 
839
  // Block device
840
- function blockDevice() {
841
- if (!currentBlockDevice) return;
842
-
843
- const device = devices.find(d => d.id == currentBlockDevice);
844
  if (!device) return;
845
 
846
  device.status = device.status === 'Active' ? 'Blocked' : 'Active';
@@ -848,10 +852,6 @@
848
  // Update storage and UI
849
  localStorage.setItem('networkDevices', JSON.stringify(devices));
850
  renderDeviceTable();
851
- updateSummary();
852
-
853
- // Close modal
854
- blockModal.classList.remove('active');
855
 
856
  // Show notification
857
  const action = device.status === 'Blocked' ? 'blocked' : 'unblocked';
@@ -868,7 +868,6 @@
868
  localStorage.setItem('networkDevices', JSON.stringify(devices));
869
  renderDeviceTable();
870
  updateSummary();
871
- setupCharts();
872
 
873
  showToast('Device deleted successfully!', 'success');
874
  }
@@ -903,8 +902,24 @@
903
  emptyState.classList.add('hidden');
904
 
905
  filteredDevices.forEach((device, index) => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
906
  const row = document.createElement('tr');
907
- row.className = 'py-3 border-b';
908
  row.innerHTML = `
909
  <td class="py-4">${index + 1}</td>
910
  <td class="py-4">
@@ -912,22 +927,25 @@
912
  </td>
913
  <td class="py-4 font-medium">${device.deviceName}</td>
914
  <td class="py-4">${device.userName}</td>
915
- <td class="py-4 font-medium ${device.paidAmount > 0 ? 'text-green-600' : 'text-red-600'}">₹${device.paidAmount}</td>
916
- <td class="py-4 text-sm text-gray-500">${device.addedTime}</td>
 
 
 
917
  <td class="py-4">
918
- <span class="status-badge ${device.status === 'Active' ? 'active-badge' : 'blocked-badge'}">
919
- ${device.status}
920
  </span>
921
  </td>
922
  <td class="py-4 text-right">
923
- <div class="inline-flex space-x-2">
924
- <button class="action-btn edit-btn" data-id="${device.id}">
925
- <i class="fas fa-edit"></i>
926
  </button>
927
- <button class="action-btn block-btn" data-id="${device.id}">
928
  <i class="fas fa-ban"></i>
929
  </button>
930
- <button class="action-btn delete-btn" data-id="${device.id}">
931
  <i class="fas fa-trash"></i>
932
  </button>
933
  </div>
@@ -937,12 +955,12 @@
937
  });
938
 
939
  // Reattach event listeners
940
- document.querySelectorAll('.edit-btn').forEach(btn => {
941
- btn.addEventListener('click', () => editDevice(btn.dataset.id));
942
  });
943
 
944
  document.querySelectorAll('.block-btn').forEach(btn => {
945
- btn.addEventListener('click', () => openBlockModal(btn.dataset.id));
946
  });
947
 
948
  document.querySelectorAll('.delete-btn').forEach(btn => {
@@ -958,11 +976,14 @@
958
  }
959
 
960
  // Create CSV header
961
- let csvContent = "MAC Address,Device Name,User Name,Paid Amount,Status,Added Time\n";
962
 
963
  // Add each device as a row
964
  devices.forEach(device => {
965
- csvContent += `"${device.mac}","${device.deviceName}","${device.userName}",${device.paidAmount},"${device.status}","${device.addedTime}"\n`;
 
 
 
966
  });
967
 
968
  // Create download link
@@ -979,22 +1000,7 @@
979
  showToast('CSV exported successfully!', 'success');
980
  }
981
 
982
- // Helper functions
983
- function isValidMAC(mac) {
984
- // Robust MAC validation
985
- return /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/.test(mac);
986
- }
987
-
988
- function formatMAC(mac) {
989
- // Format MAC address consistently
990
- return mac.replace(/-/g, ':').toUpperCase();
991
- }
992
-
993
- function isDuplicateMAC(mac) {
994
- const formattedMAC = formatMAC(mac);
995
- return devices.some(d => d.mac === formattedMAC);
996
- }
997
-
998
  function showToast(message, type = 'info') {
999
  const container = document.getElementById('toast-container');
1000
  const toast = document.createElement('div');
@@ -1016,39 +1022,52 @@
1016
  }, 3000);
1017
  }
1018
 
1019
- // Initialize sample data if empty
1020
- if (devices.length === 0) {
1021
- devices = [
1022
- {
1023
- id: 1,
1024
- mac: 'AA:BB:CC:DD:EE:FF',
1025
- deviceName: 'John\'s iPhone',
1026
- userName: 'John Smith',
1027
- paidAmount: 500,
1028
- addedTime: new Date().toLocaleString(),
1029
- status: 'Active'
1030
- },
1031
- {
1032
- id: 2,
1033
- mac: '11:22:33:44:55:66',
1034
- deviceName: 'Office Printer',
1035
- userName: 'Office Admin',
1036
- paidAmount: 0,
1037
- addedTime: new Date().toLocaleString(),
1038
- status: 'Active'
1039
- },
1040
- {
1041
- id: 3,
1042
- mac: 'FF:EE:DD:CC:BB:AA',
1043
- deviceName: 'Sarah\'s Laptop',
1044
- userName: 'Sarah Johnson',
1045
- paidAmount: 750,
1046
- addedTime: new Date().toLocaleString(),
1047
- status: 'Blocked'
1048
- }
1049
- ];
1050
- localStorage.setItem('networkDevices', JSON.stringify(devices));
 
 
 
 
 
 
 
 
 
 
1051
  }
 
 
 
1052
  </script>
1053
  </body>
1054
  </html>
 
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Network Device Monitoring with Loan System</title>
7
  <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
8
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
  <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
10
  <style>
11
  @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
12
 
13
+ :root {
14
+ --primary: #4f46e5;
15
+ --success: #10b981;
16
+ --warning: #f59e0b;
17
+ --danger: #ef4444;
18
+ --info: #3b82f6;
19
+ }
20
+
21
  body {
22
  font-family: 'Inter', sans-serif;
23
+ background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
24
  min-height: 100vh;
25
  }
26
 
27
  .card {
28
+ background: white;
 
29
  border-radius: 16px;
30
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.05);
31
  transition: all 0.3s ease;
32
+ overflow: hidden;
33
  }
34
 
35
  .card:hover {
36
+ box-shadow: 0 15px 35px rgba(0, 0, 0, 0.1);
 
 
 
 
 
 
 
 
 
37
  }
38
 
39
  .status-badge {
40
+ padding: 4px 12px;
41
  border-radius: 20px;
42
  font-size: 12px;
43
  font-weight: 500;
44
+ display: inline-block;
45
  }
46
 
47
  .active-badge {
 
54
  color: #d93025;
55
  }
56
 
57
+ .paid-badge {
58
+ background-color: #e6f4ea;
59
+ color: #0f9d58;
60
+ }
61
+
62
+ .partial-badge {
63
+ background-color: #fef3c7;
64
+ color: #b45309;
65
+ }
66
+
67
+ .unpaid-badge {
68
+ background-color: #fce8e6;
69
+ color: #d93025;
70
+ }
71
+
72
  .form-input {
73
  border: 1px solid #e2e8f0;
74
  border-radius: 10px;
75
  padding: 12px 16px;
76
  transition: all 0.3s;
77
+ width: 100%;
78
  }
79
 
80
  .form-input:focus {
81
+ border-color: var(--primary);
82
+ box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.15);
83
  outline: none;
84
  }
85
 
 
 
 
 
86
  .toast {
87
  position: fixed;
88
  bottom: 30px;
 
104
  }
105
 
106
  .toast.success {
107
+ background: var(--success);
108
  }
109
 
110
  .toast.error {
111
+ background: var(--danger);
112
  }
113
 
114
  .toast.info {
115
+ background: var(--info);
116
  }
117
 
118
+ .btn {
 
 
119
  border-radius: 10px;
120
  padding: 12px 24px;
121
  font-weight: 500;
122
  transition: all 0.3s;
123
  border: none;
124
  cursor: pointer;
125
+ display: inline-flex;
126
+ align-items: center;
127
+ justify-content: center;
128
+ }
129
+
130
+ .btn-primary {
131
+ background: var(--primary);
132
+ color: white;
133
  }
134
 
135
  .btn-primary:hover {
136
+ background: #4338ca;
137
  transform: translateY(-2px);
138
+ box-shadow: 0 10px 20px rgba(79, 70, 229, 0.3);
139
  }
140
 
141
+ .btn-success {
142
+ background: var(--success);
143
+ color: white;
144
  }
145
 
146
+ .btn-warning {
147
+ background: var(--warning);
148
+ color: white;
149
  }
150
 
151
+ .btn-danger {
152
+ background: var(--danger);
153
+ color: white;
154
  }
155
 
156
  .action-btn {
 
163
  cursor: pointer;
164
  transition: all 0.2s;
165
  color: #4b5563;
166
+ background: #f1f5f9;
167
  }
168
 
169
  .action-btn:hover {
170
  transform: scale(1.1);
171
+ color: var(--primary);
172
  }
173
 
174
  .edit-btn:hover {
175
  background-color: #eef2ff;
176
  }
177
 
178
+ .payment-btn:hover {
179
+ background-color: #ecfdf5;
180
+ color: var(--success);
181
+ }
182
+
183
  .block-btn:hover {
184
  background-color: #fef2f2;
185
+ color: var(--danger);
186
  }
187
 
188
  .delete-btn:hover {
189
  background-color: #fef2f2;
190
+ color: var(--danger);
191
  }
192
 
193
  .mac-address {
 
198
  font-weight: 500;
199
  }
200
 
201
+ .progress-bar {
202
+ height: 8px;
203
+ border-radius: 4px;
204
+ background: #e2e8f0;
205
+ overflow: hidden;
206
+ }
207
+
208
+ .progress-fill {
209
+ height: 100%;
210
+ border-radius: 4px;
211
+ transition: width 0.5s ease;
212
  }
213
 
214
  .modal-overlay {
 
237
  border-radius: 16px;
238
  width: 90%;
239
  max-width: 500px;
240
+ max-height: 90vh;
241
+ overflow-y: auto;
242
  transform: translateY(20px);
243
  opacity: 0;
244
  transition: all 0.3s;
 
249
  opacity: 1;
250
  }
251
 
252
+ .payment-item {
253
+ display: flex;
254
+ justify-content: space-between;
255
+ padding: 12px 0;
256
+ border-bottom: 1px solid #e2e8f0;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
257
  }
258
 
259
+ .payment-item:last-child {
260
+ border-bottom: none;
 
 
 
261
  }
262
 
263
+ .tab-button {
264
+ padding: 10px 20px;
265
+ border-radius: 8px;
266
+ cursor: pointer;
267
+ transition: all 0.3s;
268
+ font-weight: 500;
269
  }
270
 
271
+ .tab-button.active {
272
+ background: var(--primary);
273
+ color: white;
 
 
274
  }
275
 
276
+ .amount-display {
277
+ font-size: 1.25rem;
278
+ font-weight: 700;
279
  }
280
 
281
+ .payment-status {
282
+ padding: 4px 10px;
283
+ border-radius: 20px;
284
+ font-size: 12px;
285
+ font-weight: 500;
286
  }
287
  </style>
288
  </head>
 
290
  <!-- Toast Container -->
291
  <div id="toast-container"></div>
292
 
293
+ <!-- Payment Modal -->
294
+ <div class="modal-overlay" id="payment-modal">
295
  <div class="modal-content">
296
  <div class="p-6">
297
  <div class="flex justify-between items-center mb-4">
298
+ <h3 class="text-xl font-bold text-gray-800">Payment Management</h3>
299
+ <button class="action-btn close-payment-modal">
300
  <i class="fas fa-times"></i>
301
  </button>
302
  </div>
303
+
304
+ <div class="mb-6">
305
+ <h4 class="font-medium text-gray-700 mb-2">Device Information</h4>
306
+ <div class="grid grid-cols-2 gap-4 mb-4">
307
+ <div>
308
+ <p class="text-gray-500 text-sm">MAC Address</p>
309
+ <p id="payment-mac" class="font-medium">-</p>
310
+ </div>
311
+ <div>
312
+ <p class="text-gray-500 text-sm">Device Name</p>
313
+ <p id="payment-device" class="font-medium">-</p>
314
+ </div>
315
+ <div>
316
+ <p class="text-gray-500 text-sm">User Name</p>
317
+ <p id="payment-user" class="font-medium">-</p>
318
+ </div>
319
+ <div>
320
+ <p class="text-gray-500 text-sm">Status</p>
321
+ <p id="payment-status" class="font-medium">-</p>
322
+ </div>
323
+ </div>
324
+ </div>
325
+
326
+ <div class="grid grid-cols-3 gap-4 mb-6">
327
+ <div class="bg-gray-50 p-4 rounded-lg">
328
+ <p class="text-gray-500 text-sm">Total Amount</p>
329
+ <p id="payment-total" class="amount-display">₹0</p>
330
+ </div>
331
+ <div class="bg-green-50 p-4 rounded-lg">
332
+ <p class="text-green-500 text-sm">Paid Amount</p>
333
+ <p id="payment-paid" class="amount-display text-green-600">₹0</p>
334
+ </div>
335
+ <div class="bg-blue-50 p-4 rounded-lg">
336
+ <p class="text-blue-500 text-sm">Remaining</p>
337
+ <p id="payment-remaining" class="amount-display text-blue-600">₹0</p>
338
+ </div>
339
+ </div>
340
+
341
+ <div class="progress-bar mb-6">
342
+ <div id="payment-progress" class="progress-fill bg-green-500" style="width: 0%"></div>
343
+ </div>
344
+
345
+ <div class="mb-6">
346
+ <h4 class="font-medium text-gray-700 mb-4">Payment History</h4>
347
+ <div id="payment-history" class="space-y-2">
348
+ <!-- Payment history will be populated here -->
349
+ </div>
350
+ <div id="no-payments" class="py-4 text-center text-gray-500">
351
+ <i class="fas fa-inbox text-3xl mb-2"></i>
352
+ <p>No payment history found</p>
353
+ </div>
354
+ </div>
355
+
356
+ <div>
357
+ <h4 class="font-medium text-gray-700 mb-4">Add New Payment</h4>
358
+ <div class="flex space-x-3">
359
+ <input type="number" id="new-payment-amount" class="form-input flex-1" placeholder="Amount" min="1">
360
+ <button id="add-payment-btn" class="btn btn-success">
361
+ <i class="fas fa-plus-circle mr-2"></i> Add
362
+ </button>
363
+ </div>
364
  </div>
365
  </div>
366
  </div>
 
374
  <p class="text-gray-600 mt-2">Track, manage, and monitor all network devices</p>
375
  </div>
376
  <div class="flex items-center space-x-4 mt-4 md:mt-0">
 
 
 
 
 
 
377
  <div class="flex items-center bg-white rounded-xl shadow py-2 px-4">
378
  <div class="bg-indigo-100 p-2 rounded-lg mr-3">
379
  <i class="fas fa-user text-indigo-600"></i>
 
386
  </div>
387
  </div>
388
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
389
  <div class="flex flex-col lg:flex-row gap-8">
390
+ <!-- Left Column - Form and Stats -->
391
+ <div class="lg:w-1/3">
392
  <!-- Add Device Form -->
393
  <div class="card p-6 mb-8">
394
  <h2 class="text-xl font-bold text-gray-800 mb-4">Register New Device</h2>
395
  <form id="device-form" class="space-y-4">
396
+ <div>
397
  <label class="block text-gray-700 mb-2 font-medium">MAC Address</label>
398
+ <input type="text" id="mac-address" class="form-input" placeholder="Any format accepted">
399
+ <p class="text-gray-500 text-sm mt-1">We'll automatically format your MAC address</p>
 
 
 
 
400
  </div>
401
 
402
+ <div>
403
  <label class="block text-gray-700 mb-2 font-medium">Device Name</label>
404
+ <input type="text" id="device-name" class="form-input" placeholder="e.g. John's iPhone">
 
 
 
 
 
405
  </div>
406
 
407
+ <div>
408
  <label class="block text-gray-700 mb-2 font-medium">User Name</label>
409
+ <input type="text" id="user-name" class="form-input" placeholder="e.g. John Doe">
 
 
 
 
 
410
  </div>
411
 
412
+ <div class="grid grid-cols-2 gap-4">
413
+ <div>
414
+ <label class="block text-gray-700 mb-2 font-medium">Total Amount (₹)</label>
415
+ <input type="number" id="total-amount" class="form-input" placeholder="e.g. 3000" min="1">
416
+ </div>
417
+ <div>
418
+ <label class="block text-gray-700 mb-2 font-medium">Initial Payment (₹)</label>
419
+ <input type="number" id="initial-payment" class="form-input" placeholder="e.g. 1000" min="0">
420
  </div>
 
421
  </div>
422
 
423
+ <button type="submit" class="btn btn-primary w-full">
424
  <i class="fas fa-plus-circle mr-2"></i> Add New Device
425
  </button>
426
  </form>
427
  </div>
428
 
429
+ <!-- Financial Summary -->
430
  <div class="card p-6">
431
+ <h2 class="text-xl font-bold text-gray-800 mb-4">Financial Summary</h2>
432
+ <div class="space-y-4">
433
+ <div>
434
+ <div class="flex justify-between mb-1">
435
+ <span class="text-gray-600">Total Revenue</span>
436
+ <span class="font-medium" id="total-revenue">₹0</span>
437
+ </div>
438
+ <div class="progress-bar">
439
+ <div class="progress-fill bg-green-500" id="revenue-bar" style="width: 0%"></div>
440
+ </div>
441
+ </div>
442
+
443
+ <div>
444
+ <div class="flex justify-between mb-1">
445
+ <span class="text-gray-600">Outstanding Balance</span>
446
+ <span class="font-medium" id="outstanding-balance">₹0</span>
447
+ </div>
448
+ <div class="progress-bar">
449
+ <div class="progress-fill bg-yellow-500" id="outstanding-bar" style="width: 0%"></div>
450
+ </div>
451
+ </div>
452
+
453
+ <div>
454
+ <div class="flex justify-between mb-1">
455
+ <span class="text-gray-600">Average Payment</span>
456
+ <span class="font-medium" id="avg-payment">₹0</span>
457
+ </div>
458
+ <div class="progress-bar">
459
+ <div class="progress-fill bg-blue-500" id="avg-payment-bar" style="width: 0%"></div>
460
+ </div>
461
+ </div>
462
+ </div>
463
+
464
+ <div class="mt-6 grid grid-cols-2 gap-4">
465
+ <div class="bg-green-50 p-4 rounded-lg">
466
+ <p class="text-green-500 text-sm">Fully Paid</p>
467
+ <p class="text-xl font-bold" id="fully-paid-count">0</p>
468
+ </div>
469
+ <div class="bg-yellow-50 p-4 rounded-lg">
470
+ <p class="text-yellow-500 text-sm">Partial Paid</p>
471
+ <p class="text-xl font-bold" id="partial-paid-count">0</p>
472
+ </div>
473
  </div>
474
  </div>
475
  </div>
476
 
477
  <!-- Right Column - Device Table -->
478
+ <div class="lg:w-2/3">
479
  <div class="card p-6">
480
  <div class="flex flex-col md:flex-row justify-between items-start md:items-center mb-6">
481
  <h2 class="text-xl font-bold text-gray-800">Registered Devices</h2>
482
  <div class="flex space-x-3 mt-4 md:mt-0 w-full md:w-auto">
483
+ <div class="relative w-full md:w-64">
484
+ <input type="text" id="search-input" class="form-input pl-10" placeholder="Search devices...">
485
+ <i class="fas fa-search absolute left-3 top-3 text-gray-400"></i>
486
+ </div>
487
+ <button id="export-btn" class="btn btn-primary">
488
+ <i class="fas fa-file-export mr-2"></i> Export
489
  </button>
490
  </div>
491
  </div>
 
496
  <tr class="text-left text-gray-500 text-sm font-medium border-b">
497
  <th class="pb-3">#</th>
498
  <th class="pb-3">MAC Address</th>
499
+ <th class="pb-3">Device</th>
500
+ <th class="pb-3">User</th>
501
+ <th class="pb-3">Total</th>
502
+ <th class="pb-3">Paid</th>
503
+ <th class="pb-3">Remaining</th>
504
  <th class="pb-3">Status</th>
505
  <th class="pb-3 text-right">Actions</th>
506
  </tr>
 
518
  <p class="text-gray-500 mt-1">Add a new device to start monitoring</p>
519
  </div>
520
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
521
  </div>
522
  </div>
523
  </div>
 
526
  <script>
527
  // Initialize device data from localStorage or empty array
528
  let devices = JSON.parse(localStorage.getItem('networkDevices')) || [];
529
+ let currentPaymentDevice = null;
530
 
531
  // DOM elements
532
  const deviceForm = document.getElementById('device-form');
533
  const deviceTable = document.getElementById('device-table');
534
  const emptyState = document.getElementById('empty-state');
535
+ const paymentModal = document.getElementById('payment-modal');
536
+ const searchInput = document.getElementById('search-input');
537
  const exportBtn = document.getElementById('export-btn');
538
 
 
 
 
 
 
 
539
  // Initialize the app
540
  document.addEventListener('DOMContentLoaded', () => {
541
  renderDeviceTable();
542
  updateSummary();
543
+ attachEventListeners();
 
 
544
  });
545
 
546
+ // Event listeners
547
+ function attachEventListeners() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
548
  deviceForm.addEventListener('submit', addNewDevice);
549
+ document.querySelector('.close-payment-modal').addEventListener('click', closePaymentModal);
550
+ document.getElementById('add-payment-btn').addEventListener('click', addNewPayment);
 
 
 
 
551
  searchInput.addEventListener('input', filterDevices);
552
  exportBtn.addEventListener('click', exportToCSV);
553
  }
 
556
  function addNewDevice(e) {
557
  e.preventDefault();
558
 
559
+ const macAddress = document.getElementById('mac-address').value.trim();
560
+ const deviceName = document.getElementById('device-name').value.trim();
561
+ const userName = document.getElementById('user-name').value.trim();
562
+ const totalAmount = parseFloat(document.getElementById('total-amount').value);
563
+ const initialPayment = parseFloat(document.getElementById('initial-payment').value) || 0;
564
+
565
+ // Validation
566
+ if (!macAddress || !deviceName || !userName || isNaN(totalAmount)) {
567
+ showToast('Please fill all required fields', 'error');
568
+ return;
569
+ }
570
 
571
+ if (totalAmount <= 0) {
572
+ showToast('Total amount must be greater than 0', 'error');
573
  return;
574
  }
575
 
576
+ if (initialPayment < 0) {
577
+ showToast('Initial payment cannot be negative', 'error');
578
+ return;
579
+ }
580
 
581
  // Create new device object
582
  const newDevice = {
 
584
  mac: formatMAC(macAddress),
585
  deviceName,
586
  userName,
587
+ totalAmount,
588
+ payments: [],
589
  addedTime: new Date().toLocaleString(),
590
  status: 'Active'
591
  };
592
 
593
+ // Add initial payment if provided
594
+ if (initialPayment > 0) {
595
+ newDevice.payments.push({
596
+ id: Date.now(),
597
+ date: new Date().toLocaleString(),
598
+ amount: initialPayment
599
+ });
600
+ }
601
+
602
  // Add to devices array and update storage
603
  devices.push(newDevice);
604
  localStorage.setItem('networkDevices', JSON.stringify(devices));
605
 
606
  // Reset form and update UI
607
  deviceForm.reset();
 
608
  renderDeviceTable();
609
  updateSummary();
 
610
 
611
  // Show success message
612
  showToast('Device added successfully!', 'success');
613
  }
614
 
615
+ // Format MAC address (flexible)
616
+ function formatMAC(mac) {
617
+ // Remove any non-alphanumeric characters
618
+ let cleanMac = mac.replace(/[^0-9A-Fa-f]/g, '');
619
+
620
+ // If we have exactly 12 characters, format with colons
621
+ if (cleanMac.length === 12) {
622
+ return cleanMac.match(/.{1,2}/g).join(':').toUpperCase();
623
+ }
624
+
625
+ // Otherwise, return the cleaned version in uppercase
626
+ return cleanMac.toUpperCase();
627
  }
628
 
629
  // Render device table
 
638
  emptyState.classList.add('hidden');
639
 
640
  devices.forEach((device, index) => {
641
+ // Calculate payment totals
642
+ const paidAmount = device.payments.reduce((sum, payment) => sum + payment.amount, 0);
643
+ const remainingAmount = device.totalAmount - paidAmount;
644
+
645
+ // Determine payment status
646
+ let paymentStatus = 'Unpaid';
647
+ let statusClass = 'unpaid-badge';
648
+
649
+ if (paidAmount >= device.totalAmount) {
650
+ paymentStatus = 'Fully Paid';
651
+ statusClass = 'paid-badge';
652
+ } else if (paidAmount > 0) {
653
+ paymentStatus = 'Partial Paid';
654
+ statusClass = 'partial-badge';
655
+ }
656
+
657
  const row = document.createElement('tr');
658
+ row.className = 'border-b';
659
  row.innerHTML = `
660
  <td class="py-4">${index + 1}</td>
661
  <td class="py-4">
 
663
  </td>
664
  <td class="py-4 font-medium">${device.deviceName}</td>
665
  <td class="py-4">${device.userName}</td>
666
+ <td class="py-4 font-bold">₹${device.totalAmount.toFixed(2)}</td>
667
+ <td class="py-4 font-medium text-green-600">₹${paidAmount.toFixed(2)}</td>
668
+ <td class="py-4 font-medium ${remainingAmount > 0 ? 'text-red-600' : 'text-green-600'}">
669
+ ₹${Math.abs(remainingAmount).toFixed(2)}
670
+ </td>
671
  <td class="py-4">
672
+ <span class="payment-status ${statusClass}">
673
+ ${paymentStatus}
674
  </span>
675
  </td>
676
  <td class="py-4 text-right">
677
+ <div class="inline-flex space-x-1">
678
+ <button class="action-btn payment-btn" data-id="${device.id}" title="Manage Payments">
679
+ <i class="fas fa-rupee-sign"></i>
680
  </button>
681
+ <button class="action-btn block-btn" data-id="${device.id}" title="Block Device">
682
  <i class="fas fa-ban"></i>
683
  </button>
684
+ <button class="action-btn delete-btn" data-id="${device.id}" title="Delete Device">
685
  <i class="fas fa-trash"></i>
686
  </button>
687
  </div>
 
691
  });
692
 
693
  // Attach event listeners to action buttons
694
+ document.querySelectorAll('.payment-btn').forEach(btn => {
695
+ btn.addEventListener('click', () => openPaymentModal(btn.dataset.id));
696
  });
697
 
698
  document.querySelectorAll('.block-btn').forEach(btn => {
699
+ btn.addEventListener('click', () => blockDevice(btn.dataset.id));
700
  });
701
 
702
  document.querySelectorAll('.delete-btn').forEach(btn => {
 
706
 
707
  // Update summary stats
708
  function updateSummary() {
709
+ // Payment calculations
710
+ const totalRevenue = devices.reduce((sum, device) => {
711
+ return sum + device.payments.reduce((deviceSum, payment) => deviceSum + payment.amount, 0);
712
+ }, 0);
713
+
714
+ const totalOutstanding = devices.reduce((sum, device) => {
715
+ const paidAmount = device.payments.reduce((deviceSum, payment) => deviceSum + payment.amount, 0);
716
+ return sum + (device.totalAmount - paidAmount);
717
+ }, 0);
718
+
719
+ // Payment status counts
720
+ let fullyPaid = 0;
721
+ let partialPaid = 0;
722
 
723
+ devices.forEach(device => {
724
+ const paidAmount = device.payments.reduce((sum, payment) => sum + payment.amount, 0);
725
+ if (paidAmount >= device.totalAmount) {
726
+ fullyPaid++;
727
+ } else if (paidAmount > 0) {
728
+ partialPaid++;
729
+ }
730
+ });
731
+
732
+ // Average payment
733
+ const paymentCount = devices.reduce((count, device) => count + device.payments.length, 0);
734
+ const avgPayment = paymentCount > 0 ? totalRevenue / paymentCount : 0;
735
+
736
+ // Update UI
737
  document.getElementById('total-revenue').textContent = `₹${totalRevenue.toFixed(2)}`;
738
+ document.getElementById('outstanding-balance').textContent = `₹${Math.max(0, totalOutstanding).toFixed(2)}`;
739
+ document.getElementById('avg-payment').textContent = `₹${avgPayment.toFixed(2)}`;
740
+ document.getElementById('fully-paid-count').textContent = fullyPaid;
741
+ document.getElementById('partial-paid-count').textContent = partialPaid;
742
+
743
+ // Update progress bars
744
+ const maxRevenue = Math.max(10000, totalRevenue);
745
+ document.getElementById('revenue-bar').style.width = `${(totalRevenue / maxRevenue) * 100}%`;
746
+
747
+ const maxOutstanding = Math.max(10000, totalOutstanding);
748
+ document.getElementById('outstanding-bar').style.width = `${(totalOutstanding / maxOutstanding) * 100}%`;
749
+
750
+ const maxAvg = Math.max(1000, avgPayment);
751
+ document.getElementById('avg-payment-bar').style.width = `${(avgPayment / maxAvg) * 100}%`;
752
  }
753
 
754
+ // Open payment modal
755
+ function openPaymentModal(deviceId) {
756
+ const device = devices.find(d => d.id == deviceId);
757
+ if (!device) return;
758
+
759
+ currentPaymentDevice = device;
760
+
761
+ // Calculate payment totals
762
+ const paidAmount = device.payments.reduce((sum, payment) => sum + payment.amount, 0);
763
+ const remainingAmount = device.totalAmount - paidAmount;
764
+ const paymentPercentage = (paidAmount / device.totalAmount) * 100;
765
 
766
+ // Update modal content
767
+ document.getElementById('payment-mac').textContent = device.mac;
768
+ document.getElementById('payment-device').textContent = device.deviceName;
769
+ document.getElementById('payment-user').textContent = device.userName;
770
+ document.getElementById('payment-status').textContent = device.status;
771
+ document.getElementById('payment-total').textContent = `₹${device.totalAmount.toFixed(2)}`;
772
+ document.getElementById('payment-paid').textContent = `₹${paidAmount.toFixed(2)}`;
773
+ document.getElementById('payment-remaining').textContent = `₹${Math.abs(remainingAmount).toFixed(2)}`;
774
+ document.getElementById('payment-progress').style.width = `${paymentPercentage}%`;
775
 
776
+ // Update payment history
777
+ const paymentHistory = document.getElementById('payment-history');
778
+ const noPayments = document.getElementById('no-payments');
779
 
780
+ paymentHistory.innerHTML = '';
781
+
782
+ if (device.payments.length === 0) {
783
+ noPayments.classList.remove('hidden');
784
+ } else {
785
+ noPayments.classList.add('hidden');
786
+ device.payments.forEach(payment => {
787
+ const paymentItem = document.createElement('div');
788
+ paymentItem.className = 'payment-item';
789
+ paymentItem.innerHTML = `
790
+ <div>
791
+ <div class="font-medium">₹${payment.amount.toFixed(2)}</div>
792
+ <div class="text-gray-500 text-sm">${payment.date}</div>
793
+ </div>
794
+ <div class="text-right">
795
+ <div class="text-green-600 font-medium">Payment</div>
796
+ <div class="text-gray-500 text-sm">ID: ${payment.id.toString().slice(-6)}</div>
797
+ </div>
798
+ `;
799
+ paymentHistory.appendChild(paymentItem);
800
+ });
801
  }
802
 
803
+ // Show modal
804
+ paymentModal.classList.add('active');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
805
  }
806
 
807
+ // Close payment modal
808
+ function closePaymentModal() {
809
+ paymentModal.classList.remove('active');
810
+ currentPaymentDevice = null;
811
+ }
812
+
813
+ // Add new payment
814
+ function addNewPayment() {
815
+ if (!currentPaymentDevice) return;
816
 
817
+ const amountInput = document.getElementById('new-payment-amount');
818
+ const amount = parseFloat(amountInput.value);
819
 
820
+ if (isNaN(amount) || amount <= 0) {
821
+ showToast('Please enter a valid payment amount', 'error');
 
822
  return;
823
  }
824
 
825
+ // Add payment to device
826
+ currentPaymentDevice.payments.push({
827
+ id: Date.now(),
828
+ date: new Date().toLocaleString(),
829
+ amount: amount
830
+ });
831
 
832
  // Update storage and UI
833
  localStorage.setItem('networkDevices', JSON.stringify(devices));
834
+ openPaymentModal(currentPaymentDevice.id);
835
  renderDeviceTable();
836
  updateSummary();
 
837
 
838
+ // Reset input
839
+ amountInput.value = '';
840
+
841
+ // Show success message
842
+ showToast('Payment added successfully!', 'success');
 
 
843
  }
844
 
845
  // Block device
846
+ function blockDevice(deviceId) {
847
+ const device = devices.find(d => d.id == deviceId);
 
 
848
  if (!device) return;
849
 
850
  device.status = device.status === 'Active' ? 'Blocked' : 'Active';
 
852
  // Update storage and UI
853
  localStorage.setItem('networkDevices', JSON.stringify(devices));
854
  renderDeviceTable();
 
 
 
 
855
 
856
  // Show notification
857
  const action = device.status === 'Blocked' ? 'blocked' : 'unblocked';
 
868
  localStorage.setItem('networkDevices', JSON.stringify(devices));
869
  renderDeviceTable();
870
  updateSummary();
 
871
 
872
  showToast('Device deleted successfully!', 'success');
873
  }
 
902
  emptyState.classList.add('hidden');
903
 
904
  filteredDevices.forEach((device, index) => {
905
+ // Calculate payment totals
906
+ const paidAmount = device.payments.reduce((sum, payment) => sum + payment.amount, 0);
907
+ const remainingAmount = device.totalAmount - paidAmount;
908
+
909
+ // Determine payment status
910
+ let paymentStatus = 'Unpaid';
911
+ let statusClass = 'unpaid-badge';
912
+
913
+ if (paidAmount >= device.totalAmount) {
914
+ paymentStatus = 'Fully Paid';
915
+ statusClass = 'paid-badge';
916
+ } else if (paidAmount > 0) {
917
+ paymentStatus = 'Partial Paid';
918
+ statusClass = 'partial-badge';
919
+ }
920
+
921
  const row = document.createElement('tr');
922
+ row.className = 'border-b';
923
  row.innerHTML = `
924
  <td class="py-4">${index + 1}</td>
925
  <td class="py-4">
 
927
  </td>
928
  <td class="py-4 font-medium">${device.deviceName}</td>
929
  <td class="py-4">${device.userName}</td>
930
+ <td class="py-4 font-bold">₹${device.totalAmount.toFixed(2)}</td>
931
+ <td class="py-4 font-medium text-green-600">₹${paidAmount.toFixed(2)}</td>
932
+ <td class="py-4 font-medium ${remainingAmount > 0 ? 'text-red-600' : 'text-green-600'}">
933
+ ₹${Math.abs(remainingAmount).toFixed(2)}
934
+ </td>
935
  <td class="py-4">
936
+ <span class="payment-status ${statusClass}">
937
+ ${paymentStatus}
938
  </span>
939
  </td>
940
  <td class="py-4 text-right">
941
+ <div class="inline-flex space-x-1">
942
+ <button class="action-btn payment-btn" data-id="${device.id}" title="Manage Payments">
943
+ <i class="fas fa-rupee-sign"></i>
944
  </button>
945
+ <button class="action-btn block-btn" data-id="${device.id}" title="Block Device">
946
  <i class="fas fa-ban"></i>
947
  </button>
948
+ <button class="action-btn delete-btn" data-id="${device.id}" title="Delete Device">
949
  <i class="fas fa-trash"></i>
950
  </button>
951
  </div>
 
955
  });
956
 
957
  // Reattach event listeners
958
+ document.querySelectorAll('.payment-btn').forEach(btn => {
959
+ btn.addEventListener('click', () => openPaymentModal(btn.dataset.id));
960
  });
961
 
962
  document.querySelectorAll('.block-btn').forEach(btn => {
963
+ btn.addEventListener('click', () => blockDevice(btn.dataset.id));
964
  });
965
 
966
  document.querySelectorAll('.delete-btn').forEach(btn => {
 
976
  }
977
 
978
  // Create CSV header
979
+ let csvContent = "MAC Address,Device Name,User Name,Total Amount,Paid Amount,Remaining Amount,Status,Added Time\n";
980
 
981
  // Add each device as a row
982
  devices.forEach(device => {
983
+ const paidAmount = device.payments.reduce((sum, payment) => sum + payment.amount, 0);
984
+ const remainingAmount = device.totalAmount - paidAmount;
985
+
986
+ csvContent += `"${device.mac}","${device.deviceName}","${device.userName}",${device.totalAmount},${paidAmount},${remainingAmount},"${device.status}","${device.addedTime}"\n`;
987
  });
988
 
989
  // Create download link
 
1000
  showToast('CSV exported successfully!', 'success');
1001
  }
1002
 
1003
+ // Show toast notification
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1004
  function showToast(message, type = 'info') {
1005
  const container = document.getElementById('toast-container');
1006
  const toast = document.createElement('div');
 
1022
  }, 3000);
1023
  }
1024
 
1025
+ // Initialize sample data
1026
+ function initializeSampleData() {
1027
+ if (devices.length === 0) {
1028
+ devices = [
1029
+ {
1030
+ id: 1,
1031
+ mac: 'AA:BB:CC:DD:EE:FF',
1032
+ deviceName: 'John\'s iPhone',
1033
+ userName: 'John Smith',
1034
+ totalAmount: 3000,
1035
+ payments: [
1036
+ { id: 101, date: new Date().toLocaleString(), amount: 1000 },
1037
+ { id: 102, date: new Date().toLocaleString(), amount: 500 }
1038
+ ],
1039
+ addedTime: new Date().toLocaleString(),
1040
+ status: 'Active'
1041
+ },
1042
+ {
1043
+ id: 2,
1044
+ mac: '112233445566',
1045
+ deviceName: 'Office Printer',
1046
+ userName: 'Office Admin',
1047
+ totalAmount: 5000,
1048
+ payments: [
1049
+ { id: 201, date: new Date().toLocaleString(), amount: 5000 }
1050
+ ],
1051
+ addedTime: new Date().toLocaleString(),
1052
+ status: 'Active'
1053
+ },
1054
+ {
1055
+ id: 3,
1056
+ mac: 'FF-EE-DD-CC-BB-AA',
1057
+ deviceName: 'Sarah\'s Laptop',
1058
+ userName: 'Sarah Johnson',
1059
+ totalAmount: 2500,
1060
+ payments: [],
1061
+ addedTime: new Date().toLocaleString(),
1062
+ status: 'Blocked'
1063
+ }
1064
+ ];
1065
+ localStorage.setItem('networkDevices', JSON.stringify(devices));
1066
+ }
1067
  }
1068
+
1069
+ // Initialize app
1070
+ initializeSampleData();
1071
  </script>
1072
  </body>
1073
  </html>