large mock refector hopefully done + no errors?
All checks were successful
Deploy to Test Environment / deploy-to-test (push) Successful in 1h17m3s

This commit is contained in:
2025-12-21 10:47:58 -08:00
parent c49e5f7019
commit 9d5fea19b2
16 changed files with 253 additions and 227 deletions

View File

@@ -1,7 +1,7 @@
// src/hooks/useProfileAddress.ts
import { useState, useEffect, useCallback } from 'react';
import toast from 'react-hot-toast';
import type { Address, Profile } from '../types';
import type { Address, UserProfile } from '../types';
import { useApi } from './useApi';
import * as apiClient from '../services/apiClient';
import { logger } from '../services/logger.client';
@@ -10,62 +10,70 @@ import { useDebounce } from './useDebounce';
const geocodeWrapper = (address: string, signal?: AbortSignal) => apiClient.geocodeAddress(address, { signal });
const fetchAddressWrapper = (id: number, signal?: AbortSignal) => apiClient.getUserAddress(id, { signal });
/**
* Helper to generate a consistent address string for geocoding.
*/
const getAddressString = (address: Partial<Address>): string => {
return [
address.address_line_1,
address.city,
address.province_state,
address.postal_code,
address.country,
].filter(Boolean).join(', ');
};
/**
* A custom hook to manage a user's profile address, including fetching,
* updating, and automatic/manual geocoding.
* @param profile The user's profile object.
* @param userProfile The user's profile object.
* @param isOpen Whether the parent component (e.g., a modal) is open. This is used to reset state.
* @returns An object with address state and handler functions.
*/
export const useProfileAddress = (profile: Profile | null, isOpen: boolean) => {
export const useProfileAddress = (userProfile: UserProfile | null, isOpen: boolean) => {
const [address, setAddress] = useState<Partial<Address>>({});
const [initialAddress, setInitialAddress] = useState<Partial<Address>>({});
const { execute: geocode, loading: isGeocoding } = useApi<{ lat: number; lng: number }, [string]>(geocodeWrapper);
const { execute: fetchAddress } = useApi<Address, [number]>(fetchAddressWrapper);
const handleAddressFetch = useCallback(async (addressId: number) => {
logger.debug(`[useProfileAddress] Starting fetch for addressId: ${addressId}`);
const fetchedAddress = await fetchAddress(addressId);
if (fetchedAddress) {
logger.debug('[useProfileAddress] Successfully fetched address:', fetchedAddress);
setAddress(fetchedAddress);
setInitialAddress(fetchedAddress);
} else {
logger.warn(`[useProfileAddress] Fetch returned null or undefined for addressId: ${addressId}.`);
}
}, [fetchAddress]);
// Effect to fetch or reset address based on profile and modal state
useEffect(() => {
if (isOpen && profile) {
if (profile.address_id) {
logger.debug(`[useProfileAddress] Profile has address_id: ${profile.address_id}. Calling handleAddressFetch.`);
handleAddressFetch(profile.address_id);
const loadAddress = async () => {
if (userProfile?.address_id) {
logger.debug(`[useProfileAddress] Profile has address_id: ${userProfile.address_id}. Fetching.`);
const fetchedAddress = await fetchAddress(userProfile.address_id);
if (fetchedAddress) {
logger.debug('[useProfileAddress] Successfully fetched address:', fetchedAddress);
setAddress(fetchedAddress);
setInitialAddress(fetchedAddress);
} else {
logger.warn(`[useProfileAddress] Fetch returned null for addressId: ${userProfile.address_id}.`);
setAddress({});
setInitialAddress({});
}
} else {
logger.debug('[useProfileAddress] Profile has no address_id. Resetting address form.');
setAddress({});
setInitialAddress({});
}
};
if (isOpen && userProfile) {
loadAddress();
} else {
logger.debug('[useProfileAddress] Modal is closed or profile is null. Resetting address state.');
setAddress({});
setInitialAddress({});
}
}, [isOpen, profile, handleAddressFetch]);
}, [isOpen, userProfile, fetchAddress]); // fetchAddress is stable from useApi
const handleAddressChange = (field: keyof Address, value: string) => {
const handleAddressChange = useCallback((field: keyof Address, value: string) => {
setAddress(prev => ({ ...prev, [field]: value }));
};
}, []);
const handleManualGeocode = async () => {
const addressString = [
address.address_line_1,
address.city,
address.province_state,
address.postal_code,
address.country,
].filter(Boolean).join(', ');
const handleManualGeocode = useCallback(async () => {
const addressString = getAddressString(address);
if (!addressString) {
toast.error('Please fill in the address fields before geocoding.');
@@ -73,14 +81,13 @@ export const useProfileAddress = (profile: Profile | null, isOpen: boolean) => {
}
logger.debug(`[useProfileAddress] Manual geocode triggering for: ${addressString}`);
const result = await geocode(addressString);
if (result) {
const { lat, lng } = result;
setAddress(prev => ({ ...prev, latitude: lat, longitude: lng }));
toast.success('Address re-geocoded successfully!');
}
};
}, [address, geocode]);
// --- Automatic Geocoding Logic ---
const debouncedAddress = useDebounce(address, 1500);
@@ -89,29 +96,20 @@ export const useProfileAddress = (profile: Profile | null, isOpen: boolean) => {
const handleAutoGeocode = async () => {
logger.debug('[useProfileAddress] Auto-geocode effect triggered by debouncedAddress change');
// Prevent geocoding on initial load if the address hasn't been changed by the user.
if (JSON.stringify(debouncedAddress) === JSON.stringify(initialAddress)) {
logger.debug('[useProfileAddress] Skipping auto-geocode: address is unchanged from initial load.');
return;
}
const addressString = [
debouncedAddress.address_line_1,
debouncedAddress.city,
debouncedAddress.province_state,
debouncedAddress.postal_code,
debouncedAddress.country,
].filter(Boolean).join(', ');
logger.debug(`[useProfileAddress] addressString generated for auto-geocode: "${addressString}"`);
const addressString = getAddressString(debouncedAddress);
// Don't geocode an empty address or if we already have coordinates.
if (!addressString || (debouncedAddress.latitude && debouncedAddress.longitude)) {
logger.debug('[useProfileAddress] Skipping auto-geocode: empty string or coordinates already exist');
logger.debug('[useProfileAddress] Skipping auto-geocode: empty string or coordinates already exist', { hasString: !!addressString, hasCoords: !!debouncedAddress.latitude });
return;
}
logger.debug('[useProfileAddress] Calling auto-geocode API...');
logger.debug(`[useProfileAddress] Auto-geocoding: "${addressString}"`);
const result = await geocode(addressString);
if (result) {
logger.debug('[useProfileAddress] Auto-geocode API returned result:', result);