Problem
I was juggling tasks across:
Company ClickUp (for team collaboration)
Notion (for personal to-dos and planning)
Google Calendar (from both company & personal emails)
The chaos was real. I was missing due dates, spending too much time jumping between apps, and lacked a single place to glance at all my tasks.
The Solution
I built a read-only personal dashboard that:
Aggregates tasks/events from ClickUp, Notion, and Google Calendar
Groups tasks as Overdue, Due Today, Upcoming by Date, and No Due Date
Runs entirely on Next.js + Edge Functions
Uses no database, just live API reads
Stores my daily tasks that I need to do
Is password protected and deployed on a Vercel subdomain
Here’s how it looks after multiple polishes:
Tech Stack
Frontend: Next.js App Router + React Bootstrap + TanStack Query
API Layer: Vercel Edge Functions
Auth: Cookie-based with middleware protection
Hosting: Vercel subdomain
Core Features
Unified Task View
Each task is grouped into:
Overdue
Due Today / Tomorrow
Upcoming
No Due Date
It pulls data from these 3 APIs:
const [clickup, notion, calendar] = await Promise.all([
fetchClickupTasks(),
fetchNotionTasks(),
fetchCalendarEvents(),
]);
Auth with Middleware
I implemented simple cookie-based authentication to protect my dashboard. The middleware runs on every request and checks for a valid auth cookie before allowing access to protected routes.
// middleware.ts
import { NextRequest, NextResponse } from "next/server";
export function middleware(request: NextRequest) {
const auth = request.cookies.get("auth");
const pathname = request.nextUrl.pathname;
const publicPaths = ["/login", "/api/login", "/api/logout"];
if (publicPaths.includes(pathname)) return NextResponse.next();
if (auth?.value === "1") return NextResponse.next();
if (pathname.startsWith("/api/")) {
return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
}
return NextResponse.redirect(new URL("/login", request.url));
}
export const config = {
matcher: [
"/",
"/dashboard",
"/api/events",
"/api/clickup",
"/api/notion",
"/api/calendar",
],
};
Modular API Fetchers
I kept the codebase clean by separating each API integration into its own module. This makes it easier to maintain and test each data source independently.
// lib/sources/clickup.ts
export async function fetchClickupTasks() {
const res = await fetch("https://api.clickup.com/api/v2/...");
return await res.json();
}
The same goes for Notion & Google Calendar.
Server API Route Example
The backend API routes handle data aggregation from all sources. This route fetches tasks from all three platforms simultaneously and returns them as a unified JSON response.
// app/api/events/route.ts
import { getAllEvents } from "@/lib/getAllEvents";
export async function GET() {
const { clickup, notion, calendar } = await getAllEvents();
return Response.json({ clickup, notion, calendar });
}
Frontend UI
The frontend uses TanStack Query for efficient data fetching with automatic caching and background updates. This ensures the dashboard stays responsive while keeping data fresh.
Using TanStack Query for live fetching and caching:
const { data, isLoading } = useQuery({
queryKey: ["events"],
queryFn: () => fetch("/api/events").then((res) => res.json()),
});
Then we categorize tasks by due date:
const overdue = allTasks.filter(task => isBefore(task.dueDate, today));
const dueToday = allTasks.filter(task => isToday(task.dueDate));
const upcoming = groupByDate(allTasks.filter(...));
const noDueDate = allTasks.filter(task => !task.dueDate);
The dashboard also includes a "Today's Work List" feature where I can curate specific tasks from across all platforms. This has become my morning ritual - selecting what I want to focus on for the day creates clarity and intention around my daily goals.
Deployment
I deployed the app to Vercel and created a subdomain via Hostinger by:
Creating a subdomain DNS record
Adding the domain to Vercel
Setting env variables and password via the Vercel dashboard
No secrets or tasks are stored — it's 100% live.
What’s Next?
I'm happy with the current version, but I could add the following features in the future:
Month View toggle
Desktop notifications for overdue tasks
Auto-refresh every 10 mins
Tauri or Expo wrapper for mobile
Final Thoughts
This project helped me regain clarity over my weekly tasks. I have pinned this dashboard in my browser and open it every morning to immediately see what matters. It's fast, reliable, and mine.
If you're tired of hopping between 5 apps, build something simple that fits your brain.



Top comments (0)