101 lines
3.0 KiB
Markdown
101 lines
3.0 KiB
Markdown
# معماری — تسک ۱۶: داشبورد هوشمند
|
|
|
|
## فایلهایی که تغییر میکنند
|
|
|
|
```
|
|
src/Dashboard/Controller/DashboardController.php ← اضافه کردن from/to به query + فیلدهای جدید
|
|
src/Admin/Controller/AdminApiController.php ← متد dashboardCharts() → محدود به بازه زمانی
|
|
assets/admin/pages/DashboardPage.tsx ← date range selector + نمودارها
|
|
```
|
|
|
|
## تغییر Backend
|
|
|
|
### DashboardController — اضافه کردن from/to
|
|
|
|
```php
|
|
#[Route('/api/v1/dashboard/clinic', methods: ['GET'])]
|
|
public function clinic(Request $request): JsonResponse
|
|
{
|
|
$from = (int) $request->query->get('from', strtotime('-30 days'));
|
|
$to = (int) $request->query->get('to', time());
|
|
|
|
// query موجود را محدود به بازه میکنیم:
|
|
// WHERE created_at BETWEEN :from AND :to
|
|
|
|
// فیلدهای جدید:
|
|
$smsBalance = $this->smsWalletRepo->getBalance($entityType, $entityId);
|
|
$uniquePatients = $this->patientRecordRepo->countUnique($entityType, $entityId, $from, $to);
|
|
$revenue = $this->patientSessionRepo->sumRevenue($entityType, $entityId, $from, $to);
|
|
|
|
return $this->success([
|
|
// ... فیلدهای موجود ...
|
|
'sms_wallet_balance' => $smsBalance,
|
|
'unique_patients_count' => $uniquePatients,
|
|
'revenue_period_rials' => $revenue,
|
|
]);
|
|
}
|
|
```
|
|
|
|
### AdminApiController — dashboardCharts با بازه زمانی
|
|
|
|
```php
|
|
#[Route('/api/v1/admin/dashboard/charts', methods: ['GET'])]
|
|
public function dashboardCharts(Request $request): JsonResponse
|
|
{
|
|
$from = (int) $request->query->get('from', strtotime('-30 days'));
|
|
$to = (int) $request->query->get('to', time());
|
|
|
|
// appointments_by_day: GROUP BY DATE(FROM_UNIXTIME(created_at))
|
|
// revenue_by_day: از patient_sessions در بازه
|
|
// subscription_sales_by_plan: از clinic_subscriptions در بازه
|
|
}
|
|
```
|
|
|
|
## تغییر Frontend — DashboardPage.tsx
|
|
|
|
### date range selector component:
|
|
|
|
```tsx
|
|
type DateRangePreset = 'week' | 'month' | '3months' | 'custom';
|
|
|
|
interface DateRange {
|
|
from: number; // Unix timestamp
|
|
to: number;
|
|
}
|
|
|
|
function DateRangeSelector({ value, onChange }: {
|
|
value: DateRange;
|
|
onChange: (r: DateRange) => void;
|
|
}) {
|
|
// preset buttons + PersianCalendar برای custom range
|
|
}
|
|
```
|
|
|
|
### نمودارها با Recharts:
|
|
|
|
```tsx
|
|
import { LineChart, Line, BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer } from 'recharts';
|
|
|
|
// chart نوبتها
|
|
<ResponsiveContainer width="100%" height={250}>
|
|
<LineChart data={chartsData?.appointments_by_day}>
|
|
<XAxis dataKey="date" tickFormatter={d => formatDate(d)} />
|
|
<YAxis />
|
|
<Tooltip />
|
|
<Line dataKey="count" stroke="#3b82f6" />
|
|
</LineChart>
|
|
</ResponsiveContainer>
|
|
|
|
// chart درآمد
|
|
<BarChart data={chartsData?.revenue_by_day}>
|
|
<Bar dataKey="amount_rials" fill="#10b981" />
|
|
</BarChart>
|
|
```
|
|
|
|
### نصب dependency:
|
|
|
|
```bash
|
|
ddev exec yarn add recharts
|
|
ddev exec yarn add @types/recharts # اگر نیاز بود
|
|
```
|