-
Notifications
You must be signed in to change notification settings - Fork 0
DT-90: Visualize single row data in drawer view #91
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
DT-90: Visualize single row data in drawer view #91
Conversation
…etails in a resizable drawer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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() |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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' |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 | ||
|
|
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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, | ||
| }) | ||
| } |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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' |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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) |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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) |
| } | ||
| }} | ||
| /> |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 | ||
| } |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
feat(Dep..Page.tsx): Add functionality to view individual table row details in a resizable drawer.