Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/(main)/(authenticated)/create-trip/_layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ export default function CreateTripLayout() {
}}
/>
<Stack.Screen name={Modals.TypicalDishes} options={formSheetOptions} />
<Stack.Screen name={Modals.DishDetails} options={formSheetOptions} />
</Stack>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { DishDetailsModalPage } from '@/features/trips/pages';

const DishDetailsModal = () => {
return <DishDetailsModalPage />;
};

export default DishDetailsModal;
1 change: 1 addition & 0 deletions convex/validators/Trips.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ export const TypicalDish = v.object({
ingredients: v.array(v.string()),
isGlutenFree: v.boolean(),
isVegetarian: v.boolean(),
isVegan: v.boolean(),
Comment thread
timothyrusso marked this conversation as resolved.
});

export const Food = v.object({
Expand Down
7 changes: 5 additions & 2 deletions features/core/navigation/data/services/NavigationService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,11 @@ export class NavigationService implements INavigationService {
toChangeLanguage() {
this.client.push(`/${Stacks.Profile}/${Routes.ChangeLanguage}`);
}
toTypicalDishesModal({ tripId }: { tripId: string }) {
this.client.push({ pathname: `/${Stacks.CreateTrip}/${Modals.TypicalDishes}`, params: { tripId } });
toTypicalDishesModal(params: { tripId: string }) {
this.client.push({ pathname: `/${Stacks.CreateTrip}/${Modals.TypicalDishes}`, params });
}
toDishDetailsModal(params: { tripId: string; searchTerm: string }) {
this.client.push({ pathname: `/${Stacks.CreateTrip}/${Modals.DishDetails}`, params });
}

back() {
Expand Down
1 change: 1 addition & 0 deletions features/core/navigation/domain/entities/Modals.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const Modals = {
TypicalDishes: 'typical-dishes-modal',
DishDetails: 'dish-details-modal',
} as const;

export type Modals = (typeof Modals)[keyof typeof Modals];
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export interface INavigationService {
toTripList(): void;
toChangeLanguage(): void;
toTypicalDishesModal(params: { tripId: string }): void;
toDishDetailsModal(params: { tripId: string; searchTerm: string }): void;

back(): void;

Expand Down
7 changes: 6 additions & 1 deletion features/core/translations/libraries/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,15 @@
"DISHES_one": "dish",
"DISHES_other": "dishes",
"TYPICAL_DISHES": "Typical Dishes",
"INGREDIENTS": "INGREDIENTS",
"GLUTEN_FREE": "GLUTEN-FREE",
"VEGAN": "VEGAN",
"VEGETARIAN": "VEGETARIAN",
"PROFILE": "Profile",
"MY_TRIPS": "My Trips",
"LOGO": "HolidAI",
"UPCOMING_TRIP": "Upcoming trip:"
"UPCOMING_TRIP": "Upcoming trip:",
"MORE_DETAILS": "More details"
},
"ACTIVITY_DETAILS": {
"USEFUL_TIPS": "Useful Tips",
Expand Down
7 changes: 6 additions & 1 deletion features/core/translations/libraries/locales/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,15 @@
"DISHES_one": "piatto",
"DISHES_other": "piatti",
"TYPICAL_DISHES": "Piatti tipici",
"INGREDIENTS": "INGREDIENTI",
"GLUTEN_FREE": "SENZA GLUTINE",
"VEGAN": "VEGANO",
"VEGETARIAN": "VEGETARIANO",
"PROFILE": "Profilo",
"MY_TRIPS": "Viaggi",
"LOGO": "HolidAI",
"UPCOMING_TRIP": "Prossimo viaggio:"
"UPCOMING_TRIP": "Prossimo viaggio:",
"MORE_DETAILS": "Più dettagli"
},
"ACTIVITY_DETAILS": {
"USEFUL_TIPS": "Consigli utili",
Expand Down
Binary file added features/core/ui/assets/images/gluten_free.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added features/core/ui/assets/images/vegan.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added features/core/ui/assets/images/vegetarian.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 4 additions & 2 deletions features/core/ui/components/basic/Cheap/Cheap.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,16 @@ type CheapProps = {
title: string;
color: string;
icon?: IoniconsName;
uppercase?: boolean;
};

export const Cheap: FC<CheapProps> = ({ title, color, icon }) => {
export const Cheap: FC<CheapProps> = ({ title, color, icon, uppercase = true }) => {
const styles = cheapStyles(color);

return (
<View style={styles.container}>
{icon && <CustomIcon name={icon} size={spacing.Triple} color={colors.primaryBlack} />}
<CustomText text={title.toUpperCase()} style={styles.title} />
<CustomText text={uppercase ? title.toUpperCase() : title} style={styles.title} />
</View>
);
};
47 changes: 47 additions & 0 deletions features/core/ui/components/composite/Badge/Badge.style.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { colors } from '@/features/core/ui/style/colors';
import { components } from '@/features/core/ui/style/dimensions/components';
import { fontSize } from '@/features/core/ui/style/dimensions/fontSize';
import { spacing } from '@/features/core/ui/style/dimensions/spacing';
import { fontFamily } from '@/features/core/ui/style/fontFamily';
import { StyleSheet } from 'react-native';

export const styles = (active: boolean, backgroundColor: string) =>
StyleSheet.create({
container: {
alignItems: 'center',
gap: spacing.Single,
},
circleWrapper: {
width: components.badgeCircleSize,
height: components.badgeCircleSize,
},
circle: {
width: components.badgeCircleSize,
height: components.badgeCircleSize,
borderRadius: components.badgeCircleSize / 2,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: active ? backgroundColor : colors.primaryRed,
},
checkBadge: {
position: 'absolute',
bottom: 0,
right: 0,
width: components.badgeCheckSize,
height: components.badgeCheckSize,
borderRadius: components.badgeCheckSize / 2,
backgroundColor: colors.primaryWhite,
alignItems: 'center',
justifyContent: 'center',
},
badgeImage: {
width: components.badgeCircleSize * 0.6,
height: components.badgeCircleSize * 0.6,
},
label: {
fontSize: fontSize.XS,
fontFamily: fontFamily.interBold,
color: colors.primaryBlack,
textAlign: 'center',
},
});
Comment thread
timothyrusso marked this conversation as resolved.
39 changes: 39 additions & 0 deletions features/core/ui/components/composite/Badge/Badge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { CustomIcon } from '@/features/core/ui/components/basic/CustomIcon/CustomIcon';
import { CustomImage } from '@/features/core/ui/components/basic/CustomImage/CustomImage';
import { CustomText } from '@/features/core/ui/components/basic/CustomText/CustomText';
import { styles as badgeStyle } from '@/features/core/ui/components/composite/Badge/Badge.style';
import { colors } from '@/features/core/ui/style/colors';
import { spacing } from '@/features/core/ui/style/dimensions/spacing';
import { icons } from '@/features/core/ui/style/icons';
import type { FC } from 'react';
import type { ImageSourcePropType } from 'react-native';
import { View } from 'react-native';

type BadgeProps = {
label: string;
image: ImageSourcePropType;
backgroundColor: string;
active: boolean;
};

export const Badge: FC<BadgeProps> = ({ label, image, backgroundColor, active }) => {
const styles = badgeStyle(active, backgroundColor);

return (
<View style={styles.container}>
<View style={styles.circleWrapper}>
<View style={styles.circle}>
<CustomImage source={image} style={styles.badgeImage} accessibilityLabel={label} />
</View>
<View style={styles.checkBadge}>
<CustomIcon
name={active ? icons.success : icons.closeCircle}
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
size={spacing.TripleAndHalf}
color={active ? colors.tertiaryGreen : colors.primaryRed}
/>
</View>
</View>
<CustomText text={label.toLocaleUpperCase()} style={styles.label} />
</View>
);
};
Comment thread
timothyrusso marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { fontSize } from '@/features/core/ui/style/dimensions/fontSize';
import { spacing } from '@/features/core/ui/style/dimensions/spacing';
import { fontFamily } from '@/features/core/ui/style/fontFamily';
import { StyleSheet } from 'react-native';

export const styles = StyleSheet.create({
container: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
paddingBottom: spacing.Double,
marginBottom: spacing.Fourfold,
},
title: {
fontFamily: fontFamily.interExtraBold,
fontSize: fontSize.XL2,
maxWidth: '90%',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ButtonType } from '@/features/core/ui/components/basic/CustomButton/CustomButton.logic';
import { CustomIconButtonMedium } from '@/features/core/ui/components/basic/CustomIconButton/CustomIconButtonMedium';
import { CustomText } from '@/features/core/ui/components/basic/CustomText/CustomText';
import { styles } from '@/features/core/ui/components/composite/BottomSheetHeader/BottomSheetHeader.style';
import { icons } from '@/features/core/ui/style/icons';
import type { FC } from 'react';
import { View } from 'react-native';

type BottomSheetHeaderProps = {
title: string;
onClose: () => void;
};

export const BottomSheetHeader: FC<BottomSheetHeaderProps> = ({ title, onClose }) => (
<View style={styles.container}>
<CustomText text={title} style={styles.title} numberOfLines={2} ellipsizeMode="tail" />
<CustomIconButtonMedium iconName={icons.close} buttonType={ButtonType.Quaternary} onPress={onClose} />
</View>
);
2 changes: 2 additions & 0 deletions features/core/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ export * from '@/features/core/ui/components/basic/LinearGradientText/LinearGrad
export * from '@/features/core/ui/components/basic/LottieAnimation/LottieAnimation';

// Composite components
export * from '@/features/core/ui/components/composite/Badge/Badge';
export * from '@/features/core/ui/components/composite/BottomSheetHeader/BottomSheetHeader';
export * from '@/features/core/ui/components/composite/AnimatedHeaderImage/AnimatedHeaderImage';
export * from '@/features/core/ui/components/composite/CardWithImage/CardWithImage';
export * from '@/features/core/ui/components/composite/CustomHeader/CustomHeader';
Expand Down
1 change: 1 addition & 0 deletions features/core/ui/style/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ export const colors = {
tertiaryBlue: '#63b3ff',
primaryRed: '#FFD9D9',
secondaryGreen: '#8AFF8A',
tertiaryGreen: '#57C17B',
} as const;
2 changes: 2 additions & 0 deletions features/core/ui/style/dimensions/components.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export const components = {
buttonNumberHeight: 30,
animatedWordsHeight: 30,
carouselImageSize: 200,
badgeCircleSize: 80,
badgeCheckSize: 22,
welcomeCardHeightSmall: 130,
welcomeCardHeightMedium: 180,
welcomeCardHeightLarge: 230,
Expand Down
1 change: 1 addition & 0 deletions features/core/ui/style/dimensions/images.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export const images = {
logoRoundHeight: 80,
logoRoundWidth: 80,
dishImageSize: 80,
dishFullImageSize: 110,
};
2 changes: 2 additions & 0 deletions features/core/ui/style/icons.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@ import type { Ionicons } from '@expo/vector-icons';

export const icons: Record<string, keyof typeof Ionicons.glyphMap> = {
close: 'close',
closeCircle: 'close-circle',
location: 'location',
locationOutline: 'location-outline',
globeOutline: 'globe-outline',
checkmark: 'checkmark',
personCircleOutline: 'person-circle-outline',
arrowBack: 'arrow-back',
arrowRight: 'chevron-forward',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ export const generatedTripSchema = z.object({
ingredients: z.array(z.string()).describe('Main ingredients of the dish.'),
isGlutenFree: z.boolean().describe('Whether the dish is gluten free.'),
isVegetarian: z.boolean().describe('Whether the dish is vegetarian.'),
isVegan: z.boolean().describe('Whether the dish is vegan.'),
}),
),
}),
Expand Down
1 change: 1 addition & 0 deletions features/trips/domain/entities/TypicalDish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ export interface TypicalDish {
ingredients: string[];
isGlutenFree: boolean;
isVegetarian: boolean;
isVegan: boolean;
}
1 change: 1 addition & 0 deletions features/trips/pages.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
export { ActivityDetailsPage } from '@/features/trips/ui/pages/ActivityDetailsPage/ActivityDetailsPage';
export { DishDetailsModalPage } from '@/features/trips/ui/pages/DishDetailsModalPage/DishDetailsModalPage';
export { TripDetailsPage } from '@/features/trips/ui/pages/TripDetailsPage/TripDetailsPage';
export { TripListPage } from '@/features/trips/ui/pages/TripListPage/TripListPage';
export { TypicalDishesModalPage } from '@/features/trips/ui/pages/TypicalDishesModalPage/TypicalDishesModalPage';
Expand Down
29 changes: 28 additions & 1 deletion features/trips/ui/components/FoodCard/FoodCard.style.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { colors, fontFamily, fontSize, spacing } from '@/features/core/ui';
import { colors, fontFamily, fontSize, opacity, spacing } from '@/features/core/ui';
import { StyleSheet } from 'react-native';

export const styles = StyleSheet.create({
Expand Down Expand Up @@ -56,4 +56,31 @@ export const styles = StyleSheet.create({
marginTop: spacing.Double,
paddingHorizontal: spacing.Double,
},
titleContainer: {
flexDirection: 'row',
alignItems: 'center',
gap: spacing.Single,
},
typicalDishesBox: {
padding: spacing.Double,
backgroundColor: colors.secondaryGrey,
borderRadius: spacing.Double,
marginTop: spacing.Double,
marginHorizontal: spacing.Double,
},
pressed: {
opacity: opacity.default,
},
boxText: {
fontFamily: fontFamily.interMedium,
color: colors.primaryBlack,
},
boxButton: {
marginTop: spacing.Single,
fontFamily: fontFamily.interBold,
alignSelf: 'flex-end',
backgroundColor: colors.primaryWhite,
padding: spacing.Single,
borderRadius: spacing.Double,
},
});
17 changes: 12 additions & 5 deletions features/trips/ui/components/FoodCard/FoodCard.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { CustomButtonMedium, CustomIcon, CustomText, colors, icons, spacing } from '@/features/core/ui';
import { CustomIcon, CustomText, colors, icons, spacing } from '@/features/core/ui';
import type { Food } from '@/features/trips/domain/entities/Food';
import { useFoodCardLogic } from '@/features/trips/ui/components/FoodCard/FoodCard.logic';
import { styles } from '@/features/trips/ui/components/FoodCard/FoodCard.style';
import { LinearGradient } from 'expo-linear-gradient';
import type { FC } from 'react';
import { View } from 'react-native';
import { Pressable, View } from 'react-native';

type FoodCardProps = {
food: Food;
Expand Down Expand Up @@ -36,9 +36,16 @@ export const FoodCard: FC<FoodCardProps> = ({ food, tripId }) => {
<CustomText text="MY_TRIP.GENERAL_NOTES" style={styles.subtitle} />
</View>
<CustomText text={food.foodGeneralNotes} style={styles.contentValue} />
<View style={styles.buttonContainer}>
<CustomButtonMedium title="MY_TRIP.TYPICAL_DISHES" onPress={handleOpenModal} style={styles.button} />
</View>
<Pressable
style={({ pressed }) => [styles.typicalDishesBox, pressed && styles.pressed]}
onPress={handleOpenModal}
>
<View style={styles.titleContainer}>
<CustomIcon name={icons.cafe} size={spacing.Triple} color={colors.secondaryGreen} />
<CustomText text="MY_TRIP.TYPICAL_DISHES" style={styles.boxText} />
</View>
<CustomText text="MY_TRIP.MORE_DETAILS" style={styles.boxButton} />
</Pressable>
</View>
</View>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,26 @@
import { useGetWikimediaDishImage } from '@/features/core/images';
import type { TypicalDish } from '@/features/trips';

export const useDishItemLogic = (searchTerm: string) => {
const { data, isLoading } = useGetWikimediaDishImage(searchTerm);
return { image: data?.url, isLoading };
const glutenFreeImage = require('@/features/core/ui/assets/images/gluten_free.png');
Comment thread
timothyrusso marked this conversation as resolved.
const veganImage = require('@/features/core/ui/assets/images/vegan.png');
const vegetarianImage = require('@/features/core/ui/assets/images/vegetarian.png');

export const useDishItemLogic = (dish: TypicalDish) => {
const { data, isLoading } = useGetWikimediaDishImage(dish.searchTerm);
const hasBadge = dish.isGlutenFree || dish.isVegan || dish.isVegetarian;

return {
image: data?.url,
isLoading,
glutenFreeImage,
veganImage,
vegetarianImage,
hasBadge,
isGlutenFree: dish.isGlutenFree,
isVegan: dish.isVegan,
isVegetarian: dish.isVegetarian,
glutenFreeLabel: 'MY_TRIP.GLUTEN_FREE',
veganLabel: 'MY_TRIP.VEGAN',
vegetarianLabel: 'MY_TRIP.VEGETARIAN',
};
};
Loading
Loading