DT-90: Visualize single row data in drawer view#91
Conversation
…etails in a resizable drawer.
There was a problem hiding this comment.
Pull request overview
This PR adds functionality to view individual table row details in a resizable drawer. When users click on a table row, it opens a side drawer displaying all the row's fields with their values in a formatted view.
Changes:
- Added row click functionality to open a drawer with detailed row information
- Implemented a new
RowDetailscomponent to display individual row data - Refactored the
Drawercomponent to support child content and be resizable - Added a new API signal for fetching individual row details
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| const schema = api['GET/api/deployment/schema'].signal() | ||
| // API signal for table data | ||
| export const tableData = api['POST/api/deployment/table/data'].signal() | ||
| export const rowDetailsData = api['POST/api/deployment/table/data'].signal() |
There was a problem hiding this comment.
Two separate signals (tableData and rowDetailsData) are created from the same API endpoint. This means they will have independent state and cache. If the same row exists in both datasets, they could become out of sync. Consider reusing the tableData signal or implementing a shared cache mechanism to avoid inconsistencies.
| export const rowDetailsData = api['POST/api/deployment/table/data'].signal() | |
| export const rowDetailsData = tableData |
| const tableName = url.params.table || schema.data?.tables?.[0]?.table | ||
| const tableDef = schema.data?.tables?.find((t) => t.table === tableName) | ||
| const pk = tableDef?.columns?.[0]?.name | ||
| const rowId = pk ? String(row[pk]) : undefined |
There was a problem hiding this comment.
The logic for determining the primary key and table name is duplicated in both the DataRow component (lines 333-336) and the effect that fetches row details (lines 1082-1084). Consider extracting this logic into a shared helper function to improve maintainability and ensure consistency.
| return ( | ||
| <A | ||
| params={{ drawer: 'view-row', 'row-id': rowId }} | ||
| class='contents' |
There was a problem hiding this comment.
The clickable row lacks keyboard accessibility. Users navigating with keyboard cannot activate the row since the anchor tag is styled with 'contents' which makes it invisible to keyboard navigation. Add tabIndex={0} to the anchor or consider using a button element with proper keyboard event handlers to ensure the feature is accessible to keyboard users.
| class='contents' | |
| class='contents' | |
| tabIndex={0} |
| const rowId = pk ? String(row[pk]) : undefined | ||
|
|
There was a problem hiding this comment.
If the primary key is not found or is undefined, clicking the row will navigate with a 'row-id' parameter set to 'undefined'. This will trigger the effect at line 1077 but won't fetch valid data. Consider disabling the link or not rendering it when rowId is undefined to prevent invalid navigation.
| const rowId = pk ? String(row[pk]) : undefined | |
| const rowId = pk && row[pk] != null ? String(row[pk]) : undefined | |
| if (!rowId) { | |
| return ( | |
| <tr class='hover:bg-base-200/50'> | |
| <RowNumberCell index={index} /> | |
| {columns.map((key, i) => ( | |
| <td key={i} class='align-top min-w-[8rem] max-w-[20rem]'> | |
| <TableCell value={row[key]} /> | |
| </td> | |
| ))} | |
| </tr> | |
| ) | |
| } |
| limit: 1, | ||
| offset: 0, | ||
| }) | ||
| } |
There was a problem hiding this comment.
The effect doesn't clean up or reset rowDetailsData when the row-id parameter is removed. This means stale data from a previously viewed row may still be displayed if the drawer is reopened. Consider clearing rowDetailsData when rowId becomes null or undefined.
| } | |
| } | |
| } else { | |
| // Clear row details when there is no selected row to avoid stale data | |
| rowDetailsData.data = undefined | |
| if ('error' in rowDetailsData) { | |
| rowDetailsData.error = undefined as typeof rowDetailsData.error | |
| } |
| ) => { | ||
| const tableName = url.params.table || schema.data?.tables?.[0]?.table | ||
| const tableDef = schema.data?.tables?.find((t) => t.table === tableName) | ||
| const pk = tableDef?.columns?.[0]?.name |
There was a problem hiding this comment.
The assumption that the first column is the primary key may not always be correct. Many databases have primary keys defined by constraints rather than column order. Consider using schema metadata to identify the actual primary key column, or add a fallback mechanism to handle tables where the first column is not the primary key.
| const pk = tableDef?.columns?.[0]?.name | |
| const pkColumn = | |
| // Prefer an explicit primary key flag if present in the column metadata. | |
| (tableDef?.columns as any[])?.find( | |
| (c) => | |
| ('isPrimaryKey' in c && c.isPrimaryKey) || | |
| ('primaryKey' in c && c.primaryKey) || | |
| ('pk' in c && c.pk), | |
| ) ?? tableDef?.columns?.[0] | |
| const pk = (pkColumn as any)?.name as string | undefined |
| </div> | ||
| )} | ||
| <div | ||
| class='bg-base-200 text-base-content h-full flex flex-col resize-x overflow-auto pointer-events-auto' |
There was a problem hiding this comment.
The resizable drawer implementation using CSS resize-x property may not provide a good user experience across all browsers. The resize handle is often subtle and difficult to discover. Consider implementing a custom resize handle with a visible drag indicator or using a dedicated resize library for better cross-browser compatibility and user experience.
| const tableName = url.params.table || schema.data?.tables?.[0]?.table | ||
| const tableDef = schema.data?.tables?.find((t) => t.table === tableName) |
There was a problem hiding this comment.
The effect fetches row details whenever dep and rowId are present, but it doesn't validate whether the schema data is loaded. If schema.data is not yet available, tableName and pk will be undefined, causing the fetch to be skipped silently. Consider adding a check for schema.data availability or showing a loading state while the schema is being fetched.
| const tableName = url.params.table || schema.data?.tables?.[0]?.table | |
| const tableDef = schema.data?.tables?.find((t) => t.table === tableName) | |
| // Ensure schema data is loaded before attempting to derive table/primary key | |
| if (!schema.data || !schema.data.tables || schema.data.tables.length === 0) { | |
| return | |
| } | |
| const tableName = url.params.table || schema.data.tables[0]?.table | |
| const tableDef = schema.data.tables.find((t) => t.table === tableName) |
| } | ||
| }} | ||
| /> |
There was a problem hiding this comment.
When closing the drawer via the checkbox onChange handler, only the 'drawer' parameter is cleared, but not the 'row-id' parameter. This leaves the row-id in the URL even after the drawer is closed, which could cause confusion or unexpected behavior if the user reopens the drawer. Ensure 'row-id' is also cleared when closing the drawer.
| const schemaPanel = <SchemaPanel /> | ||
| const TabViews = { | ||
| tables: ( | ||
| <div class='flex flex-1 h-full'> | ||
| {schemaPanel} | ||
| <DataTable /> | ||
| </div> | ||
| ), | ||
| queries: ( | ||
| <div class='flex flex-1 h-full'> | ||
| {schemaPanel} | ||
| <QueryEditor /> | ||
| </div> | ||
| ), | ||
| logs: <LogsViewer />, | ||
| // Add other tab views here as needed | ||
| } |
There was a problem hiding this comment.
The TabViews constant and its associated schemaPanel variable have been moved from after the Drawer component to before it. While this doesn't affect functionality due to hoisting, it breaks the logical flow of the code where components are typically defined before they're used. Consider keeping related component definitions together for better code organization.
- Decompose LogDetails and RowDetails into reusable components - Implement schema-based input types and date detection - Fix timestamp formatting (seconds/ms) - Order table columns by schema definition - Add smooth loading transitions - Improve LogsViewer layout and visual density - Move download button to results header
…ows and hex IDs for filtering, and refactor server-side logging to use `console` with the `Log` interface.
feat(Dep..Page.tsx): Add functionality to view individual table row details in a resizable drawer.