update
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user