Reward - Kids Reward System: Product Requirements Document
Version: 1.0
Date: 2026-04-14
Original Stack: Python/Flask + SQLAlchemy + SQLite + Jinja2 + Bootstrap 5
Table of Contents
- Product Overview
- User Roles & Authentication
- Data Models & Schema
- Core Features
- Gamification System
- Approval Workflows
- Airdrops (Bonus Missions)
- Shareable Rewards
- Image Upload & Custom Rewards
- Internationalization (i18n)
- Progressive Web App (PWA)
- API Endpoints Reference
- UI/UX Specifications
- Business Rules Summary
- Planned / Non-Implemented Features
- Security Considerations
- Deployment & Infrastructure
1. Product Overview
Reward is a gamified reward and motivation system designed for young children (ages 4-6). It enables parents to define positive behaviors and rewards, while children interact with a playful, hero-themed interface to earn “stars” (coins) and redeem them for treasures (rewards).
Key Concepts
| Term in App |
Internal Name |
Description |
| Stars |
coins |
Virtual currency earned by kids for good behaviors |
| Treasures / Gifts |
rewards |
Items kids can redeem using their stars |
| Hero Medals |
badges |
Achievement medals earned at coin milestones |
| Super Days |
streak |
Consecutive days with at least one confirmed claim |
| Special Tasks |
goals |
Behavior targets (e.g., “brush teeth 5 times”) |
| Airdrops |
airdrops |
Limited-slot bonus missions created by parents |
| Hero Dashboard |
Kid Dashboard |
The kid’s personal progress page |
| Treasure Shop |
Rewards Page |
Where kids browse and redeem rewards |
| Parents Panel |
Settings Page |
Admin panel for parents (PIN-protected) |
Design Philosophy
- Kid-friendly: Large buttons, emojis, bright colors, animations, sound effects, voice feedback
- Parent-controlled: All coin awards and deductions require parent approval
- Bilingual: Full English and Arabic (RTL) support
- Offline-capable: PWA with service worker caching
- Claymorphism design: Soft 3D shadows, rounded corners, pastel gradients
2. User Roles & Authentication
2.1 Roles
| Role |
Description |
Access |
| Kid |
Child user who claims behaviors and redeems rewards |
Kid dashboard, treasure shop, home page |
| Parent |
Admin who manages the system and approves actions |
Settings panel (PIN-protected) |
| Anonymous |
Unauthenticated visitor |
PIN login screen only (if site locked) |
2.2 Authentication System
Site Access (Cookie-Based)
- A cookie named
reward_access with value granted is required to access the site
- Cookie has a 30-day expiration (
max_age=2592000)
- Set after successful PIN entry on the unlock page
- Without this cookie, all routes redirect to the login screen (except
/unlock and static files)
Parent Authentication (Session-Based)
- Parents authenticate via a numeric PIN stored in the
Setting table (key='parent_pin')
- On successful PIN entry,
session['parent_authenticated'] = True is set
- Required for: confirming claims, confirming redemptions, confirming airdrops, all delete operations, managing kids/behaviors/rewards
- Logout clears the session key
PIN Setup
- If no PIN exists in the database, the settings page is accessible without authentication
- The parent sets their PIN on first access
- PIN is stored as plaintext in the
Setting table (not hashed)
2.3 Site Lock
- A setting (
key='site_lock') toggles site-wide access
- When locked (
value='true'): all visitors must enter PIN to access any page
- When unlocked (
value='false'): the site is open to anyone with the reward_access cookie
- Toggle is available in the parent settings panel
3. Data Models & Schema
3.1 Kid
| Field |
Type |
Constraints |
Default |
Description |
| id |
Integer |
PK, Auto-increment |
- |
Unique identifier |
| name |
String(50) |
NOT NULL |
- |
Child’s display name |
| emoji |
String(10) |
- |
'👶' |
Avatar emoji |
| gender |
String(10) |
- |
'male' |
'male' or 'female' - used for reward filtering |
| coins |
Integer |
- |
0 |
Current star balance |
| target_reward_id |
Integer |
FK → Reward.id, Nullable |
NULL |
The reward the kid is currently working toward |
Relationships: Has many Claims, Goals, RewardImages, RewardRedemptions, KidBadges, AirdropClaims
3.2 Behavior
| Field |
Type |
Constraints |
Default |
Description |
| id |
Integer |
PK |
- |
Unique identifier |
| description |
String(200) |
NOT NULL |
- |
What the behavior is (e.g., “Brush teeth”) |
| emoji |
String(10) |
- |
'⭐' |
Visual icon for the behavior |
| coin_value |
Integer |
- |
1 |
Stars earned when this behavior is confirmed |
Note: A category column (VARCHAR, default 'general') is added via schema sync but not actively used in the model definition.
3.3 Reward
| Field |
Type |
Constraints |
Default |
Description |
| id |
Integer |
PK |
- |
Unique identifier |
| name |
String(100) |
NOT NULL |
- |
Reward display name |
| details |
Text |
Nullable |
- |
Optional description |
| coin_cost |
Integer |
NOT NULL |
- |
Stars required to redeem |
| image_path |
String(255) |
Nullable |
- |
Relative path to reward image |
| suitable_male |
Boolean |
- |
True |
Show this reward to boys |
| suitable_female |
Boolean |
- |
True |
Show this reward to girls |
| target_count |
Integer |
- |
1 |
Number of completions for milestone tracking |
| category |
String(50) |
- |
'general' |
'toy', 'activity', 'snack', 'other', 'general', 'custom' |
| is_milestone |
Boolean |
- |
False |
Priority reward for goal auto-selection |
| is_custom |
Boolean |
- |
False |
Created by kid via image upload |
| is_shareable |
Boolean |
- |
False |
Can be redeemed by multiple kids together |
| min_participants |
Integer |
- |
2 |
Minimum kids needed for shareable rewards |
3.4 Claim
| Field |
Type |
Constraints |
Default |
Description |
| id |
Integer |
PK |
- |
Unique identifier |
| kid_id |
Integer |
FK → Kid.id, NOT NULL |
- |
The claiming kid |
| behavior_id |
Integer |
FK → Behavior.id, NOT NULL |
- |
The behavior claimed |
| status |
String(20) |
- |
'pending' |
'pending' or 'confirmed' |
| created_at |
DateTime |
- |
UTC now |
When the claim was made |
3.5 Goal
| Field |
Type |
Constraints |
Default |
Description |
| id |
Integer |
PK |
- |
Unique identifier |
| kid_id |
Integer |
FK → Kid.id, NOT NULL |
- |
The kid this goal belongs to |
| behavior_id |
Integer |
FK → Behavior.id, NOT NULL |
- |
The behavior to track |
| target_count |
Integer |
- |
5 |
How many times the behavior must be confirmed |
| current_count |
Integer |
- |
0 |
Progress toward the target |
| created_at |
DateTime |
- |
UTC now |
When the goal was created |
Business Rule: When a claim is confirmed for a behavior that has an active goal for that kid, current_count is incremented by 1.
3.6 RewardImage
| Field |
Type |
Constraints |
Default |
Description |
| id |
Integer |
PK |
- |
Unique identifier |
| kid_id |
Integer |
FK → Kid.id, NOT NULL |
- |
The kid who uploaded |
| image_path |
String(255) |
NOT NULL |
- |
Relative path to image file |
| reward_id |
Integer |
FK → Reward.id, Nullable |
- |
Associated custom reward (if created) |
| created_at |
DateTime |
- |
UTC now |
Upload timestamp |
3.7 RewardRedemption
| Field |
Type |
Constraints |
Default |
Description |
| id |
Integer |
PK |
- |
Unique identifier |
| kid_id |
Integer |
FK → Kid.id, NOT NULL |
- |
The initiating kid |
| reward_id |
Integer |
FK → Reward.id, NOT NULL |
- |
The reward being redeemed |
| status |
String(20) |
- |
'pending' |
'pending' or 'confirmed' |
| is_shared |
Boolean |
- |
False |
Whether this is a shared redemption |
| participant_ids |
Text |
Nullable |
- |
JSON array of kid IDs (for shared rewards) |
| cost_per_kid |
Integer |
Nullable |
- |
Split cost for shared rewards |
| created_at |
DateTime |
- |
UTC now |
When redemption was initiated |
3.8 Badge
| Field |
Type |
Constraints |
Default |
Description |
| id |
Integer |
PK |
- |
Unique identifier |
| name |
String(50) |
NOT NULL |
- |
Badge display name |
| emoji |
String(10) |
Nullable |
- |
Badge emoji |
| image_path |
String(255) |
Nullable |
- |
Optional badge image |
| required_coins |
Integer |
- |
0 |
Total coins required to earn |
| description |
String(200) |
Nullable |
- |
Achievement description |
Default Badges (auto-created if none exist):
| Name |
Emoji |
Required Coins |
Description |
| Novice Hero |
🌟 |
10 |
“Earned your first 10 stars!” |
| Super Star |
⭐ |
50 |
“Earned 50 stars!” |
| Golden Hero |
🏆 |
100 |
“Earned 100 stars!” |
| Legendary |
👑 |
500 |
“Earned 500 stars!” |
3.9 KidBadge
| Field |
Type |
Constraints |
Default |
Description |
| id |
Integer |
PK |
- |
Unique identifier |
| kid_id |
Integer |
FK → Kid.id, NOT NULL |
- |
The kid who earned it |
| badge_id |
Integer |
FK → Badge.id, NOT NULL |
- |
The badge earned |
| earned_at |
DateTime |
- |
UTC now |
When the badge was earned |
3.10 Setting
| Field |
Type |
Constraints |
Default |
Description |
| id |
Integer |
PK |
- |
Unique identifier |
| key |
String(50) |
UNIQUE, NOT NULL |
- |
Setting identifier |
| value |
String(255) |
- |
- |
Setting value |
Known Keys:
| Key |
Values |
Description |
parent_pin |
Numeric string |
Parent authentication PIN |
language |
'en', 'ar' |
UI language preference |
site_lock |
'true', 'false' |
Whether site requires PIN to access |
3.11 Airdrop
| Field |
Type |
Constraints |
Default |
Description |
| id |
Integer |
PK |
- |
Unique identifier |
| title |
String(200) |
NOT NULL |
- |
Mission title |
| description |
Text |
Nullable |
- |
Mission details |
| coins |
Integer |
NOT NULL |
10 |
Star reward for completion |
| slots |
Integer |
- |
1 |
Max number of kids who can claim |
| is_active |
Boolean |
- |
True |
Whether the airdrop is available |
| emoji |
String(10) |
- |
'🔥' |
Display emoji |
| created_at |
DateTime |
- |
UTC now |
Creation timestamp |
3.12 AirdropClaim
| Field |
Type |
Constraints |
Default |
Description |
| id |
Integer |
PK |
- |
Unique identifier |
| airdrop_id |
Integer |
FK → Airdrop.id, NOT NULL |
- |
The airdrop being claimed |
| kid_id |
Integer |
FK → Kid.id, NOT NULL |
- |
The claiming kid |
| status |
String(20) |
- |
'claimed' |
'claimed' → 'completed' → 'confirmed' |
| claimed_at |
DateTime |
- |
UTC now |
When the kid claimed the airdrop |
| completed_at |
DateTime |
Nullable |
- |
When the kid marked it as done |
| confirmed_at |
DateTime |
Nullable |
- |
When the parent confirmed completion |
4. Core Features
4.1 Kid Management (Parent)
- Add Kid: Name, emoji avatar, gender (boy/girl)
- Edit Kid: Update name, emoji, gender
- Delete Kid: Cascading delete of all associated records (claims, redemptions, goals, reward images)
- Kids appear as cards on the home page with their emoji and current coin balance
4.2 Behavior Management (Parent)
- Add Behavior: Description, emoji, coin value (minimum 1)
- Edit Behavior: Update description, emoji, coin value
- Delete Behavior: Cascading delete of all associated goals and claims
- Behaviors appear as quick-action buttons on the kid dashboard
4.3 Reward Management (Parent)
- Add Reward: Name, coin cost, optional image upload, gender suitability flags, milestone flag, shareable flag
- Edit Reward: Update all fields including image replacement
- Delete Reward: Cascading delete of all associated redemptions
- Rewards are filtered by kid’s gender when displayed in the treasure shop
4.4 Behavior Claiming (Kid)
- Kid taps a behavior button on their dashboard
- System creates a
Claim record with status='pending'
- Sound effect plays, speech synthesis announces the behavior
- Celebration animation (confetti + star animation) displays
- Claim appears in “Waiting for Approval” section on kid dashboard
- Claim appears in parent’s “Stars to Approve” queue
Constraints:
- Maximum 10 pending claims per kid at any time
- Kid can cancel their own pending claims
4.5 Reward Redemption (Kid)
- Kid browses the treasure shop (filtered by gender)
- Affordable rewards have green border; unaffordable are grayscaled
- Kid taps a reward → confirmation modal appears
- If kid has enough coins →
RewardRedemption created with status='pending'
- If not enough coins → option to “Set as My Goal” (sets
target_reward_id)
- Parent sees pending redemption in “Rewards to Hand Out” queue
Coin Check: kid.coins >= reward.coin_cost
4.6 Goal Tracking
- Parent or kid can create goals linking a behavior to a target count
- When a claim is confirmed for a behavior with an active goal,
goal.current_count increments
- Progress displayed as percentage bar on kid dashboard
- Trophy emoji shown when
current_count >= target_count
- Goals can be deleted by parents
4.7 Weekly Statistics
- Bar chart showing last 7 days of confirmed claim counts
- Each day shows the count of confirmed claims for that kid
- Days labeled with abbreviated names (SUN-SAT)
- Displayed on kid dashboard
4.8 Today/Weekly Coin Tracking
today_coins: Sum of all behavior coin values from confirmed claims today
weekly_coins: Sum of all behavior coin values from confirmed claims in the last 7 days
total_earned: Lifetime sum of all confirmed claim coin values
total_redeemed: Count of confirmed redemptions
5. Gamification System
5.1 Rank System
Based on total accumulated coins (lifetime, not current balance):
| Rank |
Translation Key |
Coin Threshold |
| Small Hero |
RANK_NOVICE |
0+ |
| Super Hero |
RANK_APPRENTICE |
50+ |
| Super Star |
RANK_HERO |
100+ |
| Golden Hero |
RANK_CHAMPION |
250+ |
| Mega Legend |
RANK_LEGEND |
500+ |
5.2 Level System
level = (kid.coins // 10) + 1
Level is derived from current coin balance divided by 10, plus 1.
5.3 Streak System
- A streak counts consecutive days where the kid has at least one confirmed claim
- Calculated by looping backward from today, up to 100 days
- Breaks when a day has zero confirmed claims (except day 0)
- Different narrative messages at streak milestones:
| Streak |
Message Key |
English Text |
| 1 |
STREAK_STORY_1 |
“The journey begins! You’re a hero in training.” |
| 3 |
STREAK_STORY_3 |
“The forest is cheering! You’ve found the magic path.” |
| 5 |
STREAK_STORY_5 |
“The castle is near! You’re showing great power.” |
| 7 |
STREAK_STORY_7 |
“The dragon is a friend! You are a true champion.” |
| 10 |
STREAK_STORY_10 |
“You’re flying with the stars! Absolute legend.” |
| 30 |
STREAK_STORY_30 |
“You are the king of the hero world! Unstoppable!” |
5.4 Badge System
- Badges are auto-awarded when a kid’s total coins reach a threshold
- Checked every time a claim is confirmed (
check_for_badges())
- 4 default badges are created automatically if none exist in the database
- When new badges are earned, a modal notification is displayed on the kid dashboard
- Badges are displayed in the “My Collection” section
Badge Award Logic:
- Query all badges where
required_coins <= kid.coins
- Exclude badges the kid already has (via
KidBadge table)
- Create
KidBadge entries for newly qualified badges
- Return list of newly earned badges for UI notification
5.5 Next Reward / Progress Tracking
Auto-selection logic (if no target reward set):
- Filter rewards by kid’s gender
- Prefer rewards marked as
is_milestone=True
- Sort by
coin_cost ascending
- Find first reward with
coin_cost > kid.coins (next achievable)
- If all rewards are affordable, use the most expensive one
Progress calculation:
progress = min(100, (kid.coins / next_reward.coin_cost) * 100)
Visual progress tracker: 5 checkpoint nodes at 20% intervals, with star emojis for completed checkpoints and empty circles for remaining.
6. Approval Workflows
All coin-affecting operations require parent confirmation. There are three parallel workflows:
6.1 Behavior Claim Workflow
Kid claims behavior → Claim(status='pending')
→ Parent views in settings queue
→ Parent confirms → status='confirmed'
→ kid.coins += behavior.coin_value
→ goal.current_count += 1 (if goal exists)
→ check_for_badges(kid)
6.2 Reward Redemption Workflow
Kid redeems reward → RewardRedemption(status='pending')
→ Parent views in settings queue
→ Parent confirms → status='confirmed'
→ kid.coins -= reward.coin_cost
6.3 Airdrop Workflow
Kid claims airdrop → AirdropClaim(status='claimed')
Kid marks done → status='completed'
→ Parent views in settings queue
→ Parent confirms → status='confirmed'
→ kid.coins += airdrop.coins
6.4 Parent Confirmation UX
- Double-click safety: First click changes button text to “Are you sure?”, second click confirms
- All confirmation actions are POST requests returning JSON responses
- Parent must be authenticated (
session['parent_authenticated'])
7. Airdrops (Bonus Missions)
7.1 Concept
Airdrops are special, high-value, limited-slot bonus tasks created by parents. They appear on the home page for all kids to see and claim.
7.2 Parent Management
- Create: Title, description, coin reward, slot count, emoji
- Edit: Update all fields
- Delete: Cascading delete of all associated claims
- Default values: coins=10, slots=1, emoji=‘🔥’, is_active=True
7.3 Kid Interaction
- Airdrops appear on the home page with available slot count
- Kid selects “Claim Now” → modal asks which kid is claiming
- System validates: no duplicate claims, slots available
- Claim created with
status='claimed'
- Airdrop appears in kid’s “Active Airdrops” section on dashboard
- Kid marks as “I Finished It!” →
status='completed'
- Parent confirms → coins awarded
7.4 Constraints
- One claim per kid per airdrop (enforced by query)
- Maximum claims limited by
airdrop.slots
- Available slots displayed as:
slots - count(claims)
- Only active airdrops (
is_active=True) shown on home page
8. Shareable Rewards
8.1 Concept
Shareable rewards allow multiple kids to split the cost of a single reward they enjoy together (e.g., a box of donuts).
8.2 Initiation Flow
- Kid selects a shareable reward in the treasure shop
- Cost is split:
cost_per_kid = reward.coin_cost // 2 (hardcoded division by 2)
- Kid must have
coins >= cost_per_kid to initiate
RewardRedemption created with:
is_shared=True
participant_ids=JSON([kid_id])
cost_per_kid calculated value
8.3 Join Flow
- Pending shared rewards appear on the home page for all kids
- System polls
/get_shared_rewards endpoint to fetch pending shared redemptions
- Another kid can “Join” the shared reward
- Join validates: kid has
coins >= cost_per_kid, kid not already in participants
- Kid ID appended to
participant_ids JSON array
8.4 Confirmation Flow
- Parent sees shared redemption in settings queue
- On confirm: cost recalculated as
reward.coin_cost // len(participants)
- Validation: all participants must have enough coins
- If any participant lacks coins → error with that kid’s name
- If all valid → deduct
cost_per_kid from each participant
8.5 Current Limitation
- Cost split is hardcoded to divide by 2 on initiation (not dynamic based on actual participant count)
- On confirmation, cost is recalculated based on actual participant count
- The
min_participants field on Reward exists but is not enforced in the redemption flow
9. Image Upload & Custom Rewards
9.1 Kid Upload Flow
- Kid accesses upload form on their dashboard (album section)
- Provides: image file, reward name, coin cost, category, milestone flag
- System validates file extension:
{png, jpg, jpeg, gif}
- Filename secured:
kid_{kid_id}_{timestamp}_{original_filename}
- Image processed with Pillow: thumbnail to 800x800, quality 85, optimized
- Saved to:
static/images/rewards/
- Creates a
Reward record with is_custom=True and gender flags based on kid’s gender
- Creates a
RewardImage record linking to the reward
9.2 Parent Delete
- Parent can delete reward images
- If the associated reward is custom (
is_custom=True), it cascading deletes:
- All
RewardRedemption records for that reward
- The
Reward record itself
- The
RewardImage record
- Requires parent authentication
9.3 Album Display
- Kid dashboard shows uploaded images in a 2-column grid
- Images use lazy loading (
loading="lazy")
- Ordered by
created_at descending
10. Internationalization (i18n)
10.1 Supported Languages
| Code |
Language |
Direction |
Translation Keys |
en |
English |
LTR |
92 keys |
ar |
Arabic |
RTL |
207 keys |
10.2 Implementation
- Translations stored as JSON files in
app/translations/{lang}.json
- Language preference stored in
Setting table (key='language')
- Loaded on every request via
before_app_request hook
- Available in templates as
{{ t.KEY_NAME }}
- RTL flag (
g.rtl) set to True when language is Arabic
- Templates use
dir="{{ 'rtl' if rtl else 'ltr' }}" on HTML elements
- Fallback: if a language file is missing, English is loaded
10.3 Language Switching
- Dropdown in parent settings panel (English/Arabic with flag emojis)
- POST action
set_lang updates or creates the language setting
- Takes effect immediately on page reload
10.4 Voice/Speech
- JavaScript
speechSynthesis API used for voice feedback
- Language set to Arabic (
ar-SA) when RTL, English otherwise
- Used for: behavior claim announcements, reward reached notifications, streak celebrations
11. Progressive Web App (PWA)
11.1 Manifest
- App Name: “Reward System”
- Display: Standalone (full-screen app behavior)
- Theme Color:
#ff7e5f (coral)
- Background:
#fff5f0 (warm cream)
- Icons: Referenced from
/static/images/logo.png
11.2 Service Worker
- Cache Name:
reward-v1
- Strategy:
- Static assets (CSS, JS, images, sounds): Cache-first
- HTML/navigation: Network-first with cache fallback
- Registration: On page load in all templates
11.3 Install Prompt
- Captured via
beforeinstallprompt event
- Shown as a sticky banner on the home page
- Text: “Install App” / localized equivalent
12. API Endpoints Reference
12.1 Page Routes (GET, return HTML)
| Route |
Auth |
Description |
GET / |
Cookie |
Home page: kid cards + active airdrops + shared rewards |
GET /kid/<id> |
Cookie |
Kid dashboard with all stats and progress |
GET /kid/<id>/rewards |
Cookie |
Treasure shop (gender-filtered) |
GET /settings |
Session (PIN) |
Parent admin panel |
GET /service-worker.js |
None |
Service worker JavaScript file |
12.2 Action Routes (POST, return JSON or redirect)
Kid Actions (No parent auth required)
| Route |
Input |
Returns |
Description |
POST /claim |
kid_id, behavior_id |
JSON |
Create pending behavior claim |
POST /cancel_claim |
claim_id |
JSON |
Cancel pending claim |
POST /redeem |
kid_id, reward_id, is_shared |
JSON |
Initiate reward redemption |
POST /cancel_redemption |
redemption_id |
JSON |
Cancel pending redemption |
POST /join_shareable |
kid_id, redemption_id |
JSON |
Join a shared reward |
POST /set_target_reward |
kid_id, reward_id |
JSON |
Set kid’s goal reward |
POST /airdrop/claim |
kid_id, airdrop_id |
JSON |
Claim an airdrop slot |
POST /airdrop/complete |
claim_id |
JSON |
Mark airdrop as done |
POST /upload_reward_image |
kid_id, image, coin_cost, category, reward_name, is_milestone |
Redirect |
Upload custom reward |
POST /add_goal |
kid_id, behavior_id, target_count |
Redirect |
Create behavior goal |
Parent Actions (Require session['parent_authenticated'])
| Route |
Input |
Returns |
Description |
POST /confirm_claim |
claim_id |
JSON |
Approve claim, award coins |
POST /confirm_redemption |
redemption_id |
JSON |
Approve redemption, deduct coins |
POST /confirm_airdrop |
claim_id |
JSON |
Confirm airdrop, award coins |
POST /delete_airdrop |
airdrop_id |
Redirect |
Delete airdrop + claims |
POST /add_airdrop |
title, description, coins, slots, emoji |
Redirect |
Create new airdrop |
POST /delete_reward_image/<id> |
- |
Redirect |
Delete image + custom reward |
Settings Actions (via POST /settings with action parameter)
| Action |
Input |
Description |
login |
pin |
Authenticate parent with PIN |
logout |
- |
Clear parent session |
set_lang |
lang |
Change UI language |
set_pin |
pin |
Set or update parent PIN |
toggle_lock |
- |
Toggle site lock |
add_kid |
name, emoji, gender |
Create kid |
edit_kid |
kid_id, name, emoji, gender |
Update kid |
delete_kid |
kid_id |
Delete kid + cascade |
add_behavior |
description, emoji, coin_value |
Create behavior |
edit_behavior |
behavior_id, description, emoji, coin_value |
Update behavior |
delete_behavior |
behavior_id |
Delete behavior + cascade |
add_reward |
name, coin_cost, image, suitable_male, suitable_female, is_milestone, is_shareable |
Create reward |
edit_reward |
reward_id, name, coin_cost, image, suitable_male, suitable_female, is_milestone, is_shareable |
Update reward |
delete_reward |
reward_id |
Delete reward + cascade |
add_goal |
kid_id, behavior_id, target_count |
Create goal |
delete_goal |
goal_id |
Delete goal |
Data Endpoints
| Route |
Method |
Returns |
Description |
GET /get_shared_rewards |
GET |
JSON array |
List pending shared redemptions with participant names |
12.3 Authentication Routes
| Route |
Method |
Input |
Description |
POST /unlock |
POST |
pin |
Validate PIN, set access cookie |
13. UI/UX Specifications
13.1 Design System
- Style: Claymorphism (soft 3D shadows, rounded corners, raised surfaces)
- Color Palette:
- Primary gradient:
#ff7e5f → #feb47b (coral to peach)
- Kid-friendly colors: rotating set of 7 (pink, blue, green, orange, purple, yellow, teal)
- Background:
#fff5f0 (warm cream)
- Success: green tones
- Warning/milestone: amber/gold
- Typography: Rubik font (Google Fonts), rounded, friendly
- Border Radius: Heavy use of 20px-30px border radius
- Shadows: Multi-layer soft shadows (
8px 8px 15px rgba(0,0,0,0.1), -4px -4px 10px rgba(255,255,255,0.5))
13.2 Animations
| Animation |
Used For |
Description |
bounce |
Headers, titles, emojis |
Vertical oscillation |
wobble |
Coin balance, badges |
Horizontal wiggle |
pulse |
Progress bar, badges |
Scale in/out |
shake |
Kid emoji on click |
Rotation shake |
rainbow-title |
Dashboard title |
Color cycle through 5 colors |
float |
Rewards, images |
Vertical float + slight rotation |
float-gentle |
Clay cards |
Subtle vertical float |
float-horizontal |
Cloud decorations |
Horizontal movement |
confetti-fall |
Celebration screen |
Falling + rotating particles |
star-spawn |
Progress nodes |
Scale + rotate spawn |
bounce-soft |
Reward reached message |
Gentle bounce |
shine |
Progress bar |
Sparkle/shimmer effect |
13.3 Sound Effects
| Sound |
File |
Trigger |
| Pop |
pop.mp3 |
Button clicks, UI interactions |
| Success |
success.mp3 |
Claim confirmation, achievements |
| Coin |
coin.mp3 |
Coin collection (referenced, may not be used) |
13.4 Page Layouts
Home Page (index.html)
- Hero title with animation
- Kid cards in responsive grid
- Active airdrops section (if any)
- Shared rewards section (dynamically loaded)
- Fixed settings gear button (bottom-right)
- PWA install banner (conditional)
Kid Dashboard (kid_dashboard.html)
- Red gradient header with kid info, rank, streak
- Stats bar (coins, treasures, streak)
- Progress tracking card with 5-node visual tracker
- Weekly activity bar chart
- Pending claims section
- Active airdrops section
- My Collection (badges + redeemed rewards)
- Quick-action behavior buttons (color-coded grid)
- Goals progress section
- Photo album section
- Fixed bottom navigation bar (4 items)
Treasure Shop (rewards.html)
- Header with coin balance (large, animated)
- Responsive reward card grid
- Color-coded affordability (green border vs grayscale)
- Category badges on cards
- Shareable indicator (🍩)
- Redeem/Goal modal
- Fixed bottom navigation
Parent Settings (settings.html)
- Top bar with language selector + logout
- Site lock toggle card
- 3-column layout:
- Column 1: Approval queues (claims, airdrops, redemptions)
- Column 2: Kid management
- Column 3: Behavior management + Airdrop management
- Full-width rewards management section
- Emoji picker modal
PIN Login (settings_login.html)
- Centered card (400px max-width)
- Numeric PIN input with wide letter-spacing
- Error display
- Back to home link
13.5 Bottom Navigation Bar
Fixed at bottom on all kid-facing pages:
| Position |
Icon |
Label |
Route |
| 1 |
🏠 |
Home |
/ |
| 2 |
🌟 |
My Stars |
/kid/{id} |
| 3 |
🎁 |
Treasure Shop |
/kid/{id}/rewards |
| 4 |
⚙️ |
Settings |
/settings |
Active item highlighted in primary color.
14. Business Rules Summary
Coin Economy
- Coins are earned only through parent-confirmed behavior claims
- Coins are spent only through parent-confirmed reward redemptions
- Coins can also be earned through parent-confirmed airdrop completions
- A kid’s
coins field represents their current balance (earned minus spent)
- Coin values are always positive integers
- No concept of negative balance - redemption requires sufficient coins
Claim Rules
- Maximum 10 pending claims per kid at any time
- Only pending claims can be cancelled
- Confirming a claim: adds coins + increments goal progress + checks badges
- Claims are timestamped in UTC
Redemption Rules
- Kid must have
coins >= coin_cost to initiate redemption
- For shared rewards: kid must have
coins >= cost_per_kid (cost // 2 on initiation)
- On shared confirmation: cost recalculated as
coin_cost // actual_participant_count
- Only pending redemptions can be cancelled
- Cancellation does NOT refund coins (coins are deducted only on confirmation)
Reward Display Rules
- Rewards filtered by kid’s gender (
suitable_male / suitable_female)
- Milestone rewards get priority in auto-selection for next goal
- Custom rewards (kid-uploaded) flagged with
is_custom=True
- Shareable rewards indicated with 🍩 emoji
Airdrop Rules
- One claim per kid per airdrop (no duplicate claims)
- Total claims cannot exceed
airdrop.slots
- 3-step status flow:
claimed → completed → confirmed
- Coins awarded only on parent confirmation
- Deleting an airdrop cascades to all its claims
Goal Rules
- Goal progress increments when a related behavior claim is confirmed
- Goals are not automatically deleted when completed
- Multiple goals for the same behavior/kid combination are possible (no uniqueness constraint)
Badge Rules
- Badges auto-created on first check if none exist in database
- Awarded based on
kid.coins >= badge.required_coins
- Once earned, badges are never revoked (even if coins decrease from spending)
- Badge check runs on every claim confirmation
Deletion Cascades
- Delete Kid: removes all Claims, RewardRedemptions, Goals, RewardImages for that kid
- Delete Behavior: removes all Goals and Claims for that behavior
- Delete Reward: removes all RewardRedemptions for that reward
- Delete Airdrop: removes all AirdropClaims for that airdrop
- Delete RewardImage: if linked to custom reward, removes the Reward and its RewardRedemptions
15. Planned / Non-Implemented Features
These features are documented in project files but not yet implemented:
15.1 Airdrop Expiry Timer (from new_concepts.md)
- “Flash Airdrops” that disappear if not claimed within a time window
- No timer/expiry fields exist in the current Airdrop model
- Currently, airdrops persist until manually deleted
15.2 Airdrop Urgency Tags (from new_concepts.md)
- Visual urgency indicator (🔥) for time-sensitive airdrops
- The emoji field exists but urgency is not a separate concept from the static emoji
15.3 Shareable Reward Participation Threshold (from new_concepts.md)
min_participants field exists on Reward model but is not enforced in the redemption flow
- Currently any number of participants can join (or just the initiator)
- No maximum participant limit is enforced
15.4 Contribution Tracking for Shared Rewards (from new_concepts.md)
- Parent dashboard should show who contributed what amount to shared rewards
- Currently only shows the initiator and total cost, not per-participant breakdown in the approval queue
15.5 Shared Reward Status Flow (from new_concepts.md)
- Designed: “Pending Join” state until minimum participation is met, then moves to “Redeemed”
- Currently: shared rewards go straight to
pending and can be confirmed regardless of participant count
15.6 Sound Effect Variety (from UPDATE_LOG.md)
- “Add sound effect variety for different achievement tiers”
- Currently uses same sounds for all achievements
15.7 Prominent Progress Tab (from PLAN.md)
- “Add a ‘Progress’ tab or section specifically for long-term goals”
- Currently goals are shown within the kid dashboard but not as a standalone section
15.8 Level Names (from en.json)
Translation keys exist but are not used in the current UI:
| Key |
Value |
LEVEL_NAME_1 |
Star Scout |
LEVEL_NAME_2 |
Moon Walker |
LEVEL_NAME_3 |
Galaxy Guard |
LEVEL_NAME_4 |
Planet Protector |
LEVEL_NAME_5 |
Cosmic King |
LEVEL_NAME_6 |
Universe Hero |
15.9 Behavior Categories (from schema sync)
category column added to Behavior table via schema sync
- Default
'general'
- Not exposed in the parent settings form for editing
- Behaviors are ordered by category in queries but no category management exists
15.10 Reward Details Field
details (Text) field exists on Reward model
- Not shown in any template or editable in the parent settings form
16. Security Considerations
Current Implementation
| Aspect |
Status |
Notes |
| PIN Storage |
Plaintext |
Not hashed - stored as plain string in Setting table |
| CSRF Protection |
None |
No CSRF tokens on POST forms |
| File Upload Validation |
Extension only |
Validates by extension ({png, jpg, jpeg, gif}), not MIME type |
| SQL Injection |
Protected |
SQLAlchemy ORM used throughout |
| XSS |
Partial |
Jinja2 auto-escaping, but some {{ }} in JS contexts |
| Session Secret |
Hardcoded |
SECRET_KEY = 'dev-key-placeholder' |
| Authorization |
Session-based |
Parent actions check session['parent_authenticated'] |
| Rate Limiting |
None |
No rate limiting on any endpoint |
| Input Validation |
Minimal |
Coin values cast to int, no negative value checks |
Recommendations for Reimplementation
- Hash the parent PIN (bcrypt or similar)
- Add CSRF tokens to all forms
- Validate file MIME types, not just extensions
- Use a proper random secret key
- Add rate limiting on PIN attempts
- Validate all numeric inputs (no negatives, reasonable ranges)
- Sanitize file names more aggressively
17. Deployment & Infrastructure
Docker Setup
# docker-compose.yml
services:
app:
build: ./app
volumes:
- ./app:/app
- ./data:/data
ports:
- "5821:5000"
environment:
- DATABASE_URL=sqlite:////data/reward.db
restart: always
command: gunicorn --bind 0.0.0.0:5000 --workers 4 --threads 2 "main:create_app()"
Application Server
- Server: Gunicorn with gevent worker class
- Workers: 4
- Threads: 2 per worker
- Bind:
0.0.0.0:5000
- Entry point:
main:create_app()
Database
- Engine: SQLite
- Location:
/data/reward.db (mounted volume for persistence)
- Migrations: Flask-Migrate (Alembic)
- Schema Sync:
sync_db_schema() function adds missing columns via raw SQL ALTER TABLE
Static Files
- Served by Flask directly from
app/static/
- Images uploaded to
app/static/images/rewards/
- Image optimization: Pillow thumbnail 800x800, quality 85
Dependencies
Flask==3.0.3
Flask-SQLAlchemy==3.1.1
Flask-Migrate==4.0.7
Pillow==10.3.0
gunicorn==25.1.0
gevent==25.9.1
Appendix A: Translation Key Reference
See app/translations/en.json (92 keys) and app/translations/ar.json (207 keys) for the complete translation dictionaries. Arabic has more keys due to additional contextual translations and voice prompts.
Appendix B: Database Schema Sync Columns
The following columns are added at runtime via sync_db_schema() if they don’t exist:
| Table |
Column |
Definition |
| kid |
target_reward_id |
INTEGER |
| behavior |
category |
VARCHAR DEFAULT ‘general’ |
| reward |
category |
VARCHAR DEFAULT ‘general’ |
| reward |
is_milestone |
BOOLEAN DEFAULT 0 |
| reward |
suitable_male |
BOOLEAN DEFAULT 1 |
| reward |
suitable_female |
BOOLEAN DEFAULT 1 |
| reward |
is_custom |
BOOLEAN DEFAULT 0 |
| reward_image |
reward_id |
INTEGER (FK) |
Appendix C: Entity Relationship Summary
Kid ──┬── has many ── Claim ── belongs to ── Behavior
├── has many ── Goal ── belongs to ── Behavior
├── has many ── RewardImage ── belongs to ── Reward
├── has many ── RewardRedemption ── belongs to ── Reward
├── has many ── KidBadge ── belongs to ── Badge
├── has many ── AirdropClaim ── belongs to ── Airdrop
└── belongs to (optional) ── Reward (target_reward_id)
Setting (standalone key-value store)
التعليقات (0)
سجّل الدخول للتعليق.