feat(dashboard): fix logout flow and redesign executive dashboard with real live data, charts and feeds
deploy-chabokan / deploy (push) Canceled after 0s
deploy-chabokan / deploy (push) Canceled after 0s
This commit is contained in:
@@ -0,0 +1,117 @@
|
||||
<script setup>
|
||||
import { useLayout } from '@/layout/composables/layout';
|
||||
import { computed, onMounted, ref, watch } from 'vue';
|
||||
|
||||
const props = defineProps({
|
||||
statusStats: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
});
|
||||
|
||||
const { isDarkTheme } = useLayout();
|
||||
|
||||
const chartData = ref(null);
|
||||
const chartOptions = ref(null);
|
||||
|
||||
const defaultColors = ['#10b981', '#3b82f6', '#f59e0b', '#8b5cf6', '#ec4899', '#06b6d4', '#64748b'];
|
||||
|
||||
function updateChart() {
|
||||
const statuses = props.statusStats || [];
|
||||
const labels = statuses.length > 0 ? statuses.map((s) => s.name) : ['نامشخص'];
|
||||
const data = statuses.length > 0 ? statuses.map((s) => s.count) : [0];
|
||||
const colors = statuses.map((s, i) => s.color || defaultColors[i % defaultColors.length]);
|
||||
|
||||
chartData.value = {
|
||||
labels: labels,
|
||||
datasets: [
|
||||
{
|
||||
data: data,
|
||||
backgroundColor: colors,
|
||||
borderWidth: 2,
|
||||
borderColor: isDarkTheme.value ? '#18181b' : '#ffffff'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
chartOptions.value = {
|
||||
maintainAspectRatio: false,
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
rtl: true,
|
||||
labels: {
|
||||
usePointStyle: true,
|
||||
pointStyle: 'circle',
|
||||
padding: 16,
|
||||
font: {
|
||||
family: 'Estedad',
|
||||
size: 12
|
||||
}
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
rtl: true,
|
||||
titleFont: { family: 'Estedad', size: 13 },
|
||||
bodyFont: { family: 'Estedad', size: 12 },
|
||||
callbacks: {
|
||||
label: function (context) {
|
||||
const count = context.parsed;
|
||||
const statusItem = statuses[context.dataIndex];
|
||||
const pct = statusItem?.percentage !== undefined ? ` (${statusItem.percentage}٪)` : '';
|
||||
return `${context.label}: ${Number(count).toLocaleString('fa-IR')} ملک${pct}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
cutout: '65%'
|
||||
};
|
||||
}
|
||||
|
||||
watch(() => props.statusStats, () => {
|
||||
updateChart();
|
||||
}, { deep: true });
|
||||
|
||||
watch(isDarkTheme, () => {
|
||||
updateChart();
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
updateChart();
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="card h-full p-5 border border-surface-200 dark:border-surface-800 rounded-2xl bg-surface-0 dark:bg-surface-900">
|
||||
<div class="flex justify-between items-center mb-4">
|
||||
<div>
|
||||
<h3 class="font-bold text-lg text-surface-900 dark:text-surface-0 mb-1">وضعیت اسناد و املاک</h3>
|
||||
<p class="text-xs text-muted-color">تفکیک املاک بر اساس وضعیت سند و مالکیت</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs font-medium bg-emerald-50 dark:bg-emerald-950/40 text-emerald-600 dark:text-emerald-400">
|
||||
<i class="pi pi-check-circle text-[10px]"></i>
|
||||
وضعیتها
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="loading" class="h-72 flex items-center justify-center">
|
||||
<i class="pi pi-spin pi-spinner text-primary text-3xl"></i>
|
||||
</div>
|
||||
|
||||
<div v-else-if="!statusStats || statusStats.length === 0" class="h-72 flex flex-col items-center justify-center text-muted-color">
|
||||
<i class="pi pi-chart-pie text-4xl mb-2 opacity-40"></i>
|
||||
<span class="text-sm">اطلاعات وضعیت املاک ثبت نشده است</span>
|
||||
</div>
|
||||
|
||||
<div v-else class="h-72">
|
||||
<Chart type="doughnut" :data="chartData" :options="chartOptions" class="h-full w-full" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user