Projects: collapse by default + company tabs
- ProjectGroup starts collapsed (open=false) - Multi-company clients see tab switcher matching Invoices page style - Single-company clients: no tabs, no change Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,7 @@ import { withTimeout } from '../../lib/withTimeout';
|
||||
const rLabel = (v) => 'R' + String(v || 0).padStart(2, '0');
|
||||
|
||||
function ProjectGroup({ project, tasks, submissions, currentUserId, filter }) {
|
||||
const [open, setOpen] = useState(true);
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
const filteredTasks = filter === 'mine'
|
||||
? tasks.filter(task => {
|
||||
@@ -115,6 +115,8 @@ export default function MyProjects() {
|
||||
const [submissions, setSubmissions] = useState([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [filter, setFilter] = useState('all'); // 'all' | 'mine'
|
||||
const companies = currentUser.companies?.length ? currentUser.companies : (currentUser.company ? [currentUser.company] : []);
|
||||
const [activeCompanyId, setActiveCompanyId] = useState(() => companies[0]?.id || null);
|
||||
|
||||
useEffect(() => {
|
||||
async function load() {
|
||||
@@ -187,6 +189,27 @@ export default function MyProjects() {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{companies.length > 1 && (
|
||||
<div style={{ marginBottom: 16, display: 'flex', alignItems: 'center', gap: 10, flexWrap: 'wrap' }} className="card-title">
|
||||
{companies.map((company, index) => (
|
||||
<span key={company.id} style={{ display: 'inline-flex', alignItems: 'center', gap: 10 }}>
|
||||
{index > 0 && <span style={{ color: 'var(--text-muted)' }}>|</span>}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setActiveCompanyId(company.id)}
|
||||
style={{
|
||||
background: 'none', border: 'none', padding: 0, margin: 0,
|
||||
cursor: 'pointer', font: 'inherit', textTransform: 'inherit', letterSpacing: 'inherit',
|
||||
color: activeCompanyId === company.id ? 'var(--text-primary)' : 'var(--text-muted)',
|
||||
}}
|
||||
>
|
||||
{company.name}
|
||||
</button>
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{projects.length === 0 ? (
|
||||
<div className="empty-state">
|
||||
<h3>No projects yet</h3>
|
||||
@@ -194,47 +217,26 @@ export default function MyProjects() {
|
||||
<Link to="/new-project" className="btn btn-primary" style={{ marginTop: 16 }}>+ New Project</Link>
|
||||
</div>
|
||||
) : (() => {
|
||||
const companies = currentUser.companies || (currentUser.company ? [currentUser.company] : []);
|
||||
const multiCompany = companies.length > 1;
|
||||
const companyMap = Object.fromEntries(companies.map(c => [c.id, c.name]));
|
||||
const grouped = companies
|
||||
.map(c => ({ company: c, projects: projects.filter(p => p.company_id === c.id) }))
|
||||
.filter(g => g.projects.length > 0);
|
||||
const ungrouped = projects.filter(p => !companies.some(c => c.id === p.company_id));
|
||||
const visibleProjects = companies.length > 1
|
||||
? projects.filter(p => p.company_id === activeCompanyId)
|
||||
: projects;
|
||||
|
||||
return (
|
||||
<>
|
||||
{grouped.map(({ company, projects: groupProjects }) => (
|
||||
<div key={company.id}>
|
||||
{multiCompany && (
|
||||
<div style={{ fontSize: 11, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', color: 'var(--text-muted)', marginBottom: 8, marginTop: 16, paddingLeft: 2 }}>
|
||||
{company.name}
|
||||
if (visibleProjects.length === 0) return (
|
||||
<div className="empty-state">
|
||||
<h3>No projects for this company</h3>
|
||||
</div>
|
||||
)}
|
||||
{groupProjects.map(project => (
|
||||
<ProjectGroup
|
||||
key={project.id}
|
||||
project={project}
|
||||
tasks={tasks.filter(t => t.project_id === project.id)}
|
||||
submissions={submissions}
|
||||
currentUserId={currentUser.id}
|
||||
filter={filter}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
{ungrouped.map(project => (
|
||||
<ProjectGroup
|
||||
key={project.id}
|
||||
project={project}
|
||||
tasks={tasks.filter(t => t.project_id === project.id)}
|
||||
submissions={submissions}
|
||||
currentUserId={currentUser.id}
|
||||
filter={filter}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
|
||||
return visibleProjects.map(project => (
|
||||
<ProjectGroup
|
||||
key={project.id}
|
||||
project={project}
|
||||
tasks={tasks.filter(t => t.project_id === project.id)}
|
||||
submissions={submissions}
|
||||
currentUserId={currentUser.id}
|
||||
filter={filter}
|
||||
/>
|
||||
));
|
||||
})()}
|
||||
</Layout>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user