diff --git a/dashboards/tracker-web/package.json b/dashboards/tracker-web/package.json index 5de3de4e..8093440d 100644 --- a/dashboards/tracker-web/package.json +++ b/dashboards/tracker-web/package.json @@ -27,6 +27,7 @@ "@bytelyst/api-client": "workspace:*", "@bytelyst/config": "workspace:*", "@bytelyst/dashboard-components": "workspace:*", + "@bytelyst/data-table": "workspace:*", "@bytelyst/errors": "workspace:*", "@bytelyst/telemetry-client": "workspace:*", "@bytelyst/logger": "workspace:*", diff --git a/dashboards/tracker-web/src/app/dashboard/items/page.tsx b/dashboards/tracker-web/src/app/dashboard/items/page.tsx index b256e4f7..0d76a324 100644 --- a/dashboards/tracker-web/src/app/dashboard/items/page.tsx +++ b/dashboards/tracker-web/src/app/dashboard/items/page.tsx @@ -1,7 +1,8 @@ 'use client'; -import { useEffect, useState, useCallback } from 'react'; +import { useEffect, useState, useCallback, useMemo } from 'react'; import Link from 'next/link'; +import { DataTable, type ColumnDef } from '@bytelyst/data-table'; import { useAuth } from '@/lib/auth-context'; import { listItems, createItem, deleteItem, type TrackerItem } from '@/lib/tracker-client'; @@ -87,15 +88,105 @@ export default function ItemsListPage() { } }; - const handleDelete = async (id: string) => { - if (!confirm('Delete this item?')) return; - try { - await deleteItem(id); - fetchItems(); - } catch (err: unknown) { - setError(err instanceof Error ? err.message : 'Failed to delete'); - } - }; + const handleDelete = useCallback( + async (id: string) => { + if (!confirm('Delete this item?')) return; + try { + await deleteItem(id); + fetchItems(); + } catch (err: unknown) { + setError(err instanceof Error ? err.message : 'Failed to delete'); + } + }, + [fetchItems] + ); + + const columns = useMemo[]>( + () => [ + { + id: 'title', + accessorKey: 'title', + header: 'Title', + cell: ({ row }) => { + const item = row.original; + return ( +
+ + {item.title} + + {item.labels.length > 0 && ( +
+ {item.labels.map(l => ( + + {l} + + ))} +
+ )} +
+ ); + }, + }, + { + id: 'type', + accessorKey: 'type', + header: 'Type', + cell: ({ row }) => ( + + {row.original.type} + + ), + }, + { + id: 'status', + accessorKey: 'status', + header: 'Status', + cell: ({ row }) => ( + + {row.original.status.replace(/_/g, ' ')} + + ), + }, + { + id: 'priority', + accessorKey: 'priority', + header: 'Priority', + cell: ({ row }) => ( + + {row.original.priority} + + ), + }, + { id: 'voteCount', accessorKey: 'voteCount', header: 'Votes' }, + { id: 'commentCount', accessorKey: 'commentCount', header: 'Comments' }, + { + id: 'actions', + header: 'Actions', + enableSorting: false, + cell: ({ row }) => ( + + ), + }, + ], + [handleDelete] + ); return (
@@ -163,88 +254,21 @@ export default function ItemsListPage() {
- {/* Items table */} + {/* Items table — @bytelyst/data-table (Wave 9.C.9) */} {loading ? (
Loading...
- ) : items.length === 0 ? ( -
- No items found. Create one to get started. -
) : ( -
- - - - - - - - - - - - - - {items.map(item => ( - - - - - - - - - - ))} - -
TitleTypeStatusPriorityVotesCommentsActions
- - {item.title} - - {item.labels.length > 0 && ( -
- {item.labels.map(l => ( - - {l} - - ))} -
- )} -
- - {item.type} - - - - {item.status.replace(/_/g, ' ')} - - - - {item.priority} - - {item.voteCount} - {item.commentCount} - - -
-
+ item.id} + enableFilter={false} + enableSorting + enablePagination + pageSize={15} + emptyState="No items found. Create one to get started." + /> )} {/* Create modal */} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6cb6b4c9..8f3a6c3f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -249,6 +249,9 @@ importers: '@bytelyst/dashboard-components': specifier: workspace:* version: link:../../packages/dashboard-components + '@bytelyst/data-table': + specifier: workspace:* + version: link:../../packages/data-table '@bytelyst/errors': specifier: workspace:* version: link:../../packages/errors