import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, ScrollView, Alert, } from "react-native"; import { useState, useEffect } from "react"; import { useAuth } from "@clerk/clerk-expo"; import { Ionicons } from "@expo/vector-icons"; import { useTheme } from "../../contexts/ThemeContext"; import { MinimalCard } from "../../components/MinimalCard"; import { SectionHeader } from "../../components/SectionHeader"; import { MinimalButton } from "../../components/MinimalButton"; import { Badge } from "../../components/Badge"; import { IconContainer } from "../../components/IconContainer"; import { attendanceApi, Attendance } from "../../api/attendance"; import { AttendanceCalendar } from "../../components/AttendanceCalendar"; import { useStatistics } from "../../contexts/StatisticsContext"; import { getErrorMessage } from "../../utils/error-helpers"; import log from "../../utils/logger"; export default function AttendanceScreen() { const { getToken, userId } = useAuth(); const { colors, typography } = useTheme(); const { clearCache: clearStatisticsCache } = useStatistics(); const [loading, setLoading] = useState(true); const [activeCheckIn, setActiveCheckIn] = useState(null); const [history, setHistory] = useState([]); const fetchAttendance = async () => { try { setLoading(true); const token = await getToken(); if (!token) return; log.debug("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) { log.error("Failed to fetch 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); clearStatisticsCache(); fetchAttendance(); Alert.alert("Success", "Checked in successfully!"); } catch (error: unknown) { log.error("Failed to check in", error); Alert.alert("Error", getErrorMessage(error, "Failed to check in")); } }; const handleCheckOut = async () => { try { const token = await getToken(); if (!token) return; await attendanceApi.checkOut(token); clearStatisticsCache(); fetchAttendance(); Alert.alert("Success", "Checked out successfully!"); } catch (error: unknown) { log.error("Failed to check out", error); Alert.alert("Error", getErrorMessage(error, "Failed to check out")); } }; if (loading && !history.length) { return ( ); } return ( {/* Header */} Attendance {activeCheckIn ? "You're crushing it today!" : history.length === 0 ? "Ready to start your fitness journey?" : "Track your gym visits and build streaks!"} {/* Check In/Out Section */} {activeCheckIn ? ( ✅ Currently Checked In Since{" "} {new Date(activeCheckIn.checkInTime).toLocaleTimeString( [], { hour: "2-digit", minute: "2-digit", }, )} ) : ( )} {/* Attendance Calendar */} {/* Recent History */} {history.length === 0 ? ( 📍 No attendance history yet Check in to start building your streak! 🔥 ) : ( {history.slice(0, 10).map((record, index) => { const checkIn = new Date(record.checkInTime); const checkOut = record.checkOutTime ? new Date(record.checkOutTime) : null; const duration = checkOut ? Math.round((checkOut.getTime() - checkIn.getTime()) / 60000) : null; return ( {checkIn.toLocaleDateString()} {checkIn.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", })} {checkOut && ` - ${checkOut.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", })}`} {duration && ( )} ); })} )} {/* Bottom Spacer */} ); } const styles = StyleSheet.create({ container: { flex: 1, }, centered: { flex: 1, justifyContent: "center", alignItems: "center", }, content: { paddingBottom: 20, }, header: { paddingHorizontal: 24, paddingTop: 60, paddingBottom: 24, }, section: { paddingHorizontal: 24, marginBottom: 24, }, activeCard: { padding: 20, borderRadius: 20, }, activeHeader: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", }, activeHeaderLeft: { flexDirection: "row", alignItems: "center", flex: 1, }, emptyState: { alignItems: "center", paddingVertical: 40, paddingHorizontal: 20, }, historyList: { gap: 12, }, historyItem: { flexDirection: "row", justifyContent: "space-between", alignItems: "center", }, historyLeft: { flexDirection: "row", alignItems: "center", flex: 1, }, });