feat(dashboard): a workspace for managing an event - #355
Conversation
Greptile SummaryThe PR adds an event-management workspace with editable event details, event-specific navigation, route availability checks, and support for manual online meeting links.
Confidence Score: 4/5The PR should not merge until switching a Zoom-backed event to in-person also removes the obsolete Zoom session references. The prior-thread reply marked stale-location cleanup fixed, but the current cleanup only removes meeting_link; retained zoom_meeting and zoom_webinar references can still drive attendee Zoom registration and can repopulate the old meeting URL. Files Needing Attention: buzz/events/doctype/buzz_event/buzz_event.py, buzz/api/events/services.py, buzz/ticketing/doctype/event_ticket/event_ticket.py
|
| Filename | Overview |
|---|---|
| buzz/events/doctype/buzz_event/buzz_event.py | Adds medium-specific location cleanup, but leaves Zoom integration references active when an event becomes in-person. |
| buzz/api/events/services.py | Adds guarded event-detail and route-availability services; the meeting-link fallback can revive a retained Zoom reference. |
| dashboard/src/pages/manage/events/EventDetails.vue | Implements the event details form, dirty-state tracking, aggregate persistence, and actionable save errors. |
| dashboard/src/components/dashboard/events/EventMedium.vue | Implements medium switching and clears visible opposite-medium fields, but relies on incomplete server cleanup for persisted Zoom references. |
| dashboard/src/components/dashboard/events/EventRoute.vue | Adds saved-route controls and debounced availability feedback with stale-response protection. |
| dashboard/src/layouts/ManagerLayout.vue | Switches the manager sidebar to event-specific destinations when an event route is active. |
| dashboard/src/router.ts | Adds event details and placeholder manager routes under the existing manager shell. |
Prompt To Fix All With AI
### Issue 1
buzz/events/doctype/buzz_event/buzz_event.py:112-116
**Zoom references survive medium changes**
When a Zoom-backed event is changed from Online to In Person, `clear_unused_location` clears only `meeting_link` and retains `zoom_meeting` or `zoom_webinar`, causing ticket submission to keep registering attendees with the obsolete Zoom session and allowing the old URL to reappear if the event is switched online again.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Reviews (8): Last reviewed commit: "fix(dashboard): show the server's own me..." | Re-trigger Greptile
b158200 to
e28b2a2
Compare
f07ae4b to
3ed8280
Compare
e28b2a2 to
76c03ac
Compare
3ed8280 to
a03d5d1
Compare
76c03ac to
45a5841
Compare
a03d5d1 to
42d87f1
Compare
|
✅ UI Demo Check — a screenshot or demo is attached. |
42d87f1 to
ef955eb
Compare
45a5841 to
a42088d
Compare
a42088d to
f8b03af
Compare
An event card's Manage button had nowhere to go. It now opens the event into the manager shell at /manage/events/<id>, where the sidebar carries the event's own destinations — back out, details, attendees, talks — in place of the personal and team ones. The sections have no pages yet, so they land on the work-in-progress placeholder the other unbuilt destinations already use. Reusing the layout rather than adding a second one keeps the shell, the team switcher and the user menu identical on both sides of the switch. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A team's events page lists nothing but events the viewer can manage, so a Manage button on every card is noise. The card takes a routeToManage prop instead: when it is set, the card itself becomes the link into the event workspace and the button steps aside, since one link inside another is neither valid nor clickable. The prop only takes effect for a host, so a card the viewer merely holds a ticket to stays inert either way. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The first real section of the event workspace. It carries the same page header the team pages use, with a trail of the event and the section being looked at, and nothing else yet — the content follows. The event is named from the feed the dashboard already loads rather than a second request, so the page reads the title straight out of the events list it was opened from. Details also takes the receipt-text icon in the sidebar. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The details page now carries the event itself: the banner, the name, a short description under it, the long about, when it runs, and where. Edits are held locally and written in one go, so Save only appears once something has actually changed. Where is now asked in two steps rather than one. A pair of toggles picks the medium, and only then does the page ask for the answer that medium needs — a venue and its address for an event in a room, a meeting link with a copy button for one online. The old combobox folded both into a single list, which meant picking Zoom to say the event was virtual. Buzz Event gains meeting_link for that second case: the Zoom field belongs to an app the site may not have, and an organiser with a link of their own had nowhere to put it. get_event serves the page, resolving the venue's address and falling back to the booked Zoom meeting's link. Writes go through frappe.client.set_value, since the team permission hooks already guard the doctype. The banner moves into EventBanner, shared with the create page it came from. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The details page shows the address the event answers to, beside its name: the host as fixed text, the route after the slash as the only editable part, then a way to open the page and a way to copy the link. Routes are the public URL namespace and so are unique. check_event_route answers whether one is free while it is being typed, counting unpublished events too — an event holds its route whether or not anyone can see it yet — and refusing the segments reserved so an event cannot shadow /b/account. The verdict appears under the field rather than waiting for a failed save. The chip reads host/route the way the address is spoken, while the link and the copied text carry the /b/register prefix the page is actually served under. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Green read as a status the chip does not have. The swap alone says the link was copied. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
ef955eb to
ac11aeb
Compare
Switching an event between in person and online left the other side's value on the record. `generate_ics_file` and the booking page both read `venue` without consulting `medium`, so a leftover venue put a physical address on an online event's calendar invite and registration page. Cleared on the doctype so the Desk form and the dashboard are both covered, and in the medium toggle as well so the form shows what was saved. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Each availability response replaced the field's state unconditionally, so a slow answer for an earlier route could land last and describe a route the organiser had already moved on from. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`FrappeError.message` is the URL and exception type joined; the text a user can act on is in `messages`, which is where `_server_messages` lands. A refused save read "frappe.client.set_value ValidationError" instead of naming the reason. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
fbb2b22 to
870fe09
Compare
| """ | ||
| if self.medium == "Online": | ||
| self.venue = None | ||
| else: | ||
| self.meeting_link = None |
There was a problem hiding this comment.
Zoom references survive medium changes
When a Zoom-backed event is changed from Online to In Person, clear_unused_location clears only meeting_link and retains zoom_meeting or zoom_webinar, causing ticket submission to keep registering attendees with the obsolete Zoom session and allowing the old URL to reappear if the event is switched online again.
Knowledge Base Used:
Prompt To Fix With AI
This is a comment left during a code review.
Path: buzz/events/doctype/buzz_event/buzz_event.py
Line: 112-116
Comment:
**Zoom references survive medium changes**
When a Zoom-backed event is changed from Online to In Person, `clear_unused_location` clears only `meeting_link` and retains `zoom_meeting` or `zoom_webinar`, causing ticket submission to keep registering attendees with the obsolete Zoom session and allowing the old URL to reappear if the event is switched online again.
**Knowledge Base Used:**
- [Event management domain](https://app.greptile.com/bwh-tech/-/custom-context/knowledge-base/bwhtech/buzz/-/docs/event-management.md)
- [Frappe application integration](https://app.greptile.com/bwh-tech/-/custom-context/knowledge-base/bwhtech/buzz/-/docs/frappe-application-integration.md)
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.There was a problem hiding this comment.
The mechanism is real — create_zoom_registration_if_applicable (event_ticket.py:72-79) reads the link fields and consults neither medium nor category. But clearing them here would be the wrong fix.
medium is not what governs Zoom anywhere in this app. ZOOM_BACKED_CATEGORIES (utils.py:13) keys on category, and so do validate_zoom_last_names (booking/services.py:185) and dashboard/src/utils/zoomCategory.ts. Clearing zoom_meeting on a medium change would leave an event categorised Zoom Meeting with no meeting, which those two would then act on wrongly, and would orphan a live Zoom session that still holds registrants. Throwing instead would make already-mismatched rows unsaveable — the objection that deferred #375.
And the mismatch predates this branch. On a dev site: event 857, medium = In Person, category = Webinars, zoom_meeting set; In Person / Webinars and Online / Meetups rows both exist in aggregate. The two fields have been drifting since long before the medium was editable.
The real defect is that two independent fields both claim to say whether an event is a Zoom event. Filed as #376 — one has to be derived from the other, and the ticket-time registration should consult whichever survives. Out of scope here.
There was a problem hiding this comment.
You're right — this is a category-vs-medium mismatch, not something clear_unused_location can safely resolve. zoom_meeting and zoom_webinar are governed by category, so clearing them on a medium change could orphan an active Zoom session while leaving Zoom-specific validation and registration paths enabled. Given that mismatched legacy rows already exist, rejecting the save here would also be an inappropriate migration-by-validation fix. I'll withdraw this finding from the PR; #376 is the appropriate place to establish a single source of truth and update ticket-time registration.
Rebased onto
developnow that #354 has merged.What changed
An event card's Manage button had nowhere to go. It now opens the event inside the
manager shell at
/manage/events/<id>, where the sidebar carries the event's owndestinations — back out, Details, Attendees, Talks — in place of the personal and team
ones. No second layout:
ManagerLayoutswaps its item list when the route names anevent. Attendees and Talks land on the existing work-in-progress placeholder. On a
team's own events page every event is manageable, so those cards drop the per-card
button and become the link themselves.
Details holds the banner, name, short description, about, when it runs and where. Edits
are kept locally and written in one go, so Save only appears once something changed.
Where is asked in two steps: toggles pick the medium, then the page asks for what that
medium needs — a venue and its address, or a meeting link with a copy button. The old
combobox folded both into one list, which meant picking Zoom to say an event was virtual.
Worth a look during review:
check_event_routecounts unpublished events when deciding whether a route is free —an event holds its route whether or not anyone can see it yet — and refuses the
reserved segments so an event cannot shadow
/b/account.Buzz Eventgainsmeeting_link(Data/URL, shown when medium is Online).zoom_meetingbelongs to an app the site may not have installed, and an organiserwith a link of their own had nowhere to put it.
get_eventfalls back to the bookedZoom meeting's link when none was typed.
get_eventis guarded onwrite, so a Viewer gets a 403. Saves go throughfrappe.client.set_value— the team permission hooks already guardBuzz Event, sothere is no bespoke write endpoint.
EventBanner.vueis the create page's banner block, extracted and now shared withDetails. It takes the
disabledprop the create page's viewer state needs.Not done: Details has no viewer state of its own —
get_eventrefuses a Viewer with a403 rather than showing a read-only form, unlike the create page.
BookingEventInfo.vue:8still links to
/events/<route>, which 404s on this site; the page that serves an eventis
/b/register/<route>. Both pre-existing and outside this branch.Demo
Testing
buzz.api.events.test_events— 29 pass, 1 skipped (Zoom), 11 of them new acrossget_eventandcheck_event_route: payload shape, venue resolution, viewer /non-member / unknown-event refusals, taken and reserved routes, and an event not
blocking its own.
buzz.events.doctype.buzz_event.test_buzz_event— 53 pass.e2e/tests/manage-event.spec.ts— 9 new specs: opening on Details, a real saveround-trip, the public-address chip, the virtual/in-person swap, sidebar contents,
moving between sections, backing out, route availability, and the card entry point.
e2e/tests/create-event.spec.ts— new, guards the create page after the extraction.yarn typecheck,yarn test:unit(103 pass) andyarn buildall clean.