Agent skill

practice-gamification

Expert assistant for the daily practice and gamification system in Raamattu Nyt. Covers the shared practice package (packages/shared-practices/), scheduling, streaks, session tracking, completion flow, stats, grand plans, discipleship mode, task reordering, and future rewards/badges. Use when (1) adding or modifying practice features (scheduling, completion, timers), (2) working on streaks, stats, or gamification (badges, rewards, XP), (3) implementing PracticeCompletionScreen or post-completion flows, (4) creating new practice types or content integrations (prayer sets, spiritual paths, calendars), (5) building admin practice management pages, (6) debugging practice session flow or streak calculation issues, (7) working on encouragement messages shown after practice completion, (8) working on grand plans (personal/curated task collections, sort order, auto-linking), (9) working on discipleship mode / cinema task orchestration, (10) working on task reordering on the Tänään page, (11) working on reading duration estimates, (12) working on the "Valitse tehtävä" subscribe section (reading plan picker + practice activation), (13) working on TodayTaskList empty state or useTodayDashboard unified hook. Trigger keywords: practice, streak, gamification, harjoitus, schedule, completion, timer, badge, reward, practice template, prayer set, spiritual path, encouragement, rohkaisu, completion screen, grand plan, discipleship, tänään, task order, reorder, järjestys, cinema tasks, lukuaika, reading duration, valitse tehtävä, FeaturedReadingPlanPicker, useTodayDashboard, empty state.

Stars 0
Forks 0

Install this agent skill to your Project

npx add-skill https://github.com/Spectaculous-Code/raamattu-nyt/tree/main/.claude/skills/practice-gamification

SKILL.md

Practice & Gamification System

Architecture

packages/shared-practices/     Shared monorepo package
├── src/
│   ├── index.ts               Public API (hooks, components, utils)
│   ├── types.ts               All TypeScript interfaces (incl. Grand Plan types)
│   ├── hooks/                  React Query hooks (Supabase RPCs)
│   │   ├── useGrandPlan.ts    Grand plan CRUD + reorder
│   │   ├── useTodayTasks.ts   Unified task builder (practices + plans + prayers)
│   │   └── ...                Practice hooks
│   ├── components/             Shared UI components
│   └── utils/practiceIcons.ts  Icon mapping (PRACTICE_ICON_MAP)
│
DB: practice schema             10+ tables, 20+ RPCs (incl. grand_plan tables)
Apps: apps/raamattu-nyt/        Primary consumer (TanaanPage, CinemaReader, admin)

Key Concepts

Schedule Types

Type Behavior
daily Every day (default)
weekly Specific weekdays [1,3,5] = Mon/Wed/Fri
every_n_days Every N days
rotating_list Cycle through list items
spontaneous No schedule, manual only

Content Types (Polymorphic)

Type Ref points to
prayer_set prayer_sets.id
spiritual_path spiritual_paths.id
prayer prayers.id
prayer_calendar prayer_calendars.id

Session Flow

activate_practice_template(template, schedule, content)
        │
        ▼
get_today_practice_items()  →  TodayPracticeItem[]
        │
        ├─► Quick complete: start + complete in one roundtrip
        │
        └─► Timed session:
            start_practice_session(practice_id)
                │  usePracticeSession (client timer)
                ▼
            complete_practice_session(session_id, notes, metrics)
                │
                ▼
            PracticeCompletionScreen (streak, rhythm, verse)

Streaks

  • DB-computed via get_practice_streaks RPC
  • Schedule-aware: weekly practices don't break on off-days
  • Returns: current_streak, longest_streak, total_completions

Guest Support

All RPCs accept p_guest_session_id. Unauthenticated users work via useGuestSession().

Hooks

Hook Purpose Key RPC
useTodayPractices Today's items + start/complete/quickComplete get_today_practice_items
usePracticeTemplates(systemId) Available templates get_practice_templates
useActivatePractice Activate with schedule + content activate_practice_template
useDeactivatePractice Remove practice deactivate_practice
usePracticeSession Client timer (no DB)
usePracticeHistory(practiceId?) Paginated history get_practice_history
usePracticeStreaks Streaks per practice get_practice_streaks
usePracticeStats(practiceId?) Monthly/yearly stats get_practice_stats
useUpdateSchedule Change schedule update_practice_schedule
useWeeklyRhythm(practiceId) Week completed/scheduled get_weekly_practice_rhythm
usePrayerSets(systemId) Prayer set options get_prayer_sets
useSpiritualPaths(systemId) Spiritual path options get_spiritual_paths
useUserPrayers User's prayers Direct query
useUserPrayerCalendars User's calendars Direct query
useGrandPlan Grand plan CRUD + reorder Multiple RPCs (see Grand Plan)
useTodayTasks Unified task builder — (pure computation)
useDiscipleshipTasks All tasks for discipleship cinema Composes multiple hooks

Components

Component Purpose
PracticeTaskRow Practice row: icon, name, streak badge, quick-complete button, expandable verse
PracticeTimer Running timer with pause/resume
PracticeScheduleEditor Schedule type picker + weekday/interval config
PracticeCompletionScreen Post-completion: checkmark, duration, streak, weekly rhythm bar, encouragement, verse

Encouragement System

Post-completion encouragement messages shown on PracticeCompletionScreen.

DB Table: public.encouragement_messages

Column Type Notes
id uuid PK
system_id text 'raamattu-nyt' default
text_fi text Finnish message
text_en text English (optional)
practice_type text null = generic, or specific type filter
is_active bool
sort_order int

Service: apps/raamattu-nyt/src/lib/encouragementService.ts

  • getRandomEncouragement(systemId, practiceType?, lang) — random active message
  • Admin CRUD: getAllEncouragements, createEncouragement, updateEncouragement, deleteEncouragement, toggleEncouragementActive

Hook: apps/raamattu-nyt/src/hooks/useEncouragementMessage.ts

  • useEncouragementMessage(practiceType?, osisRef?){ message, verseText, verseReference, loading }
  • Verse fallback chain: 1) practice osis_ref, 2) encouragement reading plan verse, 3) daily slogan
  • Encouragement plan configured via app_config.encouragement_reading_plan_id

Admin: AdminPracticesPage.tsx tabs

Tab Component Purpose
Harjoitukset inline Template CRUD
Tilastot inline Aggregate stats
Rukoussetit AdminPrayerSetsTab Prayer set CRUD + items
Hengelliset polut AdminSpiritualPathsTab Spiritual path CRUD
Rohkaisut AdminEncouragementTab Encouragement message CRUD
Rohkaisujae AdminEncouragementVerseTab Reading plan verse config

App-Side Components (not in shared-practices)

Component Location Purpose
PracticeSessionDialog apps/raamattu-nyt/src/components/practice/ Full session dialog: content card, timer, notes, completion screen
PracticeActivationCard same Template activation with schedule + content picker
FeaturedReadingPlanPicker apps/raamattu-nyt/src/components/today/ Top 3 reading plans with join/active badges for "Valitse tehtävä" section
PracticeHistory apps/raamattu-nyt/src/components/profile/ Profile section history list
PracticeStatsCard same Profile section stats card
ProfilePractices same Profile practices section
AdminPracticesPage apps/raamattu-nyt/src/pages/ Full admin page with 6 tabs

Completion Flow (PracticeSessionDialog)

User clicks practice row
    │
    ├─► Quick complete (check button): quickComplete(practiceId)
    │   └─► No dialog, just toggles done
    │
    └─► Timed session (row click): opens PracticeSessionDialog
        │
        ├─ Shows content card (prayer set item / path step / custom)
        ├─ Shows verse text if osis_ref present
        ├─ PracticeTimer with pause/resume
        ├─ Notes textarea
        │
        └─► Complete button:
            completeSession(sessionId, notes, metrics)
                │
                ▼
            PracticeCompletionScreen
            ├─ Duration + streak stats
            ├─ Weekly rhythm bar (X/Y)
            ├─ Encouragement message (random from DB)
            └─ Verse card (osis_ref or encouragement plan)

Implementation Patterns

Adding a New Practice Type

  1. Insert into practice.practice_templates (migration)
  2. Add icon to PRACTICE_ICON_MAP in utils/practiceIcons.ts
  3. If content-linked: add content_type handler in activation flow

Dual-Write Pattern

Prayer practices write to both practice_sessions AND prayer_logs for backward compat. The complete_practice_session RPC handles this internally.

Query Key Conventions

["today-practices", stableId]
["practice-streaks", stableId]
["practice-stats", stableId, practiceId]
["practice-history", stableId, practiceId]
["practice-templates", systemId]
["weekly-practice-rhythm", practiceId, stableId]

Moving Features to shared-practices

  1. Extract hook/component from apps/raamattu-nyt/
  2. Place in packages/shared-practices/src/
  3. Export from index.ts
  4. Update app imports to @shared-practices/...
  5. Avoid app-specific imports (use generic Supabase client pattern)

Grand Plan System

Orchestration layer that groups diverse content types into named task collections with sort order.

Concepts

  • Personal plan: auto-created per user, holds their active practices/plans/prayers
  • Curated plan: admin-created, shared across users (e.g. "30 Days of Prayer")
  • Auto-linking: triggers automatically add/remove items when practices, reading plans, or prayers are activated/deactivated
  • Sort order: grand_plan_items.sort_order defines task display order on Tänään page

DB Tables (practice schema)

Table Purpose
grand_plans Plan metadata: name, type (personal/curated), progression_type, created_by
user_grand_plans Membership: user_id, is_primary, current_day, status
grand_plan_items Tasks: item_type, item_ref (UUID), template_ref, sort_order, day_number

Item types: practice, reading_plan, prayer, prayer_calendar, kooste

RPCs

RPC Purpose
get_or_create_personal_plan(user_id) Returns/creates personal plan UUID
add_item_to_grand_plan(...) Add item with auto sort_order
remove_item_from_grand_plan(plan_id, item_id) Remove item
reorder_grand_plan_items(plan_id, item_ids[]) Batch reorder by array position
get_user_grand_plans() List user's plans with item_count
get_grand_plan_items(plan_id) Items with resolved_name/icon
join_grand_plan(plan_id) Join curated plan

Auto-Link Triggers

Trigger Source table Action
trg_practice_auto_link_grand_plan practice.practices INSERT Add item_type='practice'
trg_practice_auto_unlink_grand_plan practice.practices UPDATE (deactivation) Delete item
trg_reading_plan_auto_link_grand_plan bible_schema.user_reading_plans INSERT Add item_type='reading_plan'
trg_reading_plan_auto_unlink_grand_plan bible_schema.user_reading_plans UPDATE Delete item
trg_prayer_auto_link_grand_plan public.prayers INSERT (status='active') Add item_type='prayer'
trg_prayer_auto_unlink_grand_plan public.prayers UPDATE (deactivated) Delete item

useGrandPlan Hook

typescript
const { plan, items, reorderItems, addItem, removeItem, loading } = useGrandPlan();
// reorderItems(itemIds[]) — optimistic UI + invalidates cache

Admin

AdminGrandPlansTab in AdminPracticesPage — CRUD for plans and items, accessed via admin RPCs.

Task Reordering (Tänään Page)

Users reorder tasks via useTodayDashboard().reorderItems.

Flow

TanaanPage
├── useTodayDashboard() → { items, reorderItems }
│   items already sorted by sort_order from the RPC
└── Pass onReorder to TodayTaskList

TodayTaskList
├── "Muokkaa järjestystä" toggle → shows ChevronUp/Down per row
└── handleSwap → collect item_ids in new order → onReorder(itemIds)
    └── reorderItems(itemIds) → reorder_grand_plan_items RPC + cache invalidation

Discipleship Mode

Full-screen Cinema Reader orchestration for structured daily routines.

Entry Point

TanaanPage → two cinema buttons → CinemaReaderScreen with discipleshipTasks

Two Launch Modes

Mode Button Props Behavior
Reading Plan Cinema Blue Play discipleshipTasks={readingPlanCinemaTasks} autoStart Auto-plays reading plans only. No task selector, no quizzes, no transitions.
Discipleship Cinema Amber Clapperboard discipleshipTasks={allTasks} Full flow: task selector → reading + prayer + practice → quizzes → kooste.

Hook: useDiscipleshipTasks

Location: apps/raamattu-nyt/src/hooks/useDiscipleshipTasks.ts

Composes multiple data sources into UnifiedTodayTask[]:

  • Active practices (filtered: no prayer_set, no empty prayer_calendar)
  • Active reading plans (with duration from useReadingPlanDurations, passes progression_type)
  • Today's scheduled prayers
  • Appends "Pohdittavat jakeet" (kooste) task at the end (NYT_KOOSTE_TASK_ID)

useTodayTasks (Unified Task Builder)

Location: packages/shared-practices/src/hooks/useTodayTasks.ts

Normalizes heterogeneous sources into UnifiedTodayTask[]:

  • ID format: rp-{id}, pr-{id}, py-{id}
  • Duration: reading plans use estimateReadingMinutes(verseCount), practices use template default, prayers use fallback
  • ReadingPlanInput accepts optional progression_type for correct completion detection
  • isCompletedToday for reading plans: Checks completed_days.includes(current_day) OR for completion-type plans (not calendar/day_of_year) also checks completed_days.includes(current_day - 1) because mark_reading_day_complete advances current_day immediately
  • Returns: { tasks, completed, uncompleted, totalDurationMinutes }

CinemaReaderScreen Discipleship Flow

Open with discipleshipTasks + optional autoStart/initialTask
    │
    ├─► autoStart=true → queue all uncompleted, start first
    │   No selector, no toggle, no quizzes, no transitions
    │
    ├─► initialTask provided → start immediately
    │
    └─► No initialTask → show DiscipleshipTaskSelector
         │
         ▼
    Task execution:
    ├─ reading_plan → fetch verses, display in cinema
    │   → mark_reading_day_complete RPC on finish
    │   → auto-insert memory quiz after (discipleship only, NOT autoStart)
    │   → quiz prioritizes user-marked verses (marked_refs from VerseBar)
    ├─ practice/prayer → DiscipleshipInlineTask (inline view)
    └─ kooste → load accumulated verse refs, display in cinema
         │
         ▼
    Task complete → DiscipleshipTransitionOverlay (auto-skipped in autoStart)
    ├─ Show completion summary
    ├─ "Next task" / free task selection
    └─ Skip completed tasks in queue

Reading Plan Completion Gotcha

mark_reading_day_complete advances current_day immediately (5→6). This affects:

  1. Dashboard (get_today_dashboard): For completion-type plans, checks completed_at::date = CURRENT_DATE (not current_day)
  2. Client (useTodayTasks): Also checks completed_days.includes(current_day - 1) for completion-type plans
  3. Query invalidation: Must invalidate ["today-dashboard"], ["reading-plan-streaks"], AND ["user-reading-plans"]

Quiz Marked Verse Priority

When user marks verses via DiscipleshipVerseBar (add to Nyt Kooste), those refs are passed as marked_refs in the quiz task metadata. useReadingPlanQuiz prioritizes them:

  • 2+ marked in plan → both quiz verses from marked
  • 1 marked → first from marked, second from remaining
  • 0 marked → prefer NT/Psalms/Proverbs

Reading Duration Estimates

Location: apps/raamattu-nyt/src/lib/readingDuration.ts

  • countVersesFromReadings(readings) — total verses across references (handles multi-chapter ranges)
  • estimateReadingMinutes(verseCount)Math.max(1, Math.round(verseCount * 0.13)) (~8 sec/verse)
  • Used by useReadingPlanDurations hook to compute per-plan estimated minutes

Tänään Page Integration

The Tänään (Today) page (TanaanPage.tsx) is the primary consumer of practice data.

Architecture

TanaanPage
├── useTodayDashboard() → unified dashboard (replaces ~13 hooks)
│   └── get_today_dashboard RPC → DashboardItem[] (practices, plans, prayers, kooste)
├── useDiscipleshipTasks() → tasks for Cinema discipleship mode
├── Daily tasks section (#today)
│   └── TodayTaskList
│       ├── Empty state: "Ei tehtäviä" → scrolls to #subscribe
│       ├── Uncompleted tasks ("Mahdollisuudet")
│       ├── "Pohdittavat jakeet" (kooste) row
│       └── Completed tasks ("Tehty tänään")
│       ├── Blue Play button → Reading Plan Cinema (autoStart)
│       └── Amber Clapperboard → Discipleship Cinema
├── Tomorrow preview (#tomorrow)
│   └── TomorrowTasksSection
├── Permanent prayers (#permanent)
│   └── PermanentTasksSection (priority_level='always')
├── Recurring tasks (#recurring)
│   └── RecurringTasksSection
└── "Valitse tehtävä" section (#subscribe)
    ├── Always visible (not gated on templates.length)
    ├── Auto-opens when user has zero dashboard items
    ├── FeaturedReadingPlanPicker (top 3 global plans, "Aloita"/"Aktiivinen")
    └── Harjoitukset (PracticeActivationCard per template)

Unified Dashboard Hook: useTodayDashboard

Location: apps/raamattu-nyt/src/hooks/useTodayDashboard.ts

Replaces the old pattern of calling useGrandPlan + useTodayPractices + useReadingPlans separately. Single RPC get_today_dashboard(p_grand_plan_id) returns DashboardItem[] with:

  • item_type: practice | reading_plan | prayer | kooste | mini_task
  • is_completed_today, current_streak, sort_order
  • metadata object varies by item_type (see DashboardPracticeMetadata, etc.)

Provides converter functions: toPracticeItem(), toReadingPlan(), toPrayer().

"Valitse tehtävä" Section

Renamed from "Valitse harjoitus". Contains two sub-sections:

  1. LukusuunnitelmatFeaturedReadingPlanPicker component

    • Shows top 3 available plans from useReadingPlans().availablePlans
    • "Aloita" button calls joinPlanWithCheck, shows "Aktiivinen" badge if joined
    • Waits for userPlansLoading before rendering to avoid false "Aloita" state
    • "Näytä kaikki" link → /reading-plans
    • onPlanJoined callback invalidates dashboard
  2. Harjoitukset — existing PracticeActivationCard list (unchanged)

prayer_calendar Content Type Gotcha

Prayer calendar items always resolve the latest active prayer from the calendar, not a today-scheduled prayer. The RPC fetches prayers where calendar_id = content_ref and status = 'active', ordered by most recent.

get_practice_items_for_date RPC

Parameterized version of get_today_practice_items that accepts a target date:

typescript
const { data } = useTodayPractices();      // today (no date param)
const { data } = useTomorrowPractices();    // calls get_practice_items_for_date with tomorrow

Future: Rewards & Badges (Not Yet Implemented)

Design considerations:

  • Streak milestones (7, 30, 100 days) trigger rewards
  • Badges for completing spiritual paths
  • XP from session metrics (duration, verse count)
  • Tables: practice.user_rewards, practice.user_badges
  • PracticeCompletionScreen already shows streak — extend with badge unlock

DB Reference

See references/db-schema.md for full table and RPC reference.

Expand your agent's capabilities with these related and highly-rated skills.

Spectaculous-Code/raamattu-nyt

docs-updater

Expert assistant for keeping documentation synchronized with code changes in the KR92 Bible Voice project. Use when updating API docs, maintaining architecture diagrams, syncing README, updating CLAUDE.MD, or generating documentation from code.

0 0
Explore
Spectaculous-Code/raamattu-nyt

ai-prompt-manager

Expert assistant for managing AI prompts, features, and configuration in the KR92 Bible Voice AI system. Use when creating AI prompts, configuring AI features, managing prompt versions, setting up AI bindings, or working with AI pricing and models.

0 0
Explore
Spectaculous-Code/raamattu-nyt

performance-auditor

Expert assistant for monitoring and optimizing performance in the KR92 Bible Voice project. Use when analyzing query performance, optimizing database indexes, reviewing React Query caching, monitoring AI call costs, or identifying N+1 queries.

0 0
Explore
Spectaculous-Code/raamattu-nyt

edge-function-generator

Expert assistant for creating and maintaining Supabase Edge Functions for the KR92 Bible Voice project. Use when creating Edge Functions, setting up CORS, integrating shared modules, adding JWT validation, or configuring environment variables.

0 0
Explore
Spectaculous-Code/raamattu-nyt

admin-panel-builder

Expert assistant for creating and maintaining admin panel pages in the KR92 Bible Voice project. Use when creating admin pages, building admin components, integrating with admin navigation, or adding admin features.

0 0
Explore
Spectaculous-Code/raamattu-nyt

lint-fixer

Expert assistant for analyzing and fixing linting and formatting issues in the KR92 Bible Voice project using Biome and TypeScript. Use when fixing lint errors, resolving TypeScript issues, applying code formatting, or reviewing code quality.

0 0
Explore

Didn't find tool you were looking for?

Be as detailed as possible for better results