摘要
Day 16 我們把「任務」轉成可操作的任務卡,並提供三個時段格子(早/午/晚)。
使用者可以用點擊把任務卡放入某個時段格子;系統會記錄安排的時段(localStorage),下次開啟仍能看到結果。
假設已具備:
HTML(新增):
放在 </section><!-- Day 15 新增:條件分支 -->
之後、historySection 之前:
<!-- ★ Day16-NEW:任務安排頁 -->
<section id="taskScheduleSection" hidden role="region" aria-labelledby="task-schedule-title">
<h2 id="task-schedule-title">把任務放進你今天最有精神的時段</h2>
<!-- 任務卡容器(載入時會把最新任務渲染在這) -->
<div id="taskCardWrap" aria-live="polite"></div>
<!-- 三個時段格子 -->
<div class="time-grid" style="display:grid;grid-template-columns:repeat(3,1fr);gap:.75rem;">
<button id="morningSlot" class="time-slot" type="button" aria-label="早上格子(☀️)">☀️ 早上</button>
<button id="afternoonSlot" class="time-slot" type="button" aria-label="下午格子(🧠)">🧠 下午</button>
<button id="eveningSlot" class="time-slot" type="button" aria-label="晚上格子(🌃)">🌃 晚上</button>
</div>
<p id="scheduleFeedback" aria-live="polite" style="margin-top:.5rem;"></p>
<div style="margin-top:1rem;">
<button id="btnBackFromSchedule" aria-controls="recordFormSection">← 回到表單</button>
</div>
<div style="margin-top:1rem;">
<button id="btnHistoryFromSchedule" aria-controls="historySection">歷史回顧 →</button>
</div>
</section>
註:此處用 button 當格子,可聚焦、可用鍵盤操作,也便於 aria-label。
JavaScript 常數與狀態(新增):
放在常數區塊之後(或靠近其他 key 區域):
// ★ Day16-NEW:任務安排狀態
const SCHEDULE_KEY = 'task_schedule_map'; // { [recordId]: 'morning'|'afternoon'|'evening' }
let currentTaskId = null; // 目前顯示為任務卡的 recordId
// Day16-NEW:任務安排頁節點
const taskScheduleSection = document.getElementById('taskScheduleSection');
const taskCardWrap = document.getElementById('taskCardWrap');
const morningSlotEl = document.getElementById('morningSlot');
const afternoonSlotEl = document.getElementById('afternoonSlot');
const eveningSlotEl = document.getElementById('eveningSlot');
const scheduleFeedback = document.getElementById('scheduleFeedback');
const btnBackFromSchedule = document.getElementById('btnBackFromSchedule');
const btnHistoryFromSchedule = document.getElementById('btnHistoryFromSchedule');
JavaScript 儲存/讀取工具(新增):
// ★ Day16-NEW:讀/寫安排映射
function readScheduleMap() {
try { return JSON.parse(localStorage.getItem(SCHEDULE_KEY)) || {}; }
catch { return {}; }
}
function writeScheduleMap(map) {
localStorage.setItem(SCHEDULE_KEY, JSON.stringify(map || {}));
}
// 取得最新一筆紀錄(用於生成任務卡)
function getLatestRecord() {
const list = readRecords();
if (!list.length) return null;
// 預設以 createdAt 由新到舊
return list.slice().sort((a,b)=>b.createdAt - a.createdAt)[0];
}
JavaScript:渲染任務卡與放回既有安排(新增):
// ★ Day16-NEW:渲染任務卡(以最新紀錄為主)
function renderTaskCard() {
const rec = getLatestRecord();
taskCardWrap.innerHTML = '';
if (!rec) {
taskCardWrap.innerHTML = '<div class="muted">尚無任務,請先到表單新增。</div>';
currentTaskId = null;
return;
}
currentTaskId = rec.id;
// 建立任務卡
const card = document.createElement('div');
card.id = 'taskCard';
card.setAttribute('role','article');
card.setAttribute('aria-label', `任務卡:${rec.task}`);
card.style.border = '1px solid #ccc';
card.style.padding = '.5rem';
card.style.borderRadius = '.5rem';
card.style.background = '#fff';
card.textContent = rec.task;
taskCardWrap.appendChild(card);
// 若已有安排,放回對應格子
const map = readScheduleMap();
const period = map[rec.id];
if (period) {
const slot = period === 'morning' ? morningSlotEl
: period === 'afternoon' ? afternoonSlotEl
: period === 'evening' ? eveningSlotEl
: null;
if (slot) slot.appendChild(card);
scheduleFeedback.textContent = `已安排到:${PERIOD_LABEL[period] || period}`;
} else {
scheduleFeedback.textContent = '點擊上方任一格子,把任務排進今天的時段。';
}
}
JavaScript:點擊格子 → 佈署任務卡並保存(新增):
// ★ Day16-NEW:把任務卡放進某時段 + 保存
function placeTaskTo(period) {
const card = document.getElementById('taskCard');
if (!card || !currentTaskId) return;
const target = period === 'morning' ? morningSlotEl
: period === 'afternoon' ? afternoonSlotEl
: period === 'evening' ? eveningSlotEl
: null;
if (!target) return;
target.appendChild(card);
// 寫入安排映射
const map = readScheduleMap();
map[currentTaskId] = period;
writeScheduleMap(map);
scheduleFeedback.textContent = `已安排到:${PERIOD_LABEL[period] || period}`;
}
// 綁定三個格子
morningSlotEl?.addEventListener('click', () => placeTaskTo('morning'));
afternoonSlotEl?.addEventListener('click', () => placeTaskTo('afternoon'));
eveningSlotEl?.addEventListener('click', () => placeTaskTo('evening'));
JavaScript:導流與頁面切換(調整):
把分支頁的「馬上開始!」改為導到任務安排頁並渲染任務卡:
// [Day16-CHANGED] 分支互動:導到任務安排頁
btnStartNow?.addEventListener('click', () => {
decisionFeedback.textContent = '就是這種精神!來安排看看吧 💪';
showPage('taskSchedule');
});
// [Day16-NEW] 從安排頁回到表單
btnBackFromSchedule?.addEventListener('click', () => showPage('home'));
btnHistoryFromSchedule?.addEventListener('click', () => showPage('history'));
在 showPage(page) 中加入 taskSchedule 分支(新增):
} else if (page === 'taskSchedule') { // ★ Day16-NEW
if (taskScheduleSection) {
taskScheduleSection.hidden = false;
renderTaskCard(); // 顯示最新任務卡,並把既有安排放回格子
const first = document.getElementById('taskCard') || morningSlotEl;
if (first && typeof first.focus === 'function') first.focus();
}
JavaScript:提交後可直接安排(可選):
若你希望送出表單後就能直接安排,也可在 onSubmit() 的尾端補一行(選擇性):
// onSubmit 末尾(選擇性導流)
showPage('taskSchedule');