Updates from beta testing
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
VITE_API_URL=https://beta.backend.ditchtheagent.com/api/
|
||||
ENABLE_REGISTRATION=true
|
||||
USE_LIVE_DATA=false
|
||||
USE_LIVE_DATA=true
|
||||
|
||||
6
ditch-the-agent/package-lock.json
generated
6
ditch-the-agent/package-lock.json
generated
@@ -27,6 +27,7 @@
|
||||
"formik": "^2.4.6",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lucide-react": "^0.525.0",
|
||||
"material-ui-popup-state": "^5.1.0",
|
||||
"react": "^18.2.0",
|
||||
@@ -4689,6 +4690,11 @@
|
||||
"resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz",
|
||||
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw=="
|
||||
},
|
||||
"node_modules/lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
"integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="
|
||||
},
|
||||
"node_modules/lodash.defaults": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "tsc && vite build",
|
||||
"build:beta": "vite build --mode beta",
|
||||
"build:prod": "tsc && vite build --mode production",
|
||||
"build:beta": "vite build --mode beta && cp -r ./dist/* /var/www/beta.app.ditchtheagent/html/",
|
||||
"build:prod": "vite build --mode production",
|
||||
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
|
||||
"preview": "vite preview",
|
||||
"predeploy": "vite build && cp ./dist/index.html ./dist/404.html",
|
||||
@@ -33,6 +33,7 @@
|
||||
"formik": "^2.4.6",
|
||||
"js-cookie": "^3.0.5",
|
||||
"jwt-decode": "^4.0.0",
|
||||
"lodash.debounce": "^4.0.8",
|
||||
"lucide-react": "^0.525.0",
|
||||
"material-ui-popup-state": "^5.1.0",
|
||||
"react": "^18.2.0",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import React, { useState, useEffect, useMemo, useCallback } from 'react';
|
||||
import {
|
||||
Dialog,
|
||||
DialogTitle,
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
Alert,
|
||||
} from '@mui/material';
|
||||
import { axiosInstance, axiosRealEstateApi } from '../../../../../axiosApi';
|
||||
|
||||
import debounce from 'lodash/debounce';
|
||||
import MapComponent from '../../../../base/MapComponent';
|
||||
import {
|
||||
AutocompleteDataResponseAPI,
|
||||
@@ -41,6 +41,26 @@ export interface PlacePrediction {
|
||||
place_id: string;
|
||||
}
|
||||
|
||||
const fetchAutocompleteOptions = async (
|
||||
value: string,
|
||||
setAutocompleteOptions: (options: any[]) => void,
|
||||
) => {
|
||||
// ... your existing API call logic from handleAddressAutocompleteInputChange
|
||||
try {
|
||||
let { data } = await axiosInstance.post<AutocompleteResponseAPI>('autocomplete-proxy/', {
|
||||
search: value,
|
||||
search_types: ['A'],
|
||||
});
|
||||
let temp = data.data.map((item) => ({ description: item.address, place_id: item.id }));
|
||||
console.log(temp);
|
||||
|
||||
setAutocompleteOptions(temp);
|
||||
} catch (error) {
|
||||
console.error('Autocomplete fetch failed:', error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const AddPropertyDialog: React.FC<AddPropertyDialogProps> = ({ open, onClose, onAddProperty }) => {
|
||||
const initalValues: Omit<PropertiesAPI, 'id' | 'owner' | 'created_at' | 'last_updated'> = {
|
||||
address: '',
|
||||
@@ -76,6 +96,11 @@ const AddPropertyDialog: React.FC<AddPropertyDialogProps> = ({ open, onClose, on
|
||||
const [formErrors, setFormErrors] = useState<{ [key: string]: string }>({});
|
||||
const [selectedPlace, setSelectedPlace] = useState<PlacePrediction | null>(null);
|
||||
|
||||
const debouncedFetch = useMemo(
|
||||
() => debounce(fetchAutocompleteOptions, 200),
|
||||
[], // Dependency array ensures the debounced function is created only once
|
||||
);
|
||||
|
||||
// Initialize Google Maps Places Service (requires Google Maps API key loaded globally)
|
||||
// This is a simplified approach. For a more robust solution, use @vis.gl/react-google-maps useMapsLibrary hook
|
||||
useEffect(() => {
|
||||
@@ -84,7 +109,6 @@ const AddPropertyDialog: React.FC<AddPropertyDialogProps> = ({ open, onClose, on
|
||||
// You might want to handle this by displaying a message or disabling autocomplete
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { name, value } = e.target;
|
||||
setNewProperty((prev) => ({ ...prev, [name]: value }));
|
||||
@@ -96,51 +120,81 @@ const AddPropertyDialog: React.FC<AddPropertyDialogProps> = ({ open, onClose, on
|
||||
});
|
||||
}
|
||||
};
|
||||
const handleAddressAutocompleteInputChange = useCallback(
|
||||
(event: React.SyntheticEvent, value: string) => {
|
||||
// 1. Update the local state immediately for a smooth input experience
|
||||
setNewProperty((prev) => ({ ...prev, address: value })); // <--- THIS IS THE CRITICAL LINE
|
||||
|
||||
const handleAddressAutocompleteInputChange = async (
|
||||
event: React.SyntheticEvent,
|
||||
value: string,
|
||||
) => {
|
||||
const test: boolean = !import.meta.env.USE_LIVE_DATA;
|
||||
let data: AutocompleteDataResponseAPI[] = [];
|
||||
if (value.length > 2) {
|
||||
if (test) {
|
||||
data = test_autocomplete.data.filter((item) => item.address.includes(value));
|
||||
// filter the data here
|
||||
} else {
|
||||
const { data } = await axiosRealEstateApi.post<AutocompleteDataResponseAPI[]>(
|
||||
'AutoComplete',
|
||||
{
|
||||
search: value,
|
||||
// 2. Clear options if the value is too short
|
||||
if (value.length < 3) {
|
||||
setAutocompleteOptions([]);
|
||||
// Important: Cancel any pending debounced calls
|
||||
debouncedFetch.cancel();
|
||||
return;
|
||||
}
|
||||
console.log('attempting the function');
|
||||
|
||||
// 3. Call the debounced function, which will fire the API call
|
||||
// only after 300ms of inactivity.
|
||||
const result = debouncedFetch(value, setAutocompleteOptions);
|
||||
console.log(result);
|
||||
},
|
||||
[debouncedFetch],
|
||||
);
|
||||
}
|
||||
setAutocompleteOptions(
|
||||
data.map((item) => ({
|
||||
description: item.address,
|
||||
place_id: item.id,
|
||||
})),
|
||||
);
|
||||
} else {
|
||||
console.log('we need more characters');
|
||||
}
|
||||
|
||||
setNewProperty((prev) => ({ ...prev, address: value }));
|
||||
// if (value.length > 2 && window.google && window.google.maps && window.google.maps.places) {
|
||||
// setAutocompleteLoading(true);
|
||||
// const service = new window.google.maps.places.AutocompleteService();
|
||||
// service.getPlacePredictions({ input: value }, (predictions, status) => {
|
||||
// if (status === window.google.maps.places.PlacesServiceStatus.OK && predictions) {
|
||||
// setAutocompleteOptions(predictions.map(p => ({ description: p.description, place_id: p.place_id })));
|
||||
// const handleAddressAutocompleteInputChange = async (
|
||||
// event: React.SyntheticEvent,
|
||||
// value: string,
|
||||
// ) => {
|
||||
// const test: boolean = import.meta.env.USE_LIVE_DATA;
|
||||
// console.log(test);
|
||||
// let data: AutocompleteDataResponseAPI[] = [];
|
||||
// if (value.length > 2) {
|
||||
// if (test) {
|
||||
// data = test_autocomplete.data.filter((item) => item.address.includes(value));
|
||||
// // filter the data here
|
||||
// setAutocompleteOptions(
|
||||
// data.map((item) => ({
|
||||
// description: item.address,
|
||||
// place_id: item.id,
|
||||
// })),
|
||||
// );
|
||||
// } else {
|
||||
// setAutocompleteOptions([]);
|
||||
// }
|
||||
// setAutocompleteLoading(false);
|
||||
// let { data } = await axiosInstance.post<AutocompleteResponseAPI>('autocomplete-proxy/', {
|
||||
// search: value,
|
||||
// search_types: ['A'],
|
||||
// });
|
||||
// } else {
|
||||
// setAutocompleteOptions([]);
|
||||
// data = data.data;
|
||||
// console.log(data);
|
||||
// const temp = data.map((item) => ({ description: item.address, place_id: item.id }));
|
||||
// console.log(temp);
|
||||
// setAutocompleteOptions(temp);
|
||||
// }
|
||||
// } else {
|
||||
// console.log('we need more characters');
|
||||
// }
|
||||
|
||||
// setNewProperty((prev) => ({ ...prev, address: value }));
|
||||
// // if (value.length > 2 && window.google && window.google.maps && window.google.maps.places) {
|
||||
// // setAutocompleteLoading(true);
|
||||
// // const service = new window.google.maps.places.AutocompleteService();
|
||||
// // service.getPlacePredictions({ input: value }, (predictions, status) => {
|
||||
// // if (status === window.google.maps.places.PlacesServiceStatus.OK && predictions) {
|
||||
// // setAutocompleteOptions(predictions.map(p => ({ description: p.description, place_id: p.place_id })));
|
||||
// // } else {
|
||||
// // setAutocompleteOptions([]);
|
||||
// // }
|
||||
// // setAutocompleteLoading(false);
|
||||
// // });
|
||||
// // } else {
|
||||
// // setAutocompleteOptions([]);
|
||||
// // }
|
||||
// };
|
||||
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
debouncedFetch.cancel();
|
||||
};
|
||||
}, [debouncedFetch]);
|
||||
|
||||
const handleAddressAutocompleteChange = async (
|
||||
event: React.SyntheticEvent,
|
||||
@@ -150,7 +204,7 @@ const AddPropertyDialog: React.FC<AddPropertyDialogProps> = ({ open, onClose, on
|
||||
console.log('here we go', value);
|
||||
if (value) {
|
||||
console.log('find the test data');
|
||||
const test: boolean = true;
|
||||
const test: boolean = import.meta.env.USE_LIVE_DATA;
|
||||
if (test) {
|
||||
const parts: string[] =
|
||||
test_property_search.data.currentMortgages[0].recordingDate.split('T');
|
||||
@@ -219,6 +273,91 @@ const AddPropertyDialog: React.FC<AddPropertyDialogProps> = ({ open, onClose, on
|
||||
},
|
||||
sale_info: sale_history,
|
||||
});
|
||||
} else {
|
||||
console.log('using live data');
|
||||
let { data } = await axiosInstance.post<PropertyResponseAPI>('property-details-proxy/', {
|
||||
comps: false,
|
||||
id: value.place_id,
|
||||
exact_match: true,
|
||||
});
|
||||
data = data.data;
|
||||
console.log(data);
|
||||
console.log(data.currentMortgages);
|
||||
console.log(data.currentMortgages);
|
||||
let parts: string;
|
||||
let loan_amount: string;
|
||||
let term: string;
|
||||
if (data.currentMortgages.length > 0) {
|
||||
parts = data.currentMortgages[0].recordingDate.split('T')[0];
|
||||
loan_amount = data.currentMortgages[0].amount.toString();
|
||||
term = data.currentMortgages[0].term;
|
||||
} else {
|
||||
parts = '';
|
||||
loan_amount = '';
|
||||
term = '';
|
||||
}
|
||||
|
||||
const schools: Omit<SchoolAPI, 'id' | 'created_at' | 'last_updated'>[] = data.schools.map(
|
||||
(item) => {
|
||||
const coordinates = extractLatLon(item.location);
|
||||
return {
|
||||
city: item.city,
|
||||
state: item.state,
|
||||
zip_code: item.zip,
|
||||
latitude: coordinates?.latitude,
|
||||
longitude: coordinates?.longitude,
|
||||
school_type: item.type,
|
||||
enrollment: item.enrollment,
|
||||
grades: item.grades,
|
||||
name: item.name,
|
||||
parent_rating: item.parentRating,
|
||||
rating: item.rating,
|
||||
};
|
||||
},
|
||||
);
|
||||
console.log(schools);
|
||||
|
||||
// get the sale history
|
||||
const sale_history: Omit<SaleHistoryAPI, 'id' | 'created_at' | 'last_updated'>[] =
|
||||
data.saleHistory.map((item) => {
|
||||
return {
|
||||
seq_no: item.seqNo,
|
||||
sale_date: item.saleDate,
|
||||
sale_amount: item.saleAmount,
|
||||
};
|
||||
});
|
||||
|
||||
setNewProperty({
|
||||
address: data.propertyInfo.address.address,
|
||||
street: data.ownerInfo.mailAddress.address,
|
||||
city: data.propertyInfo.address.city,
|
||||
state: data.propertyInfo.address.state,
|
||||
zip_code: data.propertyInfo.address.zip,
|
||||
latitude: data.propertyInfo.latitude,
|
||||
longitude: data.propertyInfo.longitude,
|
||||
market_value: data.estimatedValue.toString(),
|
||||
loan_amount: loan_amount,
|
||||
loan_term: term,
|
||||
loan_start_date: parts,
|
||||
description: '',
|
||||
features: [],
|
||||
pictures: [],
|
||||
num_bedrooms: data.propertyInfo.bedrooms,
|
||||
num_bathrooms: data.propertyInfo.bathrooms,
|
||||
sq_ft: data.propertyInfo.buildingSquareFeet,
|
||||
realestate_api_id: data.id,
|
||||
views: 0,
|
||||
saves: 0,
|
||||
property_status: 'off_market',
|
||||
schools: schools,
|
||||
tax_info: {
|
||||
assessed_value: data.taxInfo.assessedValue,
|
||||
assessment_year: data.taxInfo.assessmentYear,
|
||||
tax_amount: Number(data.taxInfo.taxAmount),
|
||||
year: data.taxInfo.year,
|
||||
},
|
||||
sale_info: sale_history,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import React, { useState, useMemo, useCallback } from 'react';
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
@@ -25,6 +25,8 @@ import { AutocompleteDataResponseAPI } from 'types';
|
||||
import { axiosInstance, axiosRealEstateApi } from '../../../../../axiosApi';
|
||||
import { extractLatLon } from 'utils';
|
||||
import { PlacePrediction } from './AddPropertyDialog';
|
||||
import { PropertyResponseAPI } from '../../../../../types';
|
||||
import debounce from 'lodash/debounce';
|
||||
|
||||
interface VendorProfileCardProps {
|
||||
vendor: VendorAPI;
|
||||
@@ -32,6 +34,26 @@ interface VendorProfileCardProps {
|
||||
onSave: (updatedVendor: VendorAPI) => void;
|
||||
}
|
||||
|
||||
const fetchAutocompleteOptions = async (
|
||||
value: string,
|
||||
setAutocompleteOptions: (options: any[]) => void,
|
||||
) => {
|
||||
// ... your existing API call logic from handleAddressAutocompleteInputChange
|
||||
try {
|
||||
let { data } = await axiosInstance.post<AutocompleteResponseAPI>('autocomplete-proxy/', {
|
||||
search: value,
|
||||
search_types: ['A'],
|
||||
});
|
||||
let temp = data.data.map((item) => ({ description: item.address, place_id: item.id }));
|
||||
console.log(temp);
|
||||
|
||||
setAutocompleteOptions(temp);
|
||||
} catch (error) {
|
||||
console.error('Autocomplete fetch failed:', error);
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const VendorProfileCard: React.FC<VendorProfileCardProps> = ({ vendor, onUpgrade, onSave }) => {
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const [editedVendor, setEditedVendor] = useState<VendorAPI>(vendor);
|
||||
@@ -107,6 +129,11 @@ const VendorProfileCard: React.FC<VendorProfileCardProps> = ({ vendor, onUpgrade
|
||||
}
|
||||
};
|
||||
|
||||
const debouncedFetch = useMemo(
|
||||
() => debounce(fetchAutocompleteOptions, 200),
|
||||
[], // Dependency array ensures the debounced function is created only once
|
||||
);
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { name, value } = e.target;
|
||||
//setNewProperty((prev) => ({ ...prev, [name]: value }));
|
||||
@@ -119,58 +146,80 @@ const VendorProfileCard: React.FC<VendorProfileCardProps> = ({ vendor, onUpgrade
|
||||
}
|
||||
};
|
||||
|
||||
const handleAddressAutocompleteInputChange = async (
|
||||
event: React.SyntheticEvent,
|
||||
value: string,
|
||||
) => {
|
||||
const test: boolean = !import.meta.env.USE_LIVE_DATA;
|
||||
let data: AutocompleteDataResponseAPI[] = [];
|
||||
if (value.length > 2) {
|
||||
if (test) {
|
||||
data = test_autocomplete.data.filter((item) => item.address.includes(value));
|
||||
// filter the data here
|
||||
} else {
|
||||
const { data } = await axiosRealEstateApi.post<AutocompleteDataResponseAPI[]>(
|
||||
'AutoComplete',
|
||||
{
|
||||
search: value,
|
||||
const handleAddressAutocompleteInputChange = useCallback(
|
||||
(event: React.SyntheticEvent, value: string) => {
|
||||
// 1. Update the local state immediately for a smooth input experience
|
||||
setEditedVendor((prev) => ({ ...prev, address: value })); // <--- THIS IS THE CRITICAL LINE
|
||||
|
||||
// 2. Clear options if the value is too short
|
||||
if (value.length < 3) {
|
||||
setAutocompleteOptions([]);
|
||||
// Important: Cancel any pending debounced calls
|
||||
debouncedFetch.cancel();
|
||||
return;
|
||||
}
|
||||
console.log('attempting the function');
|
||||
|
||||
// 3. Call the debounced function, which will fire the API call
|
||||
// only after 300ms of inactivity.
|
||||
const result = debouncedFetch(value, setAutocompleteOptions);
|
||||
console.log(result);
|
||||
},
|
||||
[debouncedFetch],
|
||||
);
|
||||
}
|
||||
setAutocompleteOptions(
|
||||
data.map((item) => ({
|
||||
description: item.address,
|
||||
place_id: item.id,
|
||||
})),
|
||||
);
|
||||
} else {
|
||||
console.log('we need more characters');
|
||||
}
|
||||
};
|
||||
|
||||
// const handleAddressAutocompleteInputChange = async (
|
||||
// event: React.SyntheticEvent,
|
||||
// value: string,
|
||||
// ) => {
|
||||
// const test: boolean = import.meta.env.USE_LIVE_DATA;
|
||||
// let data: AutocompleteDataResponseAPI[] = [];
|
||||
// if (value.length > 2) {
|
||||
// if (test) {
|
||||
// data = test_autocomplete.data.filter((item) => item.address.includes(value));
|
||||
// setAutocompleteOptions(
|
||||
// data.map((item) => ({
|
||||
// description: item.address,
|
||||
// place_id: item.id,
|
||||
// })),
|
||||
// );
|
||||
// // filter the data here
|
||||
// } else {
|
||||
// let { data } = await axiosInstance.post<AutocompleteResponseAPI>('autocomplete-proxy/', {
|
||||
// search: value,
|
||||
// search_types: ['A'],
|
||||
// });
|
||||
// data = data.data;
|
||||
// const temp = data.map((item) => ({ description: item.address, place_id: item.id }));
|
||||
// setAutocompleteOptions(temp);
|
||||
// }
|
||||
// } else {
|
||||
// console.log('we need more characters');
|
||||
// }
|
||||
// };
|
||||
|
||||
const handleAddressAutocompleteChange = async (
|
||||
event: React.SyntheticEvent,
|
||||
value: PlacePrediction | null,
|
||||
) => {
|
||||
console.log('here we go', value);
|
||||
if (1) {
|
||||
const data = test_autocomplete.data.filter((item) => item.id === value.place_id);
|
||||
if (data.length > 0) {
|
||||
const item = data[0];
|
||||
const coordinates = extractLatLon(item.location);
|
||||
let { data } = await axiosInstance.post<PropertyResponseAPI>('property-details-proxy/', {
|
||||
comps: false,
|
||||
id: value.place_id,
|
||||
exact_match: true,
|
||||
});
|
||||
data = data.data;
|
||||
console.log(data);
|
||||
|
||||
setEditedVendor((prev) => ({
|
||||
...prev,
|
||||
address: item.address,
|
||||
city: item.city,
|
||||
state: item.state,
|
||||
zip_code: item.zip,
|
||||
latitude: Number(coordinates.latitude),
|
||||
longitude: Number(coordinates.longitude),
|
||||
address: data.propertyInfo.address.address,
|
||||
street: data.propertyInfo.address.address,
|
||||
city: data.propertyInfo.address.city,
|
||||
state: data.propertyInfo.address.state,
|
||||
zip_code: data.propertyInfo.address.zip,
|
||||
latitude: data.propertyInfo.latitude,
|
||||
longitude: data.propertyInfo.longitude,
|
||||
}));
|
||||
}
|
||||
} else {
|
||||
// use the api here
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
|
||||
@@ -54,7 +54,7 @@ const SignUp = (): ReactElement => {
|
||||
password2,
|
||||
}: SignUpValues): Promise<void> => {
|
||||
try {
|
||||
const response = await axiosInstance.post('/register/', {
|
||||
const response = await axiosInstance.post('register/', {
|
||||
email: email,
|
||||
first_name: first_name,
|
||||
last_name: last_name,
|
||||
@@ -62,8 +62,11 @@ const SignUp = (): ReactElement => {
|
||||
password: password,
|
||||
password2: password2,
|
||||
});
|
||||
console.log(response);
|
||||
if (response.status == 201) {
|
||||
navigate('/authentication/login');
|
||||
|
||||
console.log('Good response');
|
||||
} else {
|
||||
console.log(`No good: ${response}`);
|
||||
}
|
||||
|
||||
@@ -379,23 +379,23 @@ export interface AutocompleteResponseAPI {
|
||||
}
|
||||
export interface AutocompleteDataResponseAPI {
|
||||
zip: string;
|
||||
address: string;
|
||||
city: string;
|
||||
address?: string;
|
||||
city?: string;
|
||||
searchType: string;
|
||||
stateId: string;
|
||||
latitude: number;
|
||||
county: string;
|
||||
fips: string;
|
||||
latitude?: number;
|
||||
county?: string;
|
||||
fips?: string;
|
||||
title: string;
|
||||
house: string;
|
||||
house?: string;
|
||||
unit?: string;
|
||||
countyId: string;
|
||||
street: string;
|
||||
location: string;
|
||||
id: string;
|
||||
countyId?: string;
|
||||
street?: string;
|
||||
location?: string;
|
||||
id?: string;
|
||||
state: string;
|
||||
apn: string;
|
||||
longitude: number;
|
||||
apn?: string;
|
||||
longitude?: number;
|
||||
}
|
||||
|
||||
export interface PropertyResponseDataMortgageAPI {
|
||||
|
||||
Reference in New Issue
Block a user