claude take :(

This commit is contained in:
dimitar committed 2025-11-26 01:57:33 +01:00
1 parent fc12cecd30
commit 73d2c4c1ed
13 files changed
+1460 -517

No files matched your search

+195 -71
View File
@@ -1,15 +1,39 @@
import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, ScrollView, Alert } from 'react-native'
import { useState, useEffect } from 'react'
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'
export default function AttendanceScreen() {
const { getToken, userId } = useAuth()
const [loading, setLoading] = useState(true)
const [activeCheckIn, setActiveCheckIn] = useState<Attendance | null>(null)
const [history, setHistory] = useState<Attendance[]>([])
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 {
@@ -70,42 +94,100 @@ export default function AttendanceScreen() {
if (loading && !history.length) {
return (
<View style={styles.centered}>
<ActivityIndicator size="large" color="#000" />
<ActivityIndicator size="large" color={theme.colors.primary} />
</View>
)
}
return (
<ScrollView style={styles.container} contentContainerStyle={styles.content}>
<Text style={styles.title}>Attendance</Text>
<Text style={styles.subtitle}>Track your gym visits</Text>
<LinearGradient
colors={theme.gradients.primary}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.header}
>
<Text style={styles.title}>Attendance</Text>
<Text style={styles.subtitle}>Track your gym visits</Text>
</LinearGradient>
<View style={styles.actionContainer}>
{activeCheckIn ? (
<View style={styles.activeCard}>
<Text style={styles.activeText}>Currently Checked In</Text>
<Text style={styles.timeText}>
Since {new Date(activeCheckIn.checkInTime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</Text>
<TouchableOpacity style={styles.checkOutButton} onPress={handleCheckOut}>
<Text style={styles.buttonText}>Check Out</Text>
<LinearGradient
colors={['rgba(16, 185, 129, 0.15)', 'rgba(5, 150, 105, 0.1)']}
style={[styles.activeCard, theme.shadows.medium]}
>
<View style={styles.activeCardContent}>
<View style={styles.activeIconContainer}>
<LinearGradient
colors={theme.gradients.success}
style={styles.activeIcon}
>
<Ionicons name="checkmark-circle" size={32} color="#fff" />
</LinearGradient>
</View>
<View style={styles.activeTextContainer}>
<Text style={styles.activeText}>Currently Checked In</Text>
<Text style={styles.timeText}>
Since {new Date(activeCheckIn.checkInTime).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
</Text>
</View>
</View>
<TouchableOpacity onPress={handleCheckOut} activeOpacity={0.8}>
<LinearGradient
colors={theme.gradients.danger}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
style={[styles.checkOutButton, theme.shadows.medium]}
>
<Ionicons name="log-out-outline" size={20} color="#fff" style={{ marginRight: 8 }} />
<Text style={styles.buttonText}>Check Out</Text>
</LinearGradient>
</TouchableOpacity>
</View>
</LinearGradient>
) : (
<TouchableOpacity style={styles.checkInButton} onPress={handleCheckIn}>
<Text style={styles.buttonText}>Check In</Text>
</TouchableOpacity>
<Animated.View style={{ transform: [{ scale: pulseAnim }] }}>
<TouchableOpacity onPress={handleCheckIn} activeOpacity={0.8}>
<LinearGradient
colors={theme.gradients.primary}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
style={[styles.checkInButton, theme.shadows.glow]}
>
<Ionicons name="log-in-outline" size={24} color="#fff" style={{ marginRight: 8 }} />
<Text style={styles.checkInButtonText}>Check In</Text>
</LinearGradient>
</TouchableOpacity>
</Animated.View>
)}
</View>
<Text style={styles.sectionTitle}>Recent History</Text>
{history.map((item) => (
<View key={item.id} style={styles.historyItem}>
<View>
<Text style={styles.dateText}>
{new Date(item.checkInTime).toLocaleDateString()}
</Text>
<Text style={styles.typeText}>{item.type.toUpperCase()}</Text>
<LinearGradient
key={item.id}
colors={['rgba(255, 255, 255, 1)', 'rgba(249, 250, 251, 1)'] as const}
style={[styles.historyItem, theme.shadows.medium]}
>
<View style={styles.historyLeft}>
<View style={styles.historyIconContainer}>
<LinearGradient
colors={item.checkOutTime ? theme.gradients.success : theme.gradients.primary}
style={styles.historyIcon}
>
<Ionicons
name={item.checkOutTime ? "checkmark" : "time-outline"}
size={16}
color="#fff"
/>
</LinearGradient>
</View>
<View>
<Text style={styles.dateText}>
{new Date(item.checkInTime).toLocaleDateString()}
</Text>
<Text style={styles.typeText}>{item.type.toUpperCase()}</Text>
</View>
</View>
<View style={styles.timeContainer}>
<Text style={styles.historyTime}>
@@ -117,7 +199,7 @@ export default function AttendanceScreen() {
</Text>
)}
</View>
</View>
</LinearGradient>
))}
</ScrollView>
)
@@ -126,105 +208,147 @@ export default function AttendanceScreen() {
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
backgroundColor: theme.colors.background,
},
content: {
padding: 20,
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: 28,
fontWeight: 'bold',
fontSize: theme.typography.fontSize['3xl'],
fontWeight: theme.typography.fontWeight.bold,
color: theme.colors.white,
marginBottom: 4,
color: '#1a1a1a',
},
subtitle: {
fontSize: 16,
color: '#666',
marginBottom: 24,
fontSize: theme.typography.fontSize.base,
color: 'rgba(255, 255, 255, 0.9)',
},
actionContainer: {
marginBottom: 32,
paddingHorizontal: 20,
},
checkInButton: {
backgroundColor: '#000',
paddingVertical: 16,
borderRadius: 12,
paddingVertical: 20,
paddingHorizontal: 24,
borderRadius: theme.borderRadius.xl,
flexDirection: 'row',
alignItems: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 4,
elevation: 3,
justifyContent: 'center',
},
checkInButtonText: {
color: theme.colors.white,
fontSize: theme.typography.fontSize.xl,
fontWeight: theme.typography.fontWeight.semibold,
},
checkOutButton: {
backgroundColor: '#ff3b30',
paddingVertical: 16,
borderRadius: 12,
paddingVertical: 14,
paddingHorizontal: 20,
borderRadius: theme.borderRadius.lg,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
marginTop: 16,
},
buttonText: {
color: '#fff',
fontSize: 18,
fontWeight: '600',
color: theme.colors.white,
fontSize: theme.typography.fontSize.base,
fontWeight: theme.typography.fontWeight.semibold,
},
activeCard: {
backgroundColor: '#fff',
padding: 20,
borderRadius: 16,
borderRadius: theme.borderRadius.xl,
borderWidth: 1,
borderColor: '#e0e0e0',
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: 18,
fontWeight: '600',
color: '#2e7d32',
fontSize: theme.typography.fontSize.lg,
fontWeight: theme.typography.fontWeight.semibold,
color: theme.colors.gray900,
marginBottom: 4,
},
timeText: {
fontSize: 14,
color: '#666',
fontSize: theme.typography.fontSize.sm,
color: theme.colors.gray600,
},
sectionTitle: {
fontSize: 20,
fontWeight: '600',
fontSize: theme.typography.fontSize.xl,
fontWeight: theme.typography.fontWeight.semibold,
marginBottom: 16,
color: '#1a1a1a',
paddingHorizontal: 20,
color: theme.colors.gray900,
},
historyItem: {
backgroundColor: '#fff',
padding: 16,
borderRadius: 12,
borderRadius: theme.borderRadius.xl,
marginBottom: 12,
marginHorizontal: 20,
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
shadowColor: '#000',
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 2,
elevation: 2,
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: 16,
fontWeight: '500',
color: '#1a1a1a',
fontSize: theme.typography.fontSize.base,
fontWeight: theme.typography.fontWeight.semibold,
color: theme.colors.gray900,
},
typeText: {
fontSize: 12,
color: '#666',
fontSize: theme.typography.fontSize.xs,
color: theme.colors.gray600,
marginTop: 2,
},
timeContainer: {
alignItems: 'flex-end',
},
historyTime: {
fontSize: 14,
color: '#444',
fontSize: theme.typography.fontSize.sm,
color: theme.colors.gray700,
},
})