Spaces:
Sleeping
Sleeping
| // Enhanced Authentication System with Local Storage | |
| class AuthManager { | |
| constructor() { | |
| this.baseStorageKeys = { | |
| user: 'ecospire_user', | |
| activities: 'ecospire_activities', | |
| stats: 'ecospire_stats', | |
| settings: 'ecospire_settings' | |
| }; | |
| this.initializeStorage(); | |
| } | |
| // Get user-specific storage keys | |
| getStorageKeys(userId = null) { | |
| const currentUser = userId || this.getCurrentUserId(); | |
| if (!currentUser || currentUser === 'guest') { | |
| return this.baseStorageKeys; | |
| } | |
| return { | |
| user: this.baseStorageKeys.user, | |
| activities: `${this.baseStorageKeys.activities}_${currentUser}`, | |
| stats: `${this.baseStorageKeys.stats}_${currentUser}`, | |
| settings: `${this.baseStorageKeys.settings}_${currentUser}` | |
| }; | |
| } | |
| getCurrentUserId() { | |
| const userData = localStorage.getItem(this.baseStorageKeys.user); | |
| if (userData) { | |
| const user = JSON.parse(userData); | |
| return user.id; | |
| } | |
| return null; | |
| } | |
| initializeStorage() { | |
| // Initialize default data if not exists | |
| if (!localStorage.getItem(this.baseStorageKeys.user)) { | |
| this.setGuestMode(); | |
| } | |
| // Initialize user-specific data | |
| const keys = this.getStorageKeys(); | |
| if (!localStorage.getItem(keys.activities)) { | |
| localStorage.setItem(keys.activities, JSON.stringify([])); | |
| } | |
| if (!localStorage.getItem(keys.stats)) { | |
| this.resetStats(); | |
| } | |
| } | |
| setGuestMode() { | |
| const guestUser = { | |
| id: 'guest', | |
| name: 'Guest User', | |
| email: 'guest@p4cng.biz.vn', | |
| isGuest: true, | |
| joinDate: new Date().toISOString(), | |
| avatar: 'π±', | |
| location: null, | |
| preferences: { | |
| notifications: true, | |
| publicProfile: false, | |
| dataSharing: false | |
| } | |
| }; | |
| localStorage.setItem(this.baseStorageKeys.user, JSON.stringify(guestUser)); | |
| } | |
| resetStats() { | |
| const defaultStats = { | |
| carbonSaved: 0, | |
| waterTests: 0, | |
| biodiversityScans: 0, | |
| wasteReduced: 0, | |
| energySaved: 0, | |
| treesPlanted: 0, | |
| totalActivities: 0, | |
| streakDays: 0, | |
| lastActivity: null, | |
| achievements: [], | |
| level: 1, | |
| points: 0 | |
| }; | |
| const keys = this.getStorageKeys(); | |
| localStorage.setItem(keys.stats, JSON.stringify(defaultStats)); | |
| } | |
| getCurrentUser() { | |
| const userData = localStorage.getItem(this.baseStorageKeys.user); | |
| return userData ? JSON.parse(userData) : null; | |
| } | |
| isAuthenticated() { | |
| const user = this.getCurrentUser(); | |
| return user && !user.isGuest; | |
| } | |
| isGuestMode() { | |
| const user = this.getCurrentUser(); | |
| return user && user.isGuest; | |
| } | |
| getUserStats() { | |
| const keys = this.getStorageKeys(); | |
| const statsData = localStorage.getItem(keys.stats); | |
| if (statsData) { | |
| return JSON.parse(statsData); | |
| } else { | |
| this.resetStats(); | |
| return JSON.parse(localStorage.getItem(keys.stats)); | |
| } | |
| } | |
| updateUserStats(updates) { | |
| const currentStats = this.getUserStats(); | |
| const newStats = { ...currentStats, ...updates }; | |
| // Update level based on points | |
| newStats.level = Math.floor(newStats.points / 1000) + 1; | |
| // Update streak | |
| const today = new Date().toDateString(); | |
| const lastActivity = newStats.lastActivity ? new Date(newStats.lastActivity).toDateString() : null; | |
| if (lastActivity !== today) { | |
| const yesterday = new Date(); | |
| yesterday.setDate(yesterday.getDate() - 1); | |
| if (lastActivity === yesterday.toDateString()) { | |
| newStats.streakDays += 1; | |
| } else if (lastActivity !== today) { | |
| newStats.streakDays = 1; | |
| } | |
| newStats.lastActivity = new Date().toISOString(); | |
| } | |
| const keys = this.getStorageKeys(); | |
| localStorage.setItem(keys.stats, JSON.stringify(newStats)); | |
| return newStats; | |
| } | |
| async logActivity(description, data = {}) { | |
| const activities = this.getActivities(); | |
| const activity = { | |
| id: Date.now(), | |
| timestamp: new Date().toISOString(), | |
| description, | |
| data, | |
| type: data.type || 'general', | |
| points: data.points || 10 | |
| }; | |
| activities.unshift(activity); | |
| // Keep only last 100 activities | |
| if (activities.length > 100) { | |
| activities.splice(100); | |
| } | |
| const keys = this.getStorageKeys(); | |
| localStorage.setItem(keys.activities, JSON.stringify(activities)); | |
| // Update stats | |
| const currentStats = this.getUserStats(); | |
| const updates = { | |
| totalActivities: currentStats.totalActivities + 1, | |
| points: currentStats.points + activity.points | |
| }; | |
| // Update specific stats based on activity type | |
| switch (data.type) { | |
| case 'water_test': | |
| updates.waterTests = (currentStats.waterTests || 0) + (data.amount || 1); | |
| break; | |
| case 'biodiversity_scan': | |
| updates.biodiversityScans = (currentStats.biodiversityScans || 0) + (data.amount || 1); | |
| break; | |
| case 'carbon_reduction': | |
| updates.carbonSaved = (currentStats.carbonSaved || 0) + (data.amount || 1); | |
| break; | |
| case 'waste_reduction': | |
| updates.wasteReduced = (currentStats.wasteReduced || 0) + (data.amount || 1); | |
| break; | |
| case 'energy_saved': | |
| updates.energySaved = (currentStats.energySaved || 0) + (data.amount || 1); | |
| break; | |
| case 'tree_planted': | |
| updates.treesPlanted = (currentStats.treesPlanted || 0) + (data.amount || 1); | |
| break; | |
| case 'general': | |
| // General activities don't update specific counters but still give points | |
| break; | |
| } | |
| this.updateUserStats(updates); | |
| this.checkAchievements(); | |
| return { success: true, id: activity.id, activity }; | |
| } | |
| getActivities(limit = 50) { | |
| const keys = this.getStorageKeys(); | |
| const activities = localStorage.getItem(keys.activities); | |
| const allActivities = activities ? JSON.parse(activities) : []; | |
| return allActivities.slice(0, limit); | |
| } | |
| async getUserData(type, limit = 10) { | |
| const activities = this.getActivities(); | |
| return activities | |
| .filter(activity => activity.type === type) | |
| .slice(0, limit) | |
| .map(activity => ({ | |
| ...activity.data, | |
| timestamp: activity.timestamp | |
| })); | |
| } | |
| checkAchievements() { | |
| try { | |
| const stats = this.getUserStats(); | |
| // Ensure stats is valid and has required properties | |
| if (!stats || typeof stats !== 'object') { | |
| console.warn('Invalid stats object in checkAchievements'); | |
| return []; | |
| } | |
| // Ensure achievements array exists | |
| if (!Array.isArray(stats.achievements)) { | |
| stats.achievements = []; | |
| } | |
| const newAchievements = []; | |
| // Define achievements | |
| const achievements = [ | |
| { id: 'first_test', name: 'First Test', description: 'Complete your first water test', condition: () => (stats.waterTests || 0) >= 1, icon: 'π§' }, | |
| { id: 'bio_explorer', name: 'Bio Explorer', description: 'Complete 5 biodiversity scans', condition: () => (stats.biodiversityScans || 0) >= 5, icon: 'π¦' }, | |
| { id: 'carbon_saver', name: 'Carbon Saver', description: 'Save 10kg of COβ', condition: () => (stats.carbonSaved || 0) >= 10, icon: 'π±' }, | |
| { id: 'streak_week', name: 'Weekly Streak', description: 'Maintain a 7-day activity streak', condition: () => (stats.streakDays || 0) >= 7, icon: 'π₯' }, | |
| { id: 'level_up', name: 'Level Up', description: 'Reach level 5', condition: () => (stats.level || 1) >= 5, icon: 'β' }, | |
| { id: 'eco_champion', name: 'Eco Champion', description: 'Complete 50 activities', condition: () => (stats.totalActivities || 0) >= 50, icon: 'π' } | |
| ]; | |
| achievements.forEach(achievement => { | |
| try { | |
| if (achievement.condition() && !stats.achievements.includes(achievement.id)) { | |
| newAchievements.push(achievement); | |
| stats.achievements.push(achievement.id); | |
| } | |
| } catch (error) { | |
| console.warn(`Error checking achievement ${achievement.id}:`, error); | |
| } | |
| }); | |
| if (newAchievements.length > 0) { | |
| const keys = this.getStorageKeys(); | |
| localStorage.setItem(keys.stats, JSON.stringify(stats)); | |
| return newAchievements; | |
| } | |
| return []; | |
| } catch (error) { | |
| console.error('Error in checkAchievements:', error); | |
| return []; | |
| } | |
| } | |
| async login(credentials) { | |
| // Simulate API call | |
| await new Promise(resolve => setTimeout(resolve, 1000)); | |
| // For demo, create a user based on email | |
| const user = { | |
| id: credentials.email.replace('@', '_').replace(/\./g, '_'), | |
| name: credentials.name || credentials.email.split('@')[0], | |
| email: credentials.email, | |
| isGuest: false, | |
| joinDate: new Date().toISOString(), | |
| avatar: this.generateAvatar(credentials.email), | |
| location: null, | |
| preferences: { | |
| notifications: true, | |
| publicProfile: true, | |
| dataSharing: true | |
| } | |
| }; | |
| // Store the new user | |
| localStorage.setItem(this.baseStorageKeys.user, JSON.stringify(user)); | |
| // Initialize user-specific data storage | |
| this.initializeUserData(user.id); | |
| return { success: true, user }; | |
| } | |
| initializeUserData(userId) { | |
| const keys = this.getStorageKeys(userId); | |
| // Initialize activities if they don't exist | |
| if (!localStorage.getItem(keys.activities)) { | |
| localStorage.setItem(keys.activities, JSON.stringify([])); | |
| } | |
| // Initialize stats if they don't exist | |
| if (!localStorage.getItem(keys.stats)) { | |
| const defaultStats = { | |
| carbonSaved: 0, | |
| waterTests: 0, | |
| biodiversityScans: 0, | |
| wasteReduced: 0, | |
| energySaved: 0, | |
| treesPlanted: 0, | |
| totalActivities: 0, | |
| streakDays: 0, | |
| lastActivity: null, | |
| achievements: [], | |
| level: 1, | |
| points: 0 | |
| }; | |
| localStorage.setItem(keys.stats, JSON.stringify(defaultStats)); | |
| } | |
| } | |
| async register(userData) { | |
| // Simulate API call | |
| await new Promise(resolve => setTimeout(resolve, 1500)); | |
| const user = { | |
| id: userData.email.replace('@', '_').replace(/\./g, '_'), | |
| name: userData.name, | |
| email: userData.email, | |
| isGuest: false, | |
| joinDate: new Date().toISOString(), | |
| avatar: this.generateAvatar(userData.email), | |
| location: null, | |
| preferences: { | |
| notifications: true, | |
| publicProfile: true, | |
| dataSharing: true | |
| } | |
| }; | |
| localStorage.setItem(this.baseStorageKeys.user, JSON.stringify(user)); | |
| // Initialize fresh user data | |
| this.initializeUserData(user.id); | |
| return { success: true, user }; | |
| } | |
| async logout() { | |
| // Log the logout activity before clearing | |
| await this.logActivity('User logged out', { | |
| type: 'auth', | |
| action: 'logout', | |
| points: 0 | |
| }); | |
| // Clear current user and reset to guest mode | |
| this.setGuestMode(); | |
| return { success: true }; | |
| } | |
| generateAvatar(email) { | |
| const avatars = ['π±', 'πΏ', 'π³', 'π¦', 'π', 'πΈ', 'πΊ', 'π', 'πΎ', 'π»']; | |
| const index = email.charCodeAt(0) % avatars.length; | |
| return avatars[index]; | |
| } | |
| updateUserProfile(updates) { | |
| const user = this.getCurrentUser(); | |
| if (user) { | |
| const updatedUser = { ...user, ...updates }; | |
| localStorage.setItem(this.baseStorageKeys.user, JSON.stringify(updatedUser)); | |
| return updatedUser; | |
| } | |
| return null; | |
| } | |
| getLeaderboard() { | |
| // Simulate leaderboard data | |
| const currentUser = this.getCurrentUser(); | |
| const currentStats = this.getUserStats(); | |
| return [ | |
| { name: 'EcoChampion2024', points: 15420, level: 16, avatar: 'π' }, | |
| { name: 'GreenWarrior', points: 12890, level: 13, avatar: 'π±' }, | |
| { name: 'BioDiversityExpert', points: 11250, level: 12, avatar: 'π¦' }, | |
| { name: currentUser?.name || 'You', points: currentStats.points, level: currentStats.level, avatar: currentUser?.avatar || 'πΏ', isCurrentUser: true }, | |
| { name: 'WaterTester', points: 8940, level: 9, avatar: 'π§' }, | |
| { name: 'CarbonSaver', points: 7650, level: 8, avatar: 'π³' }, | |
| { name: 'EcoNewbie', points: 3420, level: 4, avatar: 'πΈ' } | |
| ].sort((a, b) => b.points - a.points); | |
| } | |
| } | |
| export const authManager = new AuthManager(); | |
| export default authManager; |