소스 검색

Add architecture diagrams and update UI components

Added new draw.io architecture diagrams for documentation. Updated multiple React components, API logic, i18n files, and styles for improved UI/UX and functionality. Introduced ScrollToTop component and made various enhancements across product, modal, and view files.
hieubt 2 주 전
부모
커밋
74e0c58434
27개의 변경된 파일934개의 추가작업 그리고 230개의 파일을 삭제
  1. 276 0
      EsimLao/docs/.$skyhub.drawio.bkp
  2. 276 0
      EsimLao/docs/skyhub.drawio
  3. 6 3
      EsimLao/esim-vite/src/App.tsx
  4. 2 2
      EsimLao/esim-vite/src/apis/productApi.ts
  5. 18 17
      EsimLao/esim-vite/src/components/CompatibilityModal.tsx
  6. 1 0
      EsimLao/esim-vite/src/components/Footer.tsx
  7. 13 17
      EsimLao/esim-vite/src/components/Header.tsx
  8. 3 3
      EsimLao/esim-vite/src/components/Popup.tsx
  9. 7 5
      EsimLao/esim-vite/src/components/ProductCard.tsx
  10. 1 1
      EsimLao/esim-vite/src/components/ProductInfoModal.tsx
  11. 1 3
      EsimLao/esim-vite/src/components/QRCodeModal.tsx
  12. 18 0
      EsimLao/esim-vite/src/components/ScrollToTop.tsx
  13. 20 4
      EsimLao/esim-vite/src/i18n/locales/en.json
  14. 20 4
      EsimLao/esim-vite/src/i18n/locales/vi.json
  15. 4 4
      EsimLao/esim-vite/src/index.css
  16. 36 2
      EsimLao/esim-vite/src/logic/loigicUtils.ts
  17. 1 1
      EsimLao/esim-vite/src/pages/buy-sim/BuySimView.tsx
  18. 29 13
      EsimLao/esim-vite/src/pages/checkout/CheckoutView.tsx
  19. 17 10
      EsimLao/esim-vite/src/pages/home/HomeView.tsx
  20. 1 1
      EsimLao/esim-vite/src/pages/home/components/HomeBanner.tsx
  21. 9 32
      EsimLao/esim-vite/src/pages/home/components/HomeProduct.tsx
  22. 11 7
      EsimLao/esim-vite/src/pages/home/components/HomeSearch.tsx
  23. 2 1
      EsimLao/esim-vite/src/pages/home/components/HomeTestimonial.tsx
  24. 101 59
      EsimLao/esim-vite/src/pages/order-detail/OrderDetailView.tsx
  25. 10 8
      EsimLao/esim-vite/src/pages/order-history/OrderHistoryView.tsx
  26. 47 33
      EsimLao/esim-vite/src/pages/product-detail/ProductDetailView.tsx
  27. 4 0
      EsimLao/esim-vite/src/services/product/type.ts

+ 276 - 0
EsimLao/docs/.$skyhub.drawio.bkp

@@ -0,0 +1,276 @@
+<mxfile host="Electron" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/29.3.0 Chrome/140.0.7339.249 Electron/38.7.2 Safari/537.36" version="29.3.0">
+  <diagram id="kgpKYQtTHZ0yAKxKKP6v" name="Page-1">
+    <mxGraphModel dx="2483" dy="2183" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
+      <root>
+        <mxCell id="0" />
+        <mxCell id="1" parent="0" />
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-1" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
+          <mxGeometry height="240" width="200" x="-40" y="80" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-2" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;B2C&lt;/font&gt;" vertex="1">
+          <mxGeometry height="30" width="60" x="30" y="50" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-3" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.concepts.website;" value="Website / Mini app" vertex="1">
+          <mxGeometry height="49" width="59" x="31" y="110" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-5" parent="1" style="verticalLabelPosition=bottom;html=1;verticalAlign=top;align=center;strokeColor=none;fillColor=#00BEF2;shape=mxgraph.azure.mobile_services;pointerEvents=1;" value="Application B2C" vertex="1">
+          <mxGeometry height="50" width="32.5" x="43.25" y="220" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-6" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
+          <mxGeometry height="340" width="200" x="-40.5" y="390" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-7" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;B2B&lt;/font&gt;" vertex="1">
+          <mxGeometry height="30" width="60" x="29.5" y="360" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-8" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.concepts.website;" value="Website Portal" vertex="1">
+          <mxGeometry height="49" width="59" x="30.5" y="420" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-9" parent="1" style="verticalLabelPosition=bottom;html=1;verticalAlign=top;align=center;strokeColor=none;fillColor=#00BEF2;shape=mxgraph.azure.mobile_services;pointerEvents=1;" value="Application B2B" vertex="1">
+          <mxGeometry height="50" width="32.5" x="42.75" y="530" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-10" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
+          <mxGeometry height="130" width="200" x="-41" y="840" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-11" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;CMS / CSKH&lt;/font&gt;" vertex="1">
+          <mxGeometry height="30" width="121" x="-1.5" y="810" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-12" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.concepts.website;" value="Website CMS" vertex="1">
+          <mxGeometry height="49" width="59" x="30" y="870" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-15" parent="1" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=light-dark(#db1a1a, #ededed);" value="" vertex="1">
+          <mxGeometry height="890" width="930" x="280" y="80" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-16" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;SKYHUB&lt;/font&gt;" vertex="1">
+          <mxGeometry height="30" width="60" x="655" y="50" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-17" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.classic_load_balancer;fillColor=#F58534;gradientColor=none;" value="KONG/NGINX" vertex="1">
+          <mxGeometry height="72" width="69" x="370" y="478" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-18" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-17" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=3;" target="uRcrelpJeVWRNYr8ECgl-1" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="500" y="490" as="sourcePoint" />
+            <mxPoint x="550" y="440" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-19" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-17" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=3;" target="uRcrelpJeVWRNYr8ECgl-6" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="380" y="666" as="sourcePoint" />
+            <mxPoint x="220" y="400" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-20" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-17" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;" target="uRcrelpJeVWRNYr8ECgl-10" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="320" y="470" as="sourcePoint" />
+            <mxPoint x="140" y="654" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-25" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="KEYCLOAK" vertex="1">
+          <mxGeometry height="63" width="60" x="374.5" y="168.5" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-26" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-25" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=3;strokeColor=light-dark(#CC6600,#EDEDED);" target="uRcrelpJeVWRNYr8ECgl-1" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="570" y="684" as="sourcePoint" />
+            <mxPoint x="300" y="370" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-27" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-25" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=3;strokeColor=light-dark(#CC6600,#EDEDED);" target="uRcrelpJeVWRNYr8ECgl-6" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="670" y="260" as="sourcePoint" />
+            <mxPoint x="400" y="260" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-28" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-25" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=3;strokeColor=light-dark(#CC6600,#EDEDED);" target="uRcrelpJeVWRNYr8ECgl-10" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="570" y="430" as="sourcePoint" />
+            <mxPoint x="300" y="740" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-29" parent="1" style="aspect=fixed;sketch=0;html=1;dashed=0;whitespace=wrap;verticalLabelPosition=bottom;verticalAlign=top;fillColor=#2875E2;strokeColor=#ffffff;points=[[0.005,0.63,0],[0.1,0.2,0],[0.9,0.2,0],[0.5,0,0],[0.995,0.63,0],[0.72,0.99,0],[0.5,1,0],[0.28,0.99,0]];shape=mxgraph.kubernetes.icon2;prIcon=api" value="APIs" vertex="1">
+          <mxGeometry height="48" width="50" x="34" y="650" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-31" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
+          <mxGeometry height="333.5" width="260" x="560" y="306.5" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-33" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="ESIM" vertex="1">
+          <mxGeometry height="63" width="60" x="595" y="327" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-34" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
+          <mxGeometry height="120" width="260" x="560" y="150" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-35" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="PARTNER ADAP 1" vertex="1">
+          <mxGeometry height="63" width="60" x="600" y="168.5" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-36" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;PARTNER GW&lt;/font&gt;" vertex="1">
+          <mxGeometry height="30" width="130" x="560" y="119.5" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-37" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="PARTNER ADAP 2" vertex="1">
+          <mxGeometry height="63" width="60" x="720" y="168.5" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-39" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-33" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0.25;entryY=1;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=3;strokeColor=#0000FF;" target="uRcrelpJeVWRNYr8ECgl-34" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="580" y="420" as="sourcePoint" />
+            <mxPoint x="630" y="370" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-40" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
+          <mxGeometry height="120" width="380" x="555" y="-130" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-41" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="JOY TELECOM" vertex="1">
+          <mxGeometry height="63" width="60" x="595" y="-111.5" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-44" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;ESIM PARTNERS&lt;/font&gt;" vertex="1">
+          <mxGeometry height="30" width="160" x="775" y="-160" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-45" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=3;strokeColor=#0000FF;" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="749.5" y="149.5" as="sourcePoint" />
+            <mxPoint x="750" y="-9" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-48" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="PORTAL" vertex="1">
+          <mxGeometry height="63" width="60" x="595" y="430" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-49" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="B2C APIS" vertex="1">
+          <mxGeometry height="63" width="60" x="720" y="327" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-50" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="B2B APIS" vertex="1">
+          <mxGeometry height="63" width="60" x="720" y="430" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-52" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;CORE&lt;/font&gt;" vertex="1">
+          <mxGeometry height="30" width="60" x="760" y="640" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-53" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
+          <mxGeometry height="333.5" width="240" x="900" y="308.25" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-56" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="OnePay" vertex="1">
+          <mxGeometry height="63" width="60" x="1040" y="328.75" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-59" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;PAYMENT&lt;/font&gt;" vertex="1">
+          <mxGeometry height="30" width="90" x="1050" y="640" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-60" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="UniPay" vertex="1">
+          <mxGeometry height="63" width="60" x="1040" y="430" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-61" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="Momo" vertex="1">
+          <mxGeometry height="63" width="60" x="1040" y="540" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-62" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-31" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=0.998;exitY=0.467;exitDx=0;exitDy=0;strokeWidth=3;strokeColor=#FF00FF;entryPerimeter=0;exitPerimeter=0;" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="620" y="347" as="sourcePoint" />
+            <mxPoint x="920" y="461.5" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-63" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
+          <mxGeometry height="890" width="200" x="1320" y="80" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-64" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;PAYMENTS&lt;/font&gt;" vertex="1">
+          <mxGeometry height="30" width="60" x="1390" y="50" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-67" parent="1" style="points=[];aspect=fixed;html=1;align=center;shadow=0;dashed=0;fillColor=#FF6A00;strokeColor=none;shape=mxgraph.alibaba_cloud.dbes_database_expert_service;" value="&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;ONEPAY&lt;/div&gt;" vertex="1">
+          <mxGeometry height="47.400000000000006" width="47.400000000000006" x="1396.3" y="110" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-68" parent="1" style="points=[];aspect=fixed;html=1;align=center;shadow=0;dashed=0;fillColor=#FF6A00;strokeColor=none;shape=mxgraph.alibaba_cloud.dbes_database_expert_service;" value="&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;MOMO&lt;/div&gt;" vertex="1">
+          <mxGeometry height="47.400000000000006" width="47.400000000000006" x="1396.3" y="210" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-69" parent="1" style="points=[];aspect=fixed;html=1;align=center;shadow=0;dashed=0;fillColor=#FF6A00;strokeColor=none;shape=mxgraph.alibaba_cloud.dbes_database_expert_service;" value="&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;UNIPAY&lt;/div&gt;" vertex="1">
+          <mxGeometry height="47.400000000000006" width="47.400000000000006" x="1396.3" y="308.25" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-72" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-17" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=-0.007;entryY=0.622;entryDx=0;entryDy=0;strokeWidth=3;strokeColor=#99004D;entryPerimeter=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;" target="uRcrelpJeVWRNYr8ECgl-31" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="531" y="524.44" as="sourcePoint" />
+            <mxPoint x="620" y="524.8800000000001" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-74" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-75" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;strokeColor=#FF00FF;entryPerimeter=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;" target="uRcrelpJeVWRNYr8ECgl-56" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="850" y="450" as="sourcePoint" />
+            <mxPoint x="951" y="450" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-75" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.vpc_nat_gateway;fillColor=#F58536;gradientColor=none;" value="PAYMENT GW" vertex="1">
+          <mxGeometry height="72" width="69" x="920" y="425.5" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-76" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-75" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;strokeColor=#FF00FF;entryPerimeter=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;" target="uRcrelpJeVWRNYr8ECgl-60" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="990" y="552" as="sourcePoint" />
+            <mxPoint x="1041" y="450" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-77" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-75" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;strokeColor=#FF00FF;entryPerimeter=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;" target="uRcrelpJeVWRNYr8ECgl-61" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="990" y="460" as="sourcePoint" />
+            <mxPoint x="1031" y="450" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-80" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-53" style="endArrow=classic;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.009;entryY=0.443;entryDx=0;entryDy=0;entryPerimeter=0;strokeWidth=3;" target="uRcrelpJeVWRNYr8ECgl-63" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="1210" y="440" as="sourcePoint" />
+            <mxPoint x="1260" y="390" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-81" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-63" style="endArrow=classic;html=1;rounded=0;exitX=-0.016;exitY=0.962;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;strokeWidth=3;exitPerimeter=0;" target="uRcrelpJeVWRNYr8ECgl-17" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <Array as="points">
+              <mxPoint x="230" y="936" />
+              <mxPoint x="230" y="514" />
+            </Array>
+            <mxPoint x="940" y="871" as="sourcePoint" />
+            <mxPoint x="1122" y="870" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-92" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
+          <mxGeometry height="140" width="580" x="560" y="710" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-93" parent="1" style="image;html=1;image=img/lib/clip_art/computers/Database_128x128.png" value="&lt;div style=&quot;text-align: left;&quot;&gt;&lt;font face=&quot;Times New Roman, Times New Roman_EmbeddedFont, Times New Roman_MSFontService, serif&quot;&gt;&lt;span style=&quot;font-size: 16px; font-variant-ligatures: none; text-wrap-mode: wrap;&quot;&gt;&lt;b&gt;ORACLE&lt;/b&gt;&lt;/span&gt;&lt;/font&gt;&lt;/div&gt;" vertex="1">
+          <mxGeometry height="80" width="80" x="595" y="739" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-94" parent="1" style="image;sketch=0;aspect=fixed;html=1;points=[];align=center;fontSize=12;image=img/lib/mscae/Cache_Redis_Product.svg;" value="&lt;font style=&quot;font-size: 16px;&quot;&gt;Redis&lt;/font&gt;" vertex="1">
+          <mxGeometry height="60" width="71.43" x="900" y="750" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-95" parent="1" style="points=[];aspect=fixed;html=1;align=center;shadow=0;dashed=0;fillColor=#FF6A00;strokeColor=none;shape=mxgraph.alibaba_cloud.kafka;" value="&lt;div&gt;&lt;font style=&quot;font-size: 16px;&quot;&gt;&lt;br&gt;&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font style=&quot;font-size: 16px;&quot;&gt;&lt;br&gt;&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font style=&quot;font-size: 16px;&quot;&gt;&lt;br&gt;&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font style=&quot;font-size: 16px;&quot;&gt;&lt;br&gt;&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font style=&quot;font-size: 16px;&quot;&gt;&lt;br&gt;&lt;/font&gt;&lt;/div&gt;&lt;font style=&quot;font-size: 16px;&quot;&gt;Kafka&lt;/font&gt;" vertex="1">
+          <mxGeometry height="61" width="82.35" x="737.6500000000001" y="748.5" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-96" parent="1" style="image;html=1;image=img/lib/clip_art/computers/Database_Move_Stack_128x128.png" value="&lt;span data-contrast=&quot;auto&quot; class=&quot;TextRun MacChromeBold SCXW86909208 BCX0&quot; style=&quot;-webkit-user-drag: none; -webkit-tap-highlight-color: transparent; margin: 0px; padding: 0px; user-select: text; -webkit-font-smoothing: antialiased; text-align: left; text-wrap-mode: wrap; font-size: 12pt; line-height: 28.5px; font-family: &amp;quot;Times New Roman&amp;quot;, &amp;quot;Times New Roman_EmbeddedFont&amp;quot;, &amp;quot;Times New Roman_MSFontService&amp;quot;, serif; font-weight: bold; font-variant-ligatures: none !important;&quot;&gt;PostgreSQL&lt;/span&gt;&lt;span class=&quot;EOP SCXW86909208 BCX0&quot; data-ccp-props=&quot;{&amp;quot;201341983&amp;quot;:0,&amp;quot;335559738&amp;quot;:120,&amp;quot;335559740&amp;quot;:360}&quot; style=&quot;-webkit-user-drag: none; -webkit-tap-highlight-color: transparent; margin: 0px; padding: 0px; user-select: text; text-align: left; text-wrap-mode: wrap; font-size: 12pt; line-height: 28.5px; font-family: &amp;quot;Times New Roman&amp;quot;, &amp;quot;Times New Roman_EmbeddedFont&amp;quot;, &amp;quot;Times New Roman_MSFontService&amp;quot;, serif;&quot;&gt;&amp;nbsp;&lt;/span&gt;" vertex="1">
+          <mxGeometry height="80" width="80" x="1030" y="729.5" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-97" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-92" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.224;exitY=-0.001;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=3;" target="uRcrelpJeVWRNYr8ECgl-31" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="610" y="510" as="sourcePoint" />
+            <mxPoint x="660" y="460" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-98" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.224;exitY=-0.001;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=3;" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="1019.6800000000001" y="709" as="sourcePoint" />
+            <mxPoint x="1019.6800000000001" y="639" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-99" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-92" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;strokeWidth=3;entryPerimeter=0;" target="uRcrelpJeVWRNYr8ECgl-25" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <Array as="points">
+              <mxPoint x="520" y="780" />
+              <mxPoint x="520" y="200" />
+            </Array>
+            <mxPoint x="690" y="730" as="sourcePoint" />
+            <mxPoint x="690" y="660" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-100" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
+          <mxGeometry height="130" width="200" x="-41" y="-140" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-101" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="&lt;font style=&quot;font-size: 22px;&quot;&gt;&lt;span data-contrast=&quot;auto&quot; class=&quot;TextRun MacChromeBold SCXW168624917 BCX0&quot; style=&quot;-webkit-user-drag: none; -webkit-tap-highlight-color: transparent; margin: 0px; padding: 0px; user-select: text; -webkit-font-smoothing: antialiased; text-align: left; background-color: rgb(255, 255, 255); line-height: 28.5px; font-family: &amp;quot;Times New Roman&amp;quot;, &amp;quot;Times New Roman_EmbeddedFont&amp;quot;, &amp;quot;Times New Roman_MSFontService&amp;quot;, serif; font-weight: bold; font-variant-ligatures: none !important;&quot;&gt;&lt;span class=&quot;NormalTextRun SCXW168624917 BCX0&quot; style=&quot;-webkit-user-drag: none; -webkit-tap-highlight-color: transparent; margin: 0px; padding: 0px; user-select: text;&quot;&gt;Monitoring&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;EOP SCXW168624917 BCX0&quot; data-ccp-props=&quot;{&amp;quot;201341983&amp;quot;:0,&amp;quot;335559738&amp;quot;:120,&amp;quot;335559740&amp;quot;:360}&quot; style=&quot;-webkit-user-drag: none; -webkit-tap-highlight-color: transparent; margin: 0px; padding: 0px; user-select: text; text-align: left; background-color: rgb(255, 255, 255); line-height: 28.5px; font-family: &amp;quot;Times New Roman&amp;quot;, &amp;quot;Times New Roman_EmbeddedFont&amp;quot;, &amp;quot;Times New Roman_MSFontService&amp;quot;, serif;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/font&gt;" vertex="1">
+          <mxGeometry height="30" width="121" x="-1.5" y="-170" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-102" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.concepts.website;" value="&lt;span data-contrast=&quot;auto&quot; class=&quot;TextRun SCXW82762732 BCX0&quot; style=&quot;-webkit-user-drag: none; -webkit-tap-highlight-color: transparent; margin: 0px; padding: 0px; user-select: text; text-align: left; text-wrap-mode: wrap; background-color: rgb(255, 255, 255); font-size: 12pt; line-height: 28.5px; font-family: &amp;quot;Times New Roman&amp;quot;, &amp;quot;Times New Roman_EmbeddedFont&amp;quot;, &amp;quot;Times New Roman_MSFontService&amp;quot;, serif; font-variant-ligatures: none !important;&quot;&gt;&lt;span class=&quot;NormalTextRun SCXW82762732 BCX0&quot; style=&quot;-webkit-user-drag: none; -webkit-tap-highlight-color: transparent; margin: 0px; padding: 0px; user-select: text;&quot;&gt;Prometheus &amp;amp; Grafana&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;EOP SCXW82762732 BCX0&quot; data-ccp-props=&quot;{&amp;quot;201341983&amp;quot;:0,&amp;quot;335559738&amp;quot;:120,&amp;quot;335559740&amp;quot;:360}&quot; style=&quot;-webkit-user-drag: none; -webkit-tap-highlight-color: transparent; margin: 0px; padding: 0px; user-select: text; text-align: left; text-wrap-mode: wrap; background-color: rgb(255, 255, 255); font-size: 12pt; line-height: 28.5px; font-family: &amp;quot;Times New Roman&amp;quot;, &amp;quot;Times New Roman_EmbeddedFont&amp;quot;, &amp;quot;Times New Roman_MSFontService&amp;quot;, serif;&quot;&gt;&amp;nbsp;&lt;/span&gt;" vertex="1">
+          <mxGeometry height="49" width="59" x="30" y="-110" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-103" parent="1" style="points=[];aspect=fixed;html=1;align=center;shadow=0;dashed=0;fillColor=#FF6A00;strokeColor=none;shape=mxgraph.alibaba_cloud.elasticsearch;" value="&lt;p data-start=&quot;190&quot; data-end=&quot;216&quot;&gt;&lt;span data-start=&quot;190&quot; data-end=&quot;212&quot;&gt;&lt;font style=&quot;font-size: 15px;&quot;&gt;&lt;br&gt;&lt;/font&gt;&lt;/span&gt;&lt;/p&gt;&lt;p data-start=&quot;190&quot; data-end=&quot;216&quot;&gt;&lt;span data-start=&quot;190&quot; data-end=&quot;212&quot;&gt;&lt;font style=&quot;font-size: 15px;&quot;&gt;&lt;br&gt;&lt;/font&gt;&lt;/span&gt;&lt;/p&gt;&lt;p data-start=&quot;190&quot; data-end=&quot;216&quot;&gt;&lt;span data-start=&quot;190&quot; data-end=&quot;212&quot;&gt;&lt;font style=&quot;font-size: 15px;&quot;&gt;&lt;br&gt;&lt;/font&gt;&lt;/span&gt;&lt;/p&gt;&lt;p data-start=&quot;190&quot; data-end=&quot;216&quot;&gt;&lt;span data-start=&quot;190&quot; data-end=&quot;212&quot; style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;&lt;font style=&quot;font-size: 15px;&quot;&gt;Elasticsearch&lt;/font&gt;&lt;/span&gt;&lt;/p&gt;" vertex="1">
+          <mxGeometry height="50.099999999999994" width="45.9" x="602.05" y="550" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-104" parent="1" style="text;html=1;whiteSpace=wrap;overflow=hidden;rounded=0;" value="&lt;h1 style=&quot;margin-top: 0px;&quot;&gt;Công nghệ sử dụng&lt;/h1&gt;&lt;div&gt;Front end:&lt;/div&gt;&lt;div&gt;+ Website/MiniApp: ReactJS vite tool&lt;/div&gt;&lt;div&gt;+ Application: Flutter&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Backend:&lt;/div&gt;&lt;div&gt;+ 100% sử dụng Dotnet core 7 API&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Database:&lt;/div&gt;&lt;div&gt;+ Oracle&lt;/div&gt;&lt;div&gt;+ PostgresSQL&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Cache:&lt;/div&gt;&lt;div&gt;+ Redis&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Queue:&lt;/div&gt;&lt;div&gt;+ Kafka&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Identify service:&lt;/div&gt;&lt;div&gt;+ Keycloak&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Api Gateway:&lt;/div&gt;&lt;div&gt;+ KONG&amp;nbsp;&lt;/div&gt;" vertex="1">
+          <mxGeometry height="380" width="490" x="1030" y="-329" as="geometry" />
+        </mxCell>
+      </root>
+    </mxGraphModel>
+  </diagram>
+</mxfile>

+ 276 - 0
EsimLao/docs/skyhub.drawio

@@ -0,0 +1,276 @@
+<mxfile host="Electron" agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/29.3.0 Chrome/140.0.7339.249 Electron/38.7.2 Safari/537.36" version="29.3.0">
+  <diagram id="kgpKYQtTHZ0yAKxKKP6v" name="Page-1">
+    <mxGraphModel dx="2483" dy="2183" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0">
+      <root>
+        <mxCell id="0" />
+        <mxCell id="1" parent="0" />
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-1" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
+          <mxGeometry height="240" width="200" x="-40" y="80" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-2" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;B2C&lt;/font&gt;" vertex="1">
+          <mxGeometry height="30" width="60" x="30" y="50" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-3" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.concepts.website;" value="Website / Mini app" vertex="1">
+          <mxGeometry height="49" width="59" x="31" y="110" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-5" parent="1" style="verticalLabelPosition=bottom;html=1;verticalAlign=top;align=center;strokeColor=none;fillColor=#00BEF2;shape=mxgraph.azure.mobile_services;pointerEvents=1;" value="Application B2C" vertex="1">
+          <mxGeometry height="50" width="32.5" x="43.25" y="220" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-6" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
+          <mxGeometry height="340" width="200" x="-40.5" y="390" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-7" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;B2B&lt;/font&gt;" vertex="1">
+          <mxGeometry height="30" width="60" x="29.5" y="360" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-8" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.concepts.website;" value="Website Portal" vertex="1">
+          <mxGeometry height="49" width="59" x="30.5" y="420" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-9" parent="1" style="verticalLabelPosition=bottom;html=1;verticalAlign=top;align=center;strokeColor=none;fillColor=#00BEF2;shape=mxgraph.azure.mobile_services;pointerEvents=1;" value="Application B2B" vertex="1">
+          <mxGeometry height="50" width="32.5" x="42.75" y="530" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-10" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
+          <mxGeometry height="130" width="200" x="-41" y="840" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-11" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;CMS / CSKH&lt;/font&gt;" vertex="1">
+          <mxGeometry height="30" width="121" x="-1.5" y="810" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-12" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.concepts.website;" value="Website CMS" vertex="1">
+          <mxGeometry height="49" width="59" x="30" y="870" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-15" parent="1" style="rounded=0;whiteSpace=wrap;html=1;strokeColor=light-dark(#db1a1a, #ededed);" value="" vertex="1">
+          <mxGeometry height="890" width="930" x="280" y="80" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-16" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;SKYHUB&lt;/font&gt;" vertex="1">
+          <mxGeometry height="30" width="60" x="655" y="50" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-17" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.classic_load_balancer;fillColor=#F58534;gradientColor=none;" value="KONG/NGINX" vertex="1">
+          <mxGeometry height="72" width="69" x="370" y="478" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-18" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-17" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=3;" target="uRcrelpJeVWRNYr8ECgl-1" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="500" y="490" as="sourcePoint" />
+            <mxPoint x="550" y="440" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-19" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-17" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=3;" target="uRcrelpJeVWRNYr8ECgl-6" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="380" y="666" as="sourcePoint" />
+            <mxPoint x="220" y="400" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-20" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-17" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;" target="uRcrelpJeVWRNYr8ECgl-10" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="320" y="470" as="sourcePoint" />
+            <mxPoint x="140" y="654" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-25" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="KEYCLOAK" vertex="1">
+          <mxGeometry height="63" width="60" x="374.5" y="168.5" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-26" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-25" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=3;strokeColor=light-dark(#CC6600,#EDEDED);" target="uRcrelpJeVWRNYr8ECgl-1" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="570" y="684" as="sourcePoint" />
+            <mxPoint x="300" y="370" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-27" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-25" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=3;strokeColor=light-dark(#CC6600,#EDEDED);" target="uRcrelpJeVWRNYr8ECgl-6" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="670" y="260" as="sourcePoint" />
+            <mxPoint x="400" y="260" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-28" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-25" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=3;strokeColor=light-dark(#CC6600,#EDEDED);" target="uRcrelpJeVWRNYr8ECgl-10" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="570" y="430" as="sourcePoint" />
+            <mxPoint x="300" y="740" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-29" parent="1" style="aspect=fixed;sketch=0;html=1;dashed=0;whitespace=wrap;verticalLabelPosition=bottom;verticalAlign=top;fillColor=#2875E2;strokeColor=#ffffff;points=[[0.005,0.63,0],[0.1,0.2,0],[0.9,0.2,0],[0.5,0,0],[0.995,0.63,0],[0.72,0.99,0],[0.5,1,0],[0.28,0.99,0]];shape=mxgraph.kubernetes.icon2;prIcon=api" value="APIs" vertex="1">
+          <mxGeometry height="48" width="50" x="34" y="650" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-31" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
+          <mxGeometry height="333.5" width="260" x="560" y="306.5" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-33" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="ESIM" vertex="1">
+          <mxGeometry height="63" width="60" x="595" y="327" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-34" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
+          <mxGeometry height="120" width="260" x="560" y="150" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-35" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="PARTNER ADAP 1" vertex="1">
+          <mxGeometry height="63" width="60" x="600" y="168.5" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-36" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;PARTNER GW&lt;/font&gt;" vertex="1">
+          <mxGeometry height="30" width="130" x="560" y="119.5" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-37" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="PARTNER ADAP 2" vertex="1">
+          <mxGeometry height="63" width="60" x="720" y="168.5" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-39" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-33" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0.25;entryY=1;entryDx=0;entryDy=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=3;strokeColor=#0000FF;" target="uRcrelpJeVWRNYr8ECgl-34" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="580" y="420" as="sourcePoint" />
+            <mxPoint x="630" y="370" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-40" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
+          <mxGeometry height="120" width="380" x="555" y="-130" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-41" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="JOY TELECOM" vertex="1">
+          <mxGeometry height="63" width="60" x="595" y="-111.5" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-44" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;ESIM PARTNERS&lt;/font&gt;" vertex="1">
+          <mxGeometry height="30" width="160" x="775" y="-160" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-45" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;exitX=0.5;exitY=0;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=3;strokeColor=#0000FF;" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="749.5" y="149.5" as="sourcePoint" />
+            <mxPoint x="750" y="-9" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-48" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="PORTAL" vertex="1">
+          <mxGeometry height="63" width="60" x="595" y="430" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-49" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="B2C APIS" vertex="1">
+          <mxGeometry height="63" width="60" x="720" y="327" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-50" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="B2B APIS" vertex="1">
+          <mxGeometry height="63" width="60" x="720" y="430" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-52" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;CORE&lt;/font&gt;" vertex="1">
+          <mxGeometry height="30" width="60" x="760" y="640" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-53" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
+          <mxGeometry height="333.5" width="240" x="900" y="308.25" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-56" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="OnePay" vertex="1">
+          <mxGeometry height="63" width="60" x="1040" y="328.75" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-59" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;PAYMENT&lt;/font&gt;" vertex="1">
+          <mxGeometry height="30" width="90" x="1050" y="640" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-60" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="UniPay" vertex="1">
+          <mxGeometry height="63" width="60" x="1040" y="430" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-61" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.worker;fillColor=#D9A741;gradientColor=none;" value="Momo" vertex="1">
+          <mxGeometry height="63" width="60" x="1040" y="540" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-62" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-31" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=0.998;exitY=0.467;exitDx=0;exitDy=0;strokeWidth=3;strokeColor=#FF00FF;entryPerimeter=0;exitPerimeter=0;" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="620" y="347" as="sourcePoint" />
+            <mxPoint x="920" y="461.5" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-63" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
+          <mxGeometry height="890" width="200" x="1320" y="80" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-64" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="&lt;font style=&quot;font-size: 18px;&quot;&gt;PAYMENTS&lt;/font&gt;" vertex="1">
+          <mxGeometry height="30" width="60" x="1390" y="50" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-67" parent="1" style="points=[];aspect=fixed;html=1;align=center;shadow=0;dashed=0;fillColor=#FF6A00;strokeColor=none;shape=mxgraph.alibaba_cloud.dbes_database_expert_service;" value="&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;ONEPAY&lt;/div&gt;" vertex="1">
+          <mxGeometry height="47.400000000000006" width="47.400000000000006" x="1396.3" y="110" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-68" parent="1" style="points=[];aspect=fixed;html=1;align=center;shadow=0;dashed=0;fillColor=#FF6A00;strokeColor=none;shape=mxgraph.alibaba_cloud.dbes_database_expert_service;" value="&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;MOMO&lt;/div&gt;" vertex="1">
+          <mxGeometry height="47.400000000000006" width="47.400000000000006" x="1396.3" y="210" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-69" parent="1" style="points=[];aspect=fixed;html=1;align=center;shadow=0;dashed=0;fillColor=#FF6A00;strokeColor=none;shape=mxgraph.alibaba_cloud.dbes_database_expert_service;" value="&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;UNIPAY&lt;/div&gt;" vertex="1">
+          <mxGeometry height="47.400000000000006" width="47.400000000000006" x="1396.3" y="308.25" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-72" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-17" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=-0.007;entryY=0.622;entryDx=0;entryDy=0;strokeWidth=3;strokeColor=#99004D;entryPerimeter=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;" target="uRcrelpJeVWRNYr8ECgl-31" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="531" y="524.44" as="sourcePoint" />
+            <mxPoint x="620" y="524.8800000000001" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-74" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-75" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;strokeColor=#FF00FF;entryPerimeter=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;" target="uRcrelpJeVWRNYr8ECgl-56" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="850" y="450" as="sourcePoint" />
+            <mxPoint x="951" y="450" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-75" parent="1" style="outlineConnect=0;dashed=0;verticalLabelPosition=bottom;verticalAlign=top;align=center;html=1;shape=mxgraph.aws3.vpc_nat_gateway;fillColor=#F58536;gradientColor=none;" value="PAYMENT GW" vertex="1">
+          <mxGeometry height="72" width="69" x="920" y="425.5" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-76" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-75" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;strokeColor=#FF00FF;entryPerimeter=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;" target="uRcrelpJeVWRNYr8ECgl-60" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="990" y="552" as="sourcePoint" />
+            <mxPoint x="1041" y="450" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-77" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-75" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;strokeWidth=3;strokeColor=#FF00FF;entryPerimeter=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;exitPerimeter=0;" target="uRcrelpJeVWRNYr8ECgl-61" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="990" y="460" as="sourcePoint" />
+            <mxPoint x="1031" y="450" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-80" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-53" style="endArrow=classic;html=1;rounded=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.009;entryY=0.443;entryDx=0;entryDy=0;entryPerimeter=0;strokeWidth=3;" target="uRcrelpJeVWRNYr8ECgl-63" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="1210" y="440" as="sourcePoint" />
+            <mxPoint x="1260" y="390" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-81" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-63" style="endArrow=classic;html=1;rounded=0;exitX=-0.016;exitY=0.962;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;entryPerimeter=0;strokeWidth=3;exitPerimeter=0;" target="uRcrelpJeVWRNYr8ECgl-17" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <Array as="points">
+              <mxPoint x="230" y="936" />
+              <mxPoint x="230" y="514" />
+            </Array>
+            <mxPoint x="940" y="871" as="sourcePoint" />
+            <mxPoint x="1122" y="870" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-92" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
+          <mxGeometry height="140" width="580" x="560" y="710" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-93" parent="1" style="image;html=1;image=img/lib/clip_art/computers/Database_128x128.png" value="&lt;div style=&quot;text-align: left;&quot;&gt;&lt;font face=&quot;Times New Roman, Times New Roman_EmbeddedFont, Times New Roman_MSFontService, serif&quot;&gt;&lt;span style=&quot;font-size: 16px; font-variant-ligatures: none; text-wrap-mode: wrap;&quot;&gt;&lt;b&gt;ORACLE&lt;/b&gt;&lt;/span&gt;&lt;/font&gt;&lt;/div&gt;" vertex="1">
+          <mxGeometry height="80" width="80" x="595" y="739" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-94" parent="1" style="image;sketch=0;aspect=fixed;html=1;points=[];align=center;fontSize=12;image=img/lib/mscae/Cache_Redis_Product.svg;" value="&lt;font style=&quot;font-size: 16px;&quot;&gt;Redis&lt;/font&gt;" vertex="1">
+          <mxGeometry height="60" width="71.43" x="900" y="750" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-95" parent="1" style="points=[];aspect=fixed;html=1;align=center;shadow=0;dashed=0;fillColor=#FF6A00;strokeColor=none;shape=mxgraph.alibaba_cloud.kafka;" value="&lt;div&gt;&lt;font style=&quot;font-size: 16px;&quot;&gt;&lt;br&gt;&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font style=&quot;font-size: 16px;&quot;&gt;&lt;br&gt;&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font style=&quot;font-size: 16px;&quot;&gt;&lt;br&gt;&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font style=&quot;font-size: 16px;&quot;&gt;&lt;br&gt;&lt;/font&gt;&lt;/div&gt;&lt;div&gt;&lt;font style=&quot;font-size: 16px;&quot;&gt;&lt;br&gt;&lt;/font&gt;&lt;/div&gt;&lt;font style=&quot;font-size: 16px;&quot;&gt;Kafka&lt;/font&gt;" vertex="1">
+          <mxGeometry height="61" width="82.35" x="737.6500000000001" y="748.5" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-96" parent="1" style="image;html=1;image=img/lib/clip_art/computers/Database_Move_Stack_128x128.png" value="&lt;span data-contrast=&quot;auto&quot; class=&quot;TextRun MacChromeBold SCXW86909208 BCX0&quot; style=&quot;-webkit-user-drag: none; -webkit-tap-highlight-color: transparent; margin: 0px; padding: 0px; user-select: text; -webkit-font-smoothing: antialiased; text-align: left; text-wrap-mode: wrap; font-size: 12pt; line-height: 28.5px; font-family: &amp;quot;Times New Roman&amp;quot;, &amp;quot;Times New Roman_EmbeddedFont&amp;quot;, &amp;quot;Times New Roman_MSFontService&amp;quot;, serif; font-weight: bold; font-variant-ligatures: none !important;&quot;&gt;PostgreSQL&lt;/span&gt;&lt;span class=&quot;EOP SCXW86909208 BCX0&quot; data-ccp-props=&quot;{&amp;quot;201341983&amp;quot;:0,&amp;quot;335559738&amp;quot;:120,&amp;quot;335559740&amp;quot;:360}&quot; style=&quot;-webkit-user-drag: none; -webkit-tap-highlight-color: transparent; margin: 0px; padding: 0px; user-select: text; text-align: left; text-wrap-mode: wrap; font-size: 12pt; line-height: 28.5px; font-family: &amp;quot;Times New Roman&amp;quot;, &amp;quot;Times New Roman_EmbeddedFont&amp;quot;, &amp;quot;Times New Roman_MSFontService&amp;quot;, serif;&quot;&gt;&amp;nbsp;&lt;/span&gt;" vertex="1">
+          <mxGeometry height="80" width="80" x="1030" y="729.5" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-97" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-92" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.224;exitY=-0.001;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=3;" target="uRcrelpJeVWRNYr8ECgl-31" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="610" y="510" as="sourcePoint" />
+            <mxPoint x="660" y="460" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-98" edge="1" parent="1" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;exitX=0.224;exitY=-0.001;exitDx=0;exitDy=0;exitPerimeter=0;strokeWidth=3;" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <mxPoint x="1019.6800000000001" y="709" as="sourcePoint" />
+            <mxPoint x="1019.6800000000001" y="639" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-99" edge="1" parent="1" source="uRcrelpJeVWRNYr8ECgl-92" style="endArrow=classic;startArrow=classic;html=1;rounded=0;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=0;exitY=0.5;exitDx=0;exitDy=0;strokeWidth=3;entryPerimeter=0;" target="uRcrelpJeVWRNYr8ECgl-25" value="">
+          <mxGeometry height="50" relative="1" width="50" as="geometry">
+            <Array as="points">
+              <mxPoint x="520" y="780" />
+              <mxPoint x="520" y="200" />
+            </Array>
+            <mxPoint x="690" y="730" as="sourcePoint" />
+            <mxPoint x="690" y="660" as="targetPoint" />
+          </mxGeometry>
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-100" parent="1" style="rounded=0;whiteSpace=wrap;html=1;" value="" vertex="1">
+          <mxGeometry height="130" width="200" x="-41" y="-140" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-101" parent="1" style="text;html=1;whiteSpace=wrap;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;rounded=0;" value="&lt;font style=&quot;font-size: 22px;&quot;&gt;&lt;span data-contrast=&quot;auto&quot; class=&quot;TextRun MacChromeBold SCXW168624917 BCX0&quot; style=&quot;-webkit-user-drag: none; -webkit-tap-highlight-color: transparent; margin: 0px; padding: 0px; user-select: text; -webkit-font-smoothing: antialiased; text-align: left; background-color: rgb(255, 255, 255); line-height: 28.5px; font-family: &amp;quot;Times New Roman&amp;quot;, &amp;quot;Times New Roman_EmbeddedFont&amp;quot;, &amp;quot;Times New Roman_MSFontService&amp;quot;, serif; font-weight: bold; font-variant-ligatures: none !important;&quot;&gt;&lt;span class=&quot;NormalTextRun SCXW168624917 BCX0&quot; style=&quot;-webkit-user-drag: none; -webkit-tap-highlight-color: transparent; margin: 0px; padding: 0px; user-select: text;&quot;&gt;Monitoring&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;EOP SCXW168624917 BCX0&quot; data-ccp-props=&quot;{&amp;quot;201341983&amp;quot;:0,&amp;quot;335559738&amp;quot;:120,&amp;quot;335559740&amp;quot;:360}&quot; style=&quot;-webkit-user-drag: none; -webkit-tap-highlight-color: transparent; margin: 0px; padding: 0px; user-select: text; text-align: left; background-color: rgb(255, 255, 255); line-height: 28.5px; font-family: &amp;quot;Times New Roman&amp;quot;, &amp;quot;Times New Roman_EmbeddedFont&amp;quot;, &amp;quot;Times New Roman_MSFontService&amp;quot;, serif;&quot;&gt;&amp;nbsp;&lt;/span&gt;&lt;/font&gt;" vertex="1">
+          <mxGeometry height="30" width="121" x="-1.5" y="-170" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-102" parent="1" style="sketch=0;pointerEvents=1;shadow=0;dashed=0;html=1;strokeColor=none;fillColor=#505050;labelPosition=center;verticalLabelPosition=bottom;verticalAlign=top;outlineConnect=0;align=center;shape=mxgraph.office.concepts.website;" value="&lt;span data-contrast=&quot;auto&quot; class=&quot;TextRun SCXW82762732 BCX0&quot; style=&quot;-webkit-user-drag: none; -webkit-tap-highlight-color: transparent; margin: 0px; padding: 0px; user-select: text; text-align: left; text-wrap-mode: wrap; background-color: rgb(255, 255, 255); font-size: 12pt; line-height: 28.5px; font-family: &amp;quot;Times New Roman&amp;quot;, &amp;quot;Times New Roman_EmbeddedFont&amp;quot;, &amp;quot;Times New Roman_MSFontService&amp;quot;, serif; font-variant-ligatures: none !important;&quot;&gt;&lt;span class=&quot;NormalTextRun SCXW82762732 BCX0&quot; style=&quot;-webkit-user-drag: none; -webkit-tap-highlight-color: transparent; margin: 0px; padding: 0px; user-select: text;&quot;&gt;Prometheus &amp;amp; Grafana&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;EOP SCXW82762732 BCX0&quot; data-ccp-props=&quot;{&amp;quot;201341983&amp;quot;:0,&amp;quot;335559738&amp;quot;:120,&amp;quot;335559740&amp;quot;:360}&quot; style=&quot;-webkit-user-drag: none; -webkit-tap-highlight-color: transparent; margin: 0px; padding: 0px; user-select: text; text-align: left; text-wrap-mode: wrap; background-color: rgb(255, 255, 255); font-size: 12pt; line-height: 28.5px; font-family: &amp;quot;Times New Roman&amp;quot;, &amp;quot;Times New Roman_EmbeddedFont&amp;quot;, &amp;quot;Times New Roman_MSFontService&amp;quot;, serif;&quot;&gt;&amp;nbsp;&lt;/span&gt;" vertex="1">
+          <mxGeometry height="49" width="59" x="30" y="-110" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-103" parent="1" style="points=[];aspect=fixed;html=1;align=center;shadow=0;dashed=0;fillColor=#FF6A00;strokeColor=none;shape=mxgraph.alibaba_cloud.elasticsearch;" value="&lt;p data-start=&quot;190&quot; data-end=&quot;216&quot;&gt;&lt;span data-start=&quot;190&quot; data-end=&quot;212&quot;&gt;&lt;font style=&quot;font-size: 15px;&quot;&gt;&lt;br&gt;&lt;/font&gt;&lt;/span&gt;&lt;/p&gt;&lt;p data-start=&quot;190&quot; data-end=&quot;216&quot;&gt;&lt;span data-start=&quot;190&quot; data-end=&quot;212&quot;&gt;&lt;font style=&quot;font-size: 15px;&quot;&gt;&lt;br&gt;&lt;/font&gt;&lt;/span&gt;&lt;/p&gt;&lt;p data-start=&quot;190&quot; data-end=&quot;216&quot;&gt;&lt;span data-start=&quot;190&quot; data-end=&quot;212&quot;&gt;&lt;font style=&quot;font-size: 15px;&quot;&gt;&lt;br&gt;&lt;/font&gt;&lt;/span&gt;&lt;/p&gt;&lt;p data-start=&quot;190&quot; data-end=&quot;216&quot;&gt;&lt;span data-start=&quot;190&quot; data-end=&quot;212&quot; style=&quot;background-color: transparent; color: light-dark(rgb(0, 0, 0), rgb(255, 255, 255));&quot;&gt;&lt;font style=&quot;font-size: 15px;&quot;&gt;Elasticsearch&lt;/font&gt;&lt;/span&gt;&lt;/p&gt;" vertex="1">
+          <mxGeometry height="50.099999999999994" width="45.9" x="602.05" y="550" as="geometry" />
+        </mxCell>
+        <mxCell id="uRcrelpJeVWRNYr8ECgl-104" parent="1" style="text;html=1;whiteSpace=wrap;overflow=hidden;rounded=0;" value="&lt;h1 style=&quot;margin-top: 0px;&quot;&gt;Công nghệ sử dụng&lt;/h1&gt;&lt;div&gt;Front end:&lt;/div&gt;&lt;div&gt;+ Website/MiniApp: ReactJS vite tool&lt;/div&gt;&lt;div&gt;+ Application: Flutter&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Backend:&lt;/div&gt;&lt;div&gt;+ 100% sử dụng Dotnet core 7 API&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Database:&lt;/div&gt;&lt;div&gt;+ Oracle&lt;/div&gt;&lt;div&gt;+ PostgresSQL&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Cache:&lt;/div&gt;&lt;div&gt;+ Redis&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Queue:&lt;/div&gt;&lt;div&gt;+ Kafka&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Identify service:&lt;/div&gt;&lt;div&gt;+ Keycloak&lt;/div&gt;&lt;div&gt;&lt;br&gt;&lt;/div&gt;&lt;div&gt;Api Gateway:&lt;/div&gt;&lt;div&gt;+ KONG&amp;nbsp;&lt;/div&gt;" vertex="1">
+          <mxGeometry height="380" width="490" x="1030" y="-329" as="geometry" />
+        </mxCell>
+      </root>
+    </mxGraphModel>
+  </diagram>
+</mxfile>

+ 6 - 3
EsimLao/esim-vite/src/App.tsx

@@ -19,6 +19,7 @@ import OrderDetailView from "./pages/order-detail/OrderDetailView";
 import CompatibilityModal from "./components/CompatibilityModal";
 import QRCodeModal from "./components/QRCodeModal";
 import { useAppSelector } from "./hooks/useRedux";
+import ScrollToTop from "./components/ScrollToTop";
 
 const App: React.FC = () => {
   const location = useLocation();
@@ -38,7 +39,7 @@ const App: React.FC = () => {
       }`}
     >
       {!isPlainView && <Header />}
-
+      <ScrollToTop />
       <Popup />
       <CompatibilityModal />
       <QRCodeModal />
@@ -57,8 +58,10 @@ const App: React.FC = () => {
             <Routes>
               <Route path="/" element={<HomeView />} />
               <Route path="/buy-sim" element={<BuySimView />} />
-              <Route path="/product/:id" element={<ProductDetailView />} />
-              {/* <Route path="/product" element={<ProductDetailView />} /> */}
+              <Route
+                path="/product-detail/:id"
+                element={<ProductDetailView />}
+              />
               <Route path="/checkout" element={<CheckoutView />} />
               <Route path="/support" element={<SupportView />} />
               <Route path="/news" element={<NewsView />} />

+ 2 - 2
EsimLao/esim-vite/src/apis/productApi.ts

@@ -52,8 +52,8 @@ class ProductApi extends BaseApi {
     return this.productPost("/list_payment_channel", {});
   }
 
-  async getOrderHistory({ fromDate, toDate, status }) {
-    return this.productPost("/list_order", { fromDate, toDate, status });
+  async getOrderHistory({ searchOrder, fromDate, toDate, status }) {
+    return this.productPost("/list_order", { searchOrder, fromDate, toDate, status });
   }
 
   async getOrderDetail({ orderId }) {

+ 18 - 17
EsimLao/esim-vite/src/components/CompatibilityModal.tsx

@@ -90,23 +90,23 @@ const CompatibilityModal = () => {
         </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">
+        <div className="shrink-0 p-8 md:p-10 lg:p-10 pb-2 text-center bg-white z-10 relative">
+          <h2 className="text-xl md:text-3xl font-black text-slate-900 mb-2 tracking-tight">
             {t("doesEsimWorkWithMyDevice")} <br />
-            {/* <span className="text-[#0091ff]">support eSIM ?</span> */}
+            {/* <span className="text-[#EE0434]">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="flex-1 overflow-y-auto p-3 md:p-8 lg:p-10 pt-1 custom-scrollbar">
           <div className="text-center mb-8">
-            <h3 className="text-xl md:text-2xl font-black text-[#0091ff] mb-6">
+            <h3 className="text-xm md:text-base lg:text-xl font-black text-[#EE0434] mb-6">
               {t("selectTopicInstructions")}
             </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">
+            <div className="text-left bg-slate-50 p-3 rounded-2xl border border-slate-100 text-sm md:text-base text-slate-600 space-y-4 mb-8">
               <p className="font-bold">{t("importantNote")}</p>
-              <ul className="list-disc pl-5 space-y-1 marker:text-[#0091ff]">
+              <ul className="list-disc pl-5 space-y-1 marker:text-[#EE0434]">
                 <li>{t("someDualPhysicalSimModelsDoNotSupportEsim")}</li>
                 <li>{t("carrierLockedDevicesMayNotSupportEsim")}</li>
                 <li>
@@ -167,16 +167,16 @@ const CompatibilityModal = () => {
                   )}
                 </div>
                 <span
-                  className={`font-bold text-lg transition-colors ${
+                  className={`font-bold transition-colors text-lg md:text-lg lg:text-xl ${
                     selectedBrand === brand.brand
-                      ? "text-[#0091ff]"
+                      ? "text-[#EE0434]"
                       : "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>
+                  <div className="w-12 h-1 bg-[#EE0434] rounded-full"></div>
                 )}
               </button>
             ))}
@@ -188,7 +188,7 @@ const CompatibilityModal = () => {
               {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"
+                  className="py-4 px-4 text-center hover:bg-slate-50 transition-colors text-slate-700 font-bold text-x,"
                 >
                   {device}
                 </div>
@@ -219,14 +219,15 @@ const CompatibilityModal = () => {
 
           {/* Method 2 */}
           <div className="text-center mb-12">
-            <h3 className="text-xl md:text-3xl font-black text-[#0091ff] mb-4">
+            <h3 className="text-xl md:text-2xl font-black text-[#EE0434] mb-4">
               {t("method2CheckDirectlyOnYourDevice")}
             </h3>
-            <p className="text-slate-600 text-lg">
+            <p className="text-slate-600 text-xs md:text-lg lg:text-xl">
               {t(
                 "checkIfYourDeviceSupportsEsimAndWhetherItIsCarrierLockedByFollowing"
               )}
-              <button className="text-[#0091ff] font-bold hover:underline">
+              <button className="text-[#EE0434] font-bold hover:underline">
+                {" "}
                 {t("theStepsBelow")}
               </button>
             </p>
@@ -234,12 +235,12 @@ const CompatibilityModal = () => {
 
           {/* Not Support */}
           <div className="text-center mb-8">
-            <h3 className="text-xl md:text-3xl font-black text-[#0091ff] mb-4">
+            <h3 className="text-xl md:text-2xl font-black text-[#EE0434] mb-4">
               {t("yourDeviceDoesNotSupportEsim")}
             </h3>
-            <p className="text-slate-600 text-lg">
+            <p className="text-slate-600 text-xs md:text-lg lg:text-xl">
               {t("tryFindingAPhysicalSimThatIsCompatibleWithYourDevice")}{" "}
-              <button className="text-[#0091ff] font-bold hover:underline">
+              <button className="text-[#EE0434] font-bold hover:underline">
                 {t("here")}
               </button>
             </p>

+ 1 - 0
EsimLao/esim-vite/src/components/Footer.tsx

@@ -21,6 +21,7 @@ const Footer: React.FC = () => {
                 src={logo}
                 alt="Getgo Logo"
                 className="text-[#EE0434] mr-0"
+                style={{ width: "150px" }}
               />
               {/* <span className="text-xl font-black text-[#EE0434]">etgo</span> */}
             </div>

+ 13 - 17
EsimLao/esim-vite/src/components/Header.tsx

@@ -16,7 +16,11 @@ import { useSelector } from "react-redux";
 import { setAreas } from "../features/areas/areasSlice";
 import i18n from "../i18n";
 import { useTranslation } from "react-i18next";
-import { getWithExpiry, setWithExpiry } from "../logic/loigicUtils";
+import {
+  formatCurrency,
+  getWithExpiry,
+  setWithExpiry,
+} from "../logic/loigicUtils";
 
 const Header: React.FC = () => {
   const navigate = useNavigate();
@@ -56,8 +60,8 @@ const Header: React.FC = () => {
   const [isSearchDropdownOpen, setIsSearchDropdownOpen] = useState(false);
 
   const languages = [
-    { code: "en", label: "English", flag: "us" },
-    { code: "vi", label: "Tiếng Việt", flag: "vn" },
+    { code: "en", label: t("english"), flag: "us" },
+    { code: "vi", label: t("vietnamese"), flag: "vn" },
   ];
 
   // load product by country/region or popularity
@@ -85,16 +89,8 @@ const Header: React.FC = () => {
     },
   });
 
-  useEffect(() => {
-    const handleScroll = () => {
-      setIsScrolled(window.scrollY > 300);
-    };
-    window.addEventListener("scroll", handleScroll);
-    return () => window.removeEventListener("scroll", handleScroll);
-  }, []);
-
   const handleAreaClick = (c: { id: number }) => {
-    navigate(`/product/${c.id}`, {
+    navigate(`/product-detail/${c.id}`, {
       state: {
         ...c,
       },
@@ -124,8 +120,9 @@ const Header: React.FC = () => {
     return () => window.removeEventListener("resize", handleResize);
   }, []);
 
+  const langNow = localStorage.getItem("lang") || "en";
   const currentLangObj =
-    languages.find((l) => l.code === selectedLang) || languages[0];
+    languages.find((l) => l.code === (selectedLang || langNow)) || languages[0];
 
   const isActive = (path: string) => location.pathname === path;
 
@@ -137,7 +134,6 @@ const Header: React.FC = () => {
     if (!areas || areas.length === 0) getAreaMutation.mutate();
     else {
       setAreasList(areas);
-      console.log("Areas loaded from store:", areas);
     }
   }, []);
 
@@ -155,7 +151,7 @@ const Header: React.FC = () => {
       console.log("Get area response data:", data);
       if (data && data.errorCode === "0") {
         console.log("Get area successful");
-        setWithExpiry("areas", JSON.stringify(data.data));
+        setWithExpiry("areas", data.data);
         setAreasList(data.data as Area[]);
       } else {
         console.error("Get area failed, no token received");
@@ -170,7 +166,7 @@ const Header: React.FC = () => {
   const handleSelect = (area: Area) => {
     console.log("Selected area:", area);
     setIsSearchDropdownOpen(false);
-    navigate(`/product/${area.id}`, {
+    navigate(`/product-detail/${area.id}`, {
       state: {
         ...area,
       },
@@ -289,7 +285,7 @@ const Header: React.FC = () => {
                                   {t("from")}:
                                 </span>
                                 <span className="text-sm font-black text-[#EE0434]">
-                                  {p.minSellPrice.toLocaleString()} {p.curency}
+                                  {formatCurrency(p.minSellPrice, p.curency)}
                                 </span>
                               </div>
                             </button>

+ 3 - 3
EsimLao/esim-vite/src/components/Popup.tsx

@@ -72,7 +72,7 @@ const Popup = () => {
         </p>
 
         {/* Button */}
-        <div className="w-full flex flex-col md:flex-row gap-4 md:gap-6">
+        <div className="w-full flex flex-col md:flex-row gap-1 md:gap-1">
           {popup.hasRightButton && (
             <button
               onClick={() => {
@@ -81,7 +81,7 @@ const Popup = () => {
                 }
                 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"
+              className="mr-4 w-full py-4 rounded-2xl font-black text-xm shadow-lg active:scale-95 transition-all bg-[#EE0434] hover:bg-[#d10029] text-white shadow-red-200 p-2"
             >
               {popup.rightButtonText}
             </button>
@@ -90,7 +90,7 @@ const Popup = () => {
             onClick={() => {
               dispatch(closePopup());
             }}
-            className={`w-full py-4 rounded-2xl font-black text-lg shadow-lg active:scale-95 transition-all ${
+            className={`w-full py-4 rounded-2xl font-black text-xm shadow-lg active:scale-95 transition-all p-2 ${
               popup.isSuccess
                 ? "bg-[#00c087] hover:bg-[#00a876] text-white shadow-green-200"
                 : "bg-[#EE0434] hover:bg-[#d10029] text-white shadow-red-200"

+ 7 - 5
EsimLao/esim-vite/src/components/ProductCard.tsx

@@ -3,7 +3,7 @@ import React from "react";
 import { SimProduct } from "../services/types";
 import { Area } from "../services/product/type";
 import { useTranslation } from "react-i18next";
-import { formatNumber } from "../logic/loigicUtils";
+import { formatCurrency, formatNumber } from "../logic/loigicUtils";
 
 const ProductCard: React.FC<{
   p: Area;
@@ -30,12 +30,14 @@ const ProductCard: React.FC<{
           {p.areaName1}
         </h3>
         <p className="text-xs text-slate-400 line-through mb-1">
-          {formatNumber(p.minDisplayPrice)} {p.curency}
+          {formatCurrency(p.minDisplayPrice, p.curency)}
         </p>
         <p className="text-[#EE0434] font-bold text-xs md:text-lg">
-          {t("from")}:{" "}
-          <span className="font-black">
-            {formatNumber(p.minSellPrice)} {p.curency}
+          <span className="text-black text-sm md:text-sm lg:text-base">
+            {t("from")}:{" "}
+          </span>
+          <span className="font-black text-lg md:text-xs lg:text-xl">
+            {formatCurrency(p.minSellPrice, p.curency)}
           </span>
         </p>
       </div>

+ 1 - 1
EsimLao/esim-vite/src/components/ProductInfoModal.tsx

@@ -274,7 +274,7 @@ const ProductInfoModal: React.FC<ProductInfoModalProps> = ({
       <div className="relative bg-white rounded-[32px] w-full max-w-2xl max-h-[85vh] shadow-2xl flex flex-col overflow-hidden">
         {/* Header */}
         <div className="p-6 pb-2 border-b border-slate-100 flex justify-between items-center bg-white z-10">
-          <h2 className="text-xl md:text-2xl font-black text-slate-900">
+          <h2 className="text-xl md:text-2xl font-black text-slate-800">
             {t("productInformation")}
           </h2>
           <button

+ 1 - 3
EsimLao/esim-vite/src/components/QRCodeModal.tsx

@@ -30,9 +30,7 @@ const QRCodeModal = () => {
             <div className="aspect-square w-full bg-white rounded-xl flex items-center justify-center p-3 shadow-sm">
               {/* Placeholder QR Code for demo purposes */}
               <img
-                src={`https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=${encodeURIComponent(
-                  popup.qrData
-                )}&margin=10`}
+                src={popup.qrData}
                 alt="eSIM QR Code"
                 className="w-full h-full object-contain mix-blend-multiply"
               />

+ 18 - 0
EsimLao/esim-vite/src/components/ScrollToTop.tsx

@@ -0,0 +1,18 @@
+import { useEffect } from "react";
+import { useLocation } from "react-router-dom";
+
+const ScrollToTop = () => {
+  const { pathname } = useLocation();
+
+  useEffect(() => {
+    window.scrollTo({
+      top: 0,
+      left: 0,
+      behavior: "auto", // khuyến nghị: auto cho performance
+    });
+  }, [pathname]);
+
+  return null;
+};
+
+export default ScrollToTop;

+ 20 - 4
EsimLao/esim-vite/src/i18n/locales/en.json

@@ -45,7 +45,7 @@
   "dataData": "Data",
   "unlimitedUnlimited": "Unlimited",
   "simType": "SIM Type",
-  "quantityQuantity": "Quantity",
+  "quantity": "Quantity",
   "esimEsim": "eSIM",
   "days": "days",
   "discountDiscount": "Discount",
@@ -73,7 +73,7 @@
   "paymentStatus": "Payment Status",
   "paymentSuccessful": "Payment successful!",
   "viewOrder": "View Order",
-  "closeClose": "Close",
+  "close": "Close",
   "transactionHistory": "Transaction History",
   "enterCode": "Enter order code",
   "fromDate": "From date",
@@ -206,5 +206,21 @@
   "emailAndEsimQrCode": "Email and eSIM QR code",
   "installationGuideForIphoneIos": "Installation guide for iPhone (iOS)",
   "installationGuideForAndroid": "Installation guide for Android",
-  "buyNow": "Buy now"
-}
+  "buyNow": "Buy now",
+  "expiredAt": "Expired at",
+  "notActive": "Not Active",
+  "active": "Active",
+  "finished": "Finished",
+  "expired": "Expired",
+  "unknown": "Unknown",
+  "paymentSuccess": "Payment Successful",
+  "paymentFailed": "Payment Failed",
+  "activationMethod": "Activation Method",
+  "productDetailsMissing": "Please reload the page",
+  "goToShop": "Go to Shop",
+  "english": "English",
+  "vietnamese": "Vietnamese",
+  "numberOfDays": "Number of Days",
+  "verified": "Verified",
+  "highSpeed": "High Speed"
+}

+ 20 - 4
EsimLao/esim-vite/src/i18n/locales/vi.json

@@ -45,7 +45,7 @@
   "dataData": "Dữ liệu",
   "unlimitedUnlimited": "Không giới hạn",
   "simType": "Loại SIM",
-  "quantityQuantity": "Số lượng",
+  "quantity": "Số lượng",
   "esimEsim": "eSIM",
   "days": "ngày",
   "discountDiscount": "Giảm giá",
@@ -73,7 +73,7 @@
   "paymentStatus": "Trạng thái thanh toán",
   "paymentSuccessful": "Thanh toán thành công!",
   "viewOrder": "Xem đơn hàng",
-  "closeClose": "Đóng",
+  "close": "Đóng",
   "transactionHistory": "Lịch sử giao dịch",
   "enterCode": "Nhập mã đơn hàng",
   "fromDate": "Từ ngày",
@@ -206,5 +206,21 @@
   "emailAndEsimQrCode": "Email và mã QR eSIM",
   "installationGuideForIphoneIos": "Hướng dẫn cài đặt cho iPhone (iOS)",
   "installationGuideForAndroid": "Hướng dẫn cài đặt cho Android",
-  "buyNow": "Mua ngay"
-}
+  "buyNow": "Mua ngay",
+  "expiredAt": "Hết hạn vào",
+  "notActive": "Chưa kích hoạt",
+  "active": "Đang hoạt động",
+  "finished": "Đã sử dụng hết",
+  "expired": "Đã hết hạn",
+  "unknown": "Không xác định",
+  "paymentSuccess": "Thanh toán thành công",
+  "paymentFailed": "Thanh toán thất bại",
+  "activationMethod": "Phương thức kích hoạt",
+  "productDetailsMissing": "Vui lòng tải lại trang",
+  "goToShop": "Đi đến cửa hàng",
+  "english": "Tiếng Anh",
+  "vietnamese": "Tiếng Việt",
+  "numberOfDays": "Số ngày",
+  "verified": "Đã xác thực",
+  "highSpeed": "Tốc độ cao"
+}

+ 4 - 4
EsimLao/esim-vite/src/index.css

@@ -1,8 +1,8 @@
 @import "tailwindcss";
 
 .absolute-right-18 {
-    right: -18px;
-  }
+  right: -18px;
+}
 
 /* max 768px screen */
 @media (max-width: 768px) {
@@ -11,8 +11,8 @@
   }
 }
 
-.font-black{
-  font-weight: 700 !important;
+.font-black {
+  font-weight: 600 !important;
 }
 
 .color-EE0434 {

+ 36 - 2
EsimLao/esim-vite/src/logic/loigicUtils.ts

@@ -7,9 +7,9 @@ export const formatCarriers = (text) => {
   return text.replace(/,\s*/g, ", ");
 };
 
-export const setWithExpiry = (
+export const setWithExpiry = <T>(
   key: string,
-  value: string,
+  value: T,
   ttlMs?: number | null
 ) => {
   const now = Date.now();
@@ -40,3 +40,37 @@ export const getWithExpiry = <T>(key: string): T | null => {
     return null;
   }
 };
+
+type CurrencyCode =
+  | "VND"
+  | "USD"
+  | "EUR"
+  | "JPY"
+  | "KRW"
+  | "CNY"
+  | "SGD"
+  | "THB";
+
+export const formatCurrency = (
+  amount: number | string,
+  currency?: string,
+  locale?: string
+) => {
+  if (amount === null || amount === undefined || amount === "") return "";
+
+  const value = Number(amount);
+
+  const resolvedLocale =
+    locale ??
+    (currency === "VND"
+      ? "vi-VN"
+      : currency === "USD"
+        ? "en-US"
+        : "en-GB");
+
+  return new Intl.NumberFormat(resolvedLocale, {
+    style: "currency",
+    currency,
+    minimumFractionDigits: currency === "VND" ? 0 : 2,
+  }).format(value);
+};

+ 1 - 1
EsimLao/esim-vite/src/pages/buy-sim/BuySimView.tsx

@@ -54,7 +54,7 @@ const BuySimView: React.FC<BuySimViewProps> = ({
 
   const handleSelect = (p: Area) => {
     // open product detail view
-    navigate(`/product/${p.id}`, {
+    navigate(`/product-detail/${p.id}`, {
       state: {
         ...p,
       },

+ 29 - 13
EsimLao/esim-vite/src/pages/checkout/CheckoutView.tsx

@@ -18,8 +18,9 @@ import {
 import { useAppDispatch, useAppSelector } from "../../hooks/useRedux";
 import { productApi } from "../../apis/productApi";
 import { openPopup } from "../../features/popup/popupSlice";
-import { formatNumber } from "../../logic/loigicUtils";
+import { formatCurrency, formatNumber } from "../../logic/loigicUtils";
 import { useTranslation } from "react-i18next";
+import { format } from "path";
 
 const CheckoutView = () => {
   const navigate = useNavigate();
@@ -50,6 +51,12 @@ const CheckoutView = () => {
     checkoutDetails: CheckoutDetailResponse;
   };
 
+  if (state == null) {
+    // redirect to buy sim page
+    navigate("/buy-sim");
+    return null;
+  }
+
   const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
     const { name, value } = e.target;
     setForm((prev) => ({ ...prev, [name]: value }));
@@ -300,12 +307,16 @@ const CheckoutView = () => {
               </div>
               <div className="text-right">
                 <p className="text-xs text-slate-400 line-through font-bold">
-                  {formatNumber(state.checkoutDetails.totalMoney)}{" "}
-                  {state.checkoutDetails.curency}
+                  {formatCurrency(
+                    state.checkoutDetails.totalMoney,
+                    state.checkoutDetails.curency
+                  )}
                 </p>
                 <p className="text-[#0071e3] font-black text-xl">
-                  {formatNumber(state.checkoutDetails.paymentMoney)}{" "}
-                  {state.checkoutDetails.curency}
+                  {formatCurrency(
+                    state.checkoutDetails.paymentMoney,
+                    state.checkoutDetails.curency
+                  )}
                 </p>
               </div>
             </div>
@@ -409,17 +420,20 @@ const CheckoutView = () => {
               <div className="flex justify-between text-slate-600 font-bold">
                 <span>{t("subtotal")}:</span>
                 <span>
-                  {formatNumber(
-                    state.checkoutDetails.totalMoney * state.quantity
-                  )}{" "}
-                  {state.checkoutDetails.curency}
+                  {formatCurrency(
+                    state.checkoutDetails.totalMoney * state.quantity,
+                    state.checkoutDetails.curency
+                  )}
                 </span>
               </div>
               <div className="flex justify-between text-green-600 font-bold">
                 <span>Discount:</span>
                 <span>
-                  -{formatNumber(0 * state.quantity)}{" "}
-                  {state.checkoutDetails.curency}
+                  -
+                  {formatCurrency(
+                    state.checkoutDetails.discount,
+                    state.checkoutDetails.curency
+                  )}
                 </span>
               </div>
               <div className="flex justify-between items-center pt-4 border-t border-slate-200/50">
@@ -427,8 +441,10 @@ const CheckoutView = () => {
                   {t("totalTotal")} ({state.quantity}):
                 </span>
                 <span className="text-[#0071e3] font-black text-3xl">
-                  {formatNumber(state.checkoutDetails.paymentMoney)}{" "}
-                  {state.checkoutDetails.curency}
+                  {formatCurrency(
+                    state.checkoutDetails.paymentMoney,
+                    state.checkoutDetails.curency
+                  )}
                 </span>
               </div>
             </div>

+ 17 - 10
EsimLao/esim-vite/src/pages/home/HomeView.tsx

@@ -13,6 +13,7 @@ import partner2 from "../../assets/img/partner2.png";
 import { authApi } from "../../apis/authApi";
 import { accountLogin } from "../../features/account/accuntSlice";
 import { useTranslation } from "react-i18next";
+import i18n from "../../i18n";
 
 const HomeView: React.FC = () => {
   const [simType, setSimType] = useState<"eSIM" | "Physical">("eSIM");
@@ -20,6 +21,13 @@ const HomeView: React.FC = () => {
   const navigate = useNavigate();
   const dispatch = useAppDispatch();
   const { t } = useTranslation();
+  const langNow = localStorage.getItem("lang") || "en";
+
+  useEffect(() => {
+    // set language in i18n
+    i18n.changeLanguage(langNow);
+    localStorage.setItem("lang", langNow);
+  }, [langNow]);
 
   useEffect(() => {
     const params = new URLSearchParams(window.location.search);
@@ -31,18 +39,17 @@ const HomeView: React.FC = () => {
 
     // google callback
     const code = searchParams.get("code");
-
     if (status) {
       console.log("URL Params:", params);
       if (status === "0") {
         dispatch(
           openPopup({
             isSuccess: true,
-            message: "Payment successful!",
-            title: "Payment Status",
-            buttonText: "Close",
+            message: t("paymentSuccess"),
+            title: t("paymentStatus"),
+            buttonText: t("close"),
             hasRightButton: true,
-            rightButtonText: "View Order",
+            rightButtonText: t("viewOrder"),
             rightButtonAction: "OPEN_ORDER_HISTORY",
           })
         );
@@ -50,9 +57,9 @@ const HomeView: React.FC = () => {
         dispatch(
           openPopup({
             isSuccess: false,
-            message: "Payment failed or cancelled.",
-            title: "Payment Status",
-            buttonText: "Close",
+            message: t("paymentFailed"),
+            title: t("paymentStatus"),
+            buttonText: t("close"),
           })
         );
       }
@@ -301,7 +308,7 @@ const HomeView: React.FC = () => {
             <div className="inline-flex p-1 bg-slate-100 rounded-full shadow-inner">
               <button
                 onClick={() => setSimType("eSIM")}
-                className={`px-6 md:px-12 py-2.5 md:py-3 rounded-full text-[10px] md:text-sm font-bold transition-all ${
+                className={`px-6 md:px-12 py-2.5 md:py-3 rounded-full text-lg md:text-xs lg:text-xl font-bold transition-all ${
                   simType === "eSIM"
                     ? "bg-gradient-to-r from-[#E21c34] to-[#500B28] text-white shadow-md"
                     : "bg-[#f0f0f0] text-[#8b8e96] hover:text-slate-800"
@@ -311,7 +318,7 @@ const HomeView: React.FC = () => {
               </button>
               <button
                 onClick={() => setSimType("Physical")}
-                className={`px-6 md:px-12 py-2.5 md:py-3 rounded-full text-[10px] md:text-sm font-bold transition-all ${
+                className={`px-6 md:px-12 py-2.5 md:py-3 rounded-full text-lg md:text-xs lg:text-xl font-bold transition-all ${
                   simType === "Physical"
                     ? "bg-gradient-to-r from-[#E21c34] to-[#500B28] text-white shadow-md"
                     : "bg-[#f0f0f0] text-[#8b8e96] hover:text-slate-800"

+ 1 - 1
EsimLao/esim-vite/src/pages/home/components/HomeBanner.tsx

@@ -78,7 +78,7 @@ const HomeBanner = () => {
                 <div className="inline-block px-4 py-1.5 bg-white/20 backdrop-blur-xl rounded-full text-[10px] md:text-xs font-black uppercase tracking-[0.3em] border border-white/10">
                   {banner.title}
                 </div>
-                <h1 className="text-4xl md:text-7xl lg:text-8xl font-black leading-[1.1] tracking-tighter max-w-4xl">
+                <h1 className="text-4xl md:text-4xl lg:text-5xl font-black leading-[1.1] tracking-tighter max-w-4xl">
                   {banner.title} <br />
                   <span className="text-white/60 drop-shadow-sm">
                     {banner.subtitle}

+ 9 - 32
EsimLao/esim-vite/src/pages/home/components/HomeProduct.tsx

@@ -10,8 +10,9 @@ import {
 } from "../../../features/loading/loadingSlice";
 import { DataCacheKey, staleTime } from "../../../global/constants";
 import { Area } from "../../../services/product/type";
-import { formatNumber } from "../../../logic/loigicUtils";
+import { formatCurrency, formatNumber } from "../../../logic/loigicUtils";
 import { useTranslation } from "react-i18next";
+import ProductCard from "../../../components/ProductCard";
 
 const HomeProduct = () => {
   const [activeTab, setActiveTab] = useState<"country" | "region">("country");
@@ -31,11 +32,11 @@ const HomeProduct = () => {
         activeTab === "country"
           ? {
               isCountry: "1",
-              isPopular: "-1",
+              isPopular: "1",
             }
           : {
               isCountry: "0",
-              isPopular: "-1",
+              isPopular: "1",
             }
       );
       return res;
@@ -58,7 +59,7 @@ const HomeProduct = () => {
   });
 
   const handleProductClick = (p: Area) => {
-    navigate(`/product/${p.id}`, {
+    navigate(`/product-detail/${p.id}`, {
       state: {
         ...p,
       },
@@ -82,7 +83,7 @@ const HomeProduct = () => {
             setActiveTab("country");
             getAreaMutation.mutate();
           }}
-          className={`px-8 md:px-12 py-3 rounded-full text-xs md:text-sm font-black transition-all ${
+          className={`px-8 md:px-12 py-3 rounded-full text-lg md:text-xs lg:text-xl font-black transition-all ${
             activeTab === "country"
               ? "bg-gradient-to-r from-[#E21c34] to-[#500B28] text-white shadow-lg"
               : "bg-[#f0f0f0] text-[#8b8e96] hover:text-slate-800"
@@ -95,7 +96,7 @@ const HomeProduct = () => {
             setActiveTab("region");
             getAreaMutation.mutate();
           }}
-          className={`px-8 md:px-12 py-3 rounded-full text-xs md:text-sm font-black transition-all ${
+          className={`px-8 md:px-12 py-3 rounded-full text-lg md:text-xs lg:text-xl  font-black transition-all ${
             activeTab === "region"
               ? "bg-gradient-to-r from-[#E21c34] to-[#500B28] text-white shadow-lg"
               : "bg-[#f0f0f0] text-[#8b8e96] hover:text-slate-800"
@@ -108,31 +109,7 @@ const HomeProduct = () => {
       <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
         <div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 md:gap-8">
           {areasList.map((p) => (
-            <div
-              key={p.id}
-              onClick={() => handleProductClick(p)}
-              className="group bg-white border border-slate-100 rounded-[32px] p-6 md:p-10 shadow-sm hover:shadow-2xl transition-all relative overflow-hidden cursor-pointer flex flex-col items-center"
-            >
-              <div className="absolute top-4 right-4 bg-[#EE0434] text-white text-[10px] font-black px-2.5 py-1 rounded-lg z-10">
-                {p.promotionPercent}%
-              </div>
-              <div className="w-16 h-16 md:w-20 md:h-20 mb-6 rounded-full overflow-hidden shadow-xl border-4 border-white group-hover:scale-110 transition-transform">
-                <img
-                  src={`${p.iconUrl}`}
-                  alt={p.areaName1}
-                  className="w-full h-full object-cover scale-150"
-                />
-              </div>
-              <h3 className="text-lg md:text-xl font-black text-slate-800 mb-2 truncate w-full group-hover:text-[#EE0434] transition-colors">
-                {p.areaName1}
-              </h3>
-              <p className="text-xs text-slate-400 line-through mb-1">
-                {formatNumber(p.minDisplayPrice)} {p.curency}
-              </p>
-              <p className="text-lg md:text-2xl font-black text-[#EE0434]">
-                From {formatNumber(p.minSellPrice)} {p.curency}
-              </p>
-            </div>
+            <ProductCard key={p.id} p={p} onClick={handleProductClick} />
           ))}
         </div>
       </div>
@@ -156,7 +133,7 @@ const HomeProduct = () => {
               />
             </svg>
           </div>
-          <span className="ml-6 text-white text-2xl font-black">
+          <span className="ml-6 text-white text-lg font-black">
             {t("seeMore")}
           </span>
         </button>

+ 11 - 7
EsimLao/esim-vite/src/pages/home/components/HomeSearch.tsx

@@ -11,7 +11,11 @@ import { setAreas } from "../../../features/areas/areasSlice";
 import { Area } from "../../../services/product/type";
 import { useNavigate } from "react-router";
 import { useTranslation } from "react-i18next";
-import { getWithExpiry, setWithExpiry } from "@/src/logic/loigicUtils";
+import {
+  formatCurrency,
+  getWithExpiry,
+  setWithExpiry,
+} from "../../../logic/loigicUtils";
 import { get } from "http";
 
 const HomeSearch = () => {
@@ -27,8 +31,8 @@ const HomeSearch = () => {
   useEffect(() => {
     if (!areas || areas.length === 0) getAreaMutation.mutate();
     else {
+      console.log("Areas loaded from cache:", areas.length, areas);
       setAreasList(areas);
-      console.log("Areas loaded from store:", areas);
     }
   }, []);
 
@@ -47,7 +51,7 @@ const HomeSearch = () => {
       if (data && data.errorCode === "0") {
         console.log("Get area successful");
         // dispatch(setAreas(data.data as Area[]));
-        setWithExpiry("areas", JSON.stringify(data.data));
+        setWithExpiry("areas", data.data);
         setAreasList(data.data as Area[]);
       } else {
         console.error("Get area failed, no token received");
@@ -62,7 +66,7 @@ const HomeSearch = () => {
   const handleSelect = (area: Area) => {
     console.log("Selected area:", area);
     setIsDropdownOpen(false);
-    navigate(`/product/${area.id}`, {
+    navigate(`/product-detail/${area.id}`, {
       state: {
         ...area,
       },
@@ -144,8 +148,8 @@ const HomeSearch = () => {
                     {t("mostPopular")}
                   </h3>
                   <div className="space-y-1">
-                    {areasList.length > 0 ? (
-                      areasList.map((p) => (
+                    {areasList && areasList.length > 0 ? (
+                      areasList?.map((p) => (
                         <button
                           key={p.id}
                           onClick={() => handleSelect(p)}
@@ -168,7 +172,7 @@ const HomeSearch = () => {
                               {t("from")}:
                             </span>
                             <span className="text-lg font-black text-[#EE0434]">
-                              {p.minSellPrice.toLocaleString()} {p.curency}
+                              {formatCurrency(p.minSellPrice, p.curency)}
                             </span>
                           </div>
                         </button>

+ 2 - 1
EsimLao/esim-vite/src/pages/home/components/HomeTestimonial.tsx

@@ -44,7 +44,8 @@ const HomeTestimonial = () => {
         <div className="grid grid-cols-1 lg:grid-cols-12 gap-8 lg:gap-12 items-center">
           <div className="lg:col-span-4 text-white space-y-4 md:space-y-6 text-center lg:text-left">
             <h2 className="text-2xl md:text-4xl lg:text-[36px] font-black leading-tight tracking-tight">
-              {t("whatUs")} <br className="hidden lg:block" /> {t("sayAboutUs")}
+              {t("whatUs")}
+              {/* <br className="hidden lg:block" /> {t("sayAboutUs")} */}
             </h2>
             <p className="text-sm md:text-xl font-medium text-white/80 max-w-sm mx-auto lg:mx-0">
               {t("overMillionSatisfiedCustomers")}

+ 101 - 59
EsimLao/esim-vite/src/pages/order-detail/OrderDetailView.tsx

@@ -10,8 +10,9 @@ import { useMutation } from "@tanstack/react-query";
 import React, { useState, useEffect } from "react";
 import { useLocation, useNavigate } from "react-router-dom";
 import { openQRModal } from "../../features/popup/popupSlice";
-import { formatNumber } from "../../logic/loigicUtils";
+import { formatCurrency, formatNumber } from "../../logic/loigicUtils";
 import { useTranslation } from "react-i18next";
+import { format } from "path";
 
 const OrderDetailView = () => {
   const location = useLocation();
@@ -88,70 +89,112 @@ const OrderDetailView = () => {
   };
 
   // Circular Progress Component
-  const CircularProgress = ({
-    used,
-    total,
-    label,
-  }: {
-    used: number;
-    total: number;
-    label: string;
-  }) => {
-    const radius = 50;
+  const CircularProgress = ({ dataUsage }: { dataUsage: DataUsage }) => {
+    const size = 180;
+    const strokeWidth = 14;
+    const radius = (size - strokeWidth) / 2;
     const circumference = 2 * Math.PI * radius;
-    const percentage = (used / total) * 100;
-    const strokeDashoffset = circumference - (percentage / 100) * circumference;
+
+    // 240 degrees calculation
+    const arcAngle = 240;
+    const arcLength = (arcAngle / 360) * circumference;
+
+    const percentage = Math.min(
+      Math.max(dataUsage.usageData / dataUsage.totalData, 0),
+      1
+    );
+    const strokeDashoffset = arcLength - percentage * arcLength;
+
+    // Rotate the SVG to center the 240-degree gap at the bottom
+    // The gap is 120 degrees. We want it balanced.
+    // Default SVG circle starts at 3 o'clock.
+    // To have the 240 arc centered at top, we rotate.
+    const rotation = 150; // (360 - 240) / 2 + 90
 
     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">
+        <div className="flex flex-col items-center justify-center">
+          <span className="text-sm md:text-sm font-black text-[#000] mb-2">
+            {dataUsage.expiredTime
+              ? `${t("expiredAt")}: ${dataUsage.expiredTime}`
+              : ""}
+          </span>
+        </div>
+        <div className="relative" style={{ width: size, height: size }}>
           <svg
-            className="transform -rotate-90 w-full h-full"
-            viewBox="0 0 120 120"
+            width={size}
+            height={size}
+            style={{ transform: `rotate(${rotation}deg)` }}
+            className="overflow-visible"
           >
+            {/* Background Path (240 degrees) */}
             <circle
-              cx="60"
-              cy="60"
+              cx={size / 2}
+              cy={size / 2}
               r={radius}
-              stroke="#f1f5f9"
-              strokeWidth="10"
-              fill="transparent"
+              fill="none"
+              stroke="#f0f0f0"
+              strokeWidth={strokeWidth}
+              strokeLinecap="round"
+              strokeDasharray={`${arcLength} ${circumference}`}
             />
+            {/* Progress Path (240 degrees) */}
             <circle
-              cx="60"
-              cy="60"
+              cx={size / 2}
+              cy={size / 2}
               r={radius}
-              stroke="#00b0f0"
-              strokeWidth="10"
-              fill="transparent"
-              strokeDasharray={circumference}
-              strokeDashoffset={strokeDashoffset}
+              fill="none"
+              stroke="#EE0434"
+              strokeWidth={strokeWidth}
               strokeLinecap="round"
+              strokeDasharray={`${arcLength} ${circumference}`}
+              strokeDashoffset={strokeDashoffset}
+              className="transition-all duration-1000 ease-out"
             />
           </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
+          {/* Central Text */}
+          <div className="absolute inset-0 flex flex-col items-center justify-center">
+            <span className="text-3xl md:text-4xl font-black text-[#EE0434]">
+              {formatNumber(dataUsage.usageData)}
+              <span className="text-[16px]">{dataUsage.dataUnit}</span>
             </span>
           </div>
 
-          {/* <div className="absolute bottom-2 left-0 text-[10px] font-bold text-slate-400">
-            0 MB
+          {/* Min Label (Approximate position for 240deg start) */}
+          <div
+            className="absolute text-xl font-bold font-black text-[#000000]"
+            style={{ bottom: "5%", left: "12%" }}
+          >
+            {formatNumber(0)}
+            <span className="text-[16px]">{dataUsage.dataUnit}</span>
+          </div>
+          {/* Max Label (Approximate position for 240deg end) */}
+          <div
+            className="absolute text-xl font-bold font-black text-[#000000]"
+            style={{ bottom: "5%", right: "8%" }}
+          >
+            {formatNumber(dataUsage.totalData)}
+            <span className="text-[16px]">{dataUsage.dataUnit}</span>
           </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 className="mt-1 text-slate-400 text-sm font-bold uppercase tracking-widest">
+          {dataUsage.status === 0
+            ? t("notActive")
+            : dataUsage.status === 1
+            ? t("active")
+            : dataUsage.status === 2
+            ? t("finished")
+            : dataUsage.status === 3
+            ? t("expired")
+            : t("unknown")}
         </p>
+        {/* <p className="mt-1 text-slate-400 text-sm font-bold uppercase tracking-widest">
+          {100 - Math.round((used / total) * 100)}% {t("remaining")}
+        </p> */}
       </div>
     );
   };
+
   const getStatusColor = (status: number) => {
     switch (status) {
       case 2:
@@ -239,7 +282,7 @@ const OrderDetailView = () => {
                   </svg>
                 </div>
                 <span className="text-xl md:text-2xl font-bold text-slate-800">
-                  {state.orderHistory?.id}
+                  {state.orderHistory?.orderCode}
                 </span>
               </div>
               {state.orderHistory?.status === 2 ? (
@@ -297,10 +340,13 @@ const OrderDetailView = () => {
             <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">
                 {t("totalTotal")}:{" "}
-                {formatNumber(state.orderHistory?.totalMoney)}{" "}
-                <span className="text-slate-500 font-normal">
+                {formatCurrency(
+                  state.orderHistory?.paymentMoney,
+                  state.orderHistory?.curency
+                )}{" "}
+                {/* <span className="text-slate-500 font-normal">
                   ({state.orderHistory?.curency})
-                </span>
+                </span> */}
               </span>
             </div>
 
@@ -394,7 +440,7 @@ const OrderDetailView = () => {
                   </span>
                 </div>
                 <span className="text-slate-800 font-semibold text-sm text-[16px]">
-                  ONEPAY
+                  QR Code
                 </span>
               </div>
             </div>
@@ -437,15 +483,15 @@ const OrderDetailView = () => {
                         <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">
+                        {/* <span className="px-2 py-0.5 rounded bg-slate-100 text-slate-600 text-xs font-bold">
                           {pkg.id}
-                        </span>
+                        </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}
+                        {t("validityPeriod")}: {pkg.dayDuration} {t("days")}
                       </p>
                     </div>
 
@@ -453,7 +499,7 @@ const OrderDetailView = () => {
                       <button
                         className="flex items-center text-[#EE0434] font-bold text-sm mb-2 hover:underline"
                         onClick={() => {
-                          dispatch(openQRModal(pkg.installationUrl));
+                          dispatch(openQRModal(pkg.qrcodeUrl));
                         }}
                       >
                         <svg
@@ -479,12 +525,12 @@ const OrderDetailView = () => {
                       </button>
                       <div className="text-right">
                         <span className="text-xl font-bold text-slate-800">
-                          {formatNumber(pkg.paymentMoney)}{" "}
+                          {formatCurrency(pkg.paymentMoney, pkg.curency)}
                         </span>
-                        <span className="text-slate-500 font-medium ml-1 font-bold">
+                        {/* <span className="text-slate-500 font-medium ml-1 font-bold">
                           {" "}
                           đ
-                        </span>
+                        </span> */}
                       </div>
                     </div>
                   </div>
@@ -501,11 +547,7 @@ const OrderDetailView = () => {
                     <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}
-                    />
+                    <CircularProgress dataUsage={pkg} />
                   </div>
                 ))}
               </div>

+ 10 - 8
EsimLao/esim-vite/src/pages/order-history/OrderHistoryView.tsx

@@ -5,15 +5,16 @@ import { useAppDispatch } from "../../hooks/useRedux";
 import { OrderHistory } from "../../services/product/type";
 import { useMutation } from "@tanstack/react-query";
 import React, { useState, useEffect } from "react";
-import { formatNumber } from "../../logic/loigicUtils";
+import { formatCurrency, formatNumber } from "../../logic/loigicUtils";
 import { useTranslation } from "react-i18next";
 
 const OrderHistoryView = () => {
   const [searchOrder, setSearchOrder] = useState("");
+  const today = new Date().toISOString().split("T")[0];
   // status is one of: -1, 1, 2
   const [status, setStatus] = useState("-1");
-  const [fromDate, setFromDate] = useState("");
-  const [toDate, setToDate] = useState("");
+  const [fromDate, setFromDate] = useState(today);
+  const [toDate, setToDate] = useState(today);
   const [orders, setOrders] = useState<OrderHistory[]>([]);
   const { t } = useTranslation();
 
@@ -28,6 +29,7 @@ const OrderHistoryView = () => {
     mutationFn: async () => {
       dispatch(startLoading({}));
       const res = await productApi.getOrderHistory({
+        searchOrder: searchOrder,
         fromDate,
         toDate,
         status,
@@ -107,7 +109,7 @@ const OrderHistoryView = () => {
           <div className="md:col-span-4 relative">
             <input
               type="text"
-              placeholder="Enter order code"
+              placeholder="Enter order id"
               className={inputClass}
               value={searchOrder}
               onChange={(e) => {
@@ -115,7 +117,7 @@ const OrderHistoryView = () => {
                 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">
+            <button className="absolute right-2 top-1/2 -translate-y-1/2 w-8 h-8 bg-[#EE0434] rounded-xl flex items-center justify-center text-white shadow-md hover:scale-105 transition-transform">
               <svg
                 className="w-4 h-4"
                 fill="none"
@@ -224,7 +226,7 @@ const OrderHistoryView = () => {
                   </span>
                   <div>
                     <p className="text-xl font-black text-slate-900 tracking-tight">
-                      {order.id}
+                      {order.orderCode}
                     </p>
                     <p className="text-sm font-medium text-slate-400 mt-1">
                       {order.createdDate}
@@ -249,10 +251,10 @@ const OrderHistoryView = () => {
                   </div>
                   <div className="text-right">
                     <p className="text-xl font-black text-slate-900">
-                      {formatNumber(order.paymentMoney)} ({order.curency})
+                      {formatCurrency(order.paymentMoney, order.curency)}
                     </p>
                     <p className="text-sm font-bold text-slate-400">
-                      {formatNumber(order.totalMoney)} ({order.curency})
+                      {formatCurrency(order.totalMoney, order.curency)}
                     </p>
                   </div>
                 </div>

+ 47 - 33
EsimLao/esim-vite/src/pages/product-detail/ProductDetailView.tsx

@@ -8,18 +8,24 @@ import { useAppDispatch, useAppSelector } from "../../hooks/useRedux";
 import { startLoading, stopLoading } from "../../features/loading/loadingSlice";
 import { productApi } from "../../apis/productApi";
 import { openPopup } from "../../features/popup/popupSlice";
-import { formatNumber } from "../../logic/loigicUtils";
+import {
+  formatCurrency,
+  formatNumber,
+  getWithExpiry,
+} from "../../logic/loigicUtils";
 import ProductInfoModal from "../../components/ProductInfoModal";
 import PackageOverview from "./components/PackageOverview";
 import ProductCard from "../../components/ProductCard";
 import { useTranslation } from "react-i18next";
+import { get } from "http";
 
 const ProductDetailView: React.FC = () => {
   const location = useLocation();
   const navigate = useNavigate();
   const dispatch = useAppDispatch();
   const { t } = useTranslation();
-  const area = location.state as Area;
+  // let area = location.state as Area;
+  const areas = getWithExpiry<Area[] | []>("areas");
   const { id } = useParams<{ id: string }>();
   const loading = useAppSelector((state) => state.loading);
   const [selectedDays, setSelectedDays] = useState<number>(null);
@@ -41,32 +47,26 @@ const ProductDetailView: React.FC = () => {
   const [simType, setSimType] = useState<"eSIM" | "Physical">("eSIM");
   const [quantity, setQuantity] = useState<number>(1);
   const [packages, setPackages] = useState<Package[]>([]);
+  const [area, setArea] = useState<Area | null>(null);
 
   const [selectedPackage, setSelectedPackage] = useState<Package | null>(null);
 
-  if (!id) {
-    return (
-      <div className="min-h-screen flex items-center justify-center">
-        <div className="text-center">
-          <p className="text-xl font-bold mb-4">Product details missing</p>
-          <Link to="/buy-sim" className="text-[#EE0434] font-bold underline">
-            Go to Shop
-          </Link>
-        </div>
-      </div>
-    );
-  }
-
   useEffect(() => {
-    getProductMutation.mutate();
-    getRelatedAreaMutation.mutate();
+    console.log("ProductDetailView loaded with id:", id);
+    if (areas && areas.length > 0) {
+      const areaTmp = areas.find((a) => a.id.toString() === id.toString());
+      setArea(areaTmp);
+      getProductMutation.mutate();
+      getRelatedAreaMutation.mutate();
+      console.log("Set area from cache:", areaTmp);
+    }
   }, [id]);
 
   const getProductMutation = useMutation({
     mutationFn: async () => {
       dispatch(startLoading({}));
       const res = await productApi.loadPackage({
-        areaId: id,
+        areaId: area?.id,
         dataType: "-1",
       });
       return res;
@@ -223,7 +223,7 @@ const ProductDetailView: React.FC = () => {
     setPrices({
       original: quantityToUse * selectedPackageTmp.displayPrice,
       final: quantityToUse * selectedPackageTmp.sellPrice,
-      discountPercent: "0",
+      discountPercent: selectedPackageTmp.discountPercent,
     });
     setSelectedPackage(selectedPackageTmp);
   };
@@ -277,6 +277,19 @@ const ProductDetailView: React.FC = () => {
     }
   };
 
+  if (!id || !area) {
+    return (
+      <div className="min-h-screen flex items-center justify-center">
+        <div className="text-center">
+          <p className="text-xl font-bold mb-4">{t("productDetailsMissing")}</p>
+          <Link to="/buy-sim" className="text-[#EE0434] font-bold underline">
+            {t("goToShop")}
+          </Link>
+        </div>
+      </div>
+    );
+  }
+
   return (
     <div className="bg-white min-h-screen pb-20">
       <div className="max-w-7xl mx-auto px-4 py-4 md:py-6">
@@ -301,7 +314,7 @@ const ProductDetailView: React.FC = () => {
             />
           </svg>
           <span className="text-slate-900 font-bold text-[18px]">
-            {area.areaName1}
+            {area?.areaName1}
           </span>
         </nav>
       </div>
@@ -310,24 +323,25 @@ const ProductDetailView: React.FC = () => {
         <div className="lg:col-span-5 space-y-6">
           <div className="relative aspect-[3/4] rounded-[24px] md:rounded-[32px] overflow-hidden shadow-2xl group">
             <img
-              src={area.imgUrl}
-              alt={area.areaName1}
+              src={area?.imgUrl}
+              alt={area?.areaName1}
               className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110"
             />
             <div className="absolute top-6 left-6 flex items-start space-x-4">
               <div className="w-12 h-12 md:w-16 md:h-16 rounded-full overflow-hidden border-2 border-white/50 shadow-lg">
                 <img
-                  src={`${area.iconUrl}`}
-                  alt={area.areaName1}
+                  src={`${area?.iconUrl}`}
+                  alt={area?.areaName1}
                   className="w-full h-full object-cover scale-150"
                 />
               </div>
               <div className="text-white">
                 <h1 className="text-2xl md:text-4xl font-black drop-shadow-md">
-                  SIM {area.areaName1}
+                  SIM {area?.areaName1}
                 </h1>
                 <p className="text-sm md:text-lg font-bold text-white/90">
-                  Verified: <span className="text-[#EE0434]">High Speed</span>
+                  {t("verified")}:{" "}
+                  <span className="text-[#EE0434]">{t("highSpeed")}</span>
                 </p>
               </div>
             </div>
@@ -340,7 +354,7 @@ const ProductDetailView: React.FC = () => {
         <div className="lg:col-span-7 space-y-8 md:space-y-10 pt-4">
           <div className="space-y-4">
             <h3 className="text-lg md:text-xl font-black text-slate-900 tracking-tight">
-              Number of days
+              {t("numberOfDays")}
             </h3>
             <div className="flex flex-wrap gap-3">
               {daysOptions.map((day) => (
@@ -393,7 +407,7 @@ const ProductDetailView: React.FC = () => {
           <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
             <div className="space-y-4">
               <h3 className="text-lg md:text-xl font-black text-slate-900 tracking-tight">
-                SIM Type
+                {t("simType")}
               </h3>
               <div className="flex p-1 bg-slate-50 rounded-2xl border border-slate-100">
                 <button
@@ -420,7 +434,7 @@ const ProductDetailView: React.FC = () => {
             </div>
             <div className="space-y-4">
               <h3 className="text-lg md:text-xl font-black text-slate-900 tracking-tight">
-                Quantity
+                {t("quantity")}
               </h3>
               <div className="flex items-center space-x-4 p-2 bg-slate-50 rounded-2xl border border-slate-100 h-[68px] md:h-[76px]">
                 <button
@@ -446,14 +460,14 @@ const ProductDetailView: React.FC = () => {
             <div className="flex justify-between items-end">
               <div className="flex items-center space-x-2">
                 <span className="text-[#EE0434] font-black text-lg">
-                  -{prices.discountPercent}%
+                  {prices.discountPercent}%
                 </span>
                 <span className="text-slate-300 font-bold text-xs line-through">
-                  {formatNumber(prices.original)} {area.curency}
+                  {formatCurrency(prices.original, area?.curency)}
                 </span>
               </div>
               <span className="text-[#EE0434] font-black text-2xl md:text-3xl">
-                {formatNumber(prices.final)} {area.curency}
+                {formatCurrency(prices.final, area?.curency)}
               </span>
             </div>
             <button
@@ -483,7 +497,7 @@ const ProductDetailView: React.FC = () => {
                   key={item.id}
                   p={item}
                   onClick={() => {
-                    navigate(`/product/${item.id}`, {
+                    navigate(`/product-detail/${item.id}`, {
                       state: {
                         ...item,
                       },

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

@@ -137,6 +137,7 @@ export interface OrderHistory {
   orderDetailId: number;
   paymentUrl: string | null;
   discountPercent: number;
+  orderCode: string;
   customerInfo: {
     id: number;
     surName: string | null;
@@ -160,6 +161,7 @@ export interface OrderDetail {
   packageTitle: string;
   paymentMoney: string;
   dayDuration: number;
+  curency: string;
 }
 
 export interface DataUsage {
@@ -168,4 +170,6 @@ export interface DataUsage {
   expiredTime: string;
   isUnlimited: number;
   status: number;
+  dataUnit: string;
+  usageData: number
 }