264 lines
9 KiB
Vue
264 lines
9 KiB
Vue
<template>
|
|
<section class="py-8 space-y-6">
|
|
<div class="flex items-center justify-between">
|
|
<h2 class="text-2xl font-semibold">Operating Plan</h2>
|
|
<USelect
|
|
v-model="selectedMonth"
|
|
:options="months"
|
|
placeholder="Select month" />
|
|
</div>
|
|
|
|
<!-- Cash Waterfall Summary -->
|
|
<UCard>
|
|
<template #header>
|
|
<h3 class="text-lg font-medium">
|
|
Cash Waterfall - {{ selectedMonth }}
|
|
</h3>
|
|
</template>
|
|
<div
|
|
class="flex items-center justify-between py-4 border-b border-gray-200">
|
|
<div class="flex items-center gap-8">
|
|
<div class="text-center">
|
|
<div class="text-2xl font-bold text-blue-600">€12,000</div>
|
|
<div class="text-xs text-gray-600">Gross Revenue</div>
|
|
</div>
|
|
<UIcon name="i-heroicons-arrow-right" class="text-gray-400" />
|
|
<div class="text-center">
|
|
<div class="text-2xl font-bold text-red-600">-€450</div>
|
|
<div class="text-xs text-gray-600">Fees</div>
|
|
</div>
|
|
<UIcon name="i-heroicons-arrow-right" class="text-gray-400" />
|
|
<div class="text-center">
|
|
<div class="text-2xl font-bold text-green-600">€11,550</div>
|
|
<div class="text-xs text-gray-600">Net Revenue</div>
|
|
</div>
|
|
<UIcon name="i-heroicons-arrow-right" class="text-gray-400" />
|
|
<div class="text-center">
|
|
<div class="text-2xl font-bold text-blue-600">€300</div>
|
|
<div class="text-xs text-gray-600">To Savings</div>
|
|
</div>
|
|
<UIcon name="i-heroicons-arrow-right" class="text-gray-400" />
|
|
<div class="text-center">
|
|
<div class="text-2xl font-bold text-purple-600">€6,400</div>
|
|
<div class="text-xs text-gray-600">Payroll</div>
|
|
</div>
|
|
<UIcon name="i-heroicons-arrow-right" class="text-gray-400" />
|
|
<div class="text-center">
|
|
<div class="text-2xl font-bold text-orange-600">€2,300</div>
|
|
<div class="text-xs text-gray-600">Overhead</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="pt-4">
|
|
<div class="flex items-center justify-between">
|
|
<span class="text-lg font-medium">Available for Operations</span>
|
|
<span class="text-2xl font-bold text-green-600">€2,550</span>
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
|
|
<!-- Monthly Revenue Table -->
|
|
<UCard>
|
|
<template #header>
|
|
<h3 class="text-lg font-medium">Revenue by Stream</h3>
|
|
</template>
|
|
<UTable :rows="revenueStreams" :columns="revenueColumns">
|
|
<template #name-data="{ row }">
|
|
<div class="flex items-center gap-2">
|
|
<span class="font-medium">{{ row.name }}</span>
|
|
<RestrictionChip :restriction="row.restrictions" size="xs" />
|
|
</div>
|
|
</template>
|
|
|
|
<template #target-data="{ row }">
|
|
<span class="font-medium">€{{ row.target.toLocaleString() }}</span>
|
|
</template>
|
|
|
|
<template #committed-data="{ row }">
|
|
<span class="font-medium text-green-600"
|
|
>€{{ row.committed.toLocaleString() }}</span
|
|
>
|
|
</template>
|
|
|
|
<template #actual-data="{ row }">
|
|
<span
|
|
class="font-medium"
|
|
:class="
|
|
row.actual >= row.committed ? 'text-green-600' : 'text-orange-600'
|
|
">
|
|
€{{ row.actual.toLocaleString() }}
|
|
</span>
|
|
</template>
|
|
|
|
<template #variance-data="{ row }">
|
|
<span :class="row.variance >= 0 ? 'text-green-600' : 'text-red-600'">
|
|
{{ row.variance >= 0 ? "+" : "" }}€{{
|
|
row.variance.toLocaleString()
|
|
}}
|
|
</span>
|
|
</template>
|
|
</UTable>
|
|
</UCard>
|
|
|
|
<!-- Costs Breakdown -->
|
|
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
<UCard>
|
|
<template #header>
|
|
<h3 class="text-lg font-medium">Costs</h3>
|
|
</template>
|
|
<div class="space-y-4">
|
|
<div>
|
|
<h4 class="font-medium text-sm mb-2">Payroll</h4>
|
|
<div class="space-y-2">
|
|
<div class="flex justify-between text-sm">
|
|
<span class="text-gray-600">Wages (320h @ €20)</span>
|
|
<span class="font-medium">€6,400</span>
|
|
</div>
|
|
<div class="flex justify-between text-sm">
|
|
<span class="text-gray-600">On-costs (25%)</span>
|
|
<span class="font-medium">€1,600</span>
|
|
</div>
|
|
<div
|
|
class="flex justify-between text-sm font-medium border-t pt-2">
|
|
<span>Total Payroll</span>
|
|
<span>€8,000</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 class="font-medium text-sm mb-2">Overhead</h4>
|
|
<div class="space-y-2">
|
|
<div class="flex justify-between text-sm">
|
|
<span class="text-gray-600">Coworking space</span>
|
|
<span class="font-medium">€800</span>
|
|
</div>
|
|
<div class="flex justify-between text-sm">
|
|
<span class="text-gray-600">Tools & software</span>
|
|
<span class="font-medium">€400</span>
|
|
</div>
|
|
<div class="flex justify-between text-sm">
|
|
<span class="text-gray-600">Insurance</span>
|
|
<span class="font-medium">€200</span>
|
|
</div>
|
|
<div
|
|
class="flex justify-between text-sm font-medium border-t pt-2">
|
|
<span>Total Overhead</span>
|
|
<span>€1,400</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<h4 class="font-medium text-sm mb-2">Production</h4>
|
|
<div class="space-y-2">
|
|
<div class="flex justify-between text-sm">
|
|
<span class="text-gray-600">Dev kits</span>
|
|
<span class="font-medium">€500</span>
|
|
</div>
|
|
<div
|
|
class="flex justify-between text-sm font-medium border-t pt-2">
|
|
<span>Total Production</span>
|
|
<span>€500</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
|
|
<UCard>
|
|
<template #header>
|
|
<h3 class="text-lg font-medium">Net Impact on Savings</h3>
|
|
</template>
|
|
<div class="space-y-4">
|
|
<div class="space-y-3">
|
|
<div class="flex justify-between text-sm">
|
|
<span class="text-gray-600">Net Revenue</span>
|
|
<span class="font-medium text-green-600">€11,550</span>
|
|
</div>
|
|
<div class="flex justify-between text-sm">
|
|
<span class="text-gray-600">Total Costs</span>
|
|
<span class="font-medium text-red-600">-€9,900</span>
|
|
</div>
|
|
<div class="flex justify-between text-lg font-bold border-t pt-3">
|
|
<span>Net</span>
|
|
<span class="text-green-600">+€1,650</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="bg-gray-50 rounded-lg p-4">
|
|
<h4 class="font-medium text-sm mb-3">Allocation</h4>
|
|
<div class="space-y-2">
|
|
<div class="flex justify-between text-sm">
|
|
<span class="text-gray-600">To Savings</span>
|
|
<span class="font-medium">€1,200</span>
|
|
</div>
|
|
<div class="flex justify-between text-sm">
|
|
<span class="text-gray-600">Available</span>
|
|
<span class="font-medium">€450</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="text-xs text-gray-600 space-y-1">
|
|
<p>
|
|
<RestrictionChip restriction="Restricted" size="xs" /> funds can
|
|
only be used for approved purposes.
|
|
</p>
|
|
<p>
|
|
<RestrictionChip restriction="General" size="xs" /> funds have no
|
|
restrictions.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
</div>
|
|
</section>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
const selectedMonth = ref("2024-01");
|
|
const months = ref([
|
|
{ label: "January 2024", value: "2024-01" },
|
|
{ label: "February 2024", value: "2024-02" },
|
|
{ label: "March 2024", value: "2024-03" },
|
|
]);
|
|
|
|
const revenueStreams = ref([
|
|
{
|
|
id: 1,
|
|
name: "Client Services",
|
|
target: 7800,
|
|
committed: 6500,
|
|
actual: 7200,
|
|
variance: 700,
|
|
restrictions: "General",
|
|
},
|
|
{
|
|
id: 2,
|
|
name: "Platform Sales",
|
|
target: 3000,
|
|
committed: 2000,
|
|
actual: 2400,
|
|
variance: 400,
|
|
restrictions: "General",
|
|
},
|
|
{
|
|
id: 3,
|
|
name: "Grant Funding",
|
|
target: 1200,
|
|
committed: 0,
|
|
actual: 1400,
|
|
variance: 1400,
|
|
restrictions: "Restricted",
|
|
},
|
|
]);
|
|
|
|
const revenueColumns = [
|
|
{ id: "name", key: "name", label: "Stream" },
|
|
{ id: "target", key: "target", label: "Target" },
|
|
{ id: "committed", key: "committed", label: "Committed" },
|
|
{ id: "actual", key: "actual", label: "Actual" },
|
|
{ id: "variance", key: "variance", label: "Variance" },
|
|
];
|
|
</script>
|