This commit is contained in:
baodev0
2026-09-28 16:39:22 +07:00
parent 82080b5b84
commit b057d23d71
10 changed files with 265 additions and 29 deletions
@@ -53,28 +53,44 @@ class DashboardController extends Controller
return round((($current - $previous) / $previous) * 100, 2);
}
public function stats(): JsonResponse
/**
* @return array{0: Carbon, 1: Carbon, 2: Carbon, 3: Carbon}
*/
private function periodFromRequest(Request $request): array
{
$request->validate([
'from' => ['nullable', 'date_format:Y-m-d', 'required_with:to'],
'to' => ['nullable', 'date_format:Y-m-d', 'required_with:from', 'after_or_equal:from'],
]);
$start = $request->date('from', 'Y-m-d')?->startOfDay() ?? now()->startOfMonth();
$end = $request->date('to', 'Y-m-d')?->endOfDay() ?? now();
$days = (int) $start->diffInDays($end->copy()->startOfDay()) + 1;
return [$start, $end, $start->copy()->subDays($days), $start->copy()->subSecond()];
}
public function stats(Request $request): JsonResponse
{
$now = now();
$startOfMonth = $now->copy()->startOfMonth();
$startOfPrevMonth = $now->copy()->subMonthNoOverflow()->startOfMonth();
$endOfPrevMonth = $now->copy()->subMonthNoOverflow()->endOfMonth();
[$start, $end, $prevStart, $prevEnd] = $this->periodFromRequest($request);
// Customers
$totalCustomers = Customer::count();
$newThisMonth = Customer::where('created_at', '>=', $startOfMonth)->count();
$newPrevMonth = Customer::whereBetween('created_at', [$startOfPrevMonth, $endOfPrevMonth])->count();
$newInPeriod = Customer::whereBetween('created_at', [$start, $end])->count();
$newPrevPeriod = Customer::whereBetween('created_at', [$prevStart, $prevEnd])->count();
// Deposits (transactions completed, type ∈ DEPOSIT_TYPES)
$depositBase = Transaction::whereIn('type', self::DEPOSIT_TYPES)
->where('status', TransactionStatus::COMPLETED->value);
$totalDeposits = (float) (clone $depositBase)->sum('amount');
$depositsThisMonth = (float) (clone $depositBase)
->where('created_at', '>=', $startOfMonth)
$depositsInPeriod = (float) (clone $depositBase)
->whereBetween('created_at', [$start, $end])
->sum('amount');
$depositsPrevMonth = (float) (clone $depositBase)
->whereBetween('created_at', [$startOfPrevMonth, $endOfPrevMonth])
$depositsPrevPeriod = (float) (clone $depositBase)
->whereBetween('created_at', [$prevStart, $prevEnd])
->sum('amount');
// Số dư ví khách hàng (cột customers.total)
@@ -88,29 +104,29 @@ class DashboardController extends Controller
->count();
// Orders
$totalOrdersThisMonth = Order::where('created_at', '>=', $startOfMonth)->count();
$totalOrdersPrevMonth = Order::whereBetween('created_at', [$startOfPrevMonth, $endOfPrevMonth])->count();
$ordersInPeriod = Order::whereBetween('created_at', [$start, $end])->count();
$ordersPrevPeriod = Order::whereBetween('created_at', [$prevStart, $prevEnd])->count();
$pendingOrders = Order::where('status', OrderStatus::PENDING->value)->count();
return response()->json([
'error' => false,
'data' => [
'total_customers' => $totalCustomers,
'new_customers_this_month' => $newThisMonth,
'new_customers_delta_pct' => $this->deltaPct($newThisMonth, $newPrevMonth),
'new_customers_in_period' => $newInPeriod,
'new_customers_delta_pct' => $this->deltaPct($newInPeriod, $newPrevPeriod),
'total_deposits' => $totalDeposits,
'deposits_this_month' => $depositsThisMonth,
'deposits_delta_pct' => $this->deltaPct($depositsThisMonth, $depositsPrevMonth),
'deposits_in_period' => $depositsInPeriod,
'deposits_delta_pct' => $this->deltaPct($depositsInPeriod, $depositsPrevPeriod),
'total_balance' => $totalBalance,
'total_active_services' => $totalActiveServices,
'expiring_services_7d' => $expiring7d,
'total_orders_this_month' => $totalOrdersThisMonth,
'orders_in_period' => $ordersInPeriod,
'pending_orders' => $pendingOrders,
'orders_delta_pct' => $this->deltaPct($totalOrdersThisMonth, $totalOrdersPrevMonth),
'orders_delta_pct' => $this->deltaPct($ordersInPeriod, $ordersPrevPeriod),
],
]);
}
@@ -192,7 +208,7 @@ class DashboardController extends Controller
$limit = max(1, min(50, $limit));
$orders = Order::with([
'customer:id,fullname,email',
'customer:id,uuid,fullname,email',
'orderItems:id,order_id,product_id',
'orderItems.product:id,name',
'invoice:id,order_id,status',
@@ -203,16 +219,18 @@ class DashboardController extends Controller
$items = $orders->map(function (Order $o) {
$firstItem = $o->orderItems->first();
$serviceName = $firstItem?->product?->name ?? '—';
$product = $firstItem?->product;
$invoiceStatus = $o->invoice?->status ?? null;
return [
'id' => $o->id,
'code' => $o->order_num,
'customer_id' => $o->customer_id,
'customer_uuid' => $o->customer?->uuid,
'customer_name' => $o->customer->fullname ?? '—',
'customer_email' => $o->customer->email ?? '',
'service_name' => $serviceName,
'product_id' => $product?->id,
'service_name' => $product?->name ?? '—',
'amount' => (float) $o->total,
'payment_status' => $this->paymentStatusFromInvoice($invoiceStatus),
'status' => $this->orderStatusLabel((int) $o->status),
@@ -271,9 +289,7 @@ class DashboardController extends Controller
$limit = (int) $request->integer('limit', 5);
$limit = max(1, min(20, $limit));
$startOfMonth = now()->startOfMonth();
$startOfPrevMonth = now()->copy()->subMonthNoOverflow()->startOfMonth();
$endOfPrevMonth = now()->copy()->subMonthNoOverflow()->endOfMonth();
[$start, $end, $prevStart, $prevEnd] = $this->periodFromRequest($request);
// Top theo số service đang active của từng product
$rows = Service::query()
@@ -297,7 +313,7 @@ class DashboardController extends Controller
$soldCurrent = DB::table('order_items')
->join('orders', 'orders.id', '=', 'order_items.order_id')
->whereIn('order_items.product_id', $productIds)
->where('orders.created_at', '>=', $startOfMonth)
->whereBetween('orders.created_at', [$start, $end])
->selectRaw('order_items.product_id, COUNT(*) as sold, SUM(order_items.price * order_items.quantity) as revenue')
->groupBy('order_items.product_id')
->get()
@@ -306,7 +322,7 @@ class DashboardController extends Controller
$soldPrev = DB::table('order_items')
->join('orders', 'orders.id', '=', 'order_items.order_id')
->whereIn('order_items.product_id', $productIds)
->whereBetween('orders.created_at', [$startOfPrevMonth, $endOfPrevMonth])
->whereBetween('orders.created_at', [$prevStart, $prevEnd])
->selectRaw('order_items.product_id, SUM(order_items.price * order_items.quantity) as revenue')
->groupBy('order_items.product_id')
->get()
@@ -22,7 +22,11 @@ class StaffOnlineController extends Controller
*/
public function index(): JsonResponse
{
$users = User::whereHas('staffOnline')->with('staffOnline')->get();
$onlineSince = fn ($q) => $q->where('last_visited', '>=', time() - self::ONLINE_THRESHOLD_SECONDS);
$users = User::whereHas('staffOnline', $onlineSince)
->with(['staffOnline' => $onlineSince])
->get();
$online = $users->map(fn ($user) => [
'user_id' => $user->id,