Courses

Practical Livewire 3: Order Management System Step-by-Step

Orders Total Revenue Chart

In this last lesson of this course, we will make a last 7 days total revenue chart using Chart.js and Livewire Polling feature.

finished chart

We'll start by adding Chart.js from CDN.

resources/views/layouts/app.blade.php:

<script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/select2.min.js"></script>
@livewireScripts
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="https://unpkg.com/@nextapps-be/[email protected]/dist/livewire-sortable.js"></script>
<script src="//cdn.jsdelivr.net/npm/sweetalert2@11"></script>

Next, of course, we need to create the Livewire component and add it to the dashboard.

php artisan make:livewire TotalRevenueChart

resources/views/dashboard.blade.php:

<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 dark:text-gray-200 leading-tight">
{{ __('Dashboard') }}
{{ __('Total Revenue: Last 7 Days') }}
</h2>
</x-slot>
 
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<div class="bg-white dark:bg-gray-800 overflow-hidden shadow-sm sm:rounded-lg">
<div class="p-6 text-gray-900 dark:text-gray-100">
{{ __("You're logged in!") }}
@livewire('total-revenue-chart')
</div>
</div>
</div>
</div>
</x-app-layout>

For showing the chart, we will use Livewire in combination with Alpine.js. First, in the component, we will add the getData() method in which we will make a query to DB and later will return the prepared array to use in the chart.

app/Http/Livewire/TotalRevenueChart.php:

class TotalRevenueChart extends Component
{
// ...
 
protected function getData(): array
{
$data = Order::query()
->select('order_date', \DB::raw('sum(total) as total'))
->where('order_date', '>=', now()->subDays(7))
->groupBy('order_date')
->get();
 
return [
'datasets' => [
[
'label' => 'Total revenue from last 7 days',
'data' => $data->map(fn (Order $order) => $order->total / 100),
]
],
'labels' => $data->map(fn (Order $order) => $order->order_date->format('d/m/Y')),
];
}
}

This will return a result similar to this:

array:2 [▼ // app/Http/Livewire/TotalRevenueChart.php:18
"datasets" => array:1 [▶]
"labels" => Illuminate\Support\Collection {#1503 ▼
#items: array:7 [▼
0 => "30/01/2023"
1 => "31/01/2023"
2 => "01/02/2023"
3 => "02/02/2023"
4 => "03/02/2023"
5 => "04/02/2023"
6 => "05/02/2023"
]
#escapeWhenCastingToString: false
}
]

Now we can show the chart. In Alpine everything starts with the x-data directive, so we will add all the code to it.

resources/views/livewire/total-revenue-chart.blade.php:

<div>
<canvas
x-data="{
chart: null,
 
init: function () {
let chart = new Chart($el, {
type: 'line',
data: @js($this->getData()),
})
}
}"
style="height: 320px;">
</canvas>
</div>

Our chart now should show some data:

chart with data

Now we can add functionality to auto-refresh the chart every 1-minute using Livewire polling. With wire:poll we will call the updateChartData() method in which we will fire an event called updateChartData with the newly fetched data from DB. Then this event will be caught by Alpine.js which will update the chart. The very important here is to add wire:ignore to the canvas element so that Livewire would ignore DOM changes.

app/Livewire/TotalRevenueChart.php:

class TotalRevenueChart extends Component
{
// ...
 
public function updateChartData(): void
{
$this->dispatch('updateChartData', data: $this->getData())->self();
}
 
// ...
}

resources/views/livewire/total-revenue-chart.blade.php:

<div wire:poll.60s="updateChartData">
<canvas
x-data="{
chart: null,
 
init: function () {
let chart = new Chart($el, {
type: 'line',
data: @js($this->getData())
})
 
$wire.on('updateChartData', async ({ data }) => {
chart.data = data
chart.update('resize')
})
}
}"
style="height: 320px;"
wire:ignore>
</canvas>
</div>

If you would add a new order, the chart now should update automatically. Last thing here, we will make a change to the tooltip. When hovered we will only show the value and dollar sign before it.

resources/views/livewire/total-revenue-chart.blade.php:

<div wire:poll.60s="updateChartData">
<canvas
x-data="{
chart: null,
 
init: function () {
let chart = new Chart($el, {
type: 'line',
data: @js($this->getData()),
options: {
plugins: {
tooltip: {
callbacks: {
label: function (context) {
return '$' + context.formattedValue
}
}
}
}
}
})
 
$wire.on('updateChartData', async ({ data }) => {
chart.data = data
chart.update('resize')
})
}
}"
style="height: 320px;"
wire:ignore>
</canvas>
</div>

changed tooltip label


And that's it for this course! The repository is available here on GitHub.

avatar

Thank You Sir For The Courses !!!

avatar
You can use Markdown
avatar

Great course, and well explained ! there is a mistake on this line (10) options: { { -> options: {

avatar

Well noticed! Fixed now.

avatar
You can use Markdown
avatar

Just finished this course. Awesome. I hope can implement this tutorials to my projects.

avatar
You can use Markdown
avatar

Finally finished the course thanks!

avatar
You can use Markdown
avatar

Amazing Content

avatar
You can use Markdown
avatar

Grate Course Got everything working not meney issues Thanks.

avatar

I just finished retakeing this course using Laravel 11 and I had Verry little issues Grate course even with updating to v11

avatar
You can use Markdown
avatar
Luis Antonio Parrado

Course finished! Great! Only a issue, If I'm creating an order and press Save button without save some product I'm getting an error!

Next challenge! Upgrade this code to Livewire 3.

avatar
You can use Markdown
avatar

Question How Hard Would it be to do this in Filament?

AND can you do a class on that?

avatar

Class really doubt. In filament should be easy. Only basic features should be needed

avatar
You can use Markdown
avatar

Great course, thank you!

avatar
You can use Markdown
avatar
You can use Markdown