Session 2026-05-30: tasks/projects unification, code consolidation, file renames

- Unified Tasks page: 3 role render blocks → 1, normalized row shape, single renderRow/sort/tabs
- Fixed role scoping: external = all tasks in member projects, client = all tasks in company projects
- Fixed client bug: was scoped by submitted_by, now company→project→tasks
- Aligned dashboard client scope to match tasks page
- Hot tasks dashboard: now filters to active statuses only (not completed/invoiced/paid)
- Removed Projects.jsx (dead), fixed /project/ → /projects/ links
- Renamed all page files to match folder path (Team*, External*, Client* prefixes)
- Renamed: RequestsPage→Tasks, Settings→Profile, CompaniesPage→Companies, ProjectDetailPage→ProjectDetail
- Dropped dead fetches from Tasks page (invoices, invoice_items, subcontractor_invoice_items)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Krao Hasanee
2026-05-30 10:19:23 -04:00
parent 6fe7ab1059
commit 13ef1f4ded
30 changed files with 1177 additions and 1258 deletions
+23 -19
View File
@@ -5,6 +5,9 @@ import FileAttachment from './FileAttachment';
import { addDaysToDateOnly, getTodayDateOnlyEST } from '../lib/dates';
const defaultDeadline = () => addDaysToDateOnly(getTodayDateOnlyEST(), 3);
const modalLabelStyle = { fontSize: 11, fontWeight: 500, color: 'var(--text-secondary)', textTransform: 'uppercase', letterSpacing: 0.8, marginBottom: 6 };
const modalInputStyle = { fontSize: 13, padding: '0 5px', textAlign: 'left', lineHeight: 1 };
const modalTextAreaStyle = { ...modalInputStyle, minHeight: 100, padding: '8px 5px', lineHeight: 1.4 };
const emptyForm = (companyId = '') => ({
companyId,
project: '',
@@ -123,11 +126,12 @@ export default function RequestForm({
<form onSubmit={handleSubmit}>
{showCompanySelect && (
<div className="form-group">
<label>Company *</label>
<label style={modalLabelStyle}>Company *</label>
<select
value={form.companyId}
onChange={e => setForm(f => ({ ...f, companyId: e.target.value }))}
required
style={modalInputStyle}
>
<option value="">Select company...</option>
{companies.map(co => <option key={co.id} value={co.id}>{co.name}</option>)}
@@ -136,7 +140,7 @@ export default function RequestForm({
)}
<div className="form-group">
<label>Project *</label>
<label style={modalLabelStyle}>Project *</label>
{isTypingProject ? (
<div style={{ display: 'flex', gap: 8 }}>
<input
@@ -146,13 +150,13 @@ export default function RequestForm({
onChange={e => setNewProjectName(e.target.value)}
onKeyDown={e => { if (e.key === 'Enter') { e.preventDefault(); handleAddProject(); } }}
autoFocus
style={{ flex: 1 }}
style={{ ...modalInputStyle, flex: 1 }}
/>
<button type="button" className="btn btn-primary btn-sm" onClick={handleAddProject} disabled={!newProjectName.trim()}>Add</button>
<button type="button" className="btn btn-outline btn-sm" onClick={() => { setIsTypingProject(false); setNewProjectName(''); }}>Cancel</button>
<button type="button" className="btn btn-outline" onClick={handleAddProject} disabled={!newProjectName.trim()}>Add</button>
<button type="button" className="btn btn-outline" onClick={() => { setIsTypingProject(false); setNewProjectName(''); }}>Cancel</button>
</div>
) : (
<select value={form.project} onChange={handleProjectSelect} required disabled={showCompanySelect && !companyId}>
<select value={form.project} onChange={handleProjectSelect} required disabled={showCompanySelect && !companyId} style={modalInputStyle}>
<option value="">{showCompanySelect && !companyId ? 'Select company first' : 'Select a project...'}</option>
{allProjectNames.map(name => <option key={name} value={name}>{name}</option>)}
{(!showCompanySelect || companyId) && <option value="__new__"> Create new project...</option>}
@@ -162,20 +166,20 @@ export default function RequestForm({
<div className="grid-2">
<div className="form-group">
<label>Service Type *</label>
<select value={form.serviceType} onChange={set('serviceType')} required>
<label style={modalLabelStyle}>Service Type *</label>
<select value={form.serviceType} onChange={set('serviceType')} required style={modalInputStyle}>
<option value="">Select service...</option>
{serviceTypes.map(s => <option key={s} value={s}>{s}</option>)}
</select>
</div>
<div className="form-group">
<label>Desired Deadline</label>
<input type="date" value={form.deadline} onChange={set('deadline')} />
<label style={modalLabelStyle}>Desired Deadline</label>
<input type="date" value={form.deadline} onChange={set('deadline')} style={modalInputStyle} />
</div>
</div>
<div className="form-group" style={{ marginTop: -4 }}>
<label style={{ display: 'flex', alignItems: 'center', gap: 10, fontWeight: 500, cursor: 'pointer' }}>
<label style={{ ...modalLabelStyle, display: 'flex', alignItems: 'center', gap: 10, cursor: 'pointer', marginBottom: 0 }}>
<input type="checkbox" checked={form.isHot} onChange={e => setForm(f => ({ ...f, isHot: e.target.checked }))} />
<span>Mark as Hot</span>
</label>
@@ -183,8 +187,8 @@ export default function RequestForm({
{showRequester && (
<div className="form-group">
<label>Requested By *</label>
<select value={form.requestedBy} onChange={set('requestedBy')} disabled={!companyId} required>
<label style={modalLabelStyle}>Requested By *</label>
<select value={form.requestedBy} onChange={set('requestedBy')} disabled={!companyId} required style={modalInputStyle}>
<option value="">{companyId ? 'Select requester...' : 'Select company first'}</option>
{requesterOptions.map(user => (
<option key={user.id} value={user.id}>{user.name}{user.email ? ` (${user.email})` : ''}</option>
@@ -194,21 +198,21 @@ export default function RequestForm({
)}
<div className="form-group">
<label>Request Title *</label>
<input type="text" placeholder="e.g. City, State or Site Name" value={form.title} onChange={set('title')} required />
<label style={modalLabelStyle}>Request Title *</label>
<input type="text" placeholder="e.g. City, State or Site Name" value={form.title} onChange={set('title')} required style={modalInputStyle} />
</div>
<div className="form-group">
<label>Description *</label>
<textarea placeholder="Notes on the request..." value={form.description} onChange={set('description')} style={{ minHeight: 100 }} required />
<label style={modalLabelStyle}>Description *</label>
<textarea placeholder="Notes on the request..." value={form.description} onChange={set('description')} style={modalTextAreaStyle} required />
</div>
<FileAttachment files={files} onChange={setFiles} />
{error && <div style={{ fontSize: 13, color: 'var(--danger)', marginBottom: 12 }}> {error}</div>}
<div className="action-buttons">
<button type="submit" className="btn btn-primary" disabled={saving}>
<div className="action-buttons" style={{ justifyContent: 'flex-end' }}>
<button type="submit" className="btn btn-outline" disabled={saving}>
{saving ? 'Submitting...' : submitLabel}
</button>
{onCancel && <button type="button" className="btn btn-outline" onClick={onCancel}>Cancel</button>}