Skip to content

feat(check-in): express guest list with one-tap check-in, gated to the event's team - #556

Merged
harshtandiya merged 7 commits into
developfrom
feat/check-in-guest-list
Oct 12, 2026
Merged

harshtandiya merged 7 commits into
developfrom
feat/check-in-guest-list

Conversation

@harshtandiya

@harshtandiya harshtandiya commented Oct 11, 2026 •

Copy link
Copy Markdown
Collaborator

What changed

  • Check-in is now gated to the event team's Owner, Admin, Manager and Frontdesk members, enforced on the server.
    • Before, it needed the site-wide Frontdesk Manager role plus team read access. A Frontdesk member of one team could check in another team's tickets, and Managers couldn't check in at all.
  • /check-in/<event> is rebuilt:
    • The event's name is the heading, in League Gothic (now self-hosted beside Inter), under a small Check-in label, with date and venue below and a large Scan on the right. The page sits in the same column as the navbar.
    • Search, then subtle tabs: All, Checked in, Not checked in. "Checked in" counts today's check-ins.
    • A line of help text under the tabs.
    • Every row has its own Check in button, so most arrivals take one tap.
  • Tapping a guest's name opens their ticket: tier, add-ons and a large Check in button. On desktop it's a dialog; on phones it's a bottom sheet.
  • A checked-in guest's dialog has a quiet Undo check-in. It cancels today's Event Check In rather than deleting it, and the guest can be checked in again.
  • The event's Guests page has a Check in guests tile in Quick actions that opens the check-in page in a new tab.
  • Scan opens the existing QR scanner in a dialog. A valid ticket opens the same guest view, and closing it returns to the camera.
  • The event picker lists only events the user can check in for.
  • New endpoints get_checkin_events, get_checkin_guests and undo_checkin (POST). get_checkin_guests reuses event_guests, which now also returns checked_in_at per guest and a checked_in count. checkin_ticket is POST-only.

Demo

check-in-desktop.png
check-in-dialog-undo.png
check-in-phone-light check-in-phone-dark
guests-quick-action.png

Testing

  • Backend tests in test_checkin.py cover:
    • Frontdesk and Manager can check in.
    • A Viewer is refused.
    • A Frontdesk member of another team is refused.
    • The event list is scoped to the user's teams.
    • The guest list's checked-in filter and count.
  • Updated the check-in Playwright spec and page object for the new flow.
  • Typecheck, oxlint and pre-commit pass locally.

…o the event's team

Check-in is now allowed for the event team's Owner, Admin, Manager and
Frontdesk members only. It used to need the site-wide Frontdesk Manager
role plus read access, which let a Frontdesk member of one team check in
another team's tickets, and kept Managers out.

The check-in page lists the event's guests with search and tabs for
everyone, checked in and not checked in. A guest opens a dialog with
their ticket tier and a check-in button. Scanning still validates the
ticket first, then opens the same dialog and returns to the camera.

The event picker lists only the events the user can check in for.

@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 Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

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

@github-actions

github-actions Bot commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

Backend test coverage: 91%

Commit 5d3801e. Test modules are excluded.

Coverage by file
Name Stmts Miss Cover
buzz/__init__.py 5 5 0%
buzz/api/account/__init__.py 30 2 93%
buzz/api/account/exceptions.py 8 0 100%
buzz/api/account/schemas.py 4 0 100%
buzz/api/account/services.py 15 1 93%
buzz/api/auth/__init__.py 11 0 100%
buzz/api/auth/schemas.py 3 0 100%
buzz/api/booking/__init__.py 30 1 97%
buzz/api/booking/coupons.py 30 3 90%
buzz/api/booking/details.py 93 7 92%
buzz/api/booking/event_data.py 42 4 90%
buzz/api/booking/exceptions.py 21 0 100%
buzz/api/booking/guests.py 71 10 86%
buzz/api/booking/my_bookings.py 9 3 67%
buzz/api/booking/schemas.py 38 0 100%
buzz/api/booking/services.py 212 19 91%
buzz/api/campaigns/__init__.py 34 19 44%
buzz/api/campaigns/exceptions.py 11 0 100%
buzz/api/campaigns/schemas.py 2 0 100%
buzz/api/checkin/__init__.py 21 1 95%
buzz/api/checkin/exceptions.py 17 0 100%
buzz/api/checkin/schemas.py 9 0 100%
buzz/api/checkin/services.py 82 8 90%
buzz/api/communications/__init__.py 16 0 100%
buzz/api/communications/exceptions.py 11 0 100%
buzz/api/communications/schemas.py 6 0 100%
buzz/api/communications/services.py 65 1 98%
buzz/api/communities/__init__.py 49 0 100%
buzz/api/communities/exceptions.py 14 0 100%
buzz/api/communities/notifications.py 34 2 94%
buzz/api/communities/queries.py 36 0 100%
buzz/api/communities/reviews.py 39 1 97%
buzz/api/communities/schemas.py 13 0 100%
buzz/api/communities/submissions.py 54 1 98%
buzz/api/events/__init__.py 49 0 100%
buzz/api/events/exceptions.py 20 0 100%
buzz/api/events/schemas.py 88 0 100%
buzz/api/events/services.py 230 13 94%
buzz/api/events/taxes.py 19 0 100%
buzz/api/events/ticket_types.py 43 0 100%
buzz/api/events/zoom.py 15 1 93%
buzz/api/exceptions.py 23 0 100%
buzz/api/filters/conditions.py 106 2 98%
buzz/api/filters/exceptions.py 5 0 100%
buzz/api/filters/schemas.py 6 0 100%
buzz/api/forms/__init__.py 24 0 100%
buzz/api/forms/answers.py 77 14 82%
buzz/api/forms/exceptions.py 27 0 100%
buzz/api/forms/fields.py 82 3 96%
buzz/api/forms/schemas.py 7 0 100%
buzz/api/forms/services.py 119 1 99%
buzz/api/maps/__init__.py 16 0 100%
buzz/api/maps/exceptions.py 11 0 100%
buzz/api/maps/schemas.py 9 0 100%
buzz/api/maps/services.py 57 0 100%
buzz/api/payments/__init__.py 10 1 90%
buzz/api/payments/currencies.py 7 2 71%
buzz/api/payments/schemas.py 2 0 100%
buzz/api/proposals/__init__.py 18 0 100%
buzz/api/proposals/exceptions.py 5 0 100%
buzz/api/proposals/schemas.py 26 0 100%
buzz/api/proposals/services.py 104 1 99%
buzz/api/schemas.py 13 0 100%
buzz/api/sponsorships/__init__.py 41 0 100%
buzz/api/sponsorships/enquiries.py 74 2 97%
buzz/api/sponsorships/exceptions.py 22 0 100%
buzz/api/sponsorships/forms.py 64 2 97%
buzz/api/sponsorships/manage.py 21 1 95%
buzz/api/sponsorships/schemas.py 20 0 100%
buzz/api/sponsorships/services.py 67 1 99%
buzz/api/sponsorships/sponsors.py 29 0 100%
buzz/api/tags/__init__.py 9 0 100%
buzz/api/tags/desk.py 35 9 74%
buzz/api/tags/exceptions.py 8 0 100%
buzz/api/tags/schemas.py 2 0 100%
buzz/api/tags/services.py 56 0 100%
buzz/api/teams/__init__.py 47 2 96%
buzz/api/teams/calendar.py 28 0 100%
buzz/api/teams/exceptions.py 24 0 100%
buzz/api/teams/invitations.py 34 0 100%
buzz/api/teams/public_page.py 26 15 42%
buzz/api/teams/schemas.py 14 0 100%
buzz/api/teams/services.py 100 1 99%
buzz/api/tickets/__init__.py 15 0 100%
buzz/api/tickets/emails.py 14 9 36%
buzz/api/tickets/exceptions.py 29 0 100%
buzz/api/tickets/schemas.py 4 0 100%
buzz/api/tickets/services.py 85 0 100%
buzz/api/tickets/windows.py 12 0 100%
buzz/buzz/doctype/buzz_custom_field/buzz_custom_field.py 20 4 80%
buzz/buzz/web_form/apply_for_sponsorship/apply_for_sponsorship.py 3 3 0%
buzz/buzz/web_form/propose_a_talk/propose_a_talk.py 3 3 0%
buzz/buzz_marketing/doctype/buzz_campaign/buzz_campaign.py 21 7 67%
buzz/emails.py 29 3 90%
buzz/events/banner_pattern.py 35 0 100%
buzz/events/doctype/additional_event_page/additional_event_page.py 15 1 93%
buzz/events/doctype/buzz_event/buzz_event.py 267 23 91%
buzz/events/doctype/buzz_event_form/buzz_event_form.py 11 4 64%
buzz/events/doctype/buzz_form_field/buzz_form_field.py 3 0 100%
buzz/events/doctype/buzz_price/buzz_price.py 10 0 100%
buzz/events/doctype/buzz_settings/buzz_settings.py 13 2 85%
buzz/events/doctype/buzz_tag/buzz_tag.py 30 2 93%
buzz/events/doctype/buzz_tag_link/buzz_tag_link.py 14 2 86%
buzz/events/doctype/buzz_team/buzz_team.py 75 3 96%
buzz/events/doctype/buzz_team/team_map.py 23 0 100%
buzz/events/doctype/buzz_team/team_page.py 74 2 97%
buzz/events/doctype/buzz_team_membership/buzz_team_membership.py 59 0 100%
buzz/events/doctype/buzz_team_settings/buzz_team_settings.py 44 0 100%
buzz/events/doctype/buzz_theme/buzz_theme.py 42 2 95%
buzz/events/doctype/buzz_theme/tokens.py 29 1 97%
buzz/events/doctype/buzz_theme_token/buzz_theme_token.py 3 0 100%
buzz/events/doctype/community_event_request/community_event_request.py 56 12 79%
buzz/events/doctype/event_category/event_category.py 9 0 100%
buzz/events/doctype/event_check_in/event_check_in.py 10 1 90%
buzz/events/doctype/event_cohost/event_cohost.py 4 0 100%
buzz/events/doctype/event_communication/event_communication.py 16 0 100%
buzz/events/doctype/event_external_link/event_external_link.py 4 0 100%
buzz/events/doctype/event_featured_speaker/event_featured_speaker.py 4 0 100%
buzz/events/doctype/event_feedback/event_feedback.py 4 4 0%
buzz/events/doctype/event_host/event_host.py 4 0 100%
buzz/events/doctype/event_payment_gateway/event_payment_gateway.py 4 0 100%
buzz/events/doctype/event_sponsor/event_sponsor.py 15 2 87%
buzz/events/doctype/event_talk/event_talk.py 8 0 100%
buzz/events/doctype/event_template/event_template.py 38 4 89%
buzz/events/doctype/event_template_add_on/event_template_add_on.py 4 0 100%
buzz/events/doctype/event_template_custom_field/event_template_custom_field.py 4 0 100%
buzz/events/doctype/event_template_ticket_type/event_template_ticket_type.py 4 0 100%
buzz/events/doctype/event_track/event_track.py 4 4 0%
buzz/events/doctype/event_user_preferences/event_user_preferences.py 4 4 0%
buzz/events/doctype/event_venue/event_venue.py 53 1 98%
buzz/events/doctype/event_venue/geocode.py 69 9 87%
buzz/events/doctype/event_venue/map_link.py 106 5 95%
buzz/events/doctype/offline_payment_method/offline_payment_method.py 11 0 100%
buzz/events/doctype/schedule_item/schedule_item.py 4 0 100%
buzz/events/doctype/social_media_link/social_media_link.py 3 0 100%
buzz/events/doctype/speaker_profile/speaker_profile.py 8 1 88%
buzz/events/doctype/sponsor_enquiry_form/sponsor_enquiry_form.py 54 5 91%
buzz/events/doctype/sponsorship_tier/sponsorship_tier.py 27 1 96%
buzz/events/doctype/talk_speaker/talk_speaker.py 3 0 100%
buzz/events/doctype/utm_parameter/utm_parameter.py 4 0 100%
buzz/events/og_image.py 141 7 95%
buzz/events/og_text.py 66 1 98%
buzz/events/online_meeting.py 42 1 98%
buzz/events/report/event_attendance_summary/event_attendance_summary.py 61 61 0%
buzz/events/report/event_overview/event_overview.py 25 25 0%
buzz/hooks.py 32 0 100%
buzz/install.py 60 28 53%
buzz/integrations/zoom.py 3 3 0%
buzz/islands.py 10 5 50%
buzz/payments.py 96 33 66%
buzz/permissions.py 140 12 91%
buzz/proposals/doctype/event_proposal/event_proposal.py 71 5 93%
buzz/proposals/doctype/proposal_speaker/proposal_speaker.py 4 0 100%
buzz/proposals/doctype/sponsorship_deck_item/sponsorship_deck_item.py 4 0 100%
buzz/proposals/doctype/sponsorship_enquiry/sponsorship_enquiry.py 81 5 94%
buzz/proposals/doctype/talk_proposal/talk_proposal.py 92 6 93%
buzz/proposals/doctype/talk_proposal_status/talk_proposal_status.py 4 0 100%
buzz/tasks.py 4 4 0%
buzz/telemetry.py 46 0 100%
buzz/telemetry_scan.py 55 8 85%
buzz/ticketing/doctype/additional_field/additional_field.py 4 0 100%
buzz/ticketing/doctype/attendee_ticket_add_on/attendee_ticket_add_on.py 3 0 100%
buzz/ticketing/doctype/buzz_coupon_code/buzz_coupon_code.py 99 4 96%
buzz/ticketing/doctype/coupon_free_add_on/coupon_free_add_on.py 3 0 100%
buzz/ticketing/doctype/event_booking/event_booking.py 355 28 92%
buzz/ticketing/doctype/event_booking_attendee/event_booking_attendee.py 20 7 65%
buzz/ticketing/doctype/event_booking_refund/event_booking_refund.py 56 0 100%
buzz/ticketing/doctype/event_booking_refund_ticket/event_booking_refund_ticket.py 4 0 100%
buzz/ticketing/doctype/event_payment/event_payment.py 4 0 100%
buzz/ticketing/doctype/event_ticket/event_ticket.py 80 10 88%
buzz/ticketing/doctype/event_ticket_type/event_ticket_type.py 69 0 100%
buzz/ticketing/doctype/ticket_add_on/ticket_add_on.py 9 1 89%
buzz/ticketing/doctype/ticket_add_on_value/ticket_add_on_value.py 3 0 100%
buzz/ticketing/doctype/ticket_cancellation_item/ticket_cancellation_item.py 4 0 100%
buzz/ticketing/doctype/ticket_cancellation_request/ticket_cancellation_request.py 19 1 95%
buzz/ticketing/report/detailed_event_registrations/detailed_event_registrations.py 111 5 95%
buzz/ticketing/report/event_add_ons_overview/event_add_ons_overview.py 20 20 0%
buzz/uninstall.py 10 2 80%
buzz/utils.py 138 22 84%
buzz/www/dashboard.py 24 3 88%
buzz/www/event/date_range.py 42 5 88%
buzz/www/event/index.py 151 6 96%
buzz/www/event/link_icons.py 1 0 100%
buzz/www/event/meta.py 65 4 94%
buzz/www/event/venue_map.py 35 1 97%
buzz/www/events.py 69 6 91%
buzz/www/site_header.py 25 0 100%
TOTAL 7054 612 91%

91 empty files skipped.

…hic heading

Each guest row carries its own Check in button, so most arrivals take a
single tap. Tapping a name opens the guest's ticket: a dialog on desktop,
a bottom sheet on phones. A line under the tabs says what each control
does.

The page opens on a "Check-in" display heading set in League Gothic, now
self-hosted beside Inter, with the event's name, date and venue under it.
The underline tabs span the full column, and the "All events" link is gone.

@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 changed the title feat(check-in): guest list with search and a check-in dialog, gated to the event's team feat(check-in): express guest list with one-tap check-in, gated to the event's team Oct 11, 2026
@harshtandiya
harshtandiya marked this pull request as draft October 12, 2026 03:48
@harshtandiya harshtandiya mentioned this pull request Oct 12, 2026
4 of 8 tasks
A "Check in guests" tile sits above Download CSV and opens the event's
check-in page in a new tab, so the desk can run check-in beside the
dashboard.
The page and the event picker set their own narrower width and padding
inside the layout's, so content sat indented from the navbar. Both now
use the layout's column.

The event name takes the display type, with "Check-in" as a small label
above it. Scan is a size up, the tabs use the subtle variant, and a
checked-in row uses a stronger green with a solid check mark that fades
in when a guest is checked in.
A mis-tapped check-in can now be reversed. The guest dialog shows a
checked-in guest as a green status block with a quiet "Undo check-in"
button under it, replacing the disabled button that read too faint.

The new undo_checkin endpoint cancels today's Event Check In rather
than deleting it, so the record stays. The already-checked-in guard now
looks only at submitted check-ins, so a guest whose check-in was undone
can be checked in again.
@harshtandiya
harshtandiya marked this pull request as ready for review October 12, 2026 04:29

@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.

…arch

- Scan opens the camera inline with a shutter reveal; a grey cover explains
  permission prompts, camera start-up and blocked cameras. The scanner pauses
  while a scanned guest is open instead of closing and reopening.
- Event header, scanner and search stay pinned on a glass panel while the
  list scrolls.
- Search shows a skeleton on first load, "Searching" from the first
  keystroke, and keeps the current rows dimmed until results replace them.
  No-results state offers Clear search.
- Guests can be found by ticket ID; the guest list payload carries the
  event's public route.
- Slim site header on the check-in page (logo, theme, account), with links
  back to the event dashboard and out to the event page.
- Guest dialog: ticket type stacks above payment, add-ons get a label and
  wrap in full, long names and emails wrap.
- Rows, header and tabs wrap or scroll at narrow widths and large text;
  white text on green buttons in dark mode; formatted tab counts.
- Button check-ins play the scanner's success beep.

@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.

…icon

The guest page's check-in tile becomes a ghost button beside Download CSV, and both it and the check-in page's Scan button use the scan-barcode icon.

@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 e4cc529 into develop Oct 12, 2026
8 checks passed
@harshtandiya
harshtandiya deleted the feat/check-in-guest-list branch October 12, 2026 11:59
@NagariaHussain

Copy link
Copy Markdown
Contributor

Did we also refactor the scanner itself?

👀

@harshtandiya

Copy link
Copy Markdown
Collaborator Author

@NagariaHussain not the mechanism, just UI

image

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.

2 participants