app/components/RevenueMixTable.vue

93 lines
No EOL
2.9 KiB
Vue

<template>
<div class="space-y-3">
<!-- Revenue streams table -->
<div class="space-y-2">
<div v-for="stream in sortedStreams" :key="stream.id"
class="flex justify-between items-center text-sm">
<div class="flex items-center gap-2">
<div
class="w-3 h-3 rounded"
:style="{ backgroundColor: getStreamColor(stream.id) }"
/>
<span class="font-medium">{{ stream.name }}</span>
</div>
<div class="flex items-center gap-2">
<span class="text-neutral-600">{{ formatCurrency(stream.targetMonthlyAmount || 0) }}</span>
<span class="font-medium min-w-[40px] text-right">{{ stream.percentage }}%</span>
</div>
</div>
</div>
<!-- Concentration warning -->
<div v-if="concentrationWarning"
class="p-3 bg-yellow-50 border border-yellow-200 rounded-md">
<div class="flex items-start gap-2">
<UIcon name="i-heroicons-exclamation-triangle" class="w-4 h-4 text-yellow-600 mt-0.5" />
<div class="text-sm text-yellow-800">
<span class="font-medium">{{ concentrationWarning.stream }}</span>
= {{ concentrationWarning.percentage }}% of total consider balancing
</div>
</div>
</div>
<!-- Totals -->
<div class="pt-2 border-t border-neutral-200 text-xs text-neutral-600">
<div class="flex justify-between">
<span>Total monthly target:</span>
<span class="font-medium">{{ formatCurrency(totalMonthly) }}</span>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { storeToRefs } from 'pinia'
const streamsStore = useStreamsStore()
const { streams } = storeToRefs(streamsStore)
const totalMonthly = computed(() => {
return streams.value.reduce((sum, s) => sum + (s.targetMonthlyAmount || 0), 0)
})
const sortedStreams = computed(() => {
const withPercentages = streams.value
.map(stream => {
const amount = stream.targetMonthlyAmount || 0
const percentage = totalMonthly.value > 0
? Math.round((amount / totalMonthly.value) * 100)
: 0
return { ...stream, percentage }
})
.filter(s => s.percentage > 0)
return withPercentages.sort((a, b) => b.percentage - a.percentage)
})
const concentrationWarning = computed(() => {
const topStream = sortedStreams.value[0]
if (topStream && topStream.percentage >= 50) {
return {
stream: topStream.name,
percentage: topStream.percentage
}
}
return null
})
const colors = ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6', '#06b6d4', '#84cc16']
function getStreamColor(streamId: string) {
const index = streams.value.findIndex(s => s.id === streamId)
return colors[index % colors.length]
}
function formatCurrency(amount: number) {
return new Intl.NumberFormat('en-EU', {
style: 'currency',
currency: 'EUR',
minimumFractionDigits: 0,
maximumFractionDigits: 0
}).format(amount)
}
</script>