feat: Refactor dashboard data to public properties, update media handling, and fix analysis period logic.

This commit is contained in:
Yoga Pangestu 2026-01-03 15:00:46 +07:00
parent acfa2977b3
commit a9759f92c1
6 changed files with 49 additions and 29 deletions

View File

@ -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,
]);
}
}

View File

@ -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');
}
}

View File

@ -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;

View File

@ -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);
}
}
}

View File

@ -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 () {

View File

@ -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 () {