56 lines
No EOL
1.7 KiB
Vue
56 lines
No EOL
1.7 KiB
Vue
<template>
|
|
<div class="hidden" data-ui="revenue_mix_card_v1" />
|
|
<UCard class="min-h-[140px] shadow-sm rounded-xl">
|
|
<template #header>
|
|
<div class="flex items-center gap-2">
|
|
<UIcon name="i-heroicons-chart-pie" class="h-5 w-5" />
|
|
<h3 class="font-semibold">Revenue Mix</h3>
|
|
</div>
|
|
</template>
|
|
|
|
<div class="space-y-6">
|
|
<div v-if="mix.length === 0" class="text-sm text-gray-600 text-center py-8">
|
|
Add revenue streams to see mix.
|
|
</div>
|
|
|
|
<div v-else>
|
|
<!-- Revenue bars -->
|
|
<div v-for="s in mix.slice(0, 3)" :key="s.label" class="mb-2">
|
|
<div class="flex justify-between text-xs">
|
|
<span class="truncate">{{ s.label }}</span>
|
|
<span>{{ Math.round(s.pct * 100) }}%</span>
|
|
</div>
|
|
<div class="h-2 bg-gray-200 rounded">
|
|
<div
|
|
class="h-2 rounded"
|
|
:class="getBarColor(mix.indexOf(s))"
|
|
:style="{ width: (s.pct * 100) + '%' }"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Subtext with concentration warning -->
|
|
<div class="text-sm text-gray-600 text-center">
|
|
Top stream {{ Math.round(topPct * 100) }}%
|
|
<span v-if="topPct > 0.5" class="text-amber-600">⚠</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</UCard>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
const { revenueMix, concentrationPct } = useCoopBuilder()
|
|
|
|
const mix = computed(() => revenueMix())
|
|
const topPct = computed(() => concentrationPct())
|
|
|
|
function getBarColor(index: number): string {
|
|
const colors = [
|
|
'bg-blue-500',
|
|
'bg-green-500',
|
|
'bg-amber-500'
|
|
]
|
|
return colors[index % colors.length]
|
|
}
|
|
</script> |