Add client filter tabs to team Projects page

This commit is contained in:
Krao Hasanee
2026-05-14 15:23:39 -04:00
parent b2f165a8f1
commit 4980ebb09a
2 changed files with 58 additions and 10 deletions
+16
View File
@@ -1254,3 +1254,19 @@ select option { background: #222; color: #fff; }
/* Version timeline */ /* Version timeline */
.version-item { padding: 12px; } .version-item { padding: 12px; }
} }
/* Tab bar */
.tab-btn {
padding: 6px 14px;
border-radius: 20px;
border: 1px solid var(--border);
background: transparent;
color: var(--text-muted);
font-size: 13px;
font-weight: 500;
cursor: pointer;
transition: all 0.15s;
white-space: nowrap;
}
.tab-btn:hover { border-color: var(--interactive-hover-border); color: var(--text-secondary); }
.tab-btn.active { background: var(--accent); border-color: var(--accent); color: #000; font-weight: 600; }
+42 -10
View File
@@ -1,4 +1,4 @@
import { useEffect, useState } from 'react'; import { useEffect, useState, useMemo } from 'react';
import { useNavigate } from 'react-router-dom'; import { useNavigate } from 'react-router-dom';
import Layout from '../../components/Layout'; import Layout from '../../components/Layout';
import StatusBadge from '../../components/StatusBadge'; import StatusBadge from '../../components/StatusBadge';
@@ -9,6 +9,7 @@ export default function TeamProjects() {
const [projects, setProjects] = useState([]); const [projects, setProjects] = useState([]);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [search, setSearch] = useState(''); const [search, setSearch] = useState('');
const [activeTab, setActiveTab] = useState('all');
useEffect(() => { useEffect(() => {
supabase supabase
@@ -21,11 +22,21 @@ export default function TeamProjects() {
}); });
}, []); }, []);
const filtered = projects.filter(p => const companies = useMemo(() => {
!search || const seen = new Map();
p.name.toLowerCase().includes(search.toLowerCase()) || projects.forEach(p => {
p.company?.name?.toLowerCase().includes(search.toLowerCase()) if (p.company?.id && !seen.has(p.company.id)) seen.set(p.company.id, p.company.name);
); });
return [...seen.entries()].sort((a, b) => a[1].localeCompare(b[1]));
}, [projects]);
const filtered = projects.filter(p => {
const matchesTab = activeTab === 'all' || p.company?.id === activeTab;
const matchesSearch = !search ||
p.name.toLowerCase().includes(search.toLowerCase()) ||
p.company?.name?.toLowerCase().includes(search.toLowerCase());
return matchesTab && matchesSearch;
});
return ( return (
<Layout> <Layout>
@@ -36,13 +47,34 @@ export default function TeamProjects() {
</div> </div>
<input <input
type="text" type="text"
placeholder="Search projects or clients..." placeholder="Search projects..."
value={search} value={search}
onChange={e => setSearch(e.target.value)} onChange={e => setSearch(e.target.value)}
style={{ width: 240 }} style={{ width: 220 }}
/> />
</div> </div>
<div className="tab-bar" style={{ marginBottom: 20, display: 'flex', gap: 4, flexWrap: 'wrap' }}>
<button
className={`tab-btn${activeTab === 'all' ? ' active' : ''}`}
onClick={() => setActiveTab('all')}
>
All ({projects.length})
</button>
{companies.map(([id, name]) => {
const count = projects.filter(p => p.company?.id === id).length;
return (
<button
key={id}
className={`tab-btn${activeTab === id ? ' active' : ''}`}
onClick={() => setActiveTab(id)}
>
{name} ({count})
</button>
);
})}
</div>
{loading ? ( {loading ? (
<p style={{ padding: 24, color: 'var(--text-muted)' }}>Loading...</p> <p style={{ padding: 24, color: 'var(--text-muted)' }}>Loading...</p>
) : filtered.length === 0 ? ( ) : filtered.length === 0 ? (
@@ -56,7 +88,7 @@ export default function TeamProjects() {
<thead> <thead>
<tr> <tr>
<th>Project</th> <th>Project</th>
<th>Client</th> {activeTab === 'all' && <th>Client</th>}
<th>Status</th> <th>Status</th>
<th>Started</th> <th>Started</th>
</tr> </tr>
@@ -65,7 +97,7 @@ export default function TeamProjects() {
{filtered.map(p => ( {filtered.map(p => (
<tr key={p.id} style={{ cursor: 'pointer' }} onClick={() => navigate(`/projects/${p.id}`)}> <tr key={p.id} style={{ cursor: 'pointer' }} onClick={() => navigate(`/projects/${p.id}`)}>
<td style={{ fontWeight: 600 }}>{p.name}</td> <td style={{ fontWeight: 600 }}>{p.name}</td>
<td style={{ color: 'var(--text-muted)' }}>{p.company?.name || '—'}</td> {activeTab === 'all' && <td style={{ color: 'var(--text-muted)' }}>{p.company?.name || '—'}</td>}
<td><StatusBadge status={p.status} /></td> <td><StatusBadge status={p.status} /></td>
<td style={{ color: 'var(--text-muted)' }}>{new Date(p.created_at).toLocaleDateString()}</td> <td style={{ color: 'var(--text-muted)' }}>{new Date(p.created_at).toLocaleDateString()}</td>
</tr> </tr>