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:
137
src/app/components/AddressSuggestions.tsx
Normal file
137
src/app/components/AddressSuggestions.tsx
Normal 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;
|
||||
@ -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">
|
||||
|
||||
@ -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
|
||||
{/* Address Input */}
|
||||
<AddressSuggestions
|
||||
address={address}
|
||||
setAddress={setAddress}
|
||||
postcode={postcode}
|
||||
setPostcode={setPostcode}
|
||||
setCity={setCity}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="postcode" className="block text-sm font-medium text-gray-700">Postcode</label>
|
||||
<input
|
||||
type="number"
|
||||
{/* Postcode Input */}
|
||||
<Input
|
||||
id="postcode"
|
||||
label="Postcode"
|
||||
type="text"
|
||||
value={postcode}
|
||||
onChange={(e) => setPostcode(e.target.value)}
|
||||
className="mt-1 p-2 block w-full border border-gray-300 rounded-md shadow-sm"
|
||||
setChange={(e) => setPostcode(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="city" className="block text-sm font-medium text-gray-700">City</label>
|
||||
<input
|
||||
type="text"
|
||||
{/* City Input */}
|
||||
<Input
|
||||
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>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="phone" className="block text-sm font-medium text-gray-700">Phone</label>
|
||||
<input
|
||||
label="City"
|
||||
type="text"
|
||||
value={city}
|
||||
setChange={(e) => setCity(e.target.value)}
|
||||
required
|
||||
/>
|
||||
|
||||
<Input
|
||||
id="phone"
|
||||
type="text"
|
||||
label="Phone"
|
||||
required
|
||||
setChange={(e) => setPhone(e.target.value)}
|
||||
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"
|
||||
<Input
|
||||
id="email"
|
||||
type="email"
|
||||
label="Email"
|
||||
required
|
||||
setChange={(e) => setEmail(e.target.value)}
|
||||
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"
|
||||
<Input
|
||||
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"
|
||||
type="password"
|
||||
label="Password"
|
||||
required
|
||||
setChange={(e) => setPassword(e.target.value)}
|
||||
value={password}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
|
||||
Reference in New Issue
Block a user