7fe4ae5365
✨ 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.
159 lines
6.1 KiB
React
159 lines
6.1 KiB
React
import React, { useEffect } from 'react';
|
|
import useAppStore from './store/appStore';
|
|
import MainDashboard from './components/MainDashboard_production';
|
|
import ErrorBoundary from './components/ui/error-boundary';
|
|
import { FullPageLoader } from './components/ui/loading';
|
|
import { Card, CardHeader, CardTitle, CardContent } from './components/ui/card';
|
|
import { Button } from './components/ui/button';
|
|
import { Input } from './components/ui/input';
|
|
|
|
const LoginForm = () => {
|
|
const { login, register, loading } = useAppStore();
|
|
const [isRegistering, setIsRegistering] = React.useState(false);
|
|
const [formData, setFormData] = React.useState({
|
|
email: '',
|
|
password: '',
|
|
name: ''
|
|
});
|
|
const [error, setError] = React.useState('');
|
|
|
|
const handleSubmit = async (e) => {
|
|
e.preventDefault();
|
|
setError('');
|
|
|
|
const result = isRegistering
|
|
? await register(formData)
|
|
: await login({ email: formData.email, password: formData.password });
|
|
|
|
if (!result.success) {
|
|
setError(result.error);
|
|
}
|
|
};
|
|
|
|
const handleInputChange = (e) => {
|
|
setFormData(prev => ({
|
|
...prev,
|
|
[e.target.name]: e.target.value
|
|
}));
|
|
};
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-purple-900 to-slate-900 flex items-center justify-center p-4">
|
|
<Card className="w-full max-w-md">
|
|
<CardHeader className="text-center space-y-4">
|
|
<div className="text-6xl">🧙♂️</div>
|
|
<CardTitle className="text-3xl font-bold bg-gradient-to-r from-purple-400 to-pink-400 bg-clip-text text-transparent">
|
|
The Wizard's Grimoire
|
|
</CardTitle>
|
|
<p className="text-slate-400">
|
|
{isRegistering ? 'Join the Magical Order' : 'Enter the Sanctum'}
|
|
</p>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<form onSubmit={handleSubmit} className="space-y-4">
|
|
{isRegistering && (
|
|
<div>
|
|
<label className="block text-sm font-medium text-purple-300 mb-2">
|
|
Wizard Name
|
|
</label>
|
|
<Input
|
|
name="name"
|
|
type="text"
|
|
value={formData.name}
|
|
onChange={handleInputChange}
|
|
placeholder="Enter your wizard name"
|
|
required
|
|
/>
|
|
</div>
|
|
)}
|
|
<div>
|
|
<label className="block text-sm font-medium text-purple-300 mb-2">
|
|
Mystical Email
|
|
</label>
|
|
<Input
|
|
name="email"
|
|
type="email"
|
|
value={formData.email}
|
|
onChange={handleInputChange}
|
|
placeholder="wizard@grimoire.com"
|
|
required
|
|
/>
|
|
</div>
|
|
<div>
|
|
<label className="block text-sm font-medium text-purple-300 mb-2">
|
|
Secret Incantation
|
|
</label>
|
|
<Input
|
|
name="password"
|
|
type="password"
|
|
value={formData.password}
|
|
onChange={handleInputChange}
|
|
placeholder="Enter your secret password"
|
|
required
|
|
/>
|
|
</div>
|
|
|
|
{error && (
|
|
<div className="bg-red-900/50 border border-red-500 text-red-200 px-4 py-3 rounded">
|
|
{error}
|
|
</div>
|
|
)}
|
|
|
|
<Button
|
|
type="submit"
|
|
variant="magical"
|
|
className="w-full"
|
|
disabled={loading}
|
|
>
|
|
{loading ? (
|
|
<span className="flex items-center justify-center">
|
|
<div className="animate-spin rounded-full h-4 w-4 border-b-2 border-white mr-2" />
|
|
Casting Spell...
|
|
</span>
|
|
) : (
|
|
`🪄 ${isRegistering ? 'Join Order' : 'Enter Sanctum'}`
|
|
)}
|
|
</Button>
|
|
|
|
<Button
|
|
type="button"
|
|
variant="ghost"
|
|
className="w-full"
|
|
onClick={() => setIsRegistering(!isRegistering)}
|
|
>
|
|
{isRegistering
|
|
? 'Already a wizard? Enter here'
|
|
: 'New to magic? Join the order'
|
|
}
|
|
</Button>
|
|
</form>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const App = () => {
|
|
const { user, isAuthenticated, loading, checkAuth, logout } = useAppStore();
|
|
|
|
useEffect(() => {
|
|
checkAuth();
|
|
}, [checkAuth]);
|
|
|
|
if (loading) {
|
|
return <FullPageLoader />;
|
|
}
|
|
|
|
return (
|
|
<ErrorBoundary>
|
|
{isAuthenticated && user ? (
|
|
<MainDashboard user={user} onLogout={logout} />
|
|
) : (
|
|
<LoginForm />
|
|
)}
|
|
</ErrorBoundary>
|
|
);
|
|
};
|
|
|
|
export default App;
|