context and goals definition added
This commit is contained in:
1 parent
28b5b52a8f
commit
803c205994
10 files changed
+1073
-170
No files matched your search
@@ -0,0 +1,251 @@
|
||||
import React from 'react';
|
||||
import { View, Text, StyleSheet, TouchableOpacity, Alert } from 'react-native';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
import type { FitnessGoal } from '../services/fitnessGoals';
|
||||
|
||||
interface GoalProgressCardProps {
|
||||
goal: FitnessGoal;
|
||||
onPress?: () => void;
|
||||
onComplete?: () => void;
|
||||
onDelete?: () => void;
|
||||
}
|
||||
|
||||
export function GoalProgressCard({ goal, onPress, onComplete, onDelete }: GoalProgressCardProps) {
|
||||
const isCompleted = goal.status === 'completed';
|
||||
const progress = goal.progress || 0;
|
||||
|
||||
// Calculate days remaining
|
||||
const daysRemaining = goal.targetDate
|
||||
? Math.ceil((new Date(goal.targetDate).getTime() - Date.now()) / (1000 * 60 * 60 * 24))
|
||||
: null;
|
||||
|
||||
const getGoalTypeIcon = (type: string) => {
|
||||
switch (type) {
|
||||
case 'weight_target': return 'scale-outline';
|
||||
case 'strength_milestone': return 'barbell-outline';
|
||||
case 'endurance_target': return 'bicycle-outline';
|
||||
case 'flexibility_goal': return 'body-outline';
|
||||
case 'habit_building': return 'calendar-outline';
|
||||
default: return 'flag-outline';
|
||||
}
|
||||
};
|
||||
|
||||
const getPriorityColor = (priority: string) => {
|
||||
switch (priority) {
|
||||
case 'high': return '#ef4444';
|
||||
case 'medium': return '#f59e0b';
|
||||
case 'low': return '#10b981';
|
||||
default: return '#6b7280';
|
||||
}
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
Alert.alert(
|
||||
'Delete Goal',
|
||||
'Are you sure you want to delete this goal?',
|
||||
[
|
||||
{ text: 'Cancel', style: 'cancel' },
|
||||
{ text: 'Delete', style: 'destructive', onPress: onDelete },
|
||||
]
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<TouchableOpacity
|
||||
style={[styles.card, isCompleted && styles.cardCompleted]}
|
||||
onPress={onPress}
|
||||
activeOpacity={0.7}
|
||||
>
|
||||
<View style={styles.header}>
|
||||
<View style={styles.titleRow}>
|
||||
<Ionicons
|
||||
name={getGoalTypeIcon(goal.goalType) as any}
|
||||
size={24}
|
||||
color={isCompleted ? '#9ca3af' : '#2563eb'}
|
||||
/>
|
||||
<View style={styles.titleContainer}>
|
||||
<Text style={[styles.title, isCompleted && styles.titleCompleted]}>
|
||||
{goal.title}
|
||||
</Text>
|
||||
{goal.description && (
|
||||
<Text style={styles.description} numberOfLines={2}>
|
||||
{goal.description}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View style={styles.actions}>
|
||||
{!isCompleted && onComplete && (
|
||||
<TouchableOpacity onPress={onComplete} style={styles.actionButton}>
|
||||
<Ionicons name="checkmark-circle-outline" size={24} color="#10b981" />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
{onDelete && (
|
||||
<TouchableOpacity onPress={handleDelete} style={styles.actionButton}>
|
||||
<Ionicons name="trash-outline" size={22} color="#ef4444" />
|
||||
</TouchableOpacity>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{goal.targetValue && (
|
||||
<View style={styles.progressSection}>
|
||||
<View style={styles.progressInfo}>
|
||||
<Text style={styles.progressText}>
|
||||
{goal.currentValue || 0} / {goal.targetValue} {goal.unit || ''}
|
||||
</Text>
|
||||
<Text style={styles.progressPercentage}>{progress.toFixed(0)}%</Text>
|
||||
</View>
|
||||
|
||||
<View style={styles.progressBarContainer}>
|
||||
<View
|
||||
style={[
|
||||
styles.progressBar,
|
||||
{ width: `${Math.min(progress, 100)}%` },
|
||||
isCompleted && styles.progressBarCompleted
|
||||
]}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
|
||||
<View style={styles.footer}>
|
||||
<View style={[styles.priorityBadge, { backgroundColor: getPriorityColor(goal.priority) }]}>
|
||||
<Text style={styles.priorityText}>{goal.priority.toUpperCase()}</Text>
|
||||
</View>
|
||||
|
||||
{daysRemaining !== null && !isCompleted && (
|
||||
<Text style={[styles.daysRemaining, daysRemaining < 0 && styles.overdue]}>
|
||||
{daysRemaining < 0
|
||||
? `${Math.abs(daysRemaining)} days overdue`
|
||||
: `${daysRemaining} days remaining`
|
||||
}
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{isCompleted && goal.completedDate && (
|
||||
<Text style={styles.completedDate}>
|
||||
Completed {new Date(goal.completedDate).toLocaleDateString()}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
</TouchableOpacity>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
card: {
|
||||
backgroundColor: '#fff',
|
||||
borderRadius: 12,
|
||||
padding: 16,
|
||||
marginBottom: 12,
|
||||
shadowColor: '#000',
|
||||
shadowOffset: { width: 0, height: 2 },
|
||||
shadowOpacity: 0.1,
|
||||
shadowRadius: 4,
|
||||
elevation: 3,
|
||||
},
|
||||
cardCompleted: {
|
||||
backgroundColor: '#f0fdf4',
|
||||
borderColor: '#bbf7d0',
|
||||
borderWidth: 1,
|
||||
},
|
||||
header: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'flex-start',
|
||||
marginBottom: 12,
|
||||
},
|
||||
titleRow: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'flex-start',
|
||||
flex: 1,
|
||||
},
|
||||
titleContainer: {
|
||||
marginLeft: 12,
|
||||
flex: 1,
|
||||
},
|
||||
title: {
|
||||
fontSize: 18,
|
||||
fontWeight: '600',
|
||||
color: '#111827',
|
||||
marginBottom: 4,
|
||||
},
|
||||
titleCompleted: {
|
||||
color: '#9ca3af',
|
||||
textDecorationLine: 'line-through',
|
||||
},
|
||||
description: {
|
||||
fontSize: 14,
|
||||
color: '#6b7280',
|
||||
lineHeight: 20,
|
||||
},
|
||||
actions: {
|
||||
flexDirection: 'row',
|
||||
gap: 8,
|
||||
},
|
||||
actionButton: {
|
||||
padding: 4,
|
||||
},
|
||||
progressSection: {
|
||||
marginBottom: 12,
|
||||
},
|
||||
progressInfo: {
|
||||
flexDirection: 'row',
|
||||
justifyContent: 'space-between',
|
||||
marginBottom: 8,
|
||||
},
|
||||
progressText: {
|
||||
fontSize: 14,
|
||||
fontWeight: '500',
|
||||
color: '#374151',
|
||||
},
|
||||
progressPercentage: {
|
||||
fontSize: 14,
|
||||
fontWeight: '600',
|
||||
color: '#2563eb',
|
||||
},
|
||||
progressBarContainer: {
|
||||
height: 8,
|
||||
backgroundColor: '#e5e7eb',
|
||||
borderRadius: 4,
|
||||
overflow: 'hidden',
|
||||
},
|
||||
progressBar: {
|
||||
height: '100%',
|
||||
backgroundColor: '#2563eb',
|
||||
borderRadius: 4,
|
||||
},
|
||||
progressBarCompleted: {
|
||||
backgroundColor: '#10b981',
|
||||
},
|
||||
footer: {
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
priorityBadge: {
|
||||
paddingHorizontal: 8,
|
||||
paddingVertical: 4,
|
||||
borderRadius: 4,
|
||||
},
|
||||
priorityText: {
|
||||
fontSize: 10,
|
||||
fontWeight: '600',
|
||||
color: '#fff',
|
||||
},
|
||||
daysRemaining: {
|
||||
fontSize: 12,
|
||||
color: '#6b7280',
|
||||
},
|
||||
overdue: {
|
||||
color: '#ef4444',
|
||||
fontWeight: '600',
|
||||
},
|
||||
completedDate: {
|
||||
fontSize: 12,
|
||||
color: '#10b981',
|
||||
fontWeight: '500',
|
||||
},
|
||||
});
|
||||
Reference in new issue
Block a user