Added check spelling for country names
This commit is contained in:
parent
0108719a84
commit
9cb6ffbfb4
26
frontend/package-lock.json
generated
26
frontend/package-lock.json
generated
@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "mheidi-frontend-v2",
|
||||
"name": "heidi-frontend-v2",
|
||||
"version": "0.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mheidi-frontend-v2",
|
||||
"name": "heidi-frontend-v2",
|
||||
"version": "0.0.0",
|
||||
"dependencies": {
|
||||
"@aldabil/react-scheduler": "^2.9.5",
|
||||
@ -27,6 +27,7 @@
|
||||
"dotenv": "^16.4.7",
|
||||
"exceljs": "^4.4.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"fuse.js": "^7.0.0",
|
||||
"openapi-typescript-codegen": "^0.29.0",
|
||||
"react": "^18.3.1",
|
||||
"react-big-calendar": "^1.15.0",
|
||||
@ -1160,9 +1161,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@eslint/plugin-kit": {
|
||||
"version": "0.2.2",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.2.tgz",
|
||||
"integrity": "sha512-CXtq5nR4Su+2I47WPOlWud98Y5Lv8Kyxp2ukhgFx/eW6Blm18VXJO5WuQylPugRo8nbluoi6GvvxBLqHcvqUUw==",
|
||||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.4.tgz",
|
||||
"integrity": "sha512-zSkKow6H5Kdm0ZUQUB2kV5JIXqoG0+uH5YADhaEHswm664N9Db8dXSi0nMJpacpMf+MyyglF1vnZohpEg5yUtg==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
@ -4225,6 +4226,15 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/fuse.js": {
|
||||
"version": "7.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.0.0.tgz",
|
||||
"integrity": "sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/gensync": {
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||
@ -5071,9 +5081,9 @@
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/nanoid": {
|
||||
"version": "3.3.7",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
|
||||
"integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
|
||||
"version": "3.3.8",
|
||||
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.8.tgz",
|
||||
"integrity": "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==",
|
||||
"dev": true,
|
||||
"funding": [
|
||||
{
|
||||
|
@ -12,7 +12,7 @@
|
||||
"start-test": "vite --mode test",
|
||||
"start-prod": "vite --mode prod",
|
||||
"watch:openapi": "node fetch-openapi.js"
|
||||
},
|
||||
},
|
||||
"dependencies": {
|
||||
"@aldabil/react-scheduler": "^2.9.5",
|
||||
"@bitnoi.se/react-scheduler": "^0.3.1",
|
||||
@ -33,6 +33,7 @@
|
||||
"dotenv": "^16.4.7",
|
||||
"exceljs": "^4.4.0",
|
||||
"file-saver": "^2.0.5",
|
||||
"fuse.js": "^7.0.0",
|
||||
"openapi-typescript-codegen": "^0.29.0",
|
||||
"react": "^18.3.1",
|
||||
"react-big-calendar": "^1.15.0",
|
||||
|
198
frontend/src/components/CountryList.tsx
Normal file
198
frontend/src/components/CountryList.tsx
Normal file
@ -0,0 +1,198 @@
|
||||
export const CountryList = [
|
||||
'Afghanistan',
|
||||
'Albania',
|
||||
'Algeria',
|
||||
'Andorra',
|
||||
'Angola',
|
||||
'Antigua and Barbuda',
|
||||
'Argentina',
|
||||
'Armenia',
|
||||
'Australia',
|
||||
'Austria',
|
||||
'Azerbaijan',
|
||||
'Bahamas',
|
||||
'Bahrain',
|
||||
'Bangladesh',
|
||||
'Barbados',
|
||||
'Belarus',
|
||||
'Belgium',
|
||||
'Belize',
|
||||
'Benin',
|
||||
'Bhutan',
|
||||
'Bolivia',
|
||||
'Bosnia and Herzegovina',
|
||||
'Botswana',
|
||||
'Brazil',
|
||||
'Brunei',
|
||||
'Bulgaria',
|
||||
'Burkina Faso',
|
||||
'Burundi',
|
||||
'Cabo Verde',
|
||||
'Cambodia',
|
||||
'Cameroon',
|
||||
'Canada',
|
||||
'Central African Republic',
|
||||
'Chad',
|
||||
'Chile',
|
||||
'China',
|
||||
'Colombia',
|
||||
'Comoros',
|
||||
'Congo (Congo-Brazzaville)',
|
||||
'Costa Rica',
|
||||
'Croatia',
|
||||
'Cuba',
|
||||
'Cyprus',
|
||||
'Czechia (Czech Republic)',
|
||||
'Denmark',
|
||||
'Djibouti',
|
||||
'Dominica',
|
||||
'Dominican Republic',
|
||||
'Ecuador',
|
||||
'Egypt',
|
||||
'El Salvador',
|
||||
'Equatorial Guinea',
|
||||
'Eritrea',
|
||||
'Estonia',
|
||||
'Eswatini (fmr. "Swaziland")',
|
||||
'Ethiopia',
|
||||
'Fiji',
|
||||
'Finland',
|
||||
'France',
|
||||
'Gabon',
|
||||
'Gambia',
|
||||
'Georgia',
|
||||
'Germany',
|
||||
'Ghana',
|
||||
'Greece',
|
||||
'Grenada',
|
||||
'Guatemala',
|
||||
'Guinea',
|
||||
'Guinea-Bissau',
|
||||
'Guyana',
|
||||
'Haiti',
|
||||
'Holy See',
|
||||
'Honduras',
|
||||
'Hungary',
|
||||
'Iceland',
|
||||
'India',
|
||||
'Indonesia',
|
||||
'Iran',
|
||||
'Iraq',
|
||||
'Ireland',
|
||||
'Israel',
|
||||
'Italy',
|
||||
'Jamaica',
|
||||
'Japan',
|
||||
'Jordan',
|
||||
'Kazakhstan',
|
||||
'Kenya',
|
||||
'Kiribati',
|
||||
'Korea (North)',
|
||||
'Korea (South)',
|
||||
'Kosovo',
|
||||
'Kuwait',
|
||||
'Kyrgyzstan',
|
||||
'Laos',
|
||||
'Latvia',
|
||||
'Lebanon',
|
||||
'Lesotho',
|
||||
'Liberia',
|
||||
'Libya',
|
||||
'Liechtenstein',
|
||||
'Lithuania',
|
||||
'Luxembourg',
|
||||
'Madagascar',
|
||||
'Malawi',
|
||||
'Malaysia',
|
||||
'Maldives',
|
||||
'Mali',
|
||||
'Malta',
|
||||
'Marshall Islands',
|
||||
'Mauritania',
|
||||
'Mauritius',
|
||||
'Mexico',
|
||||
'Micronesia',
|
||||
'Moldova',
|
||||
'Monaco',
|
||||
'Mongolia',
|
||||
'Montenegro',
|
||||
'Morocco',
|
||||
'Mozambique',
|
||||
'Myanmar (formerly Burma)',
|
||||
'Namibia',
|
||||
'Nauru',
|
||||
'Nepal',
|
||||
'Netherlands',
|
||||
'New Zealand',
|
||||
'Nicaragua',
|
||||
'Niger',
|
||||
'Nigeria',
|
||||
'North Macedonia (formerly Macedonia)',
|
||||
'Norway',
|
||||
'Oman',
|
||||
'Pakistan',
|
||||
'Palau',
|
||||
'Palestine State',
|
||||
'Panama',
|
||||
'Papua New Guinea',
|
||||
'Paraguay',
|
||||
'Peru',
|
||||
'Philippines',
|
||||
'Poland',
|
||||
'Portugal',
|
||||
'Qatar',
|
||||
'Romania',
|
||||
'Russia',
|
||||
'Rwanda',
|
||||
'Saint Kitts and Nevis',
|
||||
'Saint Lucia',
|
||||
'Saint Vincent and the Grenadines',
|
||||
'Samoa',
|
||||
'San Marino',
|
||||
'Sao Tome and Principe',
|
||||
'Saudi Arabia',
|
||||
'Senegal',
|
||||
'Serbia',
|
||||
'Seychelles',
|
||||
'Sierra Leone',
|
||||
'Singapore',
|
||||
'Slovakia',
|
||||
'Slovenia',
|
||||
'Solomon Islands',
|
||||
'Somalia',
|
||||
'South Africa',
|
||||
'South Sudan',
|
||||
'Spain',
|
||||
'Sri Lanka',
|
||||
'Sudan',
|
||||
'Suriname',
|
||||
'Sweden',
|
||||
'Switzerland',
|
||||
'Syria',
|
||||
'Tajikistan',
|
||||
'Tanzania',
|
||||
'Thailand',
|
||||
'Timor-Leste',
|
||||
'Togo',
|
||||
'Tonga',
|
||||
'Trinidad and Tobago',
|
||||
'Tunisia',
|
||||
'Turkey',
|
||||
'Turkmenistan',
|
||||
'Tuvalu',
|
||||
'Uganda',
|
||||
'Ukraine',
|
||||
'United Arab Emirates',
|
||||
'United Kingdom',
|
||||
'United States of America',
|
||||
'Uruguay',
|
||||
'Uzbekistan',
|
||||
'Vanuatu',
|
||||
'Venezuela',
|
||||
'Vietnam',
|
||||
'Yemen',
|
||||
'Zambia',
|
||||
'Zimbabwe',
|
||||
];
|
||||
|
||||
export default CountryList
|
@ -1,4 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import Fuse from 'fuse.js';
|
||||
import {
|
||||
Box, Button, TextField, Typography, Select, MenuItem, Stack, FormControl, InputLabel
|
||||
} from '@mui/material';
|
||||
@ -9,6 +10,7 @@ import {
|
||||
OpenAPI, ShipmentCreate, ShipmentsService
|
||||
} from '../../openapi';
|
||||
import { useEffect } from 'react';
|
||||
import { CountryList } from './CountryList'; // Import the list of countries
|
||||
|
||||
const MAX_COMMENTS_LENGTH = 200;
|
||||
|
||||
@ -18,7 +20,14 @@ interface ShipmentFormProps {
|
||||
refreshShipments: () => void;
|
||||
}
|
||||
|
||||
// Set up Fuse.js for fuzzy searching
|
||||
const fuse = new Fuse(CountryList, {
|
||||
threshold: 0.3,
|
||||
includeScore: true,
|
||||
});
|
||||
|
||||
const ShipmentForm: React.FC<ShipmentFormProps> = ({ sx = {}, onCancel, refreshShipments }) => {
|
||||
const [countrySuggestions, setCountrySuggestions] = React.useState<string[]>([]);
|
||||
const [contactPersons, setContactPersons] = React.useState<ContactPerson[]>([]);
|
||||
const [returnAddresses, setReturnAddresses] = React.useState<Address[]>([]);
|
||||
const [proposals, setProposals] = React.useState<Proposal[]>([]);
|
||||
@ -95,6 +104,19 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({ sx = {}, onCancel, refreshS
|
||||
getProposals();
|
||||
}, []);
|
||||
|
||||
const handleCountryInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const value = event.target.value;
|
||||
|
||||
setNewReturnAddress({ ...newReturnAddress, country: value });
|
||||
|
||||
if (value) {
|
||||
const suggestions = fuse.search(value).map((result) => result.item);
|
||||
setCountrySuggestions(suggestions);
|
||||
} else {
|
||||
setCountrySuggestions([]);
|
||||
}
|
||||
};
|
||||
|
||||
const validateEmail = (email: string) => /\S+@\S+\.\S+/.test(email);
|
||||
const validatePhoneNumber = (phone: string) => /^\+?[1-9]\d{1,14}$/.test(phone);
|
||||
const validateZipCode = (zipcode: string) => {
|
||||
@ -410,10 +432,37 @@ const ShipmentForm: React.FC<ShipmentFormProps> = ({ sx = {}, onCancel, refreshS
|
||||
label="Country"
|
||||
name="country"
|
||||
value={newReturnAddress.country}
|
||||
onChange={(e) => setNewReturnAddress({ ...newReturnAddress, country: e.target.value })}
|
||||
onChange={handleCountryInputChange} // Ensure this matches the function name exactly
|
||||
fullWidth
|
||||
required
|
||||
error={!newReturnAddress.country} // Optional: Add an error indicator if the input is empty
|
||||
helperText={!newReturnAddress.country ? 'Country is required' : ''}
|
||||
/>
|
||||
|
||||
{/* Render country suggestions below the input field */}
|
||||
{countrySuggestions.length > 0 && (
|
||||
<Box sx={{ marginTop: '0.5rem', border: '1px solid #ccc', borderRadius: '4px', padding: '0.5rem' }}>
|
||||
{countrySuggestions.map((suggestion, index) => (
|
||||
<Typography
|
||||
key={index}
|
||||
sx={{
|
||||
cursor: 'pointer',
|
||||
padding: '0.2rem 0',
|
||||
'&:hover': {
|
||||
backgroundColor: '#f0f0f0',
|
||||
},
|
||||
}}
|
||||
onClick={() => {
|
||||
// Update country field with selected suggestion
|
||||
setNewReturnAddress({ ...newReturnAddress, country: suggestion });
|
||||
setCountrySuggestions([]); // Clear suggestions
|
||||
}}
|
||||
>
|
||||
{suggestion}
|
||||
</Typography>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
|
@ -1,4 +1,6 @@
|
||||
import React from 'react';
|
||||
import Fuse from 'fuse.js';
|
||||
import { CountryList } from '../components/CountryList';
|
||||
import {
|
||||
Container, Typography, List, ListItem, IconButton, TextField, Box, ListItemText, ListItemSecondaryAction, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle, Button
|
||||
} from '@mui/material';
|
||||
@ -9,7 +11,13 @@ import AddIcon from '@mui/icons-material/Add';
|
||||
import { AddressesService } from '../../openapi';
|
||||
import type { Address, AddressCreate, AddressUpdate } from '../models/Address';
|
||||
|
||||
const fuse = new Fuse(CountryList, {
|
||||
threshold: 0.3, // Lower threshold for stricter matches
|
||||
includeScore: true,
|
||||
});
|
||||
|
||||
const AddressManager: React.FC = () => {
|
||||
const [countrySuggestions, setCountrySuggestions] = React.useState<string[]>([]);
|
||||
const [addresses, setAddresses] = React.useState<Address[]>([]);
|
||||
const [newAddress, setNewAddress] = React.useState<Partial<Address>>({
|
||||
street: '',
|
||||
@ -21,6 +29,20 @@ const AddressManager: React.FC = () => {
|
||||
const [errorMessage, setErrorMessage] = React.useState<string | null>(null);
|
||||
const [dialogOpen, setDialogOpen] = React.useState(false);
|
||||
const [selectedAddress, setSelectedAddress] = React.useState<Address | null>(null);
|
||||
const handleCountryInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { value } = event.target;
|
||||
|
||||
// Update the corresponding field in `newAddress`
|
||||
setNewAddress({ ...newAddress, country: value });
|
||||
|
||||
// Perform fuzzy search using Fuse.js
|
||||
if (value) {
|
||||
const suggestions = fuse.search(value).map((result) => result.item);
|
||||
setCountrySuggestions(suggestions); // Update suggestions state
|
||||
} else {
|
||||
setCountrySuggestions([]); // Clear suggestions if the input is empty
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
const fetchAddresses = async () => {
|
||||
@ -102,7 +124,65 @@ const AddressManager: React.FC = () => {
|
||||
<TextField label="Street" name="street" value={newAddress.street || ''} onChange={handleInputChange} />
|
||||
<TextField label="City" name="city" value={newAddress.city || ''} onChange={handleInputChange} />
|
||||
<TextField label="Zipcode" name="zipcode" value={newAddress.zipcode || ''} onChange={handleInputChange} />
|
||||
<TextField label="Country" name="country" value={newAddress.country || ''} onChange={handleInputChange} />
|
||||
<TextField
|
||||
label="Country"
|
||||
name="country"
|
||||
value={newAddress.country || ''}
|
||||
onChange={handleCountryInputChange}
|
||||
fullWidth
|
||||
required
|
||||
error={
|
||||
!!((editAddressId !== null || newAddress.street || newAddress.city || newAddress.zipcode) && !newAddress.country)
|
||||
} // Show an error only if in add/edit mode and country is empty
|
||||
helperText={
|
||||
!!((editAddressId !== null || newAddress.street || newAddress.city || newAddress.zipcode) && !newAddress.country)
|
||||
? 'Country is required'
|
||||
: ''
|
||||
}
|
||||
/>
|
||||
|
||||
{/* Render suggestions dynamically */}
|
||||
<Box sx={{ position: 'relative' }}>
|
||||
<TextField
|
||||
label="Country"
|
||||
name="country"
|
||||
value={newAddress.country || ''}
|
||||
onChange={handleCountryInputChange}
|
||||
fullWidth
|
||||
/>
|
||||
|
||||
{countrySuggestions.length > 0 && (
|
||||
<Box
|
||||
sx={{
|
||||
border: '1px solid #ccc',
|
||||
borderRadius: '4px',
|
||||
background: 'white',
|
||||
marginTop: '4px', /* Add space below the input */
|
||||
position: 'absolute',
|
||||
width: '100%', /* Match the TextField width */
|
||||
zIndex: 10, /* Ensure it is above other UI elements */
|
||||
}}
|
||||
>
|
||||
{countrySuggestions.map((suggestion, index) => (
|
||||
<Typography
|
||||
key={index}
|
||||
sx={{
|
||||
padding: '8px',
|
||||
cursor: 'pointer',
|
||||
'&:hover': { background: '#f5f5f5' },
|
||||
}}
|
||||
onClick={() => {
|
||||
// Update country field with the clicked suggestion
|
||||
setNewAddress({ ...newAddress, country: suggestion });
|
||||
setCountrySuggestions([]); // Clear suggestions
|
||||
}}
|
||||
>
|
||||
{suggestion}
|
||||
</Typography>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
<IconButton color="primary" onClick={handleAddOrUpdateAddress}>
|
||||
{editAddressId !== null ? <SaveIcon /> : <AddIcon />}
|
||||
</IconButton>
|
||||
|
Loading…
x
Reference in New Issue
Block a user