Add /crush33 comp-ticket portal + ticket-type badge on scan
Some checks failed
Build Android APK / build-apk (push) Failing after 1h11m4s
Some checks failed
Build Android APK / build-apk (push) Failing after 1h11m4s
Portal (/crush33): password-gated page (PORTAL_PASSWORD) for admins to create entry-only tickets from just name + email, with a category (Guest/Worker/Performer/Volunteer/Speaker). Creates a 1-admission ticket, emails the QR, and shows the QR on-screen. New Ticket Type column. Scanner: shows a prominent TYPE badge (🎭 PERFORMER, 🛠️ WORKER, …) on the confirm + success screens and in admin, so staff can see it's a special ticket. Added Worker + Performer personas to /test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
parent
b7c77afe02
commit
4b78c82b35
9 changed files with 230 additions and 0 deletions
|
|
@ -202,6 +202,7 @@ function TicketCard({ ticket, onAdjust }: { ticket: TicketView; onAdjust: (t: Ti
|
|||
|
||||
const tags: string[] = [];
|
||||
const e = ticket.extras;
|
||||
if (ticket.ticketType) tags.push(`🎫 ${ticket.ticketType}`);
|
||||
if (e.donorTier === "member") tags.push("🐻 Member");
|
||||
else if (e.isDonor) tags.push("⭐ Donor");
|
||||
if (e.carParking) tags.push("🚗 Car");
|
||||
|
|
|
|||
|
|
@ -242,6 +242,7 @@ export default function ScannerScreen() {
|
|||
{phase === "success" && ticket && (
|
||||
<ResultOverlay status="success" onDismiss={resume}>
|
||||
<Text style={styles.bigIcon}>✓</Text>
|
||||
<TypeBadge type={ticket.ticketType} />
|
||||
{isIce ? (
|
||||
<>
|
||||
<Text style={styles.bigTitle}>
|
||||
|
|
@ -366,6 +367,25 @@ function ExtrasRow({ ticket }: { ticket: TicketView }) {
|
|||
);
|
||||
}
|
||||
|
||||
const TYPE_ICON: Record<string, string> = {
|
||||
Guest: "🎫",
|
||||
Worker: "🛠️",
|
||||
Performer: "🎭",
|
||||
Volunteer: "🙌",
|
||||
Speaker: "🎤",
|
||||
};
|
||||
|
||||
function TypeBadge({ type }: { type: string }) {
|
||||
if (!type) return null;
|
||||
return (
|
||||
<View style={styles.typeBadge}>
|
||||
<Text style={styles.typeBadgeText}>
|
||||
{(TYPE_ICON[type] ?? "🎫") + " " + type.toUpperCase()}
|
||||
</Text>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
function AdultNames({ names }: { names: string[] }) {
|
||||
if (!names.length) return null;
|
||||
return (
|
||||
|
|
@ -405,6 +425,7 @@ function ConfirmCard({
|
|||
return (
|
||||
<ScrollView style={styles.card} contentContainerStyle={styles.cardContent}>
|
||||
<Text style={styles.cardName}>{ticket.name}</Text>
|
||||
<TypeBadge type={ticket.ticketType} />
|
||||
<Text style={styles.cardCode}>{ticket.code}</Text>
|
||||
<Text style={styles.cardCounts}>
|
||||
<Text style={{ color: theme.successBright, fontWeight: "800" }}>{remaining}</Text> of {total} {unit} remaining
|
||||
|
|
@ -507,6 +528,14 @@ const styles = StyleSheet.create({
|
|||
donorFigureDivider: { width: 1, alignSelf: "stretch", backgroundColor: "rgba(255,255,255,0.35)", marginVertical: 8 },
|
||||
donorEmail: { color: "rgba(255,255,255,0.85)", fontSize: 14, marginTop: 18 },
|
||||
|
||||
typeBadge: {
|
||||
backgroundColor: "rgba(255,255,255,0.22)",
|
||||
borderRadius: 999,
|
||||
paddingHorizontal: 18,
|
||||
paddingVertical: 8,
|
||||
marginTop: 10,
|
||||
},
|
||||
typeBadgeText: { color: "#fff", fontSize: 20, fontWeight: "900", letterSpacing: 1 },
|
||||
namesBox: { marginTop: 12, alignItems: "center", gap: 3 },
|
||||
nameLine: { color: "#fff", fontSize: 18, fontWeight: "600", textAlign: "center" },
|
||||
tags: { flexDirection: "row", flexWrap: "wrap", justifyContent: "center", gap: 8, marginTop: 14 },
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ export interface TicketView {
|
|||
code: string;
|
||||
name: string;
|
||||
email: string;
|
||||
ticketType: string;
|
||||
total: number;
|
||||
redeemed: number;
|
||||
remaining: number;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue