fix: revert leftover Project→Client mislabels, drop tracked .env.backfill
Rename in 91045a1 was only partially reverted by later commits, leaving
~20 UI spots calling the project entity "Client" while table headers
already said "Project". Reverted all of them plus the /clients/:id
route (now /projects/:id canonical again, nothing was live yet).
Also: stop tracking .env.backfill (had a live admin password/token
committed - rotate those credentials), remove stale pre-redesign
layout.md superseded by REDESIGN-LAYOUT2.md.
This commit is contained in:
@@ -239,7 +239,7 @@ function TeamCompanies() {
|
||||
)}
|
||||
<div className="card" style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
|
||||
{companies.length === 0 ? (
|
||||
<div className="card-empty-center">No clients</div>
|
||||
<div className="card-empty-center">No companies</div>
|
||||
) : (
|
||||
<div className="table-wrapper" style={{ flex: 1, minHeight: 0, overflowY: 'auto' }}>
|
||||
<table className="table-sticky-head">
|
||||
|
||||
@@ -154,7 +154,7 @@ export default function CompanyDetail() {
|
||||
};
|
||||
|
||||
const handleDeleteCompany = async () => {
|
||||
if (!window.confirm(`Delete "${company.name}"? This will permanently delete all clients, jobs, files, and data. This cannot be undone.`)) return;
|
||||
if (!window.confirm(`Delete "${company.name}"? This will permanently delete all projects, jobs, files, and data. This cannot be undone.`)) return;
|
||||
await deleteCompanyData(id);
|
||||
navigate('/company');
|
||||
};
|
||||
@@ -309,7 +309,7 @@ export default function CompanyDetail() {
|
||||
<div className="stat-card">
|
||||
<div className="stat-icon">📁</div>
|
||||
<div className="stat-value">{projects.length}</div>
|
||||
<div className="stat-label">Clients</div>
|
||||
<div className="stat-label">Projects</div>
|
||||
</div>
|
||||
<div className="stat-card">
|
||||
<div className="stat-icon">⚡</div>
|
||||
@@ -477,16 +477,16 @@ export default function CompanyDetail() {
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
{isTeam && <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||
<button className="btn btn-primary btn-sm" onClick={() => setShowNewProject(s => !s)}>
|
||||
{showNewProject ? 'Cancel' : '+ New Client'}
|
||||
{showNewProject ? 'Cancel' : '+ New Project'}
|
||||
</button>
|
||||
</div>}
|
||||
|
||||
{showNewProject && (
|
||||
<div className="card">
|
||||
<div className="card-title">New Client</div>
|
||||
<div className="card-title">New Project</div>
|
||||
<form onSubmit={handleCreateProject} style={{ display: 'flex', gap: 8, alignItems: 'flex-end' }}>
|
||||
<div className="form-group" style={{ flex: 1, marginBottom: 0 }}>
|
||||
<label>Client Name *</label>
|
||||
<label>Project Name *</label>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="e.g. Brand Identity 2026"
|
||||
@@ -504,7 +504,7 @@ export default function CompanyDetail() {
|
||||
)}
|
||||
|
||||
{projects.length === 0 ? (
|
||||
<div className="card card-empty-center">No clients</div>
|
||||
<div className="card card-empty-center">No projects</div>
|
||||
) : (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
|
||||
{projects.map(project => {
|
||||
@@ -513,7 +513,7 @@ export default function CompanyDetail() {
|
||||
const done = projectTasks.filter(t => t.status === 'client_approved').length;
|
||||
return (
|
||||
<div key={project.id} className="interactive-surface" style={{ display: 'flex', alignItems: 'center', background: 'var(--card-bg)', border: 'var(--card-border)', borderRadius: 4, overflow: 'hidden' }}>
|
||||
<Link to={`/clients/${project.id}`} className="interactive-row" style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '14px 16px', textDecoration: 'none', cursor: 'pointer' }}>
|
||||
<Link to={`/projects/${project.id}`} className="interactive-row" style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '14px 16px', textDecoration: 'none', cursor: 'pointer' }}>
|
||||
<div>
|
||||
<div style={{ fontWeight: 400, fontSize: 14, color: 'var(--text-primary)' }}>{project.name}</div>
|
||||
<div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 3 }}>
|
||||
@@ -529,7 +529,7 @@ export default function CompanyDetail() {
|
||||
type="button"
|
||||
onClick={() => handleDeleteProject(project)}
|
||||
style={{ background: 'none', border: 'none', borderLeft: '1px solid var(--border)', color: 'var(--danger, var(--danger-strong))', cursor: 'pointer', fontSize: 16, padding: '0 14px', alignSelf: 'stretch', display: 'flex', alignItems: 'center' }}
|
||||
title="Delete client"
|
||||
title="Delete project"
|
||||
>✕</button>}
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -392,7 +392,7 @@ export default function ProfilePage() {
|
||||
const ACTION_LABEL = {
|
||||
task_created: 'Task created', task_started: 'Task started', task_on_hold: 'Task put on hold',
|
||||
task_resumed: 'Task resumed', task_submitted: 'Task submitted', task_approved: 'Task approved',
|
||||
project_created: 'Client created', request_submitted: 'Request submitted', revision_requested: 'Task rejected',
|
||||
project_created: 'Project created', request_submitted: 'Request submitted', revision_requested: 'Task rejected',
|
||||
};
|
||||
|
||||
const toSentenceTitle = (value = '') => {
|
||||
|
||||
@@ -232,7 +232,7 @@ export default function ProjectDetailPage() {
|
||||
};
|
||||
|
||||
if (loading) return <Layout><PageLoader /></Layout>;
|
||||
if (!project) return <Layout><p style={{ padding: 24, color: 'var(--text-muted)' }}>Client not found.</p></Layout>;
|
||||
if (!project) return <Layout><p style={{ padding: 24, color: 'var(--text-muted)' }}>Project not found.</p></Layout>;
|
||||
|
||||
const rows = tasks.map(task => {
|
||||
const derived = getTaskDerivedState(task, submissions, deliveries);
|
||||
@@ -506,7 +506,7 @@ export default function ProjectDetailPage() {
|
||||
<th style={{ ...TASK_TABLE_TH_STYLE, padding: '0 0 12px' }} />
|
||||
<SortTh col="name" sortKey={subSortKey} sortDir={subSortDir} onSort={toggleSubSort} style={TASK_TABLE_TH_STYLE}>Name</SortTh>
|
||||
<SortTh col="email" sortKey={subSortKey} sortDir={subSortDir} onSort={toggleSubSort} style={TASK_TABLE_TH_STYLE}>Email</SortTh>
|
||||
<SortTh col="projects" sortKey={subSortKey} sortDir={subSortDir} onSort={toggleSubSort} style={{ ...TASK_TABLE_TH_STYLE, textAlign: 'center' }}>Clients</SortTh>
|
||||
<SortTh col="projects" sortKey={subSortKey} sortDir={subSortDir} onSort={toggleSubSort} style={{ ...TASK_TABLE_TH_STYLE, textAlign: 'center' }}>Projects</SortTh>
|
||||
<th style={{ ...TASK_TABLE_TH_STYLE, padding: '0 0 12px' }} />
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
@@ -757,9 +757,9 @@ export default function TaskDetail() {
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="var(--text-muted)" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
|
||||
<div>
|
||||
<div style={CARD_META_LABEL}>Client</div>
|
||||
<div style={CARD_META_LABEL}>Project</div>
|
||||
{project
|
||||
? <Link to={`/clients/${project.id}`} className="table-link" style={{ fontSize: 13 }}>{project.name}</Link>
|
||||
? <Link to={`/projects/${project.id}`} className="table-link" style={{ fontSize: 13 }}>{project.name}</Link>
|
||||
: <div style={{ fontSize: 13, color: 'var(--text-muted)' }}>—</div>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+5
-5
@@ -435,7 +435,7 @@ export default function RequestsPage() {
|
||||
setAddProjectSaving(true); setAddProjectError('');
|
||||
try {
|
||||
const name = addProjectForm.name.trim();
|
||||
if (!name) throw new Error('Client name required.');
|
||||
if (!name) throw new Error('Project name required.');
|
||||
if (!addProjectForm.companyId) throw new Error('Company required.');
|
||||
const { data: newProject, error: insertError } = await supabase
|
||||
.from('projects').insert({ name, company_id: addProjectForm.companyId, status: 'active' })
|
||||
@@ -530,7 +530,7 @@ export default function RequestsPage() {
|
||||
];
|
||||
|
||||
const PROJECT_FILTER_OPTIONS = [
|
||||
{ value: '', label: 'All Clients' },
|
||||
{ value: '', label: 'All Projects' },
|
||||
...projects
|
||||
.filter(p => !filterCompany || p.company_id === filterCompany)
|
||||
.slice()
|
||||
@@ -609,7 +609,7 @@ export default function RequestsPage() {
|
||||
<Link to={`/tasks/${row.id}`} className="table-link">{row.title || '—'}</Link>
|
||||
</td>
|
||||
<td className="tcol-project" style={{ ...TASK_TABLE_TD_BASE, fontSize: 13, color: 'var(--text-primary)' }}>
|
||||
{project ? <Link to={`/clients/${project.id}`} className="table-link">{project.name}</Link> : '—'}
|
||||
{project ? <Link to={`/projects/${project.id}`} className="table-link">{project.name}</Link> : '—'}
|
||||
</td>
|
||||
<td style={{ ...TASK_TABLE_TD_BASE, textAlign: 'center' }}>
|
||||
{(() => {
|
||||
@@ -656,7 +656,7 @@ export default function RequestsPage() {
|
||||
<div style={popupOverlayStyle} onClick={() => { setShowAddProjectForm(false); setAddProjectForm({ name: '', companyId: '' }); setAddProjectError(''); }}>
|
||||
<div style={PROJECT_MODAL_SHELL_STYLE} onClick={e => e.stopPropagation()}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: 8, marginBottom: 14 }}>
|
||||
<div style={TASK_MODAL_TITLE_STYLE}>Add Client</div>
|
||||
<div style={TASK_MODAL_TITLE_STYLE}>Add Project</div>
|
||||
</div>
|
||||
<form onSubmit={handleAddProject}>
|
||||
<div className="form-group">
|
||||
@@ -667,7 +667,7 @@ export default function RequestsPage() {
|
||||
</select>
|
||||
</div>
|
||||
<div className="form-group">
|
||||
<label style={TASK_MODAL_LABEL_STYLE}>Client Name *</label>
|
||||
<label style={TASK_MODAL_LABEL_STYLE}>Project Name *</label>
|
||||
<input type="text" placeholder="e.g. Brand Identity 2026" value={addProjectForm.name} onChange={e => setAddProjectForm(f => ({ ...f, name: e.target.value }))} required autoFocus style={TASK_MODAL_INPUT_STYLE} />
|
||||
</div>
|
||||
{addProjectError && <div style={{ fontSize: 13, color: 'var(--danger)', marginBottom: 12 }}>{addProjectError}</div>}
|
||||
|
||||
@@ -208,9 +208,9 @@ export default function CreateSubcontractorPO() {
|
||||
</select>
|
||||
</div>
|
||||
<div className="form-group" style={{ marginBottom: 0 }}>
|
||||
<label>Client</label>
|
||||
<label>Project</label>
|
||||
<select value={form.project_id} onChange={e => handleProjectChange(e.target.value)}>
|
||||
<option value="">No client</option>
|
||||
<option value="">No project</option>
|
||||
{projects.map(project => (
|
||||
<option key={project.id} value={project.id}>
|
||||
{project.name}{project.company?.name ? ` · ${project.company.name}` : ''}
|
||||
@@ -224,7 +224,7 @@ export default function CreateSubcontractorPO() {
|
||||
{form.project_id && (
|
||||
<div className="card" style={{ marginBottom: 24 }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 12 }}>
|
||||
<div className="card-title" style={{ marginBottom: 0 }}>Client Tasks</div>
|
||||
<div className="card-title" style={{ marginBottom: 0 }}>Project Tasks</div>
|
||||
{availableTasks.length > 0 && (
|
||||
<button className="btn btn-outline btn-sm" onClick={() => availableTasks.forEach(task => addTaskAsLineItem(task))}>
|
||||
+ Add All
|
||||
|
||||
@@ -414,7 +414,7 @@ function HotItemsCard({ submissions, tasks, isClient = false, isExternal = false
|
||||
<thead style={{ background: 'transparent' }}>
|
||||
<tr style={{ background: 'transparent' }}>
|
||||
<SortTh col="task" sortKey={sortKey} sortDir={sortDir} onSort={toggle} style={{ fontSize: 10, fontWeight: 500, color: 'var(--text-muted)', textAlign: 'left', letterSpacing: 0.6, textTransform: 'uppercase', padding: '0 0 12px 5px', border: 'none', background: 'transparent', verticalAlign: 'top' }}>Task</SortTh>
|
||||
<SortTh col="project" sortKey={sortKey} sortDir={sortDir} onSort={toggle} style={{ fontSize: 10, fontWeight: 500, color: 'var(--text-muted)', textAlign: 'left', letterSpacing: 0.6, textTransform: 'uppercase', padding: '0 0 12px 0', border: 'none', background: 'transparent', verticalAlign: 'top' }}>Client</SortTh>
|
||||
<SortTh col="project" sortKey={sortKey} sortDir={sortDir} onSort={toggle} style={{ fontSize: 10, fontWeight: 500, color: 'var(--text-muted)', textAlign: 'left', letterSpacing: 0.6, textTransform: 'uppercase', padding: '0 0 12px 0', border: 'none', background: 'transparent', verticalAlign: 'top' }}>Project</SortTh>
|
||||
<SortTh col="status" sortKey={sortKey} sortDir={sortDir} onSort={toggle} style={{ fontSize: 10, fontWeight: 500, color: 'var(--text-muted)', textAlign: 'center', letterSpacing: 0.6, textTransform: 'uppercase', padding: '0 0 12px 0', border: 'none', background: 'transparent', verticalAlign: 'top' }}>Status</SortTh>
|
||||
<SortTh col="deadline" sortKey={sortKey} sortDir={sortDir} onSort={toggle} style={{ fontSize: 10, fontWeight: 500, color: 'var(--text-muted)', textAlign: 'center', letterSpacing: 0.6, textTransform: 'uppercase', padding: '0 0 12px 0', border: 'none', background: 'transparent', verticalAlign: 'top' }}>Due By</SortTh>
|
||||
</tr>
|
||||
|
||||
@@ -187,13 +187,13 @@ const invNewItem = (description = '', unit_price = '', quantity = 1, task_id = n
|
||||
({ id: crypto.randomUUID(), description, unit_price, quantity, task_id, submission_id });
|
||||
|
||||
const invBuildNewItemDescription = (task) => {
|
||||
const projectName = task.project?.name || 'No Client';
|
||||
const projectName = task.project?.name || 'No Project';
|
||||
const taskTitle = task.title || task.service_type || 'Untitled';
|
||||
return `${projectName} • ${taskTitle}`;
|
||||
};
|
||||
|
||||
const invBuildRevisionItemDescription = (revision) => {
|
||||
const projectName = revision.task?.project?.name || 'No Client';
|
||||
const projectName = revision.task?.project?.name || 'No Project';
|
||||
const taskTitle = revision.task?.title || revision.service_type || 'Revision';
|
||||
const versionLabel = 'R' + String(revision.version_number || 0).padStart(2, '0');
|
||||
return `${projectName} • ${taskTitle} • Revision ${versionLabel}`;
|
||||
|
||||
@@ -278,7 +278,7 @@ export default function TeamReports() {
|
||||
|
||||
autoTable(doc, {
|
||||
startY: 78,
|
||||
head: [['Company', 'Client', 'Task', 'Version', 'Type', 'Version Status', 'Client Billing', 'Sub Billing']],
|
||||
head: [['Company', 'Project', 'Task', 'Version', 'Type', 'Version Status', 'Client Billing', 'Sub Billing']],
|
||||
body: sortedRows.map((row) => [
|
||||
row.company_name,
|
||||
row.project_name,
|
||||
@@ -329,9 +329,9 @@ export default function TeamReports() {
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ fontSize: 11, fontWeight: 500, color: 'var(--text-secondary)', letterSpacing: 0.8, textTransform: 'uppercase', marginBottom: 4 }}>Client</div>
|
||||
<div style={{ fontSize: 11, fontWeight: 500, color: 'var(--text-secondary)', letterSpacing: 0.8, textTransform: 'uppercase', marginBottom: 4 }}>Project</div>
|
||||
<select value={projectFilter} onChange={(event) => setProjectFilter(event.target.value)}>
|
||||
<option value="all">All Clients</option>
|
||||
<option value="all">All Projects</option>
|
||||
{filteredProjects.map((project) => (
|
||||
<option key={project.id} value={project.id}>{project.name}</option>
|
||||
))}
|
||||
@@ -385,7 +385,7 @@ export default function TeamReports() {
|
||||
<thead>
|
||||
<tr>
|
||||
<SortTh col="company_name" sortKey={sortKey} sortDir={sortDir} onSort={toggle}>Company</SortTh>
|
||||
<SortTh col="project_name" sortKey={sortKey} sortDir={sortDir} onSort={toggle}>Client</SortTh>
|
||||
<SortTh col="project_name" sortKey={sortKey} sortDir={sortDir} onSort={toggle}>Project</SortTh>
|
||||
<SortTh col="task_title" sortKey={sortKey} sortDir={sortDir} onSort={toggle}>Task Name</SortTh>
|
||||
<SortTh col="version_number" sortKey={sortKey} sortDir={sortDir} onSort={toggle}>R#</SortTh>
|
||||
<SortTh col="version_type" sortKey={sortKey} sortDir={sortDir} onSort={toggle}>Type</SortTh>
|
||||
|
||||
@@ -158,7 +158,7 @@ export default function SubcontractorPODetail() {
|
||||
<div className="detail-item"><label>PO Date</label><p>{new Date(po.date).toLocaleDateString()}</p></div>
|
||||
<div className="detail-item"><label>Due Date</label><p>{po.due_date ? new Date(po.due_date).toLocaleDateString() : '—'}</p></div>
|
||||
<div className="detail-item"><label>Terms</label><p>{po.terms || 'Net 15'}</p></div>
|
||||
<div className="detail-item"><label>Client</label><p>{po.project?.name || 'No client'}</p></div>
|
||||
<div className="detail-item"><label>Project</label><p>{po.project?.name || 'No project'}</p></div>
|
||||
<div className="detail-item"><label>Company</label><p>{po.project?.company?.name || '—'}</p></div>
|
||||
<div className="detail-item"><label>Total</label><p style={{ fontSize: 18, fontWeight: 400, color: 'var(--accent)' }}>${Number(po.amount).toFixed(2)}</p></div>
|
||||
{po.paid_at && <div className="detail-item"><label>Paid On</label><p>{new Date(po.paid_at).toLocaleDateString()}</p></div>}
|
||||
@@ -172,7 +172,7 @@ export default function SubcontractorPODetail() {
|
||||
<table className="table-sticky-head">
|
||||
<thead>
|
||||
<tr>
|
||||
<SortTh col="project" sortKey={sortKey} sortDir={sortDir} onSort={toggle}>Client</SortTh>
|
||||
<SortTh col="project" sortKey={sortKey} sortDir={sortDir} onSort={toggle}>Project</SortTh>
|
||||
<SortTh col="task" sortKey={sortKey} sortDir={sortDir} onSort={toggle}>Task</SortTh>
|
||||
<SortTh col="description" sortKey={sortKey} sortDir={sortDir} onSort={toggle}>Description</SortTh>
|
||||
<SortTh col="amount" sortKey={sortKey} sortDir={sortDir} onSort={toggle} style={{ textAlign: 'right' }}>Amount</SortTh>
|
||||
@@ -181,7 +181,7 @@ export default function SubcontractorPODetail() {
|
||||
<tbody>
|
||||
{tableItems.map(item => (
|
||||
<tr key={item.id}>
|
||||
<td>{po.project?.name || 'No client'}</td>
|
||||
<td>{po.project?.name || 'No project'}</td>
|
||||
<td>{item.task?.title || '—'}</td>
|
||||
<td>{item.description}</td>
|
||||
<td style={{ textAlign: 'right', fontWeight: 400 }}>${Number(item.amount).toFixed(2)}</td>
|
||||
|
||||
Reference in New Issue
Block a user