155 lines
5.4 KiB
Markdown
155 lines
5.4 KiB
Markdown
# Subscription Table WSI Pointer Grouping Implementation Plan
|
|
|
|
## Objective
|
|
Modify the subscription details table to show the WSI Pointer value only once per group - on the first row of each WSI Pointer group, leaving it blank for subsequent rows with the same WSI Pointer.
|
|
|
|
## Current Behavior
|
|
- All rows show the WSI Pointer value
|
|
- Rows are sorted by WSI Pointer (grouping is working)
|
|
- Visual grouping is not clear
|
|
|
|
## Desired Behavior
|
|
- First row of each WSI Pointer group shows the full WSI Pointer value
|
|
- Subsequent rows in the same group have an empty cell for WSI Pointer
|
|
- This creates a clear visual grouping effect
|
|
|
|
## Implementation Details
|
|
|
|
### File to Modify
|
|
`api/index.js` - Function `populateSubscriptionDetailsTable()` (lines 4277-4384)
|
|
|
|
### Code Changes Required
|
|
|
|
#### Current Code (lines 4291-4383):
|
|
```javascript
|
|
// Sort subscriptions by wsi_pointer to group them together
|
|
subscriptionsData.sort((a, b) => {
|
|
const wsiA = a.wsi_pointer || '';
|
|
const wsiB = b.wsi_pointer || '';
|
|
return wsiA.localeCompare(wsiB);
|
|
});
|
|
|
|
subscriptionsData.forEach((subscription, index) => {
|
|
const row = document.createElement('tr');
|
|
|
|
// Calculate duration
|
|
const now = Math.floor(Date.now() / 1000);
|
|
const duration = now - subscription.created_at;
|
|
const durationStr = formatDuration(duration);
|
|
|
|
// Format client IP (show full IP for admin view)
|
|
const clientIP = subscription.client_ip || 'unknown';
|
|
|
|
// Format wsi_pointer (show full pointer)
|
|
const wsiPointer = subscription.wsi_pointer || 'N/A';
|
|
|
|
// Format filters (show actual filter details)
|
|
let filtersDisplay = 'None';
|
|
// ... filter formatting code ...
|
|
|
|
row.innerHTML = `
|
|
<td style="font-family: 'Courier New', monospace; font-size: 12px;">${wsiPointer}</td>
|
|
<td style="font-family: 'Courier New', monospace; font-size: 12px;">${subscription.id || 'N/A'}</td>
|
|
<!-- <td style="font-family: 'Courier New', monospace; font-size: 12px;">${clientIP}</td> -->
|
|
<td>${durationStr}</td>
|
|
<td>${filtersDisplay}</td>
|
|
`;
|
|
tableBody.appendChild(row);
|
|
});
|
|
```
|
|
|
|
#### Modified Code:
|
|
```javascript
|
|
// Sort subscriptions by wsi_pointer to group them together
|
|
subscriptionsData.sort((a, b) => {
|
|
const wsiA = a.wsi_pointer || '';
|
|
const wsiB = b.wsi_pointer || '';
|
|
return wsiA.localeCompare(wsiB);
|
|
});
|
|
|
|
// Track previous WSI Pointer to detect group changes
|
|
let previousWsiPointer = null;
|
|
|
|
subscriptionsData.forEach((subscription, index) => {
|
|
const row = document.createElement('tr');
|
|
|
|
// Calculate duration
|
|
const now = Math.floor(Date.now() / 1000);
|
|
const duration = now - subscription.created_at;
|
|
const durationStr = formatDuration(duration);
|
|
|
|
// Format client IP (show full IP for admin view)
|
|
const clientIP = subscription.client_ip || 'unknown';
|
|
|
|
// Format wsi_pointer - only show if it's different from previous row
|
|
const currentWsiPointer = subscription.wsi_pointer || 'N/A';
|
|
let wsiPointerDisplay = '';
|
|
|
|
if (currentWsiPointer !== previousWsiPointer) {
|
|
// This is the first row of a new group - show the WSI Pointer
|
|
wsiPointerDisplay = currentWsiPointer;
|
|
previousWsiPointer = currentWsiPointer;
|
|
} else {
|
|
// This is a continuation of the same group - leave blank
|
|
wsiPointerDisplay = '';
|
|
}
|
|
|
|
// Format filters (show actual filter details)
|
|
let filtersDisplay = 'None';
|
|
// ... filter formatting code remains the same ...
|
|
|
|
row.innerHTML = `
|
|
<td style="font-family: 'Courier New', monospace; font-size: 12px;">${wsiPointerDisplay}</td>
|
|
<td style="font-family: 'Courier New', monospace; font-size: 12px;">${subscription.id || 'N/A'}</td>
|
|
<!-- <td style="font-family: 'Courier New', monospace; font-size: 12px;">${clientIP}</td> -->
|
|
<td>${durationStr}</td>
|
|
<td>${filtersDisplay}</td>
|
|
`;
|
|
tableBody.appendChild(row);
|
|
});
|
|
```
|
|
|
|
### Key Changes Explained
|
|
|
|
1. **Add tracking variable**: `let previousWsiPointer = null;` before the forEach loop
|
|
2. **Store current WSI Pointer**: `const currentWsiPointer = subscription.wsi_pointer || 'N/A';`
|
|
3. **Compare with previous**: Check if `currentWsiPointer !== previousWsiPointer`
|
|
4. **Conditional display**:
|
|
- If different: Show the WSI Pointer value and update `previousWsiPointer`
|
|
- If same: Show empty string (blank cell)
|
|
5. **Use display variable**: Replace `${wsiPointer}` with `${wsiPointerDisplay}` in the row HTML
|
|
|
|
### Visual Result
|
|
|
|
**Before:**
|
|
```
|
|
WSI Pointer | Subscription ID | Duration | Filters
|
|
0x12345678 | sub-001 | 5m 30s | kinds:[1]
|
|
0x12345678 | sub-002 | 3m 15s | kinds:[3]
|
|
0x87654321 | sub-003 | 1m 45s | kinds:[1,3]
|
|
```
|
|
|
|
**After:**
|
|
```
|
|
WSI Pointer | Subscription ID | Duration | Filters
|
|
0x12345678 | sub-001 | 5m 30s | kinds:[1]
|
|
| sub-002 | 3m 15s | kinds:[3]
|
|
0x87654321 | sub-003 | 1m 45s | kinds:[1,3]
|
|
```
|
|
|
|
## Testing Checklist
|
|
|
|
1. ✅ Verify first row of each group shows WSI Pointer
|
|
2. ✅ Verify subsequent rows in same group are blank
|
|
3. ✅ Verify grouping works with multiple subscriptions per WSI Pointer
|
|
4. ✅ Verify single subscription per WSI Pointer still shows the value
|
|
5. ✅ Verify empty/null WSI Pointers are handled correctly
|
|
6. ✅ Verify table still displays correctly when no subscriptions exist
|
|
|
|
## Next Steps
|
|
|
|
1. Review this plan
|
|
2. Switch to Code mode
|
|
3. Implement the changes in `api/index.js`
|
|
4. Test the implementation
|
|
5. Verify the visual grouping effect |