feat(14-03): render subscriptions cost-breakdown table in Formatted tab
- New section guarded by Array.isArray(data.subscriptions), rendered as the first child so it appears above field groups (UI-SPEC focal point) - Product label falls back productName -> sku -> 'Unknown item' - Amounts use latestBilledAmount directly (never unit_price * quantity), rendered font-mono tabular-nums, plus a summed total row - Empty array shows a muted "No subscriptions" state instead of crashing - Raw tab and existing ticket Description block unchanged
This commit is contained in:
parent
3492a16176
commit
04c75bedf8
1 changed files with 44 additions and 0 deletions
|
|
@ -443,6 +443,50 @@ export default function DetailModal({ open, onOpenChange, title, data, fields, k
|
|||
{/* ── Formatted Tab ── */}
|
||||
<TabsContent value="formatted" className="flex-1 overflow-y-auto px-6 py-4 mt-0">
|
||||
<div className="space-y-6">
|
||||
{/* Subscriptions & cost-breakdown — renders above field groups (UI-SPEC: focal point) */}
|
||||
{Array.isArray(data.subscriptions) && (
|
||||
<div>
|
||||
<h3 className="text-xs font-semibold uppercase tracking-wider text-muted-foreground mb-3">Subscriptions & Cost</h3>
|
||||
<div className="rounded-lg border overflow-hidden">
|
||||
{data.subscriptions.length === 0 ? (
|
||||
<div className="px-4 py-3 text-sm text-muted-foreground italic">No subscriptions</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="grid grid-cols-[1fr_80px_140px_120px] gap-2 px-4 py-2 bg-muted/40 text-xs font-medium text-muted-foreground border-b">
|
||||
<div>Product</div>
|
||||
<div className="text-right">Qty</div>
|
||||
<div>Billing Term</div>
|
||||
<div className="text-right">Amount</div>
|
||||
</div>
|
||||
{data.subscriptions.map((sub: any, idx: number) => {
|
||||
const productLabel = sub.productName || sub.sku || 'Unknown item';
|
||||
const amount = Number(sub.latestBilledAmount ?? 0);
|
||||
const currency = sub.currency ?? 'USD';
|
||||
return (
|
||||
<div key={sub.subscriptionId ?? idx}>
|
||||
{idx > 0 && <Separator />}
|
||||
<div className="grid grid-cols-[1fr_80px_140px_120px] gap-2 px-4 py-2.5 items-center text-sm">
|
||||
<div className="min-w-0 truncate">{productLabel}</div>
|
||||
<div className="text-right font-mono tabular-nums">{sub.quantity ?? '—'}</div>
|
||||
<div className="text-muted-foreground">{sub.billingTerm ?? '—'}</div>
|
||||
<div className="text-right font-mono tabular-nums">{currency} {amount.toFixed(2)}</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
<Separator />
|
||||
<div className="grid grid-cols-[1fr_80px_140px_120px] gap-2 px-4 py-2.5 items-center text-sm font-semibold bg-muted/20">
|
||||
<div className="col-span-3">Total</div>
|
||||
<div className="text-right font-mono tabular-nums">
|
||||
{(data.subscriptions[0]?.currency ?? 'USD')} {data.subscriptions.reduce((s: number, sub: any) => s + Number(sub.latestBilledAmount ?? 0), 0).toFixed(2)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{(() => {
|
||||
const rendered = new Set<string>();
|
||||
return groups.map((group) => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue