53 lines
1.7 KiB
Vue
53 lines
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-neutral-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-neutral-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-neutral-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>
|