HomeView.tsx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. import React, { useState, useEffect, useCallback } from "react";
  2. import { useNavigate, useSearchParams } from "react-router-dom";
  3. import { SimProduct } from "../../services/types";
  4. import HomeBanner from "./components/HomeBanner";
  5. import HomeTestimonial from "./components/HomeTestimonial";
  6. import HomeProduct from "./components/HomeProduct";
  7. import HomeSearch from "./components/HomeSearch";
  8. import HomeFaq from "./components/HomeFaq";
  9. import { useAppDispatch } from "../../hooks/useRedux";
  10. import { openPopup } from "../../features/popup/popupSlice";
  11. import partner1 from "../../assets/img/partner1.png";
  12. import partner2 from "../../assets/img/partner2.png";
  13. import { authApi } from "../../apis/authApi";
  14. import { accountLogin } from "../../features/account/accuntSlice";
  15. import { useTranslation } from "react-i18next";
  16. import i18n from "../../i18n";
  17. const HomeView: React.FC = () => {
  18. const [simType, setSimType] = useState<"eSIM" | "Physical">("eSIM");
  19. const [searchParams] = useSearchParams();
  20. const navigate = useNavigate();
  21. const dispatch = useAppDispatch();
  22. const { t } = useTranslation();
  23. const langNow = localStorage.getItem("lang") || "en";
  24. useEffect(() => {
  25. // set language in i18n
  26. i18n.changeLanguage(langNow);
  27. localStorage.setItem("lang", langNow);
  28. }, [langNow]);
  29. useEffect(() => {
  30. const params = new URLSearchParams(window.location.search);
  31. console.log("URL Params: ", params.toString());
  32. const status = params.get("vpc_TxnResponseCode");
  33. console.log("searchParams: ", searchParams.toString());
  34. // google callback
  35. const code = searchParams.get("code");
  36. if (status) {
  37. console.log("URL Params:", params);
  38. if (status === "0") {
  39. dispatch(
  40. openPopup({
  41. isSuccess: true,
  42. message: t("paymentSuccess"),
  43. title: t("paymentStatus"),
  44. buttonText: t("close"),
  45. hasRightButton: true,
  46. rightButtonText: t("viewOrder"),
  47. rightButtonAction: "OPEN_ORDER_HISTORY",
  48. }),
  49. );
  50. } else {
  51. dispatch(
  52. openPopup({
  53. isSuccess: false,
  54. message: t("paymentFailed"),
  55. title: t("paymentStatus"),
  56. buttonText: t("close"),
  57. }),
  58. );
  59. }
  60. } else if (code) {
  61. console.log("Handling Google callback with code:", code);
  62. handleGoogleCallback(code);
  63. }
  64. }, []);
  65. const handleGoogleCallback = async (code: string) => {
  66. try {
  67. const response = await authApi.googleCallback({ code });
  68. if (response.errorCode === "0") {
  69. dispatch(accountLogin(response.data));
  70. navigate("/");
  71. } else {
  72. console.error("Google callback failed:", response.message);
  73. }
  74. } catch (error) {
  75. console.error("Google callback error:", error);
  76. }
  77. };
  78. const steps = [
  79. {
  80. number: "1",
  81. title: t("compatibleDevice"),
  82. description: t("checkIfYourPhoneSupportsESIMTechnology"),
  83. },
  84. {
  85. number: "2",
  86. title: t("pickPlan"),
  87. description: t("selectADataPackageForYourDestination"),
  88. },
  89. {
  90. number: "3",
  91. title: t("instantActivation"),
  92. description: t("scanTheQRCodeAndConnectToHighSpeedData"),
  93. },
  94. ];
  95. return (
  96. <div className="bg-white overflow-x-hidden">
  97. <style>{`
  98. @keyframes marquee-up { 0% { transform: translateY(0); } 100% { transform: translateY(-50%); } }
  99. @keyframes marquee-down { 0% { transform: translateY(-50%); } 100% { transform: translateY(0); } }
  100. @keyframes scroll-x { 0% { transform: translateX(0); } 100% { transform: translateX(-33.33%); } }
  101. @keyframes progress-bar { 0% { width: 0%; } 100% { width: 100%; } }
  102. .animate-marquee-up { animation: marquee-up 40s linear infinite; }
  103. .animate-marquee-down { animation: marquee-down 40s linear infinite; }
  104. .animate-scroll-x { animation: scroll-x 10s linear infinite; }
  105. .animate-progress { animation: progress-bar 6s linear infinite; }
  106. `}</style>
  107. {/* Main Search Section */}
  108. <HomeSearch />
  109. {/* Hero Banner */}
  110. <HomeBanner />
  111. {/* Product Selection */}
  112. <HomeProduct />
  113. {/* Customer Testimonials */}
  114. <HomeTestimonial />
  115. {/* WHY CHOOSE INFIGATE */}
  116. <section className="py-16 md:py-24 bg-white px-4">
  117. <div className="max-w-7xl mx-auto">
  118. <h2 className="text-3xl md:text-6xl font-black text-center mb-12 md:mb-20 tracking-tight text-slate-900">
  119. <span className="text-[#EE0434]">{t("chooseGetgo")}</span>
  120. </h2>
  121. <div className="bg-gradient-to-br from-[#E21c34] to-[#500B28] rounded-[32px] md:rounded-[60px] p-8 md:p-16 lg:p-24 shadow-2xl relative overflow-hidden">
  122. <div className="absolute top-0 right-0 w-[500px] h-[500px] bg-white/5 rounded-full blur-[100px] -translate-y-1/2 translate-x-1/2"></div>
  123. <div className="absolute bottom-0 left-0 w-[300px] h-[300px] bg-black/10 rounded-full blur-[80px] translate-y-1/2 -translate-x-1/2"></div>
  124. <div className="grid grid-cols-1 md:grid-cols-2 gap-12 md:gap-x-20 md:gap-y-16 relative z-10">
  125. <div className="space-y-6">
  126. <div className="flex items-center space-x-4">
  127. <div className="w-12 h-12 md:w-16 md:h-16 rounded-full bg-white/20 flex items-center justify-center shrink-0 border border-white/10 shadow-lg backdrop-blur-sm">
  128. <svg
  129. className="w-6 h-6 md:w-8 md:h-8 text-white"
  130. fill="none"
  131. stroke="currentColor"
  132. viewBox="0 0 24 24"
  133. >
  134. <path
  135. strokeLinecap="round"
  136. strokeLinejoin="round"
  137. strokeWidth={2}
  138. d="M13 10V3L4 14h7v7l9-11h-7z"
  139. />
  140. </svg>
  141. </div>
  142. <h3 className="text-lg md:text-2xl font-black text-white leading-tight">
  143. {t("fastCost")}
  144. </h3>
  145. </div>
  146. <ul className="space-y-4 text-sm md:text-lg text-white/90 list-disc pl-6 marker:text-red-200">
  147. <li>{t("internationalTravelStarts")}</li>
  148. <li>{t("easilyChooseSuitableSim")}</li>
  149. <li>{t("eSIMReceiveQR")}</li>
  150. <li>{t("physicalSIMDelivery")}</li>
  151. </ul>
  152. </div>
  153. <div className="space-y-6">
  154. <div className="flex items-center space-x-4">
  155. <div className="w-12 h-12 md:w-16 md:h-16 rounded-full bg-white/20 flex items-center justify-center shrink-0 border border-white/10 shadow-lg backdrop-blur-sm">
  156. <svg
  157. className="w-6 h-6 md:w-8 md:h-8 text-white"
  158. fill="none"
  159. stroke="currentColor"
  160. viewBox="0 0 24 24"
  161. >
  162. <path
  163. strokeLinecap="round"
  164. strokeLinejoin="round"
  165. strokeWidth={2}
  166. d="M21 12a9 9 0 01-9 9m9-9a9 9 0 00-9-9m9 9H3m9 9a9 9 0 01-9-9m9 9c1.657 0 3-4.03 3-9s-1.343-9-3-9m0 18c-1.657 0-3-4.03-3-9s1.343-9 3-9m-9 9a9 9 0 019-9"
  167. />
  168. </svg>
  169. </div>
  170. <h3 className="text-lg md:text-2xl font-black text-white leading-tight">
  171. {t("globalAnywhere")}
  172. </h3>
  173. </div>
  174. <ul className="space-y-4 text-sm md:text-lg text-white/90 list-disc pl-6 marker:text-red-200">
  175. <li>{t("fastestData")}</li>
  176. <li>{t("worldLeadingNetwork")}</li>
  177. <li>{t("flexibleDiversePackages")}</li>
  178. </ul>
  179. </div>
  180. <div className="space-y-6">
  181. <div className="flex items-center space-x-4">
  182. <div className="w-12 h-12 md:w-16 md:h-16 rounded-full bg-white/20 flex items-center justify-center shrink-0 border border-white/10 shadow-lg backdrop-blur-sm">
  183. <svg
  184. className="w-6 h-6 md:w-8 md:h-8 text-white"
  185. fill="none"
  186. stroke="currentColor"
  187. viewBox="0 0 24 24"
  188. >
  189. <path
  190. strokeLinecap="round"
  191. strokeLinejoin="round"
  192. strokeWidth={2}
  193. d="M18.364 5.636l-3.536 3.536m0 5.656l3.536 3.536M9.172 9.172L5.636 5.636m3.536 9.192l-3.536 3.536M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-5 0a4 4 0 11-8 0 4 4 0 018 0z"
  194. />
  195. </svg>
  196. </div>
  197. <h3 className="text-lg md:text-2xl font-black text-white leading-tight">
  198. {t("customerSupport")}
  199. </h3>
  200. </div>
  201. <ul className="space-y-4 text-sm md:text-lg text-white/90 list-disc pl-6 marker:text-red-200">
  202. <li>{t("customerServiceAvailable")}</li>
  203. <li>{t("supportChannels")}</li>
  204. </ul>
  205. </div>
  206. <div className="space-y-6">
  207. <div className="flex items-center space-x-4">
  208. <div className="w-12 h-12 md:w-16 md:h-16 rounded-full bg-white/20 flex items-center justify-center shrink-0 border border-white/10 shadow-lg backdrop-blur-sm">
  209. <svg
  210. className="w-6 h-6 md:w-8 md:h-8 text-white"
  211. fill="none"
  212. stroke="currentColor"
  213. viewBox="0 0 24 24"
  214. >
  215. <path
  216. strokeLinecap="round"
  217. strokeLinejoin="round"
  218. strokeWidth={2}
  219. d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z"
  220. />
  221. </svg>
  222. </div>
  223. <h3 className="text-lg md:text-2xl font-black text-white leading-tight">
  224. {t("customerPolicy")}
  225. </h3>
  226. </div>
  227. <ul className="space-y-4 text-sm md:text-lg text-white/90 list-disc pl-6 marker:text-red-200">
  228. <li>{t("refundDefective")}</li>
  229. <li>{t("commitmentPeaceOfMind")}</li>
  230. </ul>
  231. </div>
  232. </div>
  233. </div>
  234. </div>
  235. </section>
  236. {/* PARTNERSHIP */}
  237. <section className="py-12 md:py-20 bg-white px-4">
  238. <div className="max-w-7xl mx-auto space-y-10 md:space-y-16">
  239. <h2 className="text-3xl md:text-6xl font-black text-center tracking-tight text-slate-900">
  240. SkySimHub <span className="text-[#EE0434]">{t("getgoWith")}</span>
  241. </h2>
  242. <div className="space-y-8 md:space-y-12 max-w-5xl mx-auto">
  243. <div className="space-y-4 md:space-y-6">
  244. <div className="flex items-center space-x-3">
  245. <div className="w-5 h-5 md:w-6 md:h-6 bg-[#EE0434] rounded-full flex items-center justify-center shrink-0">
  246. <svg
  247. className="w-3 h-3 md:w-4 md:h-4 text-white"
  248. fill="currentColor"
  249. viewBox="0 0 20 20"
  250. >
  251. <path
  252. fillRule="evenodd"
  253. d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
  254. clipRule="evenodd"
  255. />
  256. </svg>
  257. </div>
  258. <p className="text-sm md:text-2xl font-semibold text-slate-500">
  259. {t("partnershipNetwork")}
  260. </p>
  261. </div>
  262. <div className="flex flex-wrap gap-3 md:gap-8 justify-center lg:justify-start">
  263. <div className="bg-white border border-[#EE0434]/20 rounded-xl px-4 py-2 w-36 md:w-56 h-14 flex items-center justify-center shadow-sm">
  264. <img
  265. src="https://skysimhub.vn/assets/partners/1.png"
  266. className="max-h-full max-w-full object-contain"
  267. />
  268. </div>
  269. <div className="bg-white border border-[#EE0434]/20 rounded-xl px-4 py-2 w-36 md:w-56 h-14 flex items-center justify-center shadow-sm">
  270. <img
  271. src="https://skysimhub.vn/assets/partners/2.png"
  272. className="max-h-full max-w-full object-contain"
  273. />
  274. </div>
  275. <div className="bg-white border border-[#EE0434]/20 rounded-xl px-4 py-2 w-36 md:w-56 h-14 flex items-center justify-center shadow-sm">
  276. <img
  277. src="https://skysimhub.vn/assets/partners/3.png"
  278. className="max-h-full max-w-full object-contain"
  279. />
  280. </div>
  281. <div className="bg-white border border-[#EE0434]/20 rounded-xl px-4 py-2 w-36 md:w-56 h-14 flex items-center justify-center shadow-sm">
  282. <img
  283. src="https://skysimhub.vn/assets/partners/4.png"
  284. className="max-h-full max-w-full object-contain"
  285. />
  286. </div>
  287. <div className="bg-white border border-[#EE0434]/20 rounded-xl px-4 py-2 w-36 md:w-56 h-14 flex items-center justify-center shadow-sm">
  288. <img
  289. src="https://skysimhub.vn/assets/partners/5.png"
  290. className="max-h-full max-w-full object-contain"
  291. />
  292. </div>
  293. <div className="bg-white border border-[#EE0434]/20 rounded-xl px-4 py-2 w-36 md:w-56 h-14 flex items-center justify-center shadow-sm">
  294. <img
  295. src="https://skysimhub.vn/assets/partners/6.png"
  296. className="max-h-full max-w-full object-contain"
  297. />
  298. </div>
  299. </div>
  300. </div>
  301. </div>
  302. </div>
  303. </section>
  304. {/* How it Works */}
  305. <section className="py-12 md:py-24 bg-white px-4">
  306. <div className="max-w-7xl mx-auto">
  307. <h2 className="text-2xl md:text-6xl font-black text-[#EE0434] text-center mb-6 md:mb-10 tracking-tight leading-tight">
  308. {t("buyExplore")}
  309. </h2>
  310. <div className="flex justify-center mb-10 md:mb-20">
  311. <div className="inline-flex p-1 bg-slate-100 rounded-full shadow-inner">
  312. <button
  313. onClick={() => setSimType("eSIM")}
  314. className={`px-6 md:px-12 py-2.5 md:py-3 rounded-full text-lg md:text-xs lg:text-xl font-bold transition-all ${
  315. simType === "eSIM"
  316. ? "bg-gradient-to-r from-[#E21c34] to-[#500B28] text-white shadow-md"
  317. : "bg-[#f0f0f0] text-[#8b8e96] hover:text-slate-800"
  318. }`}
  319. >
  320. eSIM
  321. </button>
  322. <button
  323. onClick={() => setSimType("Physical")}
  324. className={`px-6 md:px-12 py-2.5 md:py-3 rounded-full text-lg md:text-xs lg:text-xl font-bold transition-all ${
  325. simType === "Physical"
  326. ? "bg-gradient-to-r from-[#E21c34] to-[#500B28] text-white shadow-md"
  327. : "bg-[#f0f0f0] text-[#8b8e96] hover:text-slate-800"
  328. }`}
  329. >
  330. {t("physicalSim")}
  331. </button>
  332. </div>
  333. </div>
  334. <div className="grid grid-cols-1 lg:grid-cols-2 gap-8 md:gap-16 items-center">
  335. <div className="relative aspect-square bg-[#EE0434]/5 rounded-[24px] md:rounded-[48px] overflow-hidden flex items-center justify-center shadow-xl border border-red-50">
  336. <div className="absolute inset-0 bg-gradient-to-br from-[#EE0434]/20 to-white/0 opacity-50"></div>
  337. <div className="w-4/5 h-4/5 relative flex items-center justify-center">
  338. <svg
  339. className="w-full h-full text-[#EE0434] drop-shadow-lg"
  340. viewBox="0 0 100 100"
  341. fill="none"
  342. >
  343. <path
  344. d="M0 70 Q 25 20, 50 70 T 100 70"
  345. stroke="currentColor"
  346. strokeWidth="8"
  347. strokeLinecap="round"
  348. className="opacity-80"
  349. />
  350. </svg>
  351. </div>
  352. </div>
  353. <div className="space-y-4 md:space-y-12">
  354. {steps.map((step, index) => (
  355. <div
  356. key={index}
  357. className="flex items-start space-x-4 md:space-x-6 p-4 md:p-8 rounded-[20px] md:rounded-[32px] border bg-transparent border-transparent hover:bg-white hover:shadow-lg hover:border-slate-50 transition-all"
  358. >
  359. <span className="text-xl md:text-3xl font-black text-[#EE0434] mt-0.5 shrink-0">
  360. {step.number}
  361. </span>
  362. <div>
  363. <h3 className="text-base md:text-2xl font-black text-slate-900 mb-0.5 md:mb-2">
  364. {step.title}
  365. </h3>
  366. <p className="text-slate-500 text-[11px] md:text-lg leading-relaxed">
  367. {step.description}
  368. </p>
  369. </div>
  370. </div>
  371. ))}
  372. </div>
  373. </div>
  374. </div>
  375. </section>
  376. {/* FAQ */}
  377. <HomeFaq />
  378. {/* Refund */}
  379. <section className="py-16 md:py-24 bg-white px-4">
  380. <div className="max-w-7xl mx-auto flex flex-col items-center">
  381. <h2 className="text-3xl md:text-[56px] font-black text-center mb-12 md:mb-20 tracking-tight text-slate-900">
  382. {t("refundDefective")}
  383. </h2>
  384. <button className="flex items-center bg-gradient-to-r from-[#E21c34] to-[#500B28] p-2 pr-10 rounded-full shadow-xl hover:scale-105 transition-all">
  385. <div className="w-16 h-16 bg-white rounded-full flex items-center justify-center text-slate-800">
  386. <svg
  387. className="w-8 h-8"
  388. fill="none"
  389. stroke="currentColor"
  390. viewBox="0 0 24 24"
  391. >
  392. <path
  393. strokeLinecap="round"
  394. strokeLinejoin="round"
  395. strokeWidth={2.5}
  396. d="M17 8l4 4m0 0l-4 4m4-4H3"
  397. />
  398. </svg>
  399. </div>
  400. <span className="ml-6 text-white text-2xl font-black">
  401. {t("returnGuide")}
  402. </span>
  403. </button>
  404. </div>
  405. </section>
  406. </div>
  407. );
  408. };
  409. export default HomeView;