Team task detail: group amendments in version history, fix dark mode backgrounds
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -286,26 +286,41 @@ export default function TaskDetail() {
|
||||
{submissions.length === 0 ? (
|
||||
<p style={{ color: 'var(--text-muted)', fontSize: 13 }}>No request notes yet.</p>
|
||||
) : (() => {
|
||||
const latest = submissions[submissions.length - 1];
|
||||
const currentVersion = task.current_version + 1;
|
||||
const currentGroup = submissions.filter(s => s.version_number === currentVersion);
|
||||
const primary = currentGroup.find(s => s.type !== 'amendment') || currentGroup[0];
|
||||
const amendments = currentGroup.filter(s => s.type === 'amendment');
|
||||
if (!primary) return null;
|
||||
return (
|
||||
<>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 14 }}>
|
||||
<span style={{ fontWeight: 700, fontSize: 13 }}>{vLabel(latest.version_number - 1)}</span>
|
||||
<StatusBadge status={latest.type} />
|
||||
<span style={{ fontWeight: 700, fontSize: 13 }}>{vLabel(primary.version_number - 1)}</span>
|
||||
<StatusBadge status={primary.type} />
|
||||
<span style={{ fontSize: 12, color: 'var(--text-muted)', marginLeft: 'auto' }}>
|
||||
{latest.submitted_by_name} · {new Date(latest.submitted_at).toLocaleDateString()}
|
||||
{primary.submitted_by_name} · {new Date(primary.submitted_at).toLocaleDateString()}
|
||||
</span>
|
||||
</div>
|
||||
<div className="detail-grid" style={{ marginBottom: 14 }}>
|
||||
<div className="detail-item"><label>Service Type</label><p>{latest.service_type}</p></div>
|
||||
<div className="detail-item"><label>Deadline</label><p>{latest.deadline || '—'}</p></div>
|
||||
<div className="detail-item"><label>Service Type</label><p>{primary.service_type}</p></div>
|
||||
<div className="detail-item"><label>Deadline</label><p>{primary.deadline || '—'}</p></div>
|
||||
</div>
|
||||
<div>
|
||||
<label style={{ fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.5px', color: 'var(--text-muted)' }}>Description</label>
|
||||
<p style={{ marginTop: 8, fontSize: 14, lineHeight: 1.7, color: 'var(--text-primary)', background: 'var(--bg)', padding: '12px 14px', borderRadius: 8, border: '1px solid var(--border)', whiteSpace: 'pre-wrap' }}>
|
||||
{latest.description}
|
||||
{primary.description}
|
||||
</p>
|
||||
</div>
|
||||
{amendments.map(amendment => (
|
||||
<div key={amendment.id} style={{ marginTop: 12, padding: '12px 14px', background: 'var(--bg)', borderRadius: 8, border: '1px solid var(--border)' }}>
|
||||
<div style={{ fontSize: 11, fontWeight: 700, textTransform: 'uppercase', color: 'var(--accent)', marginBottom: 6, letterSpacing: 0.5 }}>
|
||||
Amended Request
|
||||
</div>
|
||||
<div style={{ fontSize: 12, color: 'var(--text-muted)', marginBottom: 6 }}>
|
||||
{amendment.submitted_by_name} · {new Date(amendment.submitted_at).toLocaleDateString()}
|
||||
</div>
|
||||
<p style={{ fontSize: 13, lineHeight: 1.6, color: 'var(--text-secondary)', whiteSpace: 'pre-wrap', margin: 0 }}>{amendment.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
@@ -316,54 +331,73 @@ export default function TaskDetail() {
|
||||
<div className="card">
|
||||
<div className="card-title">Version History</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
|
||||
{submissions.map((sub, i) => {
|
||||
const delivery = sub.delivery;
|
||||
const isCurrent = i === submissions.length - 1;
|
||||
{Object.values(
|
||||
submissions.reduce((groups, sub) => {
|
||||
const key = sub.version_number;
|
||||
if (!groups[key]) groups[key] = [];
|
||||
groups[key].push(sub);
|
||||
return groups;
|
||||
}, {})
|
||||
).map((group, gi, all) => {
|
||||
const primary = group.find(s => s.type !== 'amendment') || group[0];
|
||||
const amendments = group.filter(s => s.type === 'amendment');
|
||||
const delivery = primary.delivery;
|
||||
const isCurrent = gi === all.length - 1;
|
||||
return (
|
||||
<div key={sub.id} style={{ borderRadius: 8, border: `1px solid ${isCurrent ? '#fde68a' : 'var(--border)'}`, background: isCurrent ? '#fffbeb' : 'var(--bg)', overflow: 'hidden' }}>
|
||||
<div style={{ padding: '12px 16px', display: 'flex', alignItems: 'center', gap: 10, borderBottom: '1px solid var(--border)' }}>
|
||||
<span style={{ fontWeight: 700, fontSize: 13 }}>{vLabel(sub.version_number - 1)}</span>
|
||||
<StatusBadge status={sub.type} />
|
||||
{isCurrent && <span style={{ fontSize: 11, color: '#d97706', fontWeight: 600 }}>Current</span>}
|
||||
<div key={primary.id} style={{ borderRadius: 8, border: `1px solid ${isCurrent ? 'var(--accent)' : 'var(--border)'}`, background: 'var(--bg)', overflow: 'hidden', opacity: isCurrent ? 1 : 0.85 }}>
|
||||
<div style={{ padding: '12px 16px', display: 'flex', alignItems: 'center', gap: 10, borderBottom: '1px solid var(--border)', background: 'var(--card-bg-2)' }}>
|
||||
<span style={{ fontWeight: 700, fontSize: 13 }}>{vLabel(primary.version_number - 1)}</span>
|
||||
<StatusBadge status={primary.type} />
|
||||
{isCurrent && <span style={{ fontSize: 11, color: 'var(--accent)', fontWeight: 600 }}>Current</span>}
|
||||
<span style={{ fontSize: 12, color: 'var(--text-muted)', marginLeft: 'auto' }}>
|
||||
{sub.submitted_by_name} · {new Date(sub.submitted_at).toLocaleDateString()}
|
||||
{primary.submitted_by_name} · {new Date(primary.submitted_at).toLocaleDateString()}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ padding: '12px 16px', borderBottom: delivery ? '1px solid var(--border)' : 'none' }}>
|
||||
<div style={{ padding: '12px 16px' }}>
|
||||
<div style={{ display: 'flex', gap: 24, marginBottom: 8 }}>
|
||||
<div>
|
||||
<span style={{ fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.5px', color: 'var(--text-muted)' }}>Service</span>
|
||||
<div style={{ fontSize: 13, marginTop: 2 }}>{sub.service_type}</div>
|
||||
<div style={{ fontSize: 13, marginTop: 2 }}>{primary.service_type}</div>
|
||||
</div>
|
||||
<div>
|
||||
<span style={{ fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.5px', color: 'var(--text-muted)' }}>Deadline</span>
|
||||
<div style={{ fontSize: 13, marginTop: 2 }}>{sub.deadline || '—'}</div>
|
||||
<div style={{ fontSize: 13, marginTop: 2 }}>{primary.deadline || '—'}</div>
|
||||
</div>
|
||||
</div>
|
||||
<p style={{ fontSize: 13, color: 'var(--text-secondary)', lineHeight: 1.6, whiteSpace: 'pre-wrap' }}>{sub.description}</p>
|
||||
<p style={{ fontSize: 13, color: 'var(--text-secondary)', lineHeight: 1.6, whiteSpace: 'pre-wrap', margin: 0 }}>{primary.description}</p>
|
||||
|
||||
{amendments.map(amendment => (
|
||||
<div key={amendment.id} style={{ marginTop: 12, padding: '12px 14px', background: 'var(--card-bg)', borderRadius: 8, border: '1px solid var(--border)' }}>
|
||||
<div style={{ fontSize: 11, fontWeight: 700, textTransform: 'uppercase', color: 'var(--accent)', marginBottom: 6, letterSpacing: 0.5 }}>
|
||||
Amended Request
|
||||
</div>
|
||||
<div style={{ fontSize: 12, color: 'var(--text-muted)', marginBottom: 6 }}>
|
||||
{amendment.submitted_by_name} · {new Date(amendment.submitted_at).toLocaleDateString()}
|
||||
</div>
|
||||
<p style={{ fontSize: 13, lineHeight: 1.6, color: 'var(--text-secondary)', whiteSpace: 'pre-wrap', margin: 0 }}>{amendment.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
{delivery ? (
|
||||
<div style={{ padding: '10px 16px', background: '#f0fdf4', borderTop: '1px solid #bbf7d0' }}>
|
||||
<div style={{ padding: '10px 16px', borderTop: '1px solid var(--border)' }}>
|
||||
<div style={{ fontSize: 11, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.5px', color: '#16a34a', marginBottom: 8 }}>
|
||||
✓ Delivered by {delivery.sent_by} on {new Date(delivery.sent_at).toLocaleDateString()}
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
|
||||
{(delivery.files || []).map((file, fi) => (
|
||||
<div key={fi} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '6px 10px', background: 'white', borderRadius: 6, border: '1px solid #bbf7d0' }}>
|
||||
<div key={fi} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '6px 10px', background: 'var(--card-bg-2)', borderRadius: 6, border: '1px solid var(--border)' }}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<span>📄</span>
|
||||
<span style={{ fontSize: 13, fontWeight: 500 }}>{file.name}</span>
|
||||
{file.size > 0 && <span style={{ fontSize: 11, color: 'var(--text-muted)' }}>{formatSize(file.size)}</span>}
|
||||
</div>
|
||||
<button className="btn btn-outline btn-sm" onClick={() => getFileUrl(file.storage_path)}>
|
||||
📥 View
|
||||
</button>
|
||||
<button className="btn btn-outline btn-sm" onClick={() => getFileUrl(file.storage_path)}>📥 View</button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div style={{ padding: '10px 16px', background: '#f8f8f8' }}>
|
||||
<div style={{ padding: '10px 16px', borderTop: '1px solid var(--border)' }}>
|
||||
<span style={{ fontSize: 12, color: 'var(--text-muted)' }}>📎 No file delivered yet for this version.</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user