From a9759f92c1238d9bc0248d9caa60133c8e70736d Mon Sep 17 00:00:00 2001 From: Yoga Pangestu Date: Sat, 3 Jan 2026 15:00:46 +0700 Subject: [PATCH] feat: Refactor dashboard data to public properties, update media handling, and fix analysis period logic. --- app/Livewire/Studio/Dashboard/Analysis.php | 33 ++++++++++++++----- app/Livewire/Studio/Dashboard/Overview.php | 16 +++++---- app/Livewire/Studio/Finance/Expense.php | 3 +- app/Traits/Media/WithMediaHandler.php | 6 +++- .../Feature/Studio/Dashboard/AnalysisTest.php | 6 ++-- .../Feature/Studio/Dashboard/OverviewTest.php | 14 ++++---- 6 files changed, 49 insertions(+), 29 deletions(-) diff --git a/app/Livewire/Studio/Dashboard/Analysis.php b/app/Livewire/Studio/Dashboard/Analysis.php index 89008bd..91682de 100644 --- a/app/Livewire/Studio/Dashboard/Analysis.php +++ b/app/Livewire/Studio/Dashboard/Analysis.php @@ -69,6 +69,18 @@ class Analysis extends Component public array $voucherUsageTrendChart = []; + public array $countingData = []; + + public array $orders = []; + + public array $expenses = []; + + public array $topPerfumes = []; + + public array $topProducts = []; + + public array $topBottles = []; + public function mount(): void { $this->outlets = auth()->user()->outlets()->get()->pluck('name', 'id')->toArray(); @@ -83,7 +95,7 @@ public function render(): View $range = $this->range; $dateQuery = match ($period) { - '7' => now()->subDays(1), + '7' => now()->subDays(7), '30' => now()->subDays(30), '90' => now()->subDays(90), '365' => now()->subDays(365), @@ -397,8 +409,9 @@ public function render(): View ]; // 2. busiest hour + $hourFunc = DB::getDriverName() === 'sqlite' ? "CAST(strftime('%H', created_at) AS INTEGER)" : 'HOUR(created_at)'; $hourlySales = (clone $orderQuery) - ->select(DB::raw('HOUR(created_at) as hour'), DB::raw('count(*) as count')) + ->select(DB::raw("$hourFunc as hour"), DB::raw('count(*) as count')) ->groupBy('hour') ->orderBy('hour') ->get() @@ -484,8 +497,9 @@ public function render(): View ]; // 7. Day of Week Sales + $dayOfWeekFunc = DB::getDriverName() === 'sqlite' ? "(CAST(strftime('%w', created_at) AS INTEGER) + 1)" : 'DAYOFWEEK(created_at)'; $dayOfWeekSales = (clone $orderQuery) - ->select(DB::raw('DAYOFWEEK(created_at) as day'), DB::raw('SUM(total) as revenue')) + ->select(DB::raw("$dayOfWeekFunc as day"), DB::raw('SUM(total) as revenue')) ->groupBy('day') ->get() ->keyBy('day'); @@ -564,14 +578,15 @@ public function render(): View 'data' => $trendDates->map(fn ($d) => (int) ($vouchersTrend->get($d)?->count ?? 0))->toArray(), ]; + $this->countingData = $countingData; + $this->orders = array_values(array_filter($orders)); + $this->expenses = array_values(array_filter($expenses)); + $this->topPerfumes = $topPerfumes; + $this->topProducts = $topProducts; + $this->topBottles = $topBottles; + return view('livewire.studio.dashboard.analysis', [ 'pageTitle' => 'Analisa', - 'countingData' => $countingData, - 'orders' => $orders, - 'expenses' => $expenses, - 'topPerfumes' => $topPerfumes, - 'topProducts' => $topProducts, - 'topBottles' => $topBottles, ]); } } diff --git a/app/Livewire/Studio/Dashboard/Overview.php b/app/Livewire/Studio/Dashboard/Overview.php index e9118a5..bf67707 100644 --- a/app/Livewire/Studio/Dashboard/Overview.php +++ b/app/Livewire/Studio/Dashboard/Overview.php @@ -40,6 +40,8 @@ class Overview extends Component public array $todayDiscountChart = []; + public array $stats = []; + public function mount(): void { $this->outlets = auth()->user()->outlets()->get()->pluck('name', 'id')->toArray(); @@ -171,15 +173,17 @@ public function render(): View // --- Chart Data Logic --- // 1. Hourly Sales Comparison (Today vs Yesterday) + $hourFunc = DB::getDriverName() === 'sqlite' ? "CAST(strftime('%H', created_at) AS INTEGER)" : 'HOUR(created_at)'; + $todayHourly = Order::whereDate('created_at', now()) ->when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds)) - ->select(DB::raw('HOUR(created_at) as hour'), DB::raw('count(*) as count')) + ->select(DB::raw("$hourFunc as hour"), DB::raw('count(*) as count')) ->groupBy('hour') ->get()->keyBy('hour'); $yesterdayHourly = Order::whereDate('created_at', now()->yesterday()) ->when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds)) - ->select(DB::raw('HOUR(created_at) as hour'), DB::raw('count(*) as count')) + ->select(DB::raw("$hourFunc as hour"), DB::raw('count(*) as count')) ->groupBy('hour') ->get()->keyBy('hour'); @@ -282,7 +286,7 @@ public function render(): View 'data' => [$withDiscount, $noDiscount], ]; - $stats = array_filter([ + $this->stats = array_values(array_filter([ [ 'title' => 'Average Order Value (AOV)', 'value' => formatCurrencyNumber($todayAov, 'Rp'), @@ -418,10 +422,8 @@ public function render(): View : '∞%', 'trendUp' => ($todayTopPerfume?->total_sold ?? 0) > ($yesterdayTopPerfume?->total_sold ?? 0), ], - ]); + ])); - return view('livewire.studio.dashboard.overview', [ - 'stats' => $stats, - ]); + return view('livewire.studio.dashboard.overview'); } } diff --git a/app/Livewire/Studio/Finance/Expense.php b/app/Livewire/Studio/Finance/Expense.php index 15b743f..e6186b5 100644 --- a/app/Livewire/Studio/Finance/Expense.php +++ b/app/Livewire/Studio/Finance/Expense.php @@ -16,12 +16,11 @@ use Livewire\Attributes\On; use Livewire\Attributes\Title; use Livewire\Component; -use Livewire\WithFileUploads; #[Title('Pengeluaran')] class Expense extends Component { - use WithAuthorization, WithCloseModal, WithConfirmation, WithFileUploads, WithSubscribeNotification, WithToast, WithUpdatedData; + use WithAuthorization, WithCloseModal, WithConfirmation, WithSubscribeNotification, WithToast, WithUpdatedData; public ExpenseForm $form; diff --git a/app/Traits/Media/WithMediaHandler.php b/app/Traits/Media/WithMediaHandler.php index 8f103e5..b3641b1 100644 --- a/app/Traits/Media/WithMediaHandler.php +++ b/app/Traits/Media/WithMediaHandler.php @@ -39,10 +39,14 @@ protected function syncMedia(array $newMedia, $model, string $collectionName): v protected function uploadMedia(array $mediaArray, $model, string $collectionName): void { foreach ($mediaArray as $file) { - if (isset($file['path'])) { + if (is_array($file) && isset($file['path'])) { $model->addMedia($file['path']) ->preservingOriginal() ->toMediaCollection($collectionName); + } elseif ($file instanceof \Illuminate\Http\UploadedFile) { + $model->addMedia($file->getRealPath()) + ->preservingOriginal() + ->toMediaCollection($collectionName); } } } diff --git a/tests/Feature/Studio/Dashboard/AnalysisTest.php b/tests/Feature/Studio/Dashboard/AnalysisTest.php index ad92324..482dffb 100644 --- a/tests/Feature/Studio/Dashboard/AnalysisTest.php +++ b/tests/Feature/Studio/Dashboard/AnalysisTest.php @@ -66,10 +66,10 @@ Livewire::actingAs($this->user) ->test(Analysis::class) // All period (default) - ->assertSet('orders.1.value', 'Rp150.000') + ->assertSet('orders.5.value', 'Rp150.000') // Last 7 days (only shows today's order) ->set('period', '7') - ->assertSet('orders.1.value', 'Rp50.000'); + ->assertSet('orders.5.value', 'Rp50.000'); }); it('filters data by custom date range', function () { @@ -89,7 +89,7 @@ ->test(Analysis::class) ->set('range.start', '2024-01-01') ->set('range.end', '2024-01-31') - ->assertSet('orders.1.value', 'Rp200.000'); + ->assertSet('orders.5.value', 'Rp200.000'); }); it('shows top performers correctly with aggregation', function () { diff --git a/tests/Feature/Studio/Dashboard/OverviewTest.php b/tests/Feature/Studio/Dashboard/OverviewTest.php index e1d7589..f056b82 100644 --- a/tests/Feature/Studio/Dashboard/OverviewTest.php +++ b/tests/Feature/Studio/Dashboard/OverviewTest.php @@ -48,10 +48,10 @@ Livewire::actingAs($this->user) ->test(Overview::class) - ->assertSet('stats.0.value', 2) // Total Order Today - ->assertSet('stats.0.previous', 1) // Total Order Yesterday - ->assertSet('stats.1.value', 'Rp200.000') // Pendapatan Today (2 * 100k) - ->assertSet('stats.1.previous', 'Rp80.000'); // Pendapatan Yesterday (1 * 80k) + ->assertSet('stats.2.value', 2) // Total Order Today + ->assertSet('stats.2.previous', 1) // Total Order Yesterday + ->assertSet('stats.3.value', 'Rp200.000') // Pendapatan Today (2 * 100k) + ->assertSet('stats.3.previous', 'Rp80.000'); // Pendapatan Yesterday (1 * 80k) }); it('filters stats by selected outlets', function () { @@ -66,13 +66,13 @@ Livewire::actingAs($this->user) ->test(Overview::class) // No filter (shows both) - ->assertSet('stats.1.value', 'Rp150.000') + ->assertSet('stats.3.value', 'Rp150.000') // Filter by main outlet only ->set('selectedOutletIds', [$this->outlet->id]) - ->assertSet('stats.1.value', 'Rp100.000') + ->assertSet('stats.3.value', 'Rp100.000') // Filter by another outlet only ->set('selectedOutletIds', [$anotherOutlet->id]) - ->assertSet('stats.1.value', 'Rp50.000'); + ->assertSet('stats.3.value', 'Rp50.000'); }); it('shows top perfume correctly', function () {