本週完成度:{completion}%
例:冒險→「用インカム回報一次」;療癒→「睡前自我擁抱」
import React, { useMemo, useState } from "react"; import { motion } from "framer-motion"; import { Card, CardContent, CardHeader, CardTitle, } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"; import { Progress } from "@/components/ui/progress"; import { Badge } from "@/components/ui/badge"; import { Checkbox } from "@/components/ui/checkbox"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { CalendarDays, Sparkles, Heart, CheckCircle2, Plus, Flag, CloudSun, Mountain, FlameKindling, Flower2, ChevronRight } from "lucide-react"; /** * Grow Me 成長之島|單頁 Web 版本 * - 雙模式:冒險者 Adventure / 療癒者 Healing * - 本地 state 儲存(可擴充到後端) * - 每日任務、技能卡、心情紀錄、島嶼視覺(SVG) */ const seedWeek = () => { const today = new Date(); const start = new Date(today); start.setDate(start.getDate() - ((today.getDay() + 6) % 7)); // Monday start const days = Array.from({ length: 7 }).map((_, i) => { const d = new Date(start); d.setDate(start.getDate() + i); return { date: d, tasks: { adventure: [ { id: crypto.randomUUID(), text: "主動用敬語詢問一次", done: false }, { id: crypto.randomUUID(), text: "用インカム回報一次", done: false }, ], healing: [ { id: crypto.randomUUID(), text: "3分鐘肩頸伸展", done: false }, { id: crypto.randomUUID(), text: "寫下一句感謝", done: false }, ], }, mood: "🙂", }; }); return days; }; const defaultSkills = [ { key: "敬語應對", level: 2, xp: 40, color: "from-indigo-400 to-fuchsia-400" }, { key: "インカム溝通", level: 1, xp: 60, color: "from-sky-400 to-cyan-400" }, { key: "飲み放題說明", level: 2, xp: 25, color: "from-emerald-400 to-lime-400" }, { key: "料理補充", level: 1, xp: 15, color: "from-amber-400 to-orange-400" }, { key: "身心照護", level: 2, xp: 50, color: "from-rose-400 to-pink-400" }, ]; export default function GrowMeIsland() { const [mode, setMode] = useState("adventure"); const [week, setWeek] = useState(seedWeek()); const [skills, setSkills] = useState(defaultSkills); const [newTask, setNewTask] = useState(""); const [selectedDayIndex, setSelectedDayIndex] = useState(() => { const today = new Date(); return (today.getDay() + 6) % 7; // Monday=0 }); const day = week[selectedDayIndex]; const completion = useMemo(() => { const total = week.reduce((acc, d) => acc + d.tasks.adventure.length + d.tasks.healing.length, 0); const done = week.reduce( (acc, d) => acc + d.tasks.adventure.filter((t) => t.done).length + d.tasks.healing.filter((t) => t.done).length, 0 ); return Math.round((done / Math.max(1, total)) * 100); }, [week]); const islandBloom = useMemo(() => { // 決定島上元素數量:依完成度與技能平均等級 const avgLv = skills.reduce((a, s) => a + s.level, 0) / skills.length; const flowers = Math.min(30, Math.floor((completion / 100) * 20 + avgLv * 2)); const trees = Math.min(12, Math.floor(avgLv + completion / 20)); const fireflies = completion > 60 ? Math.floor((completion - 60) / 5) : 0; return { flowers, trees, fireflies }; }, [completion, skills]); const toggleTask = (taskId: string) => { setWeek((prev) => prev.map((d, i) => i === selectedDayIndex ? { ...d, tasks: { ...d.tasks, [mode]: d.tasks[mode as "adventure" | "healing"].map((t) => t.id === taskId ? { ...t, done: !t.done } : t ), }, } : d ) ); }; const addTask = () => { if (!newTask.trim()) return; setWeek((prev) => prev.map((d, i) => i === selectedDayIndex ? { ...d, tasks: { ...d.tasks, [mode]: [ ...d.tasks[mode as "adventure" | "healing"], { id: crypto.randomUUID(), text: newTask.trim(), done: false }, ], }, } : d ) ); setNewTask(""); }; const gainXP = (skillKey: string, amount = 10) => { setSkills((prev) => prev.map((s) => { if (s.key !== skillKey) return s; const newXP = s.xp + amount; if (newXP >= 100) { return { ...s, level: s.level + 1, xp: newXP - 100 }; } return { ...s, xp: newXP }; }) ); }; return (
本週完成度:{completion}%
例:冒險→「用インカム回報一次」;療癒→「睡前自我擁抱」