ProductInfoModal.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402
  1. import React, { useState } from "react";
  2. import { Area, Package } from "../services/product/type";
  3. import { useTranslation } from "react-i18next";
  4. interface ProductInfoModalProps {
  5. isOpen: boolean;
  6. onClose: () => void;
  7. packageInfo: Package | null;
  8. }
  9. const ProductInfoModal: React.FC<ProductInfoModalProps> = ({
  10. isOpen,
  11. onClose,
  12. packageInfo,
  13. }) => {
  14. const { t } = useTranslation();
  15. const [activeTab, setActiveTab] = useState<"specs" | "notes">("specs");
  16. if (!isOpen || !packageInfo) return null;
  17. const specs = [
  18. {
  19. label: t("coverageArea"),
  20. value: packageInfo?.coverageArea,
  21. icon: (
  22. <svg
  23. className="w-5 h-5 color-EE0434"
  24. fill="none"
  25. stroke="currentColor"
  26. viewBox="0 0 24 24"
  27. >
  28. <path
  29. strokeLinecap="round"
  30. strokeLinejoin="round"
  31. strokeWidth={2}
  32. d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0"
  33. />
  34. </svg>
  35. ),
  36. },
  37. {
  38. label: t("networkProvider"),
  39. value: packageInfo?.carriers,
  40. icon: (
  41. <svg
  42. className="w-5 h-5 color-EE0434"
  43. fill="none"
  44. stroke="currentColor"
  45. viewBox="0 0 24 24"
  46. >
  47. <path
  48. strokeLinecap="round"
  49. strokeLinejoin="round"
  50. strokeWidth={2}
  51. d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"
  52. />
  53. </svg>
  54. ),
  55. },
  56. {
  57. label: t("infoRegistration") + " (eKYC)",
  58. value: packageInfo?.ekycRequired ? t("required") : t("notRequired"),
  59. icon: (
  60. <svg
  61. className="w-5 h-5 color-EE0434"
  62. fill="none"
  63. stroke="currentColor"
  64. viewBox="0 0 24 24"
  65. >
  66. <path
  67. strokeLinecap="round"
  68. strokeLinejoin="round"
  69. strokeWidth={2}
  70. d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
  71. />
  72. </svg>
  73. ),
  74. },
  75. {
  76. label: t("activationMethod"),
  77. value:
  78. packageInfo?.activationMethod === 1
  79. ? t("usageTimeCalculatedFromSignal")
  80. : t("usageTimeCalculatedFromPurchase"),
  81. icon: (
  82. <svg
  83. className="w-5 h-5 color-EE0434"
  84. fill="none"
  85. stroke="currentColor"
  86. viewBox="0 0 24 24"
  87. >
  88. <path
  89. strokeLinecap="round"
  90. strokeLinejoin="round"
  91. strokeWidth={2}
  92. d="M12 4v1m6 11h2m-6 0h-2v4m0-11v3m0 0h.01M12 14.5V12m0 0V8.832c0-1.12 1.682-1.43 2.182-1.832l.75-1.05a.5.5 0 00-.864-.53l-1.07 1.635M12 21a9 9 0 110-18 9 9 0 010 18z"
  93. />
  94. </svg>
  95. ),
  96. },
  97. {
  98. label: t("deliveryMethod"),
  99. value:
  100. "Bạn sẽ nhận được email chứa eSIM trong vòng 5 phút sau khi thanh toán thành công. ",
  101. // packageInfo?.deliveryMethod === 0
  102. // ? t(
  103. // "youWillReceiveAnEmailImmediatelyAfterPaymentWithEsimWaitForShippingAndReceiveYourPhysicalSim",
  104. // )
  105. // : t("physicalSimWillBeShippedToYourAddressWithin3-5BusinessDays")
  106. icon: (
  107. <svg
  108. className="w-5 h-5 color-EE0434"
  109. fill="none"
  110. stroke="currentColor"
  111. viewBox="0 0 24 24"
  112. >
  113. <path d="M9 17a2 2 0 11-4 0 2 2 0 014 0zM19 17a2 2 0 11-4 0 2 2 0 014 0z" />
  114. <path
  115. strokeLinecap="round"
  116. strokeLinejoin="round"
  117. strokeWidth={2}
  118. d="M13 16V6a1 1 0 00-1-1H4a1 1 0 00-1 1v10a1 1 0 001 1h1m8-1a1 1 0 01-1 1H9m4-1V8a1 1 0 011-1h2.586a1 1 0 01.707.293l3.414 3.414a1 1 0 01.293.707V16a1 1 0 01-1 1h-1m-6-1a1 1 0 001 1h1M5 17a2 2 0 10-4 0 2 2 0 004 0zm10 0a2 2 0 10-4 0 2 2 0 004 0z"
  119. />
  120. </svg>
  121. ),
  122. },
  123. {
  124. label: t("phoneNumber"),
  125. value: packageInfo?.phoneNumber != 0 ? packageInfo.phoneNumber : "No",
  126. icon: (
  127. <svg
  128. className="w-5 h-5 color-EE0434"
  129. fill="none"
  130. stroke="currentColor"
  131. viewBox="0 0 24 24"
  132. >
  133. <path
  134. strokeLinecap="round"
  135. strokeLinejoin="round"
  136. strokeWidth={2}
  137. d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"
  138. />
  139. </svg>
  140. ),
  141. },
  142. {
  143. label: t("wifiHotspot"),
  144. value: packageInfo?.wifiSharing === 1 ? t("yesSupported") : t("no"),
  145. icon: (
  146. <svg
  147. className="w-5 h-5 color-EE0434"
  148. fill="none"
  149. stroke="currentColor"
  150. viewBox="0 0 24 24"
  151. >
  152. <path
  153. strokeLinecap="round"
  154. strokeLinejoin="round"
  155. strokeWidth={2}
  156. d="M8.111 16.404a5.5 5.5 0 017.778 0M12 20h.01m-7.08-7.071c3.904-3.905 10.236-3.905 14.141 0M1.394 9.393c5.857-5.857 15.355-5.857 21.213 0"
  157. />
  158. </svg>
  159. ),
  160. },
  161. {
  162. label: t("networkTechnology"),
  163. value: packageInfo?.networkTechnology,
  164. icon: (
  165. <svg
  166. className="w-5 h-5 color-EE0434"
  167. fill="none"
  168. stroke="currentColor"
  169. viewBox="0 0 24 24"
  170. >
  171. <path
  172. strokeLinecap="round"
  173. strokeLinejoin="round"
  174. strokeWidth={2}
  175. d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"
  176. />
  177. </svg>
  178. ),
  179. },
  180. {
  181. label: t("packageStartTime"),
  182. value:
  183. packageInfo?.activationMethod === 1
  184. ? t("usageTimeCalculatedFromSignal")
  185. : t("usageTimeCalculatedFromPurchase"),
  186. icon: (
  187. <svg
  188. className="w-5 h-5 color-EE0434"
  189. fill="none"
  190. stroke="currentColor"
  191. viewBox="0 0 24 24"
  192. >
  193. <path
  194. strokeLinecap="round"
  195. strokeLinejoin="round"
  196. strokeWidth={2}
  197. d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"
  198. />
  199. </svg>
  200. ),
  201. },
  202. {
  203. label: t("dataResetTime"),
  204. value:
  205. // 0: Một ngày được tính đến 23:59 Giờ địa phương
  206. // 1:Gói được tính theo giờ Trung Quốc (UTC+8), nhưng sẽ reset vào 0:00 theo giờ quốc tế UTC+0.
  207. // 2: Gói được tính theo ngày (0h–24h) của múi giờ quốc gia nơi gói cước được kích hoạt.
  208. // 3: Một ngày được tính đến 23:59 giờ Bắc Kinh (GMT+8).
  209. // 4: Thời gian tính theo múi giờ UTC+0, reset lúc 0:00.
  210. // 5: Một ngày được tính đến 23:59 giờ Bắc Kinh (GMT+8)
  211. // 6: Thời gian tính theo ngày (0h–24h) của múi giờ quốc gia nơi kích hoạt gói.
  212. packageInfo?.dataResetTime === 0
  213. ? "Một ngày được tính đến 23:59 Giờ địa phương."
  214. : packageInfo?.dataResetTime === 1
  215. ? "Gói được tính theo giờ Trung Quốc (UTC+8), nhưng sẽ reset vào 0:00 theo giờ quốc tế UTC+0."
  216. : packageInfo?.dataResetTime === 2
  217. ? "Gói được tính theo ngày (0h–24h) của múi giờ quốc gia nơi gói cước được kích hoạt."
  218. : packageInfo?.dataResetTime === 3
  219. ? "Một ngày được tính đến 23:59 giờ Bắc Kinh (GMT+8)."
  220. : packageInfo?.dataResetTime === 4
  221. ? "Thời gian tính theo múi giờ UTC+0, reset lúc 0:00."
  222. : packageInfo?.dataResetTime === 5
  223. ? "Một ngày được tính đến 23:59 giờ Bắc Kinh (GMT+8)."
  224. : packageInfo?.dataResetTime === 6
  225. ? "Thời gian tính theo ngày (0h–24h) của múi giờ quốc gia nơi kích hoạt gói."
  226. : "",
  227. icon: (
  228. <svg
  229. className="w-5 h-5 color-EE0434"
  230. fill="none"
  231. stroke="currentColor"
  232. viewBox="0 0 24 24"
  233. >
  234. <path
  235. strokeLinecap="round"
  236. strokeLinejoin="round"
  237. strokeWidth={2}
  238. d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
  239. />
  240. </svg>
  241. ),
  242. },
  243. {
  244. label: t("unlimitedPolicy"),
  245. value: packageInfo?.unlimitedPolicy,
  246. icon: (
  247. <svg
  248. className="w-5 h-5 color-EE0434"
  249. fill="none"
  250. stroke="currentColor"
  251. viewBox="0 0 24 24"
  252. >
  253. <path
  254. strokeLinecap="round"
  255. strokeLinejoin="round"
  256. strokeWidth={2}
  257. d="M13 10V3L4 14h7v7l9-11h-7z"
  258. />
  259. </svg>
  260. ),
  261. },
  262. {
  263. label: t("appSupport"),
  264. value: packageInfo?.appSupport,
  265. icon: (
  266. <svg
  267. className="w-5 h-5 color-EE0434"
  268. fill="none"
  269. stroke="currentColor"
  270. viewBox="0 0 24 24"
  271. >
  272. <path
  273. strokeLinecap="round"
  274. strokeLinejoin="round"
  275. strokeWidth={2}
  276. 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"
  277. />
  278. </svg>
  279. ),
  280. },
  281. ];
  282. return (
  283. <div className="fixed inset-0 z-[100] flex items-center justify-center p-4 animate-in fade-in duration-200">
  284. <div
  285. className="absolute inset-0 bg-slate-900/60 backdrop-blur-sm"
  286. onClick={onClose}
  287. ></div>
  288. <div className="relative bg-white rounded-[32px] w-full max-w-2xl max-h-[85vh] shadow-2xl flex flex-col overflow-hidden">
  289. {/* Header */}
  290. <div className="p-6 pb-2 border-b border-slate-100 flex justify-between items-center bg-white z-10">
  291. <h2 className="text-xl md:text-2xl font-black text-slate-800">
  292. {t("productInformation")}
  293. </h2>
  294. <button
  295. onClick={onClose}
  296. className="p-2 text-slate-400 hover:text-slate-600 hover:bg-slate-50 rounded-full transition-all"
  297. >
  298. <svg
  299. className="w-6 h-6"
  300. fill="none"
  301. stroke="currentColor"
  302. viewBox="0 0 24 24"
  303. >
  304. <path
  305. strokeLinecap="round"
  306. strokeLinejoin="round"
  307. strokeWidth={2}
  308. d="M6 18L18 6M6 6l12 12"
  309. />
  310. </svg>
  311. </button>
  312. </div>
  313. {/* Tabs */}
  314. <div className="flex px-6 border-b border-slate-100 bg-white z-10">
  315. <button
  316. onClick={() => setActiveTab("specs")}
  317. className={`flex-1 py-4 text-sm md:text-base font-bold text-center border-b-2 transition-colors ${
  318. activeTab === "specs"
  319. ? "border-[#EE0434] text-[#EE0434]"
  320. : "border-transparent text-slate-400 hover:text-slate-600"
  321. }`}
  322. >
  323. {t("specifications")}
  324. </button>
  325. <button
  326. onClick={() => setActiveTab("notes")}
  327. className={`flex-1 py-4 text-sm md:text-base font-bold text-center border-b-2 transition-colors ${
  328. activeTab === "notes"
  329. ? "border-[#EE0434] text-[#EE0434]"
  330. : "border-transparent text-slate-400 hover:text-slate-600"
  331. }`}
  332. >
  333. {t("notes")}
  334. </button>
  335. </div>
  336. {/* Content */}
  337. <div className="flex-1 overflow-y-auto p-6 md:p-8 custom-scrollbar bg-white">
  338. {activeTab === "specs" ? (
  339. <div className="space-y-6">
  340. {specs.map((spec, idx) => (
  341. <div key={idx} className="flex items-start space-x-4">
  342. <div className="w-6 h-6 text-[#0091ff] shrink-0 mt-0.5">
  343. {spec.icon}
  344. </div>
  345. <div className="flex-1">
  346. <p className="font-bold text-slate-600 text-sm mb-1">
  347. {spec.label}:
  348. </p>
  349. <p className="text-slate-800 font-medium text-sm md:text-base leading-relaxed">
  350. {spec.value}
  351. </p>
  352. </div>
  353. </div>
  354. ))}
  355. </div>
  356. ) : (
  357. <div className="space-y-4 text-slate-600">
  358. <p className="font-medium leading-relaxed">
  359. <span className="font-bold text-[16px] color-EE0434">1.</span>{" "}
  360. {t("note1")}
  361. </p>
  362. <p className="font-medium leading-relaxed">
  363. <span className="font-bold text-[16px] color-EE0434">2.</span>{" "}
  364. {t("note2")}
  365. </p>
  366. <p className="font-medium leading-relaxed">
  367. <span className="font-bold text-[16px] color-EE0434">3.</span>{" "}
  368. {t("note3")}
  369. </p>
  370. <p className="font-medium leading-relaxed">
  371. <span className="font-bold text-[16px] color-EE0434">4.</span>{" "}
  372. {t("note4")}
  373. </p>
  374. <p className="font-medium leading-relaxed">
  375. <span className="font-bold text-[16px] color-EE0434">5.</span>{" "}
  376. {/* display html */}
  377. <span dangerouslySetInnerHTML={{ __html: t("note5") }} />
  378. </p>
  379. <p className="font-medium leading-relaxed">
  380. <span className="font-bold text-[16px] color-EE0434">6.</span>{" "}
  381. {t("note6")}
  382. </p>
  383. </div>
  384. )}
  385. </div>
  386. {/* Bottom Fade */}
  387. <div className="absolute bottom-0 left-0 right-0 h-12 bg-gradient-to-t from-white to-transparent pointer-events-none"></div>
  388. </div>
  389. </div>
  390. );
  391. };
  392. export default ProductInfoModal;