Skip to content

feat: Google Maps and map links for event venues - #509

Merged
harshtandiya merged 20 commits into
developfrom
feat/google-maps-venue
Sep 30, 2026
Merged

harshtandiya merged 20 commits into
developfrom
feat/google-maps-venue

Conversation

@harshtandiya

@harshtandiya harshtandiya commented Sep 30, 2026 •

Copy link
Copy Markdown
Collaborator

What changed

Venues and Google Maps

  • Event Venue is named by hash; venue_name carries the label. A patch backfills venues and events.
  • Optional Google Maps (Buzz Settings → Map Integration): place search when adding a venue, Google map on the event page. Off until keys are set.
  • Manual venues take a map link; coordinates are read from Google Maps and OpenStreetMap links, no API key.
  • Event page: venue name links to its map; map links in the embed open outside the iframe sandbox.

Location on create and manage pages

  • Both pages pick the location in a dialog with a preview. The old combobox and AddVenueDialog are gone.

Converting an event between in person and virtual (manage page)

  • The In person / Virtual tabs are replaced by a line under the location: "Convert to a virtual event" / "Convert to an in-person event".
  • To virtual: dialog with a meeting link, or a Zoom meeting when zoom_integration is installed. To in person: the location dialog; converts only once a venue is confirmed.
  • Each conversion saves on its own with a loading toast; unsaved edits elsewhere on the page are kept.
  • convert_to_zoom_meeting: books before switching the medium (a Zoom failure changes nothing), reuses an already linked meeting, refuses events that have ended.

Fixes

  • Place search token no longer uses crypto.randomUUID (missing on plain HTTP, which broke E2E).
  • has_ended treats a blank end_date as the start date instead of today.

Not in this PR

Demo

https://cap.so/s/rbjn7y6m5wwp9mm

Testing

  • Python: test_zoom, test_event_venue, test_maps, test_event, www.test_events pass locally. Google, OpenStreetMap and Zoom calls are stubbed.
  • E2E: manage-event.spec.ts covers converting with a link and back through the location dialog. CI green.
  • Checked in the browser: both conversion dialogs, Zoom option, error toast on a failed conversion.

Event Venue was autonamed by prompt, so its docname doubled as its label and two
venues could not share a name. It is now autonamed by hash, with venue_name as
the title field. Existing venues keep their docname; a patch copies it into
venue_name, so no link is rewritten.

Buzz Event gains a hidden venue_name fetched from the venue. Most readers print
the venue straight off the event row or over a link hop in a list query, so the
label has to live on the event. Relabelling a venue pushes the new label to its
events without moving their modified timestamp, which the manage page uses as
its conflict check.

Payload changes: ConfirmationVenue.name is now venue_name, and the guest event
payload gains a venue_name key. Every other key keeps its name and now carries
the label.
Buzz Settings gains a Map Integration tab with a Google Maps switch and two
keys: a secret Places key used from the server, and a public embed key for the
event page map, which nothing reads yet. The tab sits before Communications so
custom fields anchored to the end of the form stay where they were.

buzz.api.maps.search_places proxies Places Autocomplete for anyone who can add
venues, so the Places key never reaches the browser. The venue picker shows
the results in a Google Maps group under the saved venues once three
characters are typed. Picking a result does nothing yet.

The switch reaches the dashboard as google_place_search_enabled in the boot
data. The Vite dev server renders no boot data, so in development the
dashboard now fetches it from get_context_for_dev.

With the switch off or no Places key, the picker sends no request and looks
as it did.
Links inside the embedded map open a new tab. With allow-popups alone that tab
inherited the sandbox, and Google Maps refuses to load in a sandboxed tab
(ERR_BLOCKED_BY_RESPONSE). allow-popups-to-escape-sandbox opens it as a normal
page; the map itself stays sandboxed.
Picking a place in the venue picker calls buzz.api.maps.add_place_as_venue,
which asks Place Details for the address and inserts an Event Venue carrying
the new google_place_id. The name comes from the prediction, and no
coordinates are stored: Google's terms let a site keep the place id for good
but cap cached coordinates at 30 days. A place the team already saved is
reused without a Google call. The insert runs with the caller's permissions.

The event page embeds the place through the Maps Embed API when Google Maps is
enabled and the embed key is set. Without the key such a venue shows no map,
since it has no coordinates to draw an OpenStreetMap from. Venues added by
hand keep their pasted embed or OpenStreetMap.

The map link in emails points at the exact place for these venues.
The create event page replaces the venue combobox with an Add Location button
that opens a dialog. Saved venues and Google Maps results list on the left; the
highlighted one shows its map and address on the right, and only Use this
location picks it. A Google place is therefore saved as a venue, and Place
Details is called, once on confirm rather than on every click.

Adding a venue by hand and creating a Zoom meeting live in the same dialog. A
chosen location shows as a card with change and remove actions.

The public embed key joins the dashboard boot data for the preview map, and
the venue list now carries google_place_id. The manage event page keeps the
combobox picker for now.
A chosen location on the create event page now shows its name and address,
the Google map for venues saved from Google Maps, and Change and Remove
buttons underneath. Venues added by hand and Zoom meetings show the text
only.

The map iframe moves into LocationMap, shared with the dialog preview.
Adding a venue by hand now starts with a Map link field. Event Venue gains
map_link, and on save the link is read without any Google API:

- Google Maps links give coordinates, and the place name from the path.
- Google short links (maps.app.goo.gl, goo.gl) are resolved with one request
  to that host; only the redirect target is read, never followed.
- OpenStreetMap links give coordinates from mlat/mlon, #map=, or a decoded
  /go/ short code. Node, way and relation links are looked up on Nominatim,
  which also gives a name and an address.
- Pasted Google embed code becomes the embed, as before.

Coordinates draw the existing OpenStreetMap embed. An address is now required
only when the venue has no location.

buzz.api.maps.locate_map_link reads a link the same way without saving. The
form calls it when the link field loses focus, fills an empty name or address
from the answer, and shows the map in the dialog. The name field is disabled
while that runs.

The dashboard draws OpenStreetMap maps for venues with coordinates, and shows
skeletons while a map loads, a link is read, venues load or a place search
runs.
The venue name under Where is now a link with an arrow icon. It opens the
organiser's own map link when the venue has one, else the Google place, the
coordinates, or the address as a Google Maps search. Pasted embed code is
never used as a link target, and only http and https links are accepted.

The map link in emails follows the same order.
When the text typed in the location dialog matches no saved venue and no
Google Maps result, the list offers a single highlighted row to add it
manually. Enter in the search box, or a click on the row, opens the manual
form with the name filled in and focus on the map link.

The row waits for the venue list and any place search to finish, so Enter
never acts on a list that is still loading.
Browsers only expose crypto.randomUUID in secure contexts. E2E runs on
plain HTTP, where the call threw during setup and the venue picker on the
manage event page rendered nothing. crypto.getRandomValues works on any
page.
…nt page

The manage event page now uses the same location field as the create
page: Add Location opens the dialog, and the chosen venue shows with its
map. The dialog leaves out Zoom there, since a meeting is only booked
when the event is created. The old combobox picker and its add venue
dialog are removed.
@harshtandiya
harshtandiya marked this pull request as ready for review September 30, 2026 18:31

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown
Contributor

✅ UI Demo Check — a screenshot or demo is attached.

…f an event has ended

A one-day event leaves end_date blank, and getdate(None) returns today, so
has_ended compared the end time against today instead of the event's own
day.
… manage page

The In person / Virtual tabs are gone. Under the location, a line offers
to convert the event. Converting to virtual opens a dialog that takes a
meeting link, or, when zoom_integration is installed, offers a radio
choice between a link and a Zoom meeting. Converting back to in person
happens straight away. Each conversion saves on its own, apart from the
page's Save, and shows a loading toast until the server answers.

convert_to_zoom_meeting books the meeting before switching the medium, so
a Zoom failure leaves the event as it was, and reuses a meeting still
linked from an earlier conversion. It refuses events that have ended.

medium leaves the page's form, and after a conversion the page takes only
venue and meeting_link from the server, so unsaved edits elsewhere survive
and Save does not send the old location back. useCall settles on failure
too, so the toast reads the call's error rather than trusting the promise.
…rson

Convert to an in-person event now opens the location dialog, titled for
the conversion and without Zoom. The event converts only when a venue is
confirmed with Use this location.

The dialog loads the team's venues each time it opens. It used to rely on
the location field loading them, and that field is not on the page while
the event is virtual, so the list came up empty.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

…opy on the event

Buzz Event no longer keeps a fetched venue_name. The copy needed a sync
hook on every relabel, and it kept the old name when the venue was
cleared, so a virtual event still showed its last venue in lists and
emails.

Single-event readers call BuzzEvent.get_venue_name(). Event lists join
Event Venue. Lists two links away (proposals, bookings) resolve names
with set_venue_names(), because Frappe's dotted fields reach one link
only. Attendees cannot read Event Venue, so the bookings page moves from
useList to a new get_my_bookings endpoint, keeping its 20-row page.
Printed tickets take the venue from the event feed row, and ticket
details returns it as venue.
convert_to_zoom_meeting booked the meeting first, so a save that failed
validation rolled the event back but left the meeting on Zoom, and each
retry booked another. The event is now saved first. A Zoom failure after
that rolls the save back with the request, so the event is still left
as it was.

zoom.py imported buzz.www.events at module level, which imports the
event page, which imports buzz.api.events again. Loading the share-image
module first raised an ImportError, so the import moves into the
function.
… saves

The venue's map link went into href unescaped in emails and on the
public event page. Frappe's Jinja environment does not autoescape, so a
link with a quote could add attributes or markup. Both are escaped now.

A venue added from a map link may have no address, so the calendar
invite and the event's structured data fall back to the venue name.

read_map_link keeps a place it found in Redis for 10 minutes, so the
picker's preview and the save that follows make one lookup, not two.
A lookup that found nothing is not kept.

add_place_as_venue is rate limited and checks that the user can add a
venue to the team before the billed Place Details call.

The location dialog offers Zoom only when zoom_integration is installed.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

An online event had nothing in the sidebar where an in-person event
shows its venue, and the mobile summary marked it with a map pin. The
sidebar now has a Where block with a video icon, and the mobile row uses
the same label and icon.

The label names the service when it can be told, from the event's Zoom
meeting or webinar or the meeting link's host (Zoom, Google Meet,
Microsoft Teams), and falls back to Online. The link itself never
reaches the page. While registration is open a note says registered
guests get the link to join.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@greptile-apps greptile-apps Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Your trial has ended. Reactivate Greptile to resume code reviews.

@harshtandiya
harshtandiya merged commit 6296dfa into develop Sep 30, 2026
8 checks passed
@harshtandiya
harshtandiya deleted the feat/google-maps-venue branch September 30, 2026 21:19
@harshtandiya harshtandiya mentioned this pull request Oct 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant