Files
mdb/app/components/recherche/SecteurTab.tsx
Bastien COIGNOUX 2b8741de08 recherche
2026-05-04 21:52:51 +02:00

169 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { Ionicons } from '@expo/vector-icons';
import type { ComponentProps } from 'react';
import { useEffect, useState } from 'react';
import {
ActivityIndicator,
Alert,
Keyboard,
Linking,
Pressable,
ScrollView,
Text,
TextInput,
View,
} from 'react-native';
import {
dvfSearchUrl,
meilleursAgentsUrlForVille,
SECTOR_TOOLS,
type SectorTool,
} from '@/constants/rechercheMarche';
import { UI } from '@/constants/uiTheme';
import { useAnalyseSecteurForVille, useSaveAnalyseSecteur } from '@/hooks/useAnalysesSecteur';
import { formatPocketBaseError } from '@/utils/pocketbaseErrors';
type IonName = ComponentProps<typeof Ionicons>['name'];
function resolveToolUrl(tool: SectorTool, ville: string): string {
if (tool.id === 'ma') return meilleursAgentsUrlForVille(ville);
if (tool.id === 'dvf') return dvfSearchUrl(ville);
return tool.url;
}
export function SecteurTab() {
const [ville, setVille] = useState('');
const [notes, setNotes] = useState('');
const secteurQ = useAnalyseSecteurForVille(ville);
const saveMut = useSaveAnalyseSecteur();
useEffect(() => {
if (secteurQ.data?.notes != null) setNotes(secteurQ.data.notes);
}, [secteurQ.data?.id, secteurQ.data?.notes]);
const onOpenTool = async (tool: SectorTool) => {
const url = resolveToolUrl(tool, ville);
const ok = await Linking.canOpenURL(url);
if (!ok) {
Alert.alert('Lien', 'Impossible douvrir ce lien sur cet appareil.');
return;
}
void Linking.openURL(url);
};
const onAnalyser = () => {
const v = ville.trim();
if (!v) {
Alert.alert('Ville', 'Indique une ville ou une commune pour cadrer lanalyse.');
return;
}
Keyboard.dismiss();
Alert.alert(
'Secteur',
`Analyse pour « ${v} » : utilise les outils ci-dessous (données externes), puis consigne tes notes en bas de page.`,
);
};
const onSaveNotes = async () => {
const v = ville.trim();
if (!v) {
Alert.alert('Ville', 'Renseigne la ville avant de sauvegarder les notes.');
return;
}
try {
await saveMut.mutateAsync({ ville: v, notes });
} catch (e) {
Alert.alert('Erreur', formatPocketBaseError(e));
}
};
return (
<ScrollView className="flex-1 px-3 pt-2" contentContainerStyle={{ paddingBottom: 120 }} keyboardShouldPersistTaps="handled">
<Text className="text-base font-semibold" style={{ color: UI.text }}>
Ville / commune
</Text>
<TextInput
className="mt-2 rounded-2xl border-2 px-4 text-lg"
style={{ borderColor: UI.border, color: UI.text, minHeight: 52, backgroundColor: UI.card }}
placeholder="Ex. Lyon 3e, Bordeaux…"
placeholderTextColor={UI.textMuted}
value={ville}
onChangeText={setVille}
onSubmitEditing={onAnalyser}
/>
<Pressable
accessibilityRole="button"
onPress={onAnalyser}
className="mt-3 min-h-[52px] items-center justify-center rounded-2xl active:opacity-90"
style={{ backgroundColor: UI.primary }}
>
<Text className="text-lg font-bold text-white">Analyser</Text>
</Pressable>
<Text className="mb-2 mt-8 text-xl font-bold" style={{ color: UI.text }}>
Outils marché
</Text>
<Text className="mb-3 text-base" style={{ color: UI.textMuted }}>
Données externes ouverture dans le navigateur.
</Text>
{SECTOR_TOOLS.map((tool) => (
<Pressable
key={tool.id}
accessibilityRole="button"
onPress={() => void onOpenTool(tool)}
className="mb-3 flex-row items-center rounded-2xl border-2 bg-white p-4 active:opacity-90"
style={{ borderColor: UI.border }}
>
<View
className="mr-3 h-12 w-12 items-center justify-center rounded-xl"
style={{ backgroundColor: '#EFF6FF' }}
>
<Ionicons name={tool.icon as IonName} size={24} color={UI.primary} />
</View>
<View className="min-w-0 flex-1">
<Text className="text-lg font-bold" style={{ color: UI.text }}>
{tool.title}
</Text>
<Text className="mt-1 text-base leading-5" style={{ color: UI.textMuted }}>
{tool.description}
</Text>
<View className="mt-2 self-start rounded-full px-2 py-1" style={{ backgroundColor: '#E0E7FF' }}>
<Text className="text-xs font-bold" style={{ color: UI.primary }}>
Externe
</Text>
</View>
</View>
</Pressable>
))}
<Text className="mb-2 mt-4 text-xl font-bold" style={{ color: UI.text }}>
Notes secteur
</Text>
{secteurQ.isFetching ? <ActivityIndicator color={UI.primary} className="mb-2" /> : null}
<TextInput
className="min-h-[140px] rounded-2xl border-2 px-4 py-3 text-lg"
style={{ borderColor: UI.border, color: UI.text, backgroundColor: UI.card }}
multiline
textAlignVertical="top"
placeholder="Synthèse prix, tension, typologie…"
placeholderTextColor={UI.textMuted}
value={notes}
onChangeText={setNotes}
/>
<Pressable
accessibilityRole="button"
onPress={() => void onSaveNotes()}
disabled={saveMut.isPending}
className="mt-3 min-h-[52px] items-center justify-center rounded-2xl active:opacity-90"
style={{ backgroundColor: UI.success }}
>
{saveMut.isPending ? (
<ActivityIndicator color="#fff" />
) : (
<Text className="text-lg font-bold text-white">Sauvegarder</Text>
)}
</Pressable>
</ScrollView>
);
}