Files
mdb/app/app/contact/[id].tsx
2026-05-04 09:09:10 +02:00

165 lines
5.3 KiB
TypeScript

import { Link, Stack, useLocalSearchParams } from 'expo-router';
import {
ActivityIndicator,
Linking,
Pressable,
ScrollView,
Text,
View,
} from 'react-native';
import { labelContactCategorie } from '@/constants/contactCategories';
import { useContactBiens, useContactDetail } from '@/hooks/useContacts';
import { getCurrentUserId } from '@/services/pocketbase';
import { formatPocketBaseError } from '@/utils/pocketbaseErrors';
function routeParamId(raw: string | string[] | undefined): string | undefined {
if (raw == null) return undefined;
return Array.isArray(raw) ? raw[0] : raw;
}
function openTel(raw?: string | null) {
if (!raw?.trim()) return;
void Linking.openURL(`tel:${raw.replace(/\s/g, '')}`);
}
function openMail(raw?: string | null) {
if (!raw?.trim()) return;
void Linking.openURL(`mailto:${raw.trim()}`);
}
export default function ContactDetailScreen() {
const { id: rawId } = useLocalSearchParams<{ id?: string | string[] }>();
const id = routeParamId(rawId);
const uid = getCurrentUserId();
const q = useContactDetail(id);
const biensQ = useContactBiens(id);
if (!id) {
return (
<>
<Stack.Screen options={{ title: 'Contact', headerShown: true }} />
<View className="flex-1 items-center justify-center p-6">
<Text>Identifiant manquant.</Text>
</View>
</>
);
}
if (q.isPending) {
return (
<>
<Stack.Screen options={{ title: '…', headerShown: true }} />
<View className="flex-1 items-center justify-center">
<ActivityIndicator color="#1D4ED8" />
</View>
</>
);
}
if (q.error || !q.data) {
return (
<>
<Stack.Screen options={{ title: 'Erreur', headerShown: true }} />
<View className="flex-1 items-center justify-center p-6">
<Text className="text-center text-red-700">
{q.error ? formatPocketBaseError(q.error) : 'Introuvable.'}
</Text>
</View>
</>
);
}
const c = q.data;
if (uid && c.user !== uid) {
return (
<>
<Stack.Screen options={{ title: 'Contact', headerShown: true }} />
<View className="flex-1 items-center justify-center p-6">
<Text>Accès refusé.</Text>
</View>
</>
);
}
return (
<>
<Stack.Screen options={{ title: c.nom, headerShown: true }} />
<ScrollView className="flex-1 bg-slate-50 p-4">
<Text className="text-xl font-bold text-slate-900">
{c.prenom ? `${c.prenom} ` : ''}
{c.nom}
</Text>
{c.societe ? <Text className="mt-1 text-slate-600">{c.societe}</Text> : null}
<Text className="mt-3 text-sm text-slate-500">Catégorie</Text>
<Text className="text-base text-slate-900">{labelContactCategorie(c.categorie)}</Text>
{c.email ? (
<>
<Text className="mt-3 text-sm text-slate-500">Email</Text>
<Pressable onPress={() => openMail(c.email)}>
<Text className="text-base text-blue-700">{c.email}</Text>
</Pressable>
</>
) : null}
{c.telephone ? (
<>
<Text className="mt-3 text-sm text-slate-500">Téléphone</Text>
<Pressable onPress={() => openTel(c.telephone)}>
<Text className="text-base text-blue-700">{c.telephone}</Text>
</Pressable>
</>
) : null}
{c.telephone_2 ? (
<>
<Text className="mt-3 text-sm text-slate-500">Téléphone 2</Text>
<Pressable onPress={() => openTel(c.telephone_2)}>
<Text className="text-base text-blue-700">{c.telephone_2}</Text>
</Pressable>
</>
) : null}
{(c.ville || c.zone_intervention) ? (
<>
<Text className="mt-3 text-sm text-slate-500">Localisation</Text>
<Text className="text-base text-slate-900">
{[c.ville, c.zone_intervention].filter(Boolean).join(' · ')}
</Text>
</>
) : null}
{c.notes ? (
<>
<Text className="mt-5 text-lg font-bold text-slate-900">Notes</Text>
<Text className="mt-1 text-slate-800">{c.notes}</Text>
</>
) : null}
<Text className="mt-6 text-lg font-bold text-slate-900">Biens associés</Text>
{biensQ.isPending ? (
<ActivityIndicator className="mt-2" color="#1D4ED8" />
) : biensQ.error ? (
<Text className="mt-2 text-red-700">{formatPocketBaseError(biensQ.error)}</Text>
) : (biensQ.data?.length ?? 0) === 0 ? (
<Text className="mt-2 text-slate-600">Aucun bien lié (source contact).</Text>
) : (
<View className="mt-2 gap-2">
{biensQ.data!.map((b) => (
<Link key={b.id} href={`/bien/${b.id}`} asChild>
<Pressable className="rounded-xl border border-slate-200 bg-white px-3 py-3">
<Text className="font-semibold text-slate-900">
{b.titre?.trim() || `${b.ville ?? ''} (${b.type_bien ?? 'bien'})`.trim()}
</Text>
<Text className="text-sm text-slate-500">
{[b.adresse, b.code_postal, b.ville].filter(Boolean).join(', ')}
</Text>
</Pressable>
</Link>
))}
</View>
)}
</ScrollView>
</>
);
}