feat: implement staff management and subscription system
- Added StaffController for managing clinic staff, including listing, creating, updating, and toggling staff status. - Created ClinicStaff entity and repository for staff data handling. - Developed SubscriptionController to manage subscription plans and periods, including trial subscriptions. - Introduced SubscriptionPlan, SubscriptionPeriod, and ClinicSubscription entities for subscription management. - Implemented SubscriptionService for handling subscription logic, including trial activation and subscription creation from payments. - Added necessary repositories for subscription entities to facilitate data access and manipulation.
This commit is contained in:
@@ -1687,34 +1687,37 @@ class AdminApiController extends BaseController
|
||||
// ── Dashboard Charts ──────────────────────────────────────────────────────
|
||||
|
||||
#[Route('/api/v1/admin/dashboard/charts', methods: ['GET'])]
|
||||
public function dashboardCharts(): JsonResponse
|
||||
public function dashboardCharts(Request $request): JsonResponse
|
||||
{
|
||||
$conn = $this->em->getConnection();
|
||||
$days = 30;
|
||||
$now = time();
|
||||
$start = $now - ($days * 86400);
|
||||
$conn = $this->em->getConnection();
|
||||
$now = time();
|
||||
|
||||
$from = $request->query->get('from') ? (int) $request->query->get('from') : $now - (30 * 86400);
|
||||
$to = $request->query->get('to') ? (int) $request->query->get('to') : $now;
|
||||
|
||||
$apptRows = $conn->fetchAllAssociative(
|
||||
'SELECT DATE(FROM_UNIXTIME(slot_start)) AS d, COUNT(*) AS cnt
|
||||
FROM appointments WHERE slot_start >= :s GROUP BY d ORDER BY d',
|
||||
['s' => $start]
|
||||
FROM appointments WHERE slot_start BETWEEN :s AND :e GROUP BY d ORDER BY d',
|
||||
['s' => $from, 'e' => $to]
|
||||
);
|
||||
$apptMap = array_column($apptRows, 'cnt', 'd');
|
||||
|
||||
$revRows = $conn->fetchAllAssociative(
|
||||
'SELECT DATE(FROM_UNIXTIME(created_at)) AS d, COALESCE(SUM(amount_rials), 0) AS total
|
||||
FROM payments WHERE status = :st AND created_at >= :s GROUP BY d ORDER BY d',
|
||||
['st' => 'received', 's' => $start]
|
||||
FROM payments WHERE status = :st AND created_at BETWEEN :s AND :e GROUP BY d ORDER BY d',
|
||||
['st' => 'received', 's' => $from, 'e' => $to]
|
||||
);
|
||||
$revMap = array_column($revRows, 'total', 'd');
|
||||
|
||||
$appt30d = [];
|
||||
$rev30d = [];
|
||||
for ($i = $days - 1; $i >= 0; $i--) {
|
||||
$date = date('Y-m-d', $now - $i * 86400);
|
||||
$shortDate = date('m/d', $now - $i * 86400);
|
||||
$appt30d[] = ['date' => $shortDate, 'count' => (int)($apptMap[$date] ?? 0)];
|
||||
$rev30d[] = ['date' => $shortDate, 'amount' => (int)($revMap[$date] ?? 0)];
|
||||
$days = max(1, (int) ceil(($to - $from) / 86400));
|
||||
$apptByDay = [];
|
||||
$revByDay = [];
|
||||
for ($i = 0; $i < $days; $i++) {
|
||||
$ts = $from + $i * 86400;
|
||||
$date = date('Y-m-d', $ts);
|
||||
$shortDate = date('m/d', $ts);
|
||||
$apptByDay[] = ['date' => $shortDate, 'count' => (int)($apptMap[$date] ?? 0)];
|
||||
$revByDay[] = ['date' => $shortDate, 'amount' => (int)($revMap[$date] ?? 0)];
|
||||
}
|
||||
|
||||
$statusRows = $conn->fetchAllAssociative(
|
||||
@@ -1729,11 +1732,27 @@ class AdminApiController extends BaseController
|
||||
GROUP BY s.id, s.name ORDER BY cnt DESC LIMIT 8'
|
||||
);
|
||||
|
||||
$subRows = $conn->fetchAllAssociative(
|
||||
'SELECT sp.name AS plan_name, COUNT(cs.id) AS cnt, COALESCE(SUM(p.amount_rials), 0) AS revenue
|
||||
FROM clinic_subscriptions cs
|
||||
JOIN subscription_plans sp ON sp.id = cs.plan_id
|
||||
LEFT JOIN payments p ON p.id = cs.payment_id AND p.status = :st
|
||||
WHERE cs.created_at BETWEEN :s AND :e
|
||||
GROUP BY sp.id, sp.name ORDER BY cnt DESC',
|
||||
['st' => 'received', 's' => $from, 'e' => $to]
|
||||
);
|
||||
|
||||
return $this->success([
|
||||
'appointments_30d' => $appt30d,
|
||||
'revenue_30d' => $rev30d,
|
||||
'appointment_status' => array_map(fn($r) => ['status' => $r['status'], 'count' => (int) $r['cnt']], $statusRows),
|
||||
'top_specialties' => array_map(fn($r) => ['name' => $r['name'], 'count' => (int) $r['cnt']], $specRows),
|
||||
'appointments_by_day' => $apptByDay,
|
||||
'revenue_by_day' => $revByDay,
|
||||
'appointment_status' => array_map(fn($r) => ['status' => $r['status'], 'count' => (int) $r['cnt']], $statusRows),
|
||||
'top_specialties' => array_map(fn($r) => ['name' => $r['name'], 'count' => (int) $r['cnt']], $specRows),
|
||||
'subscription_sales_by_plan' => array_map(fn($r) => [
|
||||
'plan' => $r['plan_name'],
|
||||
'count' => (int) $r['cnt'],
|
||||
'revenue' => (int) $r['revenue'],
|
||||
], $subRows),
|
||||
'period' => ['from' => $from, 'to' => $to],
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user