From 2b9c03915715a4a860967ffb65c7803421bff3ad Mon Sep 17 00:00:00 2001 From: Mondo Diaz Date: Wed, 28 Jan 2026 16:13:32 +0000 Subject: [PATCH] Use DataTable component for teams and projects tables Consistent table styling across the app with: - Row hover highlighting - Clickable rows - Standard cell padding and borders - Proper header styling --- frontend/src/pages/TeamDashboardPage.css | 81 +--------------- frontend/src/pages/TeamDashboardPage.tsx | 116 ++++++++++++----------- frontend/src/pages/TeamsPage.css | 55 +---------- frontend/src/pages/TeamsPage.tsx | 96 ++++++++++--------- 4 files changed, 112 insertions(+), 236 deletions(-) diff --git a/frontend/src/pages/TeamDashboardPage.css b/frontend/src/pages/TeamDashboardPage.css index 88d2aa1..5c9c4d2 100644 --- a/frontend/src/pages/TeamDashboardPage.css +++ b/frontend/src/pages/TeamDashboardPage.css @@ -61,93 +61,18 @@ font-size: 1.25rem; } -/* Projects Table */ -.projects-table-container { - background: var(--color-bg); - border: 1px solid var(--color-border); - border-radius: var(--radius-md); - overflow: hidden; -} - -.projects-table { - width: 100%; - border-collapse: collapse; -} - -.projects-table th { - text-align: left; - padding: 0.75rem 1rem; - font-size: 0.75rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.05em; - color: var(--color-text-muted); - background: var(--color-bg-secondary); - border-bottom: 1px solid rgba(0, 0, 0, 0.1); - border-right: 1px solid rgba(0, 0, 0, 0.06); -} - -.projects-table th:last-child { - border-right: none; -} - -.projects-table td { - padding: 0.75rem 1rem; - font-size: 0.875rem; - vertical-align: middle; - border-bottom: 1px solid rgba(0, 0, 0, 0.06); - border-right: 1px solid rgba(0, 0, 0, 0.06); -} - -.projects-table td:last-child { - border-right: none; -} - -.projects-table tbody tr:last-child td { - border-bottom: none; -} - -.project-row { - cursor: pointer; - transition: background 0.1s ease; -} - -.project-row:hover { - background: var(--color-bg-secondary); -} - -.project-name-link { - font-weight: 500; - color: var(--color-text); - text-decoration: none; -} - -.project-name-link:hover { - color: var(--color-primary); -} - -.project-description-cell { - color: var(--color-text-secondary); - max-width: 300px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - +/* Table utility classes */ .text-muted { color: var(--color-text-muted); } -.actions-cell { - width: 48px; - text-align: right; -} - .btn-ghost { background: transparent; color: var(--color-text-muted); border: none; padding: 0.375rem; + cursor: pointer; + border-radius: var(--radius-sm); } .btn-ghost:hover { diff --git a/frontend/src/pages/TeamDashboardPage.tsx b/frontend/src/pages/TeamDashboardPage.tsx index 56e7bb3..00acbce 100644 --- a/frontend/src/pages/TeamDashboardPage.tsx +++ b/frontend/src/pages/TeamDashboardPage.tsx @@ -5,6 +5,7 @@ import { getTeam, listTeamProjects, createProject } from '../api'; import { useAuth } from '../contexts/AuthContext'; import { Badge } from '../components/Badge'; import { Breadcrumb } from '../components/Breadcrumb'; +import { DataTable } from '../components/DataTable'; import './TeamDashboardPage.css'; function TeamDashboardPage() { @@ -202,64 +203,65 @@ function TeamDashboardPage() { )} ) : ( -
- - - - - - - - {isAdminOrOwner && } - - - - {projects?.items.map(project => ( - navigate(`/project/${project.name}`)} + project.id} + onRowClick={(project) => navigate(`/project/${project.name}`)} + columns={[ + { + key: 'name', + header: 'Name', + render: (project) => ( + e.stopPropagation()} > - - - - - {isAdminOrOwner && ( - - )} - - ))} - -
NameDescriptionVisibilityCreated By
- e.stopPropagation()} - > - {project.name} - - - {project.description || } - - - {project.is_public ? 'Public' : 'Private'} - - {project.created_by} - -
-
+ {project.name} + + ), + }, + { + key: 'description', + header: 'Description', + className: 'cell-description', + render: (project) => project.description || , + }, + { + key: 'visibility', + header: 'Visibility', + render: (project) => ( + + {project.is_public ? 'Public' : 'Private'} + + ), + }, + { + key: 'created_by', + header: 'Created By', + render: (project) => {project.created_by}, + }, + ...(isAdminOrOwner ? [{ + key: 'actions', + header: '', + render: (project: Project) => ( + + ), + }] : []), + ]} + /> )} {projects && projects.pagination.total > 10 && ( diff --git a/frontend/src/pages/TeamsPage.css b/frontend/src/pages/TeamsPage.css index a081e29..56a0428 100644 --- a/frontend/src/pages/TeamsPage.css +++ b/frontend/src/pages/TeamsPage.css @@ -171,60 +171,7 @@ color: var(--color-text-muted); } -/* Table */ -.teams-table-container { - background: var(--color-bg); - border: 1px solid var(--color-border); - border-radius: var(--radius-md); - overflow: hidden; -} - -.teams-table { - width: 100%; - border-collapse: collapse; -} - -.teams-table th { - text-align: left; - padding: 0.75rem 1rem; - font-size: 0.75rem; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.05em; - color: var(--color-text-muted); - background: var(--color-bg-secondary); - border-bottom: 1px solid rgba(0, 0, 0, 0.1); - border-right: 1px solid rgba(0, 0, 0, 0.06); -} - -.teams-table th:last-child { - border-right: none; -} - -.teams-table td { - padding: 0.75rem 1rem; - font-size: 0.875rem; - vertical-align: middle; - border-bottom: 1px solid rgba(0, 0, 0, 0.06); - border-right: 1px solid rgba(0, 0, 0, 0.06); -} - -.teams-table td:last-child { - border-right: none; -} - -.teams-table tbody tr:last-child td { - border-bottom: none; -} - -.team-row { - cursor: pointer; - transition: background 0.1s ease; -} - -.team-row:hover { - background: var(--color-bg-secondary); -} +/* Table cell styles */ .team-name-cell { display: flex; diff --git a/frontend/src/pages/TeamsPage.tsx b/frontend/src/pages/TeamsPage.tsx index 102acf1..9a08dc4 100644 --- a/frontend/src/pages/TeamsPage.tsx +++ b/frontend/src/pages/TeamsPage.tsx @@ -4,6 +4,7 @@ import { TeamDetail, TeamCreate, PaginatedResponse } from '../types'; import { listTeams, createTeam } from '../api'; import { useAuth } from '../contexts/AuthContext'; import { Badge } from '../components/Badge'; +import { DataTable } from '../components/DataTable'; import './TeamsPage.css'; function TeamsPage() { @@ -265,53 +266,54 @@ function TeamsPage() { )} ) : ( -
- - - - - - - - - - - - {filteredTeams.map(team => ( - navigate(`/teams/${team.slug}`)} - > - - - - - - - ))} - -
NameDescriptionRoleMembersProjects
-
- e.stopPropagation()} - > - {team.name} - - @{team.slug} -
-
- {team.description || } - - {team.user_role && ( - - {roleConfig[team.user_role]?.label || team.user_role} - - )} - {team.member_count}{team.project_count}
-
+ team.id} + onRowClick={(team) => navigate(`/teams/${team.slug}`)} + columns={[ + { + key: 'name', + header: 'Name', + render: (team) => ( +
+ e.stopPropagation()} + > + {team.name} + + @{team.slug} +
+ ), + }, + { + key: 'description', + header: 'Description', + className: 'cell-description', + render: (team) => team.description || , + }, + { + key: 'role', + header: 'Role', + render: (team) => team.user_role ? ( + + {roleConfig[team.user_role]?.label || team.user_role} + + ) : null, + }, + { + key: 'members', + header: 'Members', + render: (team) => {team.member_count}, + }, + { + key: 'projects', + header: 'Projects', + render: (team) => {team.project_count}, + }, + ]} + /> )} );