feature(deals by category pie chart):

- add a pie chart that shows deals by category
- change UI to make it more clear
This commit is contained in:
kusowl 2026-02-02 17:22:01 +05:30
parent 1edfd7b9d4
commit d7c06c38a6
7 changed files with 115 additions and 47 deletions

View File

@ -4,6 +4,8 @@
use App\Enums\UserTypes;
use App\Http\Resources\ActiveUsersStatsCollection;
use App\Http\Resources\DealsCountByCategoryCollection;
use App\Models\DealCategory;
use App\Queries\PageVisitStatsQuery;
use Illuminate\Http\Request;
@ -22,4 +24,13 @@ public function getActiveUsers(Request $request, PageVisitStatsQuery $baseQuery)
'activeBrokers' => new ActiveUsersStatsCollection($activeBrokers),
]);
}
public function getDealsByCategory()
{
return new DealsCountByCategoryCollection(
DealCategory::select(['id', 'name'])
->withCount('deals')
->get()
);
}
}

View File

@ -0,0 +1,16 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\ResourceCollection;
class DealsCountByCategoryCollection extends ResourceCollection
{
public function toArray(Request $request): array
{
return [
'data' => $this->collection,
];
}
}

View File

@ -0,0 +1,17 @@
<?php
namespace App\Http\Resources;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\Json\JsonResource;
class DealsCountByCategoryResource extends JsonResource
{
public function toArray(Request $request): array
{
return [
'name' => $this->name,
'dealsCount' => $this->deals_count,
];
}
}

View File

@ -1,5 +1,7 @@
<div id="active-users-chart-parent" class="col-span-2">
<x-dashboard.card data-is-loading="false" class="h-75">
<x-dashboard.card class="h-75">
<div class="flex justify-between items-baseline">
<h3 class="text-md font-bold mb-4">User Activity</h3>
<x-ui.toggle-button-group class="mb-4">
<x-ui.toggle-button :active="request('sortBy') == null">
<button onclick="switchGraph(this, 30)" class="graphBtn flex items-center px-2 space-x-2">
@ -14,22 +16,41 @@
</x-ui.toggle-button>
<x-ui.toggle-button>
<button class="flex items-center pt-0.5 px-4 space-x-2">
<button id="dateRange" class="flex items-center pt-0.5 px-4 space-x-2">
<x-heroicon-o-calendar-date-range class="w-4"/>
</button>
</x-ui.toggle-button>
</x-ui.toggle-button-group>
</div>
<div class="h-50">
<div data-is-loading="false" class="h-50">
<canvas id="active-users-chart"></canvas>
</div>
</x-dashboard.card>
</div>
@push('scripts')
<script>
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
<script>
window.addEventListener('DOMContentLoaded', () => {
// BY default load 30 days data
flatpickr("#dateRange", {
mode: "range",
dateFormat: "Y-m-d",
onClose: function (selectedDates) {
if (selectedDates.length === 2) {
const start = selectedDates[0].toISOString().split('T')[0];
const end = selectedDates[1].toISOString().split('T')[0];
activeBtn(document.getElementById('dateRange'));
generateActiveUsersChart(start, end);
}
}
});
// By default load 30 days data
const end = new Date();
const start = new Date();
start.setDate(end.getDate() - 30);

View File

@ -1,37 +1,32 @@
<x-dashboard.card class="col-span-1 h-75">
<div id="deals-pie-chart-parent" class="col-span-1">
<x-dashboard.card class="col-span-1 h-75">
<h3 class="text-md font-bold mb-4">Deals by category</h3>
<div data-is-loading="false" class="h-55">
<canvas id="category-wise-deals-pie-chart"></canvas>
</x-dashboard.card>
</div>
</x-dashboard.card>
</div>
@push('scripts')
<script async>
window.addEventListener('DOMContentLoaded', async () => {
const dealsPieChart = document.getElementById('category-wise-deals-pie-chart');
const dealsPieChartParent = document.getElementById('deals-pie-chart-parent');
if (!dealsPieChart || !dealsPieChartParent) {
console.error('canvas not defined');
return;
}
toggleShimmer(false, dealsPieChartParent);
try {
// const response = await axios.get('/api/stats/customer/active/30');
// const apiData = response.data.data;
const {data: apiData} = await axios.get('/api/stats/deals-by-category');
// Fill the data from api response
// const chartData = labels.map((date) => {
// let found = apiData.find(item => item.date === date);
// return found ? found.userCount : 0;
// })
const categories = apiData?.data.map((item) => item?.name);
const dealsCounts = apiData?.data.map((item) => item?.dealsCount);
const data = {
labels: [
'Real Estate',
'Food',
'Sell & Deal',
'Palaces'
],
labels: categories,
datasets: [{
label: 'My First Dataset',
data: [30, 20, 15, 5],
backgroundColor: [
'rgb(255, 99, 132)',
'rgb(54, 162, 235)',
'rgb(255, 205, 86)',
'rgb(99,102,255)',
],
data: dealsCounts,
hoverOffset: 4
}]
};
@ -42,10 +37,15 @@
options: {
maintainAspectRatio: false,
responsive: true,
plugins: {
legend: {
position: 'bottom',
}
}
}
};
const chart = new Chart(dealsPieChart, config);
// toggleShimmer(false, activeChartParent);
toggleShimmer(true, dealsPieChartParent);
} catch (e) {
console.log(e)
}

View File

@ -19,6 +19,8 @@
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=instrument-sans:400,500,600" rel="stylesheet"/>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
<!-- Styles / Scripts -->
@vite(['resources/css/app.css', 'resources/js/app.js'])

View File

@ -4,4 +4,5 @@
Route::prefix('/stats')->group(function () {
Route::get('/active-users', [StatsController::class, 'getActiveUsers']);
Route::get('/deals-by-category', [StatsController::class, 'getDealsByCategory'])->name('stats.deals-by-category');
});