feat(analytic): membuat halaman analitik
This commit is contained in:
parent
5f8b09f9d1
commit
423856d740
190
app/Http/Controllers/Admin/Dashboard/AnalyticController.php
Normal file
190
app/Http/Controllers/Admin/Dashboard/AnalyticController.php
Normal file
@ -0,0 +1,190 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin\Dashboard;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Expense;
|
||||
use App\Models\Inventory;
|
||||
use App\Models\Service;
|
||||
use App\Models\Transaction;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class AnalyticController extends Controller
|
||||
{
|
||||
public function index(): View
|
||||
{
|
||||
$totalIncome = Transaction::query()->barbershop()->sum('total');
|
||||
$totalExpense = Expense::query()->barbershop()->sum('amount');
|
||||
$totalShaving = Transaction::query()->barbershop()->count();
|
||||
|
||||
$topService = Service::query()->barbershop()->withCount('transactions')->get();
|
||||
|
||||
$paymentMethod = Transaction::select('payment_method', DB::raw('SUM(total) as total_amount'))
|
||||
->barbershop()
|
||||
->groupBy('payment_method')
|
||||
->get();
|
||||
|
||||
$incomeByBarber = User::with('biography')->barbershop()->barber()->withSum('transactions', 'total')->get();
|
||||
|
||||
$transactionByBarber = User::with('biography')->barbershop()->barber()->withCount('transactions')->get();
|
||||
|
||||
$totalAsset = Inventory::query()->barbershop()->sum(DB::raw('quantity * price'));
|
||||
|
||||
$topBarber = User::with('biography')
|
||||
->barbershop()
|
||||
->barber()
|
||||
->withSum('transactions', 'total')
|
||||
->orderBy('transactions_sum_total', 'desc')
|
||||
->first();
|
||||
|
||||
$totalUser = User::query()->barbershop()->count();
|
||||
$totalBarber = User::query()->barbershop()->barber()->count();
|
||||
|
||||
return view('pages.admin.dashboard.analytic', [
|
||||
'pageTitle' => 'Analitik',
|
||||
'totalIncome' => $totalIncome,
|
||||
'totalExpense' => $totalExpense,
|
||||
'totalShaving' => $totalShaving,
|
||||
'topService' => $topService,
|
||||
'paymentMethod' => $paymentMethod,
|
||||
'incomeByBarber' => $incomeByBarber,
|
||||
'transactionByBarber' => $transactionByBarber,
|
||||
'totalAsset' => $totalAsset,
|
||||
'topBarber' => $topBarber,
|
||||
'totalUser' => $totalUser,
|
||||
'totalBarber' => $totalBarber,
|
||||
]);
|
||||
}
|
||||
|
||||
public function filter(Request $request): JsonResponse
|
||||
{
|
||||
$startDate = $request->start_date;
|
||||
$endDate = $request->end_date;
|
||||
|
||||
$totalIncome = Transaction::query()
|
||||
->barbershop()
|
||||
->whereBetween('created_at', [
|
||||
Carbon::parse($startDate)->startOfDay(),
|
||||
Carbon::parse($endDate)->endOfDay(),
|
||||
])
|
||||
->sum('total');
|
||||
|
||||
$totalExpense = Expense::query()
|
||||
->barbershop()
|
||||
->whereBetween('created_at', [
|
||||
Carbon::parse($startDate)->startOfDay(),
|
||||
Carbon::parse($endDate)->endOfDay(),
|
||||
])
|
||||
->sum('amount');
|
||||
|
||||
$totalShaving = Transaction::query()
|
||||
->barbershop()
|
||||
->whereBetween('created_at', [
|
||||
Carbon::parse($startDate)->startOfDay(),
|
||||
Carbon::parse($endDate)->endOfDay(),
|
||||
])
|
||||
->count();
|
||||
|
||||
$topService = Service::query()
|
||||
->barbershop()
|
||||
->withCount(['transactions' => function ($query) use ($startDate, $endDate) {
|
||||
$query->whereBetween('created_at', [
|
||||
Carbon::parse($startDate)->startOfDay(),
|
||||
Carbon::parse($endDate)->endOfDay(),
|
||||
]);
|
||||
}])
|
||||
->get();
|
||||
|
||||
$paymentMethod = Transaction::select('payment_method', DB::raw('SUM(total) as total_amount'))
|
||||
->barbershop()
|
||||
->whereBetween('created_at', [
|
||||
Carbon::parse($startDate)->startOfDay(),
|
||||
Carbon::parse($endDate)->endOfDay(),
|
||||
])
|
||||
->groupBy('payment_method')
|
||||
->get();
|
||||
|
||||
$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(),
|
||||
]);
|
||||
}], 'total')
|
||||
->get();
|
||||
|
||||
$transactionByBarber = User::with('biography')
|
||||
->barbershop()
|
||||
->barber()
|
||||
->withCount(['transactions' => function ($query) use ($startDate, $endDate) {
|
||||
$query->whereBetween('created_at', [
|
||||
Carbon::parse($startDate)->startOfDay(),
|
||||
Carbon::parse($endDate)->endOfDay(),
|
||||
]);
|
||||
}])
|
||||
->get();
|
||||
|
||||
$totalAsset = Inventory::query()
|
||||
->barbershop()
|
||||
->whereBetween('created_at', [
|
||||
Carbon::parse($startDate)->startOfDay(),
|
||||
Carbon::parse($endDate)->endOfDay(),
|
||||
])
|
||||
->sum(DB::raw('quantity * price'));
|
||||
|
||||
$topBarber = User::with('biography')
|
||||
->barbershop()
|
||||
->barber()
|
||||
->withSum(['transactions' => function ($query) use ($startDate, $endDate) {
|
||||
$query->whereBetween('created_at', [
|
||||
Carbon::parse($startDate)->startOfDay(),
|
||||
Carbon::parse($endDate)->endOfDay(),
|
||||
]);
|
||||
}], 'total')
|
||||
->orderBy('transactions_sum_total', 'desc')
|
||||
->first();
|
||||
|
||||
$totalUser = User::query()
|
||||
->barbershop()
|
||||
->whereBetween('created_at', [
|
||||
Carbon::parse($startDate)->startOfDay(),
|
||||
Carbon::parse($endDate)->endOfDay(),
|
||||
])
|
||||
->count();
|
||||
$totalBarber = User::query()
|
||||
->barbershop()
|
||||
->whereBetween('created_at', [
|
||||
Carbon::parse($startDate)->startOfDay(),
|
||||
Carbon::parse($endDate)->endOfDay(),
|
||||
])
|
||||
->barber()
|
||||
->count();
|
||||
|
||||
return response()->json([
|
||||
'status' => true,
|
||||
'message' => 'Data Successfully Retrieved',
|
||||
'data' => [
|
||||
'startDate' => $startDate,
|
||||
'endDate' => $endDate,
|
||||
'totalIncome' => $totalIncome,
|
||||
'totalExpense' => $totalExpense,
|
||||
'totalShaving' => $totalShaving,
|
||||
'topService' => $topService,
|
||||
'paymentMethod' => $paymentMethod,
|
||||
'incomeByBarber' => $incomeByBarber,
|
||||
'transactionByBarber' => $transactionByBarber,
|
||||
'totalAsset' => $totalAsset,
|
||||
'topBarber' => $topBarber,
|
||||
'totalUser' => $totalUser,
|
||||
'totalBarber' => $totalBarber,
|
||||
],
|
||||
], 200);
|
||||
}
|
||||
}
|
||||
42
public/assets/admin/js/chart/finance.js
Normal file
42
public/assets/admin/js/chart/finance.js
Normal file
@ -0,0 +1,42 @@
|
||||
let incomeExpenseCtx = document.getElementById('incomeExpenseChart').getContext('2d');
|
||||
let incomeExpenseChart = new Chart(incomeExpenseCtx, {
|
||||
type: 'pie',
|
||||
data: {
|
||||
labels: ['Pendapatan', 'Pengeluaran'],
|
||||
datasets: [{
|
||||
label: 'Pendapatan vs Pengeluaran',
|
||||
data: [totalIncome, totalExpense],
|
||||
backgroundColor: [
|
||||
'rgba(75, 192, 192, 0.2)',
|
||||
'rgba(255, 99, 132, 0.2)'
|
||||
],
|
||||
borderColor: [
|
||||
'rgb(75, 192, 192)',
|
||||
'rgb(255, 99, 132)'
|
||||
],
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function (tooltipItem) {
|
||||
let rupiahFormat = new Intl.NumberFormat('id-ID', {
|
||||
style: 'currency',
|
||||
currency: 'IDR',
|
||||
maximumFractionDigits: 0,
|
||||
useGrouping: true
|
||||
}).format(tooltipItem.raw);
|
||||
|
||||
return tooltipItem.label + ': ' + rupiahFormat.replace('Rp ', 'Rp').replace(/\s/g, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
44
public/assets/admin/js/chart/income-by-barber.js
Normal file
44
public/assets/admin/js/chart/income-by-barber.js
Normal file
@ -0,0 +1,44 @@
|
||||
|
||||
let incomeByBarberCtx = document.getElementById('incomeByBarberChart').getContext('2d');
|
||||
|
||||
let incomeByBarberLabels = incomeByBarber.map(incomeByBarber => incomeByBarber.biography.full_name);
|
||||
let incomeByBarberData = incomeByBarber.map(incomeByBarber => incomeByBarber.transactions_sum_total);
|
||||
|
||||
let incomeByBarberBackgroundColors = incomeByBarberData.map(() => getRandomColor());
|
||||
let incomeByBarberBorderColors = incomeByBarberBackgroundColors.map(color => color.replace('0.2', '1'));
|
||||
|
||||
let incomeByBarberChart = new Chart(incomeByBarberCtx, {
|
||||
type: 'pie',
|
||||
data: {
|
||||
labels: incomeByBarberLabels,
|
||||
datasets: [{
|
||||
label: 'Pendapatan',
|
||||
data: incomeByBarberData,
|
||||
backgroundColor: incomeByBarberBackgroundColors,
|
||||
borderColor: incomeByBarberBorderColors,
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function (tooltipItem) {
|
||||
let rupiahFormat = new Intl.NumberFormat('id-ID', {
|
||||
style: 'currency',
|
||||
currency: 'IDR',
|
||||
maximumFractionDigits: 0,
|
||||
useGrouping: true
|
||||
}).format(tooltipItem.raw);
|
||||
|
||||
return tooltipItem.label + ': ' + rupiahFormat.replace('Rp ', 'Rp').replace(/\s/g, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
56
public/assets/admin/js/chart/payment-method.js
Normal file
56
public/assets/admin/js/chart/payment-method.js
Normal file
@ -0,0 +1,56 @@
|
||||
|
||||
let paymentMethodCtx = document.getElementById('paymentMethodChart').getContext('2d');
|
||||
|
||||
let paymentMethodLabels = paymentMethod.map(payment => {
|
||||
switch (payment.payment_method) {
|
||||
case '1':
|
||||
return 'Tunai';
|
||||
case '2':
|
||||
return 'Qris';
|
||||
case '3':
|
||||
return 'Lainnya';
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
});
|
||||
|
||||
let paymentMethodData = paymentMethod.map(paymentMethod => paymentMethod.total_amount);
|
||||
|
||||
let paymentMethodBackgroundColors = paymentMethodData.map(() => getRandomColor());
|
||||
let paymentMethodBorderColors = paymentMethodBackgroundColors.map(color => color.replace('0.2', '1'));
|
||||
|
||||
let paymentMethodChart = new Chart(paymentMethodCtx, {
|
||||
type: 'doughnut',
|
||||
data: {
|
||||
labels: paymentMethodLabels,
|
||||
datasets: [{
|
||||
label: 'Jumlah Transaksi',
|
||||
data: paymentMethodData,
|
||||
backgroundColor: paymentMethodBackgroundColors,
|
||||
borderColor: paymentMethodBorderColors,
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function (tooltipItem) {
|
||||
let rupiahFormat = new Intl.NumberFormat('id-ID', {
|
||||
style: 'currency',
|
||||
currency: 'IDR',
|
||||
maximumFractionDigits: 0,
|
||||
useGrouping: true
|
||||
}).format(tooltipItem.raw);
|
||||
|
||||
return tooltipItem.label + ': ' + rupiahFormat.replace('Rp ', 'Rp').replace(/\s/g, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
30
public/assets/admin/js/chart/top-service.js
Normal file
30
public/assets/admin/js/chart/top-service.js
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
let topServiceCtx = document.getElementById('topServiceChart').getContext('2d');
|
||||
|
||||
let topServiceLabels = topService.map(topService => topService.name);
|
||||
let topServiceData = topService.map(topService => topService.transactions_count);
|
||||
|
||||
let topServiceBackgroundColors = topServiceData.map(() => getRandomColor());
|
||||
let topServiceBorderColors = topServiceBackgroundColors.map(color => color.replace('0.2', '1'));
|
||||
|
||||
let topServiceChart = new Chart(topServiceCtx, {
|
||||
type: 'polarArea',
|
||||
data: {
|
||||
labels: topServiceLabels,
|
||||
datasets: [{
|
||||
label: 'Jumlah Transaksi',
|
||||
data: topServiceData,
|
||||
backgroundColor: topServiceBackgroundColors,
|
||||
borderColor: topServiceBorderColors,
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
},
|
||||
}
|
||||
},
|
||||
});
|
||||
30
public/assets/admin/js/chart/transaction-by-barber.js
Normal file
30
public/assets/admin/js/chart/transaction-by-barber.js
Normal file
@ -0,0 +1,30 @@
|
||||
|
||||
let transactionByBarberCtx = document.getElementById('transactionByBarberChart').getContext('2d');
|
||||
|
||||
let transactionByBarberLabels = transactionByBarber.map(transactionByBarber => transactionByBarber.biography.full_name);
|
||||
let transactionByBarberData = transactionByBarber.map(transactionByBarber => transactionByBarber.transactions_count);
|
||||
|
||||
let transactionByBarberBackgroundColors = transactionByBarberData.map(() => getRandomColor());
|
||||
let transactionByBarberBorderColors = transactionByBarberBackgroundColors.map(color => color.replace('0.2', '1'));
|
||||
|
||||
let transactionByBarberChart = new Chart(transactionByBarberCtx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: transactionByBarberLabels,
|
||||
datasets: [{
|
||||
label: 'Jumlah Transaksi',
|
||||
data: transactionByBarberData,
|
||||
backgroundColor: transactionByBarberBackgroundColors,
|
||||
borderColor: transactionByBarberBorderColors,
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'top',
|
||||
},
|
||||
}
|
||||
}
|
||||
});
|
||||
92
public/assets/admin/js/filter.js
Normal file
92
public/assets/admin/js/filter.js
Normal file
@ -0,0 +1,92 @@
|
||||
$(document).ready(function () {
|
||||
function formatToRupiah(amount) {
|
||||
return new Intl.NumberFormat('id-ID', {
|
||||
style: 'currency',
|
||||
currency: 'IDR',
|
||||
maximumFractionDigits: 0,
|
||||
useGrouping: true
|
||||
}).format(amount).replace('Rp ', 'Rp').replace(/\s/g, '');
|
||||
}
|
||||
|
||||
$("#filter-button").on("click", function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const startDate = $("#start_date").val();
|
||||
const endDate = $("#end_date").val();
|
||||
|
||||
$.ajax({
|
||||
url: "/dashboard/analytic/filter",
|
||||
method: "GET",
|
||||
data: {
|
||||
start_date: startDate,
|
||||
end_date: endDate,
|
||||
_token: $('meta[name="csrf-token"]').attr('content')
|
||||
},
|
||||
success: function (response) {
|
||||
$("#total-asset").text(formatToRupiah(response.data.totalAsset));
|
||||
$("#total-barber").text(response.data.totalBarber);
|
||||
$("#total-user").text(response.data.totalUser);
|
||||
$("#top-barber-name").text(response.data.topBarber.biography.full_name || "-deleted-");
|
||||
$("#top-barber-total").text(formatToRupiah(response.data.topBarber.transactions_sum_total));
|
||||
|
||||
// income expense
|
||||
let totalIncome = response.data.totalIncome;
|
||||
let totalExpense = response.data.totalExpense;
|
||||
|
||||
incomeExpenseChart.data.datasets[0].data = [
|
||||
totalIncome,
|
||||
totalExpense,
|
||||
];
|
||||
incomeExpenseChart.update();
|
||||
|
||||
// top service
|
||||
let topService = response.data.topService;
|
||||
let topServiceLabels = topService.map(item => item.name);
|
||||
let topServiceData = topService.map(item => item.transactions_count);
|
||||
|
||||
topServiceChart.data.labels = topServiceLabels;
|
||||
topServiceChart.data.datasets[0].data = topServiceData;
|
||||
topServiceChart.update();
|
||||
|
||||
// payment method
|
||||
let paymentMethod = response.data.paymentMethod;
|
||||
let paymentMethodLabels = paymentMethod.map(item => {
|
||||
switch (item.payment_method) {
|
||||
case '1':
|
||||
return 'Tunai';
|
||||
case '2':
|
||||
return 'Qris';
|
||||
case '3':
|
||||
return 'Lainnya';
|
||||
default:
|
||||
return 'Unknown';
|
||||
}
|
||||
});
|
||||
let paymentMethodData = paymentMethod.map(item => item.total_amount);
|
||||
|
||||
paymentMethodChart.data.labels = paymentMethodLabels;
|
||||
paymentMethodChart.data.datasets[0].data = paymentMethodData;
|
||||
paymentMethodChart.update();
|
||||
|
||||
// income by barber
|
||||
let updatedIncomeByBarber = response.data.incomeByBarber;
|
||||
let updatedLabels = updatedIncomeByBarber.map(item => item.biography.full_name);
|
||||
let updatedData = updatedIncomeByBarber.map(item => item.transactions_sum_total);
|
||||
|
||||
incomeByBarberChart.data.labels = updatedLabels;
|
||||
incomeByBarberChart.data.datasets[0].data = updatedData;
|
||||
incomeByBarberChart.update();
|
||||
|
||||
// transaction by barber
|
||||
let transactionByBarber = response.data.transactionByBarber;
|
||||
let transactionByBarberLabels = transactionByBarber.map(item => item.biography.full_name);
|
||||
let transactionByBarberData = transactionByBarber.map(item => item.transactions_count);
|
||||
|
||||
transactionByBarberChart.data.labels = transactionByBarberLabels;
|
||||
transactionByBarberChart.data.datasets[0].data = transactionByBarberData;
|
||||
transactionByBarberChart.update();
|
||||
},
|
||||
error: function (xhr) { },
|
||||
});
|
||||
});
|
||||
});
|
||||
6
public/assets/admin/js/random-color.js
Normal file
6
public/assets/admin/js/random-color.js
Normal file
@ -0,0 +1,6 @@
|
||||
function getRandomColor() {
|
||||
const r = Math.floor(Math.random() * 256);
|
||||
const g = Math.floor(Math.random() * 256);
|
||||
const b = Math.floor(Math.random() * 256);
|
||||
return `rgba(${r}, ${g}, ${b}, 0.2)`;
|
||||
}
|
||||
209
resources/views/pages/admin/dashboard/analytic.blade.php
Normal file
209
resources/views/pages/admin/dashboard/analytic.blade.php
Normal file
@ -0,0 +1,209 @@
|
||||
@extends('layouts.admin')
|
||||
@section('styles')
|
||||
<link rel="stylesheet" type="text/css" href="{{ asset('assets/admin/css/vendors/flatpickr/flatpickr.min.css') }}">
|
||||
@endsection
|
||||
@section('app')
|
||||
<div class="container-fluid">
|
||||
<div class="mb-3">
|
||||
<div class="input-group">
|
||||
<div class="flatpicker-calender">
|
||||
<input name="start_date" class="form-control human-friendly" id="start_date" type="date"
|
||||
value="{{ $startDate ?? '2024-01-01' }}">
|
||||
</div>
|
||||
<span class="input-group-text">To</span>
|
||||
<div class="flatpicker-calender">
|
||||
<input name="end_date" class="form-control human-friendly" id="end_date" type="date"
|
||||
value="{{ $endDate ?? now() }}">
|
||||
</div>
|
||||
<button class="btn btn-primary" id="filter-button">Filter</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row widget-grid">
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<div class="card small-widget">
|
||||
<div class="card-body primary">
|
||||
<span class="f-light">Total Aset</span>
|
||||
<div class="d-flex align-items-end gap-1">
|
||||
<h4 id="total-asset">{{ formatToRupiah($totalAsset) }}</h4>
|
||||
</div>
|
||||
<div class="bg-gradient">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<ul class="user-list">
|
||||
<li>
|
||||
<div class="user-icon primary">
|
||||
<div class="user-box">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
|
||||
viewBox="-3 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
||||
stroke-linecap="round" stroke-linejoin="round"
|
||||
class="feather feather-user-plus font-primary">
|
||||
<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="8.5" cy="7" r="4"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="mb-1" id="total-barber">{{ $totalBarber }}</h5>
|
||||
<span class="font-primary d-flex align-items-center">
|
||||
<i class="icon-arrow-up icon-rotate me-1"> </i>
|
||||
<span class="f-w-500">Barber</span>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="user-icon success">
|
||||
<div class="user-box">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24"
|
||||
viewBox="-3 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
|
||||
stroke-linecap="round" stroke-linejoin="round"
|
||||
class="feather feather-user-minus font-success">
|
||||
<path d="M16 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path>
|
||||
<circle cx="8.5" cy="7" r="4"></circle>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h5 class="mb-1" id="total-user">{{ $totalUser }}</h5>
|
||||
<span class="font-danger d-flex align-items-center">
|
||||
<i class="icon-arrow-down icon-rotate me-1"></i>
|
||||
<span class="f-w-500">Semua</span>
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card 0-hidden">
|
||||
<div class="card-body balance-widget">
|
||||
<span class="f-w-500 f-light">Top Barber</span>
|
||||
<h4 class="mb-3 mt-1 f-w-500 mb-0 f-22">
|
||||
<span class="counter"
|
||||
id="top-barber-name">{{ $topBarber->biography->full_name ?? '-deleted-' }}</span>
|
||||
</h4>
|
||||
<span class=" btn-hover-effect f-w-500"
|
||||
id="top-barber-total">{{ formatToRupiah($topBarber->transactions_sum_total) }}</span>
|
||||
<div class="mobile-right-img">
|
||||
<img class="mobile-img" src="{{ asset('assets/admin/images/crown.png') }}"
|
||||
alt="mobile with coin">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-header card-no-border">
|
||||
<h5>Grafik Keuangan</h5>
|
||||
</div>
|
||||
<div class="card-body pt-0">
|
||||
<div class="row m-0 overall-card">
|
||||
<div class="chart-right">
|
||||
<div class="card-body p-0">
|
||||
<div class="current-sale-container">
|
||||
<canvas id="incomeExpenseChart" width="400" height="400"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-header card-no-border">
|
||||
<h5>Top Layanan</h5>
|
||||
</div>
|
||||
<div class="card-body pt-0">
|
||||
<div class="row m-0 overall-card">
|
||||
<div class="chart-right">
|
||||
<div class="card-body p-0">
|
||||
<div class="current-sale-container">
|
||||
<canvas id="topServiceChart" width="400" height="400"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-header card-no-border">
|
||||
<h5>Metode Pembayaran</h5>
|
||||
</div>
|
||||
<div class="card-body pt-0">
|
||||
<div class="row m-0 overall-card">
|
||||
<div class="chart-right">
|
||||
<div class="card-body p-0">
|
||||
<div class="current-sale-container">
|
||||
<canvas id="paymentMethodChart" width="400" height="400"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-header card-no-border">
|
||||
<h5>Pendapatan By Barber</h5>
|
||||
</div>
|
||||
<div class="card-body pt-0">
|
||||
<div class="row m-0 overall-card">
|
||||
<div class="chart-right">
|
||||
<div class="card-body p-0">
|
||||
<div class="current-sale-container">
|
||||
<canvas id="incomeByBarberChart" width="400" height="400"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-md-6 col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-header card-no-border">
|
||||
<h5>Transaksi By Barber</h5>
|
||||
</div>
|
||||
<div class="card-body pt-0">
|
||||
<div class="row m-0 overall-card">
|
||||
<div class="chart-right">
|
||||
<div class="card-body p-0">
|
||||
<div class="current-sale-container">
|
||||
<canvas id="transactionByBarberChart" width="400" height="400"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@section('beforeScripts')
|
||||
<script src="{{ asset('assets/admin/js/flat-pickr/flatpickr.js?ver=1.0.0') }}"></script>
|
||||
<script src="{{ asset('assets/admin/js/flat-pickr/custom-flatpickr.js?ver=1.0.0') }}"></script>
|
||||
@endsection
|
||||
@section('afterScripts')
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.6/dist/chart.umd.min.js"></script>
|
||||
<script>
|
||||
const totalIncome = @json($totalIncome);
|
||||
const totalExpense = @json($totalExpense);
|
||||
const topService = @json($topService);
|
||||
const paymentMethod = @json($paymentMethod);
|
||||
const incomeByBarber = @json($incomeByBarber);
|
||||
const transactionByBarber = @json($transactionByBarber);
|
||||
</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/payment-method.js?ver=1.0.0') }}"></script>
|
||||
<script src="{{ asset('assets/admin/js/chart/top-service.js?ver=1.0.0') }}"></script>
|
||||
<script src="{{ asset('assets/admin/js/chart/income-by-barber.js?ver=1.0.0') }}"></script>
|
||||
<script src="{{ asset('assets/admin/js/chart/transaction-by-barber.js?ver=1.0.0') }}"></script>
|
||||
<script src="{{ asset('assets/admin/js/filter.js?ver=1.0.0') }}"></script>
|
||||
@endsection
|
||||
@ -49,6 +49,13 @@
|
||||
<span>Ringkasan</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="sidebar-list">
|
||||
<i class="fa fa-thumb-tack"></i>
|
||||
<a class="sidebar-link sidebar-title link-nav" href="{{ route('dashboard.analytic') }}">
|
||||
<i data-feather="bar-chart-2"></i>
|
||||
<span>Analitik</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="sidebar-main-title">
|
||||
<div>
|
||||
<h6>Master</h6>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\Admin\Auth\LoginController;
|
||||
use App\Http\Controllers\Admin\Dashboard\AnalyticController;
|
||||
use App\Http\Controllers\Admin\Dashboard\OverviewController;
|
||||
use App\Http\Controllers\Admin\Finance\CashController;
|
||||
use App\Http\Controllers\Admin\Finance\ExpenseController;
|
||||
@ -29,6 +30,8 @@
|
||||
'prefix' => 'dashboard',
|
||||
], function () {
|
||||
Route::get('overview', [OverviewController::class, 'index'])->name('dashboard.overview');
|
||||
Route::get('analytic', [AnalyticController::class, 'index'])->name('dashboard.analytic');
|
||||
Route::get('analytic/filter', [AnalyticController::class, 'filter'])->name('dashboard.analytic.filter');
|
||||
|
||||
Route::prefix('master')->group(function () {
|
||||
Route::prefix('barbershop')->group(function () {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user