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,
},
})
+66 -36
View File
@@ -1,4 +1,4 @@
import { useState, useEffect } from "react";
import { useState, useEffect, useRef } from "react";
import {
View,
Text,
@@ -8,12 +8,15 @@ import {
ActivityIndicator,
RefreshControl,
Alert,
Animated,
} from "react-native";
import { useAuth } from "@clerk/clerk-expo";
import { Ionicons } from "@expo/vector-icons";
import { LinearGradient } from "expo-linear-gradient";
import { fitnessGoalsService, type FitnessGoal, type CreateGoalData } from "../../services/fitnessGoals";
import { GoalProgressCard } from "../../components/GoalProgressCard";
import { GoalCreationModal } from "../../components/GoalCreationModal";
import { theme } from "../../styles/theme";
export default function GoalsScreen() {
const { userId, getToken } = useAuth();
@@ -21,6 +24,7 @@ export default function GoalsScreen() {
const [loading, setLoading] = useState(true);
const [refreshing, setRefreshing] = useState(false);
const [showCreateModal, setShowCreateModal] = useState(false);
const fabScale = useRef(new Animated.Value(1)).current;
const fetchGoals = async () => {
if (!userId) return;
@@ -100,14 +104,19 @@ export default function GoalsScreen() {
<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />
}
>
<View style={styles.header}>
<LinearGradient
colors={theme.gradients.primary}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.header}
>
<View>
<Text style={styles.headerTitle}>My Fitness Goals</Text>
<Text style={styles.headerSubtitle}>
Track your fitness journey progress
</Text>
</View>
</View>
</LinearGradient>
{/* Stats Summary */}
{goals.length > 0 && (
@@ -179,12 +188,37 @@ export default function GoalsScreen() {
</ScrollView>
{/* Floating Action Button */}
<TouchableOpacity
style={styles.fab}
onPress={() => setShowCreateModal(true)}
>
<Ionicons name="add" size={28} color="#fff" />
</TouchableOpacity>
<Animated.View style={{ transform: [{ scale: fabScale }] }}>
<TouchableOpacity
onPress={() => setShowCreateModal(true)}
onPressIn={() => {
Animated.spring(fabScale, {
toValue: 0.9,
friction: 8,
tension: 100,
useNativeDriver: true,
}).start();
}}
onPressOut={() => {
Animated.spring(fabScale, {
toValue: 1,
friction: 8,
tension: 100,
useNativeDriver: true,
}).start();
}}
activeOpacity={0.9}
>
<LinearGradient
colors={theme.gradients.primary}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.fab}
>
<Ionicons name="add" size={28} color="#fff" />
</LinearGradient>
</TouchableOpacity>
</Animated.View>
{/* Create Goal Modal */}
<GoalCreationModal
@@ -199,7 +233,7 @@ export default function GoalsScreen() {
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#f3f4f6",
backgroundColor: theme.colors.background,
},
center: {
flex: 1,
@@ -207,18 +241,21 @@ const styles = StyleSheet.create({
alignItems: "center",
},
header: {
padding: 20,
backgroundColor: "#fff",
padding: 24,
paddingTop: 60,
paddingBottom: 24,
marginBottom: 10,
borderBottomLeftRadius: theme.borderRadius.xl,
borderBottomRightRadius: theme.borderRadius.xl,
},
headerTitle: {
fontSize: 28,
fontWeight: "bold",
color: "#111827",
fontSize: theme.typography.fontSize['3xl'],
fontWeight: theme.typography.fontWeight.bold,
color: theme.colors.white,
},
headerSubtitle: {
fontSize: 16,
color: "#6b7280",
fontSize: theme.typography.fontSize.base,
color: "rgba(255, 255, 255, 0.9)",
marginTop: 4,
},
statsContainer: {
@@ -228,20 +265,18 @@ const styles = StyleSheet.create({
},
statCard: {
flex: 1,
backgroundColor: "#fff",
backgroundColor: theme.colors.white,
padding: 16,
borderRadius: 12,
borderRadius: theme.borderRadius.xl,
alignItems: "center",
shadowColor: "#000",
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 2,
elevation: 2,
...theme.shadows.medium,
borderWidth: 1,
borderColor: "rgba(59, 130, 246, 0.1)",
},
statValue: {
fontSize: 24,
fontWeight: "bold",
color: "#2563eb",
fontSize: theme.typography.fontSize['2xl'],
fontWeight: theme.typography.fontWeight.bold,
color: theme.colors.primary,
marginBottom: 4,
},
statLabel: {
@@ -281,16 +316,11 @@ const styles = StyleSheet.create({
position: "absolute",
right: 20,
bottom: 20,
width: 56,
height: 56,
borderRadius: 28,
backgroundColor: "#2563eb",
width: 64,
height: 64,
borderRadius: 32,
justifyContent: "center",
alignItems: "center",
shadowColor: "#000",
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 8,
elevation: 8,
...theme.shadows.glow,
},
});
+203 -116
View File
@@ -1,18 +1,30 @@
import React from "react";
import React, { useEffect, useRef } from "react";
import {
View,
Text,
StyleSheet,
ScrollView,
TouchableOpacity,
Animated,
} from "react-native";
import { useUser } from "@clerk/clerk-expo";
import { Ionicons } from "@expo/vector-icons";
import { useRouter } from "expo-router";
import { LinearGradient } from "expo-linear-gradient";
import { theme } from "../../styles/theme";
export default function HomeScreen() {
const { user, isLoaded } = useUser();
const router = useRouter();
const fadeAnim = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.timing(fadeAnim, {
toValue: 1,
duration: 500,
useNativeDriver: true,
}).start();
}, []);
if (!isLoaded || !user) {
return (
@@ -27,32 +39,67 @@ export default function HomeScreen() {
return (
<ScrollView style={styles.container}>
<View style={styles.content}>
{/* Welcome Header */}
<View style={styles.header}>
<Animated.View style={[styles.content, { opacity: fadeAnim }]}>
{/* Gradient Header */}
<LinearGradient
colors={theme.gradients.primary}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.header}
>
<Text style={styles.greeting}>{greeting}!</Text>
<Text style={styles.name}>{firstName}</Text>
</View>
</LinearGradient>
{/* Quick Stats */}
{/* Quick Stats with Glassmorphism */}
<View style={styles.statsContainer}>
<View style={styles.statCard}>
<Ionicons name="calendar-outline" size={32} color="#2563eb" />
<LinearGradient
colors={['rgba(59, 130, 246, 0.1)', 'rgba(139, 92, 246, 0.1)']}
style={[styles.statCard, theme.shadows.medium]}
>
<View style={styles.statIconContainer}>
<LinearGradient
colors={theme.gradients.primary}
style={styles.statIconGradient}
>
<Ionicons name="calendar-outline" size={24} color="#fff" />
</LinearGradient>
</View>
<Text style={styles.statValue}>0</Text>
<Text style={styles.statLabel}>This Month</Text>
</View>
</LinearGradient>
<View style={styles.statCard}>
<Ionicons name="flame-outline" size={32} color="#ef4444" />
<LinearGradient
colors={['rgba(239, 68, 68, 0.1)', 'rgba(236, 72, 153, 0.1)']}
style={[styles.statCard, theme.shadows.medium]}
>
<View style={styles.statIconContainer}>
<LinearGradient
colors={theme.gradients.danger}
style={styles.statIconGradient}
>
<Ionicons name="flame-outline" size={24} color="#fff" />
</LinearGradient>
</View>
<Text style={styles.statValue}>0</Text>
<Text style={styles.statLabel}>Day Streak</Text>
</View>
</LinearGradient>
<View style={styles.statCard}>
<Ionicons name="trophy-outline" size={32} color="#f59e0b" />
<LinearGradient
colors={['rgba(245, 158, 11, 0.1)', 'rgba(217, 119, 6, 0.1)']}
style={[styles.statCard, theme.shadows.medium]}
>
<View style={styles.statIconContainer}>
<LinearGradient
colors={theme.gradients.warning}
style={styles.statIconGradient}
>
<Ionicons name="trophy-outline" size={24} color="#fff" />
</LinearGradient>
</View>
<Text style={styles.statValue}>0</Text>
<Text style={styles.statLabel}>Total Visits</Text>
</View>
</LinearGradient>
</View>
{/* Quick Actions */}
@@ -60,12 +107,16 @@ export default function HomeScreen() {
<Text style={styles.sectionTitle}>Quick Actions</Text>
<TouchableOpacity
style={styles.actionButton}
style={[styles.actionButton, theme.shadows.medium]}
onPress={() => router.push("/fitness-profile")}
activeOpacity={0.7}
>
<View style={styles.actionIcon}>
<LinearGradient
colors={['rgba(236, 72, 153, 0.15)', 'rgba(236, 72, 153, 0.05)']}
style={styles.actionIcon}
>
<Ionicons name="fitness-outline" size={24} color="#ec4899" />
</View>
</LinearGradient>
<View style={styles.actionContent}>
<Text style={styles.actionTitle}>Fitness Profile</Text>
<Text style={styles.actionSubtitle}>
@@ -75,12 +126,16 @@ export default function HomeScreen() {
<Ionicons name="chevron-forward" size={20} color="#9ca3af" />
</TouchableOpacity>
<View style={styles.actionButton}>
<View style={styles.actionIcon}>
<TouchableOpacity
style={[styles.actionButton, theme.shadows.medium]}
activeOpacity={0.7}
>
<LinearGradient
colors={['rgba(59, 130, 246, 0.15)', 'rgba(59, 130, 246, 0.05)']}
style={styles.actionIcon}
>
<Ionicons name="log-in-outline" size={24} color="#2563eb" />
</View>
</LinearGradient>
<View style={styles.actionContent}>
<Text style={styles.actionTitle}>Check In</Text>
<Text style={styles.actionSubtitle}>
@@ -88,13 +143,18 @@ export default function HomeScreen() {
</Text>
</View>
<Ionicons name="chevron-forward" size={20} color="#9ca3af" />
</View>
</TouchableOpacity>
<View style={styles.actionButton}>
<View style={styles.actionIcon}>
<TouchableOpacity
style={[styles.actionButton, theme.shadows.medium]}
activeOpacity={0.7}
>
<LinearGradient
colors={['rgba(16, 185, 129, 0.15)', 'rgba(16, 185, 129, 0.05)']}
style={styles.actionIcon}
>
<Ionicons name="calendar-outline" size={24} color="#10b981" />
</View>
</LinearGradient>
<View style={styles.actionContent}>
<Text style={styles.actionTitle}>View Schedule</Text>
<Text style={styles.actionSubtitle}>
@@ -102,24 +162,35 @@ export default function HomeScreen() {
</Text>
</View>
<Ionicons name="chevron-forward" size={20} color="#9ca3af" />
</View>
</TouchableOpacity>
<View style={styles.actionButton}>
<View style={styles.actionIcon}>
<TouchableOpacity
style={[styles.actionButton, theme.shadows.medium]}
activeOpacity={0.7}
>
<LinearGradient
colors={['rgba(139, 92, 246, 0.15)', 'rgba(139, 92, 246, 0.05)']}
style={styles.actionIcon}
>
<Ionicons name="card-outline" size={24} color="#8b5cf6" />
</View>
</LinearGradient>
<View style={styles.actionContent}>
<Text style={styles.actionTitle}>Payments</Text>
<Text style={styles.actionSubtitle}>View payment history</Text>
</View>
<Ionicons name="chevron-forward" size={20} color="#9ca3af" />
</View>
</TouchableOpacity>
</View>
{/* Membership Info */}
{/* Membership Info with Gradient */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Membership</Text>
<View style={styles.membershipCard}>
<LinearGradient
colors={theme.gradients.primary}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={[styles.membershipCard, theme.shadows.strong]}
>
<View style={styles.membershipHeader}>
<Text style={styles.membershipType}>Basic Plan</Text>
<View style={styles.statusBadge}>
@@ -132,21 +203,28 @@ export default function HomeScreen() {
<Text style={styles.membershipDate}>
Member since {new Date(user.createdAt!).toLocaleDateString()}
</Text>
</View>
</LinearGradient>
</View>
{/* Recent Activity */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Recent Activity</Text>
<View style={styles.emptyState}>
<Ionicons name="barbell-outline" size={48} color="#d1d5db" />
<View style={[styles.emptyState, theme.shadows.medium]}>
<View style={styles.emptyIconContainer}>
<LinearGradient
colors={['rgba(209, 213, 219, 0.3)', 'rgba(209, 213, 219, 0.1)']}
style={styles.emptyIconGradient}
>
<Ionicons name="barbell-outline" size={48} color="#9ca3af" />
</LinearGradient>
</View>
<Text style={styles.emptyStateText}>No recent activity</Text>
<Text style={styles.emptyStateSubtext}>
Check in to start tracking your workouts
</Text>
</View>
</View>
</View>
</Animated.View>
</ScrollView>
);
}
@@ -161,81 +239,90 @@ function getGreeting(): string {
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#f5f5f5",
backgroundColor: theme.colors.background,
},
content: {
padding: 20,
paddingBottom: 20,
},
header: {
marginBottom: 24,
padding: 24,
paddingTop: 60,
paddingBottom: 32,
borderBottomLeftRadius: theme.borderRadius['2xl'],
borderBottomRightRadius: theme.borderRadius['2xl'],
marginBottom: 20,
},
greeting: {
fontSize: 16,
color: "#6b7280",
fontSize: theme.typography.fontSize.base,
color: "rgba(255, 255, 255, 0.9)",
marginBottom: 4,
},
name: {
fontSize: 32,
fontWeight: "bold",
color: "#1a1a1a",
fontSize: theme.typography.fontSize['4xl'],
fontWeight: theme.typography.fontWeight.bold,
color: theme.colors.white,
},
statsContainer: {
flexDirection: "row",
justifyContent: "space-between",
paddingHorizontal: 16,
marginBottom: 24,
gap: 12,
},
statCard: {
flex: 1,
backgroundColor: "white",
borderRadius: 12,
backgroundColor: theme.colors.white,
borderRadius: theme.borderRadius.xl,
padding: 16,
alignItems: "center",
marginHorizontal: 4,
shadowColor: "#000",
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 2,
elevation: 2,
borderWidth: 1,
borderColor: "rgba(255, 255, 255, 0.3)",
},
statIconContainer: {
marginBottom: 8,
},
statIconGradient: {
width: 48,
height: 48,
borderRadius: 24,
justifyContent: "center",
alignItems: "center",
},
statValue: {
fontSize: 24,
fontWeight: "bold",
color: "#1a1a1a",
marginTop: 8,
fontSize: theme.typography.fontSize['2xl'],
fontWeight: theme.typography.fontWeight.bold,
color: theme.colors.gray900,
marginTop: 4,
marginBottom: 4,
},
statLabel: {
fontSize: 12,
color: "#6b7280",
fontSize: theme.typography.fontSize.xs,
color: theme.colors.gray600,
textAlign: "center",
fontWeight: theme.typography.fontWeight.medium,
},
section: {
marginBottom: 24,
paddingHorizontal: 20,
},
sectionTitle: {
fontSize: 18,
fontWeight: "600",
color: "#1a1a1a",
fontSize: theme.typography.fontSize.xl,
fontWeight: theme.typography.fontWeight.semibold,
color: theme.colors.gray900,
marginBottom: 12,
},
actionButton: {
flexDirection: "row",
alignItems: "center",
backgroundColor: "white",
borderRadius: 12,
backgroundColor: theme.colors.white,
borderRadius: theme.borderRadius.xl,
padding: 16,
marginBottom: 8,
shadowColor: "#000",
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 2,
elevation: 2,
marginBottom: 12,
},
actionIcon: {
width: 48,
height: 48,
borderRadius: 24,
backgroundColor: "#f0f9ff",
width: 56,
height: 56,
borderRadius: 28,
justifyContent: "center",
alignItems: "center",
marginRight: 12,
@@ -244,77 +331,77 @@ const styles = StyleSheet.create({
flex: 1,
},
actionTitle: {
fontSize: 16,
fontWeight: "600",
color: "#1a1a1a",
fontSize: theme.typography.fontSize.base,
fontWeight: theme.typography.fontWeight.semibold,
color: theme.colors.gray900,
marginBottom: 2,
},
actionSubtitle: {
fontSize: 14,
color: "#6b7280",
fontSize: theme.typography.fontSize.sm,
color: theme.colors.gray600,
},
membershipCard: {
backgroundColor: "white",
borderRadius: 12,
padding: 16,
shadowColor: "#000",
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 2,
elevation: 2,
borderRadius: theme.borderRadius.xl,
padding: 20,
},
membershipHeader: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 8,
marginBottom: 12,
},
membershipType: {
fontSize: 18,
fontWeight: "600",
color: "#1a1a1a",
fontSize: theme.typography.fontSize.xl,
fontWeight: theme.typography.fontWeight.semibold,
color: theme.colors.white,
},
statusBadge: {
backgroundColor: "#dcfce7",
backgroundColor: "rgba(255, 255, 255, 0.25)",
paddingHorizontal: 12,
paddingVertical: 4,
borderRadius: 12,
paddingVertical: 6,
borderRadius: theme.borderRadius.lg,
borderWidth: 1,
borderColor: "rgba(255, 255, 255, 0.3)",
},
statusText: {
fontSize: 12,
fontWeight: "600",
color: "#16a34a",
fontSize: theme.typography.fontSize.xs,
fontWeight: theme.typography.fontWeight.semibold,
color: theme.colors.white,
},
membershipEmail: {
fontSize: 14,
color: "#6b7280",
fontSize: theme.typography.fontSize.sm,
color: "rgba(255, 255, 255, 0.9)",
marginBottom: 4,
},
membershipDate: {
fontSize: 12,
color: "#9ca3af",
fontSize: theme.typography.fontSize.xs,
color: "rgba(255, 255, 255, 0.7)",
},
emptyState: {
backgroundColor: "white",
borderRadius: 12,
backgroundColor: theme.colors.white,
borderRadius: theme.borderRadius.xl,
padding: 32,
alignItems: "center",
shadowColor: "#000",
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 2,
elevation: 2,
},
emptyIconContainer: {
marginBottom: 16,
},
emptyIconGradient: {
width: 96,
height: 96,
borderRadius: 48,
justifyContent: "center",
alignItems: "center",
},
emptyStateText: {
fontSize: 16,
fontWeight: "600",
color: "#6b7280",
marginTop: 12,
fontSize: theme.typography.fontSize.base,
fontWeight: theme.typography.fontWeight.semibold,
color: theme.colors.gray700,
marginBottom: 4,
},
emptyStateSubtext: {
fontSize: 14,
color: "#9ca3af",
fontSize: theme.typography.fontSize.sm,
color: theme.colors.gray500,
textAlign: "center",
},
});
+188 -124
View File
@@ -10,6 +10,8 @@ import {
import { useUser, useAuth } from "@clerk/clerk-expo";
import { useRouter } from "expo-router";
import { Ionicons } from "@expo/vector-icons";
import { LinearGradient } from "expo-linear-gradient";
import { theme } from "../../styles/theme";
export default function ProfileScreen() {
const { user } = useUser();
@@ -45,20 +47,31 @@ export default function ProfileScreen() {
return (
<ScrollView style={styles.container}>
<View style={styles.content}>
{/* Profile Header */}
<View style={styles.profileCard}>
{/* Profile Header with Gradient */}
<LinearGradient
colors={theme.gradients.primary}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={[styles.profileCard, theme.shadows.strong]}
>
<View style={styles.avatarContainer}>
{user.imageUrl ? (
<View style={styles.avatar}>
<LinearGradient
colors={['rgba(255, 255, 255, 0.3)', 'rgba(255, 255, 255, 0.1)']}
style={styles.avatar}
>
<Text style={styles.avatarText}>
{user.firstName?.charAt(0)}
{user.lastName?.charAt(0)}
</Text>
</View>
</LinearGradient>
) : (
<View style={styles.avatar}>
<LinearGradient
colors={['rgba(255, 255, 255, 0.3)', 'rgba(255, 255, 255, 0.1)']}
style={styles.avatar}
>
<Ionicons name="person" size={40} color="#fff" />
</View>
</LinearGradient>
)}
</View>
@@ -74,58 +87,80 @@ export default function ProfileScreen() {
{user.primaryPhoneNumber.phoneNumber}
</Text>
)}
</View>
</LinearGradient>
{/* Account Information */}
<View style={styles.section}>
<Text style={styles.sectionTitle}>Account Information</Text>
<View style={styles.infoRow}>
<View style={styles.infoLabel}>
<Ionicons name="mail-outline" size={20} color="#666" />
<Text style={styles.infoLabelText}>Email</Text>
</View>
<Text style={styles.infoValue}>
{user.primaryEmailAddress?.emailAddress}
</Text>
</View>
{user.primaryPhoneNumber && (
<View style={[styles.infoCard, theme.shadows.medium]}>
<View style={styles.infoRow}>
<View style={styles.infoLabel}>
<Ionicons name="call-outline" size={20} color="#666" />
<Text style={styles.infoLabelText}>Phone</Text>
<LinearGradient
colors={['rgba(59, 130, 246, 0.15)', 'rgba(59, 130, 246, 0.05)']}
style={styles.infoIconContainer}
>
<Ionicons name="mail-outline" size={18} color={theme.colors.primary} />
</LinearGradient>
<Text style={styles.infoLabelText}>Email</Text>
</View>
<Text style={styles.infoValue}>
{user.primaryPhoneNumber.phoneNumber}
{user.primaryEmailAddress?.emailAddress}
</Text>
</View>
)}
<View style={styles.infoRow}>
<View style={styles.infoLabel}>
<Ionicons name="calendar-outline" size={20} color="#666" />
<Text style={styles.infoLabelText}>Member Since</Text>
</View>
<Text style={styles.infoValue}>
{new Date(user.createdAt!).toLocaleDateString()}
</Text>
</View>
{user.primaryPhoneNumber && (
<View style={styles.infoRow}>
<View style={styles.infoLabel}>
<LinearGradient
colors={['rgba(16, 185, 129, 0.15)', 'rgba(16, 185, 129, 0.05)']}
style={styles.infoIconContainer}
>
<Ionicons name="call-outline" size={18} color={theme.colors.success} />
</LinearGradient>
<Text style={styles.infoLabelText}>Phone</Text>
</View>
<Text style={styles.infoValue}>
{user.primaryPhoneNumber.phoneNumber}
</Text>
</View>
)}
<View style={styles.infoRow}>
<View style={styles.infoLabel}>
<Ionicons
name="shield-checkmark-outline"
size={20}
color="#666"
/>
<Text style={styles.infoLabelText}>Email Verified</Text>
<View style={styles.infoRow}>
<View style={styles.infoLabel}>
<LinearGradient
colors={['rgba(245, 158, 11, 0.15)', 'rgba(245, 158, 11, 0.05)']}
style={styles.infoIconContainer}
>
<Ionicons name="calendar-outline" size={18} color={theme.colors.warning} />
</LinearGradient>
<Text style={styles.infoLabelText}>Member Since</Text>
</View>
<Text style={styles.infoValue}>
{new Date(user.createdAt!).toLocaleDateString()}
</Text>
</View>
<View style={[styles.infoRow, { borderBottomWidth: 0 }]}>
<View style={styles.infoLabel}>
<LinearGradient
colors={['rgba(16, 185, 129, 0.15)', 'rgba(16, 185, 129, 0.05)']}
style={styles.infoIconContainer}
>
<Ionicons
name="shield-checkmark-outline"
size={18}
color={theme.colors.success}
/>
</LinearGradient>
<Text style={styles.infoLabelText}>Email Verified</Text>
</View>
<Text style={styles.infoValue}>
{user.primaryEmailAddress?.verification?.status === "verified"
? "Yes"
: "No"}
</Text>
</View>
<Text style={styles.infoValue}>
{user.primaryEmailAddress?.verification?.status === "verified"
? "Yes"
: "No"}
</Text>
</View>
</View>
@@ -133,35 +168,62 @@ export default function ProfileScreen() {
<View style={styles.section}>
<Text style={styles.sectionTitle}>Quick Actions</Text>
<TouchableOpacity style={styles.actionButton}>
<Ionicons name="person-outline" size={24} color="#2563eb" />
<TouchableOpacity style={[styles.actionButton, theme.shadows.medium]} activeOpacity={0.7}>
<LinearGradient
colors={['rgba(59, 130, 246, 0.15)', 'rgba(59, 130, 246, 0.05)']}
style={styles.actionIconContainer}
>
<Ionicons name="person-outline" size={22} color={theme.colors.primary} />
</LinearGradient>
<Text style={styles.actionButtonText}>Edit Profile</Text>
<Ionicons name="chevron-forward" size={20} color="#999" />
<Ionicons name="chevron-forward" size={20} color={theme.colors.gray400} />
</TouchableOpacity>
<TouchableOpacity style={styles.actionButton}>
<Ionicons name="notifications-outline" size={24} color="#2563eb" />
<TouchableOpacity style={[styles.actionButton, theme.shadows.medium]} activeOpacity={0.7}>
<LinearGradient
colors={['rgba(245, 158, 11, 0.15)', 'rgba(245, 158, 11, 0.05)']}
style={styles.actionIconContainer}
>
<Ionicons name="notifications-outline" size={22} color={theme.colors.warning} />
</LinearGradient>
<Text style={styles.actionButtonText}>Notifications</Text>
<Ionicons name="chevron-forward" size={20} color="#999" />
<Ionicons name="chevron-forward" size={20} color={theme.colors.gray400} />
</TouchableOpacity>
<TouchableOpacity style={styles.actionButton}>
<Ionicons name="card-outline" size={24} color="#2563eb" />
<TouchableOpacity style={[styles.actionButton, theme.shadows.medium]} activeOpacity={0.7}>
<LinearGradient
colors={['rgba(139, 92, 246, 0.15)', 'rgba(139, 92, 246, 0.05)']}
style={styles.actionIconContainer}
>
<Ionicons name="card-outline" size={22} color={theme.colors.purple} />
</LinearGradient>
<Text style={styles.actionButtonText}>Payment History</Text>
<Ionicons name="chevron-forward" size={20} color="#999" />
<Ionicons name="chevron-forward" size={20} color={theme.colors.gray400} />
</TouchableOpacity>
<TouchableOpacity style={styles.actionButton}>
<Ionicons name="settings-outline" size={24} color="#2563eb" />
<TouchableOpacity style={[styles.actionButton, theme.shadows.medium]} activeOpacity={0.7}>
<LinearGradient
colors={['rgba(107, 114, 128, 0.15)', 'rgba(107, 114, 128, 0.05)']}
style={styles.actionIconContainer}
>
<Ionicons name="settings-outline" size={22} color={theme.colors.gray600} />
</LinearGradient>
<Text style={styles.actionButtonText}>Settings</Text>
<Ionicons name="chevron-forward" size={20} color="#999" />
<Ionicons name="chevron-forward" size={20} color={theme.colors.gray400} />
</TouchableOpacity>
</View>
{/* Sign Out Button */}
<TouchableOpacity style={styles.logoutButton} onPress={handleLogout}>
<Ionicons name="log-out-outline" size={20} color="#fff" />
<Text style={styles.logoutText}>Sign Out</Text>
<TouchableOpacity onPress={handleLogout} activeOpacity={0.8}>
<LinearGradient
colors={theme.gradients.danger}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
style={[styles.logoutButton, theme.shadows.glowDanger]}
>
<Ionicons name="log-out-outline" size={20} color="#fff" />
<Text style={styles.logoutText}>Sign Out</Text>
</LinearGradient>
</TouchableOpacity>
{/* App Version */}
@@ -174,133 +236,135 @@ export default function ProfileScreen() {
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#f5f5f5",
backgroundColor: theme.colors.background,
},
content: {
padding: 20,
paddingTop: 60,
},
profileCard: {
backgroundColor: "white",
borderRadius: 16,
padding: 24,
borderRadius: theme.borderRadius.xl,
padding: 28,
alignItems: "center",
shadowColor: "#000",
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.1,
shadowRadius: 8,
elevation: 4,
marginBottom: 24,
},
avatarContainer: {
marginBottom: 16,
},
avatar: {
width: 80,
height: 80,
borderRadius: 40,
backgroundColor: "#2563eb",
width: 96,
height: 96,
borderRadius: 48,
justifyContent: "center",
alignItems: "center",
borderWidth: 3,
borderColor: "rgba(255, 255, 255, 0.3)",
},
avatarText: {
fontSize: 32,
fontWeight: "bold",
color: "#fff",
fontSize: theme.typography.fontSize['4xl'],
fontWeight: theme.typography.fontWeight.bold,
color: theme.colors.white,
},
name: {
fontSize: 24,
fontWeight: "bold",
color: "#1a1a1a",
marginBottom: 4,
fontSize: theme.typography.fontSize['2xl'],
fontWeight: theme.typography.fontWeight.bold,
color: theme.colors.white,
marginBottom: 6,
},
email: {
fontSize: 16,
color: "#666",
fontSize: theme.typography.fontSize.base,
color: "rgba(255, 255, 255, 0.9)",
marginBottom: 4,
},
phone: {
fontSize: 14,
color: "#999",
fontSize: theme.typography.fontSize.sm,
color: "rgba(255, 255, 255, 0.8)",
},
section: {
backgroundColor: "white",
borderRadius: 16,
padding: 20,
marginBottom: 16,
shadowColor: "#000",
shadowOffset: { width: 0, height: 1 },
shadowOpacity: 0.05,
shadowRadius: 4,
elevation: 2,
marginBottom: 24,
},
sectionTitle: {
fontSize: 18,
fontWeight: "600",
color: "#1a1a1a",
marginBottom: 16,
fontSize: theme.typography.fontSize.xl,
fontWeight: theme.typography.fontWeight.semibold,
color: theme.colors.gray900,
marginBottom: 12,
},
infoCard: {
backgroundColor: theme.colors.white,
borderRadius: theme.borderRadius.xl,
padding: 16,
},
infoRow: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingVertical: 12,
paddingVertical: 14,
borderBottomWidth: 1,
borderBottomColor: "#f0f0f0",
borderBottomColor: theme.colors.gray200,
},
infoLabel: {
flexDirection: "row",
alignItems: "center",
gap: 8,
gap: 10,
},
infoIconContainer: {
width: 36,
height: 36,
borderRadius: 18,
justifyContent: "center",
alignItems: "center",
},
infoLabelText: {
fontSize: 14,
color: "#666",
fontWeight: "500",
fontSize: theme.typography.fontSize.sm,
color: theme.colors.gray700,
fontWeight: theme.typography.fontWeight.medium,
},
infoValue: {
fontSize: 14,
color: "#1a1a1a",
fontWeight: "500",
fontSize: theme.typography.fontSize.sm,
color: theme.colors.gray900,
fontWeight: theme.typography.fontWeight.semibold,
},
actionButton: {
flexDirection: "row",
alignItems: "center",
paddingVertical: 16,
borderBottomWidth: 1,
borderBottomColor: "#f0f0f0",
backgroundColor: theme.colors.white,
borderRadius: theme.borderRadius.xl,
padding: 16,
marginBottom: 12,
},
actionIconContainer: {
width: 44,
height: 44,
borderRadius: 22,
justifyContent: "center",
alignItems: "center",
marginRight: 12,
},
actionButtonText: {
flex: 1,
fontSize: 16,
color: "#1a1a1a",
marginLeft: 12,
fontWeight: "500",
fontSize: theme.typography.fontSize.base,
color: theme.colors.gray900,
fontWeight: theme.typography.fontWeight.medium,
},
logoutButton: {
backgroundColor: "#ef4444",
paddingVertical: 16,
borderRadius: 12,
borderRadius: theme.borderRadius.lg,
alignItems: "center",
justifyContent: "center",
flexDirection: "row",
gap: 8,
marginTop: 8,
marginBottom: 16,
shadowColor: "#ef4444",
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 8,
elevation: 4,
},
logoutText: {
color: "white",
fontSize: 16,
fontWeight: "600",
color: theme.colors.white,
fontSize: theme.typography.fontSize.base,
fontWeight: theme.typography.fontWeight.semibold,
},
version: {
textAlign: "center",
fontSize: 12,
color: "#999",
fontSize: theme.typography.fontSize.xs,
color: theme.colors.gray500,
marginTop: 8,
marginBottom: 20,
},
+119 -57
View File
@@ -2,7 +2,9 @@ import { useEffect, useState } from "react";
import { View, Text, FlatList, ActivityIndicator, StyleSheet, RefreshControl } from "react-native";
import { useAuth } from "@clerk/clerk-expo";
import { Ionicons } from "@expo/vector-icons";
import { LinearGradient } from "expo-linear-gradient";
import { API_BASE_URL, API_ENDPOINTS } from "../../config/api";
import { theme } from "../../styles/theme";
interface Recommendation {
id: string;
@@ -70,37 +72,71 @@ export default function RecommendationsScreen() {
if (loading) {
return (
<View style={styles.centered}>
<ActivityIndicator size="large" color="#000" />
<ActivityIndicator size="large" color={theme.colors.primary} />
</View>
);
}
return (
<View style={styles.container}>
<Text style={styles.header}>AI Recommendations</Text>
<LinearGradient
colors={theme.gradients.primary}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }}
style={styles.header}
>
<Text style={styles.headerTitle}>AI Recommendations</Text>
</LinearGradient>
{/* AI Context Info Banner */}
<View style={styles.infoBanner}>
<Ionicons name="sparkles" size={20} color="#2563eb" />
{/* AI Context Info Banner with Glassmorphism */}
<LinearGradient
colors={['rgba(59, 130, 246, 0.15)', 'rgba(139, 92, 246, 0.1)'] as const}
style={styles.infoBanner}
>
<View style={styles.infoBannerIconContainer}>
<LinearGradient
colors={theme.gradients.primary}
style={styles.infoBannerIcon}
>
<Ionicons name="sparkles" size={16} color="#fff" />
</LinearGradient>
</View>
<Text style={styles.infoBannerText}>
Personalized based on your active fitness goals and progress
</Text>
</View>
</LinearGradient>
<FlatList
data={recommendations}
keyExtractor={(item) => item.id}
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
contentContainerStyle={styles.listContent}
ListEmptyComponent={
<View style={styles.emptyContainer}>
<View style={styles.emptyIconContainer}>
<LinearGradient
colors={['rgba(209, 213, 219, 0.3)', 'rgba(209, 213, 219, 0.1)'] as const}
style={styles.emptyIconGradient}
>
<Ionicons name="sparkles-outline" size={48} color="#9ca3af" />
</LinearGradient>
</View>
<Text style={styles.empty}>No recommendations available yet.</Text>
<Text style={styles.emptySub}>Pull down to refresh</Text>
</View>
}
renderItem={({ item }) => (
<View style={styles.card}>
<LinearGradient
colors={['rgba(255, 255, 255, 1)', 'rgba(249, 250, 251, 1)'] as const}
style={[styles.card, theme.shadows.medium]}
>
<View style={styles.cardHeader}>
<Text style={styles.status}>{item.status.toUpperCase()}</Text>
<LinearGradient
colors={theme.gradients.success}
style={styles.statusBadge}
>
<Text style={styles.statusText}>{item.status.toUpperCase()}</Text>
</LinearGradient>
<Text style={styles.date}>{new Date(item.createdAt).toLocaleDateString()}</Text>
</View>
@@ -120,7 +156,7 @@ export default function RecommendationsScreen() {
<Text style={styles.content}>{item.dietPlan}</Text>
</>
)}
</View>
</LinearGradient>
)}
/>
</View>
@@ -130,83 +166,99 @@ export default function RecommendationsScreen() {
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#f5f5f5',
backgroundColor: theme.colors.background,
},
header: {
fontSize: 28,
fontWeight: 'bold',
padding: 20,
paddingBottom: 12,
color: '#1a1a1a',
paddingTop: 60,
paddingBottom: 24,
paddingHorizontal: 24,
borderBottomLeftRadius: theme.borderRadius.xl,
borderBottomRightRadius: theme.borderRadius.xl,
},
headerTitle: {
fontSize: theme.typography.fontSize['3xl'],
fontWeight: theme.typography.fontWeight.bold,
color: theme.colors.white,
},
infoBanner: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: '#eff6ff',
marginHorizontal: 16,
marginTop: 16,
marginBottom: 12,
padding: 12,
borderRadius: 8,
borderLeftWidth: 3,
borderLeftColor: '#2563eb',
gap: 8,
padding: 14,
borderRadius: theme.borderRadius.lg,
borderWidth: 1,
borderColor: 'rgba(59, 130, 246, 0.2)',
gap: 10,
},
infoBannerIconContainer: {
marginRight: 4,
},
infoBannerIcon: {
width: 32,
height: 32,
borderRadius: 16,
justifyContent: 'center',
alignItems: 'center',
},
infoBannerText: {
flex: 1,
fontSize: 13,
color: '#1e40af',
fontSize: theme.typography.fontSize.sm,
color: theme.colors.gray700,
lineHeight: 18,
fontWeight: theme.typography.fontWeight.medium,
},
centered: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#f5f5f5',
backgroundColor: theme.colors.background,
},
listContent: {
padding: 16,
},
card: {
backgroundColor: '#fff',
padding: 16,
marginHorizontal: 16,
marginBottom: 12,
borderRadius: 12,
shadowColor: '#000',
shadowOpacity: 0.1,
shadowRadius: 4,
shadowOffset: { width: 0, height: 2 },
elevation: 3,
padding: 18,
marginBottom: 14,
borderRadius: theme.borderRadius.xl,
borderWidth: 1,
borderColor: 'rgba(59, 130, 246, 0.1)',
},
cardHeader: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 12,
marginBottom: 14,
paddingBottom: 12,
borderBottomWidth: 1,
borderBottomColor: '#e0e0e0',
borderBottomColor: theme.colors.gray200,
},
status: {
fontSize: 12,
fontWeight: '600',
color: '#2e7d32',
backgroundColor: '#e8f5e9',
paddingHorizontal: 8,
paddingVertical: 4,
borderRadius: 4,
statusBadge: {
paddingHorizontal: 10,
paddingVertical: 5,
borderRadius: theme.borderRadius.md,
},
statusText: {
fontSize: theme.typography.fontSize.xs,
fontWeight: theme.typography.fontWeight.semibold,
color: theme.colors.white,
},
date: {
fontSize: 12,
color: '#666',
fontSize: theme.typography.fontSize.xs,
color: theme.colors.gray600,
fontWeight: theme.typography.fontWeight.medium,
},
sectionTitle: {
fontSize: 14,
fontWeight: '600',
color: '#1a1a1a',
fontSize: theme.typography.fontSize.base,
fontWeight: theme.typography.fontWeight.semibold,
color: theme.colors.gray900,
marginTop: 12,
marginBottom: 6,
},
content: {
fontSize: 14,
color: '#333',
fontSize: theme.typography.fontSize.sm,
color: theme.colors.gray700,
lineHeight: 20,
},
emptyContainer: {
@@ -214,16 +266,26 @@ const styles = StyleSheet.create({
justifyContent: 'center',
paddingVertical: 60,
},
emptyIconContainer: {
marginBottom: 16,
},
emptyIconGradient: {
width: 96,
height: 96,
borderRadius: 48,
justifyContent: 'center',
alignItems: 'center',
},
empty: {
textAlign: 'center',
fontSize: 16,
color: '#666',
fontWeight: '500',
fontSize: theme.typography.fontSize.base,
color: theme.colors.gray700,
fontWeight: theme.typography.fontWeight.semibold,
marginBottom: 4,
},
emptySub: {
textAlign: 'center',
fontSize: 14,
color: '#999',
marginTop: 8,
fontSize: theme.typography.fontSize.sm,
color: theme.colors.gray500,
},
});