356 lines
14 KiB
JavaScript
356 lines
14 KiB
JavaScript
"use client";
|
||
import React, { createContext, useContext, useState, useEffect } from "react";
|
||
import axios from 'axios';
|
||
|
||
|
||
const CurrencyContext = createContext();
|
||
|
||
export const SUPPORTED_CURRENCIES = {
|
||
INR: { symbol: "₹", name: "Indian Rupee", country: "India" },
|
||
MYR: { symbol: "RM", name: "Malaysian Ringgit", country: "Malaysia" },
|
||
NPR: { symbol: "रू", name: "Nepalese Rupee", country: "Nepal" },
|
||
ERN: { symbol: "Nfk", name: "Eritrean nakfa", country: "Eritrea" },
|
||
XAF: { symbol: "Fr", name: "Central African CFA franc", country: "Cameroon" },
|
||
EUR: { symbol: "€", name: "Euro", country: "Montenegro" },
|
||
FJD: { symbol: "$", name: "Fijian dollar", country: "Fiji" },
|
||
TND: { symbol: "د.ت", name: "Tunisian dinar", country: "Tunisia" },
|
||
XOF: { symbol: "Fr", name: "West African CFA franc", country: "Senegal" },
|
||
GBP: { symbol: "£", name: "British pound", country: "South Georgia" },
|
||
BRL: { symbol: "R$", name: "Brazilian real", country: "Brazil" },
|
||
CHF: { symbol: "Fr", name: "Swiss franc", country: "Liechtenstein" },
|
||
MXN: { symbol: "$", name: "Mexican peso", country: "Mexico" },
|
||
EGP: { symbol: "E£", name: "Egyptian pound", country: "Palestine" },
|
||
DKK: { symbol: "kr.", name: "krone", country: "Greenland" },
|
||
USD: { symbol: "$", name: "United States dollar", country: "United States Minor Outlying Islands" },
|
||
TRY: { symbol: "₺", name: "Turkish lira", country: "Turkey" },
|
||
HNL: { symbol: "L", name: "Honduran lempira", country: "Honduras" },
|
||
RON: { symbol: "lei", name: "Romanian leu", country: "Romania" },
|
||
NAD: { symbol: "$", name: "Namibian dollar", country: "Namibia" },
|
||
RWF: { symbol: "Fr", name: "Rwandan franc", country: "Rwanda" },
|
||
XCD: { symbol: "$", name: "Eastern Caribbean dollar", country: "Saint Vincent and the Grenadines" },
|
||
BGN: { symbol: "лв", name: "Bulgarian lev", country: "Bulgaria" },
|
||
COP: { symbol: "$", name: "Colombian peso", country: "Colombia" },
|
||
SRD: { symbol: "$", name: "Surinamese dollar", country: "Suriname" },
|
||
ZWL: { symbol: "$", name: "Zimbabwean dollar", country: "Zimbabwe" },
|
||
VES: { symbol: "Bs.S.", name: "Venezuelan bolívar soberano", country: "Venezuela" },
|
||
SZL: { symbol: "L", name: "Swazi lilangeni", country: "Eswatini" },
|
||
PHP: { symbol: "₱", name: "Philippine peso", country: "Philippines" },
|
||
TMT: { symbol: "m", name: "Turkmenistan manat", country: "Turkmenistan" },
|
||
DZD: { symbol: "د.ج", name: "Algerian dinar", country: "Algeria" },
|
||
JMD: { symbol: "$", name: "Jamaican dollar", country: "Jamaica" },
|
||
AMD: { symbol: "֏", name: "Armenian dram", country: "Armenia" },
|
||
SBD: { symbol: "$", name: "Solomon Islands dollar", country: "Solomon Islands" },
|
||
CUC: { symbol: "$", name: "Cuban convertible peso", country: "Cuba" },
|
||
LRD: { symbol: "$", name: "Liberian dollar", country: "Liberia" },
|
||
SDG: { symbol: "ج.س", name: "Sudanese pound", country: "Sudan" },
|
||
TZS: { symbol: "Sh", name: "Tanzanian shilling", country: "Tanzania" },
|
||
BZD: { symbol: "$", name: "Belize dollar", country: "Belize" },
|
||
BBD: { symbol: "$", name: "Barbadian dollar", country: "Barbados" },
|
||
LBP: { symbol: "ل.ل", name: "Lebanese pound", country: "Lebanon" },
|
||
MDL: { symbol: "L", name: "Moldovan leu", country: "Moldova" },
|
||
TWD: { symbol: "$", name: "New Taiwan dollar", country: "Taiwan" },
|
||
VND: { symbol: "₫", name: "Vietnamese đồng", country: "Vietnam" },
|
||
XPF: { symbol: "₣", name: "CFP franc", country: "French Polynesia" },
|
||
NZD: { symbol: "$", name: "New Zealand dollar", country: "Tokelau" },
|
||
UGX: { symbol: "Sh", name: "Ugandan shilling", country: "Uganda" },
|
||
ANG: { symbol: "ƒ", name: "Netherlands Antillean guilder", country: "Sint Maarten" },
|
||
KZT: { symbol: "₸", name: "Kazakhstani tenge", country: "Kazakhstan" },
|
||
SSP: { symbol: "£", name: "South Sudanese pound", country: "South Sudan" },
|
||
TJS: { symbol: "ЅМ", name: "Tajikistani somoni", country: "Tajikistan" },
|
||
CVE: { symbol: "Esc", name: "Cape Verdean escudo", country: "Cape Verde" },
|
||
MVR: { symbol: ".ރ", name: "Maldivian rufiyaa", country: "Maldives" },
|
||
YER: { symbol: "﷼", name: "Yemeni rial", country: "Yemen" },
|
||
KPW: { symbol: "₩", name: "North Korean won", country: "North Korea" },
|
||
GEL: { symbol: "₾", name: "lari", country: "Georgia" },
|
||
UZS: { symbol: "so'm", name: "Uzbekistani soʻm", country: "Uzbekistan" },
|
||
DOP: { symbol: "$", name: "Dominican peso", country: "Dominican Republic" },
|
||
MZN: { symbol: "MT", name: "Mozambican metical", country: "Mozambique" },
|
||
THB: { symbol: "฿", name: "Thai baht", country: "Thailand" },
|
||
ILS: { symbol: "₪", name: "Israeli new shekel", country: "Israel" },
|
||
FKP: { symbol: "£", name: "Falkland Islands pound", country: "Falkland Islands" },
|
||
IDR: { symbol: "Rp", name: "Indonesian rupiah", country: "Indonesia" },
|
||
PGK: { symbol: "K", name: "Papua New Guinean kina", country: "Papua New Guinea" },
|
||
BHD: { symbol: ".د.ب", name: "Bahraini dinar", country: "Bahrain" },
|
||
MOP: { symbol: "P", name: "Macanese pataca", country: "Macau" },
|
||
SYP: { symbol: "£", name: "Syrian pound", country: "Syria" },
|
||
PLN: { symbol: "zł", name: "Polish złoty", country: "Poland" },
|
||
ZMW: { symbol: "ZK", name: "Zambian kwacha", country: "Zambia" },
|
||
AUD: { symbol: "$", name: "Australian dollar", country: "Kiribati" },
|
||
PEN: { symbol: "S/ ", name: "Peruvian sol", country: "Peru" },
|
||
UYU: { symbol: "$", name: "Uruguayan peso", country: "Uruguay" },
|
||
IRR: { symbol: "﷼", name: "Iranian rial", country: "Iran" },
|
||
HUF: { symbol: "Ft", name: "Hungarian forint", country: "Hungary" },
|
||
WST: { symbol: "T", name: "Samoan tālā", country: "Samoa" },
|
||
NGN: { symbol: "₦", name: "Nigerian naira", country: "Nigeria" },
|
||
IQD: { symbol: "ع.د", name: "Iraqi dinar", country: "Iraq" },
|
||
KWD: { symbol: "د.ك", name: "Kuwaiti dinar", country: "Kuwait" },
|
||
CZK: { symbol: "Kč", name: "Czech koruna", country: "Czechia" },
|
||
MAD: { symbol: "د.م.", name: "Moroccan dirham", country: "Morocco" }
|
||
};
|
||
|
||
export const CurrencyProvider = ({ children }) => {
|
||
const [selectedCurrency, setSelectedCurrency] = useState("INR");
|
||
useEffect(() => {
|
||
const fetchCurrencyForCountry = async () => {
|
||
try {
|
||
let userCountry = localStorage.getItem('userCountry') || "India";
|
||
|
||
const response = await axios.get(`https://restcountries.com/v3.1/name/${userCountry}`);
|
||
if (response.data && response.data.length > 0) {
|
||
const countryData = response.data[0];
|
||
|
||
if (countryData.currencies) {
|
||
const currencyCode = Object.keys(countryData.currencies)[0];
|
||
|
||
if (currencyCode) {
|
||
setSelectedCurrency(currencyCode);
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error("Error fetching currency for country:", error);
|
||
setSelectedCurrency("INR")
|
||
}
|
||
};
|
||
|
||
fetchCurrencyForCountry();
|
||
}, []);
|
||
const [exchangeRates, setExchangeRates] = useState(null);
|
||
const [isLoading, setIsLoading] = useState(true);
|
||
const [error, setError] = useState(null);
|
||
const [userCountry, setUserCountry] = useState(null);
|
||
const [allCountries, setAllCountries] = useState([]);
|
||
const [countryCurrencyMap, setCountryCurrencyMap] = useState({});
|
||
|
||
// Fetch all countries and their currencies
|
||
const fetchCountriesAndCurrencies = async () => {
|
||
try {
|
||
// First, get the list of countries
|
||
const countriesResponse = await fetch('https://countriesnow.space/api/v0.1/countries/');
|
||
const countriesData = await countriesResponse.json();
|
||
|
||
if (!countriesData.error) {
|
||
setAllCountries(countriesData.data);
|
||
|
||
const currencyResponse = await fetch('https://restcountries.com/v3.1/all?fields=name,currencies');
|
||
const currencyData = await currencyResponse.json();
|
||
|
||
const currencyMap = {};
|
||
const currencyDetails = {};
|
||
|
||
currencyData.forEach(country => {
|
||
const countryName = country.name.common;
|
||
if (country.currencies) {
|
||
const currencyCode = Object.keys(country.currencies)[0];
|
||
if (currencyCode) {
|
||
currencyMap[countryName] = currencyCode;
|
||
|
||
if (!currencyDetails[currencyCode]) {
|
||
const currencyInfo = country.currencies[currencyCode];
|
||
currencyDetails[currencyCode] = {
|
||
symbol: currencyInfo.symbol || currencyCode,
|
||
name: currencyInfo.name || `${currencyCode} Currency`,
|
||
country: countryName
|
||
};
|
||
}
|
||
}
|
||
}
|
||
});
|
||
|
||
Object.keys(currencyDetails).forEach(code => {
|
||
if (!SUPPORTED_CURRENCIES[code]) {
|
||
SUPPORTED_CURRENCIES[code] = currencyDetails[code];
|
||
}
|
||
});
|
||
|
||
setCountryCurrencyMap(currencyMap);
|
||
|
||
// Check if we have a stored country and set the currency accordingly
|
||
const storedCountry = localStorage.getItem('userCountry');
|
||
if (storedCountry && currencyMap[storedCountry]) {
|
||
const currencyCode = currencyMap[storedCountry];
|
||
if (currencyCode) {
|
||
setSelectedCurrency(currencyCode);
|
||
localStorage.setItem('selectedCurrency', currencyCode);
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error("Error fetching countries and currencies:", error);
|
||
}
|
||
};
|
||
|
||
const fetchExchangeRates = async () => {
|
||
try {
|
||
setIsLoading(true);
|
||
setError(null);
|
||
|
||
const cachedData = localStorage.getItem("exchangeRates");
|
||
const cached = cachedData ? JSON.parse(cachedData) : null;
|
||
if (cached && new Date().getTime() - cached.timestamp < 24 * 60 * 60 * 1000) {
|
||
setExchangeRates(cached.rates);
|
||
setIsLoading(false);
|
||
return;
|
||
}
|
||
|
||
try {
|
||
const response = await axios.get(`https://open.er-api.com/v6/latest/${selectedCurrency}`);
|
||
const data = response.data;
|
||
|
||
if (!data.rates) {
|
||
throw new Error("API request failed - no rates returned");
|
||
}
|
||
|
||
|
||
const rates = {};
|
||
rates["INR"] = 1; // Base currency
|
||
|
||
Object.keys(SUPPORTED_CURRENCIES).forEach(currencyCode => {
|
||
if (currencyCode !== "INR" && data.rates[currencyCode]) {
|
||
rates[currencyCode] = data.rates[currencyCode];
|
||
}
|
||
});
|
||
|
||
localStorage.setItem(
|
||
"exchangeRates",
|
||
JSON.stringify({ rates, timestamp: new Date().getTime() })
|
||
);
|
||
|
||
setExchangeRates(rates);
|
||
} catch (error) {
|
||
console.error("Error fetching exchange rates:", error);
|
||
|
||
setError("Failed to load currency conversion rates. Please try again later.");
|
||
|
||
if (cached) {
|
||
console.log("Using older cached rates as fallback");
|
||
setExchangeRates(cached.rates);
|
||
} else {
|
||
setExchangeRates(null);
|
||
}
|
||
}
|
||
|
||
setIsLoading(false);
|
||
} catch (error) {
|
||
console.error("Error in exchange rate handling:", error);
|
||
setError("Failed to load currency conversion rates");
|
||
setIsLoading(false);
|
||
}
|
||
};
|
||
|
||
const convertPrice = (price) => {
|
||
if (!price || typeof price !== "number") return price;
|
||
if (!exchangeRates || !exchangeRates[selectedCurrency]) return price;
|
||
if (selectedCurrency === "INR") return price;
|
||
|
||
try {
|
||
const rate = exchangeRates[selectedCurrency];
|
||
return rate ? Number((price * rate).toFixed(2)) : price;
|
||
} catch (err) {
|
||
console.error("Price conversion error:", err);
|
||
return price;
|
||
}
|
||
};
|
||
|
||
const formatPrice = (price) => {
|
||
const convertedPrice = convertPrice(price);
|
||
|
||
if (typeof convertedPrice !== "number")
|
||
return `${SUPPORTED_CURRENCIES[selectedCurrency]?.symbol || "$"}0.00`;
|
||
|
||
try {
|
||
return new Intl.NumberFormat(getLocaleFromCurrency(selectedCurrency), {
|
||
style: "currency",
|
||
currency: selectedCurrency,
|
||
}).format(convertedPrice);
|
||
} catch (err) {
|
||
return `${
|
||
SUPPORTED_CURRENCIES[selectedCurrency]?.symbol || "$"
|
||
}${convertedPrice.toFixed(2)}`;
|
||
}
|
||
};
|
||
|
||
const getLocaleFromCurrency = (currency) => {
|
||
const localeMap = {
|
||
INR: "en-IN",
|
||
USD: "en-US",
|
||
GBP: "en-GB",
|
||
EUR: "de-DE",
|
||
JPY: "ja-JP",
|
||
AUD: "en-AU",
|
||
CAD: "en-CA",
|
||
SGD: "en-SG",
|
||
MYR: "ms-MY",
|
||
NPR: "ne-NP",
|
||
IDR: "id-ID",
|
||
THB: "th-TH",
|
||
PHP: "fil-PH",
|
||
VND: "vi-VN",
|
||
KRW: "ko-KR",
|
||
};
|
||
return localeMap[currency] || "en-US";
|
||
};
|
||
|
||
const setUserCurrencyFromCountry = (country) => {
|
||
if (country && countryCurrencyMap[country]) {
|
||
const currencyCode = countryCurrencyMap[country];
|
||
if (currencyCode && SUPPORTED_CURRENCIES[currencyCode]) {
|
||
setSelectedCurrency(currencyCode);
|
||
localStorage.setItem('selectedCurrency', currencyCode);
|
||
}
|
||
}
|
||
};
|
||
|
||
useEffect(() => {
|
||
const storedCountry = localStorage.getItem('userCountry');
|
||
if (storedCountry) {
|
||
setUserCountry(storedCountry);
|
||
}
|
||
|
||
fetchCountriesAndCurrencies();
|
||
fetchExchangeRates();
|
||
|
||
const savedCurrency = localStorage.getItem('selectedCurrency');
|
||
if (savedCurrency && SUPPORTED_CURRENCIES[savedCurrency]) {
|
||
setSelectedCurrency(savedCurrency);
|
||
}
|
||
|
||
const interval = setInterval(fetchExchangeRates, 24 * 60 * 60 * 1000);
|
||
return () => clearInterval(interval);
|
||
}, []);
|
||
|
||
useEffect(() => {
|
||
if (userCountry && countryCurrencyMap[userCountry]) {
|
||
setUserCurrencyFromCountry(userCountry);
|
||
}
|
||
}, [userCountry, countryCurrencyMap]);
|
||
|
||
const value = {
|
||
selectedCurrency,
|
||
setSelectedCurrency,
|
||
convertPrice,
|
||
formatPrice,
|
||
isLoading,
|
||
error,
|
||
SUPPORTED_CURRENCIES,
|
||
refreshRates: fetchExchangeRates,
|
||
allCountries,
|
||
setUserCountry,
|
||
userCountry
|
||
};
|
||
|
||
return (
|
||
<CurrencyContext.Provider value={value}>
|
||
{children}
|
||
</CurrencyContext.Provider>
|
||
);
|
||
};
|
||
|
||
export const useCurrency = () => {
|
||
const context = useContext(CurrencyContext);
|
||
if (!context) {
|
||
throw new Error('useCurrency must be used within a CurrencyProvider');
|
||
}
|
||
return context;
|
||
};
|