Improve table styling and make headers more horizontal

- Add visible column borders to teams and projects tables
- Make header 2px border for visual separation
- Consolidate teams page header: title + inline stats on left, create button on right
- Consolidate team dashboard header: title/badge/slug + description + inline stats on left, action buttons on right
This commit is contained in:
Mondo Diaz
2026-01-28 15:57:11 +00:00
parent aece9e0b9f
commit 000540727c
4 changed files with 112 additions and 139 deletions

View File

@@ -3,10 +3,18 @@
} }
.team-header { .team-header {
margin-bottom: 1.5rem; display: flex;
justify-content: space-between;
align-items: flex-start;
gap: 1.5rem;
margin-bottom: 2rem;
} }
.team-header-info { .team-header-left {
flex: 1;
}
.team-header-title {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 0.75rem; gap: 0.75rem;
@@ -15,20 +23,8 @@
.team-header h1 { .team-header h1 {
margin: 0; margin: 0;
font-size: 1.75rem; font-size: 1.5rem;
} font-weight: 600;
.team-description {
margin: 0 0 0.5rem;
color: var(--color-text-secondary);
font-size: 1rem;
max-width: 600px;
}
.team-meta {
display: flex;
align-items: center;
gap: 1rem;
} }
.team-slug { .team-slug {
@@ -36,36 +32,31 @@
color: var(--color-text-muted); color: var(--color-text-muted);
} }
.team-stats { .team-description {
margin: 0 0 0.5rem;
color: var(--color-text-secondary);
font-size: 0.9375rem;
max-width: 600px;
}
.team-stats-inline {
display: flex; display: flex;
align-items: center;
gap: 1rem; gap: 1rem;
margin-bottom: 1.5rem; color: var(--color-text-muted);
font-size: 0.875rem;
} }
.stat-card { .team-stats-inline .stat-value {
background: var(--color-bg-secondary);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: 1rem 1.5rem;
min-width: 120px;
}
.stat-value {
font-size: 1.5rem;
font-weight: 600; font-weight: 600;
color: var(--color-text); color: var(--color-text);
font-size: 0.875rem;
} }
.stat-label { .team-header-actions {
font-size: 0.8125rem;
color: var(--color-text-muted);
margin-top: 0.25rem;
}
.team-actions {
display: flex; display: flex;
gap: 0.75rem; gap: 0.5rem;
margin-bottom: 2rem; flex-shrink: 0;
} }
.team-section { .team-section {
@@ -106,18 +97,24 @@
letter-spacing: 0.05em; letter-spacing: 0.05em;
color: var(--color-text-muted); color: var(--color-text-muted);
background: var(--color-bg-secondary); background: var(--color-bg-secondary);
border-bottom: 1px solid var(--color-border); border-bottom: 2px solid var(--color-border);
border-right: 1px solid var(--color-border);
}
.projects-table th:last-child {
border-right: none;
} }
.projects-table td { .projects-table td {
padding: 0.75rem 1rem; padding: 0.75rem 1rem;
font-size: 0.875rem; font-size: 0.875rem;
border-bottom: 1px solid var(--color-border); border-bottom: 1px solid var(--color-border);
border-right: 1px solid var(--color-border);
vertical-align: middle; vertical-align: middle;
} }
.projects-table tbody tr:last-child td { .projects-table td:last-child {
border-bottom: none; border-right: none;
} }
.project-row { .project-row {

View File

@@ -95,54 +95,46 @@ function TeamDashboardPage() {
/> />
<div className="team-header"> <div className="team-header">
<div className="team-header-info"> <div className="team-header-left">
<h1>{team.name}</h1> <div className="team-header-title">
{team.user_role && ( <h1>{team.name}</h1>
<Badge variant={roleVariants[team.user_role] || 'default'}> {team.user_role && (
{team.user_role} <Badge variant={roleVariants[team.user_role] || 'default'}>
</Badge> {team.user_role}
</Badge>
)}
<span className="team-slug">@{team.slug}</span>
</div>
{team.description && (
<p className="team-description">{team.description}</p>
)} )}
<div className="team-stats-inline">
<span><span className="stat-value">{team.project_count}</span> projects</span>
<span><span className="stat-value">{team.member_count}</span> members</span>
</div>
</div> </div>
{team.description && ( {isAdminOrOwner && (
<p className="team-description">{team.description}</p> <div className="team-header-actions">
<Link to={`/teams/${slug}/members`} className="btn btn-secondary">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
<circle cx="9" cy="7" r="4"/>
<path d="M23 21v-2a4 4 0 0 0-3-3.87"/>
<path d="M16 3.13a4 4 0 0 1 0 7.75"/>
</svg>
Members
</Link>
<Link to={`/teams/${slug}/settings`} className="btn btn-secondary">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<circle cx="12" cy="12" r="3"/>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
</svg>
Settings
</Link>
</div>
)} )}
<div className="team-meta">
<span className="team-slug">@{team.slug}</span>
</div>
</div> </div>
<div className="team-stats">
<div className="stat-card">
<div className="stat-value">{team.project_count}</div>
<div className="stat-label">Projects</div>
</div>
<div className="stat-card">
<div className="stat-value">{team.member_count}</div>
<div className="stat-label">Members</div>
</div>
</div>
{isAdminOrOwner && (
<div className="team-actions">
<Link to={`/teams/${slug}/settings`} className="btn btn-secondary">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<circle cx="12" cy="12" r="3"/>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
</svg>
Settings
</Link>
<Link to={`/teams/${slug}/members`} className="btn btn-secondary">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
<circle cx="9" cy="7" r="4"/>
<path d="M23 21v-2a4 4 0 0 0-3-3.87"/>
<path d="M16 3.13a4 4 0 0 1 0 7.75"/>
</svg>
Members
</Link>
</div>
)}
{showProjectForm && ( {showProjectForm && (
<div className="modal-overlay" onClick={() => setShowProjectForm(false)}> <div className="modal-overlay" onClick={() => setShowProjectForm(false)}>
<div className="modal-content" onClick={e => e.stopPropagation()}> <div className="modal-content" onClick={e => e.stopPropagation()}>

View File

@@ -8,50 +8,40 @@
.teams-header { .teams-header {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: flex-start; align-items: center;
margin-bottom: 2rem; margin-bottom: 1.5rem;
gap: 1rem; gap: 1rem;
} }
.teams-header__content {
display: flex;
align-items: center;
gap: 1.5rem;
}
.teams-header__content h1 { .teams-header__content h1 {
margin: 0; margin: 0;
font-size: 1.75rem;
font-weight: 600;
}
.teams-header__subtitle {
margin: 0.25rem 0 0;
color: var(--color-text-muted);
font-size: 0.9375rem;
}
/* Stats */
.teams-stats {
display: flex;
gap: 2rem;
padding: 1rem 1.5rem;
background: var(--color-bg-secondary);
border-radius: var(--radius-lg);
margin-bottom: 1.5rem;
}
.teams-stat {
display: flex;
flex-direction: column;
align-items: center;
}
.teams-stat__value {
font-size: 1.5rem; font-size: 1.5rem;
font-weight: 600; font-weight: 600;
color: var(--color-text);
} }
.teams-stat__label { .teams-header__stats {
font-size: 0.75rem; display: flex;
align-items: center;
gap: 1rem;
color: var(--color-text-muted); color: var(--color-text-muted);
text-transform: uppercase; font-size: 0.875rem;
letter-spacing: 0.05em; }
.teams-header__stats span {
display: flex;
align-items: center;
gap: 0.375rem;
}
.teams-header__stats .stat-value {
font-weight: 600;
color: var(--color-text);
} }
/* Search */ /* Search */
@@ -203,18 +193,24 @@
letter-spacing: 0.05em; letter-spacing: 0.05em;
color: var(--color-text-muted); color: var(--color-text-muted);
background: var(--color-bg-secondary); background: var(--color-bg-secondary);
border-bottom: 1px solid var(--color-border); border-bottom: 2px solid var(--color-border);
border-right: 1px solid var(--color-border);
}
.teams-table th:last-child {
border-right: none;
} }
.teams-table td { .teams-table td {
padding: 0.75rem 1rem; padding: 0.75rem 1rem;
font-size: 0.875rem; font-size: 0.875rem;
border-bottom: 1px solid var(--color-border); border-bottom: 1px solid var(--color-border);
border-right: 1px solid var(--color-border);
vertical-align: middle; vertical-align: middle;
} }
.teams-table tbody tr:last-child td { .teams-table td:last-child {
border-bottom: none; border-right: none;
} }
.team-row { .team-row {

View File

@@ -115,7 +115,13 @@ function TeamsPage() {
<div className="teams-header"> <div className="teams-header">
<div className="teams-header__content"> <div className="teams-header__content">
<h1>Teams</h1> <h1>Teams</h1>
<p className="teams-header__subtitle">Organize projects and collaborate with your team</p> {!loading && totalTeams > 0 && (
<div className="teams-header__stats">
<span><span className="stat-value">{totalTeams}</span> teams</span>
<span><span className="stat-value">{ownedTeams}</span> owned</span>
<span><span className="stat-value">{totalProjects}</span> projects</span>
</div>
)}
</div> </div>
<button className="btn btn-primary" onClick={() => setShowForm(true)}> <button className="btn btn-primary" onClick={() => setShowForm(true)}>
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"> <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
@@ -126,24 +132,6 @@ function TeamsPage() {
</button> </button>
</div> </div>
{/* Stats */}
{!loading && totalTeams > 0 && (
<div className="teams-stats">
<div className="teams-stat">
<span className="teams-stat__value">{totalTeams}</span>
<span className="teams-stat__label">Teams</span>
</div>
<div className="teams-stat">
<span className="teams-stat__value">{ownedTeams}</span>
<span className="teams-stat__label">Owned</span>
</div>
<div className="teams-stat">
<span className="teams-stat__value">{totalProjects}</span>
<span className="teams-stat__label">Projects</span>
</div>
</div>
)}
{/* Search */} {/* Search */}
{!loading && totalTeams > 3 && ( {!loading && totalTeams > 3 && (
<div className="teams-search"> <div className="teams-search">