From ad3eba48b009fccc3bc67a019c5f8b10c8a3f4c0 Mon Sep 17 00:00:00 2001 From: echo Date: Tue, 31 Mar 2026 20:25:04 +0200 Subject: [PATCH] remove attendance tab and screen from mobile navigation --- apps/mobile/src/app/(tabs)/_layout.tsx | 6 - apps/mobile/src/app/(tabs)/attendance.tsx | 348 ------------------ .../src/components/AttendanceCalendar.tsx | 303 --------------- apps/mobile/src/components/CustomTabBar.tsx | 4 - 4 files changed, 661 deletions(-) delete mode 100644 apps/mobile/src/app/(tabs)/attendance.tsx delete mode 100644 apps/mobile/src/components/AttendanceCalendar.tsx diff --git a/apps/mobile/src/app/(tabs)/_layout.tsx b/apps/mobile/src/app/(tabs)/_layout.tsx index 6ab4348..407aa3e 100644 --- a/apps/mobile/src/app/(tabs)/_layout.tsx +++ b/apps/mobile/src/app/(tabs)/_layout.tsx @@ -83,12 +83,6 @@ export default function TabLayout() { title: "Plans", }} /> - (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, - }, -}); diff --git a/apps/mobile/src/components/AttendanceCalendar.tsx b/apps/mobile/src/components/AttendanceCalendar.tsx deleted file mode 100644 index 1f6f923..0000000 --- a/apps/mobile/src/components/AttendanceCalendar.tsx +++ /dev/null @@ -1,303 +0,0 @@ -import React, { useState, useEffect } from "react"; -import { View, Text, StyleSheet, TouchableOpacity } from "react-native"; -import { LinearGradient } from "expo-linear-gradient"; -import { Ionicons } from "@expo/vector-icons"; -import { theme } from "../styles/theme"; - -interface AttendanceRecord { - id: string; - checkInTime: string; - checkOutTime?: string | null; - duration?: number | null; -} - -interface AttendanceCalendarProps { - attendanceRecords: AttendanceRecord[]; -} - -export function AttendanceCalendar({ - attendanceRecords, -}: AttendanceCalendarProps) { - const [currentMonth, setCurrentMonth] = useState(new Date()); - const [calendarDays, setCalendarDays] = useState< - Array<{ date: Date | null; hasAttendance: boolean; isToday: boolean }> - >([]); - - useEffect(() => { - generateCalendar(currentMonth); - }, [currentMonth, attendanceRecords]); - - const generateCalendar = (month: Date) => { - const year = month.getFullYear(); - const monthIndex = month.getMonth(); - - // Get first day of month and number of days - const firstDay = new Date(year, monthIndex, 1); - const lastDay = new Date(year, monthIndex + 1, 0); - const daysInMonth = lastDay.getDate(); - const startingDayOfWeek = firstDay.getDay(); - - // Create attendance lookup set - const attendanceDates = new Set( - attendanceRecords.map((record) => - new Date(record.checkInTime).toDateString(), - ), - ); - - const today = new Date().toDateString(); - - // Build calendar array - const days: Array<{ - date: Date | null; - hasAttendance: boolean; - isToday: boolean; - }> = []; - - // Add empty cells for days before month starts - for (let i = 0; i < startingDayOfWeek; i++) { - days.push({ date: null, hasAttendance: false, isToday: false }); - } - - // Add days of the month - for (let day = 1; day <= daysInMonth; day++) { - const date = new Date(year, monthIndex, day); - const dateString = date.toDateString(); - days.push({ - date, - hasAttendance: attendanceDates.has(dateString), - isToday: dateString === today, - }); - } - - setCalendarDays(days); - }; - - const goToPreviousMonth = () => { - setCurrentMonth( - new Date(currentMonth.getFullYear(), currentMonth.getMonth() - 1), - ); - }; - - const goToNextMonth = () => { - setCurrentMonth( - new Date(currentMonth.getFullYear(), currentMonth.getMonth() + 1), - ); - }; - - const monthName = currentMonth.toLocaleDateString("en-US", { - month: "long", - year: "numeric", - }); - - return ( - - - {/* Header */} - - Attendance Calendar - - - {/* Month Navigation */} - - - - - {monthName} - - - - - - {/* Day Headers */} - - {["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"].map((day) => ( - - {day} - - ))} - - - {/* Calendar Grid */} - - {calendarDays.map((day, index) => ( - - {day.date ? ( - - - {day.date.getDate()} - - {day.hasAttendance && } - - ) : ( - - )} - - ))} - - - {/* Legend */} - - - - Attended - - - - Today - - - - - ); -} - -const styles = StyleSheet.create({ - container: { - paddingHorizontal: 20, - marginBottom: 20, - }, - card: { - borderRadius: theme.borderRadius["2xl"], - padding: 20, - }, - header: { - marginBottom: 16, - }, - title: { - fontSize: theme.typography.fontSize.xl, - fontWeight: theme.typography.fontWeight.bold, - color: theme.colors.gray800, - }, - monthNav: { - flexDirection: "row", - justifyContent: "space-between", - alignItems: "center", - marginBottom: 16, - paddingHorizontal: 8, - }, - navButton: { - padding: 8, - }, - monthText: { - fontSize: theme.typography.fontSize.lg, - fontWeight: theme.typography.fontWeight.semibold, - color: theme.colors.gray700, - }, - dayHeaders: { - flexDirection: "row", - marginBottom: 8, - }, - dayHeader: { - flex: 1, - textAlign: "center", - fontSize: theme.typography.fontSize.xs, - fontWeight: theme.typography.fontWeight.semibold, - color: theme.colors.gray500, - }, - calendarGrid: { - flexDirection: "row", - flexWrap: "wrap", - }, - dayCell: { - width: `${100 / 7}%`, - aspectRatio: 1, - padding: 2, - }, - dayContent: { - flex: 1, - justifyContent: "center", - alignItems: "center", - borderRadius: theme.borderRadius.md, - position: "relative", - }, - todayContent: { - backgroundColor: theme.colors.primaryLight, - borderWidth: 1, - borderColor: theme.colors.primary, - }, - attendanceContent: { - backgroundColor: theme.colors.successLight, - }, - emptyCell: { - flex: 1, - }, - dayText: { - fontSize: theme.typography.fontSize.sm, - color: theme.colors.gray700, - fontWeight: theme.typography.fontWeight.medium, - }, - todayText: { - color: theme.colors.white, - fontWeight: theme.typography.fontWeight.bold, - }, - attendanceText: { - color: theme.colors.white, - fontWeight: theme.typography.fontWeight.bold, - }, - attendanceDot: { - position: "absolute", - bottom: 2, - width: 4, - height: 4, - borderRadius: 2, - backgroundColor: theme.colors.white, - }, - legend: { - flexDirection: "row", - justifyContent: "center", - gap: 20, - marginTop: 16, - paddingTop: 16, - borderTopWidth: 1, - borderTopColor: theme.colors.gray200, - }, - legendItem: { - flexDirection: "row", - alignItems: "center", - gap: 6, - }, - legendDotAttendance: { - width: 12, - height: 12, - borderRadius: 6, - backgroundColor: theme.colors.successLight, - }, - legendDotToday: { - width: 12, - height: 12, - borderRadius: 6, - backgroundColor: theme.colors.primaryLight, - borderWidth: 1, - borderColor: theme.colors.primary, - }, - legendText: { - fontSize: theme.typography.fontSize.xs, - color: theme.colors.gray600, - }, -}); diff --git a/apps/mobile/src/components/CustomTabBar.tsx b/apps/mobile/src/components/CustomTabBar.tsx index 301bed3..c6bc00f 100644 --- a/apps/mobile/src/components/CustomTabBar.tsx +++ b/apps/mobile/src/components/CustomTabBar.tsx @@ -49,8 +49,6 @@ export function CustomTabBar({ return focused ? "home" : "home-outline"; case "goals": return focused ? "trophy" : "trophy-outline"; - case "attendance": - return focused ? "calendar" : "calendar-outline"; case "recommendations": return focused ? "sparkles" : "sparkles-outline"; case "profile": @@ -66,8 +64,6 @@ export function CustomTabBar({ return "Home"; case "goals": return "Goals"; - case "attendance": - return "Attendance"; case "recommendations": return "Plans"; case "profile":