Login.cshtml 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. @using LotteryWebApp.Languages;
  2. @using LotteryWebApp.Controllers;
  3. @using LotteryWebApp.Common;
  4. @model AccountLogin_ViewModel
  5. @{
  6. ViewData["Title"] = "Login";
  7. Layout = "~/Views/Shared/_NothingLayout.cshtml";
  8. }
  9. <style>
  10. html, body {
  11. margin: 0 !important;
  12. padding: 0 !important;
  13. width: 100% !important;
  14. height: 100% !important;
  15. display: block !important;
  16. overflow-x: hidden !important;
  17. background: #FDF9F0 !important; /* Desktop base */
  18. font-family: 'Montserrat', sans-serif;
  19. }
  20. /* Trên điện thoại, cho màu nền HTML & BODY trùng khớp với màu đỏ của nội dung */
  21. @@media (max-width: 600px) {
  22. html, body {
  23. background: #EE0033 !important;
  24. }
  25. }
  26. .all-screen {
  27. width: 100% !important;
  28. display: block !important;
  29. position: relative !important;
  30. margin: 0 !important;
  31. padding: 0 !important;
  32. }
  33. .login-scaffold {
  34. width: 100% !important;
  35. width: 100vw !important; /* Ép lấy hết chiều rộng màn hình */
  36. max-width: 100% !important;
  37. min-height: 100dvh;
  38. background: #EE0033 !important; /* Nội màu đỏ */
  39. display: flex;
  40. flex-direction: column;
  41. align-items: center;
  42. padding: 20px 20px 80px 20px; /* Increased bottom padding to prevent cut-off */
  43. box-sizing: border-box;
  44. position: relative;
  45. margin: 0 auto;
  46. }
  47. /* Chỉ giới hạn chiều rộng trên màn hình lớn (Tablet/Desktop) */
  48. @@media (min-width: 600px) {
  49. .login-scaffold {
  50. max-width: 450px !important;
  51. width: 450px !important;
  52. }
  53. }
  54. /* Logo at top */
  55. .logo-container {
  56. margin-top: 15px;
  57. width: 100%;
  58. display: flex;
  59. justify-content: center;
  60. }
  61. .logo-container img {
  62. width: 280px;
  63. height: auto;
  64. }
  65. /* Form Area */
  66. .form-container {
  67. width: 100%;
  68. max-width: 360px;
  69. margin-top: 35px;
  70. z-index: 10;
  71. }
  72. /* Phone Input: White with Gray Prefix */
  73. .custom-phone-input {
  74. background: #FFFFFF;
  75. border: 2px solid #000000;
  76. border-radius: 12px;
  77. height: 56px;
  78. display: flex;
  79. overflow: hidden;
  80. margin-bottom: 20px;
  81. box-shadow: 0 4px 10px rgba(0,0,0,0.1);
  82. }
  83. .prefix-box {
  84. background: #E8E8E8;
  85. width: 25%;
  86. min-width: 80px;
  87. display: flex;
  88. align-items: center;
  89. justify-content: center;
  90. font-weight: 800;
  91. font-size: 20px;
  92. color: #000;
  93. border-right: 2px solid #DDD;
  94. }
  95. .input-box {
  96. flex: 1;
  97. padding: 0 15px;
  98. }
  99. .phone-field {
  100. width: 100%;
  101. height: 100%;
  102. border: none !important;
  103. background: transparent !important;
  104. outline: none !important;
  105. font-weight: 700;
  106. font-size: 20px;
  107. color: #000 !important;
  108. letter-spacing: 1px;
  109. }
  110. /* Remove Spinners from Number Input */
  111. .phone-field::-webkit-inner-spin-button,
  112. .phone-field::-webkit-outer-spin-button {
  113. -webkit-appearance: none;
  114. margin: 0;
  115. }
  116. .phone-field {
  117. -moz-appearance: textfield;
  118. }
  119. /* Action Button - Gold */
  120. .btn-submit-otp {
  121. background: linear-gradient(180deg, #FBD94E 0%, #F5AE31 100%);
  122. border: 1.5px solid #FFFFFF;
  123. border-radius: 14px;
  124. height: 56px;
  125. width: 100%;
  126. display: flex;
  127. align-items: center;
  128. justify-content: center;
  129. box-shadow: 0 4px 6px rgba(0,0,0,0.15);
  130. color: #000;
  131. font-family: 'Bricolage Grotesque', sans-serif;
  132. font-weight: 900;
  133. font-size: 21px;
  134. text-transform: none;
  135. cursor: pointer;
  136. }
  137. .btn-submit-otp:disabled {
  138. opacity: 0.5;
  139. filter: grayscale(0.5);
  140. cursor: not-allowed;
  141. }
  142. /* Illustration at bottom */
  143. .illustration-footer {
  144. flex: 1;
  145. display: flex;
  146. align-items: center;
  147. justify-content: center;
  148. width: 100%;
  149. pointer-events: none;
  150. margin-top: -10px;
  151. }
  152. .illustration-footer img {
  153. width: 100%;
  154. max-width: 420px;
  155. height: auto;
  156. }
  157. @@media (max-width: 400px) {
  158. .illustration-footer img {
  159. max-width: 300px; /* Shrink illustration on very small screens */
  160. }
  161. }
  162. /* Language Switcher Dark Red */
  163. .lang-switcher-fixed {
  164. margin-bottom: 40px; /* Increased bottom margin even more */
  165. background: #D53018;
  166. border-radius: 12px;
  167. padding: 4px;
  168. display: flex;
  169. gap: 4px;
  170. border: 2.5px solid #FFFFFF;
  171. }
  172. .lang-tab {
  173. padding: 7px 16px;
  174. border-radius: 10px;
  175. color: rgba(255,255,255,0.8);
  176. font-weight: 700;
  177. font-size: 18px;
  178. text-decoration: none !important;
  179. display: flex;
  180. align-items: center;
  181. gap: 6px;
  182. }
  183. .lang-tab.active {
  184. background: #420D09;
  185. color: #FFF;
  186. border: 1px solid #FFF;
  187. }
  188. .lang-tab img {
  189. width: 18px;
  190. height: auto;
  191. }
  192. /* OTP Inputs Inverted: White background again */
  193. .otp-container {
  194. display: flex;
  195. justify-content: space-between;
  196. gap: 10px;
  197. margin-bottom: 25px;
  198. }
  199. .otp-input {
  200. width: 60px;
  201. height: 60px;
  202. background: #800015; /* Màu đỏ đậm / Dark Maroon */
  203. border: none;
  204. border-radius: 12px;
  205. text-align: center;
  206. font-size: 26px;
  207. font-weight: 900;
  208. color: #FFFFFF;
  209. box-shadow: 0 4px 10px rgba(0,0,0,0.2);
  210. outline: none;
  211. transition: all 0.2s;
  212. }
  213. .otp-input:focus {
  214. background: #420D09;
  215. box-shadow: 0 0 0 2px #FFF;
  216. }
  217. .otp-input::placeholder {
  218. color: rgba(255,255,255,0.5);
  219. }
  220. .error-text {
  221. color: #FFD43D;
  222. font-weight: 800;
  223. font-size: 14px;
  224. text-align: center;
  225. margin-bottom: 15px;
  226. min-height: 20px;
  227. }
  228. /* Center Toastr */
  229. #toast-container {
  230. top: 50% !important;
  231. left: 50% !important;
  232. transform: translate(-50%, -50%) !important;
  233. right: auto !important;
  234. bottom: auto !important;
  235. position: fixed !important;
  236. display: block !important;
  237. }
  238. #toast-container > .toast {
  239. width: 300px !important;
  240. text-align: center;
  241. margin: 0 auto 12px auto !important;
  242. float: none !important;
  243. }
  244. </style>
  245. <script>
  246. // Global toastr config for this page
  247. toastr.options = {
  248. "positionClass": "toast-top-center",
  249. "showDuration": "300",
  250. "hideDuration": "1000",
  251. "timeOut": "5000",
  252. "extendedTimeOut": "1000",
  253. "showEasing": "swing",
  254. "hideEasing": "linear",
  255. "showMethod": "fadeIn",
  256. "hideMethod": "fadeOut"
  257. }
  258. </script>
  259. <div class="login-scaffold animate__animated animate__fadeIn">
  260. <!-- Logo -->
  261. <div class="logo-container">
  262. <img src="~/LotteryV2/img/ChooseApp/logo.png" alt="MEGA LOTO" />
  263. </div>
  264. <!-- Main Content -->
  265. <div class="form-container">
  266. @if (Model.step == Constants.LOGIN_ENTER_MSISDN || Model.step == null)
  267. {
  268. <!-- Step 1 -->
  269. <div class="custom-phone-input">
  270. <div class="prefix-box">+509</div>
  271. <div class="input-box">
  272. <input type="number"
  273. id="phonenumber"
  274. class="phone-field"
  275. placeholder="888 999 888"
  276. name="phonenumber"
  277. required
  278. autofocus>
  279. </div>
  280. </div>
  281. }
  282. else if (Model.step == Constants.LOGIN_ENTER_OTP)
  283. {
  284. <!-- Step 2 -->
  285. <input type="hidden" id="phonenumber_hidden" value="@Model.phonenumber" />
  286. <div style="text-align: center; color: white; margin-bottom: 20px;">
  287. <h2 style="font-weight: 900; font-size: 22px;">@Lang.verify_otp</h2>
  288. <p style="font-size: 14px; opacity: 0.8;">@Lang.enter_otp_to_login</p>
  289. </div>
  290. <form id="otpForm" action="/verify-otp" method="POST" class="otp-container">
  291. <input class="otp-input otp-box-0" pattern="\d{1}" inputmode="numeric" name="lottery-otp-box" maxlength="1" placeholder="-" required autofocus />
  292. <input class="otp-input otp-box-1" pattern="\d{1}" inputmode="numeric" name="lottery-otp-box" maxlength="1" placeholder="-" required />
  293. <input class="otp-input otp-box-2" pattern="\d{1}" inputmode="numeric" name="lottery-otp-box" maxlength="1" placeholder="-" required />
  294. <input class="otp-input otp-box-3" pattern="\d{1}" inputmode="numeric" name="lottery-otp-box" maxlength="1" placeholder="-" required />
  295. </form>
  296. <div style="text-align: center; margin-bottom: 20px;">
  297. <span style="color:white; font-size: 13px;">@Lang.not_get_otp</span>
  298. <a style="color:#FFD43D; font-weight:800; font-size: 14px; text-decoration: underline; cursor: pointer;" onclick="resentOTP_Action()">@Lang.resend_otp</a>
  299. </div>
  300. }
  301. <div class="error-text">
  302. @if(Model.message != null) {
  303. <script>toastr.error('@Model.message');</script>
  304. @Model.message
  305. }
  306. </div>
  307. <button type="button"
  308. id="btnLoginConfirm"
  309. class="btn-submit-otp"
  310. @(Model.step == Constants.LOGIN_ENTER_OTP ? "disabled" : "")
  311. onclick="@(Model.step == Constants.LOGIN_ENTER_MSISDN || Model.step == null ? "loginRedirect_Click()" : "loginButtonAction_Click()")">
  312. @(Model.step == null || Model.step == Constants.LOGIN_ENTER_MSISDN ? Lang.get_otp1 : Lang.confirm)
  313. </button>
  314. </div>
  315. <!-- Illustration at bottom -->
  316. <div class="illustration-footer">
  317. <img src="~/LotteryV2/img/top_banner1.png" alt="" />
  318. </div>
  319. @if (Model.step != Constants.LOGIN_ENTER_OTP)
  320. {
  321. <!-- Language Footer -->
  322. <div class="lang-switcher-fixed">
  323. <a href="javascript:void(0)" onclick="changeLanguageAction('fr')" class="lang-tab @(ViewBag.Lang == "fr" ? "active" : "")">
  324. <img src="~/img/en_flag.png" alt="" /> English
  325. </a>
  326. <a href="javascript:void(0)" onclick="changeLanguageAction('en')" class="lang-tab @(ViewBag.Lang == "en" || ViewBag.Lang == "fr" ? "active" : "")">
  327. <img src="~/img/Flag_of_Haiti.png" alt="" /> Natcom
  328. </a>
  329. </div>
  330. }
  331. </div>
  332. @if (Model.step == Constants.LOGIN_ENTER_OTP)
  333. {
  334. <script>
  335. $(document).ready(function() {
  336. function checkOtpComplete() {
  337. var filled = 0;
  338. $('.otp-input').each(function() {
  339. if ($(this).val().length >= 1) filled++;
  340. });
  341. $('#btnLoginConfirm').prop('disabled', filled < 4);
  342. }
  343. $('.otp-input').on('keyup', function(e) {
  344. var $this = $(this);
  345. if (e.keyCode == 8) {
  346. if ($this.val().length === 0) {
  347. $this.prev('.otp-input').val('').focus();
  348. }
  349. } else {
  350. if ($this.val().length >= 1) {
  351. $this.next('.otp-input').focus();
  352. }
  353. }
  354. checkOtpComplete();
  355. });
  356. $('.otp-input').on('input', function() {
  357. checkOtpComplete();
  358. });
  359. });
  360. </script>
  361. }
  362. @*<script type="text/javascript">
  363. function createAccountClick(message) {
  364. toastr.options.timeOut = 5000; // 1.5s
  365. toastr.error(message);
  366. }
  367. </script>*@