feat: Add demo users and trial logic uniformity

This commit is contained in:
2026-09-23 12:42:07 +05:30
parent a3e430cb67
commit 2488311661
3 changed files with 59 additions and 51 deletions

View File

@@ -248,17 +248,17 @@ def login(payload: LoginPayload, session: Session = Depends(get_session)):
import bcrypt import bcrypt
# Allow demo/admin mock logic to still work if needed # Allow demo/admin mock logic to still work if needed
# demo@pragativeer.com → Neha Verma (AI Coach demo account) # demo@pragativeer.com → Aarav (AI Coach demo account)
NEHA_UUID = "33333333-3333-3333-3333-333333333333" DEMO_UUID = "11111111-1111-1111-1111-111111111111"
if payload.email == "demo@pragativeer.com" and payload.password == "Pv9#kL2$mQ8!vX4@": if payload.email == "demo@pragativeer.com" and payload.password == "Pv9#kL2$mQ8!vX4@":
import uuid as _uuid import uuid as _uuid
demo_user = session.get(models.User, _uuid.UUID(NEHA_UUID)) demo_user = session.get(models.User, _uuid.UUID(DEMO_UUID))
if not demo_user: if not demo_user:
# Fallback: look for any demo-tagged user # Fallback: look for any demo-tagged user
demo_user = session.exec(select(models.User).where( demo_user = session.exec(select(models.User).where(
(models.User.email == "demo@pragativeer.com") | (models.User.name == "Neha Verma") (models.User.email == "demo@pragativeer.com") | (models.User.name == "Aarav")
)).first() )).first()
token_id = str(demo_user.id) if demo_user else NEHA_UUID token_id = str(demo_user.id) if demo_user else DEMO_UUID
access_token = create_access_token(data={"sub": token_id}) access_token = create_access_token(data={"sub": token_id})
return {"success": True, "role": "user", "token": access_token, "user_id": token_id} return {"success": True, "role": "user", "token": access_token, "user_id": token_id}
if payload.email == "admin@pragativeer.com" and payload.password == "Pv9#kL2$mQ8!vX4@": if payload.email == "admin@pragativeer.com" and payload.password == "Pv9#kL2$mQ8!vX4@":
@@ -683,52 +683,52 @@ def get_referral_dashboard(user_id: UUID, session: Session = Depends(get_session
# --- USERS & DEMO PROFILES --- # --- USERS & DEMO PROFILES ---
DEMO_DEFINITIONS = { DEMO_DEFINITIONS = {
UUID("b0e17378-864d-439e-8806-708dc48a891b"): { UUID("11111111-1111-1111-1111-111111111111"): {
"name": "Rahul Sharma", "name": "Aarav",
"email": "rahul.demo@pragativeer.com", "email": "aarav.jee@pragativeer.com",
"phone": "9999999999", "phone": "9999999991",
"student_class": models.ClassEnum.TWELFTH, "student_class": models.ClassEnum.TWELFTH,
"medium": models.MediumEnum.ENGLISH, "medium": models.MediumEnum.ENGLISH,
"target_exam": models.ExamEnum.JEE_MAIN, "target_exam": models.ExamEnum.JEE_MAIN,
"city": "New Delhi", "city": "New Delhi",
"state": "Delhi", "state": "Delhi",
"school": "Delhi Public School, R.K. Puram", "school": "Delhi Public School",
"tier": models.TierEnum.AI_COACH, "tier": models.TierEnum.AI_COACH,
"streak_count": 6, "streak_count": 6,
"longest_streak": 6, "longest_streak": 6,
}, },
UUID("33333333-3333-3333-3333-333333333333"): { UUID("22222222-2222-2222-2222-222222222222"): {
"name": "Neha Verma", "name": "Neha",
"email": "neha.demo@pragativeer.com", "email": "neha.neet@pragativeer.com",
"phone": "9999999997", "phone": "9999999992",
"student_class": models.ClassEnum.TWELFTH, "student_class": models.ClassEnum.TWELFTH,
"medium": models.MediumEnum.ENGLISH, "medium": models.MediumEnum.ENGLISH,
"target_exam": models.ExamEnum.JEE_MAIN, "target_exam": models.ExamEnum.NEET,
"city": "New Delhi", "city": "New Delhi",
"state": "Delhi", "state": "Delhi",
"school": "Modern School, Barakhamba Road", "school": "Modern School",
"tier": models.TierEnum.AI_COACH, "tier": models.TierEnum.AI_COACH,
"streak_count": 15, "streak_count": 15,
"longest_streak": 25, "longest_streak": 25,
}, },
UUID("22222222-2222-2222-2222-222222222222"): { UUID("33333333-3333-3333-3333-333333333333"): {
"name": "Priya Singh", "name": "Rohan",
"email": "priya.demo@pragativeer.com", "email": "rohan.jeeneet@pragativeer.com",
"phone": "9999999996", "phone": "9999999993",
"student_class": models.ClassEnum.TWELFTH, "student_class": models.ClassEnum.TWELFTH,
"medium": models.MediumEnum.ENGLISH, "medium": models.MediumEnum.ENGLISH,
"target_exam": models.ExamEnum.NEET, "target_exam": models.ExamEnum.JEE_MAIN, # Defaulting to JEE_MAIN in DB
"city": "Lucknow", "city": "Lucknow",
"state": "Uttar Pradesh", "state": "Uttar Pradesh",
"school": "City Montessori School", "school": "City Montessori School",
"tier": models.TierEnum.PRO, "tier": models.TierEnum.AI_COACH,
"streak_count": 42, "streak_count": 42,
"longest_streak": 42, "longest_streak": 42,
}, },
UUID("11111111-1111-1111-1111-111111111111"): { UUID("44444444-4444-4444-4444-444444444444"): {
"name": "Amit Kumar", "name": "Amit",
"email": "amit.demo@pragativeer.com", "email": "amit.free@pragativeer.com",
"phone": "9999999995", "phone": "9999999994",
"student_class": models.ClassEnum.ELEVENTH, "student_class": models.ClassEnum.ELEVENTH,
"medium": models.MediumEnum.ENGLISH, "medium": models.MediumEnum.ENGLISH,
"target_exam": models.ExamEnum.JEE_MAIN, "target_exam": models.ExamEnum.JEE_MAIN,
@@ -781,7 +781,7 @@ def switch_user_endpoint(id: str, session: Session = Depends(get_session)):
"usr_01": "11111111-1111-1111-1111-111111111111", "usr_01": "11111111-1111-1111-1111-111111111111",
"usr_02": "22222222-2222-2222-2222-222222222222", "usr_02": "22222222-2222-2222-2222-222222222222",
"usr_03": "33333333-3333-3333-3333-333333333333", "usr_03": "33333333-3333-3333-3333-333333333333",
"usr_04": "b0e17378-864d-439e-8806-708dc48a891b", "usr_04": "44444444-4444-4444-4444-444444444444",
} }
target_id_str = mapping.get(id, id) target_id_str = mapping.get(id, id)
try: try:

View File

@@ -275,7 +275,7 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
</Link> </Link>
{isDemoUser(userId) && ( {isDemoUser(userId) && (
<button <button
onClick={() => switchUser(DEMO_USERS.NEHA.id)} onClick={() => switchUser(DEMO_USERS.AICOACH_JEE.id)}
className="w-full text-left flex items-center gap-2 px-3 py-2 text-sm text-foreground hover:bg-secondary rounded-lg transition-colors" className="w-full text-left flex items-center gap-2 px-3 py-2 text-sm text-foreground hover:bg-secondary rounded-lg transition-colors"
> >
Reset Demo Reset Demo
@@ -357,10 +357,10 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
onChange={(e) => switchUser(e.target.value)} onChange={(e) => switchUser(e.target.value)}
className="bg-secondary border border-border text-foreground px-2.5 py-1.5 rounded-xl text-xs font-bold focus:ring-2 focus:ring-[var(--cyan-ai)] outline-none cursor-pointer hover:bg-secondary/80 transition-colors disabled:opacity-50 disabled:cursor-wait" className="bg-secondary border border-border text-foreground px-2.5 py-1.5 rounded-xl text-xs font-bold focus:ring-2 focus:ring-[var(--cyan-ai)] outline-none cursor-pointer hover:bg-secondary/80 transition-colors disabled:opacity-50 disabled:cursor-wait"
> >
<option value={DEMO_USERS.NEHA.id}>Neha Verma (AI Coach)</option> <option value={DEMO_USERS.AICOACH_JEE.id}>{DEMO_USERS.AICOACH_JEE.label}</option>
<option value={DEMO_USERS.AI_COACH.id}>Rahul (AI Coach)</option> <option value={DEMO_USERS.AICOACH_NEET.id}>{DEMO_USERS.AICOACH_NEET.label}</option>
<option value={DEMO_USERS.PRO.id}>Priya (Pro Plan)</option> <option value={DEMO_USERS.AICOACH_JEE_NEET.id}>{DEMO_USERS.AICOACH_JEE_NEET.label}</option>
<option value={DEMO_USERS.FREE.id}>Amit (Free Plan)</option> <option value={DEMO_USERS.FREE_JEE.id}>{DEMO_USERS.FREE_JEE.label}</option>
</select> </select>
</div> </div>
)} )}

View File

@@ -22,13 +22,13 @@ export interface UserProfile {
trial_end?: string; trial_end?: string;
} }
export const DEFAULT_USER_ID = "b0e17378-864d-439e-8806-708dc48a891b"; // AI Coach export const DEFAULT_USER_ID = "11111111-1111-1111-1111-111111111111"; // AI Coach - JEE
export const DEMO_USERS = { export const DEMO_USERS = {
FREE: { id: "11111111-1111-1111-1111-111111111111", label: "Amit Kumar (Free Tier)" }, AICOACH_JEE: { id: "11111111-1111-1111-1111-111111111111", label: "Aarav (AI Coach - JEE)" },
PRO: { id: "22222222-2222-2222-2222-222222222222", label: "Priya Singh (Pro Tier)" }, AICOACH_NEET: { id: "22222222-2222-2222-2222-222222222222", label: "Neha (AI Coach - NEET)" },
NEHA: { id: "33333333-3333-3333-3333-333333333333", label: "Neha Verma (AI Coach)" }, AICOACH_JEE_NEET: { id: "33333333-3333-3333-3333-333333333333", label: "Rohan (AI Coach - JEE+NEET)" },
AI_COACH: { id: DEFAULT_USER_ID, label: "Rahul Sharma (AI Coach)" }, FREE_JEE: { id: "44444444-4444-4444-4444-444444444444", label: "Amit (Free - JEE)" },
}; };
// IDs considered "demo" accounts — demo switcher only shows for these // IDs considered "demo" accounts — demo switcher only shows for these
@@ -69,25 +69,33 @@ export function computeTrialInfo(user: UserProfile | null, globalSettings: any):
return defaultResult; return defaultResult;
} }
// Compute from created_at // Compute from trial_end instead of created_at
const trialEnd = user.trial_end ? new Date(user.trial_end) : null;
const createdAt = user.created_at ? new Date(user.created_at) : null; const createdAt = user.created_at ? new Date(user.created_at) : null;
if (!createdAt) {
// If no created_at, assume trial is just starting (Day 1) if (!trialEnd) {
return { // If no trial_end is set in the DB, they don't have a trial active.
isTrialActive: true, return defaultResult;
trialDayNumber: 1,
trialDaysRemaining: trialDays,
trialTotalDays: trialDays,
effectiveTier: (globalSettings.trial_tier as "Pro" | "Al_Coach") ?? "Al_Coach",
};
} }
const now = new Date(); const now = new Date();
const msPerDay = 1000 * 60 * 60 * 24; const msPerDay = 1000 * 60 * 60 * 24;
const daysSinceCreation = Math.floor((now.getTime() - createdAt.getTime()) / msPerDay);
const trialDayNumber = Math.max(1, daysSinceCreation + 1); // 1-indexed const isTrialActive = now.getTime() < trialEnd.getTime();
const trialDaysRemaining = Math.max(0, trialDays - daysSinceCreation);
const isTrialActive = trialDaysRemaining > 0; // Calculate trial days remaining
const trialDaysRemaining = isTrialActive
? Math.max(0, Math.ceil((trialEnd.getTime() - now.getTime()) / msPerDay))
: 0;
// Calculate day number based on created_at and today
let trialDayNumber = 0;
if (isTrialActive && createdAt) {
const daysSinceCreation = Math.floor((now.getTime() - createdAt.getTime()) / msPerDay);
trialDayNumber = Math.max(1, daysSinceCreation + 1); // 1-indexed
} else if (isTrialActive) {
trialDayNumber = 1;
}
return { return {
isTrialActive, isTrialActive,