199 lines
6.0 KiB
TypeScript
199 lines
6.0 KiB
TypeScript
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,
|
|
},
|
|
});
|