diff --git a/app/api/privacy-policy/route.js b/app/api/privacy-policy/route.js index cacc09d..2ba4c84 100644 --- a/app/api/privacy-policy/route.js +++ b/app/api/privacy-policy/route.js @@ -43,7 +43,7 @@ export function GET() {

We may occasionally update this Privacy Policy, and we encourage you to periodically review it to stay informed about how we are protecting your information. We will post any changes on this page, and if the changes are significant, we will provide a more prominent notice.

How To Contact Us

-

If you have any questions or comments about this Privacy Policy, or if you would like us to update information we have about you or your preferences, please contact us by email at contact@nepalirudraksha.com.

+

If you have any questions or comments about this Privacy Policy, or if you would like us to update information we have about you or your preferences, please contact us by email at nepali.rudrakshabeads@gmail.com.

Your privacy is of utmost importance to us. We commit to safeguarding your personal information in accordance with legal obligations. By using our app, you are accepting the practices outlined in this Privacy Policy. If you do not agree to the terms of this Privacy Policy, please refrain from using our app.

diff --git a/app/contexts/currencyContext.js b/app/contexts/currencyContext.js index 71c03e8..72b48db 100644 --- a/app/contexts/currencyContext.js +++ b/app/contexts/currencyContext.js @@ -1,5 +1,7 @@ "use client"; import React, { createContext, useContext, useState, useEffect } from "react"; +import axios from 'axios'; + const CurrencyContext = createContext(); @@ -15,6 +17,7 @@ export const CurrencyProvider = ({ children }) => { const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); + const fetchExchangeRates = async () => { try { setIsLoading(true); @@ -23,60 +26,74 @@ export const CurrencyProvider = ({ children }) => { const cachedData = localStorage.getItem("exchangeRates"); const cached = cachedData ? JSON.parse(cachedData) : null; - if ( - cached && - new Date().getTime() - cached.timestamp < 24 * 60 * 60 * 1000 - ) { + if (cached && new Date().getTime() - cached.timestamp < 24 * 60 * 60 * 1000) { setExchangeRates(cached.rates); setIsLoading(false); return; } - const currencies = Object.keys(SUPPORTED_CURRENCIES) - .filter((key) => key !== "INR") - .join(","); + try { + const currencies = Object.keys(SUPPORTED_CURRENCIES) + .filter((key) => key !== "INR") + .join(","); - const response = await fetch( - `https://apilayer.net/api/live?access_key=9bcb30907dee1cda9866f7b49f0f8def¤cies=${currencies}&source=INR&format=1` - ); + const response = await axios.get("https://apilayer.net/api/live", { + params: { + access_key: "9bcb30907dee1cda9866f7b49f0f8def", + currencies: currencies, + source: "INR", + format: 1, + }, + }); - if (!response.ok) { - throw new Error("Failed to fetch exchange rates"); + const data = response.data; + + if (!data.success) { + throw new Error(data.error?.info || "API request failed"); + } + + const rates = Object.keys(SUPPORTED_CURRENCIES).reduce( + (acc, currency) => { + if (currency === "INR") { + acc[currency] = 1; + } else { + const rate = data.quotes?.[`INR${currency}`]; + if (!rate) { + throw new Error(`Rate not found for ${currency}`); + } + acc[currency] = rate; + } + return acc; + }, + {} + ); + + 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); + } } - const data = await response.json(); - if (!data.success) { - throw new Error(data.error?.info || "API request failed"); - } - - const rates = Object.keys(SUPPORTED_CURRENCIES).reduce( - (acc, currency) => { - if (currency === "INR") { - acc[currency] = 1; - } else { - const rate = data.quotes?.[`INR${currency}`]; - acc[currency] = rate || null; - } - return acc; - }, - {} - ); - - const ratesData = { - rates, - timestamp: data.timestamp * 1000, - }; - - localStorage.setItem("exchangeRates", JSON.stringify(ratesData)); - setExchangeRates(rates); - } catch (err) { - setError("Error fetching exchange rates"); - console.error("Exchange rate fetch error:", err); - } finally { + 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; diff --git a/components/common/CurrencyTooltip.jsx b/components/common/CurrencyTooltip.jsx new file mode 100644 index 0000000..49acf83 --- /dev/null +++ b/components/common/CurrencyTooltip.jsx @@ -0,0 +1,25 @@ +// Create a new component for currency conversion explanation +import React from 'react'; +import { Info } from 'lucide-react'; +import { useCurrency } from '@/app/contexts/currencyContext'; + +const CurrencyTooltip = () => { + const { selectedCurrency, SUPPORTED_CURRENCIES } = useCurrency(); + + // Only show for non-INR currencies + if (selectedCurrency === 'INR') return null; + + return ( +
+ +
+ Prices are converted from Indian Rupees (₹) to {SUPPORTED_CURRENCIES[selectedCurrency]?.name} + ({SUPPORTED_CURRENCIES[selectedCurrency]?.symbol}) based on current exchange rates. + Actual charges may vary at checkout. +
+
+ ); +}; + +export default CurrencyTooltip; diff --git a/components/common/Price.jsx b/components/common/Price.jsx new file mode 100644 index 0000000..e83fadf --- /dev/null +++ b/components/common/Price.jsx @@ -0,0 +1,57 @@ +"use client"; +import React from 'react'; +import { useCurrency } from '@/app/contexts/currencyContext'; + +const Price = ({ amount, className = "" }) => { + const { selectedCurrency, exchangeRates, isLoading, error, SUPPORTED_CURRENCIES } = useCurrency(); + + const convertPrice = (priceInINR) => { + if (!priceInINR || isNaN(priceInINR)) return 0; + + if (selectedCurrency === 'INR') return priceInINR; + + if (!exchangeRates || isLoading) { + return priceInINR; + } + + const rate = exchangeRates[selectedCurrency]; + + if (rate) { + const convertedPrice = priceInINR * rate; + return convertedPrice.toFixed(2); + } + + return priceInINR; + }; + + const formatPrice = (price) => { + const symbol = SUPPORTED_CURRENCIES[selectedCurrency]?.symbol || '₹'; + const convertedPrice = convertPrice(price); + + if (error && selectedCurrency !== 'INR') { + return `${symbol} ${convertedPrice} (approx)`; + } + + switch (selectedCurrency) { + case 'MYR': + return `${symbol} ${convertedPrice}`; + case 'NPR': + return `${symbol} ${convertedPrice}`; + case 'INR': + default: + return `${symbol} ${price}`; + } + }; + + if (isLoading && selectedCurrency !== 'INR') { + return Loading price...; + } + + return ( + + {formatPrice(amount)} + + ); +}; + +export default Price; diff --git a/components/dynamic-navbar/currencySelect.jsx b/components/dynamic-navbar/currencySelect.jsx index 4fc975d..60d4c6b 100644 --- a/components/dynamic-navbar/currencySelect.jsx +++ b/components/dynamic-navbar/currencySelect.jsx @@ -1,43 +1,88 @@ -import React, { useState } from 'react'; -import { ChevronDown } from 'lucide-react'; +import React, { useState, useEffect } from 'react'; +import { ChevronDown, AlertCircle } from 'lucide-react'; -const CurrencySelect = ({ selectedCurrency, setSelectedCurrency, SUPPORTED_CURRENCIES }) => { +const CurrencySelect = ({ selectedCurrency, setSelectedCurrency, SUPPORTED_CURRENCIES, error }) => { const [isOpen, setIsOpen] = useState(false); + const [isMobile, setIsMobile] = useState(false); + const [selectedCurrencyName, setSelectedCurrencyName] = useState(''); + + useEffect(() => { + const savedCurrency = localStorage.getItem('selectedCurrency'); + if (savedCurrency && SUPPORTED_CURRENCIES[savedCurrency]) { + setSelectedCurrency(savedCurrency); + } + + setSelectedCurrencyName(SUPPORTED_CURRENCIES[selectedCurrency]?.country || ''); + + const checkMobile = () => { + setIsMobile(window.innerWidth < 768); + }; + + checkMobile(); + window.addEventListener('resize', checkMobile); + + return () => window.removeEventListener('resize', checkMobile); + }, [SUPPORTED_CURRENCIES, setSelectedCurrency]); + + useEffect(() => { + setSelectedCurrencyName(SUPPORTED_CURRENCIES[selectedCurrency]?.country || ''); + }, [selectedCurrency, SUPPORTED_CURRENCIES]); + + const handleCurrencyChange = (code) => { + localStorage.setItem('selectedCurrency', code); + + setSelectedCurrency(code); + setSelectedCurrencyName(SUPPORTED_CURRENCIES[code]?.country || ''); + setIsOpen(false); + }; return ( -
+
{isOpen && (
- {Object.entries(SUPPORTED_CURRENCIES).map(([code, { country }]) => ( + {Object.entries(SUPPORTED_CURRENCIES).map(([code, { country, symbol }]) => ( ))} @@ -47,5 +92,4 @@ const CurrencySelect = ({ selectedCurrency, setSelectedCurrency, SUPPORTED_CURRE
); }; - export default CurrencySelect; \ No newline at end of file diff --git a/components/header/Navbar.jsx b/components/header/Navbar.jsx index 384600c..0c4f47a 100644 --- a/components/header/Navbar.jsx +++ b/components/header/Navbar.jsx @@ -18,30 +18,42 @@ const Navbar = () => { const { category } = useContext(ProductContext); const { token } = useContext(MainContext); const [cartItemCount, setCartItemCount] = useState(0); - const { selectedCurrency, setSelectedCurrency, SUPPORTED_CURRENCIES } = + const { selectedCurrency, setSelectedCurrency, SUPPORTED_CURRENCIES, error } = useCurrency(); const toggleSidebar = () => setIsSidebarOpen(!isSidebarOpen); useEffect(() => { const handleScroll = () => { - setIsScrolled(window.scrollY > 60); + const isMobile = window.innerWidth < 768; + + setIsScrolled(isMobile || window.scrollY > 60); }; + handleScroll(); + window.addEventListener("scroll", handleScroll); - return () => window.removeEventListener("scroll", handleScroll); + window.addEventListener("resize", handleScroll); + + return () => { + window.removeEventListener("scroll", handleScroll); + window.removeEventListener("resize", handleScroll); + }; }, []); const getCart = async () => { try { const response = await authAxios.get("/orders/cart/"); const cartData = response.data; - console.log(cartData) - console.log(cartData.length) - + console.log(cartData); + console.log(cartData.length); + // Calculate total items in cart if (cartData && cartData.length > 0 && cartData[0].items) { - const totalItems = cartData[0].items.reduce((sum, item) => sum + item.quantity, 0); + const totalItems = cartData[0].items.reduce( + (sum, item) => sum + item.quantity, + 0 + ); setCartItemCount(totalItems); } else { setCartItemCount(0); @@ -66,10 +78,10 @@ const Navbar = () => { if (token) { // Initial fetch getCart(); - + // Set up interval to check for updates every 3 seconds const intervalId = setInterval(getCart, 3000); - + // Clean up interval on component unmount return () => clearInterval(intervalId); } @@ -91,10 +103,9 @@ const Navbar = () => { return ( <> -
-
+
@@ -117,9 +128,9 @@ const Navbar = () => { - - {cartItemCount} - + + {cartItemCount} +
{token ? ( @@ -138,6 +149,7 @@ const Navbar = () => { selectedCurrency={selectedCurrency} setSelectedCurrency={setSelectedCurrency} SUPPORTED_CURRENCIES={SUPPORTED_CURRENCIES} + error={error} />
@@ -169,65 +181,68 @@ const Navbar = () => {
-
-
-

Quick Links

-
    -
  • - - Get a Consultation - -
  • -
  • - - Certification and Guarantee - -
  • -
  • - - Contact Us - -
  • -
  • - - Blogs - -
  • -
-
- -
-

Categories

-
    - {categoryItems.map((item) => ( -
  • +
    +
    +

    Quick Links

    +
      +
    • - {item.label} + Get a Consultation
    • - ))} -
    -
    +
  • + + Certification and Guarantee + +
  • +
  • + + Contact Us + +
  • +
  • + + Blogs + +
  • +
+
+ +
+

Categories

+
    + {categoryItems.map((item) => ( +
  • + + {item.label} + +
  • + ))} +
+
diff --git a/components/hero-page/Hero.jsx b/components/hero-page/Hero.jsx index a5fd7c0..addf962 100644 --- a/components/hero-page/Hero.jsx +++ b/components/hero-page/Hero.jsx @@ -67,7 +67,7 @@ const Hero = ({ data }) => { ]; return ( -
+
{ {heroData.map((item, index) => ( -
+
{`Slide { const response = await axios.get("https://apilayer.net/api/live", { params: { - access_key: "ytguhijok", + access_key: "9bcb30907dee1cda9866f7b49f0f8def", currencies: "USD", source: "INR", format: 1, diff --git a/components/premium-rudraksha/PremiumBanner.jsx b/components/premium-rudraksha/PremiumBanner.jsx index e587c82..e081351 100644 --- a/components/premium-rudraksha/PremiumBanner.jsx +++ b/components/premium-rudraksha/PremiumBanner.jsx @@ -1,6 +1,6 @@ "use client"; -import React, { useState } from "react"; -import { ChevronRight, Gem } from "lucide-react"; +import React, { useState, useEffect, useRef } from "react"; +import { ChevronRight, Gem, Search, ChevronDown } from "lucide-react"; import authAxios from "@/utils/axios"; import Image from "next/image"; @@ -13,14 +13,62 @@ const PremiumBanner = ({ data }) => { header_quote3, header_quote4, } = data || {}; + const [formData, setFormData] = useState({ first_name: "", last_name: "", email: "", phone_number: "", + country: "", }); const [message, setMessage] = useState(""); + const [countries, setCountries] = useState([]); + const [filteredCountries, setFilteredCountries] = useState([]); + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + const [searchTerm, setSearchTerm] = useState(""); + const dropdownRef = useRef(null); + + useEffect(() => { + const fetchCountries = async () => { + try { + const response = await fetch("https://countriesnow.space/api/v0.1/countries/"); + const data = await response.json(); + if (!data.error && data.data) { + setCountries(data.data); + setFilteredCountries(data.data); + } + } catch (error) { + console.error("Error fetching countries:", error); + } + }; + + fetchCountries(); + }, []); + + useEffect(() => { + const handleClickOutside = (event) => { + if (dropdownRef.current && !dropdownRef.current.contains(event.target)) { + setIsDropdownOpen(false); + } + }; + + document.addEventListener("mousedown", handleClickOutside); + return () => { + document.removeEventListener("mousedown", handleClickOutside); + }; + }, []); + + useEffect(() => { + if (searchTerm) { + const filtered = countries.filter(country => + country.country.toLowerCase().includes(searchTerm.toLowerCase()) + ); + setFilteredCountries(filtered); + } else { + setFilteredCountries(countries); + } + }, [searchTerm, countries]); const handleInputChange = (e) => { const { name, value } = e.target; @@ -30,8 +78,24 @@ const PremiumBanner = ({ data }) => { })); }; + const handleCountrySearch = (e) => { + setSearchTerm(e.target.value); + }; + + const selectCountry = (countryName) => { + setFormData(prev => ({ ...prev, country: countryName })); + setIsDropdownOpen(false); + setSearchTerm(""); + }; + const handleSubmit = async (e) => { e.preventDefault(); + + if (!formData.country) { + setMessage("Please select your country."); + return; + } + try { const response = await authAxios.post( "/consultation/booking/create/", @@ -43,6 +107,7 @@ const PremiumBanner = ({ data }) => { last_name: "", email: "", phone_number: "", + country: "", }); setMessage("Consultation experts will contact you shortly."); setTimeout(() => { @@ -61,8 +126,7 @@ const PremiumBanner = ({ data }) => {
- {/* Left: Form Section */} -
+

Book a Free Consultation

@@ -107,9 +171,52 @@ const PremiumBanner = ({ data }) => { className="w-full p-4 border rounded-md focus:ring-[#AC8C6B] focus:border-[#AC8C6B]" required /> + +
+
setIsDropdownOpen(!isDropdownOpen)} + > + + {formData.country || "Select Country"} + + +
+ + {isDropdownOpen && ( +
+
+
+ + +
+
+ + {filteredCountries.length > 0 ? ( + filteredCountries.map((country, index) => ( +
selectCountry(country.country)} + > + {country.country} +
+ )) + ) : ( +
No countries found
+ )} +
+ )} +
{message && ( -

+

{message}

)} @@ -123,7 +230,6 @@ const PremiumBanner = ({ data }) => {
- {/* Right: Benefits & App Download */}

{header1 ?? "Why Choose Our Consultation?"} @@ -149,7 +255,7 @@ const PremiumBanner = ({ data }) => {
  • - {header_quote3 ?? "Free initial text-based consultation"} + {header_quote4 ?? "Free initial text-based consultation"}
  • diff --git a/components/search/searchComponent.jsx b/components/search/searchComponent.jsx index 22c39d7..fb6b7b5 100644 --- a/components/search/searchComponent.jsx +++ b/components/search/searchComponent.jsx @@ -120,7 +120,7 @@ const SearchComponent = ({ isScrolled }) => { }; return ( -
    +
    {isScrolled ? ( <>