-
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?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -46,6 +46,7 @@ type AnyRecord = Record<string, unknown> | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Effect to fetch schema when deployment URL changes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| effect(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -328,16 +329,28 @@ const EmptyRow = ({ colSpan }: { colSpan: number }) => ( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const DataRow = ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| { row, columns, index }: { row: AnyRecord; columns: string[]; index: number }, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) => ( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| <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> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 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 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 |
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.
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> | |
| ) | |
| } |
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} |
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.
Wrapping a table row in an anchor tag creates invalid HTML structure. The HTML specification requires that elements must be direct children of , , or elements. Placing an element between the table body and the row breaks this requirement. Consider adding an onClick handler to the element instead, or using a button with appropriate styling and ARIA attributes for better accessibility.
| return ( | |
| <A | |
| params={{ drawer: 'view-row', 'row-id': rowId }} | |
| class='contents' | |
| > | |
| <tr class='hover:bg-base-200/50 cursor-pointer'> | |
| <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> | |
| </A> | |
| const rowUrl = url({ | |
| params: { drawer: 'view-row', 'row-id': rowId }, | |
| }) | |
| const handleActivate = () => { | |
| navigate(rowUrl) | |
| } | |
| const handleKeyDown: JSX.KeyboardEventHandler<HTMLTableRowElement> = (event) => { | |
| if (event.key === 'Enter' || event.key === ' ') { | |
| event.preventDefault() | |
| handleActivate() | |
| } | |
| } | |
| return ( | |
| <tr | |
| class='hover:bg-base-200/50 cursor-pointer' | |
| role='link' | |
| tabIndex={0} | |
| onClick={handleActivate} | |
| onKeyDown={handleKeyDown} | |
| > | |
| <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> |
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.
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.
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 | |
| } |
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.
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.
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 data-view attribute is hardcoded to 'history' but should reflect the actual drawer view being displayed. This could cause confusion when debugging or testing different drawer states. Update this to use the actual drawer parameter value.
| <div class='drawer-view h-full' data-view='history'> | |
| <div | |
| class='drawer-view h-full' | |
| data-view={url.params.drawer ?? 'none'} | |
| > |
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.