# DB Page: Grouped-by-Kind with Pagination

## Problem

The `www/db.html` page loads all events from IndexedDB at once via `store.getAll()`, parses every event, and renders every row into a single `<table>`. With thousands of events, this causes:

- Slow initial render (thousands of DOM rows created at once)
- Unresponsive filtering/sorting (re-renders entire table on every keystroke)
- No way to get a quick overview of what kinds of events exist

## Solution Overview

Replace the flat table with a **two-level grouped view**:

1. **Kind Summary View** — initial load shows one row per kind with count, newest, and oldest timestamps
2. **Expanded Kind View** — clicking a kind row expands it to show paginated event rows for that kind
3. **Pagination** — each expanded kind has its own pagination controls

```mermaid
flowchart TD
    A[Page Load] --> B[getAllEvents from IndexedDB]
    B --> C[Parse events into currentEvents]
    C --> D[groupEventsByKind]
    D --> E[renderKindSummary]
    
    E --> F{User clicks kind row}
    F -->|Expand| G[renderExpandedKind with pagination]
    F -->|Collapse| E
    
    G --> H[Per-kind filter/sort/paginate]
    H --> G
```

## Detailed Design

### New State Variables

Add to the GLOBAL VARIABLES section of `db.html`:

```javascript
// Grouped view state
let kindGroups = new Map();        // Map<number, Event[]>
let expandedKinds = new Set();     // Set<number> - which kinds are open
let kindPageState = new Map();     // Map<number, {page, pageSize, sortCol, sortDir, filters}>

const DEFAULT_PAGE_SIZE = 50;
const PAGE_SIZE_OPTIONS = [25, 50, 100];
```

### Function: groupEventsByKind

Groups the parsed `currentEvents` array into a `Map<kind, Event[]>`:

```javascript
function groupEventsByKind(events) {
    const groups = new Map();
    for (const evt of events) {
        const kind = evt.kind ?? 'unknown';
        if (!groups.has(kind)) groups.set(kind, []);
        groups.get(kind).push(evt);
    }
    return groups;
}
```

### Function: renderKindSummary

Replaces the current `renderTable()` as the initial view. Renders a compact summary table:

| ▶ | Kind | Count | Newest | Oldest |
|---|------|-------|--------|--------|
| ▶ | 0    | 42    | 2024-03-19 | 2024-01-15 |
| ▼ | 1    | 1,203 | 2024-03-19 | 2023-06-01 |
| ▶ | 3    | 87    | 2024-03-18 | 2024-02-10 |

- The ▶/▼ indicator uses the existing `HamburgerMorphing` component (arrow_right / arrow_down)
- Clicking a row toggles that kind in `expandedKinds`
- When expanded, the events table for that kind renders directly below the summary row
- `divStats` shows total: "12,847 events across 23 kinds"

### Function: renderExpandedKind

When a kind is expanded, renders:

1. A sub-table with the existing column layout: `commands | kind | created_at | id | pubkey | content | tags | relay`
2. Filter row (same wildcard inputs as current)
3. Paginated rows (only renders current page worth of `<tr>` elements)
4. Pagination controls bar below the sub-table

### Pagination Controls

Per expanded kind, rendered below the event sub-table:

```
[◀ Prev] Page 1 of 25 [Next ▶]  |  Show: [25] [50] [100]  |  Showing 1-50 of 1,203
```

- Each kind maintains independent page state in `kindPageState`
- Changing page size resets to page 1
- Filtering resets to page 1

### Per-Kind Filter and Sort

- Each expanded kind has its own filter inputs and sort state stored in `kindPageState`
- Filtering operates only on events of that kind
- Sorting operates only on events of that kind
- Both reset pagination to page 1

### CSS Additions

New styles needed:

- `.kind-summary-row` — styled like a header row, clickable, with hover effect
- `.kind-summary-row .expand-icon` — container for the HamburgerMorphing expand/collapse indicator
- `.kind-expanded-section` — container for the expanded event table + pagination
- `.pagination-bar` — flex row for pagination controls
- `.pagination-btn` — styled like existing `.btnCmd`
- `.pagination-info` — muted text showing current range
- `.page-size-btn` / `.page-size-btn.active` — page size selector buttons

### Changes to Existing Functions

#### loadEvents
- After parsing events into `currentEvents`, call `groupEventsByKind(currentEvents)` to populate `kindGroups`
- Call `renderKindSummary()` instead of `renderTable()`

#### renderTable
- Kept as-is but renamed to `renderKindEventTable(kind, events, pageState)` — only renders events for a single kind
- Receives a subset of events (already filtered/sorted/paginated)

#### getAllEvents
- No changes needed — still loads all events from IndexedDB
- Future optimization: could use IndexedDB indexes to count by kind without loading all data, but that is out of scope for this change

### Event Flow

1. Page loads → `initDatabase()` → `loadEvents()`
2. `loadEvents()` calls `getAllEvents()`, parses into `currentEvents`
3. `groupEventsByKind(currentEvents)` → `kindGroups` Map
4. `renderKindSummary()` renders the summary table (fast — only N rows where N = number of distinct kinds)
5. User clicks kind row → `toggleKind(kind)` adds/removes from `expandedKinds`
6. If expanding: initializes `kindPageState` for that kind, calls `renderExpandedKind(kind)`
7. `renderExpandedKind(kind)` gets events from `kindGroups.get(kind)`, applies filters/sort from `kindPageState`, slices for current page, renders sub-table + pagination
8. User interacts with pagination/filter/sort → updates `kindPageState`, re-renders only that kind section

### Preserved Functionality

- Raw JSON modal (view/copy) — works exactly as before, using `currentDisplayedEvents` index
- Wildcard filtering — same `wildcardToRegex` logic, scoped per-kind
- Column sorting — same `sortEvents` logic, scoped per-kind
- HamburgerMorphing sort icons — same pattern, scoped per-kind table
- Refresh button — reloads all events, re-groups, re-renders summary

## Implementation Order

1. Add new state variables
2. Create `groupEventsByKind()` function
3. Create `renderKindSummary()` function with expand/collapse
4. Create `renderExpandedKind()` function with pagination
5. Add pagination controls and handlers
6. Wire up per-kind filtering
7. Wire up per-kind sorting
8. Add CSS styles
9. Update `loadEvents()` to use new pipeline
10. Update `divStats` for summary counts
11. Test with existing raw JSON modal functionality
