diff --git a/frontend/src/components/CountryPicker.jsx b/frontend/src/components/CountryPicker.jsx new file mode 100644 index 00000000..0b781e2c --- /dev/null +++ b/frontend/src/components/CountryPicker.jsx @@ -0,0 +1,144 @@ +import React, { useState, useRef, useEffect } from "react"; +import { Input } from "./ui/input"; +import { Popover, PopoverContent, PopoverTrigger } from "./ui/popover"; +import { cn } from "@/lib/utils"; +import { ChevronDown, Search } from "lucide-react"; +import countriesData from "@/lib/countries.json"; + +const CountryPicker = ({ + value = "", + onChange, + placeholder = "Select country", + className, + disabled = false, +}) => { + const [open, setOpen] = useState(false); + const [searchQuery, setSearchQuery] = useState(""); + const inputRef = useRef(null); + const searchInputRef = useRef(null); + + // Find selected country + const selectedCountry = countriesData.find( + (country) => country.name.toLowerCase() === value.toLowerCase() + ); + + // Filter countries based on search + const filteredCountries = countriesData.filter((country) => + country.name.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + // Handle country selection + const handleSelect = (country) => { + onChange(country.name); + setOpen(false); + setSearchQuery(""); + }; + + // Reset search when popover closes + useEffect(() => { + if (!open) { + setSearchQuery(""); + } + }, [open]); + + // Focus search input when popover opens + useEffect(() => { + if (open && searchInputRef.current) { + setTimeout(() => { + searchInputRef.current?.focus(); + }, 100); + } + }, [open]); + + const getFlagUrl = (code) => { + return `https://flagcdn.com/w40/${code.toLowerCase()}.png`; + }; + + return ( + + + + + +
+
+ + setSearchQuery(e.target.value)} + className="pl-8 h-9" + /> +
+
+
+ {filteredCountries.length === 0 ? ( +
+ No countries found +
+ ) : ( +
+ {filteredCountries.map((country) => ( + + ))} +
+ )} +
+
+
+ ); +}; + +export default CountryPicker; + diff --git a/frontend/src/lib/countries.json b/frontend/src/lib/countries.json new file mode 100644 index 00000000..dc736497 --- /dev/null +++ b/frontend/src/lib/countries.json @@ -0,0 +1,1002 @@ +[ + { + "name": "Afghanistan", + "code": "AF" + }, + { + "name": "Albania", + "code": "AL" + }, + { + "name": "Algeria", + "code": "DZ" + }, + { + "name": "American Samoa", + "code": "AS" + }, + { + "name": "Andorra", + "code": "AD" + }, + { + "name": "Angola", + "code": "AO" + }, + { + "name": "Anguilla", + "code": "AI" + }, + { + "name": "Antarctica", + "code": "AQ" + }, + { + "name": "Antigua and Barbuda", + "code": "AG" + }, + { + "name": "Argentina", + "code": "AR" + }, + { + "name": "Armenia", + "code": "AM" + }, + { + "name": "Aruba", + "code": "AW" + }, + { + "name": "Australia", + "code": "AU" + }, + { + "name": "Austria", + "code": "AT" + }, + { + "name": "Azerbaijan", + "code": "AZ" + }, + { + "name": "Bahamas", + "code": "BS" + }, + { + "name": "Bahrain", + "code": "BH" + }, + { + "name": "Bangladesh", + "code": "BD" + }, + { + "name": "Barbados", + "code": "BB" + }, + { + "name": "Belarus", + "code": "BY" + }, + { + "name": "Belgium", + "code": "BE" + }, + { + "name": "Belize", + "code": "BZ" + }, + { + "name": "Benin", + "code": "BJ" + }, + { + "name": "Bermuda", + "code": "BM" + }, + { + "name": "Bhutan", + "code": "BT" + }, + { + "name": "Bolivia", + "code": "BO" + }, + { + "name": "Bosnia and Herzegovina", + "code": "BA" + }, + { + "name": "Botswana", + "code": "BW" + }, + { + "name": "Bouvet Island", + "code": "BV" + }, + { + "name": "Brazil", + "code": "BR" + }, + { + "name": "British Indian Ocean Territory", + "code": "IO" + }, + { + "name": "British Virgin Islands", + "code": "VG" + }, + { + "name": "Brunei", + "code": "BN" + }, + { + "name": "Bulgaria", + "code": "BG" + }, + { + "name": "Burkina Faso", + "code": "BF" + }, + { + "name": "Burundi", + "code": "BI" + }, + { + "name": "Cambodia", + "code": "KH" + }, + { + "name": "Cameroon", + "code": "CM" + }, + { + "name": "Canada", + "code": "CA" + }, + { + "name": "Cape Verde", + "code": "CV" + }, + { + "name": "Caribbean Netherlands", + "code": "BQ" + }, + { + "name": "Cayman Islands", + "code": "KY" + }, + { + "name": "Central African Republic", + "code": "CF" + }, + { + "name": "Chad", + "code": "TD" + }, + { + "name": "Chile", + "code": "CL" + }, + { + "name": "China", + "code": "CN" + }, + { + "name": "Christmas Island", + "code": "CX" + }, + { + "name": "Cocos (Keeling) Islands", + "code": "CC" + }, + { + "name": "Colombia", + "code": "CO" + }, + { + "name": "Comoros", + "code": "KM" + }, + { + "name": "Cook Islands", + "code": "CK" + }, + { + "name": "Costa Rica", + "code": "CR" + }, + { + "name": "Croatia", + "code": "HR" + }, + { + "name": "Cuba", + "code": "CU" + }, + { + "name": "Curaçao", + "code": "CW" + }, + { + "name": "Cyprus", + "code": "CY" + }, + { + "name": "Czechia", + "code": "CZ" + }, + { + "name": "DR Congo", + "code": "CD" + }, + { + "name": "Denmark", + "code": "DK" + }, + { + "name": "Djibouti", + "code": "DJ" + }, + { + "name": "Dominica", + "code": "DM" + }, + { + "name": "Dominican Republic", + "code": "DO" + }, + { + "name": "Ecuador", + "code": "EC" + }, + { + "name": "Egypt", + "code": "EG" + }, + { + "name": "El Salvador", + "code": "SV" + }, + { + "name": "Equatorial Guinea", + "code": "GQ" + }, + { + "name": "Eritrea", + "code": "ER" + }, + { + "name": "Estonia", + "code": "EE" + }, + { + "name": "Eswatini", + "code": "SZ" + }, + { + "name": "Ethiopia", + "code": "ET" + }, + { + "name": "Falkland Islands", + "code": "FK" + }, + { + "name": "Faroe Islands", + "code": "FO" + }, + { + "name": "Fiji", + "code": "FJ" + }, + { + "name": "Finland", + "code": "FI" + }, + { + "name": "France", + "code": "FR" + }, + { + "name": "French Guiana", + "code": "GF" + }, + { + "name": "French Polynesia", + "code": "PF" + }, + { + "name": "French Southern and Antarctic Lands", + "code": "TF" + }, + { + "name": "Gabon", + "code": "GA" + }, + { + "name": "Gambia", + "code": "GM" + }, + { + "name": "Georgia", + "code": "GE" + }, + { + "name": "Germany", + "code": "DE" + }, + { + "name": "Ghana", + "code": "GH" + }, + { + "name": "Gibraltar", + "code": "GI" + }, + { + "name": "Greece", + "code": "GR" + }, + { + "name": "Greenland", + "code": "GL" + }, + { + "name": "Grenada", + "code": "GD" + }, + { + "name": "Guadeloupe", + "code": "GP" + }, + { + "name": "Guam", + "code": "GU" + }, + { + "name": "Guatemala", + "code": "GT" + }, + { + "name": "Guernsey", + "code": "GG" + }, + { + "name": "Guinea", + "code": "GN" + }, + { + "name": "Guinea-Bissau", + "code": "GW" + }, + { + "name": "Guyana", + "code": "GY" + }, + { + "name": "Haiti", + "code": "HT" + }, + { + "name": "Heard Island and McDonald Islands", + "code": "HM" + }, + { + "name": "Honduras", + "code": "HN" + }, + { + "name": "Hong Kong", + "code": "HK" + }, + { + "name": "Hungary", + "code": "HU" + }, + { + "name": "Iceland", + "code": "IS" + }, + { + "name": "India", + "code": "IN" + }, + { + "name": "Indonesia", + "code": "ID" + }, + { + "name": "Iran", + "code": "IR" + }, + { + "name": "Iraq", + "code": "IQ" + }, + { + "name": "Ireland", + "code": "IE" + }, + { + "name": "Isle of Man", + "code": "IM" + }, + { + "name": "Israel", + "code": "IL" + }, + { + "name": "Italy", + "code": "IT" + }, + { + "name": "Ivory Coast", + "code": "CI" + }, + { + "name": "Jamaica", + "code": "JM" + }, + { + "name": "Japan", + "code": "JP" + }, + { + "name": "Jersey", + "code": "JE" + }, + { + "name": "Jordan", + "code": "JO" + }, + { + "name": "Kazakhstan", + "code": "KZ" + }, + { + "name": "Kenya", + "code": "KE" + }, + { + "name": "Kiribati", + "code": "KI" + }, + { + "name": "Kosovo", + "code": "XK" + }, + { + "name": "Kuwait", + "code": "KW" + }, + { + "name": "Kyrgyzstan", + "code": "KG" + }, + { + "name": "Laos", + "code": "LA" + }, + { + "name": "Latvia", + "code": "LV" + }, + { + "name": "Lebanon", + "code": "LB" + }, + { + "name": "Lesotho", + "code": "LS" + }, + { + "name": "Liberia", + "code": "LR" + }, + { + "name": "Libya", + "code": "LY" + }, + { + "name": "Liechtenstein", + "code": "LI" + }, + { + "name": "Lithuania", + "code": "LT" + }, + { + "name": "Luxembourg", + "code": "LU" + }, + { + "name": "Macau", + "code": "MO" + }, + { + "name": "Madagascar", + "code": "MG" + }, + { + "name": "Malawi", + "code": "MW" + }, + { + "name": "Malaysia", + "code": "MY" + }, + { + "name": "Maldives", + "code": "MV" + }, + { + "name": "Mali", + "code": "ML" + }, + { + "name": "Malta", + "code": "MT" + }, + { + "name": "Marshall Islands", + "code": "MH" + }, + { + "name": "Martinique", + "code": "MQ" + }, + { + "name": "Mauritania", + "code": "MR" + }, + { + "name": "Mauritius", + "code": "MU" + }, + { + "name": "Mayotte", + "code": "YT" + }, + { + "name": "Mexico", + "code": "MX" + }, + { + "name": "Micronesia", + "code": "FM" + }, + { + "name": "Moldova", + "code": "MD" + }, + { + "name": "Monaco", + "code": "MC" + }, + { + "name": "Mongolia", + "code": "MN" + }, + { + "name": "Montenegro", + "code": "ME" + }, + { + "name": "Montserrat", + "code": "MS" + }, + { + "name": "Morocco", + "code": "MA" + }, + { + "name": "Mozambique", + "code": "MZ" + }, + { + "name": "Myanmar", + "code": "MM" + }, + { + "name": "Namibia", + "code": "NA" + }, + { + "name": "Nauru", + "code": "NR" + }, + { + "name": "Nepal", + "code": "NP" + }, + { + "name": "Netherlands", + "code": "NL" + }, + { + "name": "New Caledonia", + "code": "NC" + }, + { + "name": "New Zealand", + "code": "NZ" + }, + { + "name": "Nicaragua", + "code": "NI" + }, + { + "name": "Niger", + "code": "NE" + }, + { + "name": "Nigeria", + "code": "NG" + }, + { + "name": "Niue", + "code": "NU" + }, + { + "name": "Norfolk Island", + "code": "NF" + }, + { + "name": "North Korea", + "code": "KP" + }, + { + "name": "North Macedonia", + "code": "MK" + }, + { + "name": "Northern Mariana Islands", + "code": "MP" + }, + { + "name": "Norway", + "code": "NO" + }, + { + "name": "Oman", + "code": "OM" + }, + { + "name": "Pakistan", + "code": "PK" + }, + { + "name": "Palau", + "code": "PW" + }, + { + "name": "Palestine", + "code": "PS" + }, + { + "name": "Panama", + "code": "PA" + }, + { + "name": "Papua New Guinea", + "code": "PG" + }, + { + "name": "Paraguay", + "code": "PY" + }, + { + "name": "Peru", + "code": "PE" + }, + { + "name": "Philippines", + "code": "PH" + }, + { + "name": "Pitcairn Islands", + "code": "PN" + }, + { + "name": "Poland", + "code": "PL" + }, + { + "name": "Portugal", + "code": "PT" + }, + { + "name": "Puerto Rico", + "code": "PR" + }, + { + "name": "Qatar", + "code": "QA" + }, + { + "name": "Republic of the Congo", + "code": "CG" + }, + { + "name": "Romania", + "code": "RO" + }, + { + "name": "Russia", + "code": "RU" + }, + { + "name": "Rwanda", + "code": "RW" + }, + { + "name": "Réunion", + "code": "RE" + }, + { + "name": "Saint Barthélemy", + "code": "BL" + }, + { + "name": "Saint Helena, Ascension and Tristan da Cunha", + "code": "SH" + }, + { + "name": "Saint Kitts and Nevis", + "code": "KN" + }, + { + "name": "Saint Lucia", + "code": "LC" + }, + { + "name": "Saint Martin", + "code": "MF" + }, + { + "name": "Saint Pierre and Miquelon", + "code": "PM" + }, + { + "name": "Saint Vincent and the Grenadines", + "code": "VC" + }, + { + "name": "Samoa", + "code": "WS" + }, + { + "name": "San Marino", + "code": "SM" + }, + { + "name": "Saudi Arabia", + "code": "SA" + }, + { + "name": "Senegal", + "code": "SN" + }, + { + "name": "Serbia", + "code": "RS" + }, + { + "name": "Seychelles", + "code": "SC" + }, + { + "name": "Sierra Leone", + "code": "SL" + }, + { + "name": "Singapore", + "code": "SG" + }, + { + "name": "Sint Maarten", + "code": "SX" + }, + { + "name": "Slovakia", + "code": "SK" + }, + { + "name": "Slovenia", + "code": "SI" + }, + { + "name": "Solomon Islands", + "code": "SB" + }, + { + "name": "Somalia", + "code": "SO" + }, + { + "name": "South Africa", + "code": "ZA" + }, + { + "name": "South Georgia", + "code": "GS" + }, + { + "name": "South Korea", + "code": "KR" + }, + { + "name": "South Sudan", + "code": "SS" + }, + { + "name": "Spain", + "code": "ES" + }, + { + "name": "Sri Lanka", + "code": "LK" + }, + { + "name": "Sudan", + "code": "SD" + }, + { + "name": "Suriname", + "code": "SR" + }, + { + "name": "Svalbard and Jan Mayen", + "code": "SJ" + }, + { + "name": "Sweden", + "code": "SE" + }, + { + "name": "Switzerland", + "code": "CH" + }, + { + "name": "Syria", + "code": "SY" + }, + { + "name": "São Tomé and Príncipe", + "code": "ST" + }, + { + "name": "Taiwan", + "code": "TW" + }, + { + "name": "Tajikistan", + "code": "TJ" + }, + { + "name": "Tanzania", + "code": "TZ" + }, + { + "name": "Thailand", + "code": "TH" + }, + { + "name": "Timor-Leste", + "code": "TL" + }, + { + "name": "Togo", + "code": "TG" + }, + { + "name": "Tokelau", + "code": "TK" + }, + { + "name": "Tonga", + "code": "TO" + }, + { + "name": "Trinidad and Tobago", + "code": "TT" + }, + { + "name": "Tunisia", + "code": "TN" + }, + { + "name": "Turkey", + "code": "TR" + }, + { + "name": "Turkmenistan", + "code": "TM" + }, + { + "name": "Turks and Caicos Islands", + "code": "TC" + }, + { + "name": "Tuvalu", + "code": "TV" + }, + { + "name": "Uganda", + "code": "UG" + }, + { + "name": "Ukraine", + "code": "UA" + }, + { + "name": "United Arab Emirates", + "code": "AE" + }, + { + "name": "United Kingdom", + "code": "GB" + }, + { + "name": "United States", + "code": "US" + }, + { + "name": "United States Minor Outlying Islands", + "code": "UM" + }, + { + "name": "United States Virgin Islands", + "code": "VI" + }, + { + "name": "Uruguay", + "code": "UY" + }, + { + "name": "Uzbekistan", + "code": "UZ" + }, + { + "name": "Vanuatu", + "code": "VU" + }, + { + "name": "Vatican City", + "code": "VA" + }, + { + "name": "Venezuela", + "code": "VE" + }, + { + "name": "Vietnam", + "code": "VN" + }, + { + "name": "Wallis and Futuna", + "code": "WF" + }, + { + "name": "Western Sahara", + "code": "EH" + }, + { + "name": "Yemen", + "code": "YE" + }, + { + "name": "Zambia", + "code": "ZM" + }, + { + "name": "Zimbabwe", + "code": "ZW" + }, + { + "name": "Åland Islands", + "code": "AX" + } +] diff --git a/frontend/src/page/CreateInvoice.jsx b/frontend/src/page/CreateInvoice.jsx index c439eaa7..ff8135fb 100644 --- a/frontend/src/page/CreateInvoice.jsx +++ b/frontend/src/page/CreateInvoice.jsx @@ -45,6 +45,7 @@ import { ERC20_ABI } from "@/contractsABI/ERC20_ABI"; import WalletConnectionAlert from "../components/WalletConnectionAlert"; import TokenPicker, { ToggleSwitch } from "@/components/TokenPicker"; import { CopyButton } from "@/components/ui/copyButton"; +import CountryPicker from "@/components/CountryPicker"; function CreateInvoice() { const { data: walletClient } = useWalletClient(); @@ -58,6 +59,8 @@ function CreateInvoice() { const [searchParams] = useSearchParams(); const [clientAddress, setClientAddress] = useState(""); + const [userCountry, setUserCountry] = useState(""); + const [clientCountry, setClientCountry] = useState(""); // Token selection state const [selectedToken, setSelectedToken] = useState(null); @@ -370,14 +373,14 @@ function CreateInvoice() { userFname: formData.get("userFname"), userLname: formData.get("userLname"), userEmail: formData.get("userEmail"), - userCountry: formData.get("userCountry"), + userCountry: userCountry || formData.get("userCountry") || "", userCity: formData.get("userCity"), userPostalcode: formData.get("userPostalcode"), clientAddress: formData.get("clientAddress"), clientFname: formData.get("clientFname"), clientLname: formData.get("clientLname"), clientEmail: formData.get("clientEmail"), - clientCountry: formData.get("clientCountry"), + clientCountry: clientCountry || formData.get("clientCountry") || "", clientCity: formData.get("clientCity"), clientPostalcode: formData.get("clientPostalcode"), itemData, @@ -542,12 +545,20 @@ function CreateInvoice() { - +
+ + +
@@ -632,12 +643,20 @@ function CreateInvoice() { - +
+ + +
diff --git a/frontend/src/page/CreateInvoicesBatch.jsx b/frontend/src/page/CreateInvoicesBatch.jsx index cbd46335..e599bc0a 100644 --- a/frontend/src/page/CreateInvoicesBatch.jsx +++ b/frontend/src/page/CreateInvoicesBatch.jsx @@ -53,6 +53,7 @@ import { ERC20_ABI } from "@/contractsABI/ERC20_ABI"; import WalletConnectionAlert from "../components/WalletConnectionAlert"; import TokenPicker, { ToggleSwitch } from "@/components/TokenPicker"; import { CopyButton } from "@/components/ui/copyButton"; +import CountryPicker from "@/components/CountryPicker"; function CreateInvoicesBatch() { const { data: walletClient } = useWalletClient(); @@ -717,17 +718,20 @@ function CreateInvoicesBatch() { - - setUserInfo((prev) => ({ - ...prev, - userCountry: e.target.value, - })) - } - /> +
+ + setUserInfo((prev) => ({ + ...prev, + userCountry: value, + })) + } + placeholder="Select country" + className="w-full border-gray-300 text-black" + disabled={loading} + /> +
@@ -1037,18 +1041,21 @@ function CreateInvoicesBatch() { - - updateInvoiceRow( - rowIndex, - "clientCountry", - e.target.value - ) - } - /> +
+ + updateInvoiceRow( + rowIndex, + "clientCountry", + value + ) + } + placeholder="Select country" + className="w-full border-gray-300 text-black" + disabled={loading} + /> +