diff --git a/backend/src/controllers/eventController.js b/backend/src/controllers/eventController.js index b6ed3ce..42eff26 100644 --- a/backend/src/controllers/eventController.js +++ b/backend/src/controllers/eventController.js @@ -241,10 +241,12 @@ const getEventsAll = async (req, res) => { try { const includePast = req.query.includePast === 'true'; const includeInactive = req.query.includeInactive === 'true'; + const excludeClosed = req.query.excludeClosed === 'true'; const where = {}; if (!includeInactive) where.isActive = true; if (!includePast) where.endDate = { gte: new Date() }; + if (excludeClosed) where.cashupStatus = { not: 'closed' }; const canIncludeTiers = !!(prisma && prisma.earlyBirdTier && typeof prisma.earlyBirdTier.findMany === 'function'); const canIncludeVariants = !!(prisma && prisma.optionVariant && typeof prisma.optionVariant.findMany === 'function'); diff --git a/backend/src/controllers/userController.js b/backend/src/controllers/userController.js index a4dc3e3..9e96e7d 100644 --- a/backend/src/controllers/userController.js +++ b/backend/src/controllers/userController.js @@ -482,13 +482,24 @@ const checkUserExists = async (req, res) => { const existingUser = await prisma.user.findFirst({ where: { OR: searchClauses }, - select: { email: true, phoneNumber: true }, + select: { name: true, email: true, phoneNumber: true, notificationPreference: true }, }); + const hasEmail = !!existingUser?.email && !existingUser.email.endsWith('@guest.local'); + const hasPhone = !!existingUser?.phoneNumber; + res.json({ exists: !!existingUser, - hasEmail: !!existingUser?.email && !existingUser.email.endsWith('@guest.local'), - hasPhone: !!existingUser?.phoneNumber, + hasEmail, + hasPhone, + // Safe-to-display fields only, for autofilling a lookup form — never the password. + // Guest placeholder emails are withheld the same way hasEmail already treats them. + user: existingUser ? { + name: existingUser.name, + email: hasEmail ? existingUser.email : null, + phoneNumber: hasPhone ? existingUser.phoneNumber : null, + notificationPreference: existingUser.notificationPreference, + } : null, }); } catch (error) { res.status(res.statusCode === 200 ? 400 : res.statusCode).json({ message: safeErrorMessage(error) }); diff --git a/frontend/src/app/self-service/page.tsx b/frontend/src/app/self-service/page.tsx index d56e5cd..32a5a27 100644 --- a/frontend/src/app/self-service/page.tsx +++ b/frontend/src/app/self-service/page.tsx @@ -180,7 +180,7 @@ export default function SelfServicePage() { setEventsLoading(true); try { const data: KioskEvent[] = await apiFetch( - "/api/events/all?includePast=false&includeInactive=false", + "/api/events/all?includePast=false&includeInactive=false&excludeClosed=true", { authToken: token } ); setEvents(data); @@ -431,7 +431,11 @@ export default function SelfServicePage() { }; }, []); - // ─── Check whether an account already exists for the entered email/phone ── + // ─── Check whether an account already exists for the entered email/phone, and + // autofill the rest of the visitor's details from it (name, the other contact + // channel, notification preference) so staff don't have to re-ask for info + // already on file. Only fills fields that are still blank — never overwrites + // whatever the operator is actively typing. ── useEffect(() => { const email = visitorEmail.trim(); const phone = visitorPhone.trim(); @@ -445,11 +449,18 @@ export default function SelfServicePage() { const params = new URLSearchParams(); if (email) params.set("email", email); if (phone) params.set("phone", phone); - const data = await apiFetch<{ exists: boolean }>( - `/api/users/check-exists?${params.toString()}`, - { authToken: supervisorToken } - ); + const data = await apiFetch<{ + exists: boolean; + user?: { name: string; email: string | null; phoneNumber: string | null; notificationPreference: "email" | "whatsapp" | "both" } | null; + }>(`/api/users/check-exists?${params.toString()}`, { authToken: supervisorToken }); setAccountExists(!!data?.exists); + if (data?.user) { + const found = data.user; + setVisitorName((prev) => (prev.trim() ? prev : found.name)); + if (found.email && !email) setVisitorEmail(found.email); + if (found.phoneNumber && !phone) setVisitorPhone(found.phoneNumber); + setNotificationPref(found.notificationPreference); + } } catch { setAccountExists(false); } finally { @@ -795,7 +806,7 @@ export default function SelfServicePage() { {/* Account creation toggle — hidden once we know an account already exists */} {accountExists ? (