|
@@ -14,11 +14,13 @@ import { Area } from "../services/product/type";
|
|
|
import { accountLogout } from "../features/account/accuntSlice";
|
|
import { accountLogout } from "../features/account/accuntSlice";
|
|
|
import { useSelector } from "react-redux";
|
|
import { useSelector } from "react-redux";
|
|
|
import { setAreas } from "../features/areas/areasSlice";
|
|
import { setAreas } from "../features/areas/areasSlice";
|
|
|
|
|
+import i18n from "../i18n";
|
|
|
|
|
+import { useTranslation } from "react-i18next";
|
|
|
|
|
|
|
|
const Header: React.FC = () => {
|
|
const Header: React.FC = () => {
|
|
|
const navigate = useNavigate();
|
|
const navigate = useNavigate();
|
|
|
const areas = useSelector((state: any) => state.areas.areas);
|
|
const areas = useSelector((state: any) => state.areas.areas);
|
|
|
-
|
|
|
|
|
|
|
+ const { t } = useTranslation();
|
|
|
const location = useLocation();
|
|
const location = useLocation();
|
|
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
|
|
const [isBuySimExpanded, setIsBuySimExpanded] = useState(false);
|
|
const [isBuySimExpanded, setIsBuySimExpanded] = useState(false);
|
|
@@ -27,7 +29,9 @@ const Header: React.FC = () => {
|
|
|
const [isBuySimMegaVisible, setIsBuySimMegaVisible] = useState(false);
|
|
const [isBuySimMegaVisible, setIsBuySimMegaVisible] = useState(false);
|
|
|
const [isGuideMegaVisible, setIsGuideMegaVisible] = useState(false);
|
|
const [isGuideMegaVisible, setIsGuideMegaVisible] = useState(false);
|
|
|
const [isLangMenuOpen, setIsLangMenuOpen] = useState(false);
|
|
const [isLangMenuOpen, setIsLangMenuOpen] = useState(false);
|
|
|
- const [selectedLang, setSelectedLang] = useState<"en" | "vi">("en");
|
|
|
|
|
|
|
+ const lang = localStorage.getItem("lang") || "en";
|
|
|
|
|
+
|
|
|
|
|
+ const [selectedLang, setSelectedLang] = useState<"en" | "vi">(lang);
|
|
|
const [activeDesktopTab, setActiveDesktopTab] = useState<
|
|
const [activeDesktopTab, setActiveDesktopTab] = useState<
|
|
|
"popular" | "region"
|
|
"popular" | "region"
|
|
|
>("popular");
|
|
>("popular");
|
|
@@ -256,7 +260,7 @@ const Header: React.FC = () => {
|
|
|
<div className="absolute top-full left-0 right-0 mt-2 bg-white rounded-2xl shadow-[0_10px_40px_-10px_rgba(0,0,0,0.1)] border border-slate-100 overflow-hidden animate-in fade-in zoom-in-95 duration-200">
|
|
<div className="absolute top-full left-0 right-0 mt-2 bg-white rounded-2xl shadow-[0_10px_40px_-10px_rgba(0,0,0,0.1)] border border-slate-100 overflow-hidden animate-in fade-in zoom-in-95 duration-200">
|
|
|
<div className="max-h-[300px] overflow-y-auto custom-scrollbar p-2">
|
|
<div className="max-h-[300px] overflow-y-auto custom-scrollbar p-2">
|
|
|
<h3 className="text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-2 px-3 pt-2">
|
|
<h3 className="text-[10px] font-bold text-slate-400 uppercase tracking-widest mb-2 px-3 pt-2">
|
|
|
- Most Popular
|
|
|
|
|
|
|
+ {t("mostPopular")}
|
|
|
</h3>
|
|
</h3>
|
|
|
<div className="space-y-0.5">
|
|
<div className="space-y-0.5">
|
|
|
{areasList.length > 0 ? (
|
|
{areasList.length > 0 ? (
|
|
@@ -280,7 +284,7 @@ const Header: React.FC = () => {
|
|
|
</div>
|
|
</div>
|
|
|
<div className="text-right flex items-center space-x-1.5">
|
|
<div className="text-right flex items-center space-x-1.5">
|
|
|
<span className="text-xs text-slate-400 font-medium">
|
|
<span className="text-xs text-slate-400 font-medium">
|
|
|
- from:
|
|
|
|
|
|
|
+ {t("from")}:
|
|
|
</span>
|
|
</span>
|
|
|
<span className="text-sm font-black text-[#EE0434]">
|
|
<span className="text-sm font-black text-[#EE0434]">
|
|
|
{p.minSellPrice.toLocaleString()} {p.curency}
|
|
{p.minSellPrice.toLocaleString()} {p.curency}
|
|
@@ -290,7 +294,7 @@ const Header: React.FC = () => {
|
|
|
))
|
|
))
|
|
|
) : (
|
|
) : (
|
|
|
<div className="text-center py-4 text-slate-400 text-sm font-medium">
|
|
<div className="text-center py-4 text-slate-400 text-sm font-medium">
|
|
|
- No matches found
|
|
|
|
|
|
|
+ {t("noMatchesFound")}
|
|
|
</div>
|
|
</div>
|
|
|
)}
|
|
)}
|
|
|
</div>
|
|
</div>
|
|
@@ -313,7 +317,7 @@ const Header: React.FC = () => {
|
|
|
: "text-slate-700 hover:text-[#EE0434]"
|
|
: "text-slate-700 hover:text-[#EE0434]"
|
|
|
}`}
|
|
}`}
|
|
|
>
|
|
>
|
|
|
- Home
|
|
|
|
|
|
|
+ {t("home")}
|
|
|
</Link>
|
|
</Link>
|
|
|
|
|
|
|
|
<div
|
|
<div
|
|
@@ -329,7 +333,7 @@ const Header: React.FC = () => {
|
|
|
: "text-slate-700 hover:text-[#EE0434]"
|
|
: "text-slate-700 hover:text-[#EE0434]"
|
|
|
}`}
|
|
}`}
|
|
|
>
|
|
>
|
|
|
- Buy SIM{" "}
|
|
|
|
|
|
|
+ {t("buySim")}{" "}
|
|
|
<svg
|
|
<svg
|
|
|
className={`ml-1 w-4 h-4 transition-transform ${
|
|
className={`ml-1 w-4 h-4 transition-transform ${
|
|
|
isBuySimMegaVisible ? "rotate-180" : ""
|
|
isBuySimMegaVisible ? "rotate-180" : ""
|
|
@@ -351,7 +355,7 @@ const Header: React.FC = () => {
|
|
|
<div className="absolute top-full left-1/2 -translate-x-1/2 w-[950px] bg-white rounded-[32px] shadow-[0_30px_60px_-15px_rgba(0,0,0,0.15)] border border-slate-100 mt-0 overflow-hidden flex animate-in fade-in slide-in-from-top-2 duration-300">
|
|
<div className="absolute top-full left-1/2 -translate-x-1/2 w-[950px] bg-white rounded-[32px] shadow-[0_30px_60px_-15px_rgba(0,0,0,0.15)] border border-slate-100 mt-0 overflow-hidden flex animate-in fade-in slide-in-from-top-2 duration-300">
|
|
|
<div className="w-[280px] bg-red-50 p-10 flex flex-col">
|
|
<div className="w-[280px] bg-red-50 p-10 flex flex-col">
|
|
|
<h3 className="text-4xl font-black text-slate-900 mb-4">
|
|
<h3 className="text-4xl font-black text-slate-900 mb-4">
|
|
|
- Buy SIM
|
|
|
|
|
|
|
+ {t("buySim")}
|
|
|
</h3>
|
|
</h3>
|
|
|
<button
|
|
<button
|
|
|
onClick={() => {
|
|
onClick={() => {
|
|
@@ -360,7 +364,7 @@ const Header: React.FC = () => {
|
|
|
}}
|
|
}}
|
|
|
className="text-[#EE0434] font-bold text-xl flex items-center group mb-8"
|
|
className="text-[#EE0434] font-bold text-xl flex items-center group mb-8"
|
|
|
>
|
|
>
|
|
|
- View all{" "}
|
|
|
|
|
|
|
+ {t("viewAll")}{" "}
|
|
|
<svg
|
|
<svg
|
|
|
className="ml-2 w-5 h-5 transition-transform group-hover:translate-x-1"
|
|
className="ml-2 w-5 h-5 transition-transform group-hover:translate-x-1"
|
|
|
fill="none"
|
|
fill="none"
|
|
@@ -386,7 +390,7 @@ const Header: React.FC = () => {
|
|
|
: "bg-slate-50 text-slate-900 hover:bg-slate-100"
|
|
: "bg-slate-50 text-slate-900 hover:bg-slate-100"
|
|
|
}`}
|
|
}`}
|
|
|
>
|
|
>
|
|
|
- Most Popular
|
|
|
|
|
|
|
+ {t("mostPopular")}
|
|
|
</button>
|
|
</button>
|
|
|
<button
|
|
<button
|
|
|
onClick={() => setActiveDesktopTab("region")}
|
|
onClick={() => setActiveDesktopTab("region")}
|
|
@@ -396,7 +400,7 @@ const Header: React.FC = () => {
|
|
|
: "bg-slate-50 text-slate-900 hover:bg-slate-100"
|
|
: "bg-slate-50 text-slate-900 hover:bg-slate-100"
|
|
|
}`}
|
|
}`}
|
|
|
>
|
|
>
|
|
|
- Region
|
|
|
|
|
|
|
+ {t("region")}
|
|
|
</button>
|
|
</button>
|
|
|
</div>
|
|
</div>
|
|
|
<div className="grid grid-cols-4 gap-y-8 gap-x-4">
|
|
<div className="grid grid-cols-4 gap-y-8 gap-x-4">
|
|
@@ -437,7 +441,7 @@ const Header: React.FC = () => {
|
|
|
: "text-slate-700 hover:text-[#EE0434]"
|
|
: "text-slate-700 hover:text-[#EE0434]"
|
|
|
}`}
|
|
}`}
|
|
|
>
|
|
>
|
|
|
- Guide{" "}
|
|
|
|
|
|
|
+ {t("guide")}{" "}
|
|
|
<svg
|
|
<svg
|
|
|
className={`ml-1 w-4 h-4 transition-transform ${
|
|
className={`ml-1 w-4 h-4 transition-transform ${
|
|
|
isGuideMegaVisible ? "rotate-180" : ""
|
|
isGuideMegaVisible ? "rotate-180" : ""
|
|
@@ -483,7 +487,7 @@ const Header: React.FC = () => {
|
|
|
: "text-slate-700 hover:text-[#EE0434]"
|
|
: "text-slate-700 hover:text-[#EE0434]"
|
|
|
}`}
|
|
}`}
|
|
|
>
|
|
>
|
|
|
- News
|
|
|
|
|
|
|
+ {t("news")}
|
|
|
</Link>
|
|
</Link>
|
|
|
|
|
|
|
|
<Link
|
|
<Link
|
|
@@ -494,7 +498,7 @@ const Header: React.FC = () => {
|
|
|
: "text-slate-700 hover:text-[#EE0434]"
|
|
: "text-slate-700 hover:text-[#EE0434]"
|
|
|
}`}
|
|
}`}
|
|
|
>
|
|
>
|
|
|
- Contact
|
|
|
|
|
|
|
+ {t("contact")}
|
|
|
</Link>
|
|
</Link>
|
|
|
</nav>
|
|
</nav>
|
|
|
|
|
|
|
@@ -546,15 +550,17 @@ const Header: React.FC = () => {
|
|
|
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
|
d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
|
|
/>
|
|
/>
|
|
|
</svg>
|
|
</svg>
|
|
|
- <span className="font-bold text-sm">Orders</span>
|
|
|
|
|
|
|
+ <span className="font-bold text-sm">
|
|
|
|
|
+ {t("orders")}
|
|
|
|
|
+ </span>
|
|
|
</button>
|
|
</button>
|
|
|
<div className="h-px bg-slate-50 mx-4 my-1"></div>
|
|
<div className="h-px bg-slate-50 mx-4 my-1"></div>
|
|
|
|
|
|
|
|
<button
|
|
<button
|
|
|
onClick={() => {
|
|
onClick={() => {
|
|
|
|
|
+ setIsUserMenuOpen(false);
|
|
|
dispatch(accountLogout());
|
|
dispatch(accountLogout());
|
|
|
navigate("/login");
|
|
navigate("/login");
|
|
|
- setIsUserMenuOpen(false);
|
|
|
|
|
}}
|
|
}}
|
|
|
className="flex items-center space-x-3 px-6 py-3.5 w-full text-left hover:bg-slate-50 text-slate-700 hover:text-[#EE0434] transition-colors"
|
|
className="flex items-center space-x-3 px-6 py-3.5 w-full text-left hover:bg-slate-50 text-slate-700 hover:text-[#EE0434] transition-colors"
|
|
|
>
|
|
>
|
|
@@ -571,7 +577,9 @@ const Header: React.FC = () => {
|
|
|
d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"
|
|
d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"
|
|
|
/>
|
|
/>
|
|
|
</svg>
|
|
</svg>
|
|
|
- <span className="font-bold text-sm">Logout</span>
|
|
|
|
|
|
|
+ <span className="font-bold text-sm">
|
|
|
|
|
+ {t("logout")}
|
|
|
|
|
+ </span>
|
|
|
</button>
|
|
</button>
|
|
|
</div>
|
|
</div>
|
|
|
)}
|
|
)}
|
|
@@ -579,9 +587,9 @@ const Header: React.FC = () => {
|
|
|
<div className="flex flex-col py-2">
|
|
<div className="flex flex-col py-2">
|
|
|
<button
|
|
<button
|
|
|
onClick={() => {
|
|
onClick={() => {
|
|
|
|
|
+ setIsUserMenuOpen(false);
|
|
|
dispatch(accountLogout());
|
|
dispatch(accountLogout());
|
|
|
navigate("/login");
|
|
navigate("/login");
|
|
|
- setIsUserMenuOpen(false);
|
|
|
|
|
}}
|
|
}}
|
|
|
className="flex items-center space-x-3 px-6 py-3.5 w-full text-left hover:bg-slate-50 text-slate-700 hover:text-[#EE0434] transition-colors"
|
|
className="flex items-center space-x-3 px-6 py-3.5 w-full text-left hover:bg-slate-50 text-slate-700 hover:text-[#EE0434] transition-colors"
|
|
|
>
|
|
>
|
|
@@ -598,7 +606,9 @@ const Header: React.FC = () => {
|
|
|
d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"
|
|
d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"
|
|
|
/>
|
|
/>
|
|
|
</svg>
|
|
</svg>
|
|
|
- <span className="font-bold text-sm">Login</span>
|
|
|
|
|
|
|
+ <span className="font-bold text-sm">
|
|
|
|
|
+ {t("login")}
|
|
|
|
|
+ </span>
|
|
|
</button>
|
|
</button>
|
|
|
</div>
|
|
</div>
|
|
|
)}
|
|
)}
|
|
@@ -647,6 +657,8 @@ const Header: React.FC = () => {
|
|
|
onClick={() => {
|
|
onClick={() => {
|
|
|
setSelectedLang(lang.code as "en" | "vi");
|
|
setSelectedLang(lang.code as "en" | "vi");
|
|
|
setIsLangMenuOpen(false);
|
|
setIsLangMenuOpen(false);
|
|
|
|
|
+ i18n.changeLanguage(lang.code);
|
|
|
|
|
+ localStorage.setItem("lang", lang.code);
|
|
|
}}
|
|
}}
|
|
|
className={`flex items-center space-x-3 px-5 py-4 w-full text-left transition-colors ${
|
|
className={`flex items-center space-x-3 px-5 py-4 w-full text-left transition-colors ${
|
|
|
selectedLang === lang.code
|
|
selectedLang === lang.code
|
|
@@ -763,7 +775,7 @@ const Header: React.FC = () => {
|
|
|
: "text-slate-800 hover:bg-slate-50"
|
|
: "text-slate-800 hover:bg-slate-50"
|
|
|
}`}
|
|
}`}
|
|
|
>
|
|
>
|
|
|
- Home
|
|
|
|
|
|
|
+ {t("home")}
|
|
|
</Link>
|
|
</Link>
|
|
|
|
|
|
|
|
<div className="w-full">
|
|
<div className="w-full">
|
|
@@ -775,7 +787,7 @@ const Header: React.FC = () => {
|
|
|
: "text-slate-800 hover:bg-slate-50"
|
|
: "text-slate-800 hover:bg-slate-50"
|
|
|
}`}
|
|
}`}
|
|
|
>
|
|
>
|
|
|
- <span>Buy SIM</span>
|
|
|
|
|
|
|
+ <span>{t("buySim")}</span>
|
|
|
<svg
|
|
<svg
|
|
|
className={`w-6 h-6 transition-transform duration-300 ${
|
|
className={`w-6 h-6 transition-transform duration-300 ${
|
|
|
isBuySimExpanded ? "rotate-180" : ""
|
|
isBuySimExpanded ? "rotate-180" : ""
|
|
@@ -807,7 +819,7 @@ const Header: React.FC = () => {
|
|
|
}}
|
|
}}
|
|
|
className="col-span-2 text-center py-4 bg-slate-50 rounded-2xl text-[#EE0434] font-black text-sm uppercase tracking-wider shadow-sm"
|
|
className="col-span-2 text-center py-4 bg-slate-50 rounded-2xl text-[#EE0434] font-black text-sm uppercase tracking-wider shadow-sm"
|
|
|
>
|
|
>
|
|
|
- View All Destinations →
|
|
|
|
|
|
|
+ {t("viewAllDestinations")} →
|
|
|
</button>
|
|
</button>
|
|
|
{products.map((c) => (
|
|
{products.map((c) => (
|
|
|
<button
|
|
<button
|
|
@@ -838,7 +850,7 @@ const Header: React.FC = () => {
|
|
|
: "text-slate-800 hover:bg-slate-50"
|
|
: "text-slate-800 hover:bg-slate-50"
|
|
|
}`}
|
|
}`}
|
|
|
>
|
|
>
|
|
|
- <span>Guide</span>
|
|
|
|
|
|
|
+ <span>{t("guide")}</span>
|
|
|
<svg
|
|
<svg
|
|
|
className={`w-6 h-6 transition-transform duration-300 ${
|
|
className={`w-6 h-6 transition-transform duration-300 ${
|
|
|
isGuideExpanded ? "rotate-180" : ""
|
|
isGuideExpanded ? "rotate-180" : ""
|
|
@@ -888,7 +900,7 @@ const Header: React.FC = () => {
|
|
|
: "text-slate-800 hover:bg-slate-50"
|
|
: "text-slate-800 hover:bg-slate-50"
|
|
|
}`}
|
|
}`}
|
|
|
>
|
|
>
|
|
|
- News
|
|
|
|
|
|
|
+ {t("news")}
|
|
|
</Link>
|
|
</Link>
|
|
|
|
|
|
|
|
<Link
|
|
<Link
|
|
@@ -900,7 +912,7 @@ const Header: React.FC = () => {
|
|
|
: "text-slate-800 hover:bg-slate-50"
|
|
: "text-slate-800 hover:bg-slate-50"
|
|
|
}`}
|
|
}`}
|
|
|
>
|
|
>
|
|
|
- Contact
|
|
|
|
|
|
|
+ {t("contact")}
|
|
|
</Link>
|
|
</Link>
|
|
|
|
|
|
|
|
{account !== null && (
|
|
{account !== null && (
|
|
@@ -915,19 +927,24 @@ const Header: React.FC = () => {
|
|
|
: "text-slate-800 hover:bg-slate-50"
|
|
: "text-slate-800 hover:bg-slate-50"
|
|
|
}`}
|
|
}`}
|
|
|
>
|
|
>
|
|
|
- Transaction History
|
|
|
|
|
|
|
+ {t("transactionHistory")}
|
|
|
</button>
|
|
</button>
|
|
|
)}
|
|
)}
|
|
|
|
|
|
|
|
<div className="w-full pt-4">
|
|
<div className="w-full pt-4">
|
|
|
<p className="text-center text-slate-400 font-bold text-xs uppercase tracking-widest mb-4">
|
|
<p className="text-center text-slate-400 font-bold text-xs uppercase tracking-widest mb-4">
|
|
|
- Select Language
|
|
|
|
|
|
|
+ {t("selectLanguage")}
|
|
|
</p>
|
|
</p>
|
|
|
<div className="flex justify-center space-x-4">
|
|
<div className="flex justify-center space-x-4">
|
|
|
{languages.map((lang) => (
|
|
{languages.map((lang) => (
|
|
|
<button
|
|
<button
|
|
|
key={lang.code}
|
|
key={lang.code}
|
|
|
- onClick={() => setSelectedLang(lang.code as "en" | "vi")}
|
|
|
|
|
|
|
+ onClick={() => {
|
|
|
|
|
+ setSelectedLang(lang.code as "en" | "vi");
|
|
|
|
|
+ setIsLangMenuOpen(false);
|
|
|
|
|
+ i18n.changeLanguage(lang.code);
|
|
|
|
|
+ localStorage.setItem("lang", lang.code);
|
|
|
|
|
+ }}
|
|
|
className={`flex items-center space-x-2 px-4 py-2 rounded-2xl transition-all border ${
|
|
className={`flex items-center space-x-2 px-4 py-2 rounded-2xl transition-all border ${
|
|
|
selectedLang === lang.code
|
|
selectedLang === lang.code
|
|
|
? "bg-red-50 border-[#EE0434] text-[#EE0434]"
|
|
? "bg-red-50 border-[#EE0434] text-[#EE0434]"
|
|
@@ -957,7 +974,7 @@ const Header: React.FC = () => {
|
|
|
}}
|
|
}}
|
|
|
className="w-full bg-gradient-to-r from-[#E21c34] to-[#500B28] text-white py-5 rounded-[40px] font-black text-2xl shadow-xl active:scale-[0.98] transition-all flex justify-center"
|
|
className="w-full bg-gradient-to-r from-[#E21c34] to-[#500B28] text-white py-5 rounded-[40px] font-black text-2xl shadow-xl active:scale-[0.98] transition-all flex justify-center"
|
|
|
>
|
|
>
|
|
|
- Login / Register
|
|
|
|
|
|
|
+ {t("login")} / {t("register")}
|
|
|
</Link>
|
|
</Link>
|
|
|
)}
|
|
)}
|
|
|
{account !== null && (
|
|
{account !== null && (
|
|
@@ -969,7 +986,7 @@ const Header: React.FC = () => {
|
|
|
}}
|
|
}}
|
|
|
className="w-full bg-gradient-to-r from-[#E21c34] to-[#500B28] text-white py-5 rounded-[40px] font-black text-2xl shadow-xl active:scale-[0.98] transition-all flex justify-center"
|
|
className="w-full bg-gradient-to-r from-[#E21c34] to-[#500B28] text-white py-5 rounded-[40px] font-black text-2xl shadow-xl active:scale-[0.98] transition-all flex justify-center"
|
|
|
>
|
|
>
|
|
|
- Logout
|
|
|
|
|
|
|
+ {t("logout")}
|
|
|
</Link>
|
|
</Link>
|
|
|
)}
|
|
)}
|
|
|
</div>
|
|
</div>
|