feat(analitik) : membuat fitur analitik untuk barber

This commit is contained in:
Yoga Pangestu 2024-12-21 14:59:11 +07:00
parent 9dd4e63f4b
commit 48f3a53343
7 changed files with 350 additions and 137 deletions

View File

@ -24,14 +24,28 @@ public function index(): View
$totalPayroll = Payroll::query()->barbershop()->sum('total'); $totalPayroll = Payroll::query()->barbershop()->sum('total');
$totalShaving = Transaction::query()->barbershop()->count(); $totalShaving = Transaction::query()->barbershop()->count();
$topService = Service::query()->barbershop()->withCount('transactions')->get(); $topService = Service::query()
$paymentMethod = Transaction::select('payment_method', DB::raw('SUM(total) as total_amount'))
->barbershop() ->barbershop()
->groupBy('payment_method') ->withCount(['transactions' => function ($query) {
$query->when(auth()->user()->role_id === 5, function ($query) {
$query->where('user_id', auth()->id());
});
}])
->when(auth()->user()->role_id === 5, function ($query) {
$query->whereHas('transactions', function ($query) {
$query->where('user_id', auth()->id());
});
})
->get(); ->get();
$incomeByBarber = User::with('biography')->barbershop()->barber()->withSum('transactions', 'total')->get(); $paymentMethod = Transaction::query()
->select('payment_method', DB::raw('SUM(total) as total_amount'))
->barbershop()
->when(auth()->user()->role_id === 5, function ($query) {
$query->where('user_id', auth()->id());
})
->groupBy('payment_method')
->get();
$transactionByBarber = User::with('biography')->barbershop()->barber()->withCount('transactions')->get(); $transactionByBarber = User::with('biography')->barbershop()->barber()->withCount('transactions')->get();
@ -47,6 +61,43 @@ public function index(): View
$totalUser = User::query()->barbershop()->count(); $totalUser = User::query()->barbershop()->count();
$totalBarber = User::query()->barbershop()->barber()->count(); $totalBarber = User::query()->barbershop()->barber()->count();
$topCustomersByTotal = Transaction::groupBy('customer_id')
->select(
'customer_id',
DB::raw('SUM(total) as total_transaction'),
DB::raw('COUNT(*) as transaction_count')
)
->orderBy('total_transaction', 'desc')
->when(auth()->user()->role_id === 5, function ($query) {
return $query->where('user_id', auth()->id());
})
->limit(5)
->get();
$incomeByBarber = User::with('biography')
->barbershop()
->barber()
->withSum(['transactions' => function ($query) {
$query->when(auth()->user()->role_id === 5, function ($query) {
return $query->where('user_id', auth()->id());
});
}], 'total')
->orderBy('transactions_sum_total', 'desc')
->withCount(['transactions' => function ($query) {
$query->when(auth()->user()->role_id === 5, function ($query) {
return $query->where('user_id', auth()->id());
});
}]);
if (in_array(auth()->user()->role_id, [1, 2, 3])) {
$incomeByBarber = $incomeByBarber->get();
} elseif (auth()->user()->role_id === 5) {
$incomeByBarber = $incomeByBarber->find(auth()->id());
$incomeByBarber = (object) [
'transactions_sum_total' => formatToRupiah($incomeByBarber->transactions_sum_total),
'transactions_count' => $incomeByBarber->transactions_count,
];
}
return view('pages.admin.dashboard.analytic', [ return view('pages.admin.dashboard.analytic', [
'pageTitle' => 'Analitik', 'pageTitle' => 'Analitik',
'totalIncome' => $totalIncome, 'totalIncome' => $totalIncome,
@ -60,6 +111,7 @@ public function index(): View
'topBarber' => $topBarber, 'topBarber' => $topBarber,
'totalUser' => $totalUser, 'totalUser' => $totalUser,
'totalBarber' => $totalBarber, 'totalBarber' => $totalBarber,
'topCustomersByTotal' => $topCustomersByTotal,
]); ]);
} }
@ -106,8 +158,20 @@ public function filter(Request $request): JsonResponse
$query->whereBetween('created_at', [ $query->whereBetween('created_at', [
Carbon::parse($startDate)->startOfDay(), Carbon::parse($startDate)->startOfDay(),
Carbon::parse($endDate)->endOfDay(), Carbon::parse($endDate)->endOfDay(),
]); ])
->when(auth()->user()->role_id === 5, function ($query) {
$query->where('user_id', auth()->id());
});
}]) }])
->when(auth()->user()->role_id === 5, function ($query) {
$query->whereHas('transactions', function ($query) {
$query->where('user_id', auth()->id());
});
})
->whereBetween('created_at', [
Carbon::parse($startDate)->startOfDay(),
Carbon::parse($endDate)->endOfDay(),
])
->get(); ->get();
$paymentMethod = Transaction::select('payment_method', DB::raw('SUM(total) as total_amount')) $paymentMethod = Transaction::select('payment_method', DB::raw('SUM(total) as total_amount'))
@ -116,6 +180,9 @@ public function filter(Request $request): JsonResponse
Carbon::parse($startDate)->startOfDay(), Carbon::parse($startDate)->startOfDay(),
Carbon::parse($endDate)->endOfDay(), Carbon::parse($endDate)->endOfDay(),
]) ])
->when(auth()->user()->role_id === 5, function ($query) {
return $query->where('user_id', auth()->id());
})
->groupBy('payment_method') ->groupBy('payment_method')
->get(); ->get();
@ -177,6 +244,55 @@ public function filter(Request $request): JsonResponse
->barber() ->barber()
->count(); ->count();
$topCustomersByTotal = Transaction::groupBy('customer_id')
->select(
'customer_id',
DB::raw('SUM(total) as total_transaction'),
DB::raw('COUNT(*) as transaction_count')
)
->whereBetween('created_at', [
Carbon::parse($startDate)->startOfDay(),
Carbon::parse($endDate)->endOfDay(),
])
->orderBy('total_transaction', 'desc')
->when(auth()->user()->role_id === 5, function ($query) {
return $query->where('user_id', auth()->id());
})
->limit(5)
->get();
$renderCustomerByTotal = view('partials.admin._customer-by-total', compact('topCustomersByTotal'))->render();
$incomeByBarber = User::with('biography')
->barbershop()
->barber()
->withSum(['transactions' => function ($query) use ($startDate, $endDate) {
$query->whereBetween('created_at', [
Carbon::parse($startDate)->startOfDay(),
Carbon::parse($endDate)->endOfDay(),
])->when(auth()->user()->role_id === 5, function ($query) {
$query->where('user_id', auth()->id());
});
}], 'total')
->orderBy('transactions_sum_total', 'desc')
->withCount(['transactions' => function ($query) use ($startDate, $endDate) {
$query->whereBetween('created_at', [
Carbon::parse($startDate)->startOfDay(),
Carbon::parse($endDate)->endOfDay(),
])->when(auth()->user()->role_id === 5, function ($query) {
$query->where('user_id', auth()->id());
});
}]);
if (in_array(auth()->user()->role_id, [1, 2, 3])) {
$incomeByBarber = $incomeByBarber->get();
} elseif (auth()->user()->role_id === 5) {
$incomeByBarber = $incomeByBarber->find(auth()->id());
$incomeByBarber = [
'transactions_sum_total' => formatToRupiah($incomeByBarber->transactions_sum_total ?? 0),
'transactions_count' => $incomeByBarber->transactions_count,
];
}
return response()->json([ return response()->json([
'status' => true, 'status' => true,
'message' => 'Data Successfully Retrieved', 'message' => 'Data Successfully Retrieved',
@ -194,6 +310,7 @@ public function filter(Request $request): JsonResponse
'topBarber' => $topBarber, 'topBarber' => $topBarber,
'totalUser' => $totalUser, 'totalUser' => $totalUser,
'totalBarber' => $totalBarber, 'totalBarber' => $totalBarber,
'renderCustomerByTotal' => $renderCustomerByTotal,
], ],
], 200); ], 200);
} }

View File

@ -3,6 +3,7 @@ let incomeByBarberCtx = document.getElementById('incomeByBarberChart').getContex
let incomeByBarberLabels = incomeByBarber.map(incomeByBarber => incomeByBarber.biography.full_name); let incomeByBarberLabels = incomeByBarber.map(incomeByBarber => incomeByBarber.biography.full_name);
let incomeByBarberData = incomeByBarber.map(incomeByBarber => incomeByBarber.transactions_sum_total); let incomeByBarberData = incomeByBarber.map(incomeByBarber => incomeByBarber.transactions_sum_total);
console.log(incomeByBarberLabels, incomeByBarberData)
let incomeByBarberBackgroundColors = incomeByBarberData.map(() => getRandomColor()); let incomeByBarberBackgroundColors = incomeByBarberData.map(() => getRandomColor());
let incomeByBarberBorderColors = incomeByBarberBackgroundColors.map(color => color.replace('0.2', '1')); let incomeByBarberBorderColors = incomeByBarberBackgroundColors.map(color => color.replace('0.2', '1'));

View File

@ -30,15 +30,25 @@ $(document).ready(function () {
$("#top-barber-name").text(response.data.topBarber.biography.full_name || "-deleted-"); $("#top-barber-name").text(response.data.topBarber.biography.full_name || "-deleted-");
$("#top-barber-total").text(formatToRupiah(response.data.topBarber.transactions_sum_total)); $("#top-barber-total").text(formatToRupiah(response.data.topBarber.transactions_sum_total));
if (userRole === 5) {
$("#top-customers-by-total").empty();
$("#top-customers-by-total").html(response.data.renderCustomerByTotal);
$("#total-transaction").text(response.data.incomeByBarber.transactions_count);
$("#total-income").text(response.data.incomeByBarber.transactions_sum_total);
}
// income expense // income expense
let totalIncome = response.data.totalIncome; let totalIncome = response.data.totalIncome;
let totalExpense = response.data.totalExpense; let totalExpense = response.data.totalExpense;
if (userRole !== 5) {
incomeExpenseChart.data.datasets[0].data = [ incomeExpenseChart.data.datasets[0].data = [
totalIncome, totalIncome,
totalExpense, totalExpense,
]; ];
incomeExpenseChart.update(); incomeExpenseChart.update();
}
// top service // top service
let topService = response.data.topService; let topService = response.data.topService;
@ -70,6 +80,7 @@ $(document).ready(function () {
paymentMethodChart.update(); paymentMethodChart.update();
// income by barber // income by barber
if (userRole !== 5) {
let updatedIncomeByBarber = response.data.incomeByBarber; let updatedIncomeByBarber = response.data.incomeByBarber;
let updatedLabels = updatedIncomeByBarber.map(item => item.biography.full_name); let updatedLabels = updatedIncomeByBarber.map(item => item.biography.full_name);
let updatedData = updatedIncomeByBarber.map(item => item.transactions_sum_total); let updatedData = updatedIncomeByBarber.map(item => item.transactions_sum_total);
@ -77,8 +88,10 @@ $(document).ready(function () {
incomeByBarberChart.data.labels = updatedLabels; incomeByBarberChart.data.labels = updatedLabels;
incomeByBarberChart.data.datasets[0].data = updatedData; incomeByBarberChart.data.datasets[0].data = updatedData;
incomeByBarberChart.update(); incomeByBarberChart.update();
}
// transaction by barber // transaction by barber
if (userRole !== 5) {
let transactionByBarber = response.data.transactionByBarber; let transactionByBarber = response.data.transactionByBarber;
let transactionByBarberLabels = transactionByBarber.map(item => item.biography.full_name); let transactionByBarberLabels = transactionByBarber.map(item => item.biography.full_name);
let transactionByBarberData = transactionByBarber.map(item => item.transactions_count); let transactionByBarberData = transactionByBarber.map(item => item.transactions_count);
@ -86,6 +99,7 @@ $(document).ready(function () {
transactionByBarberChart.data.labels = transactionByBarberLabels; transactionByBarberChart.data.labels = transactionByBarberLabels;
transactionByBarberChart.data.datasets[0].data = transactionByBarberData; transactionByBarberChart.data.datasets[0].data = transactionByBarberData;
transactionByBarberChart.update(); transactionByBarberChart.update();
}
}, },
error: function (xhr) { }, error: function (xhr) { },
}); });

View File

@ -19,6 +19,7 @@
</div> </div>
</div> </div>
<div class="row widget-grid"> <div class="row widget-grid">
@if (auth()->user()->role_id !== 5)
<div class="col-12 col-md-6 col-lg-4"> <div class="col-12 col-md-6 col-lg-4">
<div class="card small-widget"> <div class="card small-widget">
<div class="card-body primary"> <div class="card-body primary">
@ -104,7 +105,8 @@ class="feather feather-user-minus font-success">
<div class="chart-right"> <div class="chart-right">
<div class="card-body p-0"> <div class="card-body p-0">
<p>Keuntungan : <span <p>Keuntungan : <span
id="net-income">{{ formatToRupiah($totalIncome - $totalExpense) }}</span></p> id="net-income">{{ formatToRupiah($totalIncome - $totalExpense) }}</span>
</p>
<div class="current-sale-container"> <div class="current-sale-container">
<canvas id="incomeExpenseChart" width="400" height="400"></canvas> <canvas id="incomeExpenseChart" width="400" height="400"></canvas>
</div> </div>
@ -114,6 +116,7 @@ class="feather feather-user-minus font-success">
</div> </div>
</div> </div>
</div> </div>
@endif
<div class="col-12 col-md-6 col-lg-4"> <div class="col-12 col-md-6 col-lg-4">
<div class="card"> <div class="card">
<div class="card-header card-no-border"> <div class="card-header card-no-border">
@ -150,6 +153,7 @@ class="feather feather-user-minus font-success">
</div> </div>
</div> </div>
</div> </div>
@if (auth()->user()->role_id !== 5)
<div class="col-12 col-md-6 col-lg-4"> <div class="col-12 col-md-6 col-lg-4">
<div class="card"> <div class="card">
<div class="card-header card-no-border"> <div class="card-header card-no-border">
@ -186,6 +190,70 @@ class="feather feather-user-minus font-success">
</div> </div>
</div> </div>
</div> </div>
@else
<div class="col-12 col-md-6 col-lg-4">
<div class="card">
<div class="card-header card-no-border">
<div class="header-top">
<h5 class="m-0">Top Pelanggan</h5>
</div>
</div>
<div class="card-body pt-0">
<div class="table-responsive custom-scrollbar">
<table class="table table-dashed">
<thead>
<tr>
<th>Nama</th>
<th>Kontak</th>
<th>Jumlah Transaksi</th>
<th>Total Transakski</th>
</tr>
</thead>
<tbody id="top-customers-by-total">
@foreach ($topCustomersByTotal as $item)
<tr>
<td>{{ $item->customer->name ?? '--deleted--' }}</td>
<td>{{ $item->customer->contact_number ?? '-' }}</td>
<td>{{ $item->transaction_count }}</td>
<td>{{ formatToRupiah($item->total_transaction) }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="col-12 col-md-6 col-lg-4">
<div class="card">
<div class="card-header card-no-border">
<div class="header-top">
<h5 class="m-0">Pendapatan Barber</h5>
</div>
</div>
<div class="card-body pt-0">
<div class="table-responsive custom-scrollbar">
<table class="table table-dashed">
<thead>
<tr>
<th>Jumlah Transaksi</th>
<th>Total Transakski</th>
</tr>
</thead>
<tbody>
<tr>
<td id="total-transaction">{{ $incomeByBarber->transactions_count }}</td>
<td id="total-income">
{{ $incomeByBarber->transactions_sum_total }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
@endif
</div> </div>
</div> </div>
@endsection @endsection
@ -196,12 +264,16 @@ class="feather feather-user-minus font-success">
@section('afterScripts') @section('afterScripts')
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.6/dist/chart.umd.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.6/dist/chart.umd.min.js"></script>
<script> <script>
let userRole = {!! auth()->user()->role_id !!}
const totalIncome = @json($totalIncome); const totalIncome = @json($totalIncome);
const totalExpense = @json($totalExpense); const totalExpense = @json($totalExpense);
const topService = @json($topService); const topService = @json($topService);
const paymentMethod = @json($paymentMethod); const paymentMethod = @json($paymentMethod);
const incomeByBarber = @json($incomeByBarber); const incomeByBarber = @json($incomeByBarber);
const transactionByBarber = @json($transactionByBarber); const transactionByBarber = @json($transactionByBarber);
const topCustomersByTotal = @json($topCustomersByTotal);
const topBarber = @json($topBarber);
</script> </script>
<script src="{{ asset('assets/admin/js/random-color.js?ver=1.0.0') }}"></script> <script src="{{ asset('assets/admin/js/random-color.js?ver=1.0.0') }}"></script>
<script src="{{ asset('assets/admin/js/chart/finance.js?ver=1.0.0') }}"></script> <script src="{{ asset('assets/admin/js/chart/finance.js?ver=1.0.0') }}"></script>

View File

@ -0,0 +1,8 @@
@foreach ($topCustomersByTotal as $item)
<tr>
<td>{{ $item->customer->name ?? '--deleted--' }}</td>
<td>{{ $item->customer->contact_number ?? '-' }}</td>
<td>{{ $item->transaction_count }}</td>
<td>{{ formatToRupiah($item->total_transaction) }}</td>
</tr>
@endforeach

View File

@ -49,7 +49,7 @@
<span>Ringkasan</span> <span>Ringkasan</span>
</a> </a>
</li> </li>
@if (in_array(auth()->user()->role_id, [1, 2, 3])) @if (auth()->user()->role_id !== 4)
<li class="sidebar-list"> <li class="sidebar-list">
<i class="fa fa-thumb-tack"></i> <i class="fa fa-thumb-tack"></i>
<a class="sidebar-link sidebar-title link-nav" href="{{ route('dashboard.analytic') }}"> <a class="sidebar-link sidebar-title link-nav" href="{{ route('dashboard.analytic') }}">
@ -57,6 +57,8 @@
<span>Analitik</span> <span>Analitik</span>
</a> </a>
</li> </li>
@endif
@if (in_array(auth()->user()->role_id, [1, 2, 3]))
<li class="sidebar-main-title"> <li class="sidebar-main-title">
<div> <div>
<h6>Master</h6> <h6>Master</h6>

View File

@ -19,7 +19,6 @@
use App\Http\Controllers\Client\HomepageController; use App\Http\Controllers\Client\HomepageController;
use App\Models\Transaction; use App\Models\Transaction;
use Illuminate\Support\Facades\Route; use Illuminate\Support\Facades\Route;
use Psy\Readline\Transient;
Route::prefix('auth')->group(function () { Route::prefix('auth')->group(function () {
Route::get('login', [LoginController::class, 'index'])->name('login'); Route::get('login', [LoginController::class, 'index'])->name('login');
@ -32,8 +31,8 @@
'prefix' => 'dashboard', 'prefix' => 'dashboard',
], function () { ], function () {
Route::get('overview', [OverviewController::class, 'index'])->name('dashboard.overview'); Route::get('overview', [OverviewController::class, 'index'])->name('dashboard.overview');
Route::get('analytic', [AnalyticController::class, 'index'])->name('dashboard.analytic')->middleware('check_role:1,2,3'); Route::get('analytic', [AnalyticController::class, 'index'])->name('dashboard.analytic')->middleware('check_role:1,2,3,5');
Route::get('analytic/filter', [AnalyticController::class, 'filter'])->name('dashboard.analytic.filter')->middleware('check_role:1,2,3'); Route::get('analytic/filter', [AnalyticController::class, 'filter'])->name('dashboard.analytic.filter')->middleware('check_role:1,2,3,5');
Route::get('change-barbershop/{barbershop}', [OverviewController::class, 'changeBarbershop'])->name('dashboard.changeBarbershop'); Route::get('change-barbershop/{barbershop}', [OverviewController::class, 'changeBarbershop'])->name('dashboard.changeBarbershop');
@ -181,6 +180,6 @@
Route::get('/print-page', function () { Route::get('/print-page', function () {
return view('print-page', [ return view('print-page', [
'transaction' => Transaction::first() 'transaction' => Transaction::first(),
]); // Ganti dengan nama view halaman cetak Anda ]); // Ganti dengan nama view halaman cetak Anda
})->name('print.page'); })->name('print.page');