subscribers.templ 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. package app
  2. import "fmt"
  3. import "github.com/seaweedfs/seaweedfs/weed/admin/dash"
  4. templ Subscribers(data dash.SubscribersData) {
  5. <div class="container-fluid">
  6. <div class="row">
  7. <div class="col-12">
  8. <div class="d-flex justify-content-between align-items-center mb-4">
  9. <h1 class="h3 mb-0">Message Queue Subscribers</h1>
  10. <small class="text-muted">Last updated: {data.LastUpdated.Format("2006-01-02 15:04:05")}</small>
  11. </div>
  12. <!-- Summary Cards -->
  13. <div class="row mb-4">
  14. <div class="col-md-4">
  15. <div class="card text-center">
  16. <div class="card-body">
  17. <h5 class="card-title">Total Subscribers</h5>
  18. <h3 class="text-primary">{fmt.Sprintf("%d", data.TotalSubscribers)}</h3>
  19. </div>
  20. </div>
  21. </div>
  22. <div class="col-md-4">
  23. <div class="card text-center">
  24. <div class="card-body">
  25. <h5 class="card-title">Active Subscribers</h5>
  26. <h3 class="text-success">{fmt.Sprintf("%d", data.ActiveSubscribers)}</h3>
  27. </div>
  28. </div>
  29. </div>
  30. <div class="col-md-4">
  31. <div class="card text-center">
  32. <div class="card-body">
  33. <h5 class="card-title">Inactive Subscribers</h5>
  34. <h3 class="text-warning">{fmt.Sprintf("%d", data.TotalSubscribers - data.ActiveSubscribers)}</h3>
  35. </div>
  36. </div>
  37. </div>
  38. </div>
  39. <!-- Subscribers Table -->
  40. <div class="card">
  41. <div class="card-header d-flex justify-content-between align-items-center">
  42. <h5 class="mb-0">Subscribers</h5>
  43. <div>
  44. <button class="btn btn-sm btn-outline-secondary" onclick="exportSubscribersCSV()">
  45. <i class="fas fa-download me-1"></i>Export CSV
  46. </button>
  47. </div>
  48. </div>
  49. <div class="card-body">
  50. if len(data.Subscribers) == 0 {
  51. <div class="text-center py-4">
  52. <i class="fas fa-user-friends fa-3x text-muted mb-3"></i>
  53. <h5>No Subscribers Found</h5>
  54. <p class="text-muted">No message queue subscribers are currently active.</p>
  55. </div>
  56. } else {
  57. <div class="table-responsive">
  58. <table class="table table-striped" id="subscribersTable">
  59. <thead>
  60. <tr>
  61. <th>Subscriber Name</th>
  62. <th>Topic</th>
  63. <th>Consumer Group</th>
  64. <th>Status</th>
  65. <th>Messages Processed</th>
  66. <th>Last Seen</th>
  67. <th>Created</th>
  68. </tr>
  69. </thead>
  70. <tbody>
  71. for _, subscriber := range data.Subscribers {
  72. <tr>
  73. <td>
  74. <strong>{subscriber.Name}</strong>
  75. </td>
  76. <td>
  77. <span class="badge bg-info">{subscriber.Topic}</span>
  78. </td>
  79. <td>{subscriber.ConsumerGroup}</td>
  80. <td>
  81. if subscriber.Status == "active" {
  82. <span class="badge bg-success">Active</span>
  83. } else if subscriber.Status == "inactive" {
  84. <span class="badge bg-warning">Inactive</span>
  85. } else {
  86. <span class="badge bg-secondary">{subscriber.Status}</span>
  87. }
  88. </td>
  89. <td>{fmt.Sprintf("%d", subscriber.MessageCount)}</td>
  90. <td>
  91. if !subscriber.LastSeen.IsZero() {
  92. <span class="text-muted">{subscriber.LastSeen.Format("2006-01-02 15:04:05")}</span>
  93. } else {
  94. <span class="text-muted">Never</span>
  95. }
  96. </td>
  97. <td>
  98. <span class="text-muted">{subscriber.CreatedAt.Format("2006-01-02 15:04:05")}</span>
  99. </td>
  100. </tr>
  101. }
  102. </tbody>
  103. </table>
  104. </div>
  105. }
  106. </div>
  107. </div>
  108. </div>
  109. </div>
  110. </div>
  111. <script>
  112. function exportSubscribersCSV() {
  113. const table = document.getElementById('subscribersTable');
  114. if (!table) return;
  115. let csv = 'Subscriber Name,Topic,Consumer Group,Status,Messages Processed,Last Seen,Created\n';
  116. const rows = table.querySelectorAll('tbody tr');
  117. rows.forEach(row => {
  118. const cells = row.querySelectorAll('td');
  119. if (cells.length >= 7) {
  120. const rowData = [
  121. cells[0].querySelector('strong')?.textContent || '',
  122. cells[1].querySelector('.badge')?.textContent || '',
  123. cells[2].textContent || '',
  124. cells[3].querySelector('.badge')?.textContent || '',
  125. cells[4].textContent || '',
  126. cells[5].querySelector('span')?.textContent || '',
  127. cells[6].querySelector('span')?.textContent || ''
  128. ];
  129. csv += rowData.map(field => `"${field.replace(/"/g, '""')}"`).join(',') + '\n';
  130. }
  131. });
  132. const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
  133. const link = document.createElement('a');
  134. const url = URL.createObjectURL(blob);
  135. link.setAttribute('href', url);
  136. link.setAttribute('download', 'subscribers.csv');
  137. link.style.visibility = 'hidden';
  138. document.body.appendChild(link);
  139. link.click();
  140. document.body.removeChild(link);
  141. }
  142. </script>
  143. }