# 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