mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
* feat(scheduled-tasks): position week/day chips at their exact minute Replace hour-bucketed event rendering with a per-day absolute overlay that places each task chip at timeToOffset(start), so a 5:38 task sits at 5:38 instead of the top of the 5:00 cell. Hour cells become click/gridline-only; the overlay is non-interactive so empty-space clicks still create. Removes the now-obsolete eventsByHour/hourKey/bucketEventsByHour path (month view already used eventsByDay). * feat(settings): user timezone preference for scheduled tasks Add a Timezone preference under Settings → General. Scheduled tasks now run in the user's chosen IANA zone instead of whatever device created them. - settings table gains a nullable `timezone` column (migration 0236); null means "use the browser-detected zone", so existing users are unchanged - contract: validated IANA `timezone` on the settings get/update shapes - useTimezone() resolves the saved zone or the browser fallback; the task modal captures it instead of recomputing the device zone - General settings adds a searchable timezone combobox defaulting to the detected zone - shared timezone util (getBrowserTimezone / getSupportedTimezones) * fix(scheduled-tasks): interpret launch/end times in the account timezone Address review: one-time runs and the end-of-day boundary were resolved in the browser zone, so a task could fire at the wrong instant when the account zone differed from the device. Resolve wall-clock launch/end through the account zone (DST-correct), and evaluate the past-launch guard and the default seed in that zone too — matching how the recurring cron is already evaluated. - timezone util: zonedWallClockToUtc (DST-correct, no library), wallClockNow; getSupportedTimezones falls back to a common set and always includes UTC - recurrenceToScheduleFields takes the timezone and resolves time/endsAt in it - settings timezone Label drops its dangling htmlFor - tests for the zone converter (UTC / +5:30 / DST) and the zoned mappings * feat(scheduled-tasks): Google-Calendar-style side-by-side overlap layout Tasks whose pills would collide now split the column into side-by-side lanes (like Google Calendar) instead of stacking on top of each other; tasks that don't overlap keep the full width. Adds a pure layoutColumn lane-assignment helper (interval clustering + greedy lane packing) with tests. * fix(scheduled-tasks): zone-consistent recurrence/edit + duplicate + loading Address review (zone consistency): - recurrenceToCron derives weekday/day-of-month from a UTC-parsed calendar date so the cron targets the right day regardless of device zone - cronToRecurrence + editSeedFor recover the launch date/time and ends-on date read back in the schedule's zone (zonedWallClock), so editing shows the right values when the account zone differs from the device - defaultLaunch no longer compares browser-local slot days against account-zone "today" Features: - right-click Duplicate: opens a pre-filled create modal from any task (TaskEditSeed now extends a shared TaskPrefill; modal gains a prefill prop) - loading.tsx paints only the header chrome (the page is a calendar, not a table) so it no longer pops table -> calendar; the empty calendar loads tasks in - task context menu drops "See details" (finished tasks open on click) * fix(scheduled-tasks): edit/duplicate use the task's own timezone, not the account one A task created in one zone but edited after the account zone changed (or duplicated) seeded its launch in the task's stored zone while validating and submitting in the current account zone, drifting unchanged run times. TaskPrefill now carries the task's timezone; the modal seeds AND submits in it for edit/duplicate, and only blank creates use the account zone. * fix(scheduled-tasks): duplicating a past one-time task seeds a future launch Audit follow-up: a duplicate of a one-time task whose launch already passed opened with Schedule disabled. It now falls through to the next-hour default so the new task is immediately schedulable. Also clarifies the DST spring-forward note on zonedWallClockToUtc and drops a stray test comment. * fix(scheduled-tasks): clear duplicate pre-fill when starting a fresh create The create modal had two open-sources (isCreateOpen + duplicatePrefill) that weren't coordinated. Starting a header/slot create now clears any duplicate pre-fill (and duplicating closes any open create), so a stale duplicate draft can never bleed into a blank or slot-seeded create. * fix(scheduled-tasks): make create/duplicate/edit modals mutually exclusive Opening any of the three modal flows (blank create, duplicate pre-fill, task edit/record) now closes the other two, so the create modal can never co-exist with the edit modal and no stale state survives a switch. * feat(scheduled-tasks): render calendar in the effective timezone Position each occurrence at its wall-clock time in the task's own timezone, and draw the now-line / today highlight in the viewer's effective zone, so the calendar always shows a task at the local time it was scheduled for — matching the modal. Adds zonedClockDate as the single zone boundary; the default case (account zone == browser zone) is unchanged. * fix(scheduled-tasks): re-sync calendar day frame when timezone resolves When useTimezone() resolves from the browser fallback to the saved account zone after mount, re-derive today (and the focused day, while it is still on today) so the grid frame, now-line, and fetched range stay in agreement. The focused day is preserved across the change once the user has navigated away. * fix(scheduled-tasks): pad view window for timezone slop; re-center scroll on zone change visibleRange now expands the rendered span by a day on each side so an occurrence whose own-zone display day is on screen is never filtered out by the account-zone frame; bucketEventsByDay still places each on its zoned day, dropping any off-screen. The week/day auto-scroll re-centers when the effective timezone resolves. * test(scheduled-tasks): pass timezone to cronToRecurrence ends-on case The second cronToRecurrence call omitted the required timezone, so the recovered end date depended on the test runner's system zone instead of the schedule zone. Pin it to UTC for determinism. * fix(scheduled-tasks): re-seed blank-create launch when timezone resolves useTimezone() starts on the browser fallback, so a blank create's next-top-of-the-hour default (and its past-launch guard) could be seeded in the wrong zone and submitted in the resolved account zone. Re-seed the default when the effective zone changes, unless the user has edited the fields; slot/edit/duplicate seeds are zone-stable and untouched. * fix(scheduled-tasks): DST fall-back resolve, today month-cell default, late-night pill bounds Audit follow-ups: - zonedWallClockToUtc resolves to the self-consistent instant, fixing one-time launches on the autumn fall-back day (were an hour early) while keeping the spring-forward gap rolling forward; adds DST regression tests. - defaultLaunch: today's whole-day (month-cell) click defaults to the next top of the hour like the header action, not a past 9am that disables Save. - DayEvents clips to the day bounds so a late-night pill never spills past the final hour row; now-line sits above event pills.