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 [ [ 'name' => 'Completed', 'total' => Task::query()->where('is_completed', true)->count(), ], [ 'name' => 'In Progress', 'total' => Task::query()->where('is_completed', false)->count(), ], ]; } private function getTasksCreatedByDay(): array { return collect(range(0, 6)) ->map(function ($day) { $date = now()->startOfWeek()->addDays($day); return [ 'name' => $date->format('D'), 'Tasks Created' => 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 shadCN charts. Specifically:
Let's install them:
npx shadcn-vue@latest add chart-donutnpx shadcn-vue@latest add chart-bar
And complete the required guide to add the charts to the project.
resources/css/app.css
/* ... */@layer base { :root { // ... --sidebar-ring: 217.2 91.2% 59.8%; /* Charts */ --vis-tooltip-background-color: none !important; --vis-tooltip-border-color: none !important; --vis-tooltip-text-color: none !important; --vis-tooltip-shadow-color: none !important; --vis-tooltip-backdrop-filter: none !important; --vis-tooltip-padding: none !important; --vis-primary-color: var(--primary); /* change to any hsl value you want */ --vis-secondary-color: 160 81% 40%; --vis-text-color: var(--muted-foreground); } .dark { // ... --sidebar-ring: 217.2 91.2% 59.8%; /* Charts */ --vis-tooltip-background-color: none !important; --vis-tooltip-border-color: none !important; --vis-tooltip-text-color: none !important; --vis-tooltip-shadow-color: none !important; --vis-tooltip-backdrop-filter: none !important; --vis-tooltip-padding: none !important; --vis-primary-color: var(--primary); /* change to any hsl value you want */ --vis-secondary-color: 160 81% 40%; --vis-text-color: var(--muted-foreground); }}
Now, let's add the charts and the number instead of the default empty PlaceholderPattern
components.
resources/js/pages/Dashboard.vue:
<script>import { BarChart } from '@/components/ui/chart-bar';import { DonutChart } from '@/components/ui/chart-donut'; // ... interface Props { completedVsPendingTaskChart: []; pendingTasksToday: number; tasksCreatedByDay: object;} defineProps<Props>();</script> <template> <Head title="Dashboard" /> <AppLayout :breadcrumbs="breadcrumbs"> <div class="flex h-full flex-1 flex-col gap-4 rounded-xl p-4"> <div class="grid auto-rows-min gap-4 md:grid-cols-3"> <div class="relative aspect-video overflow-hidden rounded-xl border border-sidebar-border/70 p-4 dark:border-sidebar-border"> <h2 class="pb-4 text-center text-3xl font-bold">Progress Overview</h2> <DonutChart class="h-32" index="name" :category="'total'" :data="completedVsPendingTaskChart" :type="'pie'" data-size="sm" :colors="['#00a64b', '#e3544f']" :show-legend="true" /> </div> <div class="relative flex aspect-video flex-col items-center justify-start overflow-hidden rounded-xl border border-sidebar-border/70 py-4 dark:border-sidebar-border" > <h2 class="text-center text-3xl font-bold">Tasks Due Today</h2> <p class="mb-auto mt-auto text-xl">{{ pendingTasksToday }} task(-s) due today.</p> </div> <div class="relative aspect-video overflow-hidden rounded-xl border border-sidebar-border/70 dark:border-sidebar-border"> <h2 class="pb-4 text-center text-3xl font-bold pt-4">Tasks by Day</h2> <BarChart class="h-32 p-4" index="name" :data="tasksCreatedByDay" :categories="['Tasks Created']" :rounded-corners="4" /> </div> </div> <div class="relative min-h-[100vh] flex-1 rounded-xl border border-sidebar-border/70 dark:border-sidebar-border md:min-h-min"> <PlaceholderPattern /> </div> </div> </AppLayout></template>
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.
Any chance of you helping us get the charts to actually work.?
I have two 2 due dates set for today; but I am not getting any on the Dashboard.
Could you add more details on what actually isn't working? It is working perfectly fine on my end :)
Every thing is working grate up to this point On the task/index.tsx page the file is visible as well as the Categories the Status is set for in Progress and the Due Date is set for todays date.
Check the database to see if they are set to in progress. Check that your database correctly filters the records based on the query.
Really hard to say what exactly is wrong here without seeing what was done.
I have just tested this - it does seem to work and correctly filter the due date results
the Progress Overview pie chart is working grate I went through and changed quite a few of the states to completed and the pie chart has changed as well
the program has the date as 3/21/2025 however the database has it as 3/22/2025 What’s up with this and how do I fix it?
this is both with the edit.tsx and the create.tsx files
If I chouse the date of 3/20/2025 then it shows up in the dashboard and the database has the date of 3/21/2025 but the Tasks index.tsx page has the date of the 3/20/2025 Not the 21 how can we fix this?
Sorry but we can't "blindly" debug it for you.
I can zip it up and send it to you just give me a email and I will send it to you. My Email is rhoyle@comcast.net
Sorry Richard but we're not going to spend time comparing your code manually when we provide the repository that you can copy paste in FULL and see if it works.
If we debug for everyone what happens on their local environment, we wouldn't have time to actually publish anything.