From 13496fbcdafd77b30c7433d9c7f765d812aaad6e Mon Sep 17 00:00:00 2001 From: AZ0228 <53315675+AZ0228@users.noreply.github.com> Date: Tue, 24 Mar 2026 16:57:40 -0400 Subject: [PATCH 1/2] chore(MER-999-Patch): sync events @ d71ebc6 --- private-deps.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/private-deps.lock b/private-deps.lock index d6632f66..89bf8a30 100644 --- a/private-deps.lock +++ b/private-deps.lock @@ -1,7 +1,7 @@ { "events": { "repo": "git@github.com:Study-Compass/Events-Backend.git", - "ref": "0cb405c8e30676a6a1e1f6dbc0ecc4893aa3edec", + "ref": "d71ebc6b94ba1535a11eef311a0aea7712259c4f", "dest": "backend/events" } } From 683aeec0d31756943b5b3425fd3afa1bebebbbc9 Mon Sep 17 00:00:00 2001 From: AZ0228 <53315675+AZ0228@users.noreply.github.com> Date: Tue, 24 Mar 2026 17:26:20 -0400 Subject: [PATCH 2/2] MER-999 adding fully anonymous check-in --- .../components/EventCheckIn/CheckInList.jsx | 14 ++++-- .../EventCheckInButton/EventCheckInButton.jsx | 49 +++++++++++++------ .../src/pages/CheckIn/CheckInConfirmation.jsx | 36 +++++++++++++- .../pages/CheckIn/CheckInConfirmation.scss | 12 +++++ .../EventCheckInTab/CheckInSettingsModal.jsx | 18 ++++++- .../EventCheckInTab/EventCheckInTab.jsx | 5 ++ private-deps.lock | 2 +- 7 files changed, 113 insertions(+), 23 deletions(-) diff --git a/frontend/src/components/EventCheckIn/CheckInList.jsx b/frontend/src/components/EventCheckIn/CheckInList.jsx index 67e23749..73074f63 100644 --- a/frontend/src/components/EventCheckIn/CheckInList.jsx +++ b/frontend/src/components/EventCheckIn/CheckInList.jsx @@ -4,6 +4,9 @@ import { formatDistanceToNow } from 'date-fns'; import './EventCheckIn.scss'; function getAttendeeDisplayName(attendee) { + if (attendee.anonymousBrowserCheckIn) { + return attendee.guestName || 'Anonymous user'; + } if (attendee.formResponseId && (attendee.guestName || attendee.guestEmail)) { return attendee.guestName && String(attendee.guestName).trim() ? String(attendee.guestName).trim() @@ -49,12 +52,14 @@ function CheckInList({ attendees, onManualCheckIn, onRemoveCheckIn, onOpenManual }; const getUserPicture = (attendee) => { + if (attendee.anonymousBrowserCheckIn) return null; if (attendee.formResponseId) return null; const user = attendee.userId; return user?.picture || null; }; const getAttendeeRemoveId = (attendee) => { + if (attendee.anonymousBrowserCheckIn) return null; if (attendee.formResponseId) return { formResponseId: attendee.formResponseId }; const uid = attendee.userId && (attendee.userId._id || attendee.userId.id || attendee.userId); return uid ? { userId: uid } : null; @@ -117,9 +122,10 @@ function CheckInList({ attendees, onManualCheckIn, onRemoveCheckIn, onOpenManual const user = attendee.userId; const checkedInBy = attendee.checkedInBy; const isManual = !!checkedInBy; + const isAnonymousBrowser = !!attendee.anonymousBrowserCheckIn; const removeId = getAttendeeRemoveId(attendee); return ( -
+
{getUserPicture(attendee) ? ( @@ -140,9 +146,9 @@ function CheckInList({ attendees, onManualCheckIn, onRemoveCheckIn, onOpenManual {formatCheckInTime(attendee.checkedInAt)} - - - {isManual ? 'Manual' : 'Self Check-In'} + + + {isAnonymousBrowser ? 'Anonymous User' : (isManual ? 'Manual' : 'Self Check-In')}
diff --git a/frontend/src/components/EventCheckInButton/EventCheckInButton.jsx b/frontend/src/components/EventCheckInButton/EventCheckInButton.jsx index 51946d50..6f9561f1 100644 --- a/frontend/src/components/EventCheckInButton/EventCheckInButton.jsx +++ b/frontend/src/components/EventCheckInButton/EventCheckInButton.jsx @@ -7,6 +7,12 @@ import apiRequest from '../../utils/postRequest'; import { analytics } from '../../services/analytics/analytics'; import './EventCheckInButton.scss'; +const ANON_EVENT_CHECKIN_KEY_PREFIX = 'meridian_anon_event_checked_in_'; + +function getAnonEventCheckInKey(eventId) { + return `${ANON_EVENT_CHECKIN_KEY_PREFIX}${eventId}`; +} + /** * Check-in button for the event page. Shown when: * - Event has check-in enabled @@ -20,11 +26,21 @@ function EventCheckInButton({ event, onCheckedIn }) { const { user } = useAuth(); const { addNotification } = useNotification(); const [loading, setLoading] = useState(false); - const [checkedIn, setCheckedIn] = useState(!!event?.currentUserCheckedIn); + const [checkedIn, setCheckedIn] = useState(false); useEffect(() => { - setCheckedIn(!!event?.currentUserCheckedIn); - }, [event?.currentUserCheckedIn, event?._id]); + const loggedInCheckedIn = !!event?.currentUserCheckedIn; + if (loggedInCheckedIn) { + setCheckedIn(true); + return; + } + if (!user && event?._id && event?.checkInSettings?.fullyAnonymousCheckIn) { + const anonCheckedIn = localStorage.getItem(getAnonEventCheckInKey(event._id)) === '1'; + setCheckedIn(anonCheckedIn); + return; + } + setCheckedIn(false); + }, [event?.currentUserCheckedIn, event?._id, event?.checkInSettings?.fullyAnonymousCheckIn, user]); if (!event) return null; if (!event.checkInEnabled) return null; @@ -71,22 +87,25 @@ function EventCheckInButton({ event, onCheckedIn }) { }; if (checkedIn) { + const showCheckOut = !!user; return (
You're checked in - + {showCheckOut && ( + + )}
); } diff --git a/frontend/src/pages/CheckIn/CheckInConfirmation.jsx b/frontend/src/pages/CheckIn/CheckInConfirmation.jsx index e364ef4b..d5cb4e09 100644 --- a/frontend/src/pages/CheckIn/CheckInConfirmation.jsx +++ b/frontend/src/pages/CheckIn/CheckInConfirmation.jsx @@ -34,6 +34,22 @@ function HighlightMatch({ text, query }) { const APP_STORE_URL = 'https://apps.apple.com/us/app/meridian-go/id6755217537'; const PLAY_STORE_URL = 'https://play.google.com/store/apps/details?id=com.meridian.mobile'; +const ANON_CHECKIN_BROWSER_KEY = 'meridian_anon_checkin_browser_id'; +const ANON_EVENT_CHECKIN_KEY_PREFIX = 'meridian_anon_event_checked_in_'; + +function getAnonymousBrowserId() { + if (typeof window === 'undefined') return null; + const existing = localStorage.getItem(ANON_CHECKIN_BROWSER_KEY); + if (existing) return existing; + const generated = `anon-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`; + localStorage.setItem(ANON_CHECKIN_BROWSER_KEY, generated); + return generated; +} + +function markAnonymousEventCheckedIn(eventId) { + if (!eventId || typeof window === 'undefined') return; + localStorage.setItem(`${ANON_EVENT_CHECKIN_KEY_PREFIX}${eventId}`, '1'); +} function useDeviceDetection() { return useMemo(() => { @@ -64,8 +80,9 @@ function CheckInConfirmation() { const event = eventResponse?.event; const useSelfCheckIn = !token && user; + const useFullyAnonymousCheckIn = Boolean(token && !user && event?.checkInSettings?.fullyAnonymousCheckIn); // When token is present, use pick flow for everyone (avoids "must register" for anonymous registrants who later logged in) - const useAnonymousPick = !!token; + const useAnonymousPick = !!token && !useFullyAnonymousCheckIn; // Registrations for anonymous pick (token flow, no login) const { data: registrationsResponse, loading: loadingRegistrations, refetch: refetchRegistrations } = useFetch( @@ -183,10 +200,19 @@ function CheckInConfirmation() { try { const response = useSelfCheckIn ? await apiRequest(`/events/${eventId}/check-in/self`, {}, { method: 'POST' }) + : useFullyAnonymousCheckIn + ? await apiRequest( + `/events/${eventId}/check-in/anonymous`, + { token, anonymousBrowserId: getAnonymousBrowserId() }, + { method: 'POST' } + ) : await apiRequest(`/events/${eventId}/check-in`, { token }, { method: 'POST' }); if (response.success) { setCheckedIn(true); + if (!user) { + markAnonymousEventCheckedIn(eventId); + } addNotification({ title: 'Success', message: 'You have successfully checked in!', @@ -220,6 +246,9 @@ function CheckInConfirmation() { const response = await apiRequest(`/events/${eventId}/check-in/anonymous`, body, { method: 'POST' }); if (response.success) { setCheckedIn(true); + if (!user) { + markAnonymousEventCheckedIn(eventId); + } addNotification({ title: 'Success', message: 'You have successfully checked in!', @@ -593,6 +622,11 @@ function CheckInConfirmation() { )}
+ {useFullyAnonymousCheckIn && ( +

+ Anonymous check-in is enabled for this event. This anonymous user can check in once without sharing personal details. +

+ )}
)} diff --git a/private-deps.lock b/private-deps.lock index 89bf8a30..e204c068 100644 --- a/private-deps.lock +++ b/private-deps.lock @@ -1,7 +1,7 @@ { "events": { "repo": "git@github.com:Study-Compass/Events-Backend.git", - "ref": "d71ebc6b94ba1535a11eef311a0aea7712259c4f", + "ref": "5dd303c923c71b7d6b70dbba8273c6fa530ef91e", "dest": "backend/events" } }