Add client filter tabs to team Projects page
This commit is contained in:
@@ -1254,3 +1254,19 @@ select option { background: #222; color: #fff; }
|
||||
/* Version timeline */
|
||||
.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; }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useEffect, useState, useMemo } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import Layout from '../../components/Layout';
|
||||
import StatusBadge from '../../components/StatusBadge';
|
||||
@@ -9,6 +9,7 @@ export default function TeamProjects() {
|
||||
const [projects, setProjects] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [search, setSearch] = useState('');
|
||||
const [activeTab, setActiveTab] = useState('all');
|
||||
|
||||
useEffect(() => {
|
||||
supabase
|
||||
@@ -21,11 +22,21 @@ export default function TeamProjects() {
|
||||
});
|
||||
}, []);
|
||||
|
||||
const filtered = projects.filter(p =>
|
||||
!search ||
|
||||
p.name.toLowerCase().includes(search.toLowerCase()) ||
|
||||
p.company?.name?.toLowerCase().includes(search.toLowerCase())
|
||||
);
|
||||
const companies = useMemo(() => {
|
||||
const seen = new Map();
|
||||
projects.forEach(p => {
|
||||
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 (
|
||||
<Layout>
|
||||
@@ -36,13 +47,34 @@ export default function TeamProjects() {
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search projects or clients..."
|
||||
placeholder="Search projects..."
|
||||
value={search}
|
||||
onChange={e => setSearch(e.target.value)}
|
||||
style={{ width: 240 }}
|
||||
style={{ width: 220 }}
|
||||
/>
|
||||
</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 ? (
|
||||
<p style={{ padding: 24, color: 'var(--text-muted)' }}>Loading...</p>
|
||||
) : filtered.length === 0 ? (
|
||||
@@ -56,7 +88,7 @@ export default function TeamProjects() {
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Project</th>
|
||||
<th>Client</th>
|
||||
{activeTab === 'all' && <th>Client</th>}
|
||||
<th>Status</th>
|
||||
<th>Started</th>
|
||||
</tr>
|
||||
@@ -65,7 +97,7 @@ export default function TeamProjects() {
|
||||
{filtered.map(p => (
|
||||
<tr key={p.id} style={{ cursor: 'pointer' }} onClick={() => navigate(`/projects/${p.id}`)}>
|
||||
<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 style={{ color: 'var(--text-muted)' }}>{new Date(p.created_at).toLocaleDateString()}</td>
|
||||
</tr>
|
||||
|
||||
Reference in New Issue
Block a user