TransferWinMoney.cshtml 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  1. @model LotteryWebApp.Models.HomeIndex_ViewModel
  2. @{
  3. ViewData["Title"] = Lang.millions_transfer;
  4. Layout = "~/Areas/Millions/Views/Shared/_Layout.cshtml";
  5. }
  6. @using LotteryWebApp.Languages;
  7. @functions {
  8. public string FormatMoney(string amount) {
  9. if (string.IsNullOrEmpty(amount)) return "0";
  10. var clean = new string(amount.Where(c => char.IsDigit(c)).ToArray());
  11. if (long.TryParse(clean, out long val)) {
  12. return val.ToString("#,##0", new System.Globalization.CultureInfo("vi-VN")).Replace(",", ".");
  13. }
  14. return amount;
  15. }
  16. }
  17. <style>
  18. @@keyframes float {
  19. 0%, 100% { transform: translateY(0); }
  20. 50% { transform: translateY(-8px); }
  21. }
  22. .animate-float {
  23. animation: float 4s ease-in-out infinite;
  24. }
  25. .btn-premium {
  26. border: 2px solid #ffffff !important;
  27. box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15), inset 0 0 0 1px rgba(255, 255, 255, 0.1);
  28. transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  29. }
  30. .btn-premium:active {
  31. transform: scale(0.95);
  32. box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
  33. }
  34. .btn-premium-red {
  35. background: linear-gradient(135deg, #0062FF 0%, #004ecb 100%);
  36. }
  37. </style>
  38. <div class="main-container animate__animated animate__fadeIn bg-[#EAEAEA] min-h-screen font-bricolage overflow-x-hidden">
  39. <!-- Standardized Header (Matches Profile/Rules) -->
  40. <div class="sticky top-0 w-full bg-[#0062FF] h-[52px] flex items-center justify-between px-4 z-[100] shadow-sm">
  41. <!-- Back Button -->
  42. <button onclick="history.back()" class="w-10 h-10 flex items-center justify-center -ml-2 rounded-full active:bg-white/20 transition-colors z-10 text-white">
  43. <i class="fa-solid fa-arrow-left text-[20px]"></i>
  44. </button>
  45. <!-- Title centered -->
  46. <div class="absolute inset-0 flex items-center justify-center pointer-events-none">
  47. <span class="font-bold text-[24px] text-white tracking-wide uppercase">@Lang.millions_transfer</span>
  48. </div>
  49. </div>
  50. <div class="pt-[20px] px-4 pb-24 max-w-[414px] mx-auto">
  51. <!-- Account Card Section (Matching Figma: Frame 69) -->
  52. <div class="relative w-full bg-[#0062FF] rounded-[12px] overflow-hidden shadow-xl mb-6 pl-8 pr-5 py-2 flex flex-col justify-between" style="border: 4px solid transparent; border-image: linear-gradient(175deg, rgba(255,255,255,0.2) 0%, rgba(255,199,0,0.4) 100%) 1; border-radius: 12px; aspect-ratio: 383/92;">
  53. <!-- Decorative Elements (positions from Figma: card 383×92) -->
  54. <div class="absolute inset-0 pointer-events-none overflow-hidden rounded-[12px]">
  55. <!-- Golden Ribbon: Figma x:232.58 y:19.11 w:202.23 h:113.29 → right:0, top:20.8%, w:52.8% -->
  56. <img src="/Millions/img/transfer_card_bg_v2.png" alt="" class="absolute z-0" style="right: -10%; top: 20%; width: 53%; height: auto;" />
  57. <!-- Coin 2 (top-right): Figma x:336 y:11 w:27 h:26 → right:5.2%, top:12% -->
  58. <img src="/Millions/img/transfer/coin_2.png" alt="" class="absolute z-10 animate-float" style="right: 5%; top: 12%; width: 7%;" />
  59. <!-- Coin 1 (mid-right): Figma x:309 y:51 w:28 h:25 → right:12%, top:55% -->
  60. <img src="/Millions/img/transfer/coin_1.png" alt="" class="absolute z-10 animate-float" style="right: 12%; top: 55%; width: 7.4%; animation-delay: 0.5s;" />
  61. <!-- Coin 3 (small, left of ribbon): Figma x:272 y:27 w:18 h:12 → right:24.3%, top:29% -->
  62. <img src="/Millions/img/transfer/coin_3.png" alt="" class="absolute z-10 animate-pulse" style="right: 24%; top: 29%; width: 4.7%;" />
  63. <!-- Lights: Figma w:800 h:43, full-bleed glow overlays -->
  64. <img src="/Millions/img/transfer/light_1.png" alt="" class="absolute z-[1]" style="top: -23%; left: -55%; width: 210%; opacity: 0.7;" />
  65. <img src="/Millions/img/transfer/light_2.png" alt="" class="absolute z-[1]" style="bottom: -23%; left: -55%; width: 210%; opacity: 0.7;" />
  66. </div>
  67. <!-- Account Label -->
  68. <p class="relative z-10 text-white font-bold text-[16px] tracking-normal" style="font-family: 'Bricolage Grotesque', sans-serif;">@Lang.millions_account_name</p>
  69. <!-- Balance Row -->
  70. <div class="relative z-10 flex items-end gap-2">
  71. <span id="currentBetCoin" class="font-[800] text-[40px] leading-[1.2]" style="background: linear-gradient(179deg, rgba(255,255,255,1) 0%, rgba(240,201,63,1) 100%); -webkit-background-clip: text; -webkit-text-fill-color: transparent; font-family: 'Bricolage Grotesque', sans-serif;">@FormatMoney(Model?.userStatus?.bet_coin)</span>
  72. <span class="text-white font-[800] text-[20px] uppercase pb-[6px]" style="font-family: 'Bricolage Grotesque', sans-serif;">htg</span>
  73. </div>
  74. </div>
  75. <!-- Selection Area (Horizontal Cards) -->
  76. <div class="mb-6">
  77. <h2 class="text-[#534A4A] font-[900] text-[18px] mb-3 pl-1">@Lang.millions_choose_account</h2>
  78. <div class="flex gap-3">
  79. <!-- Basic Account Option -->
  80. <div id="selectBasic" onclick="selectChannel(1)" class="channel-card flex-1 bg-white h-[76px] rounded-[18px] border-2 border-[#0062FF] shadow-md flex items-center px-3 gap-2 cursor-pointer relative">
  81. <!-- Selected Icon -->
  82. <div class="check-icon absolute -top-2 -right-1 bg-white rounded-full">
  83. <i class="fa-solid fa-circle-check text-[#00AF1B] text-[20px]"></i>
  84. </div>
  85. <span class="text-[#0062FF] font-[900] text-[15px] leading-tight flex-1">@Lang.Basic<br />@Lang.Account</span>
  86. <img src="/Millions/img/transfer_basic_icon.png" alt="Basic" class="w-12 h-10 object-contain" />
  87. </div>
  88. <!-- Natcash Card -->
  89. <div id="selectNatcash" onclick="selectChannel(2)" class="channel-card flex-1 bg-white h-[76px] rounded-[18px] border border-gray-100 shadow-sm flex items-center px-3 gap-2 cursor-pointer relative overflow-visible">
  90. <!-- Selected Icon (Hidden by default) -->
  91. <div class="check-icon absolute -top-2 -right-1 bg-white rounded-full hidden">
  92. <i class="fa-solid fa-circle-check text-[#00AF1B] text-[20px]"></i>
  93. </div>
  94. <div class="flex-1 overflow-hidden h-full flex items-center">
  95. <img src="/Millions/img/transfer_other_card.png" alt="Natcash" class="w-full h-full object-contain" />
  96. </div>
  97. </div>
  98. </div>
  99. </div>
  100. <!-- Form Fields Area -->
  101. <div class="flex flex-col gap-2">
  102. <!-- Sender Phone -->
  103. <div class="flex flex-col gap-2">
  104. <label class="text-[#534A4A] font-[900] text-[16px] px-1 capitalize">@Lang.millions_sender_phone</label>
  105. <div class="relative w-full group">
  106. <input type="text" readonly value="@(Model?.userStatus?.msisdn ?? "0")" class="w-full bg-gray-100 border border-gray-300 text-[#534A4A] text-[17px] font-[800] rounded-[18px] py-[15px] px-5 outline-none transition-all cursor-not-allowed" />
  107. <div class="absolute right-4 top-1/2 -translate-y-1/2 text-gray-400">
  108. <i class="fa-solid fa-lock text-[20px] opacity-40"></i>
  109. </div>
  110. </div>
  111. </div>
  112. <!-- Receiver Phone (Mandatory same as sender) -->
  113. <div class="flex flex-col gap-2">
  114. <label class="text-[#534A4A] font-[900] text-[16px] px-1 capitalize">@Lang.millions_receiver_phone</label>
  115. <div class="relative w-full">
  116. <input id="receiverPhone" type="tel" readonly value="@(Model?.userStatus?.msisdn ?? "0")" class="w-full bg-gray-100 border border-gray-300 text-[#534A4A] text-[17px] font-[800] rounded-[18px] py-[15px] px-5 outline-none transition-all cursor-not-allowed" />
  117. <div class="absolute right-4 top-1/2 -translate-y-1/2 text-gray-400">
  118. <i class="fa-solid fa-lock text-[20px] opacity-40"></i>
  119. </div>
  120. </div>
  121. </div>
  122. <!-- Amount -->
  123. <div class="flex flex-col gap-2">
  124. <label class="text-[#534A4A] font-[900] text-[16px] px-1 capitalize">@Lang.millions_amount_htg</label>
  125. <div class="relative w-full">
  126. <input id="transferAmount" type="number" value="5000" placeholder="5,000" class="w-full bg-white border border-gray-300 focus:border-[#0062FF] focus:ring-2 focus:ring-[#0062FF]/10 text-[#0062FF] text-[17px] font-[800] rounded-[18px] py-[15px] px-5 outline-none transition-all placeholder:text-gray-300" />
  127. <span class="absolute right-5 top-1/2 -translate-y-1/2 text-gray-400 font-black">HTG</span>
  128. </div>
  129. <!-- Shortcut Buttons -->
  130. <div class="flex gap-2 mt-1">
  131. <button onclick="setAmount(10, this)" class="amt-btn flex-1 py-1.5 border border-gray-300 rounded-lg text-[14px] font-black transition-all bg-white text-[#534A4A] flex items-center justify-center gap-1 shadow-sm">
  132. 10
  133. <i class="fa-solid fa-circle-check text-[#00AF1B] text-[14px] hidden"></i>
  134. </button>
  135. <button onclick="setAmount(1000, this)" class="amt-btn flex-1 py-1.5 border border-gray-300 rounded-lg text-[14px] font-black transition-all bg-white text-[#534A4A] flex items-center justify-center gap-1 shadow-sm">
  136. 1.000
  137. <i class="fa-solid fa-circle-check text-[#00AF1B] text-[14px] hidden"></i>
  138. </button>
  139. <button id="defaultAmt" onclick="setAmount(5000, this)" class="amt-btn flex-1 py-1.5 border-2 border-white bg-[#0062FF] rounded-lg text-[14px] font-black text-white transition-all flex items-center justify-center gap-1 shadow-md">
  140. 5.000
  141. <i class="fa-solid fa-circle-check text-white text-[14px]"></i>
  142. </button>
  143. </div>
  144. <!-- Fee Section (Newly placed on a new line) -->
  145. <div class="mt-6 flex flex-col gap-1 px-1">
  146. <span class="text-gray-500 font-bold text-[14px]">@Lang.millions_fee</span>
  147. <span class="text-black font-black text-[28px] leading-none mt-1">@Lang.millions_free</span>
  148. </div>
  149. </div>
  150. </div>
  151. <!-- Main Action Button -->
  152. <div class="mt-6">
  153. <button id="btnContinue" class="btn-premium btn-premium-red w-full text-white font-[900] text-[18px] py-[18px] rounded-[22px] active:scale-95 transition-all uppercase tracking-wide">
  154. @Lang.millions_continue
  155. </button>
  156. </div>
  157. </div>
  158. <!-- Success Modal Overlay -->
  159. <div id="successModal" class="fixed inset-0 z-[110] flex items-center justify-center hidden px-6" style="background: rgba(0,0,0,0.7);">
  160. <div class="w-full max-w-[370px] bg-white rounded-[32px] overflow-hidden flex flex-col items-center animate__animated animate__zoomIn animate__faster shadow-2xl relative">
  161. <!-- Content Container -->
  162. <div class="relative z-10 w-full flex flex-col items-center pt-10 pb-8 px-6">
  163. <!-- Success Icon Group -->
  164. <div class="relative w-full flex justify-center mb-0 mt-2 z-10 px-4">
  165. <img src="/Millions/img/modal/otp_success.png" alt="Success" class="w-full max-w-[200px] h-auto object-contain">
  166. </div>
  167. <h2 class="text-[#0A9800] font-[800] text-[32px] mb-1 tracking-tight">@Lang.success</h2>
  168. <p class="text-[#000000] font-[700] text-[20px] text-center px-4 mb-6 leading-tight">
  169. @Lang.millions_payment_successfully
  170. </p>
  171. <div class="w-full border-t border-dashed border-gray-200 pt-5 flex flex-col gap-3 mb-5">
  172. <!-- Info Rows -->
  173. <div class="flex justify-between items-center text-[15px]">
  174. <span class="text-[#534A4A] font-bold">@Lang.amount_transfered</span>
  175. <span id="successAmount" class="text-black font-black text-right">5.000 HTG</span>
  176. </div>
  177. <div class="flex justify-between items-center text-[15px]">
  178. <span class="text-[#534A4A] font-bold">@Lang.sender</span>
  179. <span id="successSender" class="text-black font-black text-right">50940236545</span>
  180. </div>
  181. <div class="flex justify-between items-center text-[15px]">
  182. <span class="text-[#534A4A] font-bold">@Lang.receiver</span>
  183. <span id="successReceiver" class="text-black font-black text-right">50940236545</span>
  184. </div>
  185. <div class="flex justify-between items-center text-[15px]">
  186. <span class="text-[#534A4A] font-bold">@Lang.fee</span>
  187. <span class="text-black font-black text-right">0 HTG</span>
  188. </div>
  189. </div>
  190. <div class="w-full border-t border-dashed border-gray-200 pt-4 flex justify-between items-center mb-8">
  191. <span class="text-[#534A4A] font-bold text-[15px]">@Lang.time</span>
  192. <span id="successTime" class="text-[#534A4A] font-medium text-[14px] text-right">08/01/2022 - 10:00:20</span>
  193. </div>
  194. <button onclick="window.location.href='/Millions/Home/GameHome'" class="btn-premium btn-premium-red w-full text-white font-[800] text-[20px] py-[14px] rounded-[18px] active:scale-95 transition-all">
  195. @Lang.back_to_homepage
  196. </button>
  197. </div>
  198. </div>
  199. </div>
  200. <!-- Failure Modal Overlay -->
  201. <div id="failureModal" class="fixed inset-0 z-[110] flex items-center justify-center hidden px-6" style="background: rgba(0,0,0,0.5);">
  202. <div class="w-full max-w-[343px] min-h-[420px] bg-white rounded-[20px] overflow-hidden flex flex-col items-center p-8 animate__animated animate__zoomIn animate__faster shadow-2xl">
  203. <div class="w-full flex flex-col items-center mb-6 mt-4">
  204. <div class="relative w-full flex items-center justify-center">
  205. <img src="/Millions/img/modal/fail_icon.png" class="w-[160px] h-auto object-contain" />
  206. </div>
  207. </div>
  208. <div class="px-2 text-center mb-12 mt-2">
  209. <p id="failErrorMessage" class="text-black font-[700] text-[20px] leading-tight">
  210. @Lang.millions_enter_valid_amount
  211. </p>
  212. </div>
  213. <div class="w-full mt-auto">
  214. <button onclick="closeFailureModal()" class="btn-premium btn-premium-red w-full text-white font-[800] text-[20px] py-[10px] rounded-[14px] active:scale-95 transition-all">
  215. @Lang.try_again
  216. </button>
  217. </div>
  218. </div>
  219. </div>
  220. <!-- OTP Modal Overlay -->
  221. <div id="otpModal" class="fixed inset-0 z-[120] flex items-center justify-center hidden px-6" style="background: rgba(0,0,0,0.7);">
  222. <!-- Glitter/Sparkles Overlay -->
  223. <img src="/Millions/img/modal/otp_glitter.png" class="absolute pointer-events-none opacity-60 w-full max-w-[500px]" />
  224. <div class="w-full max-w-[360px] bg-white rounded-[32px] overflow-visible flex flex-col items-center animate__animated animate__zoomIn animate__faster relative border-4 border-[#FFCD1D]/10" style="box-shadow: 0 0 25px rgba(229, 107, 35, 0.6);">
  225. <!-- Gold Coins on Top -->
  226. <img src="/Millions/img/modal/otp_coins.png" class="absolute -top-12 -right-2 w-32 z-30 pointer-events-none drop-shadow-lg" />
  227. <!-- Red Header -->
  228. <div class="w-full bg-[#0062FF] pt-5 pb-4 px-6 rounded-t-[32px] flex justify-center border-b-4 border-white/20">
  229. <h3 class="text-white font-[900] text-[22px] tracking-tight text-center">@Lang.millions_confirm_transaction</h3>
  230. </div>
  231. <div class="relative z-10 w-full flex flex-col items-center p-6 py-6 pb-8">
  232. <!-- Shield Icon -->
  233. <div class="flex flex-col items-center gap-1 mb-2">
  234. <img src="/Millions/img/modal/otp_shield.png" class="w-[90px] h-auto object-contain drop-shadow-md" />
  235. <span class="text-[#534A4A] font-bold text-[14px]">@Lang.millions_convert</span>
  236. </div>
  237. <!-- Conversion Display Area -->
  238. <div class="flex items-center justify-center gap-2 mb-4">
  239. <span id="displayAmountCoins" class="text-[#0062FF] font-[900] text-[24px]">100</span>
  240. <span class="text-black font-[900] text-[24px]">@Lang.millions_coins</span>
  241. <i class="fa-solid fa-arrow-right text-gray-800 text-[20px] mx-1"></i>
  242. <span id="displayAmountHTG" class="text-[#0062FF] font-[900] text-[24px]">100</span>
  243. <span class="text-black font-[900] text-[24px]">@Lang.millions_htg</span>
  244. </div>
  245. <p class="text-[#534A4A] font-bold text-[14px] text-center px-4 mb-6 leading-tight">@Lang.millions_enter_otp_proceed</p>
  246. <!-- OTP Inputs Area -->
  247. <div class="flex gap-2 mb-4" id="otpInputs">
  248. <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" placeholder="-"
  249. class="otp-digit w-[42px] h-[48px] bg-[#757575] text-white rounded-[10px] text-center text-[26px] font-[900] outline-none transition-all placeholder:text-white/60 shadow-inner" />
  250. <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" placeholder="-"
  251. class="otp-digit w-[42px] h-[48px] bg-[#757575] text-white rounded-[10px] text-center text-[26px] font-[900] outline-none transition-all placeholder:text-white/60 shadow-inner" />
  252. <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" placeholder="-"
  253. class="otp-digit w-[42px] h-[48px] bg-[#757575] text-white rounded-[10px] text-center text-[26px] font-[900] outline-none transition-all placeholder:text-white/60 shadow-inner" />
  254. <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" placeholder="-"
  255. class="otp-digit w-[42px] h-[48px] bg-[#757575] text-white rounded-[10px] text-center text-[26px] font-[900] outline-none transition-all placeholder:text-white/60 shadow-inner" />
  256. <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" placeholder="-"
  257. class="otp-digit w-[42px] h-[48px] bg-[#757575] text-white rounded-[10px] text-center text-[26px] font-[900] outline-none transition-all placeholder:text-white/60 shadow-inner" />
  258. <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" placeholder="-"
  259. class="otp-digit w-[42px] h-[48px] bg-[#757575] text-white rounded-[10px] text-center text-[26px] font-[900] outline-none transition-all placeholder:text-white/60 shadow-inner" />
  260. </div>
  261. <!-- OTP Error Message -->
  262. <p id="otpError" class="text-[#0062FF] font-bold text-[14px] text-center mb-4 hidden animate__animated animate__shakeX"></p>
  263. <!-- Timer & Resend Display -->
  264. <div class="flex flex-col items-center justify-center mb-6 gap-2">
  265. <span id="otpTimer" class="text-[#0062FF] font-[900] text-[18px]">60s</span>
  266. <button id="resendOtpBtn" onclick="sendOtpRequest()" class="hidden text-[15px] font-black text-[#0A9800] underline decoration-solid underline-offset-[4px] hover:text-[#087a00] transition-all font-bricolage">@Lang.millions_request_new_otp</button>
  267. </div>
  268. <!-- Action Buttons -->
  269. <div class="w-full flex gap-4">
  270. <button onclick="closeOtpModal()" class="btn-premium flex-1 bg-[#757575] text-white font-[900] py-2.5 rounded-[16px] active:scale-95 transition-all text-[13px]">@Lang.cancel</button>
  271. <button id="btnConfirmTransfer" disabled class="btn-premium flex-1 bg-[#0A9800] text-white font-[900] py-2.5 rounded-[16px] active:scale-95 transition-all text-[13px] disabled:opacity-50 disabled:grayscale disabled:cursor-not-allowed">@Lang.confirm</button>
  272. </div>
  273. </div>
  274. </div>
  275. </div>
  276. <!-- Bottom Navbar shared component -->
  277. <partial name="_BottomNavbar" />
  278. </div>
  279. @section Scripts {
  280. <script>
  281. function setAmount(val, el) {
  282. $("#transferAmount").val(val);
  283. // Update active state visuals
  284. $(".amt-btn").removeClass("border-2 border-white bg-[#0062FF] text-white shadow-md").addClass("border-gray-300 bg-white text-[#534A4A] shadow-sm");
  285. $(".amt-btn i").addClass("hidden").removeClass("text-white").addClass("text-[#00AF1B]"); // reset icons
  286. $(el).removeClass("border-gray-300 bg-white text-[#534A4A] shadow-sm").addClass("border-2 border-white bg-[#0062FF] text-white shadow-md");
  287. $(el).find("i").removeClass("hidden").removeClass("text-[#00AF1B]").addClass("text-white"); // show current check icon in white
  288. }
  289. // Logic removed as fields are read-only
  290. let timerInterval;
  291. let isProcessing = false;
  292. let selectedChannel = 1; // 1 = Basic, 2 = Natcash
  293. function selectChannel(id) {
  294. selectedChannel = id;
  295. // Reset both
  296. $(".channel-card").removeClass("border-2 border-[#0062FF] shadow-md").addClass("border border-gray-100 shadow-sm");
  297. $(".channel-card .check-icon").addClass("hidden");
  298. // Activate selected
  299. if (id === 1) {
  300. $("#selectBasic").removeClass("border-gray-100 shadow-sm").addClass("border-2 border-[#0062FF] shadow-md");
  301. $("#selectBasic .check-icon").removeClass("hidden");
  302. } else {
  303. $("#selectNatcash").removeClass("border-gray-100 shadow-sm").addClass("border-2 border-[#0062FF] shadow-md");
  304. $("#selectNatcash .check-icon").removeClass("hidden");
  305. }
  306. }
  307. $("#btnContinue").on("click", function() {
  308. if (isProcessing) return; // Prevent double-click
  309. const rawAmount = $("#transferAmount").val();
  310. const amount = parseFloat(unformatMoneyV2(rawAmount)) || 0;
  311. const phone = $("#receiverPhone").val();
  312. const winMoney = parseFloat(unformatMoneyV2("@(Model?.userStatus?.bet_coin ?? "0")")) || 0;
  313. if(amount <= 0) {
  314. showFailureModal("@Html.Raw(Lang.millions_enter_valid_amount)");
  315. return;
  316. }
  317. if(amount > winMoney) {
  318. showFailureModal("@Html.Raw(Lang.millions_insufficient_balance)");
  319. return;
  320. }
  321. // Lock button + show loading
  322. isProcessing = true;
  323. const $btn = $(this);
  324. const originalText = $btn.html();
  325. $btn.prop('disabled', true).html('<i class="fa-solid fa-spinner fa-spin mr-2"></i> ...');
  326. // Call sendotp API
  327. $.ajax({
  328. url: subDomain + "/Millions/Home/SendOTP",
  329. type: "POST",
  330. data: { phone: phone, amount: unformatMoneyV2(rawAmount), channelPayment: selectedChannel },
  331. success: function(res) {
  332. $btn.prop('disabled', false).html(originalText);
  333. isProcessing = false;
  334. if(res.responseCode == "0" || res.responseCode == 0) {
  335. openOtpModal();
  336. } else {
  337. showFailureModal(res.message || "Failed to send OTP", res.responseCode);
  338. }
  339. },
  340. error: function() {
  341. $btn.prop('disabled', false).html(originalText);
  342. isProcessing = false;
  343. showFailureModal("Network error while sending OTP");
  344. }
  345. });
  346. });
  347. function openOtpModal() {
  348. // Clear previous OTP values
  349. $(".otp-digit").val("");
  350. $("#btnConfirmTransfer").prop('disabled', true);
  351. // Set display amounts
  352. const amt = $("#transferAmount").val();
  353. const formattedAmt = formatMoneyV2(amt);
  354. $("#displayAmountCoins").text(formattedAmt);
  355. $("#displayAmountHTG").text(formattedAmt);
  356. $("#otpModal").removeClass("hidden").addClass("flex");
  357. resetTimer();
  358. $(".otp-digit").first().focus();
  359. }
  360. function closeOtpModal() {
  361. $("#otpModal").addClass("hidden").removeClass("flex");
  362. clearInterval(timerInterval);
  363. }
  364. // OTP input: enforce single digit, auto-advance
  365. function handleOtpInput(el) {
  366. // Allow only single digit
  367. el.value = el.value.replace(/[^0-9]/g, '').slice(0, 1);
  368. if (el.value.length === 1) {
  369. const next = $(el).next('.otp-digit');
  370. if (next.length) {
  371. next.focus();
  372. }
  373. }
  374. // Check if all 6 digits filled
  375. var filled = 0;
  376. $(".otp-digit").each(function() { if ($(this).val().length >= 1) filled++; });
  377. $("#btnConfirmTransfer").prop('disabled', filled < 6);
  378. // Clear error when typing and reset color to red
  379. $("#otpError").addClass("hidden").text("").removeClass("text-[#0A9800]").addClass("text-[#0062FF] animate__shakeX");
  380. }
  381. // OTP backspace: move to previous input
  382. function handleOtpKeydown(e, el) {
  383. if (e.key === 'Backspace') {
  384. if (el.value.length === 0) {
  385. const prev = $(el).prev('.otp-digit');
  386. if (prev.length) {
  387. prev.focus();
  388. }
  389. }
  390. }
  391. }
  392. function resetTimer() {
  393. let seconds = 60;
  394. clearInterval(timerInterval);
  395. $("#otpTimer").removeClass("hidden").text("60s");
  396. $("#resendOtpBtn").addClass("hidden");
  397. // Keep confirm disabled until OTP is filled — don't re-enable here
  398. timerInterval = setInterval(() => {
  399. seconds--;
  400. $("#otpTimer").text(seconds + "s");
  401. if (seconds <= 0) {
  402. clearInterval(timerInterval);
  403. $("#otpTimer").addClass("hidden");
  404. $("#resendOtpBtn").removeClass("hidden");
  405. // Disable confirm when timer expired
  406. $("#btnConfirmTransfer").prop('disabled', true).addClass('opacity-50');
  407. }
  408. }, 1000);
  409. }
  410. function sendOtpRequest() {
  411. const $btn = $("#resendOtpBtn");
  412. const originalText = $btn.text();
  413. $btn.prop('disabled', true).text("...");
  414. $.ajax({
  415. url: subDomain + "/Millions/Home/SendOTP",
  416. type: 'POST',
  417. success: function(data) {
  418. $btn.prop('disabled', false).text(originalText);
  419. if (data.responseCode === "0" || data.responseCode === 0) {
  420. resetTimer();
  421. $("#otpError").removeClass("text-[#0062FF] animate__shakeX").addClass("text-[#0A9800]").text("@Lang.millions_otp_sent_successfully").removeClass("hidden");
  422. $(".otp-digit").val("").first().focus();
  423. } else {
  424. if (data.responseCode === "-2" || (data.responseMessage && data.responseMessage.includes("System is upgrading"))) {
  425. closeOtpModal();
  426. showFailureModal(data.responseMessage || "System is upgrading", data.responseCode);
  427. } else {
  428. $("#otpError").removeClass("text-[#0A9800]").addClass("text-[#0062FF] animate__shakeX").text(data.responseMessage || "Failed to resend OTP").removeClass("hidden");
  429. }
  430. }
  431. },
  432. error: function() {
  433. $btn.prop('disabled', false).text(originalText);
  434. $("#otpError").text("Network error occurred.").removeClass("hidden");
  435. }
  436. });
  437. }
  438. $("#btnConfirmTransfer").on("click", function() {
  439. if (isProcessing) return; // Prevent double-click
  440. let otp = "";
  441. $(".otp-digit").each(function() { otp += $(this).val(); });
  442. if(otp.length < 6) {
  443. showFailureModal("Please enter 6-digit OTP");
  444. return;
  445. }
  446. // Lock button + show loading
  447. isProcessing = true;
  448. const $btn = $(this);
  449. const originalText = $btn.html();
  450. $btn.prop('disabled', true).html('<i class="fa-solid fa-spinner fa-spin mr-2"></i> ...');
  451. // Call the unified backend action that verifies OTP and then transfers
  452. $.ajax({
  453. url: subDomain + "/Millions/Home/ConfirmTransfer",
  454. type: "POST",
  455. data: {
  456. otp: otp,
  457. phone: $("#receiverPhone").val(),
  458. amount: unformatMoneyV2($("#transferAmount").val()),
  459. channelPayment: selectedChannel
  460. },
  461. success: function(res) {
  462. $btn.prop('disabled', false).html(originalText);
  463. isProcessing = false;
  464. if(res.responseCode == "0" || res.responseCode == 0) {
  465. closeOtpModal();
  466. // Update balance in UI immediately
  467. if(res.userStatus && res.userStatus.bet_coin) {
  468. $("#currentBetCoin").text(formatMoneyV2(res.userStatus.bet_coin));
  469. }
  470. // Populate Success Modal Fields
  471. const transferAmt = $("#transferAmount").val();
  472. $("#successAmount").text(formatMoneyV2(transferAmt) + " HTG");
  473. $("#successSender").text("@(Model?.userStatus?.msisdn ?? "0")");
  474. $("#successReceiver").text($("#receiverPhone").val());
  475. const now = new Date();
  476. const pad = (n) => n.toString().padStart(2, '0');
  477. const formattedTime = `${pad(now.getDate())}/${pad(now.getMonth() + 1)}/${now.getFullYear()} - ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
  478. $("#successTime").text(formattedTime);
  479. $("#successModal").removeClass("hidden").addClass("flex");
  480. } else {
  481. if (res.responseCode === "-2" || (res.message && res.message.includes("System is upgrading"))) {
  482. closeOtpModal();
  483. showFailureModal(res.message || "System is upgrading", res.responseCode);
  484. } else {
  485. $("#otpError").text(res.message || "Invalid OTP").removeClass("hidden");
  486. $(".otp-digit").val("");
  487. $(".otp-digit").first().focus();
  488. }
  489. }
  490. },
  491. error: function() {
  492. $btn.prop('disabled', false).html(originalText);
  493. isProcessing = false;
  494. showFailureModal("Network error while confirming transfer");
  495. }
  496. });
  497. });
  498. var systemUpgrading = false;
  499. function showFailureModal(message, code) {
  500. $("#failErrorMessage").html(message);
  501. const $btn = $("#failureModal button");
  502. if (code === "-2" || (message && message.includes("System is upgrading"))) {
  503. systemUpgrading = true;
  504. $btn.text("@Lang.login");
  505. } else {
  506. systemUpgrading = false;
  507. $btn.text("@Lang.try_again");
  508. }
  509. $("#failureModal").removeClass("hidden").addClass("flex");
  510. }
  511. function closeFailureModal() {
  512. $("#failureModal").addClass("hidden").removeClass("flex");
  513. if (systemUpgrading) {
  514. window.location.href = subDomain + "/Account/Login";
  515. }
  516. }
  517. </script>
  518. }