fix(web): serve real dashboard data instead of mocks

Drop forced home/treasury/reports mock fallbacks so API and database content drive the UI.
This commit is contained in:
2026-11-07 09:04:38 +03:30
parent 4979aa8b75
commit 0998a1ec37
5 changed files with 28 additions and 968 deletions
@@ -45,11 +45,8 @@ import type {
} from 'echarts/components';
import VChart from 'vue-echarts';
import { api } from '@/lib/api';
import axios from 'axios';
import type { TreasuryDashboardDto } from '@mento/shared';
import {
TREASURY_DASHBOARD_MOCK,
isTreasuryDashboardEmpty,
} from './treasury-dashboard-mock';
import { toman } from './treasury-format';
use([CanvasRenderer, BarChart, PieChart, GridComponent, TooltipComponent, LegendComponent]);
@@ -92,7 +89,6 @@ type MetricCard = {
const data = ref<TreasuryDashboardDto | null>(null);
const error = ref('');
const loading = ref(true);
const usingMock = ref(false);
const heroCards: MetricCard[] = [
{
@@ -288,19 +284,18 @@ const mixChartOption = computed<ChartOption>(() => {
onMounted(async () => {
loading.value = true;
usingMock.value = false;
error.value = '';
try {
const res = await api.get<TreasuryDashboardDto>('/treasury/dashboard');
if (isTreasuryDashboardEmpty(res.data)) {
data.value = TREASURY_DASHBOARD_MOCK;
usingMock.value = true;
} else {
data.value = res.data;
}
} catch {
data.value = TREASURY_DASHBOARD_MOCK;
usingMock.value = true;
error.value = '';
data.value = res.data;
} catch (e) {
data.value = null;
error.value = axios.isAxiosError(e)
? String(
(e.response?.data as { message?: string })?.message ??
'بارگذاری داشبورد خزانه ناموفق بود.',
)
: 'بارگذاری داشبورد خزانه ناموفق بود.';
} finally {
loading.value = false;
}
@@ -311,13 +306,6 @@ onMounted(async () => {
<div>
<a-flex vertical :gap="token.margin">
<a-alert v-if="error" type="error" show-icon :message="error" />
<a-alert
v-if="usingMock"
type="info"
show-icon
message="در حال نمایش دادهٔ نمونه (ماک) برای پیش‌نمایش داشبورد هستید."
description="وقتی سند و حساب واقعی ثبت شود، همین صفحه دادهٔ واقعی API را نشان می‌دهد."
/>
<CrmSkeleton :loading="loading">
<a-row :gutter="[token.marginSM, token.marginSM]">
@@ -1,190 +0,0 @@
import type {
FinancialDocumentDto,
FinancialInstallmentDto,
TreasuryDashboardDto,
} from '@mento/shared';
function daysFromNow(days: number) {
const d = new Date();
d.setHours(12, 0, 0, 0);
d.setDate(d.getDate() + days);
return d.toISOString();
}
function doc(
partial: Pick<
FinancialDocumentDto,
'id' | 'kind' | 'title' | 'amount' | 'settledAmount' | 'remaining' | 'status' | 'categoryName' | 'partyName'
> &
Partial<FinancialDocumentDto>,
): FinancialDocumentDto {
return {
bookedAt: daysFromNow(-3),
dueAt: daysFromNow(10),
categoryId: null,
personId: null,
accountId: null,
accountName: 'صندوق اصلی',
method: 'transfer',
reference: null,
notes: null,
createdAt: daysFromNow(-2),
...partial,
partyKind: partial.partyKind ?? null,
companyId: partial.companyId ?? null,
userId: partial.userId ?? null,
};
}
function installment(
partial: FinancialInstallmentDto,
): FinancialInstallmentDto {
return partial;
}
/** دیتای نمایشی برای پیش‌نمایش داشبورد وقتی API خالی است */
export const TREASURY_DASHBOARD_MOCK: TreasuryDashboardDto = {
bookedIncome: '185000000',
actualReceipts: '142500000',
bookedExpense: '98000000',
actualPayments: '87500000',
operatingProfit: '87000000',
receivables: '42500000',
payables: '18500000',
totalBalance: '256000000',
upcomingInstallments: [
installment({
id: 'mock-inst-1',
position: 2,
dueDate: daysFromNow(-2),
amount: '12000000',
paidAmount: '0',
remaining: '12000000',
status: 'overdue',
}),
installment({
id: 'mock-inst-2',
position: 3,
dueDate: daysFromNow(5),
amount: '12000000',
paidAmount: '4000000',
remaining: '8000000',
status: 'partial',
}),
installment({
id: 'mock-inst-3',
position: 4,
dueDate: daysFromNow(35),
amount: '12000000',
paidAmount: '0',
remaining: '12000000',
status: 'open',
}),
],
upcomingPayables: [
doc({
id: 'mock-pay-1',
kind: 'expense',
title: 'اجاره دفتر — مهر',
amount: '45000000',
settledAmount: '30000000',
remaining: '15000000',
status: 'partial',
categoryName: 'اجاره',
partyName: 'موجر ساختمان',
}),
doc({
id: 'mock-pay-2',
kind: 'expense',
title: 'بیمه تکمیلی کارکنان',
amount: '8500000',
settledAmount: '5000000',
remaining: '3500000',
status: 'partial',
categoryName: 'بیمه',
partyName: 'بیمه ایران',
}),
],
recent: [
doc({
id: 'mock-doc-1',
kind: 'income',
title: 'پیش‌پرداخت پروژه آلفا',
amount: '75000000',
settledAmount: '75000000',
remaining: '0',
status: 'settled',
categoryName: 'فروش خدمات',
partyName: 'شرکت آریا',
bookedAt: daysFromNow(-1),
}),
doc({
id: 'mock-doc-2',
kind: 'expense',
title: 'خرید تجهیزات شبکه',
amount: '18500000',
settledAmount: '18500000',
remaining: '0',
status: 'settled',
categoryName: 'تجهیزات',
partyName: 'فروشگاه سخت‌افزار',
bookedAt: daysFromNow(-2),
}),
doc({
id: 'mock-doc-3',
kind: 'income',
title: 'قسط قرارداد بتا',
amount: '12000000',
settledAmount: '0',
remaining: '12000000',
status: 'open',
categoryName: 'فروش خدمات',
partyName: 'گروه سپهر',
bookedAt: daysFromNow(-4),
}),
doc({
id: 'mock-doc-4',
kind: 'expense',
title: 'حقوق مهر — تیم فروش',
amount: '42000000',
settledAmount: '42000000',
remaining: '0',
status: 'settled',
categoryName: 'حقوق',
partyName: null,
bookedAt: daysFromNow(-5),
}),
doc({
id: 'mock-doc-5',
kind: 'income',
title: 'فروش پکیج پشتیبانی',
amount: '28000000',
settledAmount: '10000000',
remaining: '18000000',
status: 'partial',
categoryName: 'پشتیبانی',
partyName: 'کارخانه نوین',
bookedAt: daysFromNow(-6),
}),
],
};
export function isTreasuryDashboardEmpty(data: TreasuryDashboardDto | null | undefined) {
if (!data) return true;
const keys = [
data.bookedIncome,
data.actualReceipts,
data.bookedExpense,
data.actualPayments,
data.totalBalance,
data.receivables,
data.payables,
] as const;
const allZero = keys.every((v) => !Number(v));
return (
allZero &&
data.recent.length === 0 &&
data.upcomingInstallments.length === 0 &&
data.upcomingPayables.length === 0
);
}