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; 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 ( return (
<div className="mb-4"> <div className="mb-4">
<label htmlFor={id} className="block text-sm font-medium text-gray-700"> <label htmlFor={id} className="block text-sm font-medium text-gray-700">

View File

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