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;
|
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">
|
||||||
@ -30,4 +30,4 @@ const Input: React.FC<InputProps> = ({ id, label, value, setChange, required = f
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default Input;
|
export default Input;
|
||||||
@ -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
|
{/* Postcode Input */}
|
||||||
/>
|
<Input
|
||||||
</div>
|
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">
|
<Input
|
||||||
<label htmlFor="postcode" className="block text-sm font-medium text-gray-700">Postcode</label>
|
id="phone"
|
||||||
<input
|
type="text"
|
||||||
type="number"
|
label="Phone"
|
||||||
id="postcode"
|
required
|
||||||
value={postcode}
|
setChange={(e) => setPhone(e.target.value)}
|
||||||
onChange={(e) => setPostcode(e.target.value)}
|
value={phone}
|
||||||
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="city" className="block text-sm font-medium text-gray-700">City</label>
|
id="email"
|
||||||
<input
|
type="email"
|
||||||
type="text"
|
label="Email"
|
||||||
id="city"
|
required
|
||||||
value={city}
|
setChange={(e) => setEmail(e.target.value)}
|
||||||
onChange={(e) => setCity(e.target.value)}
|
value={email}
|
||||||
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="phone" className="block text-sm font-medium text-gray-700">Phone</label>
|
id="password"
|
||||||
<input
|
type="password"
|
||||||
type="text"
|
label="Password"
|
||||||
id="phone"
|
required
|
||||||
value={phone}
|
setChange={(e) => setPassword(e.target.value)}
|
||||||
onChange={(e) => setPhone(e.target.value)}
|
value={password}
|
||||||
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>
|
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|||||||
Reference in New Issue
Block a user