mirror of
https://github.com/nagaoo0/HabbitGrid.git
synced 2026-01-11 23:44:55 +00:00
Add src
This commit is contained in:
162
src/pages/HomePage.jsx
Normal file
162
src/pages/HomePage.jsx
Normal file
@@ -0,0 +1,162 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
import { Plus, Settings, TrendingUp, Flame, Calendar } from 'lucide-react';
|
||||
import { Button } from '../components/ui/button';
|
||||
import { useToast } from '../components/ui/use-toast';
|
||||
import HabitCard from '../components/HabitCard';
|
||||
import { getHabits } from '../lib/storage';
|
||||
|
||||
const HomePage = () => {
|
||||
const navigate = useNavigate();
|
||||
const { toast } = useToast();
|
||||
const [habits, setHabits] = useState([]);
|
||||
const [isPremium] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
loadHabits();
|
||||
}, []);
|
||||
|
||||
const loadHabits = () => {
|
||||
const loadedHabits = getHabits();
|
||||
setHabits(loadedHabits);
|
||||
};
|
||||
|
||||
const handleAddHabit = () => {
|
||||
if (!isPremium && habits.length >= 1000) {
|
||||
toast({
|
||||
title: "🔒 Premium Feature",
|
||||
description: "Free tier limited to 1000 habits. Upgrade to unlock unlimited habits!",
|
||||
duration: 4000,
|
||||
});
|
||||
return;
|
||||
}
|
||||
navigate('/add');
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-br from-slate-50 via-white to-slate-50 dark:from-slate-950 dark:via-slate-900 dark:to-slate-950">
|
||||
<div className="max-w-6xl mx-auto px-4 py-6 sm:px-6 lg:px-8">
|
||||
{/* Header */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: -20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
className="flex items-center justify-between mb-8"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-12 h-12 bg-gradient-to-br from-green-400 to-emerald-600 rounded-xl flex items-center justify-center shadow-lg">
|
||||
<div className="grid grid-cols-4 gap-0.5">
|
||||
{[...Array(11)].map((_, i) => (
|
||||
<div key={i} className="w-1.5 h-1.5 bg-white rounded-sm opacity-90" />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold bg-gradient-to-r from-slate-900 to-slate-700 dark:from-white dark:to-slate-300 bg-clip-text text-transparent">
|
||||
HabitGrid
|
||||
</h1>
|
||||
<p className="text-sm text-muted-foreground">Commit to yourself, one square at a time</p>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
onClick={() => navigate('/settings')}
|
||||
className="rounded-full"
|
||||
>
|
||||
<Settings className="w-5 h-5" />
|
||||
</Button>
|
||||
</motion.div>
|
||||
|
||||
{/* Stats Overview */}
|
||||
{habits.length > 0 && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: 0.1 }}
|
||||
className="grid grid-cols-2 sm:grid-cols-2 gap-4 mb-8"
|
||||
>
|
||||
<div className="bg-white dark:bg-slate-800 rounded-2xl p-4 shadow-sm border border-slate-200 dark:border-slate-700">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Calendar className="w-4 h-4 text-blue-500" />
|
||||
<span className="text-xs font-medium text-muted-foreground">Active Habits</span>
|
||||
</div>
|
||||
<p className="text-2xl font-bold">{habits.length}</p>
|
||||
</div>
|
||||
<div className="bg-white dark:bg-slate-800 rounded-2xl p-4 shadow-sm border border-slate-200 dark:border-slate-700">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<TrendingUp className="w-4 h-4 text-green-500" />
|
||||
<span className="text-xs font-medium text-muted-foreground">Total Streaks</span>
|
||||
</div>
|
||||
<p className="text-2xl font-bold">
|
||||
{habits.reduce((sum, h) => sum + (h.currentStreak || 0), 0)}
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* Habits List */}
|
||||
<div className="space-y-4">
|
||||
<AnimatePresence mode="popLayout">
|
||||
{habits.map((habit, index) => (
|
||||
<motion.div
|
||||
key={habit.id}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, scale: 0.95 }}
|
||||
transition={{ delay: index * 0.05 }}
|
||||
>
|
||||
<HabitCard habit={habit} onUpdate={loadHabits} />
|
||||
</motion.div>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
{/* Empty State */}
|
||||
{habits.length === 0 && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.95 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
className="text-center py-16"
|
||||
>
|
||||
<div className="w-24 h-24 bg-gradient-to-br from-green-100 to-emerald-100 dark:from-green-900/20 dark:to-emerald-900/20 rounded-3xl flex items-center justify-center mx-auto mb-6">
|
||||
<Flame className="w-12 h-12 text-green-600 dark:text-green-400" />
|
||||
</div>
|
||||
<h2 className="text-2xl font-bold mb-2">Create your grid!</h2>
|
||||
<p className="text-muted-foreground mb-8 max-w-md mx-auto">
|
||||
Create your first habit and watch your progress every day as you fill in the squares. Small steps lead to big changes!
|
||||
</p>
|
||||
<Button
|
||||
onClick={handleAddHabit}
|
||||
size="lg"
|
||||
className="rounded-full shadow-lg hover:shadow-xl transition-shadow"
|
||||
>
|
||||
<Plus className="w-5 h-5 mr-2" />
|
||||
Create First Habit
|
||||
</Button>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{/* Add Button */}
|
||||
{habits.length > 0 && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ delay: 0.3 }}
|
||||
className="fixed bottom-6 right-6"
|
||||
>
|
||||
<Button
|
||||
onClick={handleAddHabit}
|
||||
size="lg"
|
||||
className="rounded-full w-14 h-14 shadow-2xl hover:shadow-3xl transition-all hover:scale-110"
|
||||
>
|
||||
<Plus className="w-6 h-6" />
|
||||
</Button>
|
||||
</motion.div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default HomePage;
|
||||
Reference in New Issue
Block a user