83 lines
6.0 KiB
TypeScript
83 lines
6.0 KiB
TypeScript
import { Show, type Component } from 'solid-js';
|
|
import { Info, Plus } from 'lucide-solid';
|
|
|
|
interface ExecutiveSummaryProps {
|
|
totals: {
|
|
subtotal: number;
|
|
markup: number;
|
|
contingency: number;
|
|
fees: number;
|
|
modifiersTotal: number;
|
|
grandTotal: number;
|
|
};
|
|
onFinalizeBid: () => void;
|
|
}
|
|
|
|
export const ExecutiveSummary: Component<ExecutiveSummaryProps> = (props) => {
|
|
const formatNumber = (num: number) => {
|
|
return num.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 });
|
|
};
|
|
|
|
return (
|
|
<div class="mt-20 p-10 bg-muted/30 rounded-[3rem] border border-border shadow-inner">
|
|
<div class="max-w-7xl mx-auto flex flex-col lg:flex-row gap-10 items-center lg:items-end justify-between">
|
|
<div class="flex-1 w-full space-y-8">
|
|
<div class="flex items-center gap-4">
|
|
<div class="px-4 py-1.5 bg-primary/10 text-primary rounded-full text-[10px] font-black uppercase tracking-[0.2em] border border-primary/20 shadow-sm">
|
|
Executive Summary
|
|
</div>
|
|
<div class="flex-1 h-px bg-border/60"></div>
|
|
</div>
|
|
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
<div class="bg-card p-6 rounded-3xl border border-border shadow-premium space-y-2 flex flex-col justify-center min-w-0 transition-transform hover:-translate-y-1">
|
|
<span class="text-[10px] font-bold text-muted-foreground uppercase tracking-widest truncate">Project Subtotal</span>
|
|
<div class="text-2xl xl:text-3xl font-black text-foreground tracking-tight truncate">${formatNumber(props.totals.subtotal)}</div>
|
|
</div>
|
|
<div class="bg-card p-6 rounded-3xl border border-border shadow-premium space-y-2 flex flex-col justify-center min-w-0 transition-transform hover:-translate-y-1">
|
|
<span class="text-[10px] font-bold text-muted-foreground uppercase tracking-widest truncate">Total Markup</span>
|
|
<div class="text-2xl xl:text-3xl font-black text-primary tracking-tight truncate">${formatNumber(props.totals.markup)}</div>
|
|
</div>
|
|
<div class="bg-card p-6 rounded-3xl border border-border shadow-premium space-y-2 flex flex-col justify-center min-w-0 transition-transform hover:-translate-y-1">
|
|
<span class="text-[10px] font-bold text-muted-foreground uppercase tracking-widest truncate">Contingency</span>
|
|
<div class="text-2xl xl:text-3xl font-black text-foreground tracking-tight truncate">${formatNumber(props.totals.contingency)}</div>
|
|
</div>
|
|
<Show when={props.totals.modifiersTotal !== 0}>
|
|
<div class="bg-card p-6 rounded-3xl border border-border shadow-premium space-y-2 flex flex-col justify-center min-w-0 transition-transform hover:-translate-y-1">
|
|
<span class="text-[10px] font-bold text-muted-foreground uppercase tracking-widest truncate">Applied Modifiers</span>
|
|
<div class="text-2xl xl:text-3xl font-black text-indigo-600 tracking-tight truncate">${formatNumber(props.totals.modifiersTotal)}</div>
|
|
</div>
|
|
</Show>
|
|
<div class="bg-card p-6 rounded-3xl border border-border shadow-premium space-y-2 flex flex-col justify-center min-w-0 transition-transform hover:-translate-y-1">
|
|
<span class="text-[10px] font-bold text-muted-foreground uppercase tracking-widest truncate">Fees & Logistics</span>
|
|
<div class="text-2xl xl:text-3xl font-black text-foreground tracking-tight truncate">${formatNumber(props.totals.fees)}</div>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-start gap-4 px-6 py-4 bg-primary/5 rounded-2xl border border-primary/10 shadow-sm">
|
|
<Info class="w-5 h-5 text-primary/60 shrink-0 mt-0.5" />
|
|
<p class="text-xs font-medium text-primary/70 leading-relaxed">
|
|
Comprehensive project total including all active scopes, sub-scopes, and unassigned takeoff items.
|
|
Pricing is calculated dynamically based on current material/labor allocations.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="shrink-0 text-center lg:text-right group bg-card p-10 rounded-[3rem] border border-primary/10 shadow-elevated min-w-[340px] lg:min-w-[380px] max-w-full transition-all hover:shadow-2xl">
|
|
<div class="text-[10px] font-black text-muted-foreground uppercase tracking-[0.4em] mb-3 group-hover:text-primary transition-colors">Total Estimate Value</div>
|
|
<div class="text-5xl sm:text-6xl xl:text-7xl font-black text-foreground tracking-tighter transition-all group-hover:scale-[1.02] duration-500 break-words drop-shadow-sm">
|
|
<span class="text-2xl sm:text-3xl text-primary font-medium align-top mr-1">$</span>
|
|
{formatNumber(props.totals.grandTotal)}
|
|
</div>
|
|
<div class="mt-8">
|
|
<button
|
|
onClick={props.onFinalizeBid}
|
|
class="w-full py-5 bg-primary hover:bg-primary/90 text-primary-foreground rounded-2xl font-black text-xs uppercase tracking-[0.2em] transition-all shadow-xl shadow-primary/20 active:scale-95 flex items-center justify-center gap-3"
|
|
>
|
|
<Plus class="w-5 h-5" /> Finalize Bid
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|