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 (

Grow Me 成長之島

Web beta
冒險者 療癒者
{/* Left: Island */} 成長之島視覺

本週完成度:{completion}%

{/* Right: Skills */} 技能卡
{skills.map((s) => (
{s.key}
Lv.{s.level}
經驗值 {s.xp}%
))}
{/* Tasks */} 本週任務
{week.map((d, i) => ( ))}
新增{mode === "adventure" ? "冒險" : "療癒"}任務
setNewTask(e.target.value)} />

例:冒險→「用インカム回報一次」;療癒→「睡前自我擁抱」

{/* Journal */} 今日一句話 { setWeek((prev) => prev.map((d, i) => (i === selectedDayIndex ? { ...d, mood } : d))); }} />
); } function TaskColumn({ title, tasks, onToggle }: { title: string; tasks: { id: string; text: string; done: boolean }[]; onToggle: (id: string) => void }) { return (
{title}
{tasks.map((t) => ( ))} {tasks.length === 0 && (
目前尚無任務,新增一個吧!
)}
); } function Journal({ day, onChange }: { day: any; onChange: (mood: string) => void }) { const [text, setText] = useState(""); return (
{"😀🙂😌🥲🥹😫".split("").map((m) => ( ))}
setText(e.target.value)} />
); } function IslandSVG({ completion, bloom, mode }: { completion: number; bloom: { flowers: number; trees: number; fireflies: number }; mode: string }) { const width = 1100; const height = 340; const skyGrad = mode === "adventure" ? ["#c7d2fe", "#f5d0fe"] : ["#d1fae5", "#a7f3d0"]; // indigo→fuchsia vs emerald tones // 生成隨機花與樹(以完成度為種子稍微穩定) const rng = (seed: number) => () => { seed = (seed * 9301 + 49297) % 233280; return seed / 233280; }; const rand = rng(completion + 7); const flowers = Array.from({ length: bloom.flowers }).map((_, i) => ({ x: 80 + rand() * 880, y: 200 + rand() * 110, r: 3 + rand() * 3, rot: rand() * 360, })); const trees = Array.from({ length: bloom.trees }).map((_, i) => ({ x: 120 + rand() * 840, y: 170 + rand() * 60, h: 20 + rand() * 40, })); const fireflies = Array.from({ length: bloom.fireflies }).map((_, i) => ({ x: 100 + rand() * 860, y: 80 + rand() * 80, })); return (
{/* sky */} {/* sun */} {/* sea */} {/* island base */} {/* path */} {/* trees */} {trees.map((t, i) => ( ))} {/* flowers */} {flowers.map((f, i) => ( ))} {/* fireflies */} {fireflies.map((p, i) => ( ))} {/* signboard */} Grow Me 成長之島
); }