Added Manager Info
This commit is contained in:
@@ -5,6 +5,9 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
||||||
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||||
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap" rel="stylesheet" />
|
||||||
<title>item-extractor-solid</title>
|
<title>item-extractor-solid</title>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ interface EstimateBuilderProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const EstimateBuilder: Component<EstimateBuilderProps> = (props) => {
|
const EstimateBuilder: Component<EstimateBuilderProps> = (props) => {
|
||||||
|
type PrintMode = 'finalized' | 'manager';
|
||||||
|
|
||||||
// 1. Initialize State
|
// 1. Initialize State
|
||||||
const { scopes, setScopes, subScopes, setSubScopes, estimateItems: items, setEstimateItems: setItems, clientInfo, jobInfo, generalEstimateNotes, setGeneralEstimateNotes, generalPreNotes, setGeneralPreNotes } = appStore;
|
const { scopes, setScopes, subScopes, setSubScopes, estimateItems: items, setEstimateItems: setItems, clientInfo, jobInfo, generalEstimateNotes, setGeneralEstimateNotes, generalPreNotes, setGeneralPreNotes } = appStore;
|
||||||
|
|
||||||
@@ -56,6 +58,7 @@ const EstimateBuilder: Component<EstimateBuilderProps> = (props) => {
|
|||||||
const [pendingCsvImportText, setPendingCsvImportText] = createSignal<string | null>(null);
|
const [pendingCsvImportText, setPendingCsvImportText] = createSignal<string | null>(null);
|
||||||
const [excludedScopeIds, setExcludedScopeIds] = createSignal<string[]>([]);
|
const [excludedScopeIds, setExcludedScopeIds] = createSignal<string[]>([]);
|
||||||
const [excludedSubScopeIds, setExcludedSubScopeIds] = createSignal<string[]>([]);
|
const [excludedSubScopeIds, setExcludedSubScopeIds] = createSignal<string[]>([]);
|
||||||
|
const [printMode, setPrintMode] = createSignal<PrintMode>('finalized');
|
||||||
|
|
||||||
// 2. Initialize custom hooks/primitives
|
// 2. Initialize custom hooks/primitives
|
||||||
const { selectedIds, setSelectedIds, lastSelectedId, setLastSelectedId, toggleSelection, clearSelection, copyColumnSum } = useSelectionManager();
|
const { selectedIds, setSelectedIds, lastSelectedId, setLastSelectedId, toggleSelection, clearSelection, copyColumnSum } = useSelectionManager();
|
||||||
@@ -92,7 +95,19 @@ const EstimateBuilder: Component<EstimateBuilderProps> = (props) => {
|
|||||||
const anyDescriptionIsLong = createMemo(() => items.some(item => (item.description || '').length > 120));
|
const anyDescriptionIsLong = createMemo(() => items.some(item => (item.description || '').length > 120));
|
||||||
|
|
||||||
const finalizeBid = () => {
|
const finalizeBid = () => {
|
||||||
|
runPrint('finalized');
|
||||||
|
};
|
||||||
|
|
||||||
|
const exportManagerInfo = () => {
|
||||||
|
runPrint('manager');
|
||||||
|
};
|
||||||
|
|
||||||
|
const runPrint = (mode: PrintMode) => {
|
||||||
|
setPrintMode(mode);
|
||||||
|
window.requestAnimationFrame(() => {
|
||||||
window.print();
|
window.print();
|
||||||
|
window.setTimeout(() => setPrintMode('finalized'), 0);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const addScope = () => {
|
const addScope = () => {
|
||||||
@@ -347,6 +362,7 @@ const EstimateBuilder: Component<EstimateBuilderProps> = (props) => {
|
|||||||
generalPreNotes={generalPreNotes()}
|
generalPreNotes={generalPreNotes()}
|
||||||
formatNumber={formatNumber}
|
formatNumber={formatNumber}
|
||||||
hideColumns={anyDescriptionIsLong()}
|
hideColumns={anyDescriptionIsLong()}
|
||||||
|
variant={printMode()}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="no-print flex flex-col min-h-dvh">
|
<div class="no-print flex flex-col min-h-dvh">
|
||||||
@@ -465,7 +481,7 @@ const EstimateBuilder: Component<EstimateBuilderProps> = (props) => {
|
|||||||
|
|
||||||
<ValidationStatusBar validationStats={validationStats()} />
|
<ValidationStatusBar validationStats={validationStats()} />
|
||||||
|
|
||||||
<ExecutiveSummary totals={grandTotals()} onFinalizeBid={finalizeBid} />
|
<ExecutiveSummary totals={grandTotals()} onFinalizeBid={finalizeBid} onExportManagerInfo={exportManagerInfo} />
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -28,9 +28,13 @@ interface PrintEstimateProps {
|
|||||||
generalEstimateNotes: string;
|
generalEstimateNotes: string;
|
||||||
formatNumber: (num: number) => string;
|
formatNumber: (num: number) => string;
|
||||||
hideColumns?: boolean;
|
hideColumns?: boolean;
|
||||||
|
variant?: 'finalized' | 'manager';
|
||||||
}
|
}
|
||||||
|
|
||||||
const PrintEstimate: Component<PrintEstimateProps> = (props) => {
|
const PrintEstimate: Component<PrintEstimateProps> = (props) => {
|
||||||
|
const variant = () => props.variant ?? 'finalized';
|
||||||
|
const isManagerVariant = () => variant() === 'manager';
|
||||||
|
|
||||||
const getScopeTotal = (scopeId: string) => {
|
const getScopeTotal = (scopeId: string) => {
|
||||||
let scopeSubtotal = 0;
|
let scopeSubtotal = 0;
|
||||||
let scopeMarkup = 0;
|
let scopeMarkup = 0;
|
||||||
@@ -75,6 +79,28 @@ const PrintEstimate: Component<PrintEstimateProps> = (props) => {
|
|||||||
return scopeSubtotal + scopeMarkup + scopeContingency + mgmtFee + deliveryFee + modifiersTotal;
|
return scopeSubtotal + scopeMarkup + scopeContingency + mgmtFee + deliveryFee + modifiersTotal;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getScopeItems = (scopeId: string) => {
|
||||||
|
const scopeLevelItems = props.items.filter((item: EstimateItem) => item.scopeId === scopeId && !item.subScopeId);
|
||||||
|
const scopeSubScopes = props.subScopes
|
||||||
|
.filter((subScope) => subScope.scopeId === scopeId)
|
||||||
|
.map((subScope) => ({
|
||||||
|
subScope,
|
||||||
|
items: props.items.filter((item: EstimateItem) => item.subScopeId === subScope.id)
|
||||||
|
}))
|
||||||
|
.filter((entry) => entry.items.length > 0);
|
||||||
|
|
||||||
|
return {
|
||||||
|
scopeLevelItems,
|
||||||
|
scopeSubScopes
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getItemSubtotal = (item: EstimateItem) => item.quantity * item.unitPrice;
|
||||||
|
const getManagerScopeVisibleTotal = (scopeId: string) =>
|
||||||
|
props.items
|
||||||
|
.filter((item: EstimateItem) => item.scopeId === scopeId)
|
||||||
|
.reduce((sum, item) => sum + getItemSubtotal(item), 0);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="print-only p-6 bg-white font-sans max-w-[8.5in] mx-auto text-sm leading-normal text-foreground">
|
<div class="print-only p-6 bg-white font-sans max-w-[8.5in] mx-auto text-sm leading-normal text-foreground">
|
||||||
{/* Company Header */}
|
{/* Company Header */}
|
||||||
@@ -88,7 +114,9 @@ const PrintEstimate: Component<PrintEstimateProps> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-right">
|
<div class="text-right">
|
||||||
<h2 class="text-2xl font-black text-foreground uppercase tracking-tighter leading-none">Estimate</h2>
|
<h2 class="text-2xl font-black text-foreground uppercase tracking-tighter leading-none">
|
||||||
|
{isManagerVariant() ? 'Manager Info' : 'Estimate'}
|
||||||
|
</h2>
|
||||||
<p class="text-[10px] text-muted-foreground italic uppercase tracking-widest font-bold mb-1">*** NOT A BILL ***</p>
|
<p class="text-[10px] text-muted-foreground italic uppercase tracking-widest font-bold mb-1">*** NOT A BILL ***</p>
|
||||||
<p class="text-foreground font-bold text-xs">Date: {new Date().toLocaleDateString()}</p>
|
<p class="text-foreground font-bold text-xs">Date: {new Date().toLocaleDateString()}</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -110,6 +138,10 @@ const PrintEstimate: Component<PrintEstimateProps> = (props) => {
|
|||||||
<div>
|
<div>
|
||||||
<h3 class="text-[10px] font-bold text-muted-foreground uppercase tracking-wider border-b border-border pb-1 mb-2">Project Details</h3>
|
<h3 class="text-[10px] font-bold text-muted-foreground uppercase tracking-wider border-b border-border pb-1 mb-2">Project Details</h3>
|
||||||
<div class="pl-1 space-y-1">
|
<div class="pl-1 space-y-1">
|
||||||
|
<div class="flex gap-2 text-xs">
|
||||||
|
<span class="font-bold text-muted-foreground/60 w-16 uppercase text-[10px] pt-0.5">Job No.</span>
|
||||||
|
<span class="font-bold text-foreground">{props.jobInfo.number || '---'}</span>
|
||||||
|
</div>
|
||||||
<div class="flex gap-2 text-xs">
|
<div class="flex gap-2 text-xs">
|
||||||
<span class="font-bold text-muted-foreground/60 w-16 uppercase text-[10px] pt-0.5">Project</span>
|
<span class="font-bold text-muted-foreground/60 w-16 uppercase text-[10px] pt-0.5">Project</span>
|
||||||
<span class="font-bold text-foreground">{props.jobInfo.name || '---'}</span>
|
<span class="font-bold text-foreground">{props.jobInfo.name || '---'}</span>
|
||||||
@@ -129,7 +161,7 @@ const PrintEstimate: Component<PrintEstimateProps> = (props) => {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* General Pre-Notes */}
|
{/* General Pre-Notes */}
|
||||||
<Show when={props.generalPreNotes}>
|
<Show when={props.generalPreNotes && !isManagerVariant()}>
|
||||||
<div class="mb-6 p-4 bg-muted/30 rounded-xl border border-border/40 break-inside-avoid shadow-sm outline outline-1 outline-border/20">
|
<div class="mb-6 p-4 bg-muted/30 rounded-xl border border-border/40 break-inside-avoid shadow-sm outline outline-1 outline-border/20">
|
||||||
<div class="text-[11px] text-foreground/80 leading-relaxed font-medium italic">
|
<div class="text-[11px] text-foreground/80 leading-relaxed font-medium italic">
|
||||||
{props.generalPreNotes}
|
{props.generalPreNotes}
|
||||||
@@ -138,6 +170,9 @@ const PrintEstimate: Component<PrintEstimateProps> = (props) => {
|
|||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
{/* Scope Details Table */}
|
{/* Scope Details Table */}
|
||||||
|
<Show
|
||||||
|
when={isManagerVariant()}
|
||||||
|
fallback={
|
||||||
<div class="mb-8 border-t-2 border-foreground">
|
<div class="mb-8 border-t-2 border-foreground">
|
||||||
<div class="grid grid-cols-12 gap-2 py-2 bg-muted/40 px-3 text-[10px] font-bold text-muted-foreground uppercase tracking-wider border-b border-border/60">
|
<div class="grid grid-cols-12 gap-2 py-2 bg-muted/40 px-3 text-[10px] font-bold text-muted-foreground uppercase tracking-wider border-b border-border/60">
|
||||||
<div class="col-span-2">Scope</div>
|
<div class="col-span-2">Scope</div>
|
||||||
@@ -161,7 +196,6 @@ const PrintEstimate: Component<PrintEstimateProps> = (props) => {
|
|||||||
if (processed.trim().startsWith('- ')) {
|
if (processed.trim().startsWith('- ')) {
|
||||||
return <li class="ml-3 list-disc pl-1 text-foreground/90" innerHTML={processed.substring(2)} />;
|
return <li class="ml-3 list-disc pl-1 text-foreground/90" innerHTML={processed.substring(2)} />;
|
||||||
}
|
}
|
||||||
// Handle empty lines more gracefully in compact mode
|
|
||||||
if (!processed.trim()) return <div class="h-1"></div>;
|
if (!processed.trim()) return <div class="h-1"></div>;
|
||||||
return <p class="mb-0.5 last:mb-0 text-foreground/90" innerHTML={processed} />;
|
return <p class="mb-0.5 last:mb-0 text-foreground/90" innerHTML={processed} />;
|
||||||
}}
|
}}
|
||||||
@@ -176,9 +210,114 @@ const PrintEstimate: Component<PrintEstimateProps> = (props) => {
|
|||||||
</For>
|
</For>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div class="mb-8 space-y-6">
|
||||||
|
<For each={props.scopes}>
|
||||||
|
{(scope) => {
|
||||||
|
const scopeItems = () => getScopeItems(scope.id);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section class={`border border-border/60 rounded-2xl overflow-hidden ${isManagerVariant() ? '' : 'break-inside-avoid'}`}>
|
||||||
|
<div class="px-4 py-3 bg-muted/30 border-b border-border/60 flex items-start justify-between gap-4">
|
||||||
|
<div>
|
||||||
|
<h4 class="text-sm font-black text-foreground uppercase">{scope.name}</h4>
|
||||||
|
<p class="text-[10px] text-muted-foreground font-bold uppercase tracking-widest mt-1">Scope Total</p>
|
||||||
|
</div>
|
||||||
|
<div class="text-right">
|
||||||
|
<div class="text-lg font-black text-foreground">${props.formatNumber(getManagerScopeVisibleTotal(scope.id))}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-4 space-y-4">
|
||||||
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2">
|
||||||
|
<div class="rounded-xl border border-border/50 p-3 bg-white">
|
||||||
|
<div class="text-[10px] font-bold text-muted-foreground uppercase tracking-widest mb-2">Scope Description</div>
|
||||||
|
<div class="prose prose-sm max-w-none prose-strong:text-foreground prose-em:text-foreground/70 text-xs text-foreground/90">
|
||||||
|
<For each={(scope.bidDescription || 'See detailed specifications.').split('\n')}>
|
||||||
|
{(line) => {
|
||||||
|
let processed = line;
|
||||||
|
processed = processed.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>');
|
||||||
|
processed = processed.replace(/(\*|_)(.*?)\1/g, '<em>$2</em>');
|
||||||
|
if (processed.trim().startsWith('- ')) {
|
||||||
|
return <li class="ml-3 list-disc pl-1 text-foreground/90" innerHTML={processed.substring(2)} />;
|
||||||
|
}
|
||||||
|
if (!processed.trim()) return <div class="h-1"></div>;
|
||||||
|
return <p class="mb-0.5 last:mb-0 text-foreground/90" innerHTML={processed} />;
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rounded-xl border border-border/50 p-3 bg-white">
|
||||||
|
<div class="text-[10px] font-bold text-muted-foreground uppercase tracking-widest mb-2">Estimator Notes</div>
|
||||||
|
<div class="text-xs text-foreground/90 whitespace-pre-wrap leading-relaxed">
|
||||||
|
{scope.estimatorNotes?.trim() || 'No estimator notes provided.'}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="border border-border/50 rounded-xl overflow-hidden">
|
||||||
|
<div class="grid grid-cols-12 gap-3 px-3 py-2 bg-muted/20 text-[10px] font-bold text-muted-foreground uppercase tracking-wider border-b border-border/50">
|
||||||
|
<div class="col-span-2">Section</div>
|
||||||
|
<div class="col-span-6">Line Item</div>
|
||||||
|
<div class="col-span-1 text-right">Qty</div>
|
||||||
|
<div class="col-span-1 text-right">Unit</div>
|
||||||
|
<div class="col-span-2 text-right">Subtotal</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Show when={scopeItems().scopeLevelItems.length > 0}>
|
||||||
|
<div class="border-b border-border/40">
|
||||||
|
<div class="px-3 py-2 bg-background text-[10px] font-black uppercase tracking-widest text-muted-foreground">Scope Level</div>
|
||||||
|
<For each={scopeItems().scopeLevelItems}>
|
||||||
|
{(item) => (
|
||||||
|
<div class="grid grid-cols-12 gap-3 px-3 py-2 text-xs border-t border-border/20 items-start">
|
||||||
|
<div class="col-span-2 font-bold text-foreground">Scope Level</div>
|
||||||
|
<div class="col-span-6 text-foreground/90">{item.description}</div>
|
||||||
|
<div class="col-span-1 text-right text-foreground">{item.quantity}</div>
|
||||||
|
<div class="col-span-1 text-right text-foreground">${props.formatNumber(item.unitPrice)}</div>
|
||||||
|
<div class="col-span-2 text-right font-bold text-foreground">${props.formatNumber(getItemSubtotal(item))}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<For each={scopeItems().scopeSubScopes}>
|
||||||
|
{(entry) => (
|
||||||
|
<div class="border-b last:border-b-0 border-border/40">
|
||||||
|
<div class="px-3 py-2 bg-background text-[10px] font-black uppercase tracking-widest text-muted-foreground">
|
||||||
|
{entry.subScope.name}
|
||||||
|
</div>
|
||||||
|
<For each={entry.items}>
|
||||||
|
{(item) => (
|
||||||
|
<div class="grid grid-cols-12 gap-3 px-3 py-2 text-xs border-t border-border/20 items-start">
|
||||||
|
<div class="col-span-2 font-bold text-foreground">{entry.subScope.name}</div>
|
||||||
|
<div class="col-span-6 text-foreground/90">{item.description}</div>
|
||||||
|
<div class="col-span-1 text-right text-foreground">{item.quantity}</div>
|
||||||
|
<div class="col-span-1 text-right text-foreground">${props.formatNumber(item.unitPrice)}</div>
|
||||||
|
<div class="col-span-2 text-right font-bold text-foreground">${props.formatNumber(getItemSubtotal(item))}</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
|
||||||
|
<Show when={scopeItems().scopeLevelItems.length === 0 && scopeItems().scopeSubScopes.length === 0}>
|
||||||
|
<div class="px-3 py-4 text-xs text-muted-foreground italic">No line items assigned to this scope.</div>
|
||||||
|
</Show>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
{/* General Estimate Notes */}
|
{/* General Estimate Notes */}
|
||||||
<Show when={props.generalEstimateNotes}>
|
<Show when={props.generalEstimateNotes && !isManagerVariant()}>
|
||||||
<div class="mb-8">
|
<div class="mb-8">
|
||||||
<h5 class="text-[10px] font-bold text-muted-foreground uppercase tracking-wider mb-1 border-b border-border/40 pb-1">General Estimate Notes</h5>
|
<h5 class="text-[10px] font-bold text-muted-foreground uppercase tracking-wider mb-1 border-b border-border/40 pb-1">General Estimate Notes</h5>
|
||||||
<div class="text-[11px] text-foreground/80 leading-relaxed whitespace-pre-wrap font-medium">
|
<div class="text-[11px] text-foreground/80 leading-relaxed whitespace-pre-wrap font-medium">
|
||||||
@@ -188,6 +327,7 @@ const PrintEstimate: Component<PrintEstimateProps> = (props) => {
|
|||||||
</Show>
|
</Show>
|
||||||
|
|
||||||
{/* Final Section: Totals & Signature aligned */}
|
{/* Final Section: Totals & Signature aligned */}
|
||||||
|
<Show when={!isManagerVariant()}>
|
||||||
<div class="grid grid-cols-12 gap-8 items-end mb-8 break-inside-avoid">
|
<div class="grid grid-cols-12 gap-8 items-end mb-8 break-inside-avoid">
|
||||||
<div class="col-span-7">
|
<div class="col-span-7">
|
||||||
<div class="flex flex-col gap-2">
|
<div class="flex flex-col gap-2">
|
||||||
@@ -233,6 +373,7 @@ const PrintEstimate: Component<PrintEstimateProps> = (props) => {
|
|||||||
|
|
||||||
{/* Print Page Counter */}
|
{/* Print Page Counter */}
|
||||||
<div class="print-page-number"></div>
|
<div class="print-page-number"></div>
|
||||||
|
</Show>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ interface ExecutiveSummaryProps {
|
|||||||
grandTotal: number;
|
grandTotal: number;
|
||||||
};
|
};
|
||||||
onFinalizeBid: () => void;
|
onFinalizeBid: () => void;
|
||||||
|
onExportManagerInfo: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ExecutiveSummary: Component<ExecutiveSummaryProps> = (props) => {
|
export const ExecutiveSummary: Component<ExecutiveSummaryProps> = (props) => {
|
||||||
@@ -68,12 +69,20 @@ export const ExecutiveSummary: Component<ExecutiveSummaryProps> = (props) => {
|
|||||||
{formatNumber(props.totals.grandTotal)}
|
{formatNumber(props.totals.grandTotal)}
|
||||||
</div>
|
</div>
|
||||||
<div class="mt-8">
|
<div class="mt-8">
|
||||||
|
<div class="space-y-3">
|
||||||
<button
|
<button
|
||||||
onClick={props.onFinalizeBid}
|
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"
|
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
|
<Plus class="w-5 h-5" /> Finalize Bid
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
onClick={props.onExportManagerInfo}
|
||||||
|
class="w-full py-4 bg-muted hover:bg-muted/80 text-foreground rounded-2xl font-black text-xs uppercase tracking-[0.2em] transition-all border border-border active:scale-95 flex items-center justify-center gap-3"
|
||||||
|
>
|
||||||
|
<Info class="w-5 h-5" /> Manager Info
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
@plugin "@tailwindcss/typography";
|
@plugin "@tailwindcss/typography";
|
||||||
|
|
||||||
/* Import Standard Shadcn Font (Inter) */
|
/* Import Standard Shadcn Font (Inter) */
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@100..900&display=swap');
|
|
||||||
|
|
||||||
@theme {
|
@theme {
|
||||||
/* Clean, single-font family for a modern SaaS look */
|
/* Clean, single-font family for a modern SaaS look */
|
||||||
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
|
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
|
||||||
|
|||||||
Reference in New Issue
Block a user