recherche
This commit is contained in:
168
app/components/recherche/SecteurTab.tsx
Normal file
168
app/components/recherche/SecteurTab.tsx
Normal file
@ -0,0 +1,168 @@
|
||||
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 d’ouvrir 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 l’analyse.');
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user