import React, { useState } from 'react'; import { Alert, KeyboardAvoidingView, Platform, Pressable, ScrollView, StyleSheet, Text, View, } from 'react-native'; import { useSafeAreaInsets } from 'react-native-safe-area-context'; import type { NativeStackScreenProps } from '@react-navigation/native-stack'; import type { AttendanceCheckInResult } from '../api/types'; import { CheckInResultModal } from '../components/CheckInResultModal'; import type { RootStackParamList } from '../navigation/types'; import { performAppAssignBlank, performAppUndoCheckIn } from '../offline/checkInService'; import { useOffline } from '../offline/OfflineContext'; import { colors, spacing, typography } from '../theme'; import { AppIcon, PrimaryButton, TextField } from '../components/ui'; type Props = NativeStackScreenProps; export function AssignBlankScreen({ navigation, route }: Props) { const { eventId, eventName, attendanceId, attendanceName, participantId, firstName: initialFirst, lastName: initialLast, email: initialEmail, phone: initialPhone, } = route.params; const insets = useSafeAreaInsets(); const { refreshStatuses } = useOffline(); const [firstName, setFirstName] = useState(initialFirst || ''); const [lastName, setLastName] = useState(initialLast || ''); const [email, setEmail] = useState(initialEmail || ''); const [phone, setPhone] = useState(initialPhone || ''); const [saving, setSaving] = useState(false); const [result, setResult] = useState(null); const [resultError, setResultError] = useState(null); const [pendingSync, setPendingSync] = useState(false); const [modalVisible, setModalVisible] = useState(false); const [undoLoading, setUndoLoading] = useState(false); const save = async () => { if (!firstName.trim()) { setResult(null); setResultError('First name is required'); setPendingSync(false); setModalVisible(true); return; } setSaving(true); const outcome = await performAppAssignBlank({ eventId, attendanceId, attendanceName, participantId, profile: { firstName, lastName, email, phone }, }); setSaving(false); if (!outcome.ok) { setResult(null); setResultError(outcome.error); setPendingSync(false); } else { setResult(outcome.result); setResultError(null); setPendingSync(outcome.pendingSync); refreshStatuses().catch(() => undefined); } setModalVisible(true); }; const closeModal = () => { if (undoLoading) return; setModalVisible(false); if (result && !resultError) { navigation.goBack(); return; } setResult(null); setResultError(null); setPendingSync(false); }; const handleUndo = async () => { if (!result) return; setUndoLoading(true); const outcome = await performAppUndoCheckIn({ eventId, attendanceId, attendanceName, participantId: result.participantId, }); setUndoLoading(false); if (!outcome.ok) { Alert.alert('Undo failed', outcome.error); return; } setModalVisible(false); setResult(null); setResultError(null); setPendingSync(false); refreshStatuses().catch(() => undefined); navigation.goBack(); }; return ( navigation.goBack()} hitSlop={10} style={styles.backBtn}> {eventName} Assign a Blank {attendanceName} Update this spare badge’s details, then save to check the person in. { void save(); }} /> ); } const styles = StyleSheet.create({ wrap: { flex: 1, backgroundColor: colors.background }, flex: { flex: 1 }, header: { paddingHorizontal: spacing.xl, paddingBottom: spacing.lg, overflow: 'hidden', }, headerBg: { ...StyleSheet.absoluteFill, backgroundColor: colors.primary, zIndex: 0, }, backBtn: { flexDirection: 'row', alignItems: 'center', gap: 8, marginBottom: spacing.md, alignSelf: 'flex-start', zIndex: 1, }, backLabel: { color: 'rgba(255,255,255,0.85)', fontSize: 14, fontWeight: '500', }, headerTitle: { color: '#fff', fontSize: 20, fontWeight: '700', marginBottom: 4, zIndex: 1, }, headerSubtitle: { color: 'rgba(255,255,255,0.90)', fontSize: 14, zIndex: 1, }, form: { paddingHorizontal: spacing.lg, paddingTop: spacing.lg, gap: 16, }, lead: { ...typography.small, color: colors.muted, marginBottom: 4, }, });