History.cshtml 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. @model LotteryWebApp.Models.UserTicketHistoryModel
  2. @using LotteryWebApp.Languages
  3. @using LotteryWebApp.Common
  4. @{
  5. ViewData["Title"] = "Millions - History";
  6. Layout = "~/Areas/Millions/Views/Shared/_Layout.cshtml";
  7. ViewData["ActiveTab"] = "History";
  8. }
  9. @section Styles {
  10. <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
  11. <link rel="stylesheet" href="/Millions/css/site.css" />
  12. <link rel="stylesheet" href="/Millions/css/results.css" />
  13. <link rel="stylesheet" href="/Millions/css/history.css" />
  14. }
  15. <div class="main-container results-container animate__animated animate__fadeIn pb-40 relative">
  16. <!-- Header Group: Static at Top -->
  17. <div class="fixed top-0 left-0 md:left-1/2 md:-translate-x-1/2 w-full md:max-w-[430px] z-[100] bg-[#0062FF]">
  18. <!-- Header -->
  19. <div class="results-top-header">
  20. <a href="/Millions/Home/GameHome" class="back-btn">
  21. <i class="fa-solid fa-arrow-left"></i>
  22. </a>
  23. <h1 class="font-bricolage">@Lang.history</h1>
  24. </div>
  25. <!-- Status Filters inside a white card-like container -->
  26. <div class="history-tabs-container bg-white rounded-t-[30px] px-4 pt-4 pb-1">
  27. <div class="status-filters">
  28. <div class="status-filter @(Model.status == Constants.ALL_DATA ? "active" : "")"
  29. onclick="changeStatus('@Constants.ALL_DATA', this)">@Lang.millions_all</div>
  30. <div class="status-filter @(Model.status == Constants.NOT_DRAW_CODE ? "active" : "")"
  31. onclick="changeStatus('@Constants.NOT_DRAW_CODE', this)">@Lang.millions_waiting</div>
  32. <div class="status-filter @(Model.status == Constants.WIN_CODE ? "active" : "")"
  33. onclick="changeStatus('@Constants.WIN_CODE', this)">@Lang.millions_win</div>
  34. <div class="status-filter @(Model.status == Constants.DRAWN_CODE ? "active" : "")"
  35. onclick="changeStatus('@Constants.DRAWN_CODE', this)">@Lang.millions_not_win</div>
  36. </div>
  37. </div>
  38. </div>
  39. <!-- Ticket List Container (spaced below fixed header) -->
  40. <div id="history-list-container"
  41. class="history-items-list bg-white rounded-t-[30px] px-4 pt-4 mt-[130px] pb-[120px] min-h-[calc(100vh-130px)] shadow-sm">
  42. <partial name="_TermUserTicketHistory" model="Model" />
  43. </div>
  44. <!-- Shared Bottom Navbar -->
  45. <partial name="_BottomNavbar" />
  46. <!-- Custom Notification Modal -->
  47. <div id="notificationModal" class="fixed inset-0 z-[300] flex items-center justify-center hidden px-6 font-bricolage" style="background: linear-gradient(135deg, rgba(26, 26, 46, 0.9) 0%, rgba(22, 33, 62, 0.9) 100%);">
  48. <div class="w-full max-w-[343px] min-h-[420px] bg-white rounded-[24px] overflow-hidden flex flex-col items-center p-8 animate__animated animate__zoomIn animate__faster shadow-2xl border border-white/50">
  49. <div class="w-full flex justify-center mb-8 mt-4">
  50. <img src="/Millions/img/modal/fail_icon.png" class="w-[160px] h-auto object-contain" alt="Notificaton icon" />
  51. </div>
  52. <div class="px-2 text-center mb-10 flex-1 flex items-center justify-center">
  53. <p id="notificationMessage" class="text-black font-[800] text-[20px] leading-snug"></p>
  54. </div>
  55. <div class="w-full">
  56. <button onclick="closeNotificationModal()" class="w-full bg-[#0062FF] text-white font-[800] text-[18px] py-[12px] rounded-[16px] shadow-lg active:scale-95 transition-all">
  57. @Lang.login
  58. </button>
  59. </div>
  60. </div>
  61. </div>
  62. </div>
  63. @section Scripts {
  64. <script>
  65. let currentTermType = '@Model.termType';
  66. let currentStatus = '@Model.status';
  67. let currentSeqPage = parseInt('@Model.seqPage');
  68. let totalPages = parseInt('@Model.totalPage' || '1');
  69. function changeGame(type, el) {
  70. if (currentTermType === type) return;
  71. currentTermType = type;
  72. currentSeqPage = 1;
  73. document.querySelectorAll('.game-tab').forEach(t => t.classList.remove('active'));
  74. el.classList.add('active');
  75. loadHistory();
  76. }
  77. function changeStatus(status, el) {
  78. if (currentStatus === status) return;
  79. currentStatus = status;
  80. currentSeqPage = 1;
  81. document.querySelectorAll('.status-filter').forEach(t => t.classList.remove('active'));
  82. el.classList.add('active');
  83. loadHistory();
  84. }
  85. function changePage(page) {
  86. if (page < 1 || page > totalPages) return;
  87. currentSeqPage = page;
  88. loadHistory();
  89. }
  90. var systemUpgrading = false;
  91. function showNotification(message, code) {
  92. $("#notificationMessage").text(message);
  93. const $btn = $("#notificationModal button");
  94. if (code === "-2" || (message && message.includes("System is upgrading"))) {
  95. systemUpgrading = true;
  96. $btn.text("@Lang.login");
  97. } else {
  98. systemUpgrading = false;
  99. $btn.text("OK");
  100. }
  101. $("#notificationModal").removeClass("hidden").addClass("flex");
  102. }
  103. function closeNotificationModal() {
  104. $("#notificationModal").addClass("hidden").removeClass("flex");
  105. if (systemUpgrading) {
  106. window.location.href = subDomain + "/Account/Login";
  107. }
  108. }
  109. function loadHistory() {
  110. const container = document.getElementById("history-list-container");
  111. container.style.opacity = "0.5";
  112. fetch(subDomain + `/Millions/Home/TermUserTicketHistory?termType=${currentTermType}&status=${currentStatus}&seqPage=${currentSeqPage}`)
  113. .then(response => {
  114. if (!response.ok) throw new Error('Network response was not ok');
  115. return response.text();
  116. })
  117. .then(html => {
  118. try {
  119. const json = JSON.parse(html);
  120. if (json.responseCode === "-2" || (json.responseMessage && json.responseMessage.includes("System is upgrading"))) {
  121. showNotification(json.responseMessage || "System is upgrading", "-2");
  122. return;
  123. }
  124. } catch (e) {
  125. // Not JSON, likely HTML partial
  126. }
  127. container.innerHTML = html;
  128. container.style.opacity = "1";
  129. updatePaginationUI();
  130. })
  131. .catch(error => {
  132. console.error("Error loading history:", error);
  133. container.style.opacity = "1";
  134. if (error.message && (error.message.includes("System is upgrading") || error.message.includes("-2"))) {
  135. showNotification(error.message, "-2");
  136. }
  137. });
  138. }
  139. function updatePaginationUI() {
  140. const pageDisplay = document.getElementById("pageDisplay");
  141. const prevBtn = document.getElementById("prevPage");
  142. const nextBtn = document.getElementById("nextPage");
  143. if (pageDisplay) {
  144. pageDisplay.innerText = `${currentSeqPage} / ${totalPages}`;
  145. }
  146. if (prevBtn) prevBtn.disabled = currentSeqPage <= 1;
  147. if (nextBtn) nextBtn.disabled = currentSeqPage >= totalPages;
  148. }
  149. function showTicketDetail(billCode, money, moneyWin, date, channel, payMethod, paymentMessage, ticketCode, gameId, drawTime, ticketId) {
  150. document.getElementById('detailBillCode').innerText = '#' + (billCode || '-');
  151. document.getElementById('detailTicketCode').innerText = '#' + (ticketId || '-');
  152. document.getElementById('detailMoney').innerText = formatMoneyV2(money || '0') + ' HTG';
  153. document.getElementById('detailMoneyWin').innerText = formatMoneyV2(moneyWin || '0') + ' HTG';
  154. document.getElementById('detailDate').innerText = date || '-';
  155. document.getElementById('detailDrawTime').innerText = drawTime || '-';
  156. document.getElementById('detailChannel').innerText = channel || 'App';
  157. // Logic for Game Name and Color
  158. const normalizedGameId = String(gameId || '').trim();
  159. let gameName = @Html.Raw(System.Text.Json.JsonSerializer.Serialize(Lang.millions));
  160. let gameColor = "#0062FF";
  161. if (normalizedGameId === "30") {
  162. gameName = @Html.Raw(System.Text.Json.JsonSerializer.Serialize(Lang.millions_classic_pick_10));
  163. } else if (normalizedGameId === "31") {
  164. gameName = "@Lang.millions_big_small";
  165. gameColor = "#00A3FF";
  166. } else if (normalizedGameId === "32") {
  167. gameName = "@Lang.millions_odd_even";
  168. gameColor = "#9333EA";
  169. } else if (normalizedGameId === "33") {
  170. gameName = @Html.Raw(System.Text.Json.JsonSerializer.Serialize(Lang.millions));
  171. }
  172. const gameEl = document.getElementById('detailGameName');
  173. gameEl.innerText = gameName;
  174. gameEl.style.color = gameColor;
  175. const normalizedPayMethod = String(payMethod || '').trim().toUpperCase();
  176. const normalizedPaymentMessage = String(paymentMessage || '').trim().toUpperCase();
  177. const isWalletPayment = normalizedPaymentMessage.includes('WALLET')
  178. || normalizedPaymentMessage.includes('NATCASH')
  179. || normalizedPayMethod === '1'
  180. || normalizedPayMethod === 'WALLET';
  181. const payMethodText = isWalletPayment
  182. ? @Html.Raw(System.Text.Json.JsonSerializer.Serialize(Lang.payment_wallet))
  183. : @Html.Raw(System.Text.Json.JsonSerializer.Serialize(Lang.payment_main_account));
  184. document.getElementById('detailPaymentMethod').innerText = payMethodText;
  185. // Render Numbers
  186. const container = document.getElementById('detailBallsContainer');
  187. container.innerHTML = '';
  188. if (ticketCode) {
  189. const balls = ticketCode.split(/[;,]/).filter(x => x.trim() !== '');
  190. balls.forEach((val, index) => {
  191. let ballVal = val.trim();
  192. let displayVal = ballVal;
  193. let isLabel = false;
  194. switch(ballVal.toUpperCase()) {
  195. case 'B': displayVal = 'Big'; isLabel = true; break;
  196. case 'S': displayVal = 'Small'; isLabel = true; break;
  197. case 'O': displayVal = 'Odd'; isLabel = true; break;
  198. case 'E': displayVal = 'Even'; isLabel = true; break;
  199. }
  200. const div = document.createElement('div');
  201. if (isLabel) {
  202. div.className = "bg-[#0062FF] text-white px-4 py-1 rounded-full font-black text-[12px] shadow-sm uppercase tracking-wider";
  203. div.innerText = displayVal;
  204. } else {
  205. const isMbBall = index === balls.length - 1;
  206. div.className = "w-8 h-8 rounded-full flex items-center justify-center text-white text-[12px] font-black shadow-md relative" + (isMbBall ? " mr-1.5" : "");
  207. div.style.background = "linear-gradient(135deg, #FF3D63 0%, #E3132D 60%, #BA0F21 100%)";
  208. div.style.border = "1px solid rgba(255, 255, 255, 0.3)";
  209. div.innerText = displayVal;
  210. if (isMbBall) {
  211. const mbLabel = document.createElement('span');
  212. mbLabel.className = 'ticket-mb-label';
  213. mbLabel.innerText = 'MB';
  214. div.appendChild(mbLabel);
  215. }
  216. }
  217. container.appendChild(div);
  218. });
  219. }
  220. const modal = document.getElementById('ticketDetailModal');
  221. modal.classList.remove('hidden');
  222. modal.classList.add('flex');
  223. }
  224. function closeDetailModal() {
  225. const modal = document.getElementById('ticketDetailModal');
  226. modal.classList.add('hidden');
  227. modal.classList.remove('flex');
  228. }
  229. </script>
  230. }
  231. <!-- Ticket Detail Modal -->
  232. <div id="ticketDetailModal" class="fixed inset-0 z-[200] bg-black/60 hidden items-center justify-center p-4 font-bricolage backdrop-blur-sm">
  233. <div class="w-full max-w-[380px] bg-white rounded-[28px] overflow-hidden flex flex-col items-center px-6 py-8 shadow-2xl relative animate__animated animate__zoomIn animate__faster">
  234. <button onclick="closeDetailModal()" class="absolute top-4 right-4 text-gray-400 hover:text-gray-600 transition-colors">
  235. <i class="fas fa-times text-xl"></i>
  236. </button>
  237. <div class="w-20 h-20 bg-gray-50 rounded-full flex items-center justify-center mb-4">
  238. <i class="fas fa-receipt text-3xl text-[#0062FF]"></i>
  239. </div>
  240. <h2 class="text-[20px] font-black text-gray-800 mb-2 uppercase tracking-tight">@Lang.millions_ticket_detail</h2>
  241. <!-- Ticket Numbers Display -->
  242. <div id="detailBallsContainer" class="flex flex-wrap justify-center gap-2 mb-6 mt-2 max-w-full">
  243. <!-- Dynamic balls here -->
  244. </div>
  245. <div class="w-full space-y-4">
  246. <div class="flex justify-between items-center text-sm border-b border-gray-50 pb-2">
  247. <span class="text-gray-400 font-bold uppercase text-[11px]">@Lang.millions_game</span>
  248. <span id="detailGameName" class="font-black">Pick 10</span>
  249. </div>
  250. <div class="flex justify-between items-center text-sm border-b border-gray-50 pb-2">
  251. <span class="text-gray-400 font-bold uppercase text-[11px]">@Lang.millions_bill_code</span>
  252. <span id="detailBillCode" class="font-black text-gray-800">#</span>
  253. </div>
  254. <div class="flex justify-between items-center text-sm border-b border-gray-50 pb-2">
  255. <span class="text-gray-400 font-bold uppercase text-[11px]">@Lang.millions_ticket_code</span>
  256. <span id="detailTicketCode" class="font-black text-gray-800">#</span>
  257. </div>
  258. <div class="flex justify-between items-center text-sm border-b border-gray-50 pb-2">
  259. <span class="text-gray-400 font-bold uppercase text-[11px]">@Lang.millions_money_ticket</span>
  260. <span id="detailMoney" class="font-black text-gray-800">0 HTG</span>
  261. </div>
  262. <div class="flex justify-between items-center text-sm border-b border-gray-50 pb-2">
  263. <span class="text-gray-400 font-bold uppercase text-[11px]">@Lang.millions_money_win</span>
  264. <span id="detailMoneyWin" class="font-black text-[#0A9800]">0 HTG</span>
  265. </div>
  266. <div class="flex justify-between items-center text-sm border-b border-gray-50 pb-2">
  267. <span class="text-gray-400 font-bold uppercase text-[11px]">@Lang.millions_purchase_date</span>
  268. <span id="detailDate" class="font-black text-gray-800">-</span>
  269. </div>
  270. <div class="flex justify-between items-center text-sm border-b border-gray-50 pb-2">
  271. <span class="text-gray-400 font-bold uppercase text-[11px]">@Lang.millions_draw_date</span>
  272. <span id="detailDrawTime" class="font-black text-gray-800">-</span>
  273. </div>
  274. <div class="flex justify-between items-center text-sm border-b border-gray-50 pb-2">
  275. <span class="text-gray-400 font-bold uppercase text-[11px]">@Lang.millions_channel</span>
  276. <span id="detailChannel" class="font-black text-gray-800">-</span>
  277. </div>
  278. <div class="flex justify-between items-center text-sm border-b border-gray-50 pb-2">
  279. <span class="text-gray-400 font-bold uppercase text-[11px]">@Lang.millions_payment_method</span>
  280. <span id="detailPaymentMethod" class="font-black text-gray-800">-</span>
  281. </div>
  282. </div>
  283. <button onclick="closeDetailModal()" class="w-full bg-[#0062FF] text-white font-black text-[16px] py-3.5 rounded-2xl shadow-lg mt-8 active:scale-95 transition-all uppercase tracking-widest">
  284. @Lang.millions_close
  285. </button>
  286. </div>
  287. </div>