Restructure with Turborepo
Run Tests on Branches / Backend Tests (push) Successful in 3m38s
Run Tests on Branches / Frontend Tests (push) Successful in 3m28s

This commit is contained in:
Antoni Nuñez Romeu
2026-07-06 15:51:53 +02:00
parent f66cafbbc3
commit 190b3d163d
277 changed files with 53253 additions and 0 deletions
+205
View File
@@ -0,0 +1,205 @@
import React, { useEffect, useState } from 'react';
import { View, Text, ScrollView, StyleSheet, TouchableOpacity } from 'react-native';
import { useLocalSearchParams, useRouter } from 'expo-router';
import { getMedicine, getMedicinePharmacies } from '../../services/medicines';
import { StockBadge } from '../../components/StockBadge';
import { LoadingSpinner } from '../../components/LoadingSpinner';
import { colors, spacing, borderRadius } from '../../constants/theme';
import { Medicine, PharmacyMedicine } from '../../types';
export default function MedicineDetailScreen() {
const { id } = useLocalSearchParams<{ id: string }>();
const router = useRouter();
const [medicine, setMedicine] = useState<Medicine | null>(null);
const [pharmacies, setPharmacies] = useState<PharmacyMedicine[]>([]);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
if (!id) return;
const fetchMedicine = async () => {
try {
const [medData, pharmData] = await Promise.all([
getMedicine(id),
getMedicinePharmacies(id),
]);
setMedicine(medData);
setPharmacies(pharmData);
} catch (error) {
console.error('Error fetching medicine:', error);
} finally {
setIsLoading(false);
}
};
fetchMedicine();
}, [id]);
if (isLoading) {
return <LoadingSpinner message="Cargando medicamento..." />;
}
if (!medicine) {
return (
<View style={styles.errorContainer}>
<Text style={styles.errorText}>Medicamento no encontrado</Text>
</View>
);
}
return (
<ScrollView style={styles.container}>
<View style={styles.header}>
<Text style={styles.name}>{medicine.nombre}</Text>
<StockBadge stock={medicine.stock} />
</View>
<View style={styles.infoSection}>
<InfoRow label="Principio activo" value={medicine.principioActivo} />
<InfoRow label="Laboratorio" value={medicine.laboratorio} />
<InfoRow label="Forma farmacéutica" value={medicine.formaFarmaceutica} />
<InfoRow
label="Precio"
value={medicine.precio ? `${medicine.precio.toFixed(2)}` : 'No disponible'}
/>
<InfoRow label="Registro" value={medicine.nregistro} />
</View>
<View style={styles.pharmaciesSection}>
<Text style={styles.sectionTitle}>
Farmacias ({pharmacies.length})
</Text>
{pharmacies.length === 0 ? (
<Text style={styles.noPharmacies}>No hay farmacias con este medicamento</Text>
) : (
pharmacies.map((pharm) => (
<TouchableOpacity
key={pharm.id}
style={styles.pharmacyCard}
onPress={() => router.push(`/pharmacy/${pharm.pharmacy_id}`)}
>
<View style={styles.pharmacyInfo}>
<Text style={styles.pharmacyName}>{pharm.pharmacy?.name}</Text>
<Text style={styles.pharmacyAddress}>{pharm.pharmacy?.address}</Text>
</View>
<View style={styles.pharmacyStock}>
<Text style={styles.price}>{pharm.price.toFixed(2)} </Text>
<Text style={styles.stock}>Stock: {pharm.stock}</Text>
</View>
</TouchableOpacity>
))
)}
</View>
</ScrollView>
);
}
function InfoRow({ label, value }: { label: string; value: string }) {
return (
<View style={styles.infoRow}>
<Text style={styles.infoLabel}>{label}</Text>
<Text style={styles.infoValue}>{value}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background,
},
header: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'flex-start',
padding: spacing.md,
backgroundColor: colors.card,
},
name: {
flex: 1,
fontSize: 22,
fontWeight: 'bold',
color: colors.text,
marginRight: spacing.sm,
},
infoSection: {
backgroundColor: colors.card,
marginTop: spacing.sm,
padding: spacing.md,
},
infoRow: {
flexDirection: 'row',
justifyContent: 'space-between',
paddingVertical: spacing.sm,
borderBottomWidth: 1,
borderBottomColor: colors.separator,
},
infoLabel: {
fontSize: 14,
color: colors.textSecondary,
},
infoValue: {
fontSize: 14,
color: colors.text,
fontWeight: '500',
},
pharmaciesSection: {
marginTop: spacing.sm,
padding: spacing.md,
},
sectionTitle: {
fontSize: 18,
fontWeight: '600',
color: colors.text,
marginBottom: spacing.md,
},
noPharmacies: {
color: colors.textSecondary,
textAlign: 'center',
padding: spacing.xl,
},
pharmacyCard: {
flexDirection: 'row',
justifyContent: 'space-between',
backgroundColor: colors.card,
borderRadius: borderRadius.md,
padding: spacing.md,
marginBottom: spacing.sm,
},
pharmacyInfo: {
flex: 1,
},
pharmacyName: {
fontSize: 16,
fontWeight: '600',
color: colors.text,
},
pharmacyAddress: {
fontSize: 14,
color: colors.textSecondary,
marginTop: spacing.xs,
},
pharmacyStock: {
alignItems: 'flex-end',
},
price: {
fontSize: 16,
fontWeight: '600',
color: colors.primary,
},
stock: {
fontSize: 12,
color: colors.textSecondary,
marginTop: spacing.xs,
},
errorContainer: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
errorText: {
fontSize: 16,
color: colors.textSecondary,
},
});