fix: add i18n support for parapharmacy products and notification bell
Run Tests on Branches / Detect Changes (push) Successful in 12s
Run Tests on Branches / Backend Tests (push) Has been skipped
Run Tests on Branches / Frontend Tests (push) Successful in 1m37s
Run Tests on Branches / Frontend Mobile Tests (push) Successful in 1m52s
Run Tests on Branches / Parapharmacy API Tests (push) Has been skipped
Run Tests on Branches / PIP Platform Tests (push) Has been skipped

- Mobile: Replace hardcoded Spanish in map.tsx with t() calls
- Mobile: Add notification bell to parapharmacy product detail screen
- Mobile: Add map/product i18n keys to es.js and ca.js locales
- Web: Add useTranslation to ProductView.jsx, replace all hardcoded strings
- Web: Add 24 productView.* translation keys to es.js and ca.js locales
- Fix 'Disponible en X farmacias' to respect language setting
This commit is contained in:
Antoni Nuñez Romeu
2026-07-17 13:04:04 +02:00
parent 8a7ef81964
commit 854de92be5
7 changed files with 153 additions and 31 deletions
@@ -1,7 +1,10 @@
import React, { useEffect, useState } from 'react';
import { View, Text, ScrollView, StyleSheet, Image, ActivityIndicator } from 'react-native';
import { View, Text, ScrollView, StyleSheet, Image, TouchableOpacity } from 'react-native';
import { useLocalSearchParams } from 'expo-router';
import { Ionicons } from '@expo/vector-icons';
import { getProduct, Product } from '../../../services/products';
import { subscribeToMedicine, unsubscribeFromMedicine } from '../../../services/notifications';
import { useAuth } from '../../../hooks/useAuth';
import { LoadingSpinner } from '../../../components/LoadingSpinner';
import { useThemeContext } from '../../../components/ThemeProvider';
import { spacing, borderRadius } from '../../../constants/theme';
@@ -11,9 +14,12 @@ export default function ProductDetailScreen() {
const { source, id } = useLocalSearchParams<{ source: string; id: string }>();
const { colors } = useThemeContext();
const { t } = useTranslation();
const { isAuthenticated } = useAuth();
const [product, setProduct] = useState<Product | null>(null);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(false);
const [isSubscribed, setIsSubscribed] = useState(false);
const [togglingSub, setTogglingSub] = useState(false);
useEffect(() => {
if (!source || !id) return;
@@ -36,6 +42,25 @@ export default function ProductDetailScreen() {
fetchProduct();
}, [source, id]);
const handleToggleSubscription = async () => {
if (!isAuthenticated || !id) return;
setTogglingSub(true);
const productId = product?._id || product?.id || id;
try {
if (isSubscribed) {
await unsubscribeFromMedicine(productId);
setIsSubscribed(false);
} else {
await subscribeToMedicine(productId, product?.name || null);
setIsSubscribed(true);
}
} catch {
// silently fail
} finally {
setTogglingSub(false);
}
};
if (isLoading) {
return <LoadingSpinner message={t('product.loading')} />;
}
@@ -63,8 +88,23 @@ export default function ProductDetailScreen() {
<View style={[styles.header, { backgroundColor: colors.card }]}>
<View style={styles.nameRow}>
<Text style={[styles.name, { color: colors.text }]}>{product.name}</Text>
<View style={[styles.badge, { backgroundColor: isCima ? '#2b5bb5' : '#4caf50' }]}>
<Text style={styles.badgeText}>{isCima ? 'CIMA' : t('product.parapharmacy')}</Text>
<View style={styles.headerRight}>
<View style={[styles.badge, { backgroundColor: isCima ? '#2b5bb5' : '#4caf50' }]}>
<Text style={styles.badgeText}>{isCima ? 'CIMA' : t('product.parapharmacy')}</Text>
</View>
{isAuthenticated && (
<TouchableOpacity
style={[styles.bellButton, { backgroundColor: isSubscribed ? colors.primary : colors.surfaceVariant }]}
onPress={handleToggleSubscription}
disabled={togglingSub}
>
<Ionicons
name={isSubscribed ? 'notifications' : 'notifications-outline'}
size={20}
color={isSubscribed ? '#fff' : colors.textSecondary}
/>
</TouchableOpacity>
)}
</View>
</View>
{product.brand ? (
@@ -171,6 +211,18 @@ const styles = StyleSheet.create({
fontSize: 22,
fontWeight: 'bold',
},
headerRight: {
flexDirection: 'row',
alignItems: 'center',
gap: spacing.sm,
},
bellButton: {
width: 36,
height: 36,
borderRadius: 18,
alignItems: 'center',
justifyContent: 'center',
},
badge: {
paddingHorizontal: spacing.sm,
paddingVertical: spacing.xs,