TransferWinMoney.cshtml 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621
  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. <p id="successTransferNote" class="transfer-success-sub" style="color: #0A9800; font-size: 14px; margin-top: -6px; margin-bottom: 12px; font-weight: 800; text-align: center;"></p>
  157. <div class="transfer-success-panel">
  158. <div class="transfer-success-row">
  159. <div class="transfer-success-row-icon"><i class="fa-solid fa-credit-card" aria-hidden="true"></i></div>
  160. <span class="transfer-success-row-label">@Lang.amount_transfered</span>
  161. <span id="successAmount" class="transfer-success-row-value transfer-success-row-value--accent">5.000 HTG</span>
  162. </div>
  163. <div class="transfer-success-row">
  164. <div class="transfer-success-row-icon"><i class="fa-solid fa-user" aria-hidden="true"></i></div>
  165. <span class="transfer-success-row-label">@Lang.sender</span>
  166. <span id="successSender" class="transfer-success-row-value">50940236545</span>
  167. </div>
  168. <div class="transfer-success-row">
  169. <div class="transfer-success-row-icon"><i class="fa-solid fa-user" aria-hidden="true"></i></div>
  170. <span class="transfer-success-row-label">@Lang.receiver</span>
  171. <span id="successReceiver" class="transfer-success-row-value">50940236545</span>
  172. </div>
  173. <div class="transfer-success-row">
  174. <div class="transfer-success-row-icon"><i class="fa-solid fa-tag" aria-hidden="true"></i></div>
  175. <span class="transfer-success-row-label">@Lang.fee</span>
  176. <span class="transfer-success-row-value">0 HTG</span>
  177. </div>
  178. </div>
  179. <div class="transfer-success-time">
  180. <div class="transfer-success-row-icon"><i class="fa-regular fa-clock" aria-hidden="true"></i></div>
  181. <span class="transfer-success-time-label">@Lang.time</span>
  182. <span id="successTime" class="transfer-success-time-value">08/01/2022 - 10:00:20</span>
  183. </div>
  184. <div class="transfer-success-illustration" aria-hidden="true">
  185. <svg viewBox="0 0 140 88" fill="none" xmlns="http://www.w3.org/2000/svg">
  186. <path d="M6 44h18M6 52h12" stroke="#0062FF" stroke-width="2.2" stroke-linecap="round"/>
  187. <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"/>
  188. <rect x="38" y="40" width="40" height="24" rx="4" transform="rotate(-10 58 52)" stroke="#0062FF" stroke-width="2" fill="#fff"/>
  189. <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>
  190. <circle cx="108" cy="60" r="17" fill="#0062FF"/>
  191. <path d="M102 60l5 5 11-12" stroke="#fff" stroke-width="2.8" fill="none" stroke-linecap="round" stroke-linejoin="round"/>
  192. </svg>
  193. </div>
  194. </div>
  195. <div class="transfer-success-actions">
  196. <button type="button" class="transfer-success-btn-continue" onclick="window.location.href = (typeof subDomain !== 'undefined' ? subDomain : '') + '@Url.Action("TransferWinMoney", "Home", new { area = "Millions" })';">
  197. @Lang.millions_continue
  198. </button>
  199. <button type="button" class="transfer-success-btn-close" onclick="window.location.href = (typeof subDomain !== 'undefined' ? subDomain : '') + '@Url.Action("GameHome", "Home", new { area = "Millions" })';">
  200. @Lang.close
  201. </button>
  202. </div>
  203. </div>
  204. </div>
  205. <!-- Failure Modal Overlay -->
  206. <div id="failureModal" class="transfer-success-overlay fixed inset-0 z-[110] flex items-center justify-center hidden px-4">
  207. <div class="transfer-fail-card animate__animated animate__zoomIn animate__faster relative w-full max-w-[370px]">
  208. <div class="transfer-fail-header">
  209. <div class="transfer-fail-header-deco" aria-hidden="true"></div>
  210. <div class="transfer-fail-icon-wrap">
  211. <div class="transfer-fail-icon-circle">
  212. <i class="fa-solid fa-xmark" aria-hidden="true"></i>
  213. </div>
  214. <div class="transfer-fail-icon-warn">
  215. <i class="fa-solid fa-triangle-exclamation" aria-hidden="true"></i>
  216. </div>
  217. </div>
  218. </div>
  219. <div class="transfer-fail-body">
  220. <h2 class="transfer-fail-title">@Lang.fail_exclamation</h2>
  221. <p id="failErrorMessage" class="transfer-fail-msg">@Lang.millions_enter_valid_amount</p>
  222. <div class="transfer-fail-divider" aria-hidden="true"><span></span></div>
  223. <div class="transfer-fail-illustration" aria-hidden="true">
  224. <svg viewBox="0 0 140 88" fill="none" xmlns="http://www.w3.org/2000/svg">
  225. <path d="M18 52h22M18 60h14" stroke="#0062FF" stroke-width="2" stroke-linecap="round"/>
  226. <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"/>
  227. <path d="M78 38c8-6 18-8 26-4" stroke="#0062FF" stroke-width="2" stroke-linecap="round"/>
  228. <circle cx="96" cy="28" r="14" stroke="#0062FF" stroke-width="2" fill="#fff"/>
  229. <path d="M92 28h8M96 24v8" stroke="#0062FF" stroke-width="1.8" stroke-linecap="round"/>
  230. <path d="M88 22l4-6 3 5M104 20l6-2-2 6" stroke="#0062FF" stroke-width="1.5" stroke-linecap="round" opacity="0.7"/>
  231. </svg>
  232. </div>
  233. </div>
  234. <div id="failureModalStandardActions" class="transfer-success-actions">
  235. <button type="button" class="transfer-success-btn-continue" onclick="window.location.href = (typeof subDomain !== 'undefined' ? subDomain : '') + '@Url.Action("TransferWinMoney", "Home", new { area = "Millions" })';">
  236. @Lang.millions_continue
  237. </button>
  238. <button type="button" class="transfer-success-btn-close" onclick="window.location.href = (typeof subDomain !== 'undefined' ? subDomain : '') + '@Url.Action("GameHome", "Home", new { area = "Millions" })';">
  239. @Lang.close
  240. </button>
  241. </div>
  242. <div id="failureModalUpgradeActions" class="transfer-success-actions is-hidden">
  243. <button type="button" class="transfer-success-btn-close" onclick="window.location.href = (typeof subDomain !== 'undefined' ? subDomain : '') + '/Account/Login';">
  244. @Lang.login
  245. </button>
  246. </div>
  247. </div>
  248. </div>
  249. <!-- OTP Modal Overlay -->
  250. <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);">
  251. <!-- Glitter/Sparkles Overlay -->
  252. <img src="/Millions/img/modal/otp_glitter.png" class="absolute pointer-events-none opacity-60 w-full max-w-[500px]" />
  253. <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);">
  254. <!-- Gold Coins on Top -->
  255. <img src="/Millions/img/modal/otp_coins.png" class="absolute -top-12 -right-2 w-32 z-30 pointer-events-none drop-shadow-lg" />
  256. <!-- Red Header -->
  257. <div class="w-full bg-[#0062FF] pt-5 pb-4 px-6 rounded-t-[32px] flex justify-center border-b-4 border-white/20">
  258. <h3 class="text-white font-[900] text-[22px] tracking-tight text-center">@Lang.millions_confirm_transaction</h3>
  259. </div>
  260. <div class="relative z-10 w-full flex flex-col items-center p-6 py-6 pb-8">
  261. <!-- Shield Icon -->
  262. <div class="flex flex-col items-center gap-1 mb-2">
  263. <img src="/Millions/img/modal/transfer_coins_exchange.png" class="w-[168px] max-w-[85vw] h-auto object-contain drop-shadow-md" alt="" />
  264. <span class="text-[#534A4A] font-bold text-[14px]">@Lang.millions_convert</span>
  265. </div>
  266. <!-- Conversion Display Area -->
  267. <div class="flex items-center justify-center gap-2 mb-4">
  268. <span id="displayAmountCoins" class="text-[#0062FF] font-[900] text-[24px]">100</span>
  269. <span class="text-black font-[900] text-[24px]">@Lang.millions_coins</span>
  270. <i class="fa-solid fa-arrow-right text-gray-800 text-[20px] mx-1"></i>
  271. <span id="displayAmountHTG" class="text-[#0062FF] font-[900] text-[24px]">100</span>
  272. <span class="text-black font-[900] text-[24px]">@Lang.millions_htg</span>
  273. </div>
  274. <p class="text-[#534A4A] font-bold text-[14px] text-center px-4 mb-6 leading-tight">@Lang.millions_enter_otp_proceed</p>
  275. <!-- OTP Inputs Area -->
  276. <div class="flex gap-2 mb-4" id="otpInputs">
  277. <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" placeholder="-"
  278. 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" />
  279. <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" placeholder="-"
  280. 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" />
  281. <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" placeholder="-"
  282. 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" />
  283. <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" placeholder="-"
  284. 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" />
  285. <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" placeholder="-"
  286. 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" />
  287. <input type="tel" maxlength="1" pattern="[0-9]" inputmode="numeric" oninput="handleOtpInput(this)" onkeydown="handleOtpKeydown(event, this)" placeholder="-"
  288. 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" />
  289. </div>
  290. <!-- OTP Error Message -->
  291. <p id="otpError" class="text-[#0062FF] font-bold text-[14px] text-center mb-4 hidden animate__animated animate__shakeX"></p>
  292. <!-- Timer & Resend Display -->
  293. <div class="flex flex-col items-center justify-center mb-6 gap-2">
  294. <span id="otpTimer" class="text-[#0062FF] font-[900] text-[18px]">60s</span>
  295. <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>
  296. </div>
  297. <!-- Action Buttons -->
  298. <div class="w-full flex gap-4">
  299. <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>
  300. <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>
  301. </div>
  302. </div>
  303. </div>
  304. </div>
  305. <!-- Bottom Navbar shared component -->
  306. <partial name="_BottomNavbar" />
  307. </div>
  308. @section Scripts {
  309. <script>
  310. function setAmount(val, el) {
  311. $("#transferAmount").val(val);
  312. // Update active state visuals
  313. $(".amt-btn").removeClass("border-2 border-white bg-[#0062FF] text-white shadow-md").addClass("border-gray-300 bg-white text-[#534A4A] shadow-sm");
  314. $(".amt-btn i").addClass("hidden").removeClass("text-white").addClass("text-[#00AF1B]"); // reset icons
  315. $(el).removeClass("border-gray-300 bg-white text-[#534A4A] shadow-sm").addClass("border-2 border-white bg-[#0062FF] text-white shadow-md");
  316. $(el).find("i").removeClass("hidden").removeClass("text-[#00AF1B]").addClass("text-white"); // show current check icon in white
  317. }
  318. // Logic removed as fields are read-only
  319. let timerInterval;
  320. let isProcessing = false;
  321. let selectedChannel = 1; // 1 = Basic, 2 = Natcash
  322. function selectChannel(id) {
  323. selectedChannel = id;
  324. // Reset both
  325. $(".channel-card").removeClass("border-2 border-[#0062FF] shadow-md").addClass("border border-gray-100 shadow-sm");
  326. $(".channel-card .check-icon").addClass("hidden");
  327. // Activate selected
  328. if (id === 1) {
  329. $("#selectBasic").removeClass("border-gray-100 shadow-sm").addClass("border-2 border-[#0062FF] shadow-md");
  330. $("#selectBasic .check-icon").removeClass("hidden");
  331. } else {
  332. $("#selectNatcash").removeClass("border-gray-100 shadow-sm").addClass("border-2 border-[#0062FF] shadow-md");
  333. $("#selectNatcash .check-icon").removeClass("hidden");
  334. }
  335. }
  336. $("#btnContinue").on("click", function() {
  337. if (isProcessing) return; // Prevent double-click
  338. const rawAmount = $("#transferAmount").val();
  339. const amount = parseFloat(unformatMoneyV2(rawAmount)) || 0;
  340. const phone = $("#receiverPhone").val();
  341. const winMoney = parseFloat(unformatMoneyV2("@(Model?.userStatus?.bet_coin ?? "0")")) || 0;
  342. if(amount <= 0) {
  343. showFailureModal("@Html.Raw(Lang.millions_enter_valid_amount)");
  344. return;
  345. }
  346. if(amount > winMoney) {
  347. showFailureModal("@Html.Raw(Lang.millions_insufficient_balance)");
  348. return;
  349. }
  350. // Lock button + show loading
  351. isProcessing = true;
  352. const $btn = $(this);
  353. const originalText = $btn.html();
  354. $btn.prop('disabled', true).html('<i class="fa-solid fa-spinner fa-spin mr-2"></i> ...');
  355. // Call sendotp API
  356. $.ajax({
  357. url: subDomain + "/Millions/Home/SendOTP",
  358. type: "POST",
  359. data: { phone: phone, amount: unformatMoneyV2(rawAmount), channelPayment: selectedChannel },
  360. success: function(res) {
  361. $btn.prop('disabled', false).html(originalText);
  362. isProcessing = false;
  363. if(res.responseCode == "0" || res.responseCode == 0) {
  364. openOtpModal();
  365. } else {
  366. showFailureModal(res.message || "Failed to send OTP", res.responseCode);
  367. }
  368. },
  369. error: function() {
  370. $btn.prop('disabled', false).html(originalText);
  371. isProcessing = false;
  372. showFailureModal("Network error while sending OTP");
  373. }
  374. });
  375. });
  376. function openOtpModal() {
  377. // Clear previous OTP values
  378. $(".otp-digit").val("");
  379. $("#btnConfirmTransfer").prop('disabled', true);
  380. // Set display amounts
  381. const amt = $("#transferAmount").val();
  382. const formattedAmt = formatMoneyV2(amt);
  383. $("#displayAmountCoins").text(formattedAmt);
  384. $("#displayAmountHTG").text(formattedAmt);
  385. $("#otpModal").removeClass("hidden").addClass("flex");
  386. resetTimer();
  387. $(".otp-digit").first().focus();
  388. }
  389. function closeOtpModal() {
  390. $("#otpModal").addClass("hidden").removeClass("flex");
  391. clearInterval(timerInterval);
  392. }
  393. // OTP input: enforce single digit, auto-advance
  394. function handleOtpInput(el) {
  395. // Allow only single digit
  396. el.value = el.value.replace(/[^0-9]/g, '').slice(0, 1);
  397. if (el.value.length === 1) {
  398. const next = $(el).next('.otp-digit');
  399. if (next.length) {
  400. next.focus();
  401. }
  402. }
  403. // Check if all 6 digits filled
  404. var filled = 0;
  405. $(".otp-digit").each(function() { if ($(this).val().length >= 1) filled++; });
  406. $("#btnConfirmTransfer").prop('disabled', filled < 6);
  407. // Clear error when typing and reset color to red
  408. $("#otpError").addClass("hidden").text("").removeClass("text-[#0A9800]").addClass("text-[#0062FF] animate__shakeX");
  409. }
  410. // OTP backspace: move to previous input
  411. function handleOtpKeydown(e, el) {
  412. if (e.key === 'Backspace') {
  413. if (el.value.length === 0) {
  414. const prev = $(el).prev('.otp-digit');
  415. if (prev.length) {
  416. prev.focus();
  417. }
  418. }
  419. }
  420. }
  421. function resetTimer() {
  422. let seconds = 60;
  423. clearInterval(timerInterval);
  424. $("#otpTimer").removeClass("hidden").text("60s");
  425. $("#resendOtpBtn").addClass("hidden");
  426. // Keep confirm disabled until OTP is filled — don't re-enable here
  427. timerInterval = setInterval(() => {
  428. seconds--;
  429. $("#otpTimer").text(seconds + "s");
  430. if (seconds <= 0) {
  431. clearInterval(timerInterval);
  432. $("#otpTimer").addClass("hidden");
  433. $("#resendOtpBtn").removeClass("hidden");
  434. // Disable confirm when timer expired
  435. $("#btnConfirmTransfer").prop('disabled', true).addClass('opacity-50');
  436. }
  437. }, 1000);
  438. }
  439. function sendOtpRequest() {
  440. const $btn = $("#resendOtpBtn");
  441. const originalText = $btn.text();
  442. $btn.prop('disabled', true).text("...");
  443. $.ajax({
  444. url: subDomain + "/Millions/Home/SendOTP",
  445. type: 'POST',
  446. success: function(data) {
  447. $btn.prop('disabled', false).text(originalText);
  448. if (data.responseCode === "0" || data.responseCode === 0) {
  449. resetTimer();
  450. $("#otpError").removeClass("text-[#0062FF] animate__shakeX").addClass("text-[#0A9800]").text("@Lang.millions_otp_sent_successfully").removeClass("hidden");
  451. $(".otp-digit").val("").first().focus();
  452. } else {
  453. if (data.responseCode === "-2" || (data.responseMessage && data.responseMessage.includes("System is upgrading"))) {
  454. closeOtpModal();
  455. showFailureModal(data.responseMessage || "System is upgrading", data.responseCode);
  456. } else {
  457. $("#otpError").removeClass("text-[#0A9800]").addClass("text-[#0062FF] animate__shakeX").text(data.responseMessage || "Failed to resend OTP").removeClass("hidden");
  458. }
  459. }
  460. },
  461. error: function() {
  462. $btn.prop('disabled', false).text(originalText);
  463. $("#otpError").text("Network error occurred.").removeClass("hidden");
  464. }
  465. });
  466. }
  467. $("#btnConfirmTransfer").on("click", function() {
  468. if (isProcessing) return; // Prevent double-click
  469. let otp = "";
  470. $(".otp-digit").each(function() { otp += $(this).val(); });
  471. if(otp.length < 6) {
  472. showFailureModal("Please enter 6-digit OTP");
  473. return;
  474. }
  475. // Lock button + show loading
  476. isProcessing = true;
  477. const $btn = $(this);
  478. const originalText = $btn.html();
  479. $btn.prop('disabled', true).html('<i class="fa-solid fa-spinner fa-spin mr-2"></i> ...');
  480. // Call the unified backend action that verifies OTP and then transfers
  481. $.ajax({
  482. url: subDomain + "/Millions/Home/ConfirmTransfer",
  483. type: "POST",
  484. data: {
  485. otp: otp,
  486. phone: $("#receiverPhone").val(),
  487. amount: unformatMoneyV2($("#transferAmount").val()),
  488. channelPayment: selectedChannel
  489. },
  490. success: function(res) {
  491. $btn.prop('disabled', false).html(originalText);
  492. isProcessing = false;
  493. if(res.responseCode == "0" || res.responseCode == 0) {
  494. closeOtpModal();
  495. // Update balance in UI immediately
  496. if(res.userStatus && res.userStatus.bet_coin) {
  497. $("#currentBetCoin").text(formatMoneyV2(res.userStatus.bet_coin));
  498. }
  499. // Populate Success Modal Fields
  500. const transferAmt = $("#transferAmount").val();
  501. $("#successAmount").text(formatMoneyV2(transferAmt) + " HTG");
  502. $("#successSender").text("@(Model?.userStatus?.msisdn ?? "0")");
  503. $("#successReceiver").text($("#receiverPhone").val());
  504. const now = new Date();
  505. const pad = (n) => n.toString().padStart(2, '0');
  506. const formattedTime = `${pad(now.getDate())}/${pad(now.getMonth() + 1)}/${now.getFullYear()} - ${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
  507. $("#successTime").text(formattedTime);
  508. // Set dynamic transfer note based on channel
  509. const noteWallet = "@Html.Raw(Lang.millions_reward_transferred_to_wallet)";
  510. const noteMainAccount = "@Html.Raw(Lang.millions_reward_transferred_to_main_account)";
  511. if (selectedChannel === 1) {
  512. $("#successTransferNote").html(noteMainAccount);
  513. } else {
  514. $("#successTransferNote").html(noteWallet);
  515. }
  516. $("#successModal").removeClass("hidden").addClass("flex");
  517. } else {
  518. if (res.responseCode === "-2" || (res.message && res.message.includes("System is upgrading"))) {
  519. closeOtpModal();
  520. showFailureModal(res.message || "System is upgrading", res.responseCode);
  521. } else {
  522. $("#otpError").text(res.message || "Invalid OTP").removeClass("hidden");
  523. $(".otp-digit").val("");
  524. $(".otp-digit").first().focus();
  525. }
  526. }
  527. },
  528. error: function() {
  529. $btn.prop('disabled', false).html(originalText);
  530. isProcessing = false;
  531. showFailureModal("Network error while confirming transfer");
  532. }
  533. });
  534. });
  535. function showFailureModal(message, code) {
  536. $("#failErrorMessage").html(message);
  537. if (code === "-2" || (message && message.includes("System is upgrading"))) {
  538. $("#failureModalStandardActions").addClass("is-hidden");
  539. $("#failureModalUpgradeActions").removeClass("is-hidden");
  540. } else {
  541. $("#failureModalStandardActions").removeClass("is-hidden");
  542. $("#failureModalUpgradeActions").addClass("is-hidden");
  543. }
  544. $("#failureModal").removeClass("hidden").addClass("flex");
  545. }
  546. </script>
  547. }