Fix table overflow and add checkbox for seen status

This commit is contained in:
Francisco Pessano
2025-07-13 21:05:47 -03:00
committed by GitHub
parent 6438fcb12f
commit 13d7866faa
2 changed files with 74 additions and 75 deletions

View File

@@ -66,7 +66,7 @@ export function ProjectCard({ project, onSeenStatusChange }: ProjectCardProps) {
{/* Project Actions */} {/* Project Actions */}
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<> <div className="flex items-center space-x-4">
<div className="flex items-center space-x-4"> <div className="flex items-center space-x-4">
{/* Project URL */} {/* Project URL */}
{project.project_url && ( {project.project_url && (
@@ -95,26 +95,24 @@ export function ProjectCard({ project, onSeenStatusChange }: ProjectCardProps) {
)} )}
</div> </div>
{/* Seen Toggle */} </div>
<Button
variant={seen ? "secondary" : "outline"} {/* Seen Checkbox */}
size="sm" <div className="flex items-center space-x-2">
onClick={toggleSeen} <input
className="ml-auto" type="checkbox"
id={`seen-${project.id}`}
className="w-4 h-4 rounded border-border bg-background"
checked={seen}
onChange={toggleSeen}
/>
<label
htmlFor={`seen-${project.id}`}
className="text-sm text-muted-foreground cursor-pointer"
> >
{seen ? ( Seen
<> </label>
<X className="w-4 h-4 mr-1" /> </div>
Mark Unseen
</>
) : (
<>
<Check className="w-4 h-4 mr-1" />
Mark Seen
</>
)}
</Button>
</>
</div> </div>
{/* Engagement Stats */} {/* Engagement Stats */}

View File

@@ -68,6 +68,36 @@ export function ProjectsTable({ projects, title, showUrlColumn = true, onSeenSta
}; };
const columns = useMemo(() => [ const columns = useMemo(() => [
columnHelper.display({
id: 'seen',
header: ({ table }) => (
<div className="flex justify-center">
<input
type="checkbox"
className="w-4 h-4 rounded border-border bg-background"
checked={table.getIsAllRowsSelected()}
indeterminate={table.getIsSomeRowsSelected()}
onChange={table.getToggleAllRowsSelectedHandler()}
/>
</div>
),
cell: ({ row }) => {
const projectId = row.original.id;
const isSeen = seenProjects.has(projectId);
return (
<div className="flex justify-center">
<input
type="checkbox"
className="w-4 h-4 rounded border-border bg-background"
checked={isSeen}
onChange={() => toggleSeen(projectId)}
/>
</div>
);
},
size: 60,
}),
columnHelper.accessor('author_name', { columnHelper.accessor('author_name', {
header: ({ column }) => ( header: ({ column }) => (
<Button <Button
@@ -96,7 +126,7 @@ export function ProjectsTable({ projects, title, showUrlColumn = true, onSeenSta
</div> </div>
</div> </div>
), ),
size: 240, size: 200,
}), }),
columnHelper.accessor('project_description', { columnHelper.accessor('project_description', {
header: ({ column }) => ( header: ({ column }) => (
@@ -120,7 +150,7 @@ export function ProjectsTable({ projects, title, showUrlColumn = true, onSeenSta
<p className="text-sm leading-relaxed line-clamp-3 pr-2">{row.original.project_description}</p> <p className="text-sm leading-relaxed line-clamp-3 pr-2">{row.original.project_description}</p>
</div> </div>
), ),
size: 400, size: 350,
}), }),
...(showUrlColumn ? [ ...(showUrlColumn ? [
columnHelper.accessor('project_url', { columnHelper.accessor('project_url', {
@@ -262,38 +292,6 @@ export function ProjectsTable({ projects, title, showUrlColumn = true, onSeenSta
), ),
size: 140, size: 140,
}), }),
columnHelper.display({
id: 'actions',
header: 'Actions',
cell: ({ row }) => {
const projectId = row.original.id;
const isSeen = seenProjects.has(projectId);
return (
<div className="w-full flex justify-center">
<Button
variant={isSeen ? "secondary" : "outline"}
size="sm"
onClick={() => toggleSeen(projectId)}
className="h-8"
>
{isSeen ? (
<>
<X className="w-3 h-3 mr-1" />
Unseen
</>
) : (
<>
<Check className="w-3 h-3 mr-1" />
Seen
</>
)}
</Button>
</div>
);
},
size: 120,
}),
], [showUrlColumn, seenProjects]); ], [showUrlColumn, seenProjects]);
const table = useReactTable({ const table = useReactTable({
@@ -327,7 +325,7 @@ export function ProjectsTable({ projects, title, showUrlColumn = true, onSeenSta
}, [projects]); }, [projects]);
// Calculate total width // Calculate total width
const totalWidth = table.getHeaderGroups()[0]?.headers.reduce((acc, header) => acc + header.getSize(), 0) || 1000; const totalWidth = table.getHeaderGroups()[0]?.headers.reduce((acc, header) => acc + header.getSize(), 0) || 800;
return ( return (
<div className="space-y-4"> <div className="space-y-4">
@@ -367,36 +365,37 @@ export function ProjectsTable({ projects, title, showUrlColumn = true, onSeenSta
</div> </div>
{/* Table Container */} {/* Table Container */}
<div className="border rounded-lg overflow-hidden bg-card"> <div className="border rounded-lg bg-card">
<div className="w-full" style={{ minWidth: `${totalWidth}px` }}> <div className="overflow-x-auto">
<div className="w-full" style={{ minWidth: `${totalWidth}px` }}>
{/* Fixed Header */} {/* Fixed Header */}
<div className="sticky top-0 z-10 bg-muted/50 border-b"> <div className="sticky top-0 z-10 bg-muted/50 border-b">
{table.getHeaderGroups().map((headerGroup) => ( {table.getHeaderGroups().map((headerGroup) => (
<div key={headerGroup.id} className="grid" style={{ gridTemplateColumns: headerGroup.headers.map(h => `${h.getSize()}px`).join(' ') }}> <div key={headerGroup.id} className="grid" style={{ gridTemplateColumns: headerGroup.headers.map(h => `${h.getSize()}px`).join(' ') }}>
{headerGroup.headers.map((header) => ( {headerGroup.headers.map((header) => (
<div <div
key={header.id} key={header.id}
className="px-4 py-3 text-left font-medium border-r border-border last:border-r-0 flex items-center" className="px-4 py-3 text-left font-medium border-r border-border last:border-r-0 flex items-center"
> >
{header.isPlaceholder {header.isPlaceholder
? null ? null
: flexRender(header.column.columnDef.header, header.getContext())} : flexRender(header.column.columnDef.header, header.getContext())}
</div> </div>
))} ))}
</div> </div>
))} ))}
</div> </div>
{/* Virtual Scrollable Body */} {/* Virtual Scrollable Body */}
<div <div
ref={parentRef} ref={parentRef}
className="h-[600px] overflow-auto relative" className="h-[600px] overflow-auto relative"
> >
<div style={{ height: `${virtualizer.getTotalSize()}px` }}> <div style={{ height: `${virtualizer.getTotalSize()}px` }}>
{virtualizer.getVirtualItems().map((virtualRow) => { {virtualizer.getVirtualItems().map((virtualRow) => {
const row = rows[virtualRow.index]; const row = rows[virtualRow.index];
return ( return (
<div <div
key={row.id} key={row.id}
className={`absolute w-full border-b border-border hover:bg-muted/50 transition-colors grid ${seenProjects.has(row.original.id) ? 'opacity-60 bg-muted/30' : ''}`} className={`absolute w-full border-b border-border hover:bg-muted/50 transition-colors grid ${seenProjects.has(row.original.id) ? 'opacity-60 bg-muted/30' : ''}`}
style={{ style={{
@@ -404,22 +403,24 @@ export function ProjectsTable({ projects, title, showUrlColumn = true, onSeenSta
transform: `translateY(${virtualRow.start}px)`, transform: `translateY(${virtualRow.start}px)`,
gridTemplateColumns: row.getVisibleCells().map(cell => `${cell.column.getSize()}px`).join(' ') gridTemplateColumns: row.getVisibleCells().map(cell => `${cell.column.getSize()}px`).join(' ')
}} }}
> >
{row.getVisibleCells().map((cell) => ( {row.getVisibleCells().map((cell) => (
<div <div
key={cell.id} key={cell.id}
className="px-4 py-3 border-r border-border last:border-r-0 flex items-center overflow-hidden" className="px-4 py-3 border-r border-border last:border-r-0 flex items-center overflow-hidden"
> >
{flexRender(cell.column.columnDef.cell, cell.getContext())} {flexRender(cell.column.columnDef.cell, cell.getContext())}
</div> </div>
))} ))}
</div> </div>
); );
})} })}
</div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div>
</div> </div>
); );
} }