commit b09cefad34ba27f9dc0850614ec081bb235df33a Author: hera Date: Mon Apr 6 13:46:31 2026 +0000 Initial commit: Schedule Planner diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6aeb1e3 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,7 @@ +FROM python:3.12-alpine +WORKDIR /app +COPY server.py . +COPY index.html sleep-buddy.html favicon.svg icon-180.png notebook.jpg manifest.json sw.js /app/static/ +ENV DATA_DIR=/data STATIC_DIR=/app/static PORT=8080 +EXPOSE 8080 +CMD ["python3", "server.py"] diff --git a/capture.html b/capture.html new file mode 100644 index 0000000..67d428a --- /dev/null +++ b/capture.html @@ -0,0 +1,316 @@ + + + + + + + + + + + + +随手记 + + + + +
+
+
随手记
+
+ +
+
+ + + + +
+ +
+ +
+
最近记录
+
+
+ +
+ + 记下了! +
+ + + + diff --git a/favicon.svg b/favicon.svg new file mode 100644 index 0000000..ce8ba22 --- /dev/null +++ b/favicon.svg @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/icon-180.png b/icon-180.png new file mode 100644 index 0000000..56327f9 Binary files /dev/null and b/icon-180.png differ diff --git a/icon-180.svg b/icon-180.svg new file mode 100644 index 0000000..0e565fd --- /dev/null +++ b/icon-180.svg @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + Hera + diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..d96cc05 --- /dev/null +++ b/icon.svg @@ -0,0 +1,4 @@ + + + ✏️ + diff --git a/index.html b/index.html new file mode 100644 index 0000000..15c4a81 --- /dev/null +++ b/index.html @@ -0,0 +1,7608 @@ + + + + + + + + +Hera's Planner + + + + + + + + + +
+ + +
+ + +
+
+
+
+
🌙
+
该准备休息啦
+
今天也辛苦了,好好睡一觉,明天会更好
+ +
+
+
+ +
+
+
+

☰ Hera's Planner v118

+
+ +
+
+
+
+
+ + + + + + + +
+
+ + + + + + +
+
+
+
+
+ + +
+
+
+

💪 健身记录

+ +
+
+
+
+ + +
+
+
+

🌸 经期记录

+ +
+
+
+
+
+ + +
+
+
+
+

活动模块

+
+
+ + +
+
+
+
+
+
+ + + + + +
+
+
+
+
+ + +
+
+
+
+
+
+ 模板内容可以在下方 JS 的 WEEK_SCHEDULE 中编辑 +
+
+
+ + +
+
+ +
+ + +
+ +
+
+ + +
+
+ +
+
+
+
+
+ + +
+
+
+

本周回顾

+
+
+ +
+
+

历史回顾 ›

+ +
+
+ + +
+
+
+

🐛 Bug 追踪

+ +
+
+
+
+ + + +
+
+ +
+
+ + +
+
+ + + + + + + + +
+
+ + +
+ + +
+ +
+ +
+
+ + +
+
+ +
+
+

今日打卡

+ +
+
+ +
+ + +
+
+

本月计划

+
+
+ + + +
+ +
+
+
+
+ + +
+

物品池 点击添加到本月计划

+
+
+ + +
+
+ + +
+
+

健康日记

+ +
+ +
+
+
+ + +
+
+
+
+

今日练习

+ +
+
+ +
+
+
+

练习记录

+
+
+ + + +
+ +
+
+
+
+
+

练习项目 点击添加到本月计划

+
+
+ + +
+
+
+
+ + +
+
+
+
+

今日目标打卡

+ +
+
+
+ +
+
+

我的目标

+ +
+
+
+
+
+ + +
+
+

新目标

+ + + + +
+ + +
+
+
+ + +
+
+
+

我的清单

+ +
+ +
+
+
+
+ + +
+
+
+
+

个人文档

+

随手记会自动识别内容,归档到对应文档

+
+ +
+
+
+
+ + +
+
+

新建文档

+ + + +
+ + +
随手记中包含这些词的内容会自动归档到这个文档
+ + +
+ + +
+
+
+ +
+
+ +
+
+
--
+
本月平均入睡
+
+
+
+

记录睡眠

+
+ + +
+
+
+
+
+ 目标入睡 +
+ 22:00 + +
+
+
+
+
+ +
+
+

睡眠趋势

+
+
+ + + +
+ + +
+
+
+ +
+ +
+ +
+

记录明细

+ + + +
日期入睡时间
+ +
+ +
+
+ + +
+
+

编辑模块

+ + + +
+ +
+
+ + +
+
+
+ + +
+ +
+ +
+
+
+ +
+
+
+ + + + diff --git a/k8s-backup-cronjob.yaml b/k8s-backup-cronjob.yaml new file mode 100644 index 0000000..d24bc04 --- /dev/null +++ b/k8s-backup-cronjob.yaml @@ -0,0 +1,51 @@ +apiVersion: batch/v1 +kind: CronJob +metadata: + name: planner-backup-minio + namespace: planner +spec: + schedule: "0 */6 * * *" # every 6 hours + successfulJobsHistoryLimit: 3 + failedJobsHistoryLimit: 3 + jobTemplate: + spec: + template: + spec: + containers: + - name: backup + image: python:3.12-alpine + command: + - /bin/sh + - -c + - | + apk add --no-cache curl >/dev/null 2>&1 + curl -sL https://dl.min.io/client/mc/release/linux-arm64/mc -o /usr/local/bin/mc + chmod +x /usr/local/bin/mc + mc alias set s3 http://minio.minio.svc:9000 admin HpYMIVH0WN79VkzF4L4z8Zx1 + TS=$(date +%Y%m%d_%H%M%S) + # Backup main data + mc cp /data/planner_data.json "s3/planner-backups/planner_${TS}.json" + # Backup buddy data + mc cp /data/sleep_buddy.json "s3/planner-backups/buddy_${TS}.json" 2>/dev/null || true + # Keep only last 60 backups (10 days worth at 6h interval) + mc ls s3/planner-backups/ --json | python3 -c " + import sys, json + files = [] + for line in sys.stdin: + d = json.loads(line) + if d.get('key','').startswith('planner_'): + files.append(d['key']) + files.sort() + for f in files[:-60]: + print(f) + " | while read f; do mc rm "s3/planner-backups/$f"; done + echo "Backup done: ${TS}" + volumeMounts: + - name: data + mountPath: /data + readOnly: true + volumes: + - name: data + persistentVolumeClaim: + claimName: planner-data + restartPolicy: OnFailure diff --git a/manifest.json b/manifest.json new file mode 100644 index 0000000..a67f651 --- /dev/null +++ b/manifest.json @@ -0,0 +1,11 @@ +{ + "name": "Hera's Planner", + "short_name": "Planner", + "start_url": "/", + "display": "standalone", + "background_color": "#f0f2f5", + "theme_color": "#667eea", + "icons": [ + { "src": "icon-180.png", "sizes": "180x180", "type": "image/png" } + ] +} diff --git a/notebook.jpg b/notebook.jpg new file mode 100644 index 0000000..0dba23c Binary files /dev/null and b/notebook.jpg differ diff --git a/schedule-tool.jsx b/schedule-tool.jsx new file mode 100644 index 0000000..5636ab6 --- /dev/null +++ b/schedule-tool.jsx @@ -0,0 +1,449 @@ +import { useState, useEffect } from "react"; + +const STORAGE_KEY = "mama-schedule-v1"; + +const WEEK_SCHEDULE = { + 周一: { + fixed: [ + { time: "5:45", task: "起床 · 深度工作 ⭐", type: "work" }, + { time: "6:45", task: "孩子起床 · 做早饭", type: "family" }, + { time: "8:00", task: "孩子自己上学 · 继续工作黄金时段", type: "work" }, + { time: "12:00", task: "午饭", type: "life" }, + { time: "13:00", task: "下午工作 + 家务30分钟", type: "work" }, + { time: "15:30", task: "出门接孩子", type: "family" }, + { time: "16:40", task: "接孩子回家", type: "family" }, + { time: "17:30", task: "做晚饭", type: "life" }, + { time: "19:00", task: "陪练琴 / 作业", type: "family" }, + { time: "20:00", task: "陪读书", type: "family" }, + { time: "20:30", task: "孩子入睡 · 轻量工作 + 日记", type: "work" }, + { time: "22:00", task: "🌙 洗漱睡觉", type: "sleep" }, + ], + note: "最自由的一天,保护上午深度工作时间", + }, + 周二: { + fixed: [ + { time: "5:45", task: "起床 · 早晨个人时间 / 日记", type: "self" }, + { time: "6:45", task: "孩子起床 · 做早饭", type: "family" }, + { time: "7:40", task: "送孩子出门", type: "family" }, + { time: "8:30", task: "去朋友家工作 ⭐", type: "work" }, + { time: "15:30", task: "出门接孩子", type: "family" }, + { time: "16:40", task: "回家 · 做晚饭", type: "life" }, + { time: "19:00", task: "陪练琴 / 作业", type: "family" }, + { time: "20:00", task: "陪读书", type: "family" }, + { time: "20:30", task: "孩子入睡 · 轻量工作 + 回消息", type: "work" }, + { time: "22:00", task: "🌙 洗漱睡觉", type: "sleep" }, + ], + note: "朋友家是深度工作最佳环境,珍惜这段时间", + }, + 周三: { + fixed: [ + { time: "5:45", task: "起床 · 轻量工作 / 回消息", type: "work" }, + { time: "6:45", task: "孩子起床 · 做早饭", type: "family" }, + { time: "7:40", task: "送孩子 → 直接去健身 🏋️", type: "self" }, + { time: "10:30", task: "回家 · 洗澡", type: "self" }, + { time: "11:00", task: "深度工作 ⭐", type: "work" }, + { time: "12:30", task: "午饭", type: "life" }, + { time: "13:00", task: "继续工作", type: "work" }, + { time: "15:30", task: "出门接孩子", type: "family" }, + { time: "16:40", task: "回家", type: "family" }, + { time: "17:30", task: "做晚饭(简化)", type: "life" }, + { time: "19:00", task: "陪练琴 / 作业", type: "family" }, + { time: "20:00", task: "陪读书", type: "family" }, + { time: "20:30", task: "孩子入睡 · 只写日记,不工作", type: "self" }, + { time: "22:00", task: "🌙 洗漱睡觉", type: "sleep" }, + ], + note: "健身日晚上不安排工作,上午已补回来了", + }, + 周四: { + fixed: [ + { time: "5:45", task: "起床 · 深度工作 ⭐", type: "work" }, + { time: "6:45", task: "孩子起床 · 做早饭", type: "family" }, + { time: "7:40", task: "送孩子出门", type: "family" }, + { time: "8:00", task: "回家 · 继续深度工作", type: "work" }, + { time: "12:00", task: "午饭", type: "life" }, + { time: "13:00", task: "工作 + 家务30分钟", type: "work" }, + { time: "15:30", task: "出门接孩子", type: "family" }, + { time: "16:40", task: "接孩子回家", type: "family" }, + { time: "17:30", task: "做晚饭", type: "life" }, + { time: "19:00", task: "陪练琴 / 作业", type: "family" }, + { time: "20:00", task: "陪读书", type: "family" }, + { time: "20:30", task: "孩子入睡 · 轻量工作 + 日记", type: "work" }, + { time: "22:00", task: "🌙 洗漱睡觉", type: "sleep" }, + ], + note: "早晨是黄金深度工作时间,下午被接送切割", + }, + 周五: { + fixed: [ + { time: "5:45", task: "起床 · 轻量工作 / 回消息", type: "work" }, + { time: "6:45", task: "孩子起床 · 做早饭", type: "family" }, + { time: "7:40", task: "送孩子 → 直接去健身 🏋️", type: "self" }, + { time: "10:30", task: "回家 · 洗澡", type: "self" }, + { time: "11:00", task: "深度工作 ⭐", type: "work" }, + { time: "12:30", task: "午饭", type: "life" }, + { time: "13:00", task: "继续工作", type: "work" }, + { time: "15:30", task: "出门接孩子", type: "family" }, + { time: "16:40", task: "回家", type: "family" }, + { time: "17:30", task: "做晚饭(简化)", type: "life" }, + { time: "19:00", task: "陪练琴 / 作业", type: "family" }, + { time: "20:00", task: "陪读书", type: "family" }, + { time: "20:30", task: "孩子入睡 · 只写日记,不工作", type: "self" }, + { time: "22:00", task: "🌙 洗漱睡觉", type: "sleep" }, + ], + note: "周五健身日,晚上放松,迎接周末", + }, +}; + +const TYPE_STYLE = { + work: { bg: "#e8f4fd", dot: "#3b82f6", label: "工作" }, + family: { bg: "#fef3c7", dot: "#f59e0b", label: "家庭" }, + self: { bg: "#f0fdf4", dot: "#22c55e", label: "自我" }, + life: { bg: "#fdf4ff", dot: "#a855f7", label: "生活" }, + sleep: { bg: "#f1f5f9", dot: "#64748b", label: "睡眠" }, +}; + +const DAYS = ["周一", "周二", "周三", "周四", "周五"]; +const WEEKS = ["第1-2周", "第3-4周", "第5-6周"]; +const SLEEP_TARGETS = { "第1-2周": "23:00前", "第3-4周": "22:30前", "第5-6周": "22:00前" }; + +function loadData() { + try { + const raw = localStorage.getItem(STORAGE_KEY); + return raw ? JSON.parse(raw) : {}; + } catch { return {}; } +} + +function saveData(data) { + try { localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); } catch {} +} + +export default function App() { + const [tab, setTab] = useState("today"); + const [selectedDay, setSelectedDay] = useState(DAYS[new Date().getDay() - 1] || "周一"); + const [data, setData] = useState(loadData); + const [newTask, setNewTask] = useState({ text: "", type: "brain" }); + const [weekReview, setWeekReview] = useState({ sleep: "", wins: "", issues: "", next: "" }); + const [currentWeek, setCurrentWeek] = useState("第1-2周"); + const [showAddTask, setShowAddTask] = useState(false); + + const todayKey = new Date().toISOString().slice(0, 10); + const tasks = data[todayKey]?.tasks || []; + const reviews = data.reviews || {}; + + function updateData(newData) { + setData(newData); + saveData(newData); + } + + function addTask() { + if (!newTask.text.trim()) return; + const updated = { + ...data, + [todayKey]: { + ...data[todayKey], + tasks: [...tasks, { id: Date.now(), text: newTask.text, type: newTask.type, done: false }], + }, + }; + updateData(updated); + setNewTask({ text: "", type: "brain" }); + setShowAddTask(false); + } + + function toggleTask(id) { + const updated = { + ...data, + [todayKey]: { + ...data[todayKey], + tasks: tasks.map(t => t.id === id ? { ...t, done: !t.done } : t), + }, + }; + updateData(updated); + } + + function deleteTask(id) { + const updated = { + ...data, + [todayKey]: { + ...data[todayKey], + tasks: tasks.filter(t => t.id !== id), + }, + }; + updateData(updated); + } + + function saveReview() { + const weekKey = `${currentWeek}-${new Date().toISOString().slice(0, 7)}`; + const updated = { + ...data, + reviews: { ...reviews, [weekKey]: { ...weekReview, date: new Date().toLocaleDateString("zh-CN") } }, + }; + updateData(updated); + setWeekReview({ sleep: "", wins: "", issues: "", next: "" }); + alert("回顾已保存 ✓"); + } + + const brainTasks = tasks.filter(t => t.type === "brain"); + const lightTasks = tasks.filter(t => t.type === "light"); + + return ( +
+ + + {/* Header */} +
+
+
+
我的时间管理
+

早睡计划

+
+
+
目标睡眠时间
+
{SLEEP_TARGETS[currentWeek]}
+ +
+
+ + {/* 6周进度 */} +
+
+ 6周养成计划 + {currentWeek} +
+
+
+
+
+ + {/* Tabs */} +
+ {[["today", "今日任务"], ["schedule", "日程表"], ["review", "周回顾"]].map(([key, label]) => ( + + ))} +
+
+ +
+ + {/* ===== 今日任务 ===== */} + {tab === "today" && ( +
+
+
+
{new Date().toLocaleDateString("zh-CN", { month: "long", day: "numeric", weekday: "long" })}
+
记录今天要处理的事情
+
+ +
+ + {showAddTask && ( +
+ setNewTask({ ...newTask, text: e.target.value })} + onKeyDown={e => e.key === "Enter" && addTask()} + style={{ marginBottom: 10 }} + autoFocus + /> +
+ + + +
+
+ )} + + {/* 脑力任务 */} +
+
+ 🧠 +
+
需要脑力的任务
+
安排在早晨 5:45 或上午时段
+
+
+ {brainTasks.length === 0 ? ( +
还没有脑力任务
点击「添加任务」→ 选择「需要脑力」
+ ) : brainTasks.map(t => ( +
+ + {t.text} + +
+ ))} +
+ + {/* 轻量任务 */} +
+
+ +
+
不需要思考的任务
+
留到晚上 20:30 后处理
+
+
+ {lightTasks.length === 0 ? ( +
还没有轻量任务
点击「添加任务」→ 选择「不需要思考」
+ ) : lightTasks.map(t => ( +
+ + {t.text} + +
+ ))} +
+ + {tasks.length > 0 && ( +
+
+ 完成进度 + {tasks.filter(t => t.done).length} / {tasks.length} 件 +
+
+
t.done).length / tasks.length * 100) : 0}%` }} /> +
+
+ )} +
+ )} + + {/* ===== 日程表 ===== */} + {tab === "schedule" && ( +
+
+ {DAYS.map(d => ( + + ))} +
+ + {WEEK_SCHEDULE[selectedDay] && ( + <> +
+ 💡 {WEEK_SCHEDULE[selectedDay].note} +
+ +
+ {WEEK_SCHEDULE[selectedDay].fixed.map((item, i) => { + const style = TYPE_STYLE[item.type]; + return ( +
+
{item.time}
+
+
+
+
+ {item.task} + {style.label} +
+
+ ); + })} +
+ + )} +
+ )} + + {/* ===== 周回顾 ===== */} + {tab === "review" && ( +
+
+
+
每周回顾
+
每周花10分钟复盘,调整下周计划
+
+ {currentWeek} +
+ +
+ {[ + { key: "sleep", label: "😴 这周睡眠情况", placeholder: "实际几点睡的?有没有改善?" }, + { key: "wins", label: "✨ 做得好的地方", placeholder: "哪些时间安排运转良好?" }, + { key: "issues", label: "🔧 遇到的问题", placeholder: "哪里卡住了?什么原因?" }, + { key: "next", label: "🎯 下周调整计划", placeholder: "下周想改变什么?具体怎么做?" }, + ].map(({ key, label, placeholder }) => ( +
+
{label}
+