Cash Flow Treasury Dashboard
blockfinanceFluxo and Mercury style corporate cash flow treasury dashboard with 30-day runway projection, liquidity buffer monitor, inflow/outflow breakdown, and connected bank accounts table.
Installation
$pnpm dlx shadcn-vue@latest add https://uipkge.dev/r/vue/cash-flow-treasury-dashboard.json$npx shadcn-vue@latest add https://uipkge.dev/r/vue/cash-flow-treasury-dashboard.json$yarn dlx shadcn-vue@latest add https://uipkge.dev/r/vue/cash-flow-treasury-dashboard.json$bunx shadcn-vue@latest add https://uipkge.dev/r/vue/cash-flow-treasury-dashboard.jsonNamed registry:
npx shadcn-vue@latest add @uipkge/cash-flow-treasury-dashboardInstalls to:app/components/blocks/Variants
Loading interactive previews…
Loading interactive previews…
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
class | HTMLAttributes['class'] | — | optional |
Schema
Type aliases exported from this item's source. Use these to shape the data you pass in.
HealthCardinterface HealthCard {
title: string
value: string
highlight?: string
badgeText: string
badgeVariant: 'default' | 'secondary' | 'outline' | 'destructive'
badgeClass?: string
description: string
metricFootnote: string
progressValue: number
icon: any
}InflowIteminterface InflowItem {
id: string
name: string
amount: string
rawAmount: number
percentage: number
category: string
detail: string
color: string
}OutflowIteminterface OutflowItem {
id: string
name: string
amount: string
rawAmount: number
percentage: number
category: string
detail: string
color: string
}BankAccountinterface BankAccount {
id: string
name: string
institution: string
accountType: string
accountMask: string
routingMask: string
purpose: string
apy: string
balance: string
balanceRaw: number
status: 'Active' | 'Yield Sweeping' | 'Ringfenced' | 'Reserve Locked'
statusType: 'success' | 'info' | 'warning'
isSweepAccount?: boolean
}Files installed (1)
app/components/blocks/CashFlowTreasuryDashboard.vue37.6 kB
<script setup lang="ts"> import { ref, type HTMLAttributes } from 'vue' import { ArrowDownLeft, ArrowUpRight, Building2, CheckCircle2, Flame, Landmark, Lock, RefreshCw, Send, Shield, ShieldCheck, TrendingUp, Zap, } from 'lucide-vue-next' import { cn } from '@/lib/utils' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Progress } from '@/components/ui/progress' import { Separator } from '@/components/ui/separator' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@/components/ui/table' const props = defineProps<{ class?: HTMLAttributes['class'] }>() const emit = defineEmits<{ deposit: [] transfer: [accountName?: string] manageSweep: [] }>() const activeTab = ref<'30d' | '60d' | '90d'>('30d') const activeMilestone = ref<number | null>(null) const actionFeedback = ref<string | null>(null) const isAutoSweepActive = ref(true) interface HealthCard { title: string value: string highlight?: string badgeText: string badgeVariant: 'default' | 'secondary' | 'outline' | 'destructive' badgeClass?: string description: string metricFootnote: string progressValue: number icon: any } const healthCards: HealthCard[] = [ { title: 'Total Cash Runway', value: '28.4 Months Runway', badgeText: 'Optimal Runway', badgeVariant: 'outline', badgeClass: 'bg-success/10 text-success border-success/20', description: 'At current net burn rate ($170.8k/mo)', metricFootnote: 'Zero dilution horizon through Q4 2028', progressValue: 94, icon: ShieldCheck, }, { title: 'Net Monthly Burn', value: '-$170,800.00 / mo', badgeText: '-12% vs budget', badgeVariant: 'outline', badgeClass: 'bg-success/10 text-success border-success/20', description: 'Gross outflow $415.8k offset by $245.0k inflow', metricFootnote: '$23,200 under monthly board approved cap', progressValue: 74, icon: Flame, }, { title: 'Projected 30-Day Inflow', value: '+$245,000.00', badgeText: '+18.4% YoY', badgeVariant: 'outline', badgeClass: 'bg-success/10 text-success border-success/20', description: 'From customer subscriptions & ARR renewals', metricFootnote: '96.2% contracted recurring enterprise revenue', progressValue: 88, icon: TrendingUp, }, { title: 'Yield on Treasury Funds', value: '5.14% APY', highlight: '$20,800/mo interest earned', badgeText: 'Yield Active', badgeVariant: 'outline', badgeClass: 'bg-success/10 text-success border-success/20', description: '$2.85M deployed in US Treasury bills & sweeps', metricFootnote: '$249,600 annualized passive treasury income', progressValue: 98, icon: TrendingUp, }, ] interface InflowItem { id: string name: string amount: string rawAmount: number percentage: number category: string detail: string color: string } const inflowBreakdown: InflowItem[] = [ { id: 'in-arr', name: 'Customer ARR', amount: '+$210,000.00', rawAmount: 210000, percentage: 85.7, category: 'Subscription ARR', detail: 'Enterprise multi-seat renewals and monthly cloud subscriptions', color: 'bg-success', }, { id: 'in-yield', name: 'Treasury Yield', amount: '+$20,800.00', rawAmount: 20800, percentage: 8.5, category: 'Money Market Sweep', detail: 'Accrued interest from 5.14% APY government sweeps & T-Bills', color: 'bg-teal-500', }, { id: 'in-rebates', name: 'Partner Rebates', amount: '+$14,200.00', rawAmount: 14200, percentage: 5.8, category: 'Interchange & Cashback', detail: 'Corporate card cashback and cloud partner referral volume', color: 'bg-cyan-500', }, ] interface OutflowItem { id: string name: string amount: string rawAmount: number percentage: number category: string detail: string color: string } const outflowBreakdown: OutflowItem[] = [ { id: 'out-payroll', name: 'Payroll & Benefits', amount: '-$138,400.00', rawAmount: 138400, percentage: 81.0, category: 'Compensation', detail: '42 full-time employees, health insurance, and 401(k) matching', color: 'bg-destructive', }, { id: 'out-infra', name: 'Cloud Infrastructure', amount: '-$24,800.00', rawAmount: 24800, percentage: 14.5, category: 'Hosting & AI APIs', detail: 'AWS compute clusters, Vercel edge deployment, & OpenAI inference', color: 'bg-warning', }, { id: 'out-office', name: 'Office & Taxes', amount: '-$7,600.00', rawAmount: 7600, percentage: 4.5, category: 'G&A and Compliance', detail: 'San Francisco hub lease, state registrations & tax prep reserves', color: 'bg-slate-500 dark:bg-slate-400', }, ] interface BankAccount { id: string name: string institution: string accountType: string accountMask: string routingMask: string purpose: string apy: string balance: string balanceRaw: number status: 'Active' | 'Yield Sweeping' | 'Ringfenced' | 'Reserve Locked' statusType: 'success' | 'info' | 'warning' isSweepAccount?: boolean } const bankAccounts: BankAccount[] = [ { id: 'acc-operating', name: 'Chase Corporate Operating', institution: 'JPMorgan Chase Bank', accountType: 'Operating Checking', accountMask: '•••• 4892', routingMask: '•••• 0210', purpose: 'Primary AP, vendor payouts & incoming customer wires', apy: '0.05% APY', balance: '$1,420,500.00', balanceRaw: 1420500, status: 'Active', statusType: 'success', }, { id: 'acc-sweep', name: 'Morgan Stanley Treasury Sweep', institution: 'Morgan Stanley Private Bank', accountType: 'High-Yield Treasury Sweep', accountMask: '•••• 9210', routingMask: '•••• 0119', purpose: 'Automated daily sweep of cash over $1.0M into 4-week US T-Bills', apy: '5.14% APY', balance: '$2,850,000.00', balanceRaw: 2850000, status: 'Yield Sweeping', statusType: 'success', isSweepAccount: true, }, { id: 'acc-payroll', name: 'SVB Payroll Escrow', institution: 'Silicon Valley Bank', accountType: 'Payroll Escrow', accountMask: '•••• 1049', routingMask: '•••• 1211', purpose: 'Ringfenced 2-cycle buffer for bi-weekly employee compensation', apy: '1.20% APY', balance: '$380,240.00', balanceRaw: 380240, status: 'Ringfenced', statusType: 'info', }, { id: 'acc-tax', name: 'Citi Corporate Tax Reserve', institution: 'Citibank N.A.', accountType: 'Tax Reserve', accountMask: '•••• 8832', routingMask: '•••• 0008', purpose: 'Q3 estimated federal/state taxes & corporate franchise reserves', apy: '2.10% APY', balance: '$199,500.00', balanceRaw: 199500, status: 'Reserve Locked', statusType: 'warning', }, ] const trajectoryMilestones = [ { day: 'Day 1', date: 'Aug 21', title: 'Starting Liquidity', amount: '$4,850,240.00', note: 'Base cash balance', cx: 50, cy: 130, }, { day: 'Day 7', date: 'Aug 28', title: 'Weekly ARR Inflow', amount: '+$52,500.00', note: 'SaaS recurring billing', cx: 180, cy: 123, }, { day: 'Day 15', date: 'Sep 05', title: 'Mid-Month Payroll Run', amount: '-$69,200.00', note: 'Bi-weekly payroll disbursal', cx: 330, cy: 144, }, { day: 'Day 22', date: 'Sep 12', title: 'Enterprise ARR Renewals', amount: '+$185,000.00', note: 'Annual contract renewals', cx: 490, cy: 89, }, { day: 'Day 30', date: 'Sep 20', title: 'Projected Ending Balance', amount: '$4,924,440.00', note: '+$74,200 net increase', cx: 650, cy: 108, }, ] function triggerNotification(msg: string) { actionFeedback.value = msg setTimeout(() => { if (actionFeedback.value === msg) { actionFeedback.value = null } }, 4000) } function handleDeposit() { emit('deposit') triggerNotification('Deposit modal opened. Direct ACH & Wire instructions ready for UIPKGE Technologies Inc.') } function handleTransfer(accountName?: string) { emit('transfer', accountName) triggerNotification( accountName ? `Transfer interface initiated from ${accountName}. Real-time settlement active.` : 'Initiating wire & multi-account transfer portal.', ) } function handleManageSweep() { emit('manageSweep') isAutoSweepActive.value = !isAutoSweepActive.value triggerNotification( isAutoSweepActive.value ? 'Auto-Sweep rule ACTIVE: Daily excess over $1.0M swept into 5.14% APY Treasury Bills.' : 'Auto-Sweep rule PAUSED. Funds will remain in standard operating checking.', ) } </script> <template> <div data-slot="cash-flow-treasury-dashboard" :class="cn('w-full space-y-6', props.class)"> <!-- Top Action Feedback Banner --> <div v-if="actionFeedback" class="bg-primary/10 border-primary/20 text-foreground flex flex-wrap items-center justify-between gap-x-2 rounded-lg border px-4 py-3 text-xs shadow-xs" > <div class="flex items-center gap-2.5"> <CheckCircle2 class="text-primary size-4 shrink-0" aria-hidden="true" /> <span class="font-medium">{{ actionFeedback }}</span> </div> <button type="button" class="text-muted-foreground hover:text-foreground inline-flex size-5 items-center justify-center rounded-sm" @click="actionFeedback = null" > <span class="sr-only">Close message</span> × </button> </div> <!-- Header Section --> <div class="bg-card border-border relative overflow-hidden rounded-xl border p-6 shadow-xs"> <div class="flex flex-col gap-6 lg:flex-row lg:items-center lg:justify-between"> <!-- Company & Title --> <div class="space-y-2"> <div class="flex flex-wrap items-center gap-2.5"> <span class="text-muted-foreground font-mono text-xs font-semibold tracking-wider uppercase"> UIPKGE Technologies Inc. </span> <Badge variant="outline" class="border-success/20 bg-success/10 text-success gap-1.5 text-xs font-medium"> <span class="bg-success size-1.5 animate-pulse rounded-full" aria-hidden="true" /> Live FDIC Insured Sweeps ($5M Aggregate) </Badge> </div> <div> <h1 class="text-foreground text-2xl font-semibold tracking-tight sm:text-3xl"> Treasury & Cash Flow Management </h1> <p class="text-muted-foreground mt-0.5 text-xs sm:text-sm"> Real-time liquidity monitoring, 30-day runway projection, and automated 5.14% APY yield sweep desk. </p> </div> </div> <!-- Header Action Controls --> <div class="flex flex-wrap items-center gap-2.5"> <Button variant="outline" class="gap-2 text-xs font-medium sm:text-sm" @click="handleDeposit"> <ArrowDownLeft class="size-4" aria-hidden="true" /> Deposit Funds </Button> <Button class="gap-2 text-xs font-medium sm:text-sm" @click="handleTransfer()"> <ArrowUpRight class="size-4" aria-hidden="true" /> Initiate Wire / Transfer </Button> </div> </div> <Separator class="my-5" /> <!-- Net Total Liquidity Hero Metric Row --> <div class="grid grid-cols-1 gap-6 md:grid-cols-12 md:items-end"> <div class="space-y-1.5 md:col-span-6 lg:col-span-5"> <div class="text-muted-foreground text-xs font-medium">Net Total Liquidity</div> <div class="flex flex-wrap items-baseline gap-3"> <span class="text-foreground text-3xl font-semibold tracking-tight tabular-nums sm:text-4xl"> $4,850,240.00 </span> <Badge variant="outline" class="border-success/20 bg-success/10 text-success gap-1 px-2 py-0.5 text-xs font-semibold" > <TrendingUp class="size-3" aria-hidden="true" /> +$74,200.00 (+1.55%) this month </Badge> </div> <p class="text-muted-foreground text-xs">Aggregated across 4 corporate custody & sweep accounts</p> </div> <!-- Liquidity Allocation Mini Pills --> <div class="grid grid-cols-1 gap-2.5 sm:grid-cols-3 md:col-span-6 lg:col-span-7"> <div class="border-border bg-muted/40 rounded-lg border p-2.5"> <span class="text-muted-foreground block text-xs">Available Cash</span> <span class="text-foreground font-mono text-sm font-semibold tabular-nums">$1,420,500.00</span> <span class="text-muted-foreground mt-0.5 block text-xs">29.3% in Operating</span> </div> <div class="border-border bg-muted/40 rounded-lg border p-2.5"> <div class="flex flex-wrap items-center justify-between gap-x-2"> <span class="text-muted-foreground block text-xs">Yield Sweep</span> <Badge variant="outline" class="border-success/30 text-success px-1 py-0 text-xs font-normal"> 5.14% APY </Badge> </div> <span class="text-foreground font-mono text-sm font-semibold tabular-nums">$2,850,000.00</span> <span class="text-muted-foreground mt-0.5 block text-xs">58.8% in T-Bills</span> </div> <div class="border-border bg-muted/40 rounded-lg border p-2.5"> <span class="text-muted-foreground block text-xs">Escrow & Reserves</span> <span class="text-foreground font-mono text-sm font-semibold tabular-nums">$579,740.00</span> <span class="text-muted-foreground mt-0.5 block text-xs">11.9% Ringfenced</span> </div> </div> </div> </div> <!-- 4 Treasury Health Metric Cards --> <div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4"> <Card v-for="card in healthCards" :key="card.title" class="border-border relative flex flex-col justify-between overflow-hidden shadow-xs" > <CardHeader class="flex flex-row items-center justify-between space-y-0 pb-2"> <CardTitle class="text-muted-foreground text-xs font-medium tracking-tight"> {{ card.title }} </CardTitle> <Badge :variant="card.badgeVariant" :class="cn('text-xs font-medium', card.badgeClass)"> {{ card.badgeText }} </Badge> </CardHeader> <CardContent class="space-y-3"> <div> <div :class=" cn( 'text-foreground text-2xl font-semibold tracking-tight tabular-nums', card.title === 'Yield on Treasury Funds' && 'text-success', ) " > {{ card.value }} </div> <p v-if="card.highlight" class="text-success mt-0.5 text-xs font-semibold"> {{ card.highlight }} </p> <p class="text-muted-foreground mt-1 text-xs leading-normal"> {{ card.description }} </p> </div> <div class="space-y-1.5 border-t pt-2.5"> <Progress :model-value="card.progressValue" class="h-1.5" /> <div class="text-muted-foreground flex flex-wrap items-center justify-between gap-x-2 text-xs"> <span>{{ card.metricFootnote }}</span> </div> </div> </CardContent> </Card> </div> <!-- 30-Day Cash Flow Projection & Inflow/Outflow Breakdown Section --> <div class="grid grid-cols-1 gap-6 lg:grid-cols-12"> <!-- 30-Day Cash Flow Projection Chart (7 Cols on LG) --> <Card class="border-border flex flex-col justify-between shadow-xs lg:col-span-7"> <CardHeader class="border-b pb-4"> <div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between"> <div class="space-y-0.5"> <div class="flex items-center gap-2"> <CardTitle class="text-base font-semibold tracking-tight"> 30-Day Cash Flow Projection </CardTitle> <Badge variant="outline" class="border-success/20 bg-success/10 text-success text-xs"> 95% Confidence Band </Badge> </div> <CardDescription class="text-xs"> Trajectory from <strong class="text-foreground">$4,850,240.00</strong> to projected <strong class="text-foreground">$4,924,440.00</strong> balance. </CardDescription> </div> <!-- Timeframe selector --> <div class="flex items-center rounded-lg border p-0.5 text-xs"> <button type="button" :class=" cn( 'rounded-md px-2.5 py-1 font-medium transition-colors', activeTab === '30d' ? 'bg-primary text-primary-foreground shadow-xs' : 'text-muted-foreground hover:text-foreground', ) " @click="activeTab = '30d'" > 30D Projection </button> <button type="button" :class=" cn( 'rounded-md px-2.5 py-1 font-medium transition-colors', activeTab === '60d' ? 'bg-primary text-primary-foreground shadow-xs' : 'text-muted-foreground hover:text-foreground', ) " @click="activeTab = '60d'" > 60D </button> <button type="button" :class=" cn( 'rounded-md px-2.5 py-1 font-medium transition-colors', activeTab === '90d' ? 'bg-primary text-primary-foreground shadow-xs' : 'text-muted-foreground hover:text-foreground', ) " @click="activeTab = '90d'" > 90D </button> </div> </div> </CardHeader> <CardContent class="space-y-4 pt-5"> <!-- Responsive Projection SVG Curve with Confidence Corridor --> <div class="bg-muted/10 relative w-full overflow-x-auto rounded-lg border p-2"> <svg viewBox="0 0 700 220" class="h-56 w-full max-w-[560px] min-w-full overflow-visible" preserveAspectRatio="none" aria-label="30-day corporate cash projection graph" > <defs> <linearGradient id="cfBandGradient" x1="0" y1="0" x2="0" y2="1"> <stop offset="0%" stop-color="#10B981" stop-opacity="0.22" /> <stop offset="100%" stop-color="#10B981" stop-opacity="0.02" /> </linearGradient> <linearGradient id="cfLineGradient" x1="0" y1="0" x2="1" y2="0"> <stop offset="0%" stop-color="#10B981" /> <stop offset="45%" stop-color="#06B6D4" /> <stop offset="100%" stop-color="#10B981" /> </linearGradient> </defs> <!-- Horizontal Grid Guidelines --> <g stroke="currentColor" class="text-border/60" stroke-width="1" stroke-dasharray="3 3"> <line x1="40" y1="55" x2="670" y2="55" /> <line x1="40" y1="115" x2="670" y2="115" /> <line x1="40" y1="175" x2="670" y2="175" /> </g> <!-- Guideline Labels --> <g class="fill-muted-foreground font-mono text-xs" font-size="12"> <text x="4" y="58">$5.10M</text> <text x="4" y="118">$4.85M</text> <text x="4" y="178">$4.60M</text> </g> <!-- 95% Confidence Band Polygon Area --> <path d="M 50,120 C 110,110 140,95 180,90 C 240,85 280,105 330,105 C 390,105 430,55 490,55 C 550,55 590,75 650,75 L 650,140 C 590,140 550,125 490,125 C 430,125 390,175 330,175 C 280,175 240,155 180,155 C 140,155 110,140 50,140 Z" fill="url(#cfBandGradient)" /> <!-- Upper Confidence Bound Line (Dashed) --> <path d="M 50,120 C 110,110 140,95 180,90 C 240,85 280,105 330,105 C 390,105 430,55 490,55 C 550,55 590,75 650,75" fill="none" stroke="#10B981" stroke-width="1.5" stroke-dasharray="4 4" opacity="0.6" /> <!-- Lower Confidence Bound Line (Dashed) --> <path d="M 50,140 C 110,140 140,155 180,155 C 240,155 280,175 330,175 C 390,175 430,125 490,125 C 550,125 590,140 650,140" fill="none" stroke="#10B981" stroke-width="1.5" stroke-dasharray="4 4" opacity="0.4" /> <!-- Median Expected Trajectory Main Curve --> <path d="M 50,130 C 110,128 140,123 180,123 C 240,123 280,144 330,144 C 390,144 430,89 490,89 C 550,89 590,108 650,108" fill="none" stroke="url(#cfLineGradient)" stroke-width="3" stroke-linecap="round" /> <!-- Milestone Markers on Curve --> <g v-for="(m, idx) in trajectoryMilestones" :key="m.day" class="cursor-pointer" @mouseenter="activeMilestone = idx" > <circle :cx="m.cx" :cy="m.cy" r="6" class="fill-background stroke-emerald-500" stroke-width="3" /> <circle v-if="activeMilestone === idx" :cx="m.cx" :cy="m.cy" r="11" class="fill-success/20 stroke-emerald-400" stroke-width="1" /> </g> </svg> <!-- Bottom X-Axis Milestone Labels --> <div class="mt-2 grid max-w-[560px] min-w-full grid-cols-5 gap-1 border-t pt-2 text-center"> <div v-for="(m, idx) in trajectoryMilestones" :key="m.day" :class=" cn( 'rounded-md p-1 transition-colors', activeMilestone === idx ? 'bg-muted ring-border/80 ring-1' : 'hover:bg-muted/40', ) " @mouseenter="activeMilestone = idx" > <span class="text-foreground block text-xs font-semibold">{{ m.day }}</span> <span class="text-muted-foreground block text-xs">{{ m.date }}</span> <span :class=" cn( 'font-mono text-xs font-medium tabular-nums', m.amount.startsWith('+') ? 'text-success' : m.amount.startsWith('-') ? 'text-destructive' : 'text-foreground', ) " > {{ m.amount }} </span> </div> </div> </div> <!-- Chart Key Legend & Milestone Detail Bar --> <div class="flex flex-wrap items-center justify-between gap-3 text-xs"> <div class="flex flex-wrap items-center gap-4"> <div class="flex items-center gap-1.5"> <span class="bg-success size-2.5 rounded-full" /> <span class="text-foreground font-medium">Expected Trajectory</span> </div> <div class="flex items-center gap-1.5"> <span class="border-success bg-success/20 h-2 w-4 rounded-xs border border-dashed" /> <span class="text-muted-foreground">95% Confidence Corridor</span> </div> <div class="flex items-center gap-1.5"> <span class="bg-background border-success size-2 rounded-full border-2" /> <span class="text-muted-foreground">Cash Events</span> </div> </div> <div class="text-muted-foreground font-mono"> Net 30-Day Delta: <strong class="text-success">+$74,200.00</strong> </div> </div> </CardContent> </Card> <!-- Inflow & Outflow Breakdown Side Column (5 Cols on LG) --> <div class="space-y-4 lg:col-span-5"> <!-- Inflow Breakdown Card --> <Card class="border-border shadow-xs"> <CardHeader class="pb-3"> <div class="flex flex-wrap items-center justify-between gap-x-2"> <div class="flex items-center gap-2"> <div class="bg-success/10 text-success flex size-7 items-center justify-center rounded-md"> <ArrowDownLeft class="size-4" aria-hidden="true" /> </div> <div> <CardTitle class="text-sm font-semibold tracking-tight">Projected 30D Inflow</CardTitle> <CardDescription class="text-xs">Total incoming cash: +$245,000.00</CardDescription> </div> </div> <Badge variant="outline" class="border-success/20 bg-success/10 text-success text-xs font-semibold"> 100% </Badge> </div> </CardHeader> <CardContent class="space-y-3"> <div v-for="item in inflowBreakdown" :key="item.id" class="space-y-1.5"> <div class="flex flex-wrap items-center justify-between gap-x-2 text-xs"> <div class="flex items-center gap-2"> <span class="text-foreground font-medium">{{ item.name }}</span> <span class="text-muted-foreground font-mono text-xs">({{ item.percentage }}%)</span> </div> <span class="text-success text-success font-mono font-semibold tabular-nums"> {{ item.amount }} </span> </div> <Progress :model-value="item.percentage" class="h-2" /> <p class="text-muted-foreground text-xs">{{ item.detail }}</p> </div> </CardContent> </Card> <!-- Outflow Breakdown Card --> <Card class="border-border shadow-xs"> <CardHeader class="pb-3"> <div class="flex flex-wrap items-center justify-between gap-x-2"> <div class="flex items-center gap-2"> <div class="bg-destructive/10 text-destructive flex size-7 items-center justify-center rounded-md"> <ArrowUpRight class="size-4" aria-hidden="true" /> </div> <div> <CardTitle class="text-sm font-semibold tracking-tight">Projected 30D Outflow</CardTitle> <CardDescription class="text-xs">Total operational burn: -$170,800.00</CardDescription> </div> </div> <Badge variant="outline" class="border-destructive/20 bg-destructive/10 text-destructive text-xs font-semibold" > 100% </Badge> </div> </CardHeader> <CardContent class="space-y-3"> <div v-for="item in outflowBreakdown" :key="item.id" class="space-y-1.5"> <div class="flex flex-wrap items-center justify-between gap-x-2 text-xs"> <div class="flex items-center gap-2"> <span class="text-foreground font-medium">{{ item.name }}</span> <span class="text-muted-foreground font-mono text-xs">({{ item.percentage }}%)</span> </div> <span class="text-destructive text-destructive font-mono font-semibold tabular-nums"> {{ item.amount }} </span> </div> <Progress :model-value="item.percentage" class="h-2" /> <p class="text-muted-foreground text-xs">{{ item.detail }}</p> </div> </CardContent> </Card> </div> </div> <!-- Connected Corporate Bank Accounts Table --> <div class="bg-card border-border overflow-hidden rounded-xl border shadow-xs"> <div class="flex flex-col gap-3 border-b p-5 sm:flex-row sm:items-center sm:justify-between"> <div class="space-y-1"> <div class="flex items-center gap-2.5"> <Landmark class="text-muted-foreground size-5" aria-hidden="true" /> <h3 class="text-foreground text-base font-semibold tracking-tight sm:text-lg"> Connected Corporate Bank & Treasury Accounts </h3> </div> <p class="text-muted-foreground text-xs"> 4 institutional accounts connected via real-time Open Banking / Plaid Exchange </p> </div> <div class="flex flex-wrap items-center gap-2.5"> <Button size="sm" variant="outline" :class=" cn( 'gap-1.5 text-xs', isAutoSweepActive ? 'border-success/30 bg-success/10 text-success' : 'text-muted-foreground', ) " @click="handleManageSweep" > <Zap class="size-3.5" aria-hidden="true" /> <span>Auto-Sweep Rule: {{ isAutoSweepActive ? 'Active (5.14% APY)' : 'Paused' }}</span> </Button> <Button size="sm" variant="outline" class="gap-1.5 text-xs" @click="triggerNotification('Bank balances refreshed from Federal Reserve Fedwire & NACHA feeds.')" > <RefreshCw class="size-3.5" aria-hidden="true" /> Refresh Balances </Button> </div> </div> <div class="overflow-x-auto"> <Table> <TableHeader> <TableRow class="bg-muted/40 hover:bg-muted/40"> <TableHead class="min-w-[240px]">Institution & Account</TableHead> <TableHead class="min-w-[220px]">Purpose & Routing</TableHead> <TableHead class="text-center">APY / Yield</TableHead> <TableHead class="text-right">Available Balance</TableHead> <TableHead class="text-center">Status</TableHead> <TableHead class="text-right">Actions</TableHead> </TableRow> </TableHeader> <TableBody> <TableRow v-for="account in bankAccounts" :key="account.id" class="hover:bg-muted/30"> <!-- Account & Institution --> <TableCell> <div class="flex items-center gap-3"> <div class="border-border/80 bg-background flex size-10 shrink-0 items-center justify-center rounded-lg border shadow-xs" > <!-- Chase --> <svg v-if="account.id === 'acc-operating'" viewBox="0 0 24 24" class="size-5 shrink-0 text-[#117ACA]" fill="currentColor" > <path d="M12 2.5L7.5 7h4.8L16.2 3.1 12 2.5zm9.5 9.5L17 7.5v4.8l3.9 3.9.6-4.2zm-9.5 9.5L16.5 17h-4.8L7.8 20.9l4.2.6zm-9.5-9.5L7 16.5v-4.8L3.1 7.8 2.5 12z" /> </svg> <!-- Morgan Stanley Sweep --> <svg v-else-if="account.id === 'acc-sweep'" viewBox="0 0 24 24" class="size-5 shrink-0" fill="none"> <rect width="24" height="24" rx="4" fill="#002D62" /> <path d="M4 17V7l5 5-5 5zm8 0V7l5 5-5 5z" fill="#00A3E0" /> <circle cx="18" cy="12" r="2" fill="#FFFFFF" /> </svg> <!-- SVB --> <svg v-else-if="account.id === 'acc-payroll'" viewBox="0 0 24 24" class="size-5 shrink-0" fill="none" > <rect width="24" height="24" rx="4" fill="#005587" /> <path d="M6 15l6-9 6 9-3 1.5-3-4.5-3 4.5L6 15z" fill="#FFFFFF" /> <path d="M9 16.5l3-4.5 3 4.5-3 1.5-3-1.5z" fill="#00A3E0" /> </svg> <!-- Citibank --> <svg v-else-if="account.id === 'acc-tax'" viewBox="0 0 24 24" class="size-5 shrink-0" fill="none"> <path d="M12 4.5C8 4.5 4.5 6.8 4 9.5h2.5c.5-1.5 3-2.5 5.5-2.5s5 1 5.5 2.5H20c-.5-2.7-4-5-8-5z" fill="#EC111A" /> <rect x="4" y="11" width="3" height="8" rx="0.5" fill="#003B70" /> <circle cx="12" cy="15" r="4" fill="#003B70" /> <rect x="17" y="11" width="3" height="8" rx="0.5" fill="#003B70" /> </svg> <Building2 v-else class="text-muted-foreground size-5" /> </div> <div class="min-w-0 space-y-0.5"> <div class="flex items-center gap-2"> <span class="text-foreground truncate text-sm font-semibold">{{ account.name }}</span> </div> <div class="flex items-center gap-2"> <span class="text-muted-foreground truncate text-xs">{{ account.institution }}</span> <span class="text-muted-foreground font-mono text-xs">{{ account.accountMask }}</span> </div> </div> </div> </TableCell> <!-- Purpose --> <TableCell> <div class="space-y-0.5"> <span class="text-foreground block text-xs font-medium">{{ account.accountType }}</span> <span class="text-muted-foreground block text-xs leading-tight">{{ account.purpose }}</span> </div> </TableCell> <!-- APY Rate --> <TableCell class="text-center"> <Badge v-if="account.isSweepAccount" variant="outline" class="border-success/30 bg-success/10 text-success font-mono text-xs font-semibold" > {{ account.apy }} </Badge> <span v-else class="text-muted-foreground font-mono text-xs"> {{ account.apy }} </span> </TableCell> <!-- Balance --> <TableCell class="text-right"> <span class="text-foreground font-mono text-sm font-semibold tabular-nums"> {{ account.balance }} </span> </TableCell> <!-- Status --> <TableCell class="text-center"> <Badge v-if="account.statusType === 'success'" variant="outline" class="border-success/20 bg-success/10 text-success gap-1 text-xs font-medium" > <span class="bg-success size-1.5 rounded-full" aria-hidden="true" /> {{ account.status }} </Badge> <Badge v-else-if="account.statusType === 'info'" variant="outline" class="border-info/20 bg-info/10 text-info gap-1 text-xs font-medium" > <span class="bg-info size-1.5 rounded-full" aria-hidden="true" /> {{ account.status }} </Badge> <Badge v-else variant="outline" class="border-warning/20 bg-warning/10 text-warning gap-1 text-xs font-medium" > <Lock class="size-3" aria-hidden="true" /> {{ account.status }} </Badge> </TableCell> <!-- Actions --> <TableCell class="text-right"> <div class="flex items-center justify-end gap-1.5"> <Button v-if="account.isSweepAccount" size="sm" variant="outline" class="h-8 gap-1 text-xs" @click="handleManageSweep" > <Zap class="text-success size-3" aria-hidden="true" /> Configure Sweep </Button> <Button v-else size="sm" variant="outline" class="h-8 text-xs" @click="handleTransfer(account.name)"> Transfer </Button> </div> </TableCell> </TableRow> </TableBody> </Table> </div> <!-- Table Summary Footer --> <div class="bg-muted/20 flex flex-col items-center justify-between gap-3 border-t p-4 sm:flex-row"> <div class="flex items-center gap-2"> <Shield class="text-muted-foreground size-4" aria-hidden="true" /> <p class="text-muted-foreground text-xs"> Custody verified through Member FINRA / SIPC & FDIC depository institutions. </p> </div> <div class="flex items-center gap-3"> <span class="text-muted-foreground font-mono text-xs"> Net Liquidity: <strong class="text-foreground">$4,850,240.00</strong> </span> <Button size="sm" class="h-8 text-xs font-medium" @click="handleTransfer()"> <Send class="mr-1.5 size-3.5" aria-hidden="true" /> Transfer Between Accounts </Button> </div> </div> </div> </div> </template>
Raw manifest:https://uipkge.dev/r/vue/cash-flow-treasury-dashboard.json