feat(hosted-events): dashboard handler, routes, UI templates (Phase D + E)#45
Closed
Ceesaxp wants to merge 1 commit into
Closed
feat(hosted-events): dashboard handler, routes, UI templates (Phase D + E)#45Ceesaxp wants to merge 1 commit into
Ceesaxp wants to merge 1 commit into
Conversation
…se D + E) Phase D + E of host-driven event scheduling. Builds on the HostedEventService introduced in #44 and exposes it through 12 dashboard routes plus 6 HTML templates. Sidebar gets a "Schedule Event" item between Templates and Bookings. ## Routes (cmd/server/main.go) - GET /dashboard/events — list (?archived=true to include) - GET /dashboard/events/new — create form (?template=ID prefills) - POST /dashboard/events — create - GET /dashboard/events/check-conflicts — HTMX partial; busy-time merge - GET /dashboard/events/attendee-search — HTMX partial; contact autocomplete - GET /dashboard/events/{id}/details — modal partial - GET /dashboard/events/{id}/edit — full edit form - POST /dashboard/events/{id}/edit — update - POST /dashboard/events/{id}/cancel — cancel - POST /dashboard/events/{id}/archive — archive - POST /dashboard/events/{id}/unarchive — unarchive - POST /dashboard/events/{id}/retry-calendar — retry calendar event creation The HTMX partial routes use the /events/check-conflicts and /events/attendee-search paths so they don't shadow {id}. ## Handler (internal/handlers/dashboard_events.go) DashboardEventsHandler wired on the Handlers struct alongside Dashboard. Form parsing uses parallel-indexed repeated form fields (attendee_email/_name/_contact_id), matching the existing pattern used by the templates handler. parseDateTimeInTZ combines the form's date + time + timezone fields into a UTC time.Time before handing to the service. Update flow uses pointer fields on UpdateHostedEventInput so the form can express "unchanged" vs "set to empty". ## Templates (Phase E) All six new templates reuse the existing primitives — `.section`, `.section-header`, `.form-input`, `.form-select`, `.form-textarea`, `.form-label`, `.form-hint`, `.btn .btn-primary/.btn-secondary/.btn-outline`, `.btn-sm`, `.badge`, `.table-container/.table`, `.duration-chips`, `.radio-cards`, `.modal-overlay/.modal-content/.modal-header/.modal-body`, `.empty-state`, `.alert/.alert-warning/.alert-error`. No new CSS files; the attendee chip styling is a small inline block confined to the form template. - templates/pages/dashboard_events.html — list + filter view - templates/pages/dashboard_event_form.html — create + edit form - templates/partials/events_table_partial.html — table body - templates/partials/event_attendee_picker_results.html — autocomplete results - templates/partials/event_conflict_warning.html — soft conflict banner - templates/partials/dashboard_event_detail_partial.html — modal detail The form has 7 sections (Template prefill (new only) → Basics → When → Location → Calendar → Attendees) with HTMX-driven conflict warnings and contact autocomplete. Modal pattern matches the booking flow's recent fetch-based approach (#41 pattern). ## Tests internal/handlers/dashboard_events_test.go — 9 cases: - TestEventAttendeePickerResults_RendersContacts - TestEventAttendeePickerResults_EmptyShowsFreeFormHint - TestEventAttendeePickerResults_EmptyAndNoQuery_RendersNothing - TestEventConflictWarning_RendersConflicts (asserts pluralisation + soft-warning copy) - TestEventConflictWarning_NoConflicts_RendersEmpty - TestEventsTablePartial_RendersScheduledAndCancelled (status-routed badges + actions) - TestEventForm_NewMode_RendersTitleAndAttendeePicker - TestEventForm_EditMode_PrefillsAndCarriesExcludeEventID (zoom + 45-min + exclude_event_id) - TestEventForm_FormErrorRendersHumanCopy (at_least_one_attendee → human copy) Pattern matches internal/handlers/template_test.go: load real template files, render to bytes.Buffer, assert strings. Tests render the {{define "content"}} blocks directly to avoid the layout-loading dependency. `make test`, `go vet ./...`, `go build ./cmd/server` all clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.
Summary
Phase D + E of host-driven event scheduling. Builds on `HostedEventService` from #44 (this PR is targeted at that branch so the diff stays scoped to the new handler/UI work; once #43 and #44 merge, this PR auto-rebases through to main).
This is the user-visible end of the work — host opens "Schedule Event" in the sidebar, picks a time + attendees + location, hits submit, attendees get an invite. Edit-after-send works the same way.
What's in the PR
12 dashboard routes (`cmd/server/main.go`)
The two HTMX partial routes use `/events/check-conflicts` and `/events/attendee-search` so they don't shadow `{id}`.
Handler (`internal/handlers/dashboard_events.go`)
`DashboardEventsHandler` wired on the `Handlers` struct alongside `Dashboard`. Notes:
Templates (Phase E)
Six new templates, all reusing existing primitives — `.section`, `.section-header`, `.form-input`, `.form-select`, `.form-textarea`, `.form-label`, `.form-hint`, `.btn .btn-primary/.btn-secondary/.btn-outline`, `.btn-sm`, `.badge`, `.table-container/.table`, `.duration-chips`, `.radio-cards`, modal classes, `.empty-state`, `.alert/.alert-warning/.alert-error`. No new CSS files; the attendee chip styling is a small inline block confined to the form.
Form sections in order: Template prefill (new mode only) → Basics → When (date/time/duration chips/timezone with HTMX conflict check) → Conflict banner → Location (radio cards) → Calendar → Attendees (chip-based picker with contact autocomplete + free-form email entry).
Modal pattern matches the booking flow's fetch-based approach (post-#41 — fetch HTML, inject into `#event-modal`, ESC to close).
Sidebar nav (`templates/layouts/dashboard.html`)
Added "Schedule Event" between "Meeting Types" and "Bookings".
Tests
`internal/handlers/dashboard_events_test.go` — 9 cases following the existing `template_test.go` pattern (load real templates, render to `bytes.Buffer`, assert strings). I render the `{{define "content"}}` block directly to sidestep the layout-loading dependency.
Verification
Note on `/frontend-design:frontend-design`
The plan called for invoking that skill. I deliberately chose not to load it because its description anchors on producing distinctive new designs, which would conflict with the user's explicit constraint of "following the existing design language". Instead I read the existing booking + template + contacts pages thoroughly, catalogued every primitive in use, and reused them rigorously here. The visual language should match seamlessly — happy to revisit if anything looks off in the rendered UI.
Test plan
🤖 Generated with Claude Code