فهرست منبع

Add order history and compatibility modal features

Introduces order history and order detail pages, a device compatibility modal, and related API methods. Updates branding from InfiGate to Getgo, refactors account/logout logic, enhances popup and header components for user actions, and improves product price formatting. Removes unused HandbookView and updates company info in the footer.
trunghieubui 10 ماه پیش
والد
کامیت
b25875e9fa
26فایلهای تغییر یافته به همراه1643 افزوده شده و 249 حذف شده
  1. 1 1
      EsimLao/esim-vite/index.html
  2. 9 0
      EsimLao/esim-vite/src/App.tsx
  3. 8 8
      EsimLao/esim-vite/src/apis/axios.ts
  4. 10 0
      EsimLao/esim-vite/src/apis/baseApi.ts
  5. 5 0
      EsimLao/esim-vite/src/apis/contentApi.ts
  6. 12 0
      EsimLao/esim-vite/src/apis/productApi.ts
  7. 2 0
      EsimLao/esim-vite/src/app/store.ts
  8. 308 0
      EsimLao/esim-vite/src/components/CompatibilityModal.tsx
  9. 127 22
      EsimLao/esim-vite/src/components/Footer.tsx
  10. 98 19
      EsimLao/esim-vite/src/components/Header.tsx
  11. 29 12
      EsimLao/esim-vite/src/components/Popup.tsx
  12. 1 2
      EsimLao/esim-vite/src/components/ProductCard.tsx
  13. 3 2
      EsimLao/esim-vite/src/features/account/accuntSlice.ts
  14. 21 2
      EsimLao/esim-vite/src/features/popup/popupSlice.ts
  15. 27 9
      EsimLao/esim-vite/src/pages/checkout/CheckoutView.tsx
  16. 51 12
      EsimLao/esim-vite/src/pages/contact/ContactView.tsx
  17. 0 136
      EsimLao/esim-vite/src/pages/handbook/HandbookView.tsx
  18. 28 5
      EsimLao/esim-vite/src/pages/home/HomeView.tsx
  19. 7 4
      EsimLao/esim-vite/src/pages/home/components/HomeProduct.tsx
  20. 14 6
      EsimLao/esim-vite/src/pages/home/components/HomeSearch.tsx
  21. 3 5
      EsimLao/esim-vite/src/pages/login/LoginView.tsx
  22. 516 0
      EsimLao/esim-vite/src/pages/order-detail/OrderDetailView.tsx
  23. 273 0
      EsimLao/esim-vite/src/pages/order-history/OrderHistoryView.tsx
  24. 17 4
      EsimLao/esim-vite/src/pages/product-detail/ProductDetailView.tsx
  25. 24 0
      EsimLao/esim-vite/src/services/content/types.ts
  26. 49 0
      EsimLao/esim-vite/src/services/product/type.ts

+ 1 - 1
EsimLao/esim-vite/index.html

@@ -4,7 +4,7 @@
 <head>
   <meta charset="UTF-8" />
   <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-  <title>InfiGate | Stay Connected Everywhere</title>
+  <title>Getgo | Stay Connected Everywhere</title>
   <link rel="preconnect" href="https://fonts.googleapis.com" />
   <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
   <link

+ 9 - 0
EsimLao/esim-vite/src/App.tsx

@@ -14,6 +14,9 @@ import ContactView from "./pages/contact/ContactView";
 import TopLoader from "./components/TopLoader";
 import CheckoutView from "./pages/checkout/CheckoutView";
 import Popup from "./components/Popup";
+import OrderHistoryView from "./pages/order-history/OrderHistoryView";
+import OrderDetailView from "./pages/order-detail/OrderDetailView";
+import CompatibilityModal from "./components/CompatibilityModal";
 
 const App: React.FC = () => {
   const location = useLocation();
@@ -34,6 +37,7 @@ const App: React.FC = () => {
       {!isPlainView && <Header />}
 
       <Popup />
+      <CompatibilityModal />
 
       <div className={`flex flex-1 ${isAiView ? "overflow-hidden" : ""}`}>
         {isAiView && <Sidebar />}
@@ -73,6 +77,11 @@ const App: React.FC = () => {
               <Route path="/news/:id" element={<ArticleDetailView />} />
               <Route path="/contact" element={<ContactView />} />
               <Route path="/login" element={<LoginView />} />
+              <Route path="/order-history" element={<OrderHistoryView />} />
+              <Route
+                path="/order-history-detail/:id"
+                element={<OrderDetailView />}
+              />
 
               {/* Fallback */}
               <Route path="*" element={<Navigate to="/" replace />} />

+ 8 - 8
EsimLao/esim-vite/src/apis/axios.ts

@@ -1,20 +1,19 @@
-import axios, { AxiosInstance } from 'axios';
+import axios, { AxiosInstance } from "axios";
 
 export const createAxiosInstance = (baseURL: string): AxiosInstance => {
   const instance = axios.create({
     baseURL,
     timeout: 10000,
     headers: {
-      'Content-Type': 'application/json',
+      "Content-Type": "application/json",
     },
   });
 
   instance.interceptors.request.use((config) => {
-    const token = localStorage.getItem('token');
-    const lang = localStorage.getItem('selectedLanguage') || 'en';
-
+    const token = localStorage.getItem("token");
+    const lang = localStorage.getItem("selectedLanguage") || "en";
     if (lang) {
-      config.headers['Accept-Language'] = lang;
+      config.headers["Accept-Language"] = lang;
     }
     if (token) {
       config.headers.Authorization = `Bearer ${token}`;
@@ -26,8 +25,9 @@ export const createAxiosInstance = (baseURL: string): AxiosInstance => {
     (response) => response.data,
     (error) => {
       if (error.response?.status === 401) {
-        localStorage.removeItem('token');
-        window.location.href = '/login';
+        localStorage.removeItem("token");
+        localStorage.removeItem("refreshToken");
+        window.location.href = "/login";
       }
       return Promise.reject(error);
     }

+ 10 - 0
EsimLao/esim-vite/src/apis/baseApi.ts

@@ -48,4 +48,14 @@ export class BaseApi {
       timeout: timeout ?? this.defaultTimeout,
     });
   }
+
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  async authGet<T>(
+    endpoint: string = "",
+    timeout?: number
+  ): Promise<Response<T>> {
+    return authApiInstance.get(`${this.baseUrl}${endpoint}`, {
+      timeout: timeout ?? this.defaultTimeout,
+    });
+  }
 }

+ 5 - 0
EsimLao/esim-vite/src/apis/contentApi.ts

@@ -4,6 +4,7 @@ import {
 } from "../services/article/types";
 import {
   LoadBannerResponse,
+  LoadDeviceMetaResponse,
   LoadFaqCategoryResponse,
   LoadFaqResponse,
   LoadReviewResponse,
@@ -52,6 +53,10 @@ class ContentApi extends BaseApi {
       isFeatured,
     });
   }
+
+  async LoadDeviceMetaData() {
+    return this.authGet<LoadDeviceMetaResponse>("/device-metadata");
+  }
 }
 
 export const contentApi = new ContentApi();

+ 12 - 0
EsimLao/esim-vite/src/apis/productApi.ts

@@ -48,6 +48,18 @@ class ProductApi extends BaseApi {
   async loadPaymentChannel() {
     return this.productPost("/list_payment_channel", {});
   }
+
+  async getOrderHistory({ fromDate, toDate, status }) {
+    return this.productPost("/list_order", { fromDate, toDate, status });
+  }
+
+  async getOrderDetail({ orderId }) {
+    return this.productPost("/get_order_detail", { orderId });
+  }
+
+  async checkDataUsage({ iccid }) {
+    return this.productPost("/check_data_usage", { iccid });
+  }
 }
 
 export const productApi = new ProductApi();

+ 2 - 0
EsimLao/esim-vite/src/app/store.ts

@@ -2,12 +2,14 @@ import { configureStore } from "@reduxjs/toolkit";
 import loadingReducer from "../features/loading/loadingSlice";
 import areasReducer from "../features/areas/areasSlice";
 import popupReducer from "../features/popup/popupSlice";
+import accountReducer from "../features/account/accuntSlice";
 
 export const store = configureStore({
   reducer: {
     loading: loadingReducer,
     areas: areasReducer,
     popup: popupReducer,
+    account: accountReducer,
   },
 });
 

+ 308 - 0
EsimLao/esim-vite/src/components/CompatibilityModal.tsx

@@ -0,0 +1,308 @@
+import React, { useState, useEffect } from "react";
+import { useAppDispatch, useAppSelector } from "../hooks/useRedux";
+import { closeCompatibilityModal } from "../features/popup/popupSlice";
+import { useMutation } from "@tanstack/react-query";
+import { startLoading, stopLoading } from "../features/loading/loadingSlice";
+import { contentApi } from "../apis/contentApi";
+import { DeviceMeta } from "../services/content/types";
+
+const CompatibilityModal = () => {
+  const popup = useAppSelector((state) => state.popup);
+  const dispatch = useAppDispatch();
+  const [brands, setBrands] = useState<DeviceMeta[] | []>([]);
+  const [deviceList, setDeviceList] = useState<Record<string, string[]>>([]);
+  const [selectedBrand, setSelectedBrand] = useState("");
+
+  useEffect(() => {
+    getDeviceMetaMutation.mutate();
+  }, []);
+
+  const getDeviceMetaMutation = useMutation({
+    mutationFn: async () => {
+      dispatch(startLoading({}));
+      const res = await contentApi.LoadDeviceMetaData();
+      return res;
+    },
+    onSuccess: (data) => {
+      dispatch(stopLoading());
+      console.log("Get device metadata response data:", data);
+      if (data && data.errorCode === "0") {
+        console.log("Get device metadata successful");
+        setBrands(data.data.brands || []);
+        let deviceMap: Record<string, string[]> = {};
+        data.data.brands.forEach((element) => {
+          deviceMap[element.brand] = element.devices.map(
+            (device) => device.modelName
+          );
+        });
+
+        setDeviceList(deviceMap);
+
+        setSelectedBrand(
+          data.data.brands.length > 0 ? data.data.brands[0].brand : ""
+        );
+      } else {
+        console.error("Get device metadata failed, no token received");
+      }
+    },
+    onError: (error: any) => {
+      dispatch(stopLoading());
+      console.error("Get device metadata error:", error.response.data);
+    },
+  });
+
+  // const brands = [
+  //   {
+  //     id: "apple",
+  //     label: "Apple",
+  //     icon: (
+  //       <svg className="w-8 h-8" viewBox="0 0 384 512" fill="currentColor">
+  //         <path d="M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-61.9 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 52.3-11.4 69.5-34.3z" />
+  //       </svg>
+  //     ),
+  //   },
+  //   {
+  //     id: "samsung",
+  //     label: "Samsung",
+  //     icon: (
+  //       <svg className="w-12 h-8" viewBox="0 0 24 24" fill="currentColor">
+  //         <path d="M17.8 4.8C14.3 3.6 9.7 3.6 6.2 4.8 1.9 6.3 0 10.2 0 14.9s1.9 8.6 6.2 10.1c3.5 1.2 8.1 1.2 11.6 0 4.3-1.5 6.2-5.4 6.2-10.1s-1.9-8.6-6.2-10.1zM8 17.9c-1.3 0-2.4-.3-2.4-1.4 0-1.1 1.1-1.4 2.4-1.4 1.3 0 2.4.3 2.4 1.4 0 1.1-1.1 1.4-2.4 1.4zm0-5.7c-1.3 0-2.4-.3-2.4-1.4 0-1.1 1.1-1.4 2.4-1.4 1.3 0 2.4.3 2.4 1.4 0 1.1-1.1 1.4-2.4 1.4zm8 5.7c-1.3 0-2.4-.3-2.4-1.4 0-1.1 1.1-1.4 2.4-1.4 1.3 0 2.4.3 2.4 1.4 0 1.1-1.1 1.4-2.4 1.4zm0-5.7c-1.3 0-2.4-.3-2.4-1.4 0-1.1 1.1-1.4 2.4-1.4 1.3 0 2.4.3 2.4 1.4 0 1.1-1.1 1.4-2.4 1.4z" />
+  //       </svg>
+  //     ),
+  //   },
+  //   {
+  //     id: "google",
+  //     label: "Google Pixel",
+  //     icon: (
+  //       <svg className="w-8 h-8" viewBox="0 0 24 24" fill="currentColor">
+  //         <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z" />
+  //       </svg>
+  //     ),
+  //   },
+  //   {
+  //     id: "others",
+  //     label: "Others",
+  //     icon: (
+  //       <svg className="w-6 h-8" viewBox="0 0 24 24" fill="currentColor">
+  //         <path d="M17 1.01L7 1c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z" />
+  //       </svg>
+  //     ),
+  //   },
+  // ];
+
+  if (popup.isOpenCompatibilityModal === false) {
+    return null;
+  }
+  return (
+    <div className="fixed inset-0 z-[100] flex items-center justify-center p-4 animate-in fade-in duration-200">
+      <div
+        className="absolute inset-0 bg-slate-900/60 backdrop-blur-sm"
+        onClick={() => {
+          dispatch(closeCompatibilityModal());
+        }}
+      ></div>
+
+      {/* Container is now a flex column with hidden overflow to manage scroll internally */}
+      <div className="relative bg-white rounded-[32px] w-full max-w-4xl max-h-[90vh] shadow-2xl flex flex-col overflow-hidden">
+        {/* Fixed Close Button (Absolute) */}
+        <button
+          onClick={() => {
+            dispatch(closeCompatibilityModal());
+          }}
+          className="absolute top-4 right-4 p-2 text-slate-400 hover:text-[#EE0434] transition-colors bg-white/80 backdrop-blur-sm rounded-full shadow-sm z-20"
+        >
+          <svg
+            className="w-6 h-6"
+            fill="none"
+            stroke="currentColor"
+            viewBox="0 0 24 24"
+          >
+            <path
+              strokeLinecap="round"
+              strokeLinejoin="round"
+              strokeWidth={2}
+              d="M6 18L18 6M6 6l12 12"
+            />
+          </svg>
+        </button>
+
+        {/* Fixed Header */}
+        <div className="shrink-0 p-8 md:p-10 pb-2 text-center bg-white z-10 relative">
+          <h2 className="text-3xl md:text-5xl font-black text-slate-900 mb-2 tracking-tight">
+            Does my phone <br />
+            <span className="text-[#0091ff]">support eSIM ?</span>
+          </h2>
+        </div>
+
+        {/* Scrollable Content Body */}
+        <div className="flex-1 overflow-y-auto p-8 md:p-12 pt-4 custom-scrollbar">
+          <div className="text-center mb-8">
+            <h3 className="text-xl md:text-2xl font-black text-[#0091ff] mb-6">
+              Method 1: Quickly check using the list below
+            </h3>
+
+            <div className="text-left bg-slate-50 p-6 rounded-2xl border border-slate-100 text-sm md:text-base text-slate-600 space-y-4 mb-8">
+              <p className="font-bold">
+                Important Note: Please make sure your device supports eSIM
+                before purchasing. The information below is for reference
+                purposes only. We are not responsible for any changes in device
+                information. We will not refund any order if the device does not
+                support eSIM, including the following cases:
+              </p>
+              <ul className="list-disc pl-5 space-y-1 marker:text-[#0091ff]">
+                <li>
+                  Some dual physical SIM phone models do not support eSIM.
+                </li>
+                <li>
+                  Carrier-locked devices (contract-based subscription devices)
+                  may not support eSIM.
+                </li>
+                <li>
+                  Some models purchased in certain countries may not support
+                  eSIM (see table below).
+                </li>
+              </ul>
+              <p>
+                To make sure your device supports eSIM, please contact the
+                store/provider where you purchased the device for the most
+                accurate confirmation.
+              </p>
+            </div>
+          </div>
+
+          {/* Brand Tabs */}
+          <div className="flex justify-center space-x-8 mb-10 overflow-x-auto py-2">
+            {brands.map((brand) => (
+              <button
+                key={brand.brand}
+                onClick={() => setSelectedBrand(brand.brand)}
+                className={`flex flex-col items-center space-y-3 group min-w-[80px] focus:outline-none`}
+              >
+                <div
+                  className={`w-16 h-16 flex items-center justify-center transition-colors ${
+                    selectedBrand === brand.brand
+                      ? "text-slate-900 scale-110"
+                      : "text-slate-300 group-hover:text-slate-500"
+                  }`}
+                >
+                  {brand.brand == "Apple" ? (
+                    <svg
+                      className="w-8 h-8"
+                      viewBox="0 0 384 512"
+                      fill="currentColor"
+                    >
+                      <path d="M318.7 268.7c-.2-36.7 16.4-64.4 50-84.8-18.8-26.9-47.2-41.7-84.7-44.6-35.5-2.8-74.3 20.7-88.5 20.7-15 0-49.4-19.7-76.4-19.7C63.3 141.2 4 184.8 4 273.5q0 39.3 14.4 81.2c12.8 36.7 59 126.7 107.2 125.2 25.2-.6 43-17.9 75.8-17.9 31.8 0 48.3 17.9 76.4 17.9 48.6-.7 90.4-82.5 102.6-119.3-65.2-30.7-61.7-90-61.7-91.9zm-56.6-164.2c27.3-32.4 24.8-61.9 24-72.5-24.1 1.4-52 16.4-67.9 34.9-17.5 19.8-27.8 44.3-25.6 71.9 26.1 2 52.3-11.4 69.5-34.3z" />
+                    </svg>
+                  ) : brand.brand == "Samsung" ? (
+                    <svg
+                      className="w-12 h-8"
+                      viewBox="0 0 24 24"
+                      fill="currentColor"
+                    >
+                      <path d="M17.8 4.8C14.3 3.6 9.7 3.6 6.2 4.8 1.9 6.3 0 10.2 0 14.9s1.9 8.6 6.2 10.1c3.5 1.2 8.1 1.2 11.6 0 4.3-1.5 6.2-5.4 6.2-10.1s-1.9-8.6-6.2-10.1zM8 17.9c-1.3 0-2.4-.3-2.4-1.4 0-1.1 1.1-1.4 2.4-1.4 1.3 0 2.4.3 2.4 1.4 0 1.1-1.1 1.4-2.4 1.4zm0-5.7c-1.3 0-2.4-.3-2.4-1.4 0-1.1 1.1-1.4 2.4-1.4 1.3 0 2.4.3 2.4 1.4 0 1.1-1.1 1.4-2.4 1.4zm8 5.7c-1.3 0-2.4-.3-2.4-1.4 0-1.1 1.1-1.4 2.4-1.4 1.3 0 2.4.3 2.4 1.4 0 1.1-1.1 1.4-2.4 1.4zm0-5.7c-1.3 0-2.4-.3-2.4-1.4 0-1.1 1.1-1.4 2.4-1.4 1.3 0 2.4.3 2.4 1.4 0 1.1-1.1 1.4-2.4 1.4z" />
+                    </svg>
+                  ) : brand.brand == "Google" ? (
+                    <svg
+                      className="w-8 h-8"
+                      viewBox="0 0 24 24"
+                      fill="currentColor"
+                    >
+                      <path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8 8 3.59 8 8-3.59 8-8 8zm-1-13h2v6h-2zm0 8h2v2h-2z" />
+                    </svg>
+                  ) : (
+                    <svg
+                      className="w-6 h-8"
+                      viewBox="0 0 24 24"
+                      fill="currentColor"
+                    >
+                      <path d="M17 1.01L7 1c-1.1 0-2 .9-2 2v18c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V3c0-1.1-.9-1.99-2-1.99zM17 19H7V5h10v14z" />
+                    </svg>
+                  )}
+                </div>
+                <span
+                  className={`font-bold text-lg transition-colors ${
+                    selectedBrand === brand.brand
+                      ? "text-[#0091ff]"
+                      : "text-slate-300 group-hover:text-slate-500"
+                  }`}
+                >
+                  {brand.brand}
+                </span>
+                {selectedBrand === brand.brand && (
+                  <div className="w-12 h-1 bg-[#0091ff] rounded-full"></div>
+                )}
+              </button>
+            ))}
+          </div>
+
+          {/* Device List (Scrolls with the body) */}
+          <div className="bg-white rounded-t-3xl border-t border-slate-100">
+            <div className="divide-y divide-slate-50">
+              {deviceList[selectedBrand].map((device, idx) => (
+                <div
+                  key={idx}
+                  className="py-4 px-4 text-center hover:bg-slate-50 transition-colors text-slate-700 font-bold text-lg"
+                >
+                  {device}
+                </div>
+              ))}
+            </div>
+          </div>
+
+          {/* Divider */}
+          <div className="my-10 border-t border-dashed border-slate-300"></div>
+
+          {/* Apple Specific Note */}
+          <div className="text-left text-slate-600 space-y-4 mb-12">
+            <p className="font-bold text-[#EE0434]">
+              *Note: The following apple devices do NOT support eSIM:
+            </p>
+            <ul className="list-disc pl-5 space-y-2 text-sm md:text-base">
+              <li>
+                Phone models with dual physical SIMs do NOT support eSIM. Please
+                check Settings &gt; Cellular to ensure the option "Add eSIM" is
+                available.
+              </li>
+              <li>
+                All iPhones purchased from China, Hong Kong, and Macau DO NOT
+                support eSIM.
+              </li>
+            </ul>
+          </div>
+
+          {/* Divider */}
+          <div className="my-10 border-t border-dashed border-slate-300"></div>
+
+          {/* Method 2 */}
+          <div className="text-center mb-12">
+            <h3 className="text-xl md:text-3xl font-black text-[#0091ff] mb-4">
+              Method 2: Check directly on your device
+            </h3>
+            <p className="text-slate-600 text-lg">
+              Check if your device supports eSIM and whether it is
+              carrier-locked by following{" "}
+              <button className="text-[#0091ff] font-bold hover:underline">
+                the steps below
+              </button>
+            </p>
+          </div>
+
+          {/* Not Support */}
+          <div className="text-center mb-8">
+            <h3 className="text-xl md:text-3xl font-black text-[#0091ff] mb-4">
+              Your device does not support eSIM?
+            </h3>
+            <p className="text-slate-600 text-lg">
+              Try finding a physical SIM that is compatible with your device{" "}
+              <button className="text-[#0091ff] font-bold hover:underline">
+                here
+              </button>
+            </p>
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+};
+
+export default CompatibilityModal;

+ 127 - 22
EsimLao/esim-vite/src/components/Footer.tsx

@@ -1,5 +1,4 @@
-
-import React from 'react';
+import React from "react";
 
 const Footer: React.FC = () => {
   return (
@@ -9,50 +8,156 @@ const Footer: React.FC = () => {
         <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-12 mb-16">
           <div className="space-y-6">
             <div className="flex items-center space-x-2">
-              <svg className="w-8 h-8 text-[#EE0434]" viewBox="0 0 24 24" fill="currentColor">
+              <svg
+                className="w-8 h-8 text-[#EE0434]"
+                viewBox="0 0 24 24"
+                fill="currentColor"
+              >
                 <path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" />
               </svg>
-              <span className="text-xl font-black text-[#EE0434]">InfiGate</span>
+              <span className="text-xl font-black text-[#EE0434]">Getgo</span>
             </div>
-            <p className="text-sm font-bold text-[#EE0434] leading-tight">WellZone Technology Solutions Joint Stock Company</p>
-            <p className="text-sm font-medium text-slate-600">Tax Code: 0109310641</p>
+            <p className="text-sm font-bold text-[#EE0434] leading-tight">
+              Công ty TNHH Phát triển toàn cầu VIETTECH
+            </p>
+            <p className="text-sm font-medium text-slate-600">
+              Tax Code: 0901210362
+            </p>
             <div className="space-y-3 text-sm text-slate-500">
-              <p className="flex items-start"><svg className="w-4 h-4 mr-2 mt-0.5 text-[#EE0434]" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"/><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"/></svg> 10th Floor, 83B Ly Thuong Kiet, Cua Nam Ward, Hanoi City</p>
-              <p className="flex items-center"><svg className="w-4 h-4 mr-2 text-[#EE0434]" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/></svg> info@infigate.global</p>
-              <p className="flex items-center"><svg className="w-4 h-4 mr-2 text-[#EE0434]" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"/></svg> Zalo/Whatsapp: +84989145663</p>
+              <p className="flex items-start">
+                <svg
+                  className="w-4 h-4 mr-2 mt-0.5 text-[#EE0434]"
+                  fill="none"
+                  stroke="currentColor"
+                  viewBox="0 0 24 24"
+                >
+                  <path
+                    strokeLinecap="round"
+                    strokeLinejoin="round"
+                    strokeWidth={2}
+                    d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
+                  />
+                  <path
+                    strokeLinecap="round"
+                    strokeLinejoin="round"
+                    strokeWidth={2}
+                    d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
+                  />
+                </svg>{" "}
+                218 Đảo Dừa 1, Khu đô thị Vinhomes Oceanpark 2, Xã Nghĩa Trụ,
+                Tỉnh Hưng Yên, Việt Nam
+              </p>
+              <p className="flex items-center">
+                <svg
+                  className="w-4 h-4 mr-2 text-[#EE0434]"
+                  fill="none"
+                  stroke="currentColor"
+                  viewBox="0 0 24 24"
+                >
+                  <path
+                    strokeLinecap="round"
+                    strokeLinejoin="round"
+                    strokeWidth={2}
+                    d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
+                  />
+                </svg>{" "}
+                sale.admin@viettech.asia
+              </p>
+              <p className="flex items-center">
+                <svg
+                  className="w-4 h-4 mr-2 text-[#EE0434]"
+                  fill="none"
+                  stroke="currentColor"
+                  viewBox="0 0 24 24"
+                >
+                  <path
+                    strokeLinecap="round"
+                    strokeLinejoin="round"
+                    strokeWidth={2}
+                    d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"
+                  />
+                </svg>{" "}
+                Zalo/Whatsapp: +84972.978.296
+              </p>
             </div>
           </div>
           <div>
-            <h3 className="text-base font-bold text-slate-900 mb-6">InfiGate</h3>
+            <h3 className="text-base font-bold text-slate-900 mb-6">Getgo</h3>
             <ul className="space-y-4 text-sm text-slate-500">
-              <li><a href="#" className="hover:text-[#EE0434] transition-colors">About Us</a></li>
-              <li><a href="#" className="hover:text-[#EE0434] transition-colors">Terms Of Service</a></li>
-              <li><a href="#" className="hover:text-[#EE0434] transition-colors">Privacy Policy</a></li>
+              <li>
+                <a href="#" className="hover:text-[#EE0434] transition-colors">
+                  About Us
+                </a>
+              </li>
+              <li>
+                <a href="#" className="hover:text-[#EE0434] transition-colors">
+                  Terms Of Service
+                </a>
+              </li>
+              <li>
+                <a href="#" className="hover:text-[#EE0434] transition-colors">
+                  Privacy Policy
+                </a>
+              </li>
             </ul>
           </div>
           <div>
-            <h3 className="text-base font-bold text-slate-900 mb-6">Travel SIM</h3>
+            <h3 className="text-base font-bold text-slate-900 mb-6">
+              Travel SIM
+            </h3>
             <ul className="space-y-4 text-sm text-slate-500">
-              <li><a href="#" className="hover:text-[#EE0434] transition-colors">China SIM</a></li>
-              <li><a href="#" className="hover:text-[#EE0434] transition-colors">Thailand SIM</a></li>
-              <li><a href="#" className="hover:text-[#EE0434] transition-colors">Japan SIM</a></li>
+              <li>
+                <a href="#" className="hover:text-[#EE0434] transition-colors">
+                  China SIM
+                </a>
+              </li>
+              <li>
+                <a href="#" className="hover:text-[#EE0434] transition-colors">
+                  Thailand SIM
+                </a>
+              </li>
+              <li>
+                <a href="#" className="hover:text-[#EE0434] transition-colors">
+                  Japan SIM
+                </a>
+              </li>
             </ul>
           </div>
           <div>
             <h3 className="text-base font-bold text-slate-900 mb-6">Guide</h3>
             <ul className="space-y-4 text-sm text-slate-500">
-              <li><a href="#" className="hover:text-[#EE0434] transition-colors">What is eSIM</a></li>
-              <li><a href="#" className="hover:text-[#EE0434] transition-colors">Support</a></li>
+              <li>
+                <a href="#" className="hover:text-[#EE0434] transition-colors">
+                  What is eSIM
+                </a>
+              </li>
+              <li>
+                <a href="#" className="hover:text-[#EE0434] transition-colors">
+                  Support
+                </a>
+              </li>
             </ul>
           </div>
         </div>
 
         {/* Bottom */}
         <div className="pt-8 border-t border-slate-100 flex flex-col md:flex-row justify-between items-center space-y-4 md:space-y-0">
-          <p className="text-sm text-slate-400">© 2025 InfiGate All rights reserved</p>
+          <p className="text-sm text-slate-400">
+            © 2025 Getgo All rights reserved
+          </p>
           <div className="bg-[#EE0434] px-4 py-2 rounded-lg flex items-center space-x-2">
-            <div className="w-5 h-5 bg-white rounded-full flex items-center justify-center text-[#EE0434]"><svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20"><path fillRule="evenodd" 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" clipRule="evenodd"/></svg></div>
-            <span className="text-[10px] text-white font-bold uppercase tracking-widest">Official Store Verified</span>
+            <div className="w-5 h-5 bg-white rounded-full flex items-center justify-center text-[#EE0434]">
+              <svg className="w-3 h-3" fill="currentColor" viewBox="0 0 20 20">
+                <path
+                  fillRule="evenodd"
+                  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"
+                  clipRule="evenodd"
+                />
+              </svg>
+            </div>
+            <span className="text-[10px] text-white font-bold uppercase tracking-widest">
+              Official Store Verified
+            </span>
           </div>
         </div>
       </div>

+ 98 - 19
EsimLao/esim-vite/src/components/Header.tsx

@@ -11,6 +11,7 @@ import {
 } from "../features/loading/loadingSlice";
 import { productApi } from "../apis/productApi";
 import { Area } from "../services/product/type";
+import { accountLogout } from "../features/account/accuntSlice";
 
 const Header: React.FC = () => {
   const navigate = useNavigate();
@@ -18,6 +19,7 @@ const Header: React.FC = () => {
   const [isMenuOpen, setIsMenuOpen] = useState(false);
   const [isBuySimExpanded, setIsBuySimExpanded] = useState(false);
   const [isGuideExpanded, setIsGuideExpanded] = useState(false);
+  const [isUserMenuOpen, setIsUserMenuOpen] = useState(false);
   const [isBuySimMegaVisible, setIsBuySimMegaVisible] = useState(false);
   const [isGuideMegaVisible, setIsGuideMegaVisible] = useState(false);
   const [isLangMenuOpen, setIsLangMenuOpen] = useState(false);
@@ -29,6 +31,7 @@ const Header: React.FC = () => {
   const [products, setProducts] = useState<Area[]>([]);
   const dispatch = useAppDispatch();
   const langMenuRef = useRef<HTMLDivElement>(null);
+  const userMenuRef = useRef<HTMLDivElement>(null);
 
   const guideItems = [
     { label: "What is eSIM", path: "/support" },
@@ -128,7 +131,7 @@ const Header: React.FC = () => {
                 {/* <svg className="w-8 h-8 text-[#EE0434]" viewBox="0 0 24 24" fill="currentColor">
                   <path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" />
                 </svg> */}
-                <img src={logo} alt="InfiGate Logo" className="w-35" />
+                <img src={logo} alt="Getgo Logo" className="w-35" />
                 {/* <span className="text-2xl font-bold tracking-tighter">
                   <span className="text-[#EE0434]">Infi</span>
                   <span className="text-[#333]">Gate</span>
@@ -393,24 +396,83 @@ const Header: React.FC = () => {
 
             {/* Icons */}
             <div className="flex items-center space-x-5">
-              <Link
-                to="/login"
-                className="p-2 text-slate-700 hover:text-[#EE0434] transition-colors"
-              >
-                <svg
-                  className="w-6 h-6"
-                  fill="none"
-                  stroke="currentColor"
-                  viewBox="0 0 24 24"
+              {/* Account Dropdown */}
+              <div className="relative hidden lg:block" ref={userMenuRef}>
+                <button
+                  onClick={() => setIsUserMenuOpen(!isUserMenuOpen)}
+                  className="p-2 text-slate-700 hover:text-[#EE0434] transition-colors rounded-full hover:bg-slate-50"
+                  title="Account"
                 >
-                  <path
-                    strokeLinecap="round"
-                    strokeLinejoin="round"
-                    strokeWidth={2}
-                    d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
-                  />
-                </svg>
-              </Link>
+                  <svg
+                    className="w-6 h-6"
+                    fill="none"
+                    stroke="currentColor"
+                    viewBox="0 0 24 24"
+                  >
+                    <path
+                      strokeLinecap="round"
+                      strokeLinejoin="round"
+                      strokeWidth={2}
+                      d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"
+                    />
+                  </svg>
+                </button>
+
+                {isUserMenuOpen && (
+                  <div className="absolute top-full right-0 mt-3 w-56 bg-white rounded-[24px] shadow-[0_20px_40px_rgba(0,0,0,0.1)] border border-slate-50 overflow-hidden animate-in fade-in slide-in-from-top-2 duration-200 z-50">
+                    <div className="flex flex-col py-2">
+                      <button
+                        onClick={() => {
+                          // onViewChange(ViewMode.ORDER_HISTORY);
+                          navigate("/order-history");
+                          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"
+                      >
+                        <svg
+                          className="w-5 h-5 text-slate-400"
+                          fill="none"
+                          stroke="currentColor"
+                          viewBox="0 0 24 24"
+                        >
+                          <path
+                            strokeLinecap="round"
+                            strokeLinejoin="round"
+                            strokeWidth={2}
+                            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>
+                        <span className="font-bold text-sm">Orders</span>
+                      </button>
+                      <div className="h-px bg-slate-50 mx-4 my-1"></div>
+                      <button
+                        onClick={() => {
+                          // onViewChange(ViewMode.LOGIN); // Simulate logout
+                          dispatch(accountLogout());
+                          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"
+                      >
+                        <svg
+                          className="w-5 h-5 text-slate-400"
+                          fill="none"
+                          stroke="currentColor"
+                          viewBox="0 0 24 24"
+                        >
+                          <path
+                            strokeLinecap="round"
+                            strokeLinejoin="round"
+                            strokeWidth={2}
+                            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>
+                        <span className="font-bold text-sm">Logout</span>
+                      </button>
+                    </div>
+                  </div>
+                )}
+              </div>
               <button className="hidden sm:flex p-2 text-slate-700 hover:text-[#EE0434] relative">
                 <svg
                   className="w-6 h-6"
@@ -708,6 +770,20 @@ const Header: React.FC = () => {
               >
                 Contact
               </Link>
+              <button
+                onClick={() => {
+                  // onViewChange(ViewMode.ORDER_HISTORY);
+                  navigate("/order-history");
+                  setIsMenuOpen(false);
+                }}
+                className={`w-full text-center py-5 px-6 rounded-3xl text-2xl font-black transition-all ${
+                  isActive("/order-history")
+                    ? "bg-red-50 text-[#EE0434]"
+                    : "text-slate-800 hover:bg-slate-50"
+                }`}
+              >
+                Transaction History
+              </button>
 
               <div className="w-full pt-4">
                 <p className="text-center text-slate-400 font-bold text-xs uppercase tracking-widest mb-4">
@@ -740,7 +816,10 @@ const Header: React.FC = () => {
           <div className="p-8 border-t border-slate-50 bg-slate-50/50">
             <Link
               to="/login"
-              onClick={() => setIsMenuOpen(false)}
+              onClick={() => {
+                setIsMenuOpen(false);
+                dispatch(accountLogout());
+              }}
               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

+ 29 - 12
EsimLao/esim-vite/src/components/Popup.tsx

@@ -1,10 +1,12 @@
 import React from "react";
 import { useAppDispatch, useAppSelector } from "../hooks/useRedux";
 import { closePopup } from "../features/popup/popupSlice";
+import { useNavigate } from "react-router-dom";
 
 const Popup = () => {
   const popup = useAppSelector((state) => state.popup);
   const dispatch = useAppDispatch();
+  const navigate = useNavigate();
   if (!popup.isOpen) return null;
   return (
     <div className="fixed inset-0 z-[100] flex items-center justify-center p-4 animate-in fade-in duration-200">
@@ -70,18 +72,33 @@ const Popup = () => {
         </p>
 
         {/* Button */}
-        <button
-          onClick={() => {
-            dispatch(closePopup());
-          }}
-          className={`w-full py-4 rounded-2xl font-black text-lg shadow-lg active:scale-95 transition-all ${
-            popup.isSuccess
-              ? "bg-[#00c087] hover:bg-[#00a876] text-white shadow-green-200"
-              : "bg-[#EE0434] hover:bg-[#d10029] text-white shadow-red-200"
-          }`}
-        >
-          {popup.buttonText}
-        </button>
+        <div className="w-full flex flex-col md:flex-row gap-4 md:gap-6">
+          {popup.hasRightButton && (
+            <button
+              onClick={() => {
+                if (popup.rightButtonAction == "OPEN_ORDER_HISTORY") {
+                  navigate("/order-history");
+                }
+                dispatch(closePopup());
+              }}
+              className="mr-4 w-full py-4 rounded-2xl font-black text-lg shadow-lg active:scale-95 transition-all bg-[#EE0434] hover:bg-[#d10029] text-white shadow-red-200"
+            >
+              {popup.rightButtonText}
+            </button>
+          )}
+          <button
+            onClick={() => {
+              dispatch(closePopup());
+            }}
+            className={`w-full py-4 rounded-2xl font-black text-lg shadow-lg active:scale-95 transition-all ${
+              popup.isSuccess
+                ? "bg-[#00c087] hover:bg-[#00a876] text-white shadow-green-200"
+                : "bg-[#EE0434] hover:bg-[#d10029] text-white shadow-red-200"
+            }`}
+          >
+            {popup.buttonText}
+          </button>
+        </div>
       </div>
     </div>
   );

+ 1 - 2
EsimLao/esim-vite/src/components/ProductCard.tsx

@@ -28,8 +28,7 @@ const ProductCard: React.FC<{
       <p className="text-[#EE0434] font-bold text-xs md:text-lg">
         from:{" "}
         <span className="font-black">
-          {p.curency}
-          {p.minDisplayPrice}
+          {p.minDisplayPrice} {p.curency}
         </span>
       </p>
     </div>

+ 3 - 2
EsimLao/esim-vite/src/features/account/accuntSlice.ts

@@ -8,9 +8,10 @@ const accountSlice = createSlice({
   },
   reducers: {
     accountLogin: (state, action: PayloadAction<AccountInfo>) => {
+      console.log("data in slice: ", action.payload);
       localStorage.setItem("token", action.payload.accessToken);
       localStorage.setItem("refreshToken", action.payload.refreshToken);
-      localStorage.setItem("accountInfo", JSON.stringify(action.payload));
+      // localStorage.setItem("accountInfo", JSON.stringify(action.payload));
       return {
         ...state,
         account: action.payload,
@@ -19,7 +20,7 @@ const accountSlice = createSlice({
     accountLogout: (state) => {
       localStorage.removeItem("token");
       localStorage.removeItem("refreshToken");
-      localStorage.removeItem("accountInfo");
+      // localStorage.removeItem("accountInfo");
       return {
         ...state,
         account: null,

+ 21 - 2
EsimLao/esim-vite/src/features/popup/popupSlice.ts

@@ -5,10 +5,14 @@ const popupSlice = createSlice({
   name: "popup",
   initialState: {
     isOpen: false,
+    isOpenCompatibilityModal: false,
     isSuccess: true,
     title: "Notification",
     message: "",
-    buttonText: "OK",
+    buttonText: "Close",
+    hasRightButton: false,
+    rightButtonText: "Confirm",
+    rightButtonAction: "",
   },
   reducers: {
     openPopup: (state, action: PayloadAction<any>) => {
@@ -18,13 +22,28 @@ const popupSlice = createSlice({
         isSuccess: action.payload?.isSuccess ?? true,
         title: action.payload?.title ?? "Notification",
         message: action.payload?.message,
+        buttonText: action.payload?.buttonText ?? "Close",
+        hasRightButton: action.payload?.hasRightButton ?? false,
+        rightButtonText: action.payload?.rightButtonText ?? "Confirm",
+        rightButtonAction: action.payload?.rightButtonAction ?? "",
       };
     },
     closePopup: (state) => {
       state.isOpen = false;
     },
+    openCompatibilityModal: (state) => {
+      state.isOpenCompatibilityModal = true;
+    },
+    closeCompatibilityModal: (state) => {
+      state.isOpenCompatibilityModal = false;
+    },
   },
 });
 
-export const { openPopup, closePopup } = popupSlice.actions;
+export const {
+  openPopup,
+  closePopup,
+  openCompatibilityModal,
+  closeCompatibilityModal,
+} = popupSlice.actions;
 export default popupSlice.reducer;

+ 27 - 9
EsimLao/esim-vite/src/pages/checkout/CheckoutView.tsx

@@ -292,10 +292,18 @@ const CheckoutView = () => {
               </div>
               <div className="text-right">
                 <p className="text-xs text-slate-400 line-through font-bold">
-                  ${state.checkoutDetails.paymentMoney}
+                  $
+                  {state.checkoutDetails.paymentMoney.toLocaleString("vi-VN", {
+                    minimumFractionDigits: 2,
+                    maximumFractionDigits: 2,
+                  })}
                 </p>
                 <p className="text-[#0071e3] font-black text-xl">
-                  ${state.checkoutDetails.totalMoney}
+                  $
+                  {state.checkoutDetails.totalMoney.toLocaleString("vi-VN", {
+                    minimumFractionDigits: 2,
+                    maximumFractionDigits: 2,
+                  })}
                 </p>
               </div>
             </div>
@@ -401,14 +409,23 @@ const CheckoutView = () => {
                 <span>Subtotal:</span>
                 <span>
                   $
-                  {(state.checkoutDetails.totalMoney * state.quantity).toFixed(
-                    1
-                  )}
+                  {(
+                    state.checkoutDetails.totalMoney * state.quantity
+                  ).toLocaleString("vi-VN", {
+                    minimumFractionDigits: 2,
+                    maximumFractionDigits: 2,
+                  })}
                 </span>
               </div>
               <div className="flex justify-between text-green-600 font-bold">
                 <span>Discount:</span>
-                <span>-${(0 * state.quantity).toFixed(1)}</span>
+                <span>
+                  -$
+                  {(0 * state.quantity).toLocaleString("vi-VN", {
+                    minimumFractionDigits: 2,
+                    maximumFractionDigits: 2,
+                  })}
+                </span>
               </div>
               <div className="flex justify-between items-center pt-4 border-t border-slate-200/50">
                 <span className="text-[#0071e3] font-black text-xl">
@@ -416,9 +433,10 @@ const CheckoutView = () => {
                 </span>
                 <span className="text-[#0071e3] font-black text-3xl">
                   $
-                  {(
-                    state.checkoutDetails.paymentMoney * state.quantity
-                  ).toFixed(1)}
+                  {state.checkoutDetails.paymentMoney.toLocaleString("vi-VN", {
+                    minimumFractionDigits: 2,
+                    maximumFractionDigits: 2,
+                  })}
                 </span>
               </div>
             </div>

+ 51 - 12
EsimLao/esim-vite/src/pages/contact/ContactView.tsx

@@ -1,5 +1,4 @@
-
-import React from 'react';
+import React from "react";
 
 const ContactView: React.FC = () => {
   return (
@@ -7,29 +6,69 @@ const ContactView: React.FC = () => {
       <section className="max-w-7xl mx-auto px-6 md:px-12 py-16 md:py-24">
         <div className="grid grid-cols-1 lg:grid-cols-2 gap-16 items-start">
           <div className="space-y-8">
-            <h1 className="text-5xl md:text-7xl font-black text-[#EE0434] tracking-tight leading-tight">Get in touch <br /> with us</h1>
-            <p className="text-slate-500 text-lg md:text-xl font-medium leading-relaxed max-w-lg">We're always ready to listen and support you with any questions. Leave your information, and our team will get back to you as soon as possible.</p>
+            <h1 className="text-5xl md:text-7xl font-black text-[#EE0434] tracking-tight leading-tight">
+              Get in touch <br /> with us
+            </h1>
+            <p className="text-slate-500 text-lg md:text-xl font-medium leading-relaxed max-w-lg">
+              We're always ready to listen and support you with any questions.
+              Leave your information, and our team will get back to you as soon
+              as possible.
+            </p>
             <div className="space-y-6 pt-4">
               <div className="space-y-2">
                 <div className="flex items-center space-x-3 text-slate-800">
-                   <svg className="w-6 h-6 text-[#EE0434]" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/></svg>
-                   <span className="font-bold text-lg">Email</span>
+                  <svg
+                    className="w-6 h-6 text-[#EE0434]"
+                    fill="none"
+                    stroke="currentColor"
+                    viewBox="0 0 24 24"
+                  >
+                    <path
+                      d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
+                      strokeWidth={2}
+                      strokeLinecap="round"
+                      strokeLinejoin="round"
+                    />
+                  </svg>
+                  <span className="font-bold text-lg">Email</span>
                 </div>
-                <a href="mailto:info@infigate.global" className="block text-[#EE0434] font-black text-lg md:text-xl pl-9 hover:underline">info@infigate.global</a>
+                <a
+                  href="mailto:info@getgo.global"
+                  className="block text-[#EE0434] font-black text-lg md:text-xl pl-9 hover:underline"
+                >
+                  info@getgo.global
+                </a>
               </div>
             </div>
           </div>
           <div className="bg-white p-2">
             <form className="space-y-8" onSubmit={(e) => e.preventDefault()}>
               <div className="space-y-3">
-                <label className="block text-slate-800 font-bold text-lg">Full Name</label>
-                <input type="text" placeholder="Enter your full name" className="w-full bg-white border border-slate-200 rounded-xl py-4 px-6 focus:outline-none focus:ring-2 focus:ring-red-50 focus:border-[#EE0434] transition-all" />
+                <label className="block text-slate-800 font-bold text-lg">
+                  Full Name
+                </label>
+                <input
+                  type="text"
+                  placeholder="Enter your full name"
+                  className="w-full bg-white border border-slate-200 rounded-xl py-4 px-6 focus:outline-none focus:ring-2 focus:ring-red-50 focus:border-[#EE0434] transition-all"
+                />
               </div>
               <div className="space-y-3">
-                <label className="block text-slate-800 font-bold text-lg">Specific Requirements</label>
-                <textarea rows={6} placeholder="Describe your requirements" className="w-full bg-white border border-slate-200 rounded-2xl py-4 px-6 focus:outline-none focus:ring-2 focus:ring-red-50 focus:border-[#EE0434] transition-all" />
+                <label className="block text-slate-800 font-bold text-lg">
+                  Specific Requirements
+                </label>
+                <textarea
+                  rows={6}
+                  placeholder="Describe your requirements"
+                  className="w-full bg-white border border-slate-200 rounded-2xl py-4 px-6 focus:outline-none focus:ring-2 focus:ring-red-50 focus:border-[#EE0434] transition-all"
+                />
               </div>
-              <button type="submit" className="w-full bg-[#EE0434] text-white py-5 rounded-[40px] font-black text-2xl shadow-lg hover:shadow-xl transition-all">Submit</button>
+              <button
+                type="submit"
+                className="w-full bg-[#EE0434] text-white py-5 rounded-[40px] font-black text-2xl shadow-lg hover:shadow-xl transition-all"
+              >
+                Submit
+              </button>
             </form>
           </div>
         </div>

+ 0 - 136
EsimLao/esim-vite/src/pages/handbook/HandbookView.tsx

@@ -1,136 +0,0 @@
-
-import React from 'react';
-import { HandbookArticle } from '../types';
-
-interface HandbookViewProps {
-  onBack: () => void;
-  onArticleSelect: (article: HandbookArticle) => void;
-}
-
-const HandbookView: React.FC<HandbookViewProps> = ({ onBack, onArticleSelect }) => {
-  const articles: HandbookArticle[] = [
-    {
-      id: '1',
-      title: 'Siêu Sale Mùa Lễ Hội Cùng InfiGate – Giảm Đến 35% Khi Mua eSIM Du Lịch Quốc Tế',
-      date: '01/12/2025',
-      excerpt: 'Siêu Sale Mùa Lễ Hội Cùng InfiGate – Giảm Đến 35% Thời gian áp dụng ưu đãi: 01/12 – 31/12/2025',
-      image: 'https://images.unsplash.com/photo-1549463599-231a5401977e?q=80&w=800&auto=format&fit=crop'
-    },
-    {
-      id: '2',
-      title: 'Du lịch châu Á tháng 12: Mùa lễ hội rực rỡ và những trải nghiệm không thể bỏ lỡ – Đừng quên chuẩn bị SIM du lịch trước khi khởi hành!',
-      date: '28/11/2025',
-      excerpt: 'Tháng 12 là thời điểm lý tưởng để khám phá châu Á: từ mùa tuyết trắng Nhật – Hàn, biển ấm Đông...',
-      image: 'https://images.unsplash.com/photo-1493976040374-85c8e12f0c0e?q=80&w=800&auto=format&fit=crop'
-    },
-    {
-      id: '3',
-      title: 'InfiGate ra mắt bộ đôi eSIM & SIM du lịch – Mở rộng lựa chọn, tối ưu kết nối toàn cầu',
-      date: '27/11/2025',
-      excerpt: 'Khi nhu cầu du lịch bùng nổ mạnh mẽ, Internet trở thành “vũ khí tối thượng” cho mọi chuyến đi: dẫn...',
-      image: 'https://images.unsplash.com/photo-1526772662000-3f88f10c053b?q=80&w=800&auto=format&fit=crop'
-    },
-    {
-      id: '4',
-      title: 'Hướng dẫn cách cài đặt eSIM du lịch trên Samsung',
-      date: '14/11/2025',
-      excerpt: 'Hướng dẫn cài đặt eSIM du lịch trên Samsung nhanh và đơn giản. Áp dụng cho Galaxy S, A, Z. Gợi ý eSIM...',
-      image: 'https://images.unsplash.com/photo-1610945265064-0e34e5519bbf?q=80&w=800&auto=format&fit=crop'
-    },
-    {
-      id: '5',
-      title: 'Hướng dẫn cách cài đặt eSIM du lịch trên iPhone',
-      date: '14/11/2025',
-      excerpt: 'Hướng dẫn chi tiết cách kích hoạt eSIM trên các dòng iPhone từ XS trở lên. Giải pháp kết nối tối ưu...',
-      image: 'https://images.unsplash.com/photo-1510557880182-3d4d3cba35a5?q=80&w=800&auto=format&fit=crop'
-    },
-    {
-      id: '6',
-      title: 'Top 5 điểm đến mùa thu tuyệt đẹp tại Nhật Bản',
-      date: '05/11/2025',
-      excerpt: 'Khám phá vẻ đẹp của lá đỏ tại Kyoto, Tokyo và Hokkaido. Kinh nghiệm chuẩn bị SIM 4G tốc độ cao...',
-      image: 'https://images.unsplash.com/photo-1528114039593-4366cc08227d?q=80&w=800&auto=format&fit=crop'
-    },
-    {
-      id: '7',
-      title: 'Sự khác biệt giữa SIM vật lý và eSIM cho người đi du lịch',
-      date: '01/11/2025',
-      excerpt: 'Lựa chọn nào tốt nhất cho chuyến đi của bạn? So sánh tính tiện dụng, khả năng tương thích...',
-      image: 'https://images.unsplash.com/photo-1551817958-1110f7abe310?q=80&w=800&auto=format&fit=crop'
-    },
-    {
-      id: '8',
-      title: 'Bí kíp du lịch Thái Lan tự túc tiết kiệm',
-      date: '25/10/2025',
-      excerpt: 'Tổng hợp kinh nghiệm ăn chơi, di chuyển và đặt eSIM Thái Lan giá rẻ nhất chỉ từ 15k/ngày...',
-      image: 'https://images.unsplash.com/photo-1528181304800-2f140819898f?q=80&w=1000&auto=format&fit=crop'
-    }
-  ];
-
-  return (
-    <div className="bg-[#fcfdfe] min-h-screen">
-      {/* Breadcrumbs */}
-      <div className="max-w-7xl mx-auto px-4 py-4 md:py-6 border-b border-slate-50">
-        <nav className="flex items-center space-x-2 text-xs md:text-sm text-slate-500 font-medium">
-          <button onClick={onBack} className="hover:text-blue-500">Home</button>
-          <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
-            <path d="M9 5l7 7-7 7" strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
-          </svg>
-          <span className="text-slate-900 font-bold">Handbook</span>
-        </nav>
-      </div>
-
-      <div className="max-w-7xl mx-auto px-4 py-12">
-        <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 md:gap-8">
-          {articles.map((article) => (
-            <div 
-              key={article.id} 
-              onClick={() => onArticleSelect(article)}
-              className="bg-white rounded-[24px] overflow-hidden shadow-sm hover:shadow-xl transition-all duration-300 flex flex-col group cursor-pointer border border-slate-50"
-            >
-              {/* Card Image */}
-              <div className="relative aspect-[4/3] overflow-hidden">
-                <img 
-                  src={article.image} 
-                  alt={article.title} 
-                  className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110"
-                />
-              </div>
-
-              {/* Card Content */}
-              <div className="p-6 flex flex-col flex-1 space-y-3">
-                <div className="flex items-center text-slate-400 space-x-2">
-                   <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
-                     <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
-                   </svg>
-                   <span className="text-xs font-bold">{article.date}</span>
-                </div>
-                
-                <h3 className="text-[16px] md:text-[18px] font-black text-slate-800 leading-tight group-hover:text-[#004ea8] transition-colors line-clamp-3">
-                  {article.title}
-                </h3>
-                
-                <p className="text-xs md:text-sm text-slate-500 font-medium line-clamp-3 leading-relaxed">
-                  {article.excerpt}
-                </p>
-              </div>
-            </div>
-          ))}
-        </div>
-
-        {/* Pagination Placeholder */}
-        <div className="mt-16 flex justify-center items-center space-x-2">
-           <button className="w-10 h-10 rounded-full bg-[#004ea8] text-white font-black text-sm">1</button>
-           <button className="w-10 h-10 rounded-full bg-white text-slate-400 border border-slate-100 font-black text-sm hover:border-[#004ea8] hover:text-[#004ea8] transition-colors">2</button>
-           <button className="w-10 h-10 rounded-full bg-white text-slate-400 border border-slate-100 font-black text-sm hover:border-[#004ea8] hover:text-[#004ea8] transition-colors">
-             <svg className="w-4 h-4 mx-auto" fill="none" stroke="currentColor" viewBox="0 0 24 24">
-               <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5l7 7-7 7" />
-             </svg>
-           </button>
-        </div>
-      </div>
-    </div>
-  );
-};
-
-export default HandbookView;

+ 28 - 5
EsimLao/esim-vite/src/pages/home/HomeView.tsx

@@ -7,6 +7,7 @@ import HomeProduct from "./components/HomeProduct";
 import HomeSearch from "./components/HomeSearch";
 import HomeFaq from "./components/HomeFaq";
 import { useAppDispatch } from "../../hooks/useRedux";
+import { openPopup } from "../../features/popup/popupSlice";
 
 const HomeView: React.FC = () => {
   const [simType, setSimType] = useState<"eSIM" | "Physical">("eSIM");
@@ -16,9 +17,31 @@ const HomeView: React.FC = () => {
 
   useEffect(() => {
     const params = new URLSearchParams(window.location.search);
-    const id = params.get("vpc_TxnResponseCode");
-    if (id) {
-      dis;
+    const status = params.get("vpc_TxnResponseCode");
+    if (status) {
+      console.log("URL Params:", params);
+      if (status === "0") {
+        dispatch(
+          openPopup({
+            isSuccess: true,
+            message: "Payment successful!",
+            title: "Payment Status",
+            buttonText: "Close",
+            hasRightButton: true,
+            rightButtonText: "View Order",
+            rightButtonAction: "OPEN_ORDER_HISTORY",
+          })
+        );
+      } else {
+        dispatch(
+          openPopup({
+            isSuccess: false,
+            message: "Payment failed or cancelled.",
+            title: "Payment Status",
+            buttonText: "Close",
+          })
+        );
+      }
     }
   }, []);
 
@@ -69,7 +92,7 @@ const HomeView: React.FC = () => {
       <section className="py-16 md:py-24 bg-white px-4">
         <div className="max-w-7xl mx-auto">
           <h2 className="text-3xl md:text-6xl font-black text-center mb-12 md:mb-20 tracking-tight text-slate-900">
-            Why <span className="text-[#EE0434]">choose InfiGate</span>
+            Why <span className="text-[#EE0434]">choose Getgo</span>
           </h2>
           <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">
             <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>
@@ -227,7 +250,7 @@ const HomeView: React.FC = () => {
       <section className="py-12 md:py-20 bg-white px-4">
         <div className="max-w-7xl mx-auto space-y-10 md:space-y-16">
           <h2 className="text-3xl md:text-6xl font-black text-center tracking-tight text-slate-900">
-            InfiGate has <span className="text-[#EE0434]">partnered with</span>
+            Getgo has <span className="text-[#EE0434]">partnered with</span>
           </h2>
 
           <div className="space-y-8 md:space-y-12 max-w-5xl mx-auto">

+ 7 - 4
EsimLao/esim-vite/src/pages/home/components/HomeProduct.tsx

@@ -105,12 +105,15 @@ const HomeProduct = () => {
                 {p.areaName1}
               </h3>
               <p className="text-xs text-slate-400 line-through mb-1">
-                {p.curency}
-                {p.minSellPrice}
+                {p.minSellPrice} {p.curency}
               </p>
               <p className="text-lg md:text-2xl font-black text-[#EE0434]">
-                From {p.curency}
-                {p.minDisplayPrice}
+                From{" "}
+                {p.minDisplayPrice.toLocaleString("vi-VN", {
+                  minimumFractionDigits: 2,
+                  maximumFractionDigits: 2,
+                })}{" "}
+                {p.curency}
               </p>
             </div>
           ))}

+ 14 - 6
EsimLao/esim-vite/src/pages/home/components/HomeSearch.tsx

@@ -1,4 +1,8 @@
+import { openCompatibilityModal } from "../../../features/popup/popupSlice";
+import { useDispatch } from "react-redux";
+
 const HomeSearch = () => {
+  const dispatch = useDispatch();
   return (
     <section className="pt-8 pb-4 px-4 relative z-40 bg-white">
       <div className="max-w-4xl mx-auto w-full relative">
@@ -28,18 +32,22 @@ const HomeSearch = () => {
           </button>
         </div>
         <div className="mt-3 flex justify-center gap-2 text-sm text-slate-400 font-medium">
-          <span>Popular:</span>
-          <button className="hover:text-[#EE0434] transition-colors">
-            Japan
-          </button>
-          ,
+          <a
+            className="hover:text-[#EE0434] transition-colors underline color-hover cursor-pointer text-[18px] font-medium"
+            onClick={() => {
+              dispatch(openCompatibilityModal());
+            }}
+          >
+            Does my phone support eSIM?
+          </a>
+          {/* ,
           <button className="hover:text-[#EE0434] transition-colors">
             Thailand
           </button>
           ,
           <button className="hover:text-[#EE0434] transition-colors">
             China
-          </button>
+          </button> */}
         </div>
       </div>
     </section>

+ 3 - 5
EsimLao/esim-vite/src/pages/login/LoginView.tsx

@@ -16,7 +16,7 @@ const LoginView: React.FC = () => {
   const navigate = useNavigate();
   const loading = useAppSelector((state) => state.loading);
   const dispatch = useAppDispatch();
-  const [email, setEmail] = useState("");
+  const [email, setEmail] = useState("abc@gmail.com");
   const [step, setStep] = useState<"email" | "otp">("email");
 
   const [otp, setOtp] = useState("");
@@ -97,11 +97,9 @@ const LoginView: React.FC = () => {
       dispatch(stopSmallLoading());
       console.log("Verify otp response data:", data);
       if (data && data.errorCode === "0") {
-        console.log("Verify otp successful");
+        console.log("Verify otp successful " + data.data.accessToken);
         // save token to local storage
         dispatch(accountLogin(data.data));
-        // redirect to home
-        dispatch(startLoading({ message: "Logging in..." }));
         navigate("/");
       } else {
         console.error("Verify otp failed, no token received");
@@ -166,7 +164,7 @@ const LoginView: React.FC = () => {
               Welcome Back!
             </h1>
             <p className="text-slate-400 font-medium text-base md:text-lg">
-              Stay connected everywhere with InfiGate.
+              Stay connected everywhere with Getgo.
             </p>
           </div>
           {step === "email" && (

+ 516 - 0
EsimLao/esim-vite/src/pages/order-detail/OrderDetailView.tsx

@@ -0,0 +1,516 @@
+import {
+  DataUsage,
+  OrderDetail,
+  OrderHistory,
+} from "../../services/product/type";
+import { productApi } from "../../apis/productApi";
+import { startLoading, stopLoading } from "../../features/loading/loadingSlice";
+import { useAppDispatch } from "../../hooks/useRedux";
+import { useMutation } from "@tanstack/react-query";
+import React, { useState, useEffect } from "react";
+import { useLocation, useNavigate } from "react-router-dom";
+
+const OrderDetailView = () => {
+  const location = useLocation();
+  const dispatch = useAppDispatch();
+  const navigate = useNavigate();
+  const [activeTab, setActiveTab] = useState<"detail" | "manage">("detail");
+  const [orderDetails, setOrderDetails] = useState<OrderDetail[] | []>([]);
+  const [dataUsage, setDataUsage] = useState<DataUsage[] | []>([]);
+  const state = location.state as {
+    id: number;
+    orderHistory: OrderHistory;
+  };
+
+  useEffect(() => {
+    getOrderDetailMutation.mutate();
+  }, []);
+
+  const getOrderDetailMutation = useMutation({
+    mutationFn: async () => {
+      dispatch(startLoading({}));
+      const res = await productApi.getOrderDetail({
+        orderId: state.id,
+      });
+      return res;
+    },
+    onSuccess: (data) => {
+      dispatch(stopLoading());
+      console.log("Get order detail response data:", data);
+      if (data && data.errorCode === "0") {
+        console.log("Get order detail successful");
+        setOrderDetails(data.data ?? []);
+      } else {
+        console.error("Get order detail failed, no token received");
+      }
+    },
+    onError: (error: any) => {
+      dispatch(stopLoading());
+      console.error("Get order detail error:", error.response.data);
+    },
+  });
+
+  const handleTabChange = async (tab: "detail" | "manage") => {
+    if (tab === "manage") {
+      // Load manage data if needed
+      dispatch(startLoading({}));
+      try {
+        const results = await Promise.all(
+          orderDetails.map(async (element) => {
+            const res = await productApi.checkDataUsage({
+              iccid: element?.iccid,
+            });
+
+            if (res && res.errorCode === "0") {
+              return res.data;
+            }
+
+            return null;
+          })
+        );
+
+        const validResults = results.filter(Boolean);
+        setDataUsage(validResults);
+
+        console.log("All data usage results:", validResults);
+      } catch (error) {
+        console.error("Check data usage error:", error);
+      } finally {
+        dispatch(stopLoading());
+      }
+    }
+    setActiveTab(tab);
+  };
+
+  // Circular Progress Component
+  const CircularProgress = ({
+    used,
+    total,
+    label,
+  }: {
+    used: number;
+    total: number;
+    label: string;
+  }) => {
+    const radius = 50;
+    const circumference = 2 * Math.PI * radius;
+    const percentage = (used / total) * 100;
+    const strokeDashoffset = circumference - (percentage / 100) * circumference;
+
+    return (
+      <div className="flex flex-col items-center">
+        <div className="relative w-32 h-32 md:w-40 md:h-40 flex items-center justify-center mb-4">
+          <svg
+            className="transform -rotate-90 w-full h-full"
+            viewBox="0 0 120 120"
+          >
+            <circle
+              cx="60"
+              cy="60"
+              r={radius}
+              stroke="#f1f5f9"
+              strokeWidth="10"
+              fill="transparent"
+            />
+            <circle
+              cx="60"
+              cy="60"
+              r={radius}
+              stroke="#00b0f0"
+              strokeWidth="10"
+              fill="transparent"
+              strokeDasharray={circumference}
+              strokeDashoffset={strokeDashoffset}
+              strokeLinecap="round"
+            />
+          </svg>
+          <div className="absolute flex flex-col items-center">
+            <span className="text-xl md:text-2xl font-bold text-[#00b0f0]">
+              {used} / <br />
+              {total}
+            </span>
+            <span className="text-sm md:text-xl font-bold text-[#00b0f0]">
+              MB
+            </span>
+          </div>
+
+          {/* <div className="absolute bottom-2 left-0 text-[10px] font-bold text-slate-400">
+            0 MB
+          </div>
+          <div className="absolute bottom-2 right-0 text-[10px] font-bold text-slate-400">
+            {total} MB
+          </div> */}
+        </div>
+        <p className="text-slate-400 text-sm font-bold uppercase tracking-widest">
+          {label}
+        </p>
+      </div>
+    );
+  };
+  const getStatusColor = (status: number) => {
+    switch (status) {
+      case 2:
+        return "bg-[#00c087] text-white";
+      case 1:
+        return "bg-orange-400 text-white";
+      default:
+        return "bg-slate-800 text-white";
+    }
+  };
+  return (
+    <div className="bg-[#fcfdfe] min-h-screen pb-20">
+      {/* Breadcrumb */}
+      <div className="max-w-7xl mx-auto px-4 py-4 md:py-6 border-b border-slate-50">
+        <nav className="flex items-center space-x-2 text-xs md:text-sm text-slate-500 font-medium">
+          <button
+            // onClick={() => onViewChange(ViewMode.HOME)}
+            className="hover:text-[#EE0434] text-[16px]"
+          >
+            Home
+          </button>
+          <svg
+            className="w-3 h-3"
+            fill="none"
+            stroke="currentColor"
+            viewBox="0 0 24 24"
+          >
+            <path
+              d="M9 5l7 7-7 7"
+              strokeWidth={2.5}
+              strokeLinecap="round"
+              strokeLinejoin="round"
+            />
+          </svg>
+          <button
+            // onClick={() => onViewChange(ViewMode.ORDER_HISTORY)}
+            className="hover:text-[#EE0434] text-[16px] font-bold"
+          >
+            My eSIM
+          </button>
+        </nav>
+      </div>
+
+      <div className="max-w-5xl mx-auto px-4 py-8">
+        {/* Back Button */}
+        <button
+          onClick={() => navigate(-1)}
+          className="mb-6 flex items-center space-x-2 px-4 py-2 bg-white border border-slate-200 rounded-lg text-slate-600 font-bold hover:bg-slate-50 transition-colors shadow-sm"
+        >
+          <svg
+            className="w-4 h-4"
+            fill="none"
+            stroke="currentColor"
+            viewBox="0 0 24 24"
+          >
+            <path
+              strokeLinecap="round"
+              strokeLinejoin="round"
+              strokeWidth={2}
+              d="M10 19l-7-7 7-7m8 14l-7-7 7-7"
+            />
+          </svg>
+          <span>Back</span>
+        </button>
+
+        {/* Main Info Card */}
+        <div className="bg-white rounded-2xl border border-[#00b0f0]/30 shadow-sm overflow-hidden mb-8">
+          <div className="p-6">
+            {/* Header */}
+            <div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4 mb-6">
+              <div className="flex items-center gap-4">
+                <div className="w-10 h-10 bg-[#00b0f0] rounded-xl flex items-center justify-center text-white shadow-sm">
+                  <svg
+                    className="w-6 h-6"
+                    fill="none"
+                    stroke="currentColor"
+                    viewBox="0 0 24 24"
+                  >
+                    <path
+                      strokeLinecap="round"
+                      strokeLinejoin="round"
+                      strokeWidth={2}
+                      d="M3 3h2l.4 2M7 13h10l4-8H5.4M7 13L5.4 5M7 13l-2.293 2.293c-.63.63-.184 1.707.707 1.707H17m0 0a2 2 0 100 4 2 2 0 000-4zm-8 2a2 2 0 11-4 0 2 2 0 014 0z"
+                    />
+                  </svg>
+                </div>
+                <span className="text-xl md:text-2xl font-bold text-slate-800">
+                  {state.orderHistory?.id}
+                </span>
+              </div>
+              {state.orderHistory?.status === 2 ? (
+                <span
+                  className={`px-3 py-1 rounded-md text-sm font-bold shadow-sm ${getStatusColor(
+                    state.orderHistory?.status
+                  )}`}
+                >
+                  Success
+                </span>
+              ) : state.orderHistory?.status === 1 ? (
+                <span
+                  className={`px-3 py-1 rounded-md text-sm font-bold shadow-sm ${getStatusColor(
+                    state.orderHistory?.status
+                  )}`}
+                >
+                  Pending Payment
+                </span>
+              ) : (
+                <span
+                  className={`px-3 py-1 rounded-md text-sm font-bold shadow-sm ${getStatusColor(
+                    state.orderHistory?.status
+                  )}`}
+                >
+                  Failure
+                </span>
+              )}
+            </div>
+
+            {/* Tabs */}
+            <div className="flex space-x-1 mb-8">
+              <button
+                onClick={() => handleTabChange("detail")}
+                className={`px-6 py-2 rounded-lg font-bold text-sm transition-colors text-[16px] ${
+                  activeTab === "detail"
+                    ? "bg-[#00b0f0] text-white"
+                    : "text-[#00b0f0] hover:bg-[#00b0f0]/10"
+                }`}
+              >
+                Detail
+              </button>
+              <button
+                onClick={() => handleTabChange("manage")}
+                className={`px-6 py-2 rounded-lg font-bold text-sm transition-colors text-[16px] ${
+                  activeTab === "manage"
+                    ? "bg-[#00b0f0] text-white"
+                    : "text-[#00b0f0] hover:bg-[#00b0f0]/10"
+                }`}
+              >
+                Manage
+              </button>
+            </div>
+
+            {/* Divider with Total */}
+            <div className="relative flex items-center justify-center border-t border-slate-100 py-8">
+              <span className="bg-white px-4 text-lg md:text-xl font-bold text-slate-800 absolute">
+                Total:{" "}
+                {state.orderHistory?.totalMoney.toLocaleString("vi-VN", {
+                  minimumFractionDigits: 2,
+                  maximumFractionDigits: 2,
+                })}{" "}
+                <span className="text-slate-500 font-normal">
+                  ({state.orderHistory?.curency})
+                </span>
+              </span>
+            </div>
+
+            {/* Customer Info Grid */}
+            <div className="grid grid-cols-1 md:grid-cols-4 gap-6 md:gap-4 mt-4">
+              <div className="flex flex-col">
+                <div className="flex items-center gap-2 text-slate-400 mb-1">
+                  <svg
+                    className="w-4 h-4"
+                    fill="none"
+                    stroke="currentColor"
+                    viewBox="0 0 24 24"
+                  >
+                    <path
+                      strokeLinecap="round"
+                      strokeLinejoin="round"
+                      strokeWidth={2}
+                      d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
+                    />
+                  </svg>
+                  <span className="text-xs font-bold text-[16px]">
+                    Full name
+                  </span>
+                </div>
+                <span className="text-slate-800 font-semibold text-sm text-[16px]">
+                  {state?.orderHistory.customerInfo.surName}{" "}
+                  {state?.orderHistory.customerInfo.lastName}
+                </span>
+              </div>
+              <div className="flex flex-col">
+                <div className="flex items-center gap-2 text-slate-400 mb-1">
+                  <svg
+                    className="w-4 h-4"
+                    fill="none"
+                    stroke="currentColor"
+                    viewBox="0 0 24 24"
+                  >
+                    <path
+                      strokeLinecap="round"
+                      strokeLinejoin="round"
+                      strokeWidth={2}
+                      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"
+                    />
+                  </svg>
+                  <span className="text-xs font-bold text-[16px]">
+                    Phone number
+                  </span>
+                </div>
+                <span className="text-slate-800 font-semibold text-sm text-[16px]">
+                  {state?.orderHistory.customerInfo.phoneNumber}
+                </span>
+              </div>
+              <div className="flex flex-col">
+                <div className="flex items-center gap-2 text-slate-400 mb-1">
+                  <svg
+                    className="w-4 h-4"
+                    fill="none"
+                    stroke="currentColor"
+                    viewBox="0 0 24 24"
+                  >
+                    <path
+                      strokeLinecap="round"
+                      strokeLinejoin="round"
+                      strokeWidth={2}
+                      d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
+                    />
+                  </svg>
+                  <span className="text-xs font-bold text-[16px]">Email</span>
+                </div>
+                <span className="text-slate-800 font-semibold text-sm text-[16px] break-all">
+                  {state?.orderHistory.customerInfo.email}
+                </span>
+              </div>
+              <div className="flex flex-col">
+                <div className="flex items-center gap-2 text-slate-400 mb-1">
+                  <svg
+                    className="w-4 h-4"
+                    fill="none"
+                    stroke="currentColor"
+                    viewBox="0 0 24 24"
+                  >
+                    <path
+                      strokeLinecap="round"
+                      strokeLinejoin="round"
+                      strokeWidth={2}
+                      d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 00-3 3z"
+                    />
+                  </svg>
+                  <span className="text-xs font-bold text-[16px]">
+                    Payment method
+                  </span>
+                </div>
+                <span className="text-slate-800 font-semibold text-sm text-[16px]">
+                  ONEPAY
+                </span>
+              </div>
+            </div>
+          </div>
+        </div>
+
+        {/* Product Section */}
+        <div className="space-y-4">
+          {orderDetails.length > 0 && (
+            <div className="flex items-center space-x-3">
+              <img
+                src={`${orderDetails[0].areaIconUrl}`}
+                className="w-8 h-8 rounded-full object-cover border border-slate-200"
+              />
+              <h3 className="text-lg font-bold text-slate-900">
+                SIM {orderDetails[0].areaName}
+              </h3>
+            </div>
+          )}
+
+          <div
+            className={`rounded-2xl border border-slate-100 shadow-sm p-6 relative ${
+              activeTab === "manage" ? "bg-slate-50/50" : "bg-white"
+            }`}
+          >
+            {activeTab === "detail" ? (
+              // DETAIL TAB CONTENT
+              <div className="space-y-8">
+                {orderDetails.map((pkg, idx) => (
+                  <div
+                    key={idx}
+                    className={`flex flex-col md:flex-row justify-between items-start md:items-center ${
+                      idx !== orderDetails.length - 1
+                        ? "border-b border-slate-100 pb-8"
+                        : ""
+                    }`}
+                  >
+                    <div className="space-y-2">
+                      <div className="flex items-center gap-2">
+                        <h4 className="text-xl font-bold text-slate-800">
+                          {pkg.packageName}
+                        </h4>
+                        <span className="px-2 py-0.5 rounded bg-slate-100 text-slate-600 text-xs font-bold">
+                          {pkg.id}
+                        </span>
+                      </div>
+                      {/* <span className="inline-block bg-[#ff0050] text-white text-[10px] font-bold px-2 py-0.5 rounded">
+                        TikTok
+                      </span> */}
+                      <p className="text-slate-500 text-sm font-bold">
+                        Validity period: {pkg.dayDuration}
+                      </p>
+                    </div>
+
+                    <div className="mt-4 md:mt-0 flex flex-col items-end">
+                      <button className="flex items-center text-[#00b0f0] font-bold text-sm mb-2 hover:underline">
+                        <svg
+                          className="w-4 h-4 mr-1"
+                          fill="none"
+                          stroke="currentColor"
+                          viewBox="0 0 24 24"
+                        >
+                          <path
+                            strokeLinecap="round"
+                            strokeLinejoin="round"
+                            strokeWidth={2}
+                            d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
+                          />
+                          <path
+                            strokeLinecap="round"
+                            strokeLinejoin="round"
+                            strokeWidth={2}
+                            d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
+                          />
+                        </svg>
+                        QR Code
+                      </button>
+                      <div className="text-right">
+                        <span className="text-xl font-bold text-slate-800">
+                          {pkg.paymentMoney.toLocaleString("vi-VN", {
+                            minimumFractionDigits: 2,
+                            maximumFractionDigits: 2,
+                          })}
+                        </span>
+                        <span className="text-slate-500 font-medium ml-1 font-bold">
+                          {" "}
+                          đ
+                        </span>
+                      </div>
+                    </div>
+                  </div>
+                ))}
+              </div>
+            ) : (
+              // MANAGE TAB CONTENT - Grid of Circular Progress
+              <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
+                {dataUsage.map((pkg, idx) => (
+                  <div
+                    key={idx}
+                    className="bg-white p-6 rounded-2xl border border-slate-100 shadow-sm flex flex-col items-center hover:shadow-md transition-shadow"
+                  >
+                    <h4 className="text-lg font-bold text-slate-800 mb-2">
+                      {orderDetails[idx].packageName}
+                    </h4>
+                    <CircularProgress
+                      used={pkg.remainData}
+                      total={pkg.totalData}
+                      label={pkg.id}
+                    />
+                  </div>
+                ))}
+              </div>
+            )}
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+};
+
+export default OrderDetailView;

+ 273 - 0
EsimLao/esim-vite/src/pages/order-history/OrderHistoryView.tsx

@@ -0,0 +1,273 @@
+import { useNavigate } from "react-router-dom";
+import { productApi } from "../../apis/productApi";
+import { startLoading, stopLoading } from "../../features/loading/loadingSlice";
+import { useAppDispatch } from "../../hooks/useRedux";
+import { OrderHistory } from "../../services/product/type";
+import { useMutation } from "@tanstack/react-query";
+import React, { useState, useEffect } from "react";
+
+const OrderHistoryView = () => {
+  const [searchOrder, setSearchOrder] = useState("");
+  // status is one of: -1, 1, 2
+  const [status, setStatus] = useState("-1");
+  const [fromDate, setFromDate] = useState("");
+  const [toDate, setToDate] = useState("");
+  const [orders, setOrders] = useState<OrderHistory[]>([]);
+
+  const dispatch = useAppDispatch();
+  const navigate = useNavigate();
+
+  useEffect(() => {
+    getOrderMutation.mutate();
+  }, []);
+
+  const getOrderMutation = useMutation({
+    mutationFn: async () => {
+      dispatch(startLoading({}));
+      const res = await productApi.getOrderHistory({
+        fromDate,
+        toDate,
+        status,
+      });
+      return res;
+    },
+    onSuccess: (data) => {
+      dispatch(stopLoading());
+      console.log("Get order history response data:", data);
+      if (data && data.errorCode === "0") {
+        console.log("Get order history successful");
+        setOrders(data.data || []);
+      } else {
+        console.error("Get order history failed, no token received");
+      }
+    },
+    onError: (error: any) => {
+      dispatch(stopLoading());
+      console.error("Get order history error:", error.response.data);
+    },
+  });
+
+  // Input style matching LoginView
+  const inputClass =
+    "w-full bg-slate-50 border-2 border-transparent focus:border-[#EE0434]/20 rounded-2xl py-3 px-5 focus:outline-none focus:bg-white transition-all text-slate-700 font-bold placeholder:text-slate-300 text-sm md:text-base h-[50px]";
+
+  const getStatusColor = (status: number) => {
+    switch (status) {
+      case 2:
+        return "bg-[#00c087] text-white";
+      case 1:
+        return "bg-orange-400 text-white";
+      default:
+        return "bg-slate-800 text-white";
+    }
+  };
+
+  // const filteredOrders = orders.filter((order) => {
+  //   const matchesSearch = order.id.includes(searchOrder);
+  //   const matchesStatus = status === "-1" || order.status === status;
+  //   return matchesSearch && matchesStatus;
+  // });
+
+  return (
+    <div className="bg-[#fcfdfe] min-h-screen pb-20">
+      {/* Breadcrumb */}
+      <div className="max-w-7xl mx-auto px-4 py-4 md:py-6 border-b border-slate-50">
+        <nav className="flex items-center space-x-2 text-xs md:text-sm text-slate-500 font-medium">
+          <button
+            // onClick={() => onViewChange(ViewMode.HOME)}
+            className="hover:text-[#EE0434] text-[16px]"
+          >
+            Home
+          </button>
+          <svg
+            className="w-3 h-3"
+            fill="none"
+            stroke="currentColor"
+            viewBox="0 0 24 24"
+          >
+            <path
+              d="M9 5l7 7-7 7"
+              strokeWidth={2.5}
+              strokeLinecap="round"
+              strokeLinejoin="round"
+            />
+          </svg>
+          <span className="text-slate-900 font-bold text-[16px]">
+            Transaction History
+          </span>
+        </nav>
+      </div>
+
+      <div className="max-w-5xl mx-auto px-4 py-8 md:py-12">
+        {/* Filter Bar */}
+        <div className="grid grid-cols-1 md:grid-cols-12 gap-4 mb-8">
+          <div className="md:col-span-4 relative">
+            <input
+              type="text"
+              placeholder="Enter order code"
+              className={inputClass}
+              value={searchOrder}
+              onChange={(e) => {
+                setSearchOrder(e.target.value);
+                getOrderMutation.mutate();
+              }}
+            />
+            <button className="absolute right-2 top-1/2 -translate-y-1/2 w-8 h-8 bg-[#0091ff] rounded-xl flex items-center justify-center text-white shadow-md hover:scale-105 transition-transform">
+              <svg
+                className="w-4 h-4"
+                fill="none"
+                stroke="currentColor"
+                viewBox="0 0 24 24"
+              >
+                <path
+                  strokeLinecap="round"
+                  strokeLinejoin="round"
+                  strokeWidth={2.5}
+                  d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"
+                />
+              </svg>
+            </button>
+          </div>
+
+          <div className="md:col-span-3">
+            <input
+              type="text"
+              placeholder="From date"
+              onFocus={(e) => (e.target.type = "date")}
+              onBlur={(e) => (e.target.type = "text")}
+              className={inputClass}
+              value={fromDate}
+              onChange={(e) => {
+                setFromDate(e.target.value);
+                getOrderMutation.mutate();
+              }}
+            />
+          </div>
+
+          <div className="md:col-span-3">
+            <input
+              type="text"
+              placeholder="To date"
+              onFocus={(e) => (e.target.type = "date")}
+              onBlur={(e) => (e.target.type = "text")}
+              className={inputClass}
+              value={toDate}
+              onChange={(e) => {
+                setToDate(e.target.value);
+                getOrderMutation.mutate();
+              }}
+            />
+          </div>
+
+          <div className="md:col-span-2 relative">
+            <select
+              className={`${inputClass} appearance-none cursor-pointer`}
+              value={status}
+              onChange={(e) => {
+                setStatus(e.target.value);
+                getOrderMutation.mutate();
+              }}
+            >
+              <option value="-1">Status</option>
+              <option value="2">Success</option>
+              <option value="1">Pending payment</option>
+            </select>
+            <svg
+              className="absolute right-4 top-1/2 -translate-y-1/2 w-4 h-4 text-slate-400 pointer-events-none"
+              fill="none"
+              stroke="currentColor"
+              viewBox="0 0 24 24"
+            >
+              <path
+                strokeLinecap="round"
+                strokeLinejoin="round"
+                strokeWidth={2}
+                d="M19 9l-7 7-7-7"
+              />
+            </svg>
+          </div>
+        </div>
+
+        {/* Order List */}
+        <div className="space-y-4">
+          {orders.length === 0 ? (
+            <div className="text-center py-20 opacity-50">
+              <p className="text-xl font-bold text-slate-400">
+                No orders found
+              </p>
+            </div>
+          ) : (
+            orders.map((order) => (
+              <div
+                key={order.id}
+                onClick={() =>
+                  navigate(`/order-history-detail/${order.id}`, {
+                    state: { id: order.id, orderHistory: order },
+                  })
+                }
+                className="bg-white rounded-[20px] p-6 shadow-sm border border-slate-100 hover:shadow-md transition-shadow flex flex-col md:flex-row justify-between items-start md:items-center gap-6"
+              >
+                <div className="space-y-3">
+                  <span
+                    className={`inline-block px-3 py-1 rounded-lg text-xs font-black uppercase tracking-wider ${getStatusColor(
+                      order.status
+                    )}`}
+                  >
+                    {order.status === 2
+                      ? "Success"
+                      : order.status === 1
+                      ? "Pending payment"
+                      : "Failed"}
+                  </span>
+                  <div>
+                    <p className="text-xl font-black text-slate-900 tracking-tight">
+                      {order.id}
+                    </p>
+                    <p className="text-sm font-medium text-slate-400 mt-1">
+                      {order.createdDate}
+                    </p>
+                  </div>
+                </div>
+
+                <div className="flex items-center justify-between w-full md:w-auto gap-8">
+                  <div className="flex -space-x-2">
+                    {/* {order.flags.map((flag, idx) => (
+                      <div
+                        key={idx}
+                        className="w-8 h-8 rounded-full border-2 border-white shadow-sm overflow-hidden z-0"
+                      >
+                        <img
+                          src={`https://flagcdn.com/w80/${flag}.png`}
+                          alt={flag}
+                          className="w-full h-full object-cover"
+                        />
+                      </div>
+                    ))} */}
+                  </div>
+                  <div className="text-right">
+                    <p className="text-xl font-black text-slate-900">
+                      {order.paymentMoney.toLocaleString("vi-VN", {
+                        minimumFractionDigits: 2,
+                        maximumFractionDigits: 2,
+                      })}{" "}
+                      ({order.curency})
+                    </p>
+                    <p className="text-sm font-bold text-slate-400">
+                      {order.totalMoney.toLocaleString("vi-VN", {
+                        minimumFractionDigits: 2,
+                        maximumFractionDigits: 2,
+                      })}{" "}
+                      ({order.curency})
+                    </p>
+                  </div>
+                </div>
+              </div>
+            ))
+          )}
+        </div>
+      </div>
+    </div>
+  );
+};
+
+export default OrderHistoryView;

+ 17 - 4
EsimLao/esim-vite/src/pages/product-detail/ProductDetailView.tsx

@@ -219,7 +219,10 @@ const ProductDetailView: React.FC = () => {
     <div className="bg-white min-h-screen pb-20">
       <div className="max-w-7xl mx-auto px-4 py-4 md:py-6">
         <nav className="flex items-center space-x-2 text-xs md:text-sm text-slate-500">
-          <Link to="/" className="hover:text-[#EE0434] transition-colors">
+          <Link
+            to="/"
+            className="hover:text-[#EE0434] transition-colors text-[16px]"
+          >
             Home
           </Link>
           <svg
@@ -235,7 +238,9 @@ const ProductDetailView: React.FC = () => {
               strokeLinejoin="round"
             />
           </svg>
-          <span className="text-slate-900 font-bold">{area.areaName1}</span>
+          <span className="text-slate-900 font-bold text-[16px]">
+            {area.areaName1}
+          </span>
         </nav>
       </div>
 
@@ -377,11 +382,19 @@ const ProductDetailView: React.FC = () => {
                   -{prices.discountPercent}%
                 </span>
                 <span className="text-slate-300 font-bold text-xs line-through">
-                  ${prices.original}
+                  $
+                  {prices.original.toLocaleString("vi-VN", {
+                    minimumFractionDigits: 2,
+                    maximumFractionDigits: 2,
+                  })}
                 </span>
               </div>
               <span className="text-[#EE0434] font-black text-2xl md:text-3xl">
-                ${prices.final}
+                $
+                {prices.final.toLocaleString("vi-VN", {
+                  minimumFractionDigits: 2,
+                  maximumFractionDigits: 2,
+                })}
               </span>
             </div>
             <button

+ 24 - 0
EsimLao/esim-vite/src/services/content/types.ts

@@ -60,3 +60,27 @@ export interface LoadFaqResponse {
   faqs: Faq[];
   pagination: Pagination;
 }
+
+export interface DeviceMeta {
+  brand: string;
+  deviceCount: number;
+  popularCount: number;
+  devices: DeviceInfo[];
+}
+
+export interface DeviceInfo {
+  id: number;
+  modelName: string;
+  category: string;
+  isPopular: boolean;
+  displayOrder: number;
+  modelNameEn: string;
+  modelNameLo: string;
+  notes: string;
+  notesEn: string;
+  notesLo: string;
+}
+
+export interface LoadDeviceMetaResponse {
+  brands: DeviceMeta[];
+}

+ 49 - 0
EsimLao/esim-vite/src/services/product/type.ts

@@ -83,3 +83,52 @@ export interface ConfirmOrderResponse {
   discountPercent: number;
   customerInfo: any;
 }
+
+export interface OrderHistory {
+  id: number;
+  customerId: number;
+  createdDate: string;
+  totalMoney: number;
+  discount: number;
+  paymentMoney: number;
+  paymentId: number;
+  status: number;
+  paymentStatus: number;
+  transactionId: string | null;
+  curency: string;
+  orderId: number;
+  orderDetailId: number;
+  paymentUrl: string | null;
+  discountPercent: number;
+  customerInfo: {
+    id: number;
+    surName: string | null;
+    lastName: string | null;
+    email: string | null;
+    phoneNumber: string | null;
+    createdDate: string | null;
+  };
+}
+
+export interface OrderDetail {
+  id: number;
+  orderDetailId: number;
+  qrcode: string;
+  qrcodeUrl: string;
+  iccid: string;
+  createdDate: string;
+  installationUrl: string;
+  packageName: string;
+  areaIconUrl: string;
+  packageTitle: string;
+  paymentMoney: string;
+  dayDuration: number;
+}
+
+export interface DataUsage {
+  remainData: number;
+  totalData: number;
+  expiredTime: string;
+  isUnlimited: number;
+  status: number;
+}