Add Asset Summary table on Ratecard tab for visual validation
- New "Asset Summary" table at top of Ratecard tab showing:
#, Client Asset name, Tier, Matched GMAL, Volume, Total Hours
- Grand total row with volume sum and hours sum
- Appears above the existing "Hours by Role" detail table
- Section labels ("Asset Summary" / "Hours by Role") for clarity
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
9eaa85dc37
commit
d85ef96a06
2 changed files with 73 additions and 0 deletions
|
|
@ -820,6 +820,15 @@ span.conf-none { background: var(--color-danger); }
|
|||
flex: 1;
|
||||
}
|
||||
|
||||
.rc-section-label {
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: var(--color-text-secondary);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.text-right { text-align: right; }
|
||||
.text-center { text-align: center; }
|
||||
|
||||
|
|
|
|||
|
|
@ -936,6 +936,70 @@ export default function ProjectView() {
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* Asset Summary Table */}
|
||||
<div className="rc-section-label">Asset Summary</div>
|
||||
<div className="table-wrap" style={{ marginBottom: 20 }}>
|
||||
<table className="rc-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>#</th>
|
||||
<th>Client Asset</th>
|
||||
<th>Tier</th>
|
||||
<th>Matched GMAL</th>
|
||||
<th>GMAL Name</th>
|
||||
<th className="text-center">Volume</th>
|
||||
<th className="text-right">Total Hours</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{(() => {
|
||||
// Group ratecard lines by client asset to get unique assets + total hours
|
||||
const assetMap: Record<number, { name: string; tier: string; gmal_id: string; gmal_name: string; volume: number; hours: number }> = {};
|
||||
for (const l of ratecard.lines) {
|
||||
if (!assetMap[l.client_asset_id]) {
|
||||
const ca = assets.find(a => a.id === l.client_asset_id);
|
||||
assetMap[l.client_asset_id] = {
|
||||
name: l.client_asset_name || '',
|
||||
tier: (ca as any)?.client_tier || '',
|
||||
gmal_id: l.gmal_id || '',
|
||||
gmal_name: '',
|
||||
volume: l.volume,
|
||||
hours: 0,
|
||||
};
|
||||
}
|
||||
const effective = l.manual_override ?? l.total_hours ?? 0;
|
||||
assetMap[l.client_asset_id].hours += effective;
|
||||
}
|
||||
const assetList = Object.entries(assetMap).sort(([a], [b]) => Number(a) - Number(b));
|
||||
const grandTotal = assetList.reduce((sum, [, a]) => sum + a.hours, 0);
|
||||
|
||||
return (
|
||||
<>
|
||||
{assetList.map(([aid, a], idx) => (
|
||||
<tr key={aid}>
|
||||
<td className="td-discipline">{idx + 1}</td>
|
||||
<td><strong>{a.name}</strong></td>
|
||||
<td>{a.tier ? <span className="tier-tag" style={{ margin: 0 }}><strong>{a.tier}</strong></span> : <span style={{ color: 'var(--color-text-muted)' }}>—</span>}</td>
|
||||
<td className="td-gmal">{a.gmal_id}</td>
|
||||
<td className="td-discipline">{a.gmal_name}</td>
|
||||
<td className="text-center"><strong>{a.volume}</strong></td>
|
||||
<td className="text-right td-total">{a.hours.toFixed(2)}</td>
|
||||
</tr>
|
||||
))}
|
||||
<tr>
|
||||
<td colSpan={5}></td>
|
||||
<td className="text-center"><strong>{assetList.reduce((s, [, a]) => s + a.volume, 0)}</strong></td>
|
||||
<td className="text-right td-total" style={{ fontSize: 14 }}><strong>{grandTotal.toFixed(2)}</strong></td>
|
||||
</tr>
|
||||
</>
|
||||
);
|
||||
})()}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Role Breakdown */}
|
||||
<div className="rc-section-label">Hours by Role</div>
|
||||
<div className="table-wrap">
|
||||
<table className="rc-table">
|
||||
<thead>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue