Skip to main content

Data Model

The NordJari database is PostgreSQL (via Supabase), modelled with Prisma.

Entity overview

User
├── UserSeason (join: user × season)
│ └── Habit
│ └── HabitLog
├── UserBadge
├── CoachMessage
├── DailyPromptLog
└── UserConceptMonth
└── (references Concept)

Season
└── SeasonChallenge

Concept
├── ConceptWeek
│ └── ConceptHabit
└── ConceptBadgeCondition

Core tables

User

Stores profile data. leaderboardOptIn controls whether the user appears on the public leaderboard.

Season

Global season records (one per quarter per year). Seeded at the start of each year.

FieldDescription
namee.g. "Nord Q1 2026"
typenord or jari
quarter1–4
startDate / endDateFull quarter date range

UserSeason

Joins a user to a season. Holds their running scorePct and status.

Habit

A single habit within a user's season. Has a targetPerWeek (default 5).

HabitLog

One record per habit per day. status is done, skip, or miss.

  • streakDay tracks the consecutive streak count at the time of logging
  • Unique constraint on (habitId, logDate) — one log per habit per day

Badge

Catalogue of all badges (11 Nord + 11 Jari). Seeded once.

conditionTypeDescription
streak_daysMaintain X consecutive days
season_scoreReach X% season score
habits_completedLog X total done entries
season_finishedComplete all 90 days
all_concepts_completedComplete all 6 concept months (Master badge)

UserBadge

Awarded when a condition is met. Unique on (userId, badgeId, seasonId) — one award per badge per season.

CoachMessage

Stores the AI coach conversation. role is user or assistant. weekKey (e.g. 2026-W20) is set on weekly brief messages to enable deduplication.

DailyPromptLog

One record per user per day per prompt type. promptType is gratitude, creativity, or reflection.

Concept tables

Concept

The 12 concept definitions (6 Nord + 6 Jari). Seeded once. order 1–5 are freely choosable; order 6 is locked until all 5 others are completed.

ConceptWeek

4 weeks per concept, each with a title, goal, and prompt.

ConceptHabit

Habits listed under each week's checklist. Seeded as Habit records when a user starts a concept month.

ConceptBadgeCondition

Defines what the user must achieve to earn a concept badge (e.g. days_outside >= 20).

UserConceptMonth

Tracks which concept a user is running in a given month.

FieldDescription
statusactive, completed, or abandoned
year / monthThe calendar month
completedAtSet when status → completed

Unique on (userId, year, month) — one concept per user per month.

Key constraints

  • Dates stored as UTC
  • logDate fields use @db.Date — date only, no time component
  • Season type (nord/jari) derived from lib/seasons.ts, never hardcoded in components
  • Service role key is server-only — client uses anon key + RLS

See also