Files
pragativeer-v2/src/hooks/useActiveUser.ts

170 lines
5.2 KiB
TypeScript

"use client";
import { useEffect, useState, useCallback } from "react";
export interface UserProfile {
id: string;
name: string;
email: string | null;
phone: string | null;
class: string;
target_exam: "JEE_Main" | "JEE_Advanced" | "NEET";
medium: "English" | "Hindi" | "Bilingual";
school: string | null;
city: string;
state: string;
tier: "Free" | "Pro" | "Al_Coach"; // Al_Coach in database schema
streak_count: number;
longest_streak: number;
active_session_token?: string;
created_at?: string;
trial_end?: string;
}
export const DEFAULT_USER_ID = "b0e17378-864d-439e-8806-708dc48a891b"; // AI Coach
export const DEMO_USERS = {
FREE: { id: "11111111-1111-1111-1111-111111111111", label: "Amit Kumar (Free Tier)" },
PRO: { id: "22222222-2222-2222-2222-222222222222", label: "Priya Singh (Pro Tier)" },
AI_COACH: { id: DEFAULT_USER_ID, label: "Rahul Sharma (AI Coach)" }
};
export function useActiveUser() {
const [userId, setUserIdState] = useState<string>(() => {
if (typeof window !== "undefined") {
return localStorage.getItem("activeUserId") || DEFAULT_USER_ID;
}
return DEFAULT_USER_ID;
});
const [user, setUser] = useState<UserProfile | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const fetchUserProfile = useCallback(async (id: string, isPoll: boolean = false) => {
if (!isPoll) setLoading(true);
setError(null);
try {
const res = await fetch(`/api/users/${id}`);
if (!res.ok) {
console.error("Failed to fetch user profile, status:", res.status);
setError("Failed to fetch user profile");
setUser(null);
return;
}
const data = await res.json();
// Single Active Session Check
if (typeof window !== 'undefined') {
const localToken = localStorage.getItem("active_session_token");
if (data.active_session_token && localToken && data.active_session_token !== localToken) {
localStorage.removeItem("activeUserId");
localStorage.removeItem("active_session_token");
alert("Your account was logged in from another device. You have been logged out.");
window.location.href = "/";
return;
}
}
setUser(data);
} catch (err: any) {
console.error("Error fetching active user profile:", err);
setError(err.message || "Failed to load user");
} finally {
if (!isPoll) setLoading(false);
}
}, []);
useEffect(() => {
if (typeof window !== "undefined") {
const storedId = localStorage.getItem("activeUserId") || DEFAULT_USER_ID;
setUserIdState(storedId);
fetchUserProfile(storedId);
}
}, [fetchUserProfile]);
useEffect(() => {
if (typeof window === "undefined") return;
const handleUpdate = () => fetchUserProfile(userId);
window.addEventListener('pragativeer-user-updated', handleUpdate);
// Poll every 10 seconds for session validity
const interval = setInterval(() => {
fetchUserProfile(userId, true);
}, 10000);
return () => {
window.removeEventListener('pragativeer-user-updated', handleUpdate);
clearInterval(interval);
};
}, [userId, fetchUserProfile]);
const switchUser = useCallback(async (newId: string) => {
if (typeof window !== "undefined") {
const sessionToken = crypto.randomUUID();
localStorage.setItem("active_session_token", sessionToken);
try {
await fetch(`/api/users/${newId}/session`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ session_token: sessionToken })
});
} catch (err) {
console.error("Failed to set session", err);
}
localStorage.setItem("activeUserId", newId);
setUserIdState(newId);
window.location.reload();
}
}, []);
const hasAccessTo = useCallback((feature: string): boolean => {
if (!user) return false;
const tier = user.tier;
// Core restrictions mapping based on PRD feature matrix
switch (feature) {
case "topic-wise-microtests-unlimited":
case "weekly-test-series":
case "full-length-mock-tests":
case "previous-year-papers":
case "detailed-performance-analytics":
case "chapter-wise-progress-tracking":
case "doubt-resolution-community":
case "parent-dashboard":
case "offline-mode":
case "pdf-export":
return tier === "Pro" || tier === "Al_Coach";
case "ai-mistake-tracking":
case "spaced-repetition":
case "personalized-study-plan":
case "ai-tutor-chat":
case "exam-readiness-score":
case "exam-day-strategy":
case "college-predictor":
case "study-time-optimization":
return tier === "Al_Coach";
default:
// Free tier features: DPS (1/day), basic dashboard, basic streak tracking, leaderboard
return true;
}
}, [user]);
return {
userId,
user,
loading,
error,
switchUser,
hasAccessTo,
refreshUser: async () => {
await fetchUserProfile(userId);
window.dispatchEvent(new CustomEvent('pragativeer-user-updated'));
}
};
}