feat: migrate CRM screens to Ant Design Vue and expand home dashboard
Preserve the local Ant UI work across views/components and add the new home dashboard chart panels before merging latest main.
This commit is contained in:
@@ -0,0 +1,83 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from 'vue';
|
||||
import { Empty as AEmpty } from 'ant-design-vue';
|
||||
import { toFaDigits } from '@/lib/jalali';
|
||||
import type { HomeDashboardSliceDto } from '@mento/shared';
|
||||
import UiChart, { CHART_PALETTE, type UiChartOption } from '@/components/ui/UiChart.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
reasons: HomeDashboardSliceDto[];
|
||||
formatAmount: (value: number) => string;
|
||||
}>();
|
||||
|
||||
const rows = computed(() => props.reasons);
|
||||
const hasData = computed(() => rows.value.some((r) => r.count > 0));
|
||||
const total = computed(() => rows.value.reduce((sum, row) => sum + row.count, 0));
|
||||
|
||||
function share(count: number) {
|
||||
if (!total.value) return 0;
|
||||
return Math.round((count / total.value) * 100);
|
||||
}
|
||||
|
||||
const option = computed<UiChartOption>(() => ({
|
||||
animationDuration: 400,
|
||||
color: CHART_PALETTE,
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
textStyle: { fontFamily: 'IRANSans, Tahoma, sans-serif' },
|
||||
formatter: (p) => {
|
||||
const item = Array.isArray(p) ? p[0] : p;
|
||||
const row = rows.value.find((r) => r.label === item.name);
|
||||
const amount =
|
||||
row?.amount != null ? `<br/>${props.formatAmount(row.amount)}` : '';
|
||||
return `${item.name}<br/>${toFaDigits(Number(item.value ?? 0))} — ${toFaDigits(item.percent ?? 0)}٪${amount}`;
|
||||
},
|
||||
},
|
||||
legend: { show: false },
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: ['58%', '78%'],
|
||||
center: ['50%', '50%'],
|
||||
itemStyle: { borderRadius: 6, borderColor: '#fff', borderWidth: 2 },
|
||||
label: { show: false },
|
||||
data: rows.value.map((r) => ({ name: r.label, value: r.count })),
|
||||
},
|
||||
],
|
||||
}));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="bg-white rounded-2xl border border-slate-200/80 p-4 shadow-sm h-full flex flex-col">
|
||||
<div class="flex items-center justify-between mb-2">
|
||||
<h3 class="font-bold text-slate-800 text-base">دلایل باخت</h3>
|
||||
<span v-if="hasData" class="text-xs text-slate-400">۱۲ ماه اخیر</span>
|
||||
</div>
|
||||
<a-empty v-if="!hasData" description="معاملهٔ باختهای ثبت نشده" />
|
||||
<template v-else>
|
||||
<div class="relative w-36 h-36 mx-auto">
|
||||
<UiChart :option="option" height="144px" />
|
||||
<div class="absolute inset-0 flex flex-col items-center justify-center pointer-events-none">
|
||||
<span class="text-2xl font-black text-slate-800">{{ toFaDigits(total) }}</span>
|
||||
<span class="text-[11px] text-slate-400">معامله باخته</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-2 mt-3">
|
||||
<div
|
||||
v-for="(item, index) in rows"
|
||||
:key="item.label"
|
||||
class="rounded-xl border border-slate-200/70 bg-slate-50 px-2.5 py-2"
|
||||
>
|
||||
<div class="flex items-center gap-1.5">
|
||||
<span
|
||||
class="w-2 h-2 rounded-full shrink-0"
|
||||
:style="{ background: CHART_PALETTE[index % CHART_PALETTE.length] }"
|
||||
></span>
|
||||
<span class="text-[11px] text-slate-600 truncate">{{ item.label }}</span>
|
||||
</div>
|
||||
<div class="text-sm font-black text-slate-800 mt-1">{{ toFaDigits(share(item.count)) }}٪</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</section>
|
||||
</template>
|
||||
Reference in New Issue
Block a user