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:
parent
1edfd7b9d4
commit
d7c06c38a6
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
use App\Enums\UserTypes;
|
use App\Enums\UserTypes;
|
||||||
use App\Http\Resources\ActiveUsersStatsCollection;
|
use App\Http\Resources\ActiveUsersStatsCollection;
|
||||||
|
use App\Http\Resources\DealsCountByCategoryCollection;
|
||||||
|
use App\Models\DealCategory;
|
||||||
use App\Queries\PageVisitStatsQuery;
|
use App\Queries\PageVisitStatsQuery;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
|
|
||||||
@ -22,4 +24,13 @@ public function getActiveUsers(Request $request, PageVisitStatsQuery $baseQuery)
|
|||||||
'activeBrokers' => new ActiveUsersStatsCollection($activeBrokers),
|
'activeBrokers' => new ActiveUsersStatsCollection($activeBrokers),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getDealsByCategory()
|
||||||
|
{
|
||||||
|
return new DealsCountByCategoryCollection(
|
||||||
|
DealCategory::select(['id', 'name'])
|
||||||
|
->withCount('deals')
|
||||||
|
->get()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
app/Http/Resources/DealsCountByCategoryCollection.php
Normal file
16
app/Http/Resources/DealsCountByCategoryCollection.php
Normal 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,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
17
app/Http/Resources/DealsCountByCategoryResource.php
Normal file
17
app/Http/Resources/DealsCountByCategoryResource.php
Normal 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,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,7 @@
|
|||||||
<div id="active-users-chart-parent" class="col-span-2">
|
<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-group class="mb-4">
|
||||||
<x-ui.toggle-button :active="request('sortBy') == null">
|
<x-ui.toggle-button :active="request('sortBy') == null">
|
||||||
<button onclick="switchGraph(this, 30)" class="graphBtn flex items-center px-2 space-x-2">
|
<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>
|
||||||
|
|
||||||
<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"/>
|
<x-heroicon-o-calendar-date-range class="w-4"/>
|
||||||
</button>
|
</button>
|
||||||
</x-ui.toggle-button>
|
</x-ui.toggle-button>
|
||||||
</x-ui.toggle-button-group>
|
</x-ui.toggle-button-group>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="h-50">
|
<div data-is-loading="false" class="h-50">
|
||||||
<canvas id="active-users-chart"></canvas>
|
<canvas id="active-users-chart"></canvas>
|
||||||
</div>
|
</div>
|
||||||
</x-dashboard.card>
|
</x-dashboard.card>
|
||||||
</div>
|
</div>
|
||||||
@push('scripts')
|
@push('scripts')
|
||||||
<script>
|
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
|
||||||
|
|
||||||
|
<script>
|
||||||
window.addEventListener('DOMContentLoaded', () => {
|
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 end = new Date();
|
||||||
const start = new Date();
|
const start = new Date();
|
||||||
start.setDate(end.getDate() - 30);
|
start.setDate(end.getDate() - 30);
|
||||||
|
|||||||
@ -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>
|
<canvas id="category-wise-deals-pie-chart"></canvas>
|
||||||
</x-dashboard.card>
|
</div>
|
||||||
|
</x-dashboard.card>
|
||||||
|
</div>
|
||||||
@push('scripts')
|
@push('scripts')
|
||||||
<script async>
|
<script async>
|
||||||
window.addEventListener('DOMContentLoaded', async () => {
|
window.addEventListener('DOMContentLoaded', async () => {
|
||||||
const dealsPieChart = document.getElementById('category-wise-deals-pie-chart');
|
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 {
|
try {
|
||||||
// const response = await axios.get('/api/stats/customer/active/30');
|
const {data: apiData} = await axios.get('/api/stats/deals-by-category');
|
||||||
// const apiData = response.data.data;
|
|
||||||
|
|
||||||
|
const categories = apiData?.data.map((item) => item?.name);
|
||||||
// Fill the data from api response
|
const dealsCounts = apiData?.data.map((item) => item?.dealsCount);
|
||||||
// const chartData = labels.map((date) => {
|
|
||||||
// let found = apiData.find(item => item.date === date);
|
|
||||||
// return found ? found.userCount : 0;
|
|
||||||
// })
|
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
labels: [
|
labels: categories,
|
||||||
'Real Estate',
|
|
||||||
'Food',
|
|
||||||
'Sell & Deal',
|
|
||||||
'Palaces'
|
|
||||||
],
|
|
||||||
datasets: [{
|
datasets: [{
|
||||||
label: 'My First Dataset',
|
data: dealsCounts,
|
||||||
data: [30, 20, 15, 5],
|
|
||||||
backgroundColor: [
|
|
||||||
'rgb(255, 99, 132)',
|
|
||||||
'rgb(54, 162, 235)',
|
|
||||||
'rgb(255, 205, 86)',
|
|
||||||
'rgb(99,102,255)',
|
|
||||||
],
|
|
||||||
hoverOffset: 4
|
hoverOffset: 4
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
@ -42,10 +37,15 @@
|
|||||||
options: {
|
options: {
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
responsive: true,
|
responsive: true,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'bottom',
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
const chart = new Chart(dealsPieChart, config);
|
const chart = new Chart(dealsPieChart, config);
|
||||||
// toggleShimmer(false, activeChartParent);
|
toggleShimmer(true, dealsPieChartParent);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.log(e)
|
console.log(e)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,8 @@
|
|||||||
<link rel="preconnect" href="https://fonts.bunny.net">
|
<link rel="preconnect" href="https://fonts.bunny.net">
|
||||||
<link href="https://fonts.bunny.net/css?family=instrument-sans:400,500,600" rel="stylesheet"/>
|
<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 -->
|
<!-- Styles / Scripts -->
|
||||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||||
|
|
||||||
|
|||||||
@ -4,4 +4,5 @@
|
|||||||
|
|
||||||
Route::prefix('/stats')->group(function () {
|
Route::prefix('/stats')->group(function () {
|
||||||
Route::get('/active-users', [StatsController::class, 'getActiveUsers']);
|
Route::get('/active-users', [StatsController::class, 'getActiveUsers']);
|
||||||
|
Route::get('/deals-by-category', [StatsController::class, 'getDealsByCategory'])->name('stats.deals-by-category');
|
||||||
});
|
});
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user