jebin2 commited on
Commit
f21967a
Β·
1 Parent(s): de3cb16
Files changed (2) hide show
  1. routers/blink.py +63 -2
  2. templates/index.html +78 -5
routers/blink.py CHANGED
@@ -198,7 +198,7 @@ async def get_gemini_jobs(
198
  "created_at": item.created_at.isoformat() if item.created_at else None,
199
  "completed_at": item.completed_at.isoformat() if item.completed_at else None
200
  }
201
- for item in items
202
  ],
203
  "total": total,
204
  "page": page,
@@ -212,7 +212,68 @@ async def get_gemini_jobs(
212
  )
213
 
214
 
215
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
216
 
217
  @router.get("/blink")
218
  async def blink(
 
198
  "created_at": item.created_at.isoformat() if item.created_at else None,
199
  "completed_at": item.completed_at.isoformat() if item.completed_at else None
200
  }
201
+ for item in items
202
  ],
203
  "total": total,
204
  "page": page,
 
212
  )
213
 
214
 
215
+ @router.get("/api/payment-transactions")
216
+ async def get_payment_transactions(
217
+ page: int = Query(1, ge=1, description="Page number"),
218
+ limit: int = Query(50, ge=1, le=500, description="Items per page"),
219
+ db: AsyncSession = Depends(get_db)
220
+ ):
221
+ """
222
+ Get paginated payment transactions.
223
+ """
224
+ from core.models import PaymentTransaction
225
+
226
+ try:
227
+ offset = (page - 1) * limit
228
+
229
+ # Get total count
230
+ total_result = await db.execute(select(func.count(PaymentTransaction.id)))
231
+ total = total_result.scalar() or 0
232
+
233
+ # Get total revenue (paid transactions only)
234
+ revenue_result = await db.execute(
235
+ select(func.sum(PaymentTransaction.amount_paise)).where(
236
+ PaymentTransaction.status == "paid"
237
+ )
238
+ )
239
+ total_revenue_paise = revenue_result.scalar() or 0
240
+
241
+ # Get paginated items
242
+ query = select(PaymentTransaction).order_by(PaymentTransaction.id.desc()).offset(offset).limit(limit)
243
+ result = await db.execute(query)
244
+ items = result.scalars().all()
245
+
246
+ return {
247
+ "items": [
248
+ {
249
+ "id": item.id,
250
+ "transaction_id": item.transaction_id,
251
+ "user_id": item.user_id,
252
+ "gateway": item.gateway,
253
+ "package_id": item.package_id,
254
+ "credits_amount": item.credits_amount,
255
+ "amount_paise": item.amount_paise,
256
+ "amount_rupees": item.amount_paise / 100,
257
+ "currency": item.currency,
258
+ "status": item.status,
259
+ "error_message": item.error_message,
260
+ "created_at": item.created_at.isoformat() if item.created_at else None,
261
+ "paid_at": item.paid_at.isoformat() if item.paid_at else None
262
+ }
263
+ for item in items
264
+ ],
265
+ "total": total,
266
+ "total_revenue_paise": total_revenue_paise,
267
+ "total_revenue_rupees": total_revenue_paise / 100,
268
+ "page": page,
269
+ "limit": limit
270
+ }
271
+ except Exception as e:
272
+ logger.error(f"Error fetching payment transactions: {e}")
273
+ raise HTTPException(
274
+ status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
275
+ detail="Error fetching payment transactions"
276
+ )
277
 
278
  @router.get("/blink")
279
  async def blink(
templates/index.html CHANGED
@@ -299,6 +299,7 @@
299
  <button class="tab-btn" onclick="switchTab('users')">πŸ‘₯ Users</button>
300
  <button class="tab-btn" onclick="switchTab('audit')">πŸ“ Audit Logs</button>
301
  <button class="tab-btn" onclick="switchTab('jobs')">⚑ Gemini Jobs</button>
 
302
  <button class="tab-btn" onclick="switchTab('keys')">πŸ”‘ API Keys</button>
303
  </div>
304
 
@@ -452,6 +453,43 @@
452
  </div>
453
  </div>
454
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
455
  <!-- API Keys Usage Table -->
456
  <div class="table-container hidden" id="keysTable">
457
  <div class="table-wrapper">
@@ -490,7 +528,8 @@
490
  blink: { page: 1, total: 0 },
491
  users: { page: 1, total: 0 },
492
  audit: { page: 1, total: 0 },
493
- jobs: { page: 1, total: 0 }
 
494
  };
495
  let currentTab = 'blink';
496
 
@@ -498,7 +537,8 @@
498
  blink: '/api/data',
499
  users: '/api/users',
500
  audit: '/api/audit-logs',
501
- jobs: '/api/gemini-jobs'
 
502
  };
503
 
504
  function switchTab(tab) {
@@ -513,10 +553,11 @@
513
  document.getElementById('usersTable').classList.toggle('hidden', tab !== 'users');
514
  document.getElementById('auditTable').classList.toggle('hidden', tab !== 'audit');
515
  document.getElementById('jobsTable').classList.toggle('hidden', tab !== 'jobs');
 
516
  document.getElementById('keysTable').classList.toggle('hidden', tab !== 'keys');
517
 
518
- // Show/hide unique users stat (only for blink)
519
- document.getElementById('uniqueUsersCard').classList.toggle('hidden', tab !== 'blink');
520
 
521
  // Load data
522
  if (tab === 'keys') {
@@ -615,11 +656,34 @@
615
  `).join('');
616
  }
617
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
618
  const renderers = {
619
  blink: renderBlinkTable,
620
  users: renderUsersTable,
621
  audit: renderAuditTable,
622
- jobs: renderJobsTable
 
623
  };
624
 
625
  function updatePagination(tab) {
@@ -640,6 +704,15 @@
640
  document.getElementById('uniqueUsers').textContent = data.unique_users.toLocaleString();
641
  }
642
 
 
 
 
 
 
 
 
 
 
643
  renderers[tab](data.items);
644
  updatePagination(tab);
645
  }
 
299
  <button class="tab-btn" onclick="switchTab('users')">πŸ‘₯ Users</button>
300
  <button class="tab-btn" onclick="switchTab('audit')">πŸ“ Audit Logs</button>
301
  <button class="tab-btn" onclick="switchTab('jobs')">⚑ Gemini Jobs</button>
302
+ <button class="tab-btn" onclick="switchTab('payments')">πŸ’³ Payments</button>
303
  <button class="tab-btn" onclick="switchTab('keys')">πŸ”‘ API Keys</button>
304
  </div>
305
 
 
453
  </div>
454
  </div>
455
 
456
+ <!-- Payment Transactions Table -->
457
+ <div class="table-container hidden" id="paymentsTable">
458
+ <div class="table-wrapper">
459
+ <table>
460
+ <thead>
461
+ <tr>
462
+ <th>ID</th>
463
+ <th>Transaction ID</th>
464
+ <th>User ID</th>
465
+ <th>Gateway</th>
466
+ <th>Package</th>
467
+ <th>Credits</th>
468
+ <th>Amount</th>
469
+ <th>Status</th>
470
+ <th>Created At</th>
471
+ <th>Paid At</th>
472
+ </tr>
473
+ </thead>
474
+ <tbody id="paymentsBody">
475
+ <tr>
476
+ <td colspan="10">
477
+ <div class="loading">
478
+ <div class="spinner"></div>Loading...
479
+ </div>
480
+ </td>
481
+ </tr>
482
+ </tbody>
483
+ </table>
484
+ </div>
485
+ <div class="pagination">
486
+ <button onclick="prevPage('payments')" id="paymentsPrev" disabled>← Previous</button>
487
+ <span class="page-info">Page <span id="paymentsCurrentPage">1</span> of <span
488
+ id="paymentsTotalPages">1</span></span>
489
+ <button onclick="nextPage('payments')" id="paymentsNext">Next β†’</button>
490
+ </div>
491
+ </div>
492
+
493
  <!-- API Keys Usage Table -->
494
  <div class="table-container hidden" id="keysTable">
495
  <div class="table-wrapper">
 
528
  blink: { page: 1, total: 0 },
529
  users: { page: 1, total: 0 },
530
  audit: { page: 1, total: 0 },
531
+ jobs: { page: 1, total: 0 },
532
+ payments: { page: 1, total: 0, revenue: 0 }
533
  };
534
  let currentTab = 'blink';
535
 
 
537
  blink: '/api/data',
538
  users: '/api/users',
539
  audit: '/api/audit-logs',
540
+ jobs: '/api/gemini-jobs',
541
+ payments: '/api/payment-transactions'
542
  };
543
 
544
  function switchTab(tab) {
 
553
  document.getElementById('usersTable').classList.toggle('hidden', tab !== 'users');
554
  document.getElementById('auditTable').classList.toggle('hidden', tab !== 'audit');
555
  document.getElementById('jobsTable').classList.toggle('hidden', tab !== 'jobs');
556
+ document.getElementById('paymentsTable').classList.toggle('hidden', tab !== 'payments');
557
  document.getElementById('keysTable').classList.toggle('hidden', tab !== 'keys');
558
 
559
+ // Show/hide unique users stat (for blink and payments)
560
+ document.getElementById('uniqueUsersCard').classList.toggle('hidden', tab !== 'blink' && tab !== 'payments');
561
 
562
  // Load data
563
  if (tab === 'keys') {
 
656
  `).join('');
657
  }
658
 
659
+ function renderPaymentsTable(items) {
660
+ const tbody = document.getElementById('paymentsBody');
661
+ if (items.length === 0) {
662
+ tbody.innerHTML = '<tr><td colspan="10"><div class="empty-state">No payment transactions found</div></td></tr>';
663
+ return;
664
+ }
665
+ tbody.innerHTML = items.map(item => `
666
+ <tr>
667
+ <td>${item.id}</td>
668
+ <td class="user-id">${item.transaction_id}</td>
669
+ <td class="user-id">${item.user_id}</td>
670
+ <td><span class="status-badge" style="background: rgba(123, 44, 191, 0.2); color: #a855f7;">${item.gateway}</span></td>
671
+ <td>${item.package_id}</td>
672
+ <td><span class="credits-badge">${item.credits_amount}</span></td>
673
+ <td>β‚Ή${item.amount_rupees}</td>
674
+ <td><span class="status-badge status-${item.status === 'paid' ? 'success' : item.status === 'failed' ? 'failed' : 'queued'}">${item.status}</span></td>
675
+ <td class="timestamp">${item.created_at ? new Date(item.created_at).toLocaleString() : '-'}</td>
676
+ <td class="timestamp">${item.paid_at ? new Date(item.paid_at).toLocaleString() : '-'}</td>
677
+ </tr>
678
+ `).join('');
679
+ }
680
+
681
  const renderers = {
682
  blink: renderBlinkTable,
683
  users: renderUsersTable,
684
  audit: renderAuditTable,
685
+ jobs: renderJobsTable,
686
+ payments: renderPaymentsTable
687
  };
688
 
689
  function updatePagination(tab) {
 
704
  document.getElementById('uniqueUsers').textContent = data.unique_users.toLocaleString();
705
  }
706
 
707
+ // Show revenue for payments tab
708
+ if (tab === 'payments' && data.total_revenue_rupees !== undefined) {
709
+ state.payments.revenue = data.total_revenue_rupees;
710
+ document.getElementById('uniqueUsers').textContent = 'β‚Ή' + data.total_revenue_rupees.toLocaleString();
711
+ document.getElementById('uniqueUsersCard').querySelector('.stat-label').textContent = 'Total Revenue';
712
+ } else if (tab === 'blink') {
713
+ document.getElementById('uniqueUsersCard').querySelector('.stat-label').textContent = 'Unique Users';
714
+ }
715
+
716
  renderers[tab](data.items);
717
  updatePagination(tab);
718
  }