feat: add Sunsama Clone task management app - #2
Conversation
- Add Drizzle schema for sunsama_users and sunsama_tasks tables - Create sunsama-api Express backend with task CRUD, scheduling, and recurring tasks - Create sunsama-web React frontend with Today/Week/All Tasks views - Features: drag-drop scheduling, Pomodoro timer, recurring tasks, timeline view - Uses @dnd-kit, @tanstack/react-query, Zustand, Tailwind CSS v4
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
End-to-End Test ResultsRan frontend (localhost:5173) and backend (localhost:3002) locally against a local PostgreSQL database. Tested all core task management flows via browser GUI. All 5 test scenarios passed. Core Feature Tests
Evidence ScreenshotsTask Created — All Tasks (1)Toggle Completion — Strikethrough StylingDrag-Drop — Task Scheduled at 10AMWeek View — Task in Mon ColumnCreate Task Modal via N KeySettings Applied — Timeline Starts at 9AMNot Tested
|
| const isToday = (dateString: string | null) => { | ||
| if (!dateString) return false; | ||
| const taskDate = dateString.split("T")[0]; | ||
|
|
||
| const today = new Date(); | ||
| const year = today.getFullYear(); | ||
| const month = String(today.getMonth() + 1).padStart(2, "0"); | ||
| const day = String(today.getDate()).padStart(2, "0"); | ||
| const todayDate = `${year}-${month}-${day}`; | ||
|
|
||
| return taskDate === todayDate; | ||
| }; |
There was a problem hiding this comment.
🔴 Today view isToday compares UTC date from ISO string against local date, breaking task display in UTC+ timezones
The isToday function extracts the date portion from the server's UTC ISO string via dateString.split("T")[0] (yielding a UTC date like "2026-05-03"), but constructs todayDate from local date components (getFullYear(), getMonth(), getDate()), yielding a local date like "2026-05-04". In any UTC+ timezone (Europe, Asia, Africa, Oceania), these will differ when the UTC representation of local midnight falls on the previous UTC day. This means tasks planned for today won't appear in the Today view for roughly half the world's timezones.
Trace example for UTC+5
- User plans task for today (May 4 local) via
handlePlanForTodayinartifacts/sunsama-web/src/pages/Tasks.tsx:66-69 today.toISOString()sends"2026-05-03T19:00:00.000Z"to server- Server stores this timestamp, returns it as ISO string in API response
isTodaysplits:taskDate = "2026-05-03"(UTC),todayDate = "2026-05-04"(local)- No match → task disappears from Today view
| const isToday = (dateString: string | null) => { | |
| if (!dateString) return false; | |
| const taskDate = dateString.split("T")[0]; | |
| const today = new Date(); | |
| const year = today.getFullYear(); | |
| const month = String(today.getMonth() + 1).padStart(2, "0"); | |
| const day = String(today.getDate()).padStart(2, "0"); | |
| const todayDate = `${year}-${month}-${day}`; | |
| return taskDate === todayDate; | |
| }; | |
| const isToday = (dateString: string | null) => { | |
| if (!dateString) return false; | |
| const taskDate = new Date(dateString); | |
| const today = new Date(); | |
| return ( | |
| taskDate.getFullYear() === today.getFullYear() && | |
| taskDate.getMonth() === today.getMonth() && | |
| taskDate.getDate() === today.getDate() | |
| ); | |
| }; | |
Was this helpful? React with 👍 or 👎 to provide feedback.
| }, | ||
| } | ||
| ); | ||
| setShowTimerModal(false); |
There was a problem hiding this comment.
🟡 handleTimerComplete closes modal synchronously before mutation resolves, leaking timer state on error
setShowTimerModal(false) at line 137 runs synchronously right after updateMutation.mutate(), before the async mutation completes. This causes two issues: (1) the PomodoroTimer unmounts immediately, causing the floating timer button (artifacts/sunsama-web/src/pages/Today.tsx:213-234) to flash briefly until onSuccess calls stopAndClear(), and (2) on mutation failure, the onError handler at line 132 only sets setShowTimerModal(false) (already false) but never calls stopAndClear(), so activeTask persists in the zustand store indefinitely. The floating button remains visible with no recovery, and clicking it reopens the timer in a stale state.
Prompt for agents
The handleTimerComplete function in artifacts/sunsama-web/src/pages/Today.tsx:120-138 has a premature setShowTimerModal(false) call at line 137 that runs synchronously before the mutation resolves. This line should be removed entirely since modal closure is already handled in both the onSuccess and onError callbacks. Additionally, the onError callback at line 132-134 should call stopAndClear() to clean up the timer state when the mutation fails, preventing the stale floating timer button from persisting.
Was this helpful? React with 👍 or 👎 to provide feedback.
Summary
Adds a full-featured Sunsama Clone task management application as two new artifacts in the monorepo, based on welch5788/sunsama-clone.
What's included
Database schema (
lib/db/src/schema/)sunsama_users— user accounts for the task appsunsama_tasks— tasks with scheduling, time tracking, and recurring task supportBackend (
artifacts/sunsama-api/)@workspace/dbFrontend (
artifacts/sunsama-web/)Tech stack alignment
catalog:versions frompnpm-workspace.yamlfor shared deps@workspace/naming, esbuild bundling, tsconfig references)Review & Testing Checklist for Human
pnpm --filter @workspace/sunsama-web devand verify the frontend loads at localhost:5173pnpm --filter @workspace/sunsama-api devwithDATABASE_URLset and verify API responds at localhost:3002/healthNotes
DATABASE_URLenvironment variable setpnpm --filter @workspace/db pushto create the newsunsama_usersandsunsama_taskstablesVITE_API_URLin the frontend if the API runs on a different port/host than default (localhost:3002)Link to Devin session: https://app.devin.ai/sessions/79e1e9b181824f3aa1cf7b8efd8ca55a
Requested by: @TanUIUX