const express = require("express"); const { DateTime } = require("luxon"); const Todo = require("../models/Todo"); const router = express.Router(); // Korea Standard Time zone const KST_TIMEZONE = "Asia/Seoul"; // Get day range in Korea Standard Time (Asia/Seoul) const getDayRange = () => { // Get current time in KST const nowKst = DateTime.now().setZone(KST_TIMEZONE); // Start of day in KST (00:00:00.000) const startOfDayKst = nowKst.startOf("day"); // End of day in KST (23:59:59.999) const endOfDayKst = nowKst.endOf("day"); // Convert to JavaScript Date objects (UTC) for MongoDB query const start = startOfDayKst.toJSDate(); const end = endOfDayKst.toJSDate(); return { start, end }; }; router.get("/", async (req, res) => { try { const todos = await Todo.find().sort({ dueDate: 1, createdAt: -1 }); res.json(todos); } catch (error) { res.status(500).json({ message: "Failed to fetch todos" }); } }); router.get("/today", async (req, res) => { try { const { start, end } = getDayRange(); // Show todos that are: // 1. Due today (between start and end of today) - both completed and incomplete // 2. Due in the future (not yet due) - only incomplete // 3. Have no due date set - both completed and incomplete (always show) // Filter out: overdue todos (due date before today) const todos = await Todo.find({ $or: [ // Today's todos (completed or not) - show with strikethrough if completed { dueDate: { $gte: start, $lte: end } }, // Future todos - only incomplete { dueDate: { $gt: end }, completed: false }, // No due date - always show (completed or not) { dueDate: { $exists: false } }, { dueDate: null } ] }).sort({ completed: 1, // Incomplete first dueDate: 1, createdAt: -1, }); res.json(todos); } catch (error) { res.status(500).json({ message: "Failed to fetch today todos" }); } }); // Normalize dueDate to noon KST to avoid timezone boundary issues const normalizeDueDate = (dueDate) => { if (!dueDate) return null; // Parse the date and set it to noon KST const date = DateTime.fromISO(dueDate, { zone: KST_TIMEZONE }); // Set to noon (12:00) of that day in KST return date.set({ hour: 12, minute: 0, second: 0, millisecond: 0 }).toJSDate(); }; router.post("/", async (req, res) => { try { const todoData = { ...req.body }; if (todoData.dueDate) { todoData.dueDate = normalizeDueDate(todoData.dueDate); } const todo = await Todo.create(todoData); res.status(201).json(todo); } catch (error) { res.status(400).json({ message: "Failed to create todo" }); } }); router.get("/:id", async (req, res) => { try { const todo = await Todo.findById(req.params.id); if (!todo) { return res.status(404).json({ message: "Todo not found" }); } res.json(todo); } catch (error) { res.status(400).json({ message: "Failed to fetch todo" }); } }); router.put("/:id", async (req, res) => { try { const updateData = { ...req.body }; if (updateData.dueDate) { updateData.dueDate = normalizeDueDate(updateData.dueDate); } const todo = await Todo.findByIdAndUpdate(req.params.id, updateData, { new: true }); if (!todo) { return res.status(404).json({ message: "Todo not found" }); } res.json(todo); } catch (error) { res.status(400).json({ message: "Failed to update todo" }); } }); router.delete("/:id", async (req, res) => { try { const todo = await Todo.findByIdAndDelete(req.params.id); if (!todo) { return res.status(404).json({ message: "Todo not found" }); } res.json({ ok: true }); } catch (error) { res.status(400).json({ message: "Failed to delete todo" }); } }); module.exports = router;