Added AddressSuggestion component to handle address suggestions and autofill in user registration. Reworked form to make everything use input component

This commit is contained in:
2024-10-16 07:25:01 +00:00
parent b6daa687af
commit 83d89b31ae
3 changed files with 190 additions and 70 deletions

View File

@ -0,0 +1,137 @@
import React, { useState, useRef, ChangeEvent, useEffect } from 'react';
interface AddressSuggestionsProps {
address: string;
setAddress: (address: string) => void;
postcode: string;
setPostcode: (postcode: string) => void;
setCity: (city: string) => void;
}
const fetchAddressSuggestions = async (query: string): Promise<any[]> => {
try {
const response = await fetch(
`https://api.dataforsyningen.dk/adgangsadresser/autocomplete?q=${encodeURIComponent(query)}&fuzzy=true`
);
const data = await response.json();
return data;
} catch (error) {
console.error('Error fetching address suggestions:', error);
return [];
}
};
const fetchCity = async (postcode: string): Promise<string | null> => {
try {
const response = await fetch(`https://api.dataforsyningen.dk/postnumre?nr=${postcode}`);
const data = await response.json();
if (data.length > 0) {
return data[0].navn; // Return the city name
}
return null;
} catch (error) {
console.error('Error fetching city:', error);
return null;
}
};
const AddressSuggestions: React.FC<AddressSuggestionsProps> = ({ address, setAddress, postcode, setPostcode, setCity }) => {
const [addressSuggestions, setAddressSuggestions] = useState<any[]>([]);
const [showSuggestions, setShowSuggestions] = useState(false);
const suggestionListRef = useRef<HTMLUListElement | null>(null);
const handleAddressChange = async (e: ChangeEvent<HTMLInputElement>) => {
const newValue = e.target.value;
setAddress(newValue);
if (newValue) {
const suggestions = await fetchAddressSuggestions(newValue);
setAddressSuggestions(suggestions);
setShowSuggestions(true);
} else {
setAddressSuggestions([]);
setShowSuggestions(false);
}
};
const handleAddressSelect = async (selectedAddress: any) => {
const adgangsadresse = selectedAddress.adgangsadresse;
if (adgangsadresse) {
const street = [adgangsadresse.vejnavn, adgangsadresse.husnr].filter(Boolean).join(' ');
const postcode = adgangsadresse.postnr || '';
setAddress(street);
setPostcode(postcode);
setAddressSuggestions([]);
setShowSuggestions(false);
// Fetch and set the city based on the selected postcode
const cityName = await fetchCity(postcode);
if (cityName) {
setCity(cityName);
}
}
};
const handleClickOutside = (event: MouseEvent) => {
if (suggestionListRef.current && !suggestionListRef.current.contains(event.target as Node)) {
setShowSuggestions(false);
}
};
useEffect(() => {
document.addEventListener('click', handleClickOutside);
return () => {
document.removeEventListener('click', handleClickOutside);
};
}, []);
useEffect(() => {
const fetchAndSetCity = async () => {
if (postcode) {
const cityName = await fetchCity(postcode);
if (cityName) {
setCity(cityName);
}
}
};
fetchAndSetCity();
}, [postcode, setCity]);
return (
<div className="relative mb-4">
<label htmlFor="address" className="block text-sm font-medium text-gray-700">Address</label>
<input
id="address"
type="text"
value={address}
onChange={handleAddressChange}
className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm"
required
/>
{showSuggestions && addressSuggestions.length > 0 && (
<ul ref={suggestionListRef} className="absolute z-50 w-full bg-white border border-gray-300 rounded-md shadow-lg max-h-60 overflow-y-auto">
{addressSuggestions.map((suggestion, index) => {
const adgangsadresse = suggestion.adgangsadresse;
if (!adgangsadresse) return null;
const vejnavn = adgangsadresse.vejnavn || '';
const husnr = adgangsadresse.husnr || '';
const postnr = adgangsadresse.postnr || '';
return (
<li
key={index}
onMouseDown={() => handleAddressSelect(suggestion)}
className="p-2 hover:bg-gray-100 cursor-pointer"
>
{vejnavn} {husnr}, {postnr}
</li>
);
})}
</ul>
)}
</div>
);
};
export default AddressSuggestions;

View File

@ -10,7 +10,7 @@ interface InputProps {
placeholder?: string;
}
const Input: React.FC<InputProps> = ({ id, label, value, setChange, required = false, type = "text", placeholder }) => {
const Input: React.FC<InputProps> = ({ id, label, value, setChange, required = false, type = "text", placeholder}) => {
return (
<div className="mb-4">
<label htmlFor={id} className="block text-sm font-medium text-gray-700">
@ -30,4 +30,4 @@ const Input: React.FC<InputProps> = ({ id, label, value, setChange, required = f
);
};
export default Input;
export default Input;

View File

@ -4,6 +4,7 @@ import { useState } from 'react';
import { useRouter } from 'next/navigation';
import Link from 'next/link';
import Input from '@/app/components/Input';
import AddressSuggestions from '@/app/components/AddressSuggestions';
const RegisterPage = () => {
const [role, setRole] = useState<'PRIVATE' | 'BUSINESS' | null>(null); // Initially, no role is selected
@ -131,77 +132,59 @@ const RegisterPage = () => {
</>
)}
<div className="mb-4">
<label htmlFor="address" className="block text-sm font-medium text-gray-700">Address</label>
<input
type="text"
id="address"
value={address}
onChange={(e) => setAddress(e.target.value)}
className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm"
required
/>
</div>
{/* Address Input */}
<AddressSuggestions
address={address}
setAddress={setAddress}
postcode={postcode}
setPostcode={setPostcode}
setCity={setCity}
/>
{/* Postcode Input */}
<Input
id="postcode"
label="Postcode"
type="text"
value={postcode}
setChange={(e) => setPostcode(e.target.value)}
required
/>
{/* City Input */}
<Input
id="city"
label="City"
type="text"
value={city}
setChange={(e) => setCity(e.target.value)}
required
/>
<div className="mb-4">
<label htmlFor="postcode" className="block text-sm font-medium text-gray-700">Postcode</label>
<input
type="number"
id="postcode"
value={postcode}
onChange={(e) => setPostcode(e.target.value)}
className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm"
required
/>
</div>
<Input
id="phone"
type="text"
label="Phone"
required
setChange={(e) => setPhone(e.target.value)}
value={phone}
/>
<div className="mb-4">
<label htmlFor="city" className="block text-sm font-medium text-gray-700">City</label>
<input
type="text"
id="city"
value={city}
onChange={(e) => setCity(e.target.value)}
className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm"
required
/>
</div>
<Input
id="email"
type="email"
label="Email"
required
setChange={(e) => setEmail(e.target.value)}
value={email}
/>
<div className="mb-4">
<label htmlFor="phone" className="block text-sm font-medium text-gray-700">Phone</label>
<input
type="text"
id="phone"
value={phone}
onChange={(e) => setPhone(e.target.value)}
className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm"
required
/>
</div>
<div className="mb-4">
<label htmlFor="email" className="block text-sm font-medium text-gray-700">Email</label>
<input
type="email"
id="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm"
required
/>
</div>
<div className="mb-4">
<label htmlFor="password" className="block text-sm font-medium text-gray-700">Password</label>
<input
type="password"
id="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm"
required
/>
</div>
<Input
id="password"
type="password"
label="Password"
required
setChange={(e) => setPassword(e.target.value)}
value={password}
/>
<button
type="submit"