diff --git a/app/Livewire/Studio/Dashboard/Analysis.php b/app/Livewire/Studio/Dashboard/Analysis.php new file mode 100644 index 0000000..ef6ef86 --- /dev/null +++ b/app/Livewire/Studio/Dashboard/Analysis.php @@ -0,0 +1,225 @@ + '2025-11-11', 'visitors' => 267], + ['date' => '2025-11-10', 'visitors' => 259], + ['date' => '2025-11-09', 'visitors' => 269], + ]; + + public function mount() + { + $this->outlets = auth()->user()->outlets()->get()->pluck('name', 'id')->toArray(); + + $this->loadData(); + } + + public function updatedSelectedOutletIds() + { + $this->loadData(); + } + + protected function loadData() + { + $outletIds = $this->selectedOutletIds; + + $this->countingData = [ + [ + 'title' => 'Outlet', + 'value' => Outlet::count(), + ], + [ + 'title' => 'Pegawai', + 'value' => User::whereHas('employee')->when(! empty($outletIds), fn ($q) => $q->whereHas('outlets', fn ($q2) => $q2->whereIn('outlets.id', $outletIds)))->count(), + ], + [ + 'title' => 'Tier', + 'value' => Tier::count(), + ], + [ + 'title' => 'Voucher Aktif', + 'value' => Voucher::when(! empty($outletIds), fn ($q) => $q->whereHas('outlets', fn ($q2) => $q2->whereIn('outlets.id', $outletIds)))->active()->count(), + ], + [ + 'title' => 'Member', + 'value' => User::whereHas('customer')->when(! empty($outletIds), fn ($q) => $q->whereHas('outlets', fn ($q2) => $q2->whereIn('outlets.id', $outletIds)))->count(), + ], + [ + 'title' => 'Customer', + 'value' => Customer::whereDoesntHave('user')->count(), + ], + [ + 'title' => 'Kategori', + 'value' => Category::count(), + ], + [ + 'title' => 'Merek', + 'value' => Brand::count(), + ], + [ + 'title' => 'Parfum', + 'value' => Perfume::when(! empty($outletIds), fn ($q) => $q->whereHas('outlets', fn ($q2) => $q2->whereIn('outlets.id', $outletIds)))->count(), + ], + [ + 'title' => 'Produk', + 'value' => Product::when(! empty($outletIds), fn ($q) => $q->whereHas('outlets', fn ($q2) => $q2->whereIn('outlets.id', $outletIds)))->count(), + ], + [ + 'title' => 'Botol', + 'value' => Bottle::when(! empty($outletIds), fn ($q) => $q->whereHas('outlets', fn ($q2) => $q2->whereIn('outlets.id', $outletIds)))->count(), + ], + ]; + + $this->orders = [ + [ + 'title' => 'Total Order', + 'value' => currency(Order::when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))->count()), + ], + [ + 'title' => 'Pendapatan', + 'value' => currency(Order::when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))->sum('total'), 'Rp'), + ], + [ + 'title' => 'HPP', + 'value' => currency(Order::when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))->sum('cogs'), 'Rp'), + ], + [ + 'title' => 'Diskon', + 'value' => currency(Order::when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))->sum('discount'), 'Rp'), + ], + [ + 'title' => 'Voucher Terpakai', + 'value' => currency(Order::when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))->whereNotNull('voucher_id')->count()), + ], + [ + 'title' => 'Parfum Terjual', + 'value' => currency(OrderItem::where('orderable_type', Perfume::class)->whereHas('order', fn ($q) => $q->when(! empty($outletIds), fn ($q2) => $q2->whereIn('outlet_id', $outletIds)))->sum('quantity')).' ml', + ], + [ + 'title' => 'Produk Terjual', + 'value' => currency(OrderItem::where('orderable_type', Product::class)->whereHas('order', fn ($q) => $q->when(! empty($outletIds), fn ($q2) => $q2->whereIn('outlet_id', $outletIds)))->sum('quantity')).' pcs', + ], + [ + 'title' => 'Botol Terjual', + 'value' => currency(OrderItem::where('orderable_type', Bottle::class)->whereHas('order', fn ($q) => $q->when(! empty($outletIds), fn ($q2) => $q2->whereIn('outlet_id', $outletIds)))->sum('quantity')).' pcs', + ], + ]; + + $this->expenses = [ + [ + 'title' => 'Beban Toko', + 'value' => currency(Expense::when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))->sum('amount'), 'Rp'), + ], + [ + 'title' => 'Belanja Barang', + 'value' => currency(Purchase::when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))->sum('total'), 'Rp'), + ], + [ + 'title' => 'Penggajian', + 'value' => currency(Payroll::when(! empty($outletIds), fn ($q) => $q->whereHas('user.outlets', fn ($q2) => $q2->whereIn('outlets.id', $outletIds)))->sum('total_salary'), 'Rp'), + ], + ]; + + $this->topPerfumes = Perfume::with(['items' => function ($query) use ($outletIds) { + $query->whereNotNull('order_id') + ->whereHas('order', function ($q) use ($outletIds) { + $q->when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds)); + }); + }]) + ->select('id', 'name', 'sale_price') + ->get() + ->map(fn ($perfume) => [ + 'name' => $perfume->name, + 'sale_price' => currency($perfume->sale_price, 'Rp'), + 'total_sold' => currency($perfume->items->sum('quantity')), + 'total_sales' => currency($perfume->items->sum(fn ($item) => $item->order?->total ?? 0), 'Rp'), + ]) + ->sortByDesc('total_sales') + ->filter(fn ($product) => $product['total_sold'] > 0) + ->take(5) + ->toArray(); + + $this->topProducts = Product::with(['items' => function ($query) use ($outletIds) { + $query->whereNotNull('order_id') + ->whereHas('order', function ($q) use ($outletIds) { + $q->when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds)); + }); + }]) + ->select('id', 'name', 'sale_price') + ->get() + ->map(fn ($product) => [ + 'name' => $product->name, + 'sale_price' => currency($product->sale_price, 'Rp'), + 'total_sold' => currency($product->items->sum('quantity')), + 'total_sales' => currency($product->items->sum(fn ($item) => $item->order?->total ?? 0), 'Rp'), + ]) + ->sortByDesc('total_sales') + ->filter(fn ($product) => $product['total_sold'] > 0) + ->take(5) + ->toArray(); + + $this->topBottles = Bottle::with(['items' => function ($query) use ($outletIds) { + $query->whereNotNull('order_id') + ->whereHas('order', function ($q) use ($outletIds) { + $q->when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds)); + }); + }]) + ->select('id', 'name', 'sale_price') + ->get() + ->map(fn ($bottle) => [ + 'name' => $bottle->name, + 'sale_price' => currency($bottle->sale_price, 'Rp'), + 'total_sold' => currency($bottle->items->sum('quantity')), + 'total_sales' => currency($bottle->items->sum(fn ($item) => $item->order?->total ?? 0), 'Rp'), + ]) + ->sortByDesc('total_sales') + ->filter(fn ($product) => $product['total_sold'] > 0) + ->take(5) + ->toArray(); + } + + public function render() + { + return view('livewire.studio.dashboard.analysis', [ + 'pageTitle' => 'Analisa', + ]); + } +} diff --git a/app/Providers/ViewServiceProvider.php b/app/Providers/ViewServiceProvider.php index b104872..fe9d011 100644 --- a/app/Providers/ViewServiceProvider.php +++ b/app/Providers/ViewServiceProvider.php @@ -21,6 +21,13 @@ public function boot(): void 'match' => 'studio.dashboard.overview', 'can' => 'view overview', ], + [ + 'label' => 'Analisa', + 'icon' => 'chart-bar', + 'route' => 'studio.dashboard.analysis', + 'match' => 'studio.dashboard.analysis', + 'can' => 'view analysis', + ], ], ], [ diff --git a/database/seeders/RolePermissionSeeder.php b/database/seeders/RolePermissionSeeder.php index 8fe3c75..f4ba740 100644 --- a/database/seeders/RolePermissionSeeder.php +++ b/database/seeders/RolePermissionSeeder.php @@ -28,6 +28,7 @@ public function run(): void 'update password', 'view overview', + 'view analysis', 'view outlet', 'create outlet', diff --git a/resources/views/livewire/studio/dashboard/analysis.blade.php b/resources/views/livewire/studio/dashboard/analysis.blade.php new file mode 100644 index 0000000..4f7871a --- /dev/null +++ b/resources/views/livewire/studio/dashboard/analysis.blade.php @@ -0,0 +1,129 @@ + + {{ $pageTitle }} + + +
+
+ + @foreach ($outlets as $key => $value) + + @endforeach + +
+ + Counting Data +
+ @foreach ($countingData as $item) +
+ {{ $item['title'] }} + {{ $item['value'] }} +
+ @endforeach +
+ + Order +
+ @foreach ($orders as $order) +
+ {{ $order['title'] }} + {{ $order['value'] }} +
+ @endforeach +
+ + Pengeluaran +
+ @foreach ($expenses as $expense) +
+ {{ $expense['title'] }} + {{ $expense['value'] }} +
+ @endforeach +
+ +
+
+ Top 5 Parfum + + + + No + Nama + Harga + Terjual + Total + + + + @foreach ($topPerfumes as $topPerfume) + + {{ $loop->iteration }} + {{ $topPerfume['name'] }} + {{ $topPerfume['sale_price'] }} + {{ $topPerfume['total_sold'] }} + {{ $topPerfume['total_sales'] }} + + + @endforeach + + + +
+ +
+ Top 5 Produk + + + + No + Nama + Harga + Terjual + Total + + + + @foreach ($topProducts as $topProduct) + + {{ $loop->iteration }} + {{ $topProduct['name'] }} + {{ $topProduct['sale_price'] }} + {{ $topProduct['total_sold'] }} + {{ $topProduct['total_sales'] }} + + + @endforeach + + + +
+ +
+ Top 5 Botol + + + + No + Nama + Harga + Terjual + Total + + + + @foreach ($topBottles as $topBottle) + + {{ $loop->iteration }} + {{ $topBottle['name'] }} + {{ $topBottle['sale_price'] }} + {{ $topBottle['total_sold'] }} + {{ $topBottle['total_sales'] }} + + @endforeach + + + +
+
+
+
diff --git a/routes/pages/studio.php b/routes/pages/studio.php index 6726f04..6c30b62 100644 --- a/routes/pages/studio.php +++ b/routes/pages/studio.php @@ -15,6 +15,7 @@ use App\Livewire\Studio\Catalog\Product\Create as ProductCreate; use App\Livewire\Studio\Catalog\Product\Edit as ProductEdit; use App\Livewire\Studio\Catalog\Product\Index as ProductIndex; +use App\Livewire\Studio\Dashboard\Analysis as AnalysisComponent; use App\Livewire\Studio\Dashboard\Overview as OverviewComponent; use App\Livewire\Studio\Feature\ChooseUs as ChooseUsComponent; use App\Livewire\Studio\Feature\PerfumeFeature as PerfumeFeatureComponent; @@ -57,6 +58,7 @@ Route::prefix('dashboard')->name('studio.dashboard.')->group(function () { Route::get('/overview', OverviewComponent::class)->name('overview')->middleware('can:view overview'); + Route::get('/analysis', AnalysisComponent::class)->name('analysis')->middleware('can:view analysis'); }); Route::prefix('master')->name('studio.master.')->group(function () {