import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, ScrollView, Alert } from 'react-native' import { useState, useEffect, useRef } from 'react' import { useAuth } from '@clerk/clerk-expo' import { LinearGradient } from 'expo-linear-gradient' import { Ionicons } from '@expo/vector-icons' import { attendanceApi, Attendance } from '../../api/attendance' import { theme } from '../../styles/theme' import { Animated } from 'react-native' import { GeofenceStatus } from '../../components/GeofenceStatus' export default function AttendanceScreen() { const { getToken, userId } = useAuth() const [loading, setLoading] = useState(true) const [activeCheckIn, setActiveCheckIn] = useState(null) const [history, setHistory] = useState([]) const pulseAnim = useRef(new Animated.Value(1)).current useEffect(() => { if (activeCheckIn) { const pulse = Animated.loop( Animated.sequence([ Animated.timing(pulseAnim, { toValue: 1.05, duration: 1000, useNativeDriver: true, }), Animated.timing(pulseAnim, { toValue: 1, duration: 1000, useNativeDriver: true, }), ]) ) pulse.start() return () => pulse.stop() } }, [activeCheckIn]) const fetchAttendance = async () => { try { setLoading(true) const token = await getToken() if (!token) return console.log('Fetching attendance history') const data = await attendanceApi.getHistory(token) setHistory(data) // Check if there's an active check-in (latest one has no checkOutTime) if (data.length > 0 && !data[0].checkOutTime) { setActiveCheckIn(data[0]) } else { setActiveCheckIn(null) } } catch (error) { console.error('Error fetching attendance:', error) Alert.alert('Error', 'Failed to load attendance data') } finally { setLoading(false) } } useEffect(() => { fetchAttendance() }, []) const handleCheckIn = async () => { try { const token = await getToken() if (!token) return await attendanceApi.checkIn('gym', token) fetchAttendance() Alert.alert('Success', 'Checked in successfully!') } catch (error: any) { console.error('Check-in error:', error) Alert.alert('Error', error.response?.data || 'Failed to check in') } } const handleCheckOut = async () => { try { const token = await getToken() if (!token) return await attendanceApi.checkOut(token) fetchAttendance() Alert.alert('Success', 'Checked out successfully!') } catch (error: any) { console.error('Check-out error:', error) Alert.alert('Error', error.response?.data || 'Failed to check out') } } if (loading && !history.length) { return ( ) } return ( Attendance Track your gym visits {/* Geofencing Status Card */} {activeCheckIn ? ( Currently Checked In Since {new Date(activeCheckIn.checkInTime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} Check Out ) : ( Check In )} Recent History {history.map((item) => ( {new Date(item.checkInTime).toLocaleDateString()} {item.type.toUpperCase()} In: {new Date(item.checkInTime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} {item.checkOutTime && ( Out: {new Date(item.checkOutTime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })} )} ))} ) } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: theme.colors.background, }, content: { paddingBottom: 20, }, centered: { flex: 1, justifyContent: 'center', alignItems: 'center', }, header: { paddingTop: 60, paddingBottom: 24, paddingHorizontal: 24, marginBottom: 24, borderBottomLeftRadius: theme.borderRadius.xl, borderBottomRightRadius: theme.borderRadius.xl, }, title: { fontSize: theme.typography.fontSize['3xl'], fontWeight: theme.typography.fontWeight.bold, color: theme.colors.white, marginBottom: 4, }, subtitle: { fontSize: theme.typography.fontSize.base, color: 'rgba(255, 255, 255, 0.9)', }, actionContainer: { marginBottom: 32, paddingHorizontal: 20, }, checkInButton: { paddingVertical: 20, paddingHorizontal: 24, borderRadius: theme.borderRadius.xl, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', }, checkInButtonText: { color: theme.colors.white, fontSize: theme.typography.fontSize.xl, fontWeight: theme.typography.fontWeight.semibold, }, checkOutButton: { paddingVertical: 14, paddingHorizontal: 20, borderRadius: theme.borderRadius.lg, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', marginTop: 16, }, buttonText: { color: theme.colors.white, fontSize: theme.typography.fontSize.base, fontWeight: theme.typography.fontWeight.semibold, }, activeCard: { padding: 20, borderRadius: theme.borderRadius.xl, borderWidth: 1, borderColor: 'rgba(16, 185, 129, 0.2)', }, activeCardContent: { flexDirection: 'row', alignItems: 'center', marginBottom: 16, }, activeIconContainer: { marginRight: 16, }, activeIcon: { width: 56, height: 56, borderRadius: 28, justifyContent: 'center', alignItems: 'center', }, activeTextContainer: { flex: 1, }, activeText: { fontSize: theme.typography.fontSize.lg, fontWeight: theme.typography.fontWeight.semibold, color: theme.colors.gray900, marginBottom: 4, }, timeText: { fontSize: theme.typography.fontSize.sm, color: theme.colors.gray600, }, sectionTitle: { fontSize: theme.typography.fontSize.xl, fontWeight: theme.typography.fontWeight.semibold, marginBottom: 16, paddingHorizontal: 20, color: theme.colors.gray900, }, historyItem: { padding: 16, borderRadius: theme.borderRadius.xl, marginBottom: 12, marginHorizontal: 20, flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', borderWidth: 1, borderColor: 'rgba(59, 130, 246, 0.1)', }, historyLeft: { flexDirection: 'row', alignItems: 'center', gap: 12, }, historyIconContainer: { marginRight: 4, }, historyIcon: { width: 32, height: 32, borderRadius: 16, justifyContent: 'center', alignItems: 'center', }, dateText: { fontSize: theme.typography.fontSize.base, fontWeight: theme.typography.fontWeight.semibold, color: theme.colors.gray900, }, typeText: { fontSize: theme.typography.fontSize.xs, color: theme.colors.gray600, marginTop: 2, }, timeContainer: { alignItems: 'flex-end', }, historyTime: { fontSize: theme.typography.fontSize.sm, color: theme.colors.gray700, }, })