All files / src/hooks useLocationCascade.ts

100% Statements 39/39
85.71% Branches 12/14
100% Functions 8/8
100% Lines 37/37

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81        57x 57x 57x 57x     57x 15x 15x 15x 14x   1x     15x       57x 22x 22x 16x 16x   6x 6x 5x   1x     22x     57x 6x 6x         57x   3x     1x 2x 2x 2x 3x 2x 1x 1x 1x 1x                   57x                    
import { useState, useEffect, useCallback } from "react";
import { taxonomyApi, type City, type Locality } from "../lib/api";
 
export function useLocationCascade() {
	const [cities, setCities] = useState<City[]>([]);
	const [localities, setLocalities] = useState<Locality[]>([]);
	const [selectedCityId, setSelectedCityId] = useState("");
	const [localityId, setLocalityId] = useState("");
 
	// Load cities on mount
	useEffect(() => {
		const loadCities = async () => {
			try {
				const response = await taxonomyApi.getCities();
				setCities(response.data || []);
			} catch (err) {
				console.error("Failed to load cities:", err);
			}
		};
		loadCities();
	}, []);
 
	// Load localities when city changes (skip zone)
	useEffect(() => {
		const loadLocalities = async () => {
			if (!selectedCityId) {
				setLocalities([]);
				return;
			}
			try {
				const response = await taxonomyApi.getLocalitiesByCity(selectedCityId);
				setLocalities(response.data || []);
			} catch (err) {
				console.error("Failed to load localities:", err);
			}
		};
		loadLocalities();
	}, [selectedCityId]);
 
	const handleCityChange = useCallback((cityId: string) => {
		setSelectedCityId(cityId);
		setLocalityId("");
	}, []);
 
	// Initialize from an existing locality ID (for edit forms)
	// Finds the city that contains this locality, sets city + locality
	const initializeFromLocality = useCallback(
		async (targetLocalityId: string) => {
			if (!targetLocalityId || cities.length === 0) return;
 
			// Try each city until we find the one containing this locality
			for (const city of cities) {
				try {
					const response = await taxonomyApi.getLocalitiesByCity(city.id);
					const cityLocalities = response.data || [];
					const match = cityLocalities.find((l) => l.id === targetLocalityId);
					if (match) {
						setSelectedCityId(city.id);
						setLocalities(cityLocalities);
						setLocalityId(targetLocalityId);
						return;
					}
				} catch {
					// Continue to next city
				}
			}
		},
		[cities],
	);
 
	return {
		cities,
		localities,
		selectedCityId,
		localityId,
		setLocalityId,
		handleCityChange,
		initializeFromLocality,
	};
}