All work

02AI learning planner

SkillSync AI

A learning planner that turns a goal like "learn Python" into a timetable that fits the hours a person actually has — then keeps it honest as real life moves things around.

Next.jsSupabasePostgreSQLGemini AIGoogle Calendar APIBrevoTailwind CSSPWAVercel
SkillSync AI learning planner showing a generated timetable with milestones

Overview

A learning planner that turns a goal like "learn Python" into a timetable that fits the hours a person actually has — then keeps it honest as real life moves things around.

  • Generates personalised timetables that fit a user's stated daily schedule.
  • Milestone tracking, streaks and auto-rescheduling when sessions slip.
  • Google Calendar sync, email reminders via Brevo, and offline PWA support.

The problem

Most learning plans fail on scheduling, not motivation. Generic course roadmaps assume free evenings that learners do not have, so the first missed session collapses the whole plan and there is no mechanism to recover.

Who it is for

Self-directed learners studying around a job or a degree, who need a plan shaped around their real week.

My responsibilities

  • Built the full application: schedule modelling, plan generation, persistence and UI.
  • Designed the prompt and generation flow that turns a goal plus a stated routine into a milestone-based timetable.
  • Implemented Google Calendar sync, Brevo email reminders, streak tracking and PWA offline support.
  • Modelled users, plans, milestones and progress in Supabase/PostgreSQL.

Product & technical decisions

The model plans; the app owns the schedule

The LLM is good at decomposing a goal into milestones and bad at being a source of truth. Generated plans are persisted as structured rows in PostgreSQL, so rescheduling, streaks and progress are computed by application logic — deterministic and inspectable — not re-asked of the model.

Supabase for auth, database and storage together

One managed service covering Postgres, authentication and row-level security removed a whole class of glue code and let the schema carry the access rules.

Meet users in their existing calendar

A plan that lives only inside its own app gets forgotten. Calendar sync plus email reminders puts sessions into tools the learner already checks daily.

PWA over native

Installability and offline access were the only mobile-specific requirements, and a PWA delivers both from the same Next.js codebase.

Architecture

How the pieces fit together, from the surface the user touches down to the sources of truth.

Client

  • Next.js app
  • PWA shell

Server

  • Route handlers
  • Plan generator
  • Scheduler

Data

  • Supabase Auth
  • PostgreSQL

Integrations

  • Gemini AI
  • Google Calendar
  • Brevo email

InterfaceApplication logicData & authThird-party

Key functionality

Schedule-aware plan generation

The user describes their real routine and picks a goal; Gemini generates a timetable that fits the gaps, broken into milestones with suggested resources rather than one undifferentiated wall of topics.

Auto-rescheduling

Missing a session rewrites the plan forward instead of leaving the learner behind a plan they can no longer follow — the failure mode that kills most study schedules.

Calendar sync and reminders

Sessions push into Google Calendar so the plan lives where the user already looks, with Brevo handling automated email nudges.

Streaks and progress

Milestone completion and streak tracking give the plan a feedback loop, so progress is visible rather than assumed.

Installable and offline-capable

Built as a PWA so the plan is reachable on a phone without a connection.

What went wrong, and what I did

An auth state boundary between Supabase and the client store

Constraint
Google OAuth created a valid Supabase session, but the client-side Zustand store stayed empty after the callback. The auth provider and the interface could therefore disagree about whether someone was signed in.
Wrong turn
My first callback handled the OAuth code in a Next.js server route and redirected straight to the dashboard. The Supabase exchange succeeded, but a server route has no way to populate browser state — so the protected UI loaded before it knew who the user was.
What I shipped
Replaced the server-only callback with a client callback page that reads the authorization code, calls exchangeCodeForSession, falls back to getSession, writes the authenticated user into Zustand, and only then redirects — returning users to login with an explicit error when any step fails.
Effect
Sign-in now lands in the dashboard with the Supabase session and the application state established together, instead of briefly treating a successfully authenticated user as logged out.

Where it stands

SkillSync AI is deployed and publicly usable — the full feature set above is live rather than a prototype.

Open the live product(opens in a new tab)

What I would improve next

  • Let learners edit a generated plan directly and have the scheduler respect the edit.
  • Spaced-repetition review sessions layered onto the milestones.
  • Shared plans so two people can study the same goal in parallel.