feat(anaylis): page baru dan juga sudah ada isinya
This commit is contained in:
parent
a970a8e290
commit
11f6b70438
225
app/Livewire/Studio/Dashboard/Analysis.php
Normal file
225
app/Livewire/Studio/Dashboard/Analysis.php
Normal file
@ -0,0 +1,225 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Studio\Dashboard;
|
||||
|
||||
use App\Models\Bottle;
|
||||
use App\Models\Brand;
|
||||
use App\Models\Category;
|
||||
use App\Models\Customer;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Order;
|
||||
use App\Models\OrderItem;
|
||||
use App\Models\Outlet;
|
||||
use App\Models\Payroll;
|
||||
use App\Models\Perfume;
|
||||
use App\Models\Product;
|
||||
use App\Models\Purchase;
|
||||
use App\Models\Tier;
|
||||
use App\Models\User;
|
||||
use App\Models\Voucher;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Component;
|
||||
|
||||
#[Title('Analisa')]
|
||||
class Analysis extends Component
|
||||
{
|
||||
public array $outlets = [];
|
||||
|
||||
public array $selectedOutletIds = [];
|
||||
|
||||
public array $countingData = [];
|
||||
|
||||
public array $orders = [];
|
||||
|
||||
public array $expenses = [];
|
||||
|
||||
public array $topPerfumes = [];
|
||||
|
||||
public array $topProducts = [];
|
||||
|
||||
public array $topBottles = [];
|
||||
|
||||
public array $data = [
|
||||
['date' => '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',
|
||||
]);
|
||||
}
|
||||
}
|
||||
@ -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',
|
||||
],
|
||||
],
|
||||
],
|
||||
[
|
||||
|
||||
@ -28,6 +28,7 @@ public function run(): void
|
||||
'update password',
|
||||
|
||||
'view overview',
|
||||
'view analysis',
|
||||
|
||||
'view outlet',
|
||||
'create outlet',
|
||||
|
||||
129
resources/views/livewire/studio/dashboard/analysis.blade.php
Normal file
129
resources/views/livewire/studio/dashboard/analysis.blade.php
Normal file
@ -0,0 +1,129 @@
|
||||
<flux:main>
|
||||
<flux:heading size="xl" level="1" class="mb-6">{{ $pageTitle }}</flux:heading>
|
||||
<flux:separator />
|
||||
|
||||
<div class="mt-6">
|
||||
<div class="mb-6 grid grid-cols-1 lg:grid-cols-2 gap-4">
|
||||
<flux:checkbox.group wire:model.live="selectedOutletIds" variant="buttons">
|
||||
@foreach ($outlets as $key => $value)
|
||||
<flux:checkbox value="{{ $key }}" label="{{ $value }}" />
|
||||
@endforeach
|
||||
</flux:checkbox.group>
|
||||
</div>
|
||||
|
||||
<flux:heading>Counting Data</flux:heading>
|
||||
<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 xl:grid-cols-6 gap-3 mb-6 mt-2">
|
||||
@foreach ($countingData as $item)
|
||||
<div class="rounded-lg px-6 py-4 bg-zinc-200 dark:bg-zinc-700">
|
||||
<flux:subheading>{{ $item['title'] }}</flux:subheading>
|
||||
<flux:heading size="xl" class="mb-2">{{ $item['value'] }}</flux:heading>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<flux:heading>Order</flux:heading>
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 xl:grid-cols-4 gap-4 mb-6 mt-2">
|
||||
@foreach ($orders as $order)
|
||||
<div class="rounded-lg px-6 py-4 bg-zinc-200 dark:bg-zinc-700">
|
||||
<flux:subheading>{{ $order['title'] }}</flux:subheading>
|
||||
<flux:heading size="xl" class="mb-2">{{ $order['value'] }}</flux:heading>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<flux:heading>Pengeluaran</flux:heading>
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6 mt-2">
|
||||
@foreach ($expenses as $expense)
|
||||
<div class="rounded-lg px-6 py-4 bg-zinc-200 dark:bg-zinc-700">
|
||||
<flux:subheading>{{ $expense['title'] }}</flux:subheading>
|
||||
<flux:heading size="xl" class="mb-2">{{ $expense['value'] }}</flux:heading>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-1 md:grid-cols-3 gap-4 mb-6 mt-2">
|
||||
<div>
|
||||
<flux:heading>Top 5 Parfum</flux:heading>
|
||||
<flux:card class="mb-6 mt-2 bg-zinc-200 dark:bg-zinc-700">
|
||||
<flux:table>
|
||||
<flux:table.columns>
|
||||
<flux:table.column>No</flux:table.column>
|
||||
<flux:table.column>Nama</flux:table.column>
|
||||
<flux:table.column>Harga</flux:table.column>
|
||||
<flux:table.column>Terjual</flux:table.column>
|
||||
<flux:table.column>Total</flux:table.column>
|
||||
</flux:table.columns>
|
||||
|
||||
<flux:table.rows>
|
||||
@foreach ($topPerfumes as $topPerfume)
|
||||
<flux:table.row>
|
||||
<flux:table.cell>{{ $loop->iteration }}</flux:table.cell>
|
||||
<flux:table.cell>{{ $topPerfume['name'] }}</flux:table.cell>
|
||||
<flux:table.cell>{{ $topPerfume['sale_price'] }}</flux:table.cell>
|
||||
<flux:table.cell>{{ $topPerfume['total_sold'] }}</flux:table.cell>
|
||||
<flux:table.cell variant="strong">{{ $topPerfume['total_sales'] }}
|
||||
</flux:table.cell>
|
||||
</flux:table.row>
|
||||
@endforeach
|
||||
</flux:table.rows>
|
||||
</flux:table>
|
||||
</flux:card>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<flux:heading>Top 5 Produk</flux:heading>
|
||||
<flux:card class="mb-6 mt-2 bg-zinc-200 dark:bg-zinc-700">
|
||||
<flux:table>
|
||||
<flux:table.columns>
|
||||
<flux:table.column>No</flux:table.column>
|
||||
<flux:table.column>Nama</flux:table.column>
|
||||
<flux:table.column>Harga</flux:table.column>
|
||||
<flux:table.column>Terjual</flux:table.column>
|
||||
<flux:table.column>Total</flux:table.column>
|
||||
</flux:table.columns>
|
||||
|
||||
<flux:table.rows>
|
||||
@foreach ($topProducts as $topProduct)
|
||||
<flux:table.row>
|
||||
<flux:table.cell>{{ $loop->iteration }}</flux:table.cell>
|
||||
<flux:table.cell>{{ $topProduct['name'] }}</flux:table.cell>
|
||||
<flux:table.cell>{{ $topProduct['sale_price'] }}</flux:table.cell>
|
||||
<flux:table.cell>{{ $topProduct['total_sold'] }}</flux:table.cell>
|
||||
<flux:table.cell variant="strong">{{ $topProduct['total_sales'] }}
|
||||
</flux:table.cell>
|
||||
</flux:table.row>
|
||||
@endforeach
|
||||
</flux:table.rows>
|
||||
</flux:table>
|
||||
</flux:card>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<flux:heading>Top 5 Botol</flux:heading>
|
||||
<flux:card class="mb-6 mt-2 bg-zinc-200 dark:bg-zinc-700">
|
||||
<flux:table>
|
||||
<flux:table.columns>
|
||||
<flux:table.column>No</flux:table.column>
|
||||
<flux:table.column>Nama</flux:table.column>
|
||||
<flux:table.column>Harga</flux:table.column>
|
||||
<flux:table.column>Terjual</flux:table.column>
|
||||
<flux:table.column>Total</flux:table.column>
|
||||
</flux:table.columns>
|
||||
|
||||
<flux:table.rows>
|
||||
@foreach ($topBottles as $topBottle)
|
||||
<flux:table.row>
|
||||
<flux:table.cell>{{ $loop->iteration }}</flux:table.cell>
|
||||
<flux:table.cell>{{ $topBottle['name'] }}</flux:table.cell>
|
||||
<flux:table.cell>{{ $topBottle['sale_price'] }}</flux:table.cell>
|
||||
<flux:table.cell>{{ $topBottle['total_sold'] }}</flux:table.cell>
|
||||
<flux:table.cell variant="strong">{{ $topBottle['total_sales'] }}</flux:table.cell>
|
||||
</flux:table.row>
|
||||
@endforeach
|
||||
</flux:table.rows>
|
||||
</flux:table>
|
||||
</flux:card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</flux:main>
|
||||
@ -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 () {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user