* Extended functionalities
* Added different UIs
This commit is contained in:
198
meal-plan-frontend-mobile/app/(main)/(tabs)/index.tsx
Normal file
198
meal-plan-frontend-mobile/app/(main)/(tabs)/index.tsx
Normal file
@@ -0,0 +1,198 @@
|
||||
import { router } from 'expo-router';
|
||||
import { ActivityIndicator, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { fetchCategories } from '@/src/api/recipes.api';
|
||||
import { ErrorView } from '@/src/components/ErrorView';
|
||||
import { Screen } from '@/src/components/Screen';
|
||||
import { useAuth } from '@/src/context/AuthContext';
|
||||
import { useAppearance } from '@/src/context/AppearanceContext';
|
||||
import { MealCategory } from '@/src/types/recipe';
|
||||
import { categoryLabel } from '@/src/utils/recipe';
|
||||
|
||||
const CATEGORY_EMOJI: Record<MealCategory, string> = {
|
||||
[MealCategory.Breakfast]: '🍳',
|
||||
[MealCategory.SecondBreakfast]: '🥐',
|
||||
[MealCategory.Lunch]: '🍲',
|
||||
[MealCategory.Dinner]: '🌙',
|
||||
};
|
||||
|
||||
export default function DashboardScreen() {
|
||||
const { t } = useTranslation();
|
||||
const { user } = useAuth();
|
||||
const { colors } = useAppearance();
|
||||
|
||||
const categoriesQuery = useQuery({
|
||||
queryKey: ['categories'],
|
||||
queryFn: fetchCategories,
|
||||
});
|
||||
|
||||
const welcome = user?.userName
|
||||
? t('dashboard.welcomeNamed', { name: user.userName })
|
||||
: t('dashboard.welcome');
|
||||
|
||||
return (
|
||||
<Screen title={welcome} subtitle={t('dashboard.subtitle')}>
|
||||
{categoriesQuery.isLoading ? (
|
||||
<View style={styles.loading}>
|
||||
<ActivityIndicator size="large" color={colors.brand} />
|
||||
</View>
|
||||
) : categoriesQuery.isError ? (
|
||||
<ErrorView
|
||||
message={t('errors.loadCategoriesFailed')}
|
||||
onRetry={() => categoriesQuery.refetch()}
|
||||
/>
|
||||
) : (
|
||||
<ScrollView contentContainerStyle={styles.list}>
|
||||
<Pressable
|
||||
onPress={() => router.push('/(main)/(tabs)/meals')}
|
||||
style={({ pressed }) => [
|
||||
styles.allCard,
|
||||
{
|
||||
backgroundColor: colors.brand,
|
||||
opacity: pressed ? 0.9 : 1,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Text style={styles.allTitle}>{t('nav.allMeals')}</Text>
|
||||
<Text style={styles.allSubtitle}>{t('dashboard.viewRecipes')}</Text>
|
||||
</Pressable>
|
||||
|
||||
<Pressable
|
||||
onPress={() => router.push('/(main)/ingredients')}
|
||||
style={({ pressed }) => [
|
||||
styles.card,
|
||||
{
|
||||
backgroundColor: colors.surface,
|
||||
borderColor: colors.border,
|
||||
opacity: pressed ? 0.9 : 1,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Text style={styles.emoji}>🥬</Text>
|
||||
<View style={styles.cardBody}>
|
||||
<Text style={[styles.cardTitle, { color: colors.text }]}>{t('nav.ingredientCatalog')}</Text>
|
||||
<Text style={[styles.cardCount, { color: colors.textMuted }]}>
|
||||
{t('ingredientCatalog.subtitle')}
|
||||
</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
|
||||
<Pressable
|
||||
onPress={() => router.push('/(main)/diet-generator')}
|
||||
style={({ pressed }) => [
|
||||
styles.card,
|
||||
{
|
||||
backgroundColor: colors.surface,
|
||||
borderColor: colors.border,
|
||||
opacity: pressed ? 0.9 : 1,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Text style={styles.emoji}>✨</Text>
|
||||
<View style={styles.cardBody}>
|
||||
<Text style={[styles.cardTitle, { color: colors.text }]}>{t('nav.dietGenerator')}</Text>
|
||||
<Text style={[styles.cardCount, { color: colors.textMuted }]}>
|
||||
{t('generators.diet.subtitle')}
|
||||
</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
|
||||
<Pressable
|
||||
onPress={() => router.push('/(main)/recipe-generator')}
|
||||
style={({ pressed }) => [
|
||||
styles.card,
|
||||
{
|
||||
backgroundColor: colors.surface,
|
||||
borderColor: colors.border,
|
||||
opacity: pressed ? 0.9 : 1,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Text style={styles.emoji}>🤖</Text>
|
||||
<View style={styles.cardBody}>
|
||||
<Text style={[styles.cardTitle, { color: colors.text }]}>{t('nav.recipeGenerator')}</Text>
|
||||
<Text style={[styles.cardCount, { color: colors.textMuted }]}>
|
||||
{t('generators.recipe.subtitle')}
|
||||
</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
|
||||
{(categoriesQuery.data ?? []).map((item) => (
|
||||
<Pressable
|
||||
key={item.category}
|
||||
onPress={() => router.push(`/(main)/category/${item.category}`)}
|
||||
style={({ pressed }) => [
|
||||
styles.card,
|
||||
{
|
||||
backgroundColor: colors.surface,
|
||||
borderColor: colors.border,
|
||||
opacity: pressed ? 0.9 : 1,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Text style={styles.emoji}>{CATEGORY_EMOJI[item.category as MealCategory] ?? '🍽️'}</Text>
|
||||
<View style={styles.cardBody}>
|
||||
<Text style={[styles.cardTitle, { color: colors.text }]}>
|
||||
{categoryLabel(item.category, t)}
|
||||
</Text>
|
||||
<Text style={[styles.cardCount, { color: colors.textMuted }]}>
|
||||
{t('common.recipeCount', { count: item.count })}
|
||||
</Text>
|
||||
</View>
|
||||
</Pressable>
|
||||
))}
|
||||
</ScrollView>
|
||||
)}
|
||||
</Screen>
|
||||
);
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
list: {
|
||||
gap: 12,
|
||||
paddingBottom: 24,
|
||||
},
|
||||
allCard: {
|
||||
borderRadius: 16,
|
||||
padding: 18,
|
||||
gap: 4,
|
||||
},
|
||||
allTitle: {
|
||||
color: '#fff',
|
||||
fontSize: 20,
|
||||
fontWeight: '700',
|
||||
},
|
||||
allSubtitle: {
|
||||
color: 'rgba(255,255,255,0.9)',
|
||||
fontSize: 14,
|
||||
},
|
||||
card: {
|
||||
borderWidth: 1,
|
||||
borderRadius: 16,
|
||||
padding: 16,
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
gap: 14,
|
||||
},
|
||||
emoji: {
|
||||
fontSize: 28,
|
||||
},
|
||||
cardBody: {
|
||||
flex: 1,
|
||||
gap: 2,
|
||||
},
|
||||
cardTitle: {
|
||||
fontSize: 18,
|
||||
fontWeight: '600',
|
||||
},
|
||||
cardCount: {
|
||||
fontSize: 14,
|
||||
},
|
||||
loading: {
|
||||
flex: 1,
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingVertical: 40,
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user