In this lesson, let's change our dashboard to something more interesting than static placeholders.
Typically, we have charts and numbers on the dashboard of such projects. So, we can calculate something like these:
- Tasks completed vs in progress
- Tasks due today
- Tasks created this week, grouped by day
Let's show them all on the dashboard.
Change Factory and Re-Seed Data
To have some data to show, we need to randomize a few things and seed more tasks.
database/factories/TaskFactory.php:
return [ 'name' => fake()->name(), 'is_completed' => fake()->boolean(), 'due_date' => fake()->dateTimeBetween('now', '+1 month'), 'created_at' => fake()->dateTimeBetween(now()->startOfWeek(), now()->endOfWeek()), ];
database/seeders/DatabaseSeeder.php:
Task::factory()->count(10)->create(); Task::factory()->count(100)->create();
And now, let's freshen and re-seed the whole database (yes, we lose the data, but this is a demo project, so I'm okay with that).
php artisan migrate:fresh --seed
Great, we have 100 random tasks in the database:
Dashboard Controller with Data
By default, we load the dashboard directly from the Routes file.
routes/web.php:
Route::get('dashboard', function () { return Inertia::render('dashboard');})->name('dashboard');
Now, let's add more logic. So, we need a Controller for that:
php artisan make:controller DashboardController
And let's assign the route to it as an invokable single action Controller.
use App\Http\Controllers\DashboardController; // ... Route::get('dashboard', DashboardController::class)->name('dashboard');
Now, inside the Controller, we will load all the needed data, creating a few private
methods to help.
app/Http/Controllers/DashboardController.php:
namespace App\Http\Controllers; use App\Models\Task;use Inertia\Inertia; class DashboardController extends Controller{ public function __invoke() { return Inertia::render('dashboard', [ 'completedVsPendingTaskChart' => $this->getCompletedVsPendingTaskChart(), 'pendingTasksToday' => Task::query() ->where('is_completed', false) ->whereDate('due_date', now()) ->count(), 'tasksCreatedByDay' => $this->getTasksCreatedByDay(), ]); } private function getCompletedVsPendingTaskChart(): array { return [ 'labels' => ['Completed', 'In Progress'], 'datasets' => [ [ 'label' => 'Tasks', 'backgroundColor' => ['#3490dc', '#f6993f'], 'data' => [ Task::query()->where('is_completed', true)->count(), Task::query()->where('is_completed', false)->count(), ], ], ], ]; } private function getTasksCreatedByDay(): array { return [ 'labels' => ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'], 'datasets' => [ [ 'label' => 'Tasks', 'backgroundColor' => '#3490dc', 'data' => collect(range(0, 6)) ->map(function ($day) { $date = now()->startOfWeek()->addDays($day); return Task::query()->whereDate('created_at', $date)->count(); }) ->toArray(), ], ], ]; }}
So, our Controller returns three datasets:
- completedVsPendingTaskChart
- pendingTasksToday
- tasksCreatedByDay
Let's display them on the front end.
Displaying Data on Dashboard
For the charts, we will use two JS libraries. Let's install them.
npm install react-chartjs-2 chart.js
Now, let's add the charts and the number instead of the default empty PlaceholderPattern
components.
resources/js/pages/dashboard.tsx:
import { ArcElement, BarElement, CategoryScale, Chart, Legend, LinearScale, Title, Tooltip } from 'chart.js';import { Bar, Doughnut } from 'react-chartjs-2'; Chart.register(ArcElement, Tooltip, Legend, CategoryScale, LinearScale, BarElement, Title, Tooltip, Legend); // ... export default function Dashboard({ completedVsPendingTaskChart, pendingTasksToday, tasksCreatedByDay,}: { completedVsPendingTaskChart: object; pendingTasksToday: number; tasksCreatedByDay: object;}) { return ( <AppLayout breadcrumbs={breadcrumbs}> <Head title="Dashboard" /> <div className="flex h-full flex-1 flex-col gap-4 rounded-xl p-4"> <div className="grid auto-rows-min gap-4 md:grid-cols-3"> <div className="border-sidebar-border/70 dark:border-sidebar-border relative aspect-video overflow-hidden rounded-xl border py-4"> <Doughnut data={completedVsPendingTaskChart} className={'mx-auto'} /> </div> <div className="border-sidebar-border/70 dark:border-sidebar-border relative flex aspect-video flex-col items-center justify-start overflow-hidden rounded-xl border py-4"> <h2 className={'text-center text-3xl font-bold'}>Tasks Due Today</h2> <p className={'mt-auto mb-auto text-xl'}>{pendingTasksToday} task(-s) due today.</p> </div> <div className="border-sidebar-border/70 dark:border-sidebar-border relative flex aspect-video flex-col items-center justify-start overflow-hidden rounded-xl border py-4"> <h2 className={'text-center text-3xl font-bold'}>Tasks This Week</h2> <Bar data={tasksCreatedByDay} className={'mx-auto'} /> </div> </div> </div> </AppLayout> );}
Here's the visual result:
It's hard to comment on the code above: we're just passing parameters to the main Dashboard()
method and using them inside to show the data with Chart components (read their docs for more) and Tailwind classes. To be honest, there's not much to add.
Here's the GitHub commit for this lesson.
The code works in that it runs, but it doesn't pass Typescript checking meaning that
data
will be higlighted as a Typescript error.To correct this, define 2 new itefaces at the top under your imports:
Then apply the correct types to
completedVsPendingTaskChart
andtasksCreatedByDay
:Tried to delete a task and got the following Error: SQLSTATE[23000]: Integrity constraint violation: 19 FOREIGN KEY constraint failed (Connection: sqlite, SQL: delete from "tasks" where "id" = 1)
Not sure where to go to fix this; can you help?
P.S. The Category Delete works fine no Errors.
I thank I know what Is happening; there is a Category assigned to it. We need to do a unassign of the category first right?
I've just finished this wonderful course, thanks to the LaravelDaily team. I've never worked with React before (only Vue), but I see quite a few similarities. The same as until now I start using Typescript (I still need to study several concepts, of course).
I found an issue that can be easily fixed in the bar chart, only the first day of the week is being graphed and this is because the
data
is wrapped in an additional array inDashboardController.php
,getTasksCreatedByDay()
method.It should be
Hi, thank you for the kind words and for telling us about the issue! I have updated the lesson to fix this issue :)