Send activation link immediately for walk-in and manual registration accounts
Accounts created by staff on someone's behalf now get their activation link (email or WhatsApp) sent right away, instead of only on a first failed login attempt, matching what the Terms of Use already promised. This also fixed a real account with a real email being silently activated with a fixed, undisclosed password (Hope123). Also fixes the self-service kiosk's "Create an account" password field, which never actually took effect server-side, and removes the "Guest (no account)" checkboxes that no longer had any backend effect once every walk-in account started behaving the same way. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -242,7 +242,6 @@ export default function AtTheDoorPage() {
|
||||
authToken: token,
|
||||
body: {
|
||||
eventId,
|
||||
guestOnly: pendingUser.guestOnly,
|
||||
user: {
|
||||
name: pendingUser.name,
|
||||
...(pendingUser.email ? { email: pendingUser.email } : {}),
|
||||
@@ -282,7 +281,7 @@ export default function AtTheDoorPage() {
|
||||
});
|
||||
setQuantities(qtyMap);
|
||||
setMinQuantities({});
|
||||
setPendingUser({ guestOnly: true, name, email: email || null, phone: phone || null, notifPref });
|
||||
setPendingUser({ name, email: email || null, phone: phone || null, notifPref });
|
||||
setPendingEditReg(null);
|
||||
setShowNewAttendeeModal(false);
|
||||
setShowOptionsModal(true);
|
||||
|
||||
@@ -18,7 +18,6 @@ export default function ManualRegistrationPage() {
|
||||
const [name, setName] = useState("");
|
||||
const [email, setEmail] = useState("");
|
||||
const [phoneNumber, setPhoneNumber] = useState("");
|
||||
const [registerAsGuest, setRegisterAsGuest] = useState(false);
|
||||
const [busy, setBusy] = useState(false);
|
||||
const [error, setError] = useDismissingState<string | null>(null);
|
||||
const [createdReg, setCreatedReg] = useState<any | null>(null);
|
||||
@@ -43,7 +42,6 @@ export default function ManualRegistrationPage() {
|
||||
eventId,
|
||||
options: [{ eventOptionId: optionId, quantity }],
|
||||
user: { name, ...(email ? { email } : {}), ...(phoneNumber ? { phoneNumber } : {}) },
|
||||
guestOnly: registerAsGuest,
|
||||
},
|
||||
});
|
||||
setCreatedReg(res);
|
||||
@@ -89,10 +87,7 @@ export default function ManualRegistrationPage() {
|
||||
<input className="w-full border rounded px-3 py-2" value={name} onChange={(e) => setName(e.target.value)} required />
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="block text-sm font-medium">Email</label>
|
||||
<label className="text-xs flex items-center gap-2"><input type="checkbox" checked={registerAsGuest} onChange={e=>setRegisterAsGuest(e.target.checked)} /> Guest (no account)</label>
|
||||
</div>
|
||||
<label className="block text-sm font-medium">Email</label>
|
||||
<input type="email" className="w-full border rounded px-3 py-2" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="email@example.com" />
|
||||
</div>
|
||||
</div>
|
||||
@@ -100,7 +95,7 @@ export default function ManualRegistrationPage() {
|
||||
<label className="block text-sm font-medium">Cell Number</label>
|
||||
<input type="tel" className="w-full border rounded px-3 py-2" value={phoneNumber} onChange={(e) => setPhoneNumber(e.target.value)} placeholder="+27…" />
|
||||
</div>
|
||||
<p className="text-xs text-gray-500">At least one of email or cell number is required. If no email is provided, a guest account is created automatically.</p>
|
||||
<p className="text-xs text-gray-500">At least one of email or cell number is required. The account is created inactive, and an activation link is sent immediately (by email if provided, otherwise WhatsApp) so the attendee can set their own password.</p>
|
||||
{error && <p className="text-sm text-red-600">{error}</p>}
|
||||
<button type="submit" disabled={busy} className="bg-brand-600 hover:bg-brand-700 text-white rounded px-4 py-2 disabled:opacity-60">
|
||||
{busy ? "Submitting..." : "Create"}
|
||||
|
||||
@@ -76,7 +76,6 @@ export default function ManualRegistrationPage() {
|
||||
const [allUsers, setAllUsers] = useState<any[]>([]);
|
||||
|
||||
const [guest, setGuest] = useState({ name: "", email: "", phoneNumber: "" });
|
||||
const [registerAsGuest, setRegisterAsGuest] = useState(false);
|
||||
const [notifPref, setNotifPref] = useState<"email" | "whatsapp" | "both">("email");
|
||||
const [quantities, setQuantities] = useState<Record<string, number>>({});
|
||||
|
||||
@@ -287,7 +286,7 @@ export default function ManualRegistrationPage() {
|
||||
setError(null);
|
||||
setMessage(null);
|
||||
if (!selectedEventId) { setError("Please select an event."); return; }
|
||||
if (!guest.name || (!registerAsGuest && !guest.email)) { setError("Guest name and email are required."); return; }
|
||||
if (!guest.name || (!guest.email.trim() && !guest.phoneNumber.trim())) { setError("Guest name and at least one of email or phone are required."); return; }
|
||||
const opts = Object.entries(quantities)
|
||||
.filter(([, qty]) => qty > 0)
|
||||
.map(([key, quantity]) => {
|
||||
@@ -308,7 +307,6 @@ export default function ManualRegistrationPage() {
|
||||
eventId: selectedEventId,
|
||||
options: opts,
|
||||
user: guest,
|
||||
guestOnly: registerAsGuest,
|
||||
notificationPreference: resolvedPref,
|
||||
}
|
||||
});
|
||||
@@ -324,7 +322,6 @@ export default function ManualRegistrationPage() {
|
||||
|
||||
// Reset guest/ticket fields so the next registration starts from a clean slate
|
||||
setGuest({ name: "", email: "", phoneNumber: "" });
|
||||
setRegisterAsGuest(false);
|
||||
setNotifPref("email");
|
||||
setUserQuery("");
|
||||
setDropdownOpen(false);
|
||||
@@ -442,13 +439,6 @@ export default function ManualRegistrationPage() {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="border-t pt-3 mb-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<input id="registerAsGuest" type="checkbox" checked={registerAsGuest} onChange={e => setRegisterAsGuest(e.target.checked)} />
|
||||
<label htmlFor="registerAsGuest" className="text-sm text-gray-700">Guest (do not link to an existing account)</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-2">
|
||||
<input
|
||||
className="border rounded px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-brand-400"
|
||||
@@ -458,11 +448,10 @@ export default function ManualRegistrationPage() {
|
||||
/>
|
||||
<input
|
||||
className="border rounded px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-brand-400"
|
||||
placeholder={registerAsGuest ? "Email (optional for guest)" : "Email"}
|
||||
placeholder="Email (or provide a phone number below)"
|
||||
type="email"
|
||||
value={guest.email}
|
||||
onChange={e => setGuest({ ...guest, email: e.target.value })}
|
||||
required={!registerAsGuest}
|
||||
/>
|
||||
<input
|
||||
className="border rounded px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-brand-400"
|
||||
|
||||
@@ -386,7 +386,9 @@ export default function SelfServicePage() {
|
||||
...(visitorEmail.trim() ? { email: visitorEmail.trim() } : {}),
|
||||
...(visitorPhone.trim() ? { phoneNumber: visitorPhone.trim() } : {}),
|
||||
},
|
||||
guestOnly: !createAccount,
|
||||
// If the visitor declined to create an account, don't push an unsolicited
|
||||
// activation email/WhatsApp at them afterward.
|
||||
skipActivationNotice: !createAccount,
|
||||
notificationPreference: notificationPref,
|
||||
};
|
||||
if (createAccount && visitorPassword) {
|
||||
|
||||
@@ -14,7 +14,7 @@ export const supervisorManualHelpContent: HelpContent = {
|
||||
icon: UserPlus,
|
||||
content: (
|
||||
<div className="space-y-4">
|
||||
<p>Pick an event, then either search for an existing user or enter guest details (name, email, phone) and tick "Guest" to skip linking an account. Choose ticket options and quantities — early-bird pricing is applied automatically — then create the registration.</p>
|
||||
<p>Pick an event, then either search for an existing user or enter guest details (name, and at least one of email or phone). Choose ticket options and quantities — early-bird pricing is applied automatically — then create the registration. A new guest account is created inactive, and an activation link is sent immediately (by email if provided, otherwise WhatsApp) so they can set their own password.</p>
|
||||
<p className="text-xs text-gray-500">After creating a registration, the Record Payment tab is pre-filled with it — switch tabs to take payment right away.</p>
|
||||
</div>
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user