Full codebase cleanup and optimization pass
- Fix all hardcoded light colors breaking dark mode (FileAttachment, TaskDetail, RequestDetail) - Parallelize sequential DB fetches in TaskDetail, CompanyDetail, MyProjects - Add error handling: NewRequest project/file upload, MyCompany update, CompanyDetail prices, AuthContext profile fetch - Fix currentUser.company_id → currentUser.company?.id in NewRequest - Remove stale company.email references from InvoiceDetail, ProjectDetail, TaskDetail - Clean up dead email field from Companies form reset Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -46,7 +46,7 @@ export default function FileAttachment({ files, onChange }) {
|
|||||||
<div style={{
|
<div style={{
|
||||||
border: `2px dashed ${files.length > 0 ? 'var(--accent)' : 'var(--border)'}`,
|
border: `2px dashed ${files.length > 0 ? 'var(--accent)' : 'var(--border)'}`,
|
||||||
borderRadius: 8, padding: '18px 16px', textAlign: 'center',
|
borderRadius: 8, padding: '18px 16px', textAlign: 'center',
|
||||||
background: files.length > 0 ? '#fffbeb' : '#fafafa', transition: 'all 0.15s',
|
background: 'var(--bg)', transition: 'all 0.15s',
|
||||||
}}>
|
}}>
|
||||||
<input type="file" multiple onChange={handleChange} style={{ display: 'none' }} id="req-file-upload" />
|
<input type="file" multiple onChange={handleChange} style={{ display: 'none' }} id="req-file-upload" />
|
||||||
<label htmlFor="req-file-upload" style={{ cursor: 'pointer' }}>
|
<label htmlFor="req-file-upload" style={{ cursor: 'pointer' }}>
|
||||||
@@ -67,7 +67,7 @@ export default function FileAttachment({ files, onChange }) {
|
|||||||
{files.map((file, i) => (
|
{files.map((file, i) => (
|
||||||
<div key={i} style={{
|
<div key={i} style={{
|
||||||
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
|
display: 'flex', alignItems: 'center', justifyContent: 'space-between',
|
||||||
padding: '7px 12px', background: 'white', borderRadius: 8, border: '1px solid var(--border)',
|
padding: '7px 12px', background: 'var(--card-bg)', borderRadius: 8, border: '1px solid var(--border)',
|
||||||
}}>
|
}}>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||||
<span>📄</span>
|
<span>📄</span>
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export function AuthProvider({ children }) {
|
|||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
const fetchAndCacheProfile = async (authUser) => {
|
const fetchAndCacheProfile = async (authUser) => {
|
||||||
|
try {
|
||||||
const { data } = await supabase
|
const { data } = await supabase
|
||||||
.from('profiles')
|
.from('profiles')
|
||||||
.select('*, company:companies(id, name, phone, address)')
|
.select('*, company:companies(id, name, phone, address)')
|
||||||
@@ -26,6 +27,9 @@ export function AuthProvider({ children }) {
|
|||||||
setCurrentUser(profile);
|
setCurrentUser(profile);
|
||||||
localStorage.setItem(PROFILE_CACHE_KEY, JSON.stringify(profile));
|
localStorage.setItem(PROFILE_CACHE_KEY, JSON.stringify(profile));
|
||||||
}
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('Profile fetch failed:', err);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -27,12 +27,13 @@ export default function MyCompany() {
|
|||||||
const handleSave = async (e) => {
|
const handleSave = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
await supabase.from('companies').update({
|
const { error } = await supabase.from('companies').update({
|
||||||
name: form.name.trim(),
|
name: form.name.trim(),
|
||||||
phone: form.phone.trim(),
|
phone: form.phone.trim(),
|
||||||
address: form.address.trim(),
|
address: form.address.trim(),
|
||||||
}).eq('id', company.id);
|
}).eq('id', company.id);
|
||||||
setSaving(false);
|
setSaving(false);
|
||||||
|
if (error) { alert('Failed to save. Please try again.'); return; }
|
||||||
setEditing(false);
|
setEditing(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -125,15 +125,11 @@ export default function MyProjects() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function load() {
|
async function load() {
|
||||||
const { data: p } = await supabase
|
const [{ data: p }, { data: t }] = await Promise.all([
|
||||||
.from('projects').select('*').order('created_at', { ascending: false });
|
supabase.from('projects').select('*').order('created_at', { ascending: false }),
|
||||||
|
supabase.from('tasks').select('*').order('submitted_at', { ascending: false }),
|
||||||
|
]);
|
||||||
setProjects(p || []);
|
setProjects(p || []);
|
||||||
|
|
||||||
if (!p || p.length === 0) { setLoading(false); return; }
|
|
||||||
|
|
||||||
const { data: t } = await supabase
|
|
||||||
.from('tasks').select('*').in('project_id', p.map(pr => pr.id))
|
|
||||||
.order('submitted_at', { ascending: false });
|
|
||||||
setTasks(t || []);
|
setTasks(t || []);
|
||||||
|
|
||||||
if (t && t.length > 0) {
|
if (t && t.length > 0) {
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export default function NewRequest() {
|
|||||||
const [existingProjects, setExistingProjects] = useState([]);
|
const [existingProjects, setExistingProjects] = useState([]);
|
||||||
const [submitted, setSubmitted] = useState(false);
|
const [submitted, setSubmitted] = useState(false);
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
|
const [error, setError] = useState(null);
|
||||||
const [form, setForm] = useState({ project: preselectedProject, serviceType: '', title: '', deadline: '', description: '' });
|
const [form, setForm] = useState({ project: preselectedProject, serviceType: '', title: '', deadline: '', description: '' });
|
||||||
const [files, setFiles] = useState([]);
|
const [files, setFiles] = useState([]);
|
||||||
const [customProjects, setCustomProjects] = useState([]);
|
const [customProjects, setCustomProjects] = useState([]);
|
||||||
@@ -24,16 +25,16 @@ export default function NewRequest() {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function load() {
|
async function load() {
|
||||||
if (!currentUser.company_id) return;
|
if (!currentUser.company?.id) return;
|
||||||
const { data: p } = await supabase
|
const { data: p } = await supabase
|
||||||
.from('projects')
|
.from('projects')
|
||||||
.select('id, name')
|
.select('id, name')
|
||||||
.eq('company_id', currentUser.company_id)
|
.eq('company_id', currentUser.company.id)
|
||||||
.order('created_at', { ascending: false });
|
.order('created_at', { ascending: false });
|
||||||
setExistingProjects((p || []).map(pr => ({ id: pr.id, name: pr.name })));
|
setExistingProjects((p || []).map(pr => ({ id: pr.id, name: pr.name })));
|
||||||
}
|
}
|
||||||
load();
|
load();
|
||||||
}, [currentUser.company_id]);
|
}, [currentUser.company?.id]);
|
||||||
|
|
||||||
const allProjectNames = [
|
const allProjectNames = [
|
||||||
...existingProjects.map(p => p.name),
|
...existingProjects.map(p => p.name),
|
||||||
@@ -64,11 +65,12 @@ export default function NewRequest() {
|
|||||||
|
|
||||||
const handleSubmit = async (e) => {
|
const handleSubmit = async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (!currentUser.company_id) {
|
if (!currentUser.company?.id) {
|
||||||
alert('Your account is not yet assigned to a company. Please contact support.');
|
alert('Your account is not yet assigned to a company. Please contact support.');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
setSaving(true);
|
setSaving(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
// Find existing project by name within this company, or create new one
|
// Find existing project by name within this company, or create new one
|
||||||
let projectId;
|
let projectId;
|
||||||
@@ -76,12 +78,13 @@ export default function NewRequest() {
|
|||||||
if (existing) {
|
if (existing) {
|
||||||
projectId = existing.id;
|
projectId = existing.id;
|
||||||
} else {
|
} else {
|
||||||
const { data: newProject } = await supabase.from('projects').insert({
|
const { data: newProject, error: projectError } = await supabase.from('projects').insert({
|
||||||
company_id: currentUser.company_id,
|
company_id: currentUser.company.id,
|
||||||
name: form.project,
|
name: form.project,
|
||||||
status: 'active',
|
status: 'active',
|
||||||
}).select().single();
|
}).select().single();
|
||||||
projectId = newProject?.id;
|
if (projectError || !newProject) { setError('Failed to create project.'); setSaving(false); return; }
|
||||||
|
projectId = newProject.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!projectId) { setSaving(false); return; }
|
if (!projectId) { setSaving(false); return; }
|
||||||
@@ -112,7 +115,12 @@ export default function NewRequest() {
|
|||||||
if (submission && files.length > 0) {
|
if (submission && files.length > 0) {
|
||||||
for (const file of files) {
|
for (const file of files) {
|
||||||
const path = `${task.id}/${Date.now()}_${file.name}`;
|
const path = `${task.id}/${Date.now()}_${file.name}`;
|
||||||
const { data: uploaded } = await supabase.storage.from('submissions').upload(path, file);
|
const { data: uploaded, error: uploadError } = await supabase.storage.from('submissions').upload(path, file);
|
||||||
|
if (uploadError) {
|
||||||
|
setError(`Failed to upload "${file.name}": ${uploadError.message}`);
|
||||||
|
setSaving(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (uploaded) {
|
if (uploaded) {
|
||||||
await supabase.from('submission_files').insert({
|
await supabase.from('submission_files').insert({
|
||||||
submission_id: submission.id,
|
submission_id: submission.id,
|
||||||
@@ -139,7 +147,7 @@ export default function NewRequest() {
|
|||||||
setSubmitted(true);
|
setSubmitted(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (!currentUser.company_id) {
|
if (!currentUser.company?.id) {
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
<div style={{ maxWidth: 480, margin: '0 auto', textAlign: 'center', paddingTop: 48 }}>
|
<div style={{ maxWidth: 480, margin: '0 auto', textAlign: 'center', paddingTop: 48 }}>
|
||||||
@@ -251,6 +259,12 @@ export default function NewRequest() {
|
|||||||
|
|
||||||
<FileAttachment files={files} onChange={setFiles} />
|
<FileAttachment files={files} onChange={setFiles} />
|
||||||
|
|
||||||
|
{error && (
|
||||||
|
<div className="notification notification-error" style={{ marginBottom: 16 }}>
|
||||||
|
{error}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="notification notification-info" style={{ marginBottom: 16 }}>
|
<div className="notification notification-info" style={{ marginBottom: 16 }}>
|
||||||
Submitting as <strong>{currentUser?.name}</strong> · {currentUser?.company?.name}
|
Submitting as <strong>{currentUser?.name}</strong> · {currentUser?.company?.name}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ export default function RequestDetail() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{action === 'confirm-delete' && (
|
{action === 'confirm-delete' && (
|
||||||
<div className="card" style={{ background: '#fef2f2', borderColor: '#fecaca', marginBottom: 24 }}>
|
<div className="card" style={{ background: 'var(--bg)', borderColor: 'var(--danger)', marginBottom: 24 }}>
|
||||||
<div style={{ fontWeight: 600, marginBottom: 8 }}>⚠ Delete this request?</div>
|
<div style={{ fontWeight: 600, marginBottom: 8 }}>⚠ Delete this request?</div>
|
||||||
<p style={{ fontSize: 13, color: 'var(--text-secondary)', marginBottom: 16 }}>
|
<p style={{ fontSize: 13, color: 'var(--text-secondary)', marginBottom: 16 }}>
|
||||||
This will permanently delete <strong>{titleWithVersion}</strong> and all its history. This cannot be undone.
|
This will permanently delete <strong>{titleWithVersion}</strong> and all its history. This cannot be undone.
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ export default function Companies() {
|
|||||||
setSaving(false);
|
setSaving(false);
|
||||||
if (data) {
|
if (data) {
|
||||||
setShowNew(false);
|
setShowNew(false);
|
||||||
setNewForm({ name: '', email: '', phone: '' });
|
setNewForm({ name: '', phone: '', address: '' });
|
||||||
navigate(`/companies/${data.id}`);
|
navigate(`/companies/${data.id}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,27 +25,20 @@ export default function CompanyDetail() {
|
|||||||
}, [id]);
|
}, [id]);
|
||||||
|
|
||||||
async function load() {
|
async function load() {
|
||||||
const [{ data: co }, { data: p }, { data: pr }, { data: u }, { data: unassignedUsers }] = await Promise.all([
|
const [{ data: co }, { data: p }, { data: pr }, { data: u }, { data: unassignedUsers }, { data: t }] = await Promise.all([
|
||||||
supabase.from('companies').select('*').eq('id', id).single(),
|
supabase.from('companies').select('*').eq('id', id).single(),
|
||||||
supabase.from('projects').select('*').eq('company_id', id).order('created_at', { ascending: false }),
|
supabase.from('projects').select('*').eq('company_id', id).order('created_at', { ascending: false }),
|
||||||
supabase.from('company_prices').select('*').eq('company_id', id),
|
supabase.from('company_prices').select('*').eq('company_id', id),
|
||||||
supabase.from('profiles').select('id, name, email, created_at').eq('company_id', id).eq('role', 'client'),
|
supabase.from('profiles').select('id, name, email, created_at').eq('company_id', id).eq('role', 'client'),
|
||||||
supabase.from('profiles').select('id, name, email').eq('role', 'client').is('company_id', null),
|
supabase.from('profiles').select('id, name, email').eq('role', 'client').is('company_id', null),
|
||||||
|
supabase.from('tasks').select('*, project:projects!inner(company_id)').eq('project.company_id', id),
|
||||||
]);
|
]);
|
||||||
setCompany(co);
|
setCompany(co);
|
||||||
const projectList = p || [];
|
setProjects(p || []);
|
||||||
setProjects(projectList);
|
|
||||||
setPrices(pr || []);
|
setPrices(pr || []);
|
||||||
setUsers(u || []);
|
setUsers(u || []);
|
||||||
setUnassigned(unassignedUsers || []);
|
setUnassigned(unassignedUsers || []);
|
||||||
|
|
||||||
if (projectList.length > 0) {
|
|
||||||
const { data: t } = await supabase
|
|
||||||
.from('tasks')
|
|
||||||
.select('*')
|
|
||||||
.in('project_id', projectList.map(pr => pr.id));
|
|
||||||
setTasks(t || []);
|
setTasks(t || []);
|
||||||
}
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -86,11 +79,13 @@ export default function CompanyDetail() {
|
|||||||
const priceVal = getPrice(serviceType);
|
const priceVal = getPrice(serviceType);
|
||||||
const existing = prices.find(p => p.service_type === serviceType && p.id);
|
const existing = prices.find(p => p.service_type === serviceType && p.id);
|
||||||
if (existing) {
|
if (existing) {
|
||||||
await supabase.from('company_prices').update({ price: Number(priceVal) }).eq('id', existing.id);
|
const { error: updateError } = await supabase.from('company_prices').update({ price: Number(priceVal) }).eq('id', existing.id);
|
||||||
|
if (updateError) { setSavingPrice(null); alert('Failed to save price. Please try again.'); return; }
|
||||||
} else {
|
} else {
|
||||||
const { data } = await supabase.from('company_prices').insert({
|
const { data, error: insertError } = await supabase.from('company_prices').insert({
|
||||||
company_id: id, service_type: serviceType, price: Number(priceVal),
|
company_id: id, service_type: serviceType, price: Number(priceVal),
|
||||||
}).select().single();
|
}).select().single();
|
||||||
|
if (insertError) { setSavingPrice(null); alert('Failed to save price. Please try again.'); return; }
|
||||||
if (data) setPrices(prev => prev.map(p => p.service_type === serviceType ? data : p));
|
if (data) setPrices(prev => prev.map(p => p.service_type === serviceType ? data : p));
|
||||||
}
|
}
|
||||||
setSavingPrice(null);
|
setSavingPrice(null);
|
||||||
|
|||||||
@@ -89,7 +89,6 @@ export default function InvoiceDetail() {
|
|||||||
<div className="card">
|
<div className="card">
|
||||||
<div className="card-title">Bill To</div>
|
<div className="card-title">Bill To</div>
|
||||||
<div style={{ fontSize: 15, fontWeight: 700 }}>{company?.name}</div>
|
<div style={{ fontSize: 15, fontWeight: 700 }}>{company?.name}</div>
|
||||||
{company?.email && <div style={{ fontSize: 13, color: 'var(--text-muted)', marginTop: 2 }}>{company.email}</div>}
|
|
||||||
<div style={{ marginTop: 12 }}>
|
<div style={{ marginTop: 12 }}>
|
||||||
<Link to={`/companies/${company?.id}`} className="btn btn-outline btn-sm">View Company</Link>
|
<Link to={`/companies/${company?.id}`} className="btn btn-outline btn-sm">View Company</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ export default function ProjectDetail() {
|
|||||||
<div className="card-title">Project Info</div>
|
<div className="card-title">Project Info</div>
|
||||||
<div className="detail-grid" style={{ marginBottom: 0 }}>
|
<div className="detail-grid" style={{ marginBottom: 0 }}>
|
||||||
<div className="detail-item"><label>Company</label><p>{company?.name || '—'}</p></div>
|
<div className="detail-item"><label>Company</label><p>{company?.name || '—'}</p></div>
|
||||||
<div className="detail-item"><label>Contact</label><p>{company?.email || '—'}</p></div>
|
|
||||||
<div className="detail-item"><label>Status</label><p><StatusBadge status={project.status} /></p></div>
|
<div className="detail-item"><label>Status</label><p><StatusBadge status={project.status} /></p></div>
|
||||||
<div className="detail-item"><label>Started</label><p>{new Date(project.created_at).toLocaleDateString()}</p></div>
|
<div className="detail-item"><label>Started</label><p>{new Date(project.created_at).toLocaleDateString()}</p></div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -37,18 +37,14 @@ export default function TaskDetail() {
|
|||||||
setTask(t);
|
setTask(t);
|
||||||
|
|
||||||
const [{ data: p }, { data: subs }, { data: team }] = await Promise.all([
|
const [{ data: p }, { data: subs }, { data: team }] = await Promise.all([
|
||||||
supabase.from('projects').select('*').eq('id', t.project_id).single(),
|
supabase.from('projects').select('*, company:companies(*)').eq('id', t.project_id).single(),
|
||||||
supabase.from('submissions').select('*, delivery:deliveries(*, files:delivery_files(*))').eq('task_id', id).order('version_number'),
|
supabase.from('submissions').select('*, delivery:deliveries(*, files:delivery_files(*))').eq('task_id', id).order('version_number'),
|
||||||
supabase.from('profiles').select('*').eq('role', 'team'),
|
supabase.from('profiles').select('*').eq('role', 'team'),
|
||||||
]);
|
]);
|
||||||
setProject(p);
|
setProject(p);
|
||||||
|
setCompany(p?.company || null);
|
||||||
setSubmissions(subs || []);
|
setSubmissions(subs || []);
|
||||||
setTeamMembers(team || []);
|
setTeamMembers(team || []);
|
||||||
|
|
||||||
if (p) {
|
|
||||||
const { data: co } = await supabase.from('companies').select('*').eq('id', p.company_id).single();
|
|
||||||
setCompany(co);
|
|
||||||
}
|
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
load();
|
load();
|
||||||
@@ -187,7 +183,7 @@ export default function TaskDetail() {
|
|||||||
<div className="card" style={{ marginBottom: 24, border: '1px solid var(--accent)', borderRadius: 12 }}>
|
<div className="card" style={{ marginBottom: 24, border: '1px solid var(--accent)', borderRadius: 12 }}>
|
||||||
<div className="card-title">Send to Client — {company?.name}</div>
|
<div className="card-title">Send to Client — {company?.name}</div>
|
||||||
<p style={{ fontSize: 13, color: 'var(--text-secondary)', marginBottom: 16 }}>
|
<p style={{ fontSize: 13, color: 'var(--text-secondary)', marginBottom: 16 }}>
|
||||||
Upload the completed file and add an optional message. An email will be sent to <strong>{company?.email}</strong>.
|
Upload the completed file and add an optional message for the client.
|
||||||
</p>
|
</p>
|
||||||
<form onSubmit={handleSendToClient}>
|
<form onSubmit={handleSendToClient}>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
@@ -200,7 +196,7 @@ export default function TaskDetail() {
|
|||||||
<div style={{
|
<div style={{
|
||||||
border: `2px dashed ${sendForm.files.length > 0 ? 'var(--accent)' : 'var(--border)'}`,
|
border: `2px dashed ${sendForm.files.length > 0 ? 'var(--accent)' : 'var(--border)'}`,
|
||||||
borderRadius: 8, padding: '20px 16px', textAlign: 'center',
|
borderRadius: 8, padding: '20px 16px', textAlign: 'center',
|
||||||
background: sendForm.files.length > 0 ? '#fffbeb' : '#fafafa',
|
background: 'var(--bg)',
|
||||||
}}>
|
}}>
|
||||||
<input type="file" multiple onChange={handleFileChange} style={{ display: 'none' }} id="file-upload" />
|
<input type="file" multiple onChange={handleFileChange} style={{ display: 'none' }} id="file-upload" />
|
||||||
<label htmlFor="file-upload" style={{ cursor: 'pointer' }}>
|
<label htmlFor="file-upload" style={{ cursor: 'pointer' }}>
|
||||||
@@ -215,7 +211,7 @@ export default function TaskDetail() {
|
|||||||
{sendForm.files.length > 0 && (
|
{sendForm.files.length > 0 && (
|
||||||
<div style={{ marginTop: 10, display: 'flex', flexDirection: 'column', gap: 6 }}>
|
<div style={{ marginTop: 10, display: 'flex', flexDirection: 'column', gap: 6 }}>
|
||||||
{sendForm.files.map((file, i) => (
|
{sendForm.files.map((file, i) => (
|
||||||
<div key={i} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '8px 12px', background: 'white', borderRadius: 8, border: '1px solid var(--border)' }}>
|
<div key={i} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '8px 12px', background: 'var(--card-bg)', borderRadius: 8, border: '1px solid var(--border)' }}>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||||
<span>📄</span>
|
<span>📄</span>
|
||||||
<div>
|
<div>
|
||||||
@@ -278,12 +274,12 @@ export default function TaskDetail() {
|
|||||||
<button className="btn btn-primary" onClick={handleResume} disabled={saving}>▶ Resume</button>
|
<button className="btn btn-primary" onClick={handleResume} disabled={saving}>▶ Resume</button>
|
||||||
)}
|
)}
|
||||||
{task.status === 'client_review' && (
|
{task.status === 'client_review' && (
|
||||||
<div style={{ padding: '10px 14px', background: '#f5f3ff', borderRadius: 8, fontSize: 13, color: '#7c3aed', fontWeight: 500 }}>
|
<div style={{ padding: '10px 14px', background: 'var(--bg)', borderRadius: 8, fontSize: 13, color: '#7c3aed', fontWeight: 500 }}>
|
||||||
⏳ Awaiting client review — no action needed.
|
⏳ Awaiting client review — no action needed.
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
{task.status === 'client_approved' && (
|
{task.status === 'client_approved' && (
|
||||||
<div style={{ padding: '10px 14px', background: '#f0fdf4', borderRadius: 8, fontSize: 13, color: '#16a34a', fontWeight: 500 }}>
|
<div style={{ padding: '10px 14px', background: 'var(--bg)', borderRadius: 8, fontSize: 13, color: '#16a34a', fontWeight: 500 }}>
|
||||||
✓ Client approved this job.
|
✓ Client approved this job.
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user