package model import "time" // SadhnaLog is one persisted row of the sadhna_logs table — the chanting // half of a day's sādhana. The posting half is derived from the posts // table at read time, so it has no column here. type SadhnaLog struct { ID string `json:"id"` UserID string `json:"user_id"` LogDate time.Time `json:"log_date"` RoundsCompleted int `json:"rounds_completed"` ChantDone bool `json:"chant_done"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } // SadhnaDay is one cell of the 7-day streak strip. `Completed` is the // conjunction the streak counts: both the chanting and the posting were // done that calendar day. type SadhnaDay struct { Date string `json:"date"` // YYYY-MM-DD (UTC) ChantDone bool `json:"chant_done"` QuoteDone bool `json:"quote_done"` Completed bool `json:"completed"` } // VineDay is one fold of the sādhana creeper — one day of practice the // vine has wound around its pole. The creeper grows a fresh fold for each // day of the current streak (oldest first), plus a final in-progress fold // for an unfinished today. Rounds/Posts annotate the fold so the devotee // sees exactly what they offered that day. type VineDay struct { Date string `json:"date"` // YYYY-MM-DD (UTC) Rounds int `json:"rounds"` // rounds chanted that day Posts int `json:"posts"` // quotes posted that day Completed bool `json:"completed"` // both disciplines done (a full fold) InProgress bool `json:"in_progress"` // today, not yet complete (the growing tip) } // SadhnaReward is one gift item in the streak-reward catalog. type SadhnaReward struct { ID string `json:"id"` Key string `json:"key"` Name string `json:"name"` Meaning string `json:"meaning"` ImageURL string `json:"image_url"` Active bool `json:"active"` Sort int `json:"sort"` } // SadhnaRewardGrant is a reward a user earned at a streak milestone (a // multiple of 7 days). The full reward is embedded for direct rendering. type SadhnaRewardGrant struct { Milestone int `json:"milestone"` GrantedAt time.Time `json:"granted_at"` Reward SadhnaReward `json:"reward"` } // SadhnaDashboard is the single payload the /sadhna-streak page reads. It // folds together today's progress, the week strip, the streak counters, // and the plant's growth state so the client renders without further // round-trips. type SadhnaDashboard struct { // Today's two disciplines. TargetRounds int `json:"target_rounds"` // user's goal (16 if unset) RoundsCompleted int `json:"rounds_completed"` // saved progress today ChantDone bool `json:"chant_done"` // rounds reached target QuoteDone bool `json:"quote_done"` // posted at least one quote today TodayCompleted bool `json:"today_completed"` // both done today // Streak counters. CurrentStreak int `json:"current_streak"` LongestStreak int `json:"longest_streak"` // Plant state. Growth is 0–100; Stage is the derived label the client // maps to a visual; Alive is false only when growth has decayed to 0. Growth int `json:"growth"` Stage string `json:"stage"` // dead | sprout | seedling | sapling | young | mature Alive bool `json:"alive"` // The trailing 7 calendar days, oldest first, for the top strip. Week []SadhnaDay `json:"week"` // The creeper: one fold per day of the current streak (oldest first), // plus a final in-progress fold for an unfinished today. Capped to the // most recent vineMaxDays folds for payload sanity; CurrentStreak holds // the true length. VineDays []VineDay `json:"vine_days"` // Rewards. NextRewardDate is the projected UTC date (YYYY-MM-DD) the next // gift lands if the streak continues — for the calendar's gift marker. // PendingReward is a freshly earned, not-yet-celebrated gift the client // should celebrate now (nil when there's nothing to show). NextRewardDate *string `json:"next_reward_date"` PendingReward *SadhnaRewardGrant `json:"pending_reward"` }