Claude/admiring clarke 7sdyyq#253
Open
krpeacock wants to merge 32 commits into
Open
Conversation
supporting weekly, biweekly, and monthly frequencies with timezone-aware time and duration fields. Closes #1
Builds on every push to main and publishes to ghcr.io/krpeacock/gathio:latest. Also removes the leftover exit 0 workaround in the Dockerfile now that the TypeScript migration is complete. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
pnpm install --prod skipped devDependencies including @types/* packages, causing tsc to fail. Install everything, compile, then prune back to prod-only before the final image stage. --ignore-scripts bypasses pnpm's supply-chain policy prompt for build tools like esbuild. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
generate-timezones.js was crashing on pages without a #timezone element, leaving #recurrenceTimezone empty. Now handles both selectors safely. group-edit.js was gating on a generateTimezoneOptions function that was never defined; replaced with direct select2 init on the populated element. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
eventGroupURL was sending the string "undefined" when no URL was set, failing URL validation. Default to empty string instead. Timezone select2 was initializing before generate-timezones.js had populated the options (document.ready race). Moved init to shown.bs.modal so options are guaranteed to exist, and added dropdownParent so the dropdown renders correctly inside the modal. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After a successful image push, joins the Tailscale network via OAuth and calls the Portainer stack webhook to trigger an automatic redeploy. Requires TS_OAUTH_CLIENT_ID, TS_OAUTH_SECRET, and PORTAINER_WEBHOOK_URL secrets, and tag:ci in the Tailscale ACL. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Timezone: render options server-side with America/Los_Angeles default, remove select2 dependency that was causing empty dropdown - Group URL: treat empty/undefined as optional in both JS and validation - Dockerfile: lowercase BUILD_IMAGE stage name - Actions: pin to Node 24-compatible versions Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The UI uses recurrenceNthDayOfWeek for the nth-weekday day picker, but both POST and PUT handlers were reading recurrenceDayOfWeek (the weekly field) for all cases. This caused the wrong weekday to be stored — e.g. Friday was stored as Monday. Now reads the correct field based on monthlyType. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previously events were only generated on container startup and daily. Now creating or editing a group with recurrence enabled immediately generates upcoming events, so fixes take effect without a restart. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…pe field conflict Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…nces Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Generated instances inherit the template's edit token so the creator can edit any occurrence with a single token. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rror handling Group events were failing validation (missing type, empty location string). Per-entry try/catch prevents one broken series from blocking others. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- computeOccurrences: convert resolved date to rule timezone before setting hour/minute so 18:00 means local time, not UTC - deleteevent route: skip ActivityPub broadcast for events without activityPubActor (auto-generated instances) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add excludedDates to IEventGroup interface and EventGroupSchema (was only in local working tree, not committed) - Explicitly import IEventGroup type in recurrence.ts to make the type annotation unambiguous for TypeScript in all build environments - Include packageManager field in package.json (pnpm corepack) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Conflicts with the pnpm version specified in the GitHub Actions workflow config, causing ERR_PNPM_BAD_PM_VERSION during CI setup. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Recurring event generation produced duplicate Event documents whenever `generateRecurringEvents` ran concurrently or on different calendar days. Three causes are addressed: 1. Application-level dedup with no DB guarantee. Replaced read-then-insert with `Event.updateOne(..., upsert: true)` keyed on a new compound unique index `(recurrenceId, occurrenceKey)`. Concurrent runs now collapse to a single document per occurrence; E11000 from the loser is swallowed. 2. Non-anchored biweekly stepping. `computeOccurrences` now steps in fixed units from a canonical `anchorDate` stored on the rule, never from `now`. The same series produces the same UTC instants regardless of when generation runs. Anchor is set explicitly to the template event's start on event-level rules and synthesized (and persisted) on first generation for legacy group-level rules. 3. Stale occurrences after rule edits. Group edits that move the rule (frequency / weekday / time / timezone / monthly mode change) now clear future auto-generated, attendee-free instances before regenerating, so edits update future instances instead of duplicating them. Legacy group-level recurrence now also flows through the upsert path under a synthetic `recurrenceId = "group:<groupId>"`, with one-shot backfill of `recurrenceId`/`occurrenceKey` on existing rows so the unique index applies to them too. Pre-existing duplicates are logged and skipped; a separate dedup migration is still required before the index can fully apply on databases that already contain duplicates. Refs: #7
…eTimezone not sent
Two bugs prevented newly-created recurring events from generating occurrences:
1. recurrenceTime was never auto-populated from the event's start time.
The form field started as "" and computeOccurrences() received rule.time = "",
causing moment.hour(NaN) → invalid candidate moments → zero occurrences.
Fix: watch recurrenceEnabled and eventStart in Alpine init() to auto-populate
recurrenceTime from the event's start datetime substring.
2. The recurrenceTimezone <select> had no x-model, so Alpine's data.recurrenceTimezone
stayed as "" regardless of the user's selection. The server stored rule.timezone = "",
causing moment-timezone to silently use UTC for all occurrence stepping.
Fix: read the select's DOM value in submitForm() (same pattern as editEventGroupForm).
Also fixes the same timezone bug in newEventGroupForm, which lacked both recurrence
fields in data{} and the DOM-read fix in submitForm(). Adds server-side validation
that rejects event creation with invalid/missing recurrenceTime or recurrenceTimezone
when recurrenceEnabled=true, surfacing the error clearly instead of silently storing
broken recurrence data.
https://claude.ai/code/session_018jCUGYAWLjrtP5525S32u4
…s test
When the user reports "I marked the event recurring but it isn't repeating,"
the previous form behavior gave them nothing to troubleshoot with:
- A blank recurrenceTime / recurrenceTimezone was silently accepted client-
side, accepted server-side, and only manifested as zero generated
occurrences hours later.
- Non-400 errors swallowed the actual server message and showed "An
unexpected error has occurred."
This change:
1. validateRecurrenceFields (public/js/util.js) mirrors the server's
validation rules and runs before submit. If any field is invalid, the
form renders the field-level message and decorates the offending input
with `is-invalid` — no network round-trip.
2. extractErrors (public/js/util.js) reads `response.errors`,
`response.message`, or the raw response body in that order, so the
user sees the real reason for a non-200, not a generic message.
3. newEventForm and newEventGroupForm both use the helpers and report
network failures with the underlying error message.
4. New Cypress test cypress/e2e/recurringEvent.cy.ts covers two paths:
a) Happy path — recurrenceTime auto-populates from eventStart, event
submits, /api/event confirms recurrence.time and timezone are
non-empty, /api/recurrence/generate produces multiple instances.
b) Validation path — clearing recurrenceTime shows the inline error
and decorates the field; no submit happens.
https://claude.ai/code/session_018jCUGYAWLjrtP5525S32u4
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
No description provided.