TransferWinMoney.cshtml 36 KB

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