Files
LifeRPG_v2.0/modern/frontend/src/App.jsx
T
TLimoges33 7fe4ae5365 🧙‍♂️ Transform LifeRPG into The Wizard's Grimoire - Production-Ready Application
 Major Features Added:
- Complete magical theming and rebranding from LifeRPG to The Wizard's Grimoire
- Production-grade React frontend with Tailwind CSS v4 and magical aesthetics
- Comprehensive analytics dashboard with Recharts integration (ScryingPortal)
- Push notifications system with PWA service worker support
- Drag & drop functionality using @dnd-kit for habit reordering
- Social features with friends system and leaderboards
- Performance optimization tools and monitoring
- Mobile app enhancement with PWA installation support

🏗️ Technical Infrastructure:
- Advanced service worker with offline support and background sync
- Zustand state management for scalable application state
- Production-ready UI component system with enhanced Button, Card, Input
- Progressive Web App (PWA) with manifest and app installation
- FastAPI backend with comprehensive API endpoints
- Docker containerization and CI/CD pipeline setup

📱 Progressive Web App Features:
- Offline functionality with intelligent caching
- Push notification support for habit reminders
- App installation on mobile and desktop platforms
- Background sync for offline data management
- Performance monitoring and optimization tools

🎨 User Experience:
- Magical wizard/grimoire theming throughout application
- Responsive design optimized for all device sizes
- Drag & drop habit management with smooth animations
- Interactive analytics with multiple chart types
- Social connectivity with friends and competitive features
- Comprehensive notification and performance settings

🔧 Developer Experience:
- Modern development stack with Vite and React
- Comprehensive testing setup and CI/CD pipelines
- Code quality tools with pre-commit hooks
- Docker development environment
- Detailed documentation and implementation guides

This represents a complete transformation from prototype to production-ready application with enterprise-grade features and magical user experience.
2025-08-30 17:32:42 +00:00

311 lines
12 KiB
React

import React, { useState, useEffect } from 'react';
import MainDashboard from './components/MainDashboard';
import ScryingPortal from './components/ScryingPortal';
import SocialFeatures from './components/SocialFeatures';
import NotificationSettings from './components/NotificationSettings';
import PerformanceOptimization from './components/PerformanceOptimization';
import MobileAppEnhancement from './components/MobileAppEnhancement';
import { Card, CardHeader, CardTitle, CardContent } from './components/ui/card';
import { Button } from './components/ui/button';
import { Input } from './components/ui/input';
import { User, Lock, Mail, BarChart3, Users, Settings, Zap, Smartphone, Home } from 'lucide-react';
const App = () => {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [loginForm, setLoginForm] = useState({ email: '', password: '' });
const [registering, setRegistering] = useState(false);
const [currentView, setCurrentView] = useState('dashboard');
useEffect(() => {
checkAuth();
registerServiceWorker();
}, []);
const registerServiceWorker = async () => {
if ('serviceWorker' in navigator) {
try {
const registration = await navigator.serviceWorker.register('/sw.js');
console.log('Service Worker registered:', registration);
} catch (error) {
console.error('Service Worker registration failed:', error);
}
}
};
const checkAuth = async () => {
const token = localStorage.getItem('token');
if (!token) {
setLoading(false);
return;
}
try {
const response = await fetch('/api/v1/me', {
headers: {
'Authorization': `Bearer ${token}`
}
});
if (response.ok) {
const userData = await response.json();
setUser(userData);
} else {
localStorage.removeItem('token');
}
} catch (error) {
console.error('Auth check failed:', error);
localStorage.removeItem('token');
} finally {
setLoading(false);
}
};
const handleLogin = async (e) => {
e.preventDefault();
setLoading(true);
try {
const response = await fetch('/api/v1/auth/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(loginForm)
});
if (response.ok) {
const data = await response.json();
localStorage.setItem('token', data.token);
setUser(data.user);
} else {
const error = await response.json();
alert(error.detail || 'Login failed');
}
} catch (error) {
console.error('Login failed:', error);
alert('Login failed. Please try again.');
} finally {
setLoading(false);
}
};
const handleRegister = async (e) => {
e.preventDefault();
setLoading(true);
try {
const response = await fetch('/api/v1/auth/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
...loginForm,
display_name: loginForm.email.split('@')[0] // Simple display name
})
});
if (response.ok) {
const data = await response.json();
localStorage.setItem('token', data.token);
setUser(data.user);
} else {
const error = await response.json();
alert(error.detail || 'Registration failed');
}
} catch (error) {
console.error('Registration failed:', error);
alert('Registration failed. Please try again.');
} finally {
setLoading(false);
}
};
const handleLogout = () => {
localStorage.removeItem('token');
setUser(null);
};
if (loading) {
return (
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
<div className="text-center">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900 mx-auto mb-4"></div>
<p className="text-gray-600">Loading...</p>
</div>
</div>
);
}
if (!user) {
return (
<div className="min-h-screen bg-gradient-to-br from-purple-900 via-blue-900 to-indigo-900 flex items-center justify-center px-4">
<Card className="w-full max-w-md bg-gradient-to-b from-purple-100 to-blue-50 border-2 border-gold-400 shadow-2xl">
<CardHeader className="text-center">
<div className="w-16 h-16 bg-gradient-to-br from-purple-600 to-indigo-600 rounded-full flex items-center justify-center mx-auto mb-4 shadow-lg">
<span className="text-2xl">🔮</span>
</div>
<CardTitle className="text-2xl text-purple-900">
{registering ? 'Join the Academy' : 'Welcome to The Wizard\'s Grimoire'}
</CardTitle>
<p className="text-purple-700 mt-2">
{registering
? 'Begin your magical journey and master daily spells'
: 'Enter your sanctum to practice spells and unlock mystical powers'
}
</p>
</CardHeader>
<CardContent>
<form onSubmit={registering ? handleRegister : handleLogin} className="space-y-4">
<div>
<label className="text-sm font-medium text-purple-800">Mystic Email</label>
<div className="relative">
<Mail className="absolute left-3 top-3 h-4 w-4 text-purple-500" />
<Input
type="email"
placeholder="Enter your mystical contact"
value={loginForm.email}
onChange={(e) => setLoginForm({ ...loginForm, email: e.target.value })}
className="pl-10 border-purple-300 focus:border-purple-500"
required
/>
</div>
</div>
<div>
<label className="text-sm font-medium text-purple-800">Arcane Password</label>
<div className="relative">
<Lock className="absolute left-3 top-3 h-4 w-4 text-purple-500" />
<Input
type="password"
placeholder="Speak the secret incantation"
value={loginForm.password}
onChange={(e) => setLoginForm({ ...loginForm, password: e.target.value })}
className="pl-10 border-purple-300 focus:border-purple-500"
required
/>
</div>
</div>
<Button
type="submit"
className="w-full bg-gradient-to-r from-purple-600 to-indigo-600 hover:from-purple-700 hover:to-indigo-700 text-white shadow-lg"
disabled={loading}
>
{loading ? 'Summoning...' : registering ? 'Begin Journey' : 'Enter Sanctum'}
</Button>
</form>
<div className="mt-6 text-center">
<button
type="button"
onClick={() => setRegistering(!registering)}
className="text-purple-600 hover:text-purple-700 text-sm font-medium"
>
{registering
? 'Already initiated? Enter your sanctum'
: "New to magic? Join the Academy"
}
</button>
</div>
{!registering && (
<div className="mt-4 text-center">
<p className="text-xs text-purple-600">
🧙‍♂️ Demo: Use any incantation to create a practice realm
</p>
</div>
)}
</CardContent>
</Card>
</div>
);
}
// Navigation component
const Navigation = () => (
<div className="bg-slate-800 border-b border-purple-500/30 p-4 mb-6">
<div className="flex flex-wrap gap-2">
<Button
onClick={() => setCurrentView('dashboard')}
variant={currentView === 'dashboard' ? 'default' : 'outline'}
className="flex items-center gap-2"
>
<Home className="w-4 h-4" />
Dashboard
</Button>
<Button
onClick={() => setCurrentView('analytics')}
variant={currentView === 'analytics' ? 'default' : 'outline'}
className="flex items-center gap-2"
>
<BarChart3 className="w-4 h-4" />
Analytics
</Button>
<Button
onClick={() => setCurrentView('social')}
variant={currentView === 'social' ? 'default' : 'outline'}
className="flex items-center gap-2"
>
<Users className="w-4 h-4" />
Social
</Button>
<Button
onClick={() => setCurrentView('notifications')}
variant={currentView === 'notifications' ? 'default' : 'outline'}
className="flex items-center gap-2"
>
<Settings className="w-4 h-4" />
Notifications
</Button>
<Button
onClick={() => setCurrentView('performance')}
variant={currentView === 'performance' ? 'default' : 'outline'}
className="flex items-center gap-2"
>
<Zap className="w-4 h-4" />
Performance
</Button>
<Button
onClick={() => setCurrentView('mobile')}
variant={currentView === 'mobile' ? 'default' : 'outline'}
className="flex items-center gap-2"
>
<Smartphone className="w-4 h-4" />
Mobile
</Button>
</div>
</div>
);
// Render current view
const renderCurrentView = () => {
switch (currentView) {
case 'analytics':
return <ScryingPortal />;
case 'social':
return <SocialFeatures />;
case 'notifications':
return <NotificationSettings />;
case 'performance':
return <PerformanceOptimization />;
case 'mobile':
return <MobileAppEnhancement />;
default:
return <MainDashboard user={user} onLogout={handleLogout} />;
}
};
return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900">
<Navigation />
<div className="container mx-auto px-4">
{renderCurrentView()}
</div>
</div>
);
};
export default App;