Add bundle deals to manual registration
Lists the event's deals and shows the Complete bundle / + cards, as at the door and on the kiosk. Part of 1.28.1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+1
-1
@@ -11,7 +11,7 @@ and this project follows [Semantic Versioning](https://semver.org/).
|
||||
|
||||
### Added
|
||||
|
||||
- At the door and the self-service kiosk now list the event's bundle deals and show the same bundle cards as the register form: once a ticket from a bundle is picked, **Complete bundle** tops up the ticket numbers, and **+** adds another full set.
|
||||
- At the door, manual registration and the self-service kiosk now list the event's bundle deals and show the same bundle cards as the register form: once a ticket from a bundle is picked, **Complete bundle** tops up the ticket numbers, and **+** adds another full set.
|
||||
|
||||
### Fixed
|
||||
|
||||
|
||||
@@ -7,12 +7,12 @@ import { apiFetch, fetchAllUsers } from "@/lib/api";
|
||||
import { scoreUser } from "@/lib/fuzzyMatch";
|
||||
import { useDismissingState } from "@/hooks/useDismissingState";
|
||||
import { CouponCodeField, AppliedCoupon } from "@/components/shared/CouponCodeField";
|
||||
import { BundleSavings, useBundleQuote } from "@/components/shared/BundleDeals";
|
||||
import { BundleBuilder, BundleOffers, BundleSavings, useBundleQuote } from "@/components/shared/BundleDeals";
|
||||
import { AttendeeNamesEditor, AttendeeEntries, TicketDelivery, attendeesPayload, consentDetailsMissing } from "@/components/shared/AttendeeNamesEditor";
|
||||
import { UserPlus, CreditCard } from "lucide-react";
|
||||
import { errorMessage } from "@/lib/utils";
|
||||
import { optionUnitNow, variantUnitNow } from "@/lib/pricing";
|
||||
import type { Event, EventOption, Registration, RegistrationOption, User } from "@/types";
|
||||
import type { Event, EventOption, PublicBundle, Registration, RegistrationOption, User } from "@/types";
|
||||
import { BackLink } from "@/components/shared/BackLink";
|
||||
|
||||
// ─── Pricing helpers ─────────────────────────────────────────────────────────
|
||||
@@ -60,6 +60,7 @@ export default function ManualRegistrationPage() {
|
||||
const [guest, setGuest] = useState({ name: "", email: "", phoneNumber: "" });
|
||||
const [notifPref, setNotifPref] = useState<"email" | "whatsapp" | "both">("email");
|
||||
const [quantities, setQuantities] = useState<Record<string, number>>({});
|
||||
const [bundles, setBundles] = useState<PublicBundle[]>([]);
|
||||
|
||||
// User search state
|
||||
const [userQuery, setUserQuery] = useState("");
|
||||
@@ -267,6 +268,16 @@ export default function ManualRegistrationPage() {
|
||||
}
|
||||
}, [selectedEventId, events]);
|
||||
|
||||
// The events list doesn't carry bundle deals; the single-event response does.
|
||||
useEffect(() => {
|
||||
if (!selectedEventId) { setBundles([]); return; }
|
||||
let cancelled = false;
|
||||
apiFetch<Event>(`/api/events/${selectedEventId}`, { authToken: token || undefined })
|
||||
.then(ev => { if (!cancelled) setBundles(ev?.bundles || []); })
|
||||
.catch(() => { if (!cancelled) setBundles([]); });
|
||||
return () => { cancelled = true; };
|
||||
}, [selectedEventId, token]);
|
||||
|
||||
const totalDue = useMemo(() => {
|
||||
return options.reduce((sum, o) => {
|
||||
if ((o.variants || []).length > 0) {
|
||||
@@ -535,6 +546,7 @@ export default function ManualRegistrationPage() {
|
||||
|
||||
<div className="mt-6 border rounded-xl p-4 bg-white shadow-sm">
|
||||
<div className="text-lg font-semibold mb-3">3) Select ticket options</div>
|
||||
{options.length > 0 && <BundleOffers bundles={bundles} className="mb-3" />}
|
||||
{options.length === 0 ? (
|
||||
<div className="text-sm text-gray-500">Select an event to view options.</div>
|
||||
) : (
|
||||
@@ -598,6 +610,13 @@ export default function ManualRegistrationPage() {
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<BundleBuilder
|
||||
className="mt-3"
|
||||
bundles={bundles}
|
||||
options={options}
|
||||
qty={(o, v) => quantities[v ? `${o}::${v}` : o] || 0}
|
||||
onSetQuantity={(o, v, n) => setQuantities(q => ({ ...q, [v ? `${o}::${v}` : o]: n }))}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{selectedEventAllowsNames && (
|
||||
|
||||
Reference in New Issue
Block a user