student пре 1 недеља
родитељ
комит
9c9b3d56c8

+ 0 - 172
KittyFallWs/KittyFallWs/sql/20260713_result_request_inbox_online.sql

@@ -1,172 +0,0 @@
--- ============================================================================
--- KittyFallWs - durable resultRequest inbox
--- Oracle online-safe migration
---
--- Muc tieu:
---   1. Tao ledger chong xu ly trung khi resultRequest duoc doc lai tu file.
---   2. Ho tro doi soat/replay theo EVENT_ID va TRANSACTION_ID.
---   3. Khong ALTER/LOCK cac bang nghiep vu dang nong nhu REG_INFO, CHARGE_LOG.
---
--- Dac tinh trien khai:
---   - Chi tao object moi.
---   - Co the chay lai; cac object da ton tai se duoc bo qua.
---   - Khong DML vao bang nghiep vu hien tai.
---   - Oracle DDL tu dong COMMIT. Nen backup schema metadata truoc khi chay.
---
--- Cach chay (SQL*Plus/SQLcl) bang schema ma KittyFallWs dang ket noi:
---   sqlplus <user>/<password>@<service> @20260713_result_request_inbox_online.sql
--- ============================================================================
-
-SET DEFINE OFF
-SET SERVEROUTPUT ON
-SET FEEDBACK ON
-SET VERIFY OFF
-WHENEVER SQLERROR EXIT SQL.SQLCODE ROLLBACK
-
-PROMPT [1/4] Creating RESULT_REQUEST_INBOX when missing...
-
-DECLARE
-    v_count NUMBER;
-BEGIN
-    SELECT COUNT(*)
-      INTO v_count
-      FROM USER_TABLES
-     WHERE TABLE_NAME = 'RESULT_REQUEST_INBOX';
-
-    IF v_count = 0 THEN
-        EXECUTE IMMEDIATE q'[
-            CREATE TABLE RESULT_REQUEST_INBOX
-            (
-                EVENT_ID          VARCHAR2(64 CHAR)   NOT NULL,
-                TRANSACTION_ID    VARCHAR2(128 CHAR),
-                SERVICE_ID        VARCHAR2(100 CHAR)  NOT NULL,
-                MSISDN            VARCHAR2(32 CHAR)   NOT NULL,
-                REQUEST_TIME      TIMESTAMP(6)        NOT NULL,
-                CHARGE_TIME_RAW   VARCHAR2(64 CHAR),
-                PARAMS_VALUE      VARCHAR2(32 CHAR),
-                MODE_VALUE        VARCHAR2(32 CHAR),
-                AMOUNT_VALUE      VARCHAR2(64 CHAR),
-                COMMAND_VALUE     VARCHAR2(100 CHAR),
-                PAYLOAD_HASH      VARCHAR2(64 CHAR)   NOT NULL,
-                SOURCE_FILE       VARCHAR2(512 CHAR),
-                SOURCE_LINE       NUMBER(12),
-                STATUS            VARCHAR2(20 CHAR)   DEFAULT 'PROCESSED' NOT NULL,
-                CHARGE_LOG_ID     NUMBER,
-                PROCESSED_TIME    TIMESTAMP(6)        DEFAULT SYSTIMESTAMP NOT NULL,
-                RETRY_COUNT       NUMBER(10)          DEFAULT 0 NOT NULL,
-                LAST_ERROR        VARCHAR2(2000 CHAR),
-                CREATED_TIME      TIMESTAMP(6)        DEFAULT SYSTIMESTAMP NOT NULL,
-                UPDATED_TIME      TIMESTAMP(6),
-                CONSTRAINT PK_RESULT_REQUEST_INBOX PRIMARY KEY (EVENT_ID),
-                CONSTRAINT CK_RESULT_REQUEST_STATUS CHECK
-                    (STATUS IN ('PROCESSING', 'PROCESSED', 'SKIPPED', 'FAILED')),
-                CONSTRAINT CK_RESULT_REQUEST_RETRY CHECK (RETRY_COUNT >= 0)
-            )
-        ]';
-        DBMS_OUTPUT.PUT_LINE('Created table RESULT_REQUEST_INBOX.');
-    ELSE
-        DBMS_OUTPUT.PUT_LINE('Table RESULT_REQUEST_INBOX already exists; skipped.');
-    END IF;
-END;
-/
-
-PROMPT [2/4] Creating lookup indexes when missing...
-
-DECLARE
-    v_count NUMBER;
-BEGIN
-    SELECT COUNT(*)
-      INTO v_count
-      FROM USER_INDEXES
-     WHERE INDEX_NAME = 'IX_RRI_TRANSACTION_SERVICE';
-
-    IF v_count = 0 THEN
-        EXECUTE IMMEDIATE
-            'CREATE INDEX IX_RRI_TRANSACTION_SERVICE '
-            || 'ON RESULT_REQUEST_INBOX (TRANSACTION_ID, SERVICE_ID)';
-        DBMS_OUTPUT.PUT_LINE('Created index IX_RRI_TRANSACTION_SERVICE.');
-    ELSE
-        DBMS_OUTPUT.PUT_LINE('Index IX_RRI_TRANSACTION_SERVICE already exists; skipped.');
-    END IF;
-
-    SELECT COUNT(*)
-      INTO v_count
-      FROM USER_INDEXES
-     WHERE INDEX_NAME = 'IX_RRI_REQUEST_TIME';
-
-    IF v_count = 0 THEN
-        EXECUTE IMMEDIATE
-            'CREATE INDEX IX_RRI_REQUEST_TIME '
-            || 'ON RESULT_REQUEST_INBOX (REQUEST_TIME)';
-        DBMS_OUTPUT.PUT_LINE('Created index IX_RRI_REQUEST_TIME.');
-    ELSE
-        DBMS_OUTPUT.PUT_LINE('Index IX_RRI_REQUEST_TIME already exists; skipped.');
-    END IF;
-
-    SELECT COUNT(*)
-      INTO v_count
-      FROM USER_INDEXES
-     WHERE INDEX_NAME = 'IX_RRI_STATUS_PROCESSED';
-
-    IF v_count = 0 THEN
-        EXECUTE IMMEDIATE
-            'CREATE INDEX IX_RRI_STATUS_PROCESSED '
-            || 'ON RESULT_REQUEST_INBOX (STATUS, PROCESSED_TIME)';
-        DBMS_OUTPUT.PUT_LINE('Created index IX_RRI_STATUS_PROCESSED.');
-    ELSE
-        DBMS_OUTPUT.PUT_LINE('Index IX_RRI_STATUS_PROCESSED already exists; skipped.');
-    END IF;
-END;
-/
-
-PROMPT [3/4] Adding metadata comments...
-
-COMMENT ON TABLE RESULT_REQUEST_INBOX IS
-    'Idempotency and audit ledger for KittyFallWs resultRequest file processing';
-COMMENT ON COLUMN RESULT_REQUEST_INBOX.EVENT_ID IS
-    'SHA-256 idempotency key derived from transaction and request identity';
-COMMENT ON COLUMN RESULT_REQUEST_INBOX.PAYLOAD_HASH IS
-    'SHA-256 hash of the normalized durable file payload';
-COMMENT ON COLUMN RESULT_REQUEST_INBOX.CHARGE_LOG_ID IS
-    'Optional reference to CHARGE_LOG.ID without altering the hot CHARGE_LOG table';
-COMMENT ON COLUMN RESULT_REQUEST_INBOX.SOURCE_FILE IS
-    'Spool/archive file containing the original durable event';
-COMMENT ON COLUMN RESULT_REQUEST_INBOX.SOURCE_LINE IS
-    'Line number of the event in the spool segment';
-
-PROMPT [4/4] Verifying created objects...
-
-COLUMN TABLE_NAME FORMAT A30
-COLUMN INDEX_NAME FORMAT A32
-COLUMN STATUS FORMAT A10
-
-SELECT TABLE_NAME, STATUS
-  FROM USER_TABLES
- WHERE TABLE_NAME = 'RESULT_REQUEST_INBOX';
-
-SELECT INDEX_NAME, STATUS
-  FROM USER_INDEXES
- WHERE TABLE_NAME = 'RESULT_REQUEST_INBOX'
- ORDER BY INDEX_NAME;
-
-SELECT CONSTRAINT_NAME, CONSTRAINT_TYPE, STATUS
-  FROM USER_CONSTRAINTS
- WHERE TABLE_NAME = 'RESULT_REQUEST_INBOX'
- ORDER BY CONSTRAINT_TYPE, CONSTRAINT_NAME;
-
-PROMPT Migration completed successfully.
-
--- ============================================================================
--- IMPORTANT - transaction usage expected from the future worker:
---
---   1. INSERT RESULT_REQUEST_INBOX(EVENT_ID, ..., STATUS='PROCESSING')
---   2. UPDATE REG_INFO ...
---   3. INSERT CHARGE_LOG ...
---   4. UPDATE RESULT_REQUEST_INBOX
---         SET STATUS='PROCESSED', CHARGE_LOG_ID=:id, PROCESSED_TIME=SYSTIMESTAMP
---   5. COMMIT
---
--- Tat ca cac buoc tren phai dung CUNG MOT Oracle Connection/transaction.
--- Neu EVENT_ID trung PK, worker coi event da duoc xu ly va khong renew lan hai.
--- ============================================================================
-

+ 76 - 76
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/DailyWinners/Index.cshtml

@@ -1,87 +1,87 @@
 @model Common.Http.Apis.Request.UserDailyWinnersData
 @{
-    ViewData["Title"] = $"Kitty Fall - {Lang.DailyWinners}";
-    var profile = Model?.profile ?? new Common.Http.Apis.Request.UserGameActivityProfile();
-    var profilePicture = string.IsNullOrWhiteSpace(profile.picture)
-        ? Url.Content("~/kitty/assets/kitty-fall/default.png")
-        : Url.Content("~/kitty/assets/kitty-fall/") + profile.picture;
-    var selectedDate = Model?.selectedDate ?? "";
-    var calendarMinDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1).ToString("yyyy-MM-dd");
-    var calendarMaxDate = DateTime.Now.ToString("yyyy-MM-dd");
+	ViewData["Title"] = $"Kitty Fall - {Lang.DailyWinners}";
+	var profile = Model?.profile ?? new Common.Http.Apis.Request.UserGameActivityProfile();
+	var profilePicture = string.IsNullOrWhiteSpace(profile.picture)
+		? Url.Content("~/kitty/assets/kitty-fall/default.png")
+		: Url.Content("~/kitty/assets/kitty-fall/") + profile.picture;
+	var selectedDate = Model?.selectedDate ?? "";
+	var calendarMinDate = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1).ToString("yyyy-MM-dd");
+	var calendarMaxDate = DateTime.Now.ToString("yyyy-MM-dd");
 }
 
 <main class="kitty-app is-winner-screen is-daily-winner-screen" aria-label="@Lang.DailyWinnerList">
-    <img class="kitty-bg" src="~/kitty/assets/kitty-fall/bg.png" alt="">
-    <div class="kitty-bg-fade"></div>
+	<img class="kitty-bg" src="~/kitty/assets/kitty-fall/bg.png" alt="">
+	<div class="kitty-bg-fade"></div>
 
-    <header class="topbar">
-        <div class="profile">
-            <img class="profile-avatar" src="@profilePicture" alt="@Lang.PlayerAvatar">
-            <div class="profile-copy">
-                <strong>@Lang.Hello</strong>
-                <span>@profile.msisdn</span>
-            </div>
-        </div>
-        <button class="menu-button" aria-label="@Lang.OpenMenu">
-            <span></span>
-            <span></span>
-            <span></span>
-        </button>
-    </header>
+	<header class="topbar">
+		<div class="profile">
+			<img class="profile-avatar" src="@profilePicture" alt="@Lang.PlayerAvatar">
+			<div class="profile-copy">
+				<strong>@Lang.Hello</strong>
+				<span>@profile.msisdn</span>
+			</div>
+		</div>
+		<button class="menu-button" aria-label="@Lang.OpenMenu">
+			<span></span>
+			<span></span>
+			<span></span>
+		</button>
+	</header>
 
-    <section class="daily-winner-screen" aria-label="@Lang.DailyWinnerList">
-        <div class="daily-winner-content">
-            <div class="daily-title">
-                <img src="~/kitty/assets/kitty-fall/daily-winner-ribbon.png" alt="">
-                <svg class="daily-title-text" viewBox="0 0 320 75" aria-hidden="true">
-                    <defs>
-                        <path id="daily-winner-title-path" d="M 58 44 Q 160 15 262 44" />
-                    </defs>
-                    <text text-anchor="middle" textLength="175" lengthAdjust="spacingAndGlyphs">
-                        <textPath href="#daily-winner-title-path" startOffset="50%">@Lang.DailyWinnerList</textPath>
-                    </text>
-                </svg>
-                <span class="daily-title-paw daily-title-paw-left" aria-hidden="true"></span>
-                <span class="daily-title-paw daily-title-paw-right" aria-hidden="true"></span>
-            </div>
+	<section class="daily-winner-screen" aria-label="@Lang.DailyWinnerList">
+		<div class="daily-winner-content">
+			<div class="daily-title">
+				<img src="~/kitty/assets/kitty-fall/daily-winner-ribbon.png" alt="">
+				<svg class="daily-title-text" viewBox="0 0 320 75" aria-hidden="true">
+					<defs>
+						<path id="daily-winner-title-path" d="M 58 44 Q 160 15 262 44" />
+					</defs>
+					<text text-anchor="middle" textLength="175" lengthAdjust="spacingAndGlyphs">
+						<textPath href="#daily-winner-title-path" startOffset="50%">@Lang.DailyWinnerList</textPath>
+					</text>
+				</svg>
+				<span class="daily-title-paw daily-title-paw-left" aria-hidden="true"></span>
+				<span class="daily-title-paw daily-title-paw-right" aria-hidden="true"></span>
+			</div>
 
-            <form class="winner-date-picker" method="get" action="@Url.Action("Index", "DailyWinners")" aria-label="@Lang.SelectDate"
-                  data-calendar-picker data-selected-date="@selectedDate" data-min-date="@calendarMinDate" data-max-date="@calendarMaxDate">
-                <img class="date-calendar" src="~/kitty/assets/kitty-fall/icon-calendar.png" alt="">
-                <strong>@Model?.selectedDateText</strong>
-                <input type="hidden" name="selectedDate" value="@selectedDate" data-calendar-value>
-                <img class="date-chevron" src="~/kitty/assets/kitty-fall/icon-chevron-down.png" alt="">
-            </form>
+			<form class="winner-date-picker" method="get" action="@Url.Action("Index", "DailyWinners")" aria-label="@Lang.SelectDate"
+				  data-calendar-picker data-selected-date="@selectedDate" data-min-date="@calendarMinDate" data-max-date="@calendarMaxDate">
+				<img class="date-calendar" src="~/kitty/assets/kitty-fall/icon-calendar.png" alt="">
+				<strong>@Model?.selectedDateText</strong>
+				<input type="hidden" name="selectedDate" value="@selectedDate" data-calendar-value>
+				<img class="date-chevron" src="~/kitty/assets/kitty-fall/icon-chevron-down.png" alt="">
+			</form>
 
-            <div class="daily-table-shell">
-                <div class="daily-table">
-                    <div class="daily-table-head">
-                        <span>@Lang.No</span>
-                        <span>@Lang.PhoneNumber</span>
-                        <span>@Lang.Time</span>
-                        <span>@Lang.Rewards</span>
-                    </div>
+			<div class="daily-table-shell">
+				<div class="daily-table">
+					<div class="daily-table-head">
+						<span>@Lang.No</span>
+						<span>@Lang.PhoneNumber</span>
+						<span>@Lang.Time</span>
+						<span>@Lang.Rewards</span>
+					</div>
 
-                    <div class="daily-table-body">
-                        @if (Model?.items != null && Model.items.Count > 0)
-                        {
-                            foreach (var item in Model.items)
-                            {
-                                <div class="daily-row">
-                                    <span>@item.no</span>
-                                    <span>@item.phoneText</span>
-                                    <span>@item.timeText</span>
-                                    <strong class="reward-pill @item.rewardCssClass">
-                                        <img src="@item.rewardIcon" alt="">
-                                        <b>@item.rewardValueText</b>
-                                        <small>@item.rewardUnitText</small>
-                                    </strong>
-                                </div>
-                            }
-                        }
-                    </div>
-                </div>
-            </div>
-        </div>
-    </section>
+					<div class="daily-table-body">
+						@if (Model?.items != null && Model.items.Count > 0)
+						{
+							foreach (var item in Model.items)
+							{
+								<div class="daily-row">
+									<span>@item.no</span>
+									<span>@item.phoneText</span>
+									<span>@item.timeText</span>
+									<strong class="reward-pill @item.rewardCssClass">
+										<img src="@item.rewardIcon" alt="">
+										<b>@item.rewardValueText</b>
+										<small>@item.rewardUnitText</small>
+									</strong>
+								</div>
+							}
+						}
+					</div>
+				</div>
+			</div>
+		</div>
+	</section>
 </main>

+ 220 - 220
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Guide/Index.cshtml

@@ -1,238 +1,238 @@
 @model Common.Http.Apis.Request.UserGameActivityProfile
 @{
-    ViewData["Title"] = "Kitty Fall - Guide";
-    var profile = Model ?? new Common.Http.Apis.Request.UserGameActivityProfile();
-    var profilePicture = string.IsNullOrWhiteSpace(profile.picture)
-        ? Url.Content("~/kitty/assets/kitty-fall/default.png")
-        : Url.Content("~/kitty/assets/kitty-fall/") + profile.picture;
+	ViewData["Title"] = "Kitty Fall - Guide";
+	var profile = Model ?? new Common.Http.Apis.Request.UserGameActivityProfile();
+	var profilePicture = string.IsNullOrWhiteSpace(profile.picture)
+		? Url.Content("~/kitty/assets/kitty-fall/default.png")
+		: Url.Content("~/kitty/assets/kitty-fall/") + profile.picture;
 }
 
 <main class="kitty-app is-winner-screen is-guide-screen" aria-label="@Lang.GuideAriaLabel">
-    <img class="kitty-bg" src="~/kitty/assets/kitty-fall/bg.png" alt="">
-    <div class="kitty-bg-fade"></div>
+	<img class="kitty-bg" src="~/kitty/assets/kitty-fall/bg.png" alt="">
+	<div class="kitty-bg-fade"></div>
 
-    <header class="topbar">
-        <div class="profile">
-            <img class="profile-avatar" src="@profilePicture" alt="@Lang.PlayerAvatar">
-            <div class="profile-copy">
-                <strong>@Lang.Hello</strong>
-                <span>@profile.msisdn</span>
-            </div>
-        </div>
-        <button class="menu-button" aria-label="@Lang.OpenMenu">
-            <span></span>
-            <span></span>
-            <span></span>
-        </button>
-    </header>
+	<header class="topbar">
+		<div class="profile">
+			<img class="profile-avatar" src="@profilePicture" alt="@Lang.PlayerAvatar">
+			<div class="profile-copy">
+				<strong>@Lang.Hello</strong>
+				<span>@profile.msisdn</span>
+			</div>
+		</div>
+		<button class="menu-button" aria-label="@Lang.OpenMenu">
+			<span></span>
+			<span></span>
+			<span></span>
+		</button>
+	</header>
 
-    <section class="guide-screen" aria-label="@Lang.GuideAriaLabel">
-        <article class="guide-panel">
-            <div class="guide-content">
-                <h1>@Html.Raw(Lang.GuideTitle)</h1>
-                <img class="guide-logo" src="~/kitty/assets/kitty-fall/logo.png" alt="Kittyfall">
+	<section class="guide-screen" aria-label="@Lang.GuideAriaLabel">
+		<article class="guide-panel">
+			<div class="guide-content">
+				<h1>@Html.Raw(Lang.GuideTitle)</h1>
+				<img class="guide-logo" src="~/kitty/assets/kitty-fall/logo.png" alt="Kittyfall">
 
-                <section class="guide-section">
-                    <h2><b>1</b><span>@Lang.GuideServiceIntroduction</span></h2>
-                    <p>@Lang.GuideIntroP1</p>
-                    <p>@Lang.GuideIntroP2</p>
-                    <p>@Lang.GuideIntroP3</p>
-                    <img class="guide-cat" src="~/kitty/assets/kitty-fall/cat.png" alt="">
-                    <p>@Lang.GuideIntroP4</p>
-                    <p>@Lang.GuideIntroP5</p>
-                    <p>@Lang.GuideIntroP6</p>
-                </section>
+				<section class="guide-section">
+					<h2><b>1</b><span>@Lang.GuideServiceIntroduction</span></h2>
+					<p>@Lang.GuideIntroP1</p>
+					<p>@Lang.GuideIntroP2</p>
+					<p>@Lang.GuideIntroP3</p>
+					<img class="guide-cat" src="~/kitty/assets/kitty-fall/cat.png" alt="">
+					<p>@Lang.GuideIntroP4</p>
+					<p>@Lang.GuideIntroP5</p>
+					<p>@Lang.GuideIntroP6</p>
+				</section>
 
-                <section class="guide-section">
-                    <h2><b>2</b><span>@Lang.GuideHowToParticipate</span></h2>
-                    <p><strong>@Lang.GuideCustomersCanPlay</strong></p>
-                    <p><strong>@Lang.GuideRegistrationChannels</strong></p>
-                    <p><strong>@Lang.GuidePurchaseOrSubscribe</strong></p>
-                    <p>@Lang.GuideStartPlayingOptions</p>
-                    <ul>
-                        <li>@Lang.GuideSubscribeTurnPackages</li>
-                        <li>@Lang.GuideOneTimePurchase1</li>
-                        <li>@Lang.GuideOneTimePurchase5</li>
-                    </ul>
-                    <div class="guide-package">
-                        <img src="~/kitty/assets/kitty-fall/gift.png" alt="">
-                        <strong>3</strong>
-                        <span>HTG/Day</span>
-                        <button type="button">@Lang.SubscribeNow</button>
-                    </div>
-                </section>
+				<section class="guide-section">
+					<h2><b>2</b><span>@Lang.GuideHowToParticipate</span></h2>
+					<p><strong>@Lang.GuideCustomersCanPlay</strong></p>
+					<p><strong>@Lang.GuideRegistrationChannels</strong></p>
+					<p><strong>@Lang.GuidePurchaseOrSubscribe</strong></p>
+					<p>@Lang.GuideStartPlayingOptions</p>
+					<ul>
+						<li>@Lang.GuideSubscribeTurnPackages</li>
+						<li>@Lang.GuideOneTimePurchase1</li>
+						<li>@Lang.GuideOneTimePurchase5</li>
+					</ul>
+					<div class="guide-package">
+						<img src="~/kitty/assets/kitty-fall/gift.png" alt="">
+						<strong>3</strong>
+						<span>HTG/Day</span>
+						<button type="button">@Lang.SubscribeNow</button>
+					</div>
+				</section>
 
-                <section class="guide-section">
-                    <h2><b>3</b><span>@Lang.GuideGameOverview</span></h2>
-                    <p>@Lang.GameDifficultyLevelGuide</p>
-                    <p>@Lang.GuideModeLevels</p>
-                    <p>@Lang.GuideTotalLevels</p>
-                    <p>@Lang.GuideProgressRewards</p>
-                    <p>@Lang.GuideFailLevel</p>
-                    <p>@Lang.GuideRescueOption</p>
+				<section class="guide-section">
+					<h2><b>3</b><span>@Lang.GuideGameOverview</span></h2>
+					<p>@Lang.GameDifficultyLevelGuide</p>
+					<p>@Lang.GuideModeLevels</p>
+					<p>@Lang.GuideTotalLevels</p>
+					<p>@Lang.GuideProgressRewards</p>
+					<p>@Lang.GuideFailLevel</p>
+					<p>@Lang.GuideRescueOption</p>
 
-                    <h3>@Lang.GuideLevelScoreTimeRewardTable</h3>
-                    <div class="guide-table-wrap">
-                        <table class="guide-table">
-                            <caption>@Lang.Easy @Lang.Mode</caption>
-                            <thead>
-                                <tr><th>@Lang.Level</th><th>@Lang.Score</th><th>@Lang.Time</th><th>@Lang.Rewards</th></tr>
-                            </thead>
-                            <tbody>
-                                <tr><td>1-2-3-4</td><td>10</td><td>150s</td><td>-</td></tr>
-                                <tr><td>5</td><td>-</td><td>150s</td><td>@Lang.GuideEasyReward5</td></tr>
-                                <tr><td>6-7-8-9</td><td>15</td><td>150s</td><td>-</td></tr>
-                                <tr><td>10</td><td>-</td><td>150s</td><td>@Lang.GuideEasyReward10</td></tr>
-                                <tr><td>11-12-13-14</td><td>20</td><td>150s</td><td>-</td></tr>
-                                <tr><td>15</td><td>-</td><td>150s</td><td>@Lang.GuideEasyReward15</td></tr>
-                                <tr><td>16-17-18-19</td><td>25</td><td>150s</td><td>-</td></tr>
-                                <tr><td>20</td><td>-</td><td>150s</td><td>@Lang.GuideEasyReward20</td></tr>
-                                <tr><td>21-22-23-24</td><td>35</td><td>150s</td><td>-</td></tr>
-                                <tr><td>25</td><td>-</td><td>150s</td><td>@Lang.GuideEasyReward25</td></tr>
-                                <tr><td>26-27-28-29</td><td>45</td><td>150s</td><td>-</td></tr>
-                                <tr><td>30</td><td>-</td><td>150s</td><td>@Lang.GuideEasyReward30</td></tr>
-                                <tr><td>31-32-33-34</td><td>55</td><td>150s</td><td>-</td></tr>
-                                <tr><td>35</td><td>-</td><td>150s</td><td>@Lang.GuideEasyReward35</td></tr>
-                                <tr><td>36-37-38-39</td><td>65</td><td>150s</td><td>-</td></tr>
-                                <tr><td>40</td><td>-</td><td>150s</td><td>@Lang.GuideEasyReward40</td></tr>
-                                <tr><td>41-42-43-44</td><td>80</td><td>150s</td><td>-</td></tr>
-                                <tr><td>45</td><td>-</td><td>150s</td><td>@Lang.GuideEasyReward45</td></tr>
-                                <tr><td>46-47-48-49</td><td>100</td><td>150s</td><td>-</td></tr>
-                                <tr><td>50</td><td>-</td><td>150s</td><td>@Lang.GuideEasyReward50</td></tr>
-                            </tbody>
-                        </table>
-                    </div>
+					<h3>@Lang.GuideLevelScoreTimeRewardTable</h3>
+					<div class="guide-table-wrap">
+						<table class="guide-table">
+							<caption>@Lang.Easy @Lang.Mode</caption>
+							<thead>
+								<tr><th>@Lang.Level</th><th>@Lang.Score</th><th>@Lang.Time</th><th>@Lang.Rewards</th></tr>
+							</thead>
+							<tbody>
+								<tr><td>1-2-3-4</td><td>10</td><td>150s</td><td>-</td></tr>
+								<tr><td>5</td><td>-</td><td>150s</td><td>@Lang.GuideEasyReward5</td></tr>
+								<tr><td>6-7-8-9</td><td>15</td><td>150s</td><td>-</td></tr>
+								<tr><td>10</td><td>-</td><td>150s</td><td>@Lang.GuideEasyReward10</td></tr>
+								<tr><td>11-12-13-14</td><td>20</td><td>150s</td><td>-</td></tr>
+								<tr><td>15</td><td>-</td><td>150s</td><td>@Lang.GuideEasyReward15</td></tr>
+								<tr><td>16-17-18-19</td><td>25</td><td>150s</td><td>-</td></tr>
+								<tr><td>20</td><td>-</td><td>150s</td><td>@Lang.GuideEasyReward20</td></tr>
+								<tr><td>21-22-23-24</td><td>35</td><td>150s</td><td>-</td></tr>
+								<tr><td>25</td><td>-</td><td>150s</td><td>@Lang.GuideEasyReward25</td></tr>
+								<tr><td>26-27-28-29</td><td>45</td><td>150s</td><td>-</td></tr>
+								<tr><td>30</td><td>-</td><td>150s</td><td>@Lang.GuideEasyReward30</td></tr>
+								<tr><td>31-32-33-34</td><td>55</td><td>150s</td><td>-</td></tr>
+								<tr><td>35</td><td>-</td><td>150s</td><td>@Lang.GuideEasyReward35</td></tr>
+								<tr><td>36-37-38-39</td><td>65</td><td>150s</td><td>-</td></tr>
+								<tr><td>40</td><td>-</td><td>150s</td><td>@Lang.GuideEasyReward40</td></tr>
+								<tr><td>41-42-43-44</td><td>80</td><td>150s</td><td>-</td></tr>
+								<tr><td>45</td><td>-</td><td>150s</td><td>@Lang.GuideEasyReward45</td></tr>
+								<tr><td>46-47-48-49</td><td>100</td><td>150s</td><td>-</td></tr>
+								<tr><td>50</td><td>-</td><td>150s</td><td>@Lang.GuideEasyReward50</td></tr>
+							</tbody>
+						</table>
+					</div>
 
-                    <div class="guide-table-wrap">
-                        <table class="guide-table is-compact">
-                            <caption>@Lang.Medium @Lang.Mode</caption>
-                            <thead>
-                                <tr><th>@Lang.Level</th><th>@Lang.Score</th><th>@Lang.Time</th><th>@Lang.Rewards</th></tr>
-                            </thead>
-                            <tbody>
-                                <tr><td>1-2-3-4</td><td>20</td><td>100s</td><td>-</td></tr>
-                                <tr><td>5</td><td>-</td><td>100s</td><td>@Lang.GuideMediumReward5</td></tr>
-                                <tr><td>6-7-8-9</td><td>25</td><td>100s</td><td>-</td></tr>
-                                <tr><td>10</td><td>-</td><td>100s</td><td>@Lang.GuideMediumReward10</td></tr>
-                                <tr><td>11-12-13-14</td><td>30</td><td>100s</td><td>-</td></tr>
-                                <tr><td>15</td><td>-</td><td>100s</td><td>@Lang.GuideMediumReward15</td></tr>
-                                <tr><td>16-17-18-19</td><td>35</td><td>100s</td><td>-</td></tr>
-                                <tr><td>20</td><td>-</td><td>100s</td><td>@Lang.GuideMediumReward20</td></tr>
-                                <tr><td>21-22-23-24</td><td>40</td><td>100s</td><td>-</td></tr>
-                                <tr><td>25</td><td>-</td><td>100s</td><td>@Lang.GuideMediumReward25</td></tr>
-                                <tr><td>26-27-28-29</td><td>50</td><td>100s</td><td>-</td></tr>
-                                <tr><td>30</td><td>-</td><td>100s</td><td>@Lang.GuideMediumReward30</td></tr>
-                                <tr><td>31-32-33-34</td><td>60</td><td>100s</td><td>-</td></tr>
-                                <tr><td>35</td><td>-</td><td>100s</td><td>@Lang.GuideMediumReward35</td></tr>
-                                <tr><td>36-37-38-39</td><td>70</td><td>100s</td><td>-</td></tr>
-                                <tr><td>40</td><td>-</td><td>100s</td><td>@Lang.GuideMediumReward40</td></tr>
-                                <tr><td>41-42-43-44</td><td>80</td><td>100s</td><td>-</td></tr>
-                                <tr><td>45</td><td>-</td><td>100s</td><td>@Lang.GuideMediumReward45</td></tr>
-                                <tr><td>46-47-48-49</td><td>100</td><td>100s</td><td>-</td></tr>
-                                <tr><td>50</td><td>-</td><td>100s</td><td>@Lang.GuideMediumReward50</td></tr>
-                            </tbody>
-                        </table>
-                    </div>
+					<div class="guide-table-wrap">
+						<table class="guide-table is-compact">
+							<caption>@Lang.Medium @Lang.Mode</caption>
+							<thead>
+								<tr><th>@Lang.Level</th><th>@Lang.Score</th><th>@Lang.Time</th><th>@Lang.Rewards</th></tr>
+							</thead>
+							<tbody>
+								<tr><td>1-2-3-4</td><td>20</td><td>100s</td><td>-</td></tr>
+								<tr><td>5</td><td>-</td><td>100s</td><td>@Lang.GuideMediumReward5</td></tr>
+								<tr><td>6-7-8-9</td><td>25</td><td>100s</td><td>-</td></tr>
+								<tr><td>10</td><td>-</td><td>100s</td><td>@Lang.GuideMediumReward10</td></tr>
+								<tr><td>11-12-13-14</td><td>30</td><td>100s</td><td>-</td></tr>
+								<tr><td>15</td><td>-</td><td>100s</td><td>@Lang.GuideMediumReward15</td></tr>
+								<tr><td>16-17-18-19</td><td>35</td><td>100s</td><td>-</td></tr>
+								<tr><td>20</td><td>-</td><td>100s</td><td>@Lang.GuideMediumReward20</td></tr>
+								<tr><td>21-22-23-24</td><td>40</td><td>100s</td><td>-</td></tr>
+								<tr><td>25</td><td>-</td><td>100s</td><td>@Lang.GuideMediumReward25</td></tr>
+								<tr><td>26-27-28-29</td><td>50</td><td>100s</td><td>-</td></tr>
+								<tr><td>30</td><td>-</td><td>100s</td><td>@Lang.GuideMediumReward30</td></tr>
+								<tr><td>31-32-33-34</td><td>60</td><td>100s</td><td>-</td></tr>
+								<tr><td>35</td><td>-</td><td>100s</td><td>@Lang.GuideMediumReward35</td></tr>
+								<tr><td>36-37-38-39</td><td>70</td><td>100s</td><td>-</td></tr>
+								<tr><td>40</td><td>-</td><td>100s</td><td>@Lang.GuideMediumReward40</td></tr>
+								<tr><td>41-42-43-44</td><td>80</td><td>100s</td><td>-</td></tr>
+								<tr><td>45</td><td>-</td><td>100s</td><td>@Lang.GuideMediumReward45</td></tr>
+								<tr><td>46-47-48-49</td><td>100</td><td>100s</td><td>-</td></tr>
+								<tr><td>50</td><td>-</td><td>100s</td><td>@Lang.GuideMediumReward50</td></tr>
+							</tbody>
+						</table>
+					</div>
 
-                    <div class="guide-table-wrap">
-                        <table class="guide-table is-compact">
-                            <caption>@Lang.Hard @Lang.Mode</caption>
-                            <thead>
-                                <tr><th>@Lang.Level</th><th>@Lang.Score</th><th>@Lang.Time</th><th>@Lang.Rewards</th></tr>
-                            </thead>
-                            <tbody>
-                                <tr><td>1-2-3-4</td><td>40</td><td>60s</td><td>-</td></tr>
-                                <tr><td>5</td><td>-</td><td>60s</td><td>@Lang.GuideHardReward5</td></tr>
-                                <tr><td>6-7-8-9</td><td>50</td><td>60s</td><td>-</td></tr>
-                                <tr><td>10</td><td>-</td><td>60s</td><td>@Lang.GuideHardReward10</td></tr>
-                                <tr><td>11-12-13-14</td><td>60</td><td>60s</td><td>-</td></tr>
-                                <tr><td>15</td><td>-</td><td>60s</td><td>@Lang.GuideHardReward15</td></tr>
-                                <tr><td>16-17-18-19</td><td>70</td><td>60s</td><td>-</td></tr>
-                                <tr><td>20</td><td>-</td><td>60s</td><td>@Lang.GuideHardReward20</td></tr>
-                                <tr><td>21-22-23-24</td><td>85</td><td>60s</td><td>-</td></tr>
-                                <tr><td>25</td><td>-</td><td>60s</td><td>@Lang.GuideHardReward25</td></tr>
-                                <tr><td>26-27-28-29</td><td>100</td><td>60s</td><td>-</td></tr>
-                                <tr><td>30</td><td>-</td><td>60s</td><td>@Lang.GuideHardReward30</td></tr>
-                                <tr><td>31-32-33-34</td><td>120</td><td>60s</td><td>-</td></tr>
-                                <tr><td>35</td><td>-</td><td>60s</td><td>@Lang.GuideHardReward35</td></tr>
-                                <tr><td>36-37-38-39</td><td>140</td><td>60s</td><td>-</td></tr>
-                                <tr><td>40</td><td>-</td><td>60s</td><td>@Lang.GuideHardReward40</td></tr>
-                                <tr><td>41-42-43-44</td><td>160</td><td>60s</td><td>-</td></tr>
-                                <tr><td>45</td><td>-</td><td>60s</td><td>@Lang.GuideHardReward45</td></tr>
-                                <tr><td>46-47-48-49</td><td>180</td><td>60s</td><td>-</td></tr>
-                                <tr><td>50</td><td>-</td><td>60s</td><td>@Lang.GuideHardReward50</td></tr>
-                            </tbody>
-                        </table>
-                    </div>
-                </section>
+					<div class="guide-table-wrap">
+						<table class="guide-table is-compact">
+							<caption>@Lang.Hard @Lang.Mode</caption>
+							<thead>
+								<tr><th>@Lang.Level</th><th>@Lang.Score</th><th>@Lang.Time</th><th>@Lang.Rewards</th></tr>
+							</thead>
+							<tbody>
+								<tr><td>1-2-3-4</td><td>40</td><td>60s</td><td>-</td></tr>
+								<tr><td>5</td><td>-</td><td>60s</td><td>@Lang.GuideHardReward5</td></tr>
+								<tr><td>6-7-8-9</td><td>50</td><td>60s</td><td>-</td></tr>
+								<tr><td>10</td><td>-</td><td>60s</td><td>@Lang.GuideHardReward10</td></tr>
+								<tr><td>11-12-13-14</td><td>60</td><td>60s</td><td>-</td></tr>
+								<tr><td>15</td><td>-</td><td>60s</td><td>@Lang.GuideHardReward15</td></tr>
+								<tr><td>16-17-18-19</td><td>70</td><td>60s</td><td>-</td></tr>
+								<tr><td>20</td><td>-</td><td>60s</td><td>@Lang.GuideHardReward20</td></tr>
+								<tr><td>21-22-23-24</td><td>85</td><td>60s</td><td>-</td></tr>
+								<tr><td>25</td><td>-</td><td>60s</td><td>@Lang.GuideHardReward25</td></tr>
+								<tr><td>26-27-28-29</td><td>100</td><td>60s</td><td>-</td></tr>
+								<tr><td>30</td><td>-</td><td>60s</td><td>@Lang.GuideHardReward30</td></tr>
+								<tr><td>31-32-33-34</td><td>120</td><td>60s</td><td>-</td></tr>
+								<tr><td>35</td><td>-</td><td>60s</td><td>@Lang.GuideHardReward35</td></tr>
+								<tr><td>36-37-38-39</td><td>140</td><td>60s</td><td>-</td></tr>
+								<tr><td>40</td><td>-</td><td>60s</td><td>@Lang.GuideHardReward40</td></tr>
+								<tr><td>41-42-43-44</td><td>160</td><td>60s</td><td>-</td></tr>
+								<tr><td>45</td><td>-</td><td>60s</td><td>@Lang.GuideHardReward45</td></tr>
+								<tr><td>46-47-48-49</td><td>180</td><td>60s</td><td>-</td></tr>
+								<tr><td>50</td><td>-</td><td>60s</td><td>@Lang.GuideHardReward50</td></tr>
+							</tbody>
+						</table>
+					</div>
+				</section>
 
-                <section class="guide-section">
-                    <h2><b>4</b><span>@Lang.Rewards</span></h2>
-                    <p><strong>@Lang.GuideMonthlyRewardEligibility</strong><br>@Lang.GuidePlayersRequired</p>
-                    <div class="guide-table-wrap">
-                        <table class="guide-table is-reward">
-                            <thead>
-                                <tr><th>@Lang.GuideConditions</th><th>@Lang.GuideValue</th></tr>
-                            </thead>
-                            <tbody>
-                                <tr><td>@Lang.GuideTotalScore</td><td>@Html.Raw(Lang.GuideTotalScoreCondition)</td></tr>
-                            </tbody>
-                        </table>
-                    </div>
-                    <p>@Lang.GuideMonthlyRewardChance</p>
-                    <p><strong>Monthly Prize Table</strong></p>
-                    <div class="guide-table-wrap">
-                        <table class="guide-table is-reward">
-                            <thead>
-                                <tr><th>Prize</th><th>Quantity</th><th>@Lang.Rewards</th></tr>
-                            </thead>
-                            <tbody>
-                                <tr><td>@Lang.FirstPrize</td><td>1</td><td>20,000 HTG</td></tr>
-                                <tr><td>@Lang.SecondPrize</td><td>1</td><td>10,000 HTG</td></tr>
-                                <tr><td>@Lang.ThirdPrize</td><td>2</td><td>5,000 HTG</td></tr>
-                                <tr><td>Fourth Prize</td><td>5</td><td>2,000 HTG</td></tr>
-                                <tr><td>Fifth Prize</td><td>10</td><td>1,000 HTG</td></tr>
-                            </tbody>
-                        </table>
-                    </div>
-                    <ul>
-                        <li>@Lang.GuideRewardDistributionMechanism</li>
-                        <li>@Lang.GuideEndOfMonth</li>
-                        <li>@Lang.GuideCollectEligibleUsers</li>
-                        <li>@Lang.GuideRandomDraw</li>
-                        <li>@Lang.GuideTicketsByPoint</li>
-                    </ul>
+				<section class="guide-section">
+					<h2><b>4</b><span>@Lang.Rewards</span></h2>
+					<p><strong>@Lang.GuideMonthlyRewardEligibility</strong><br>@Lang.GuidePlayersRequired</p>
+					<div class="guide-table-wrap">
+						<table class="guide-table is-reward">
+							<thead>
+								<tr><th>@Lang.GuideConditions</th><th>@Lang.GuideValue</th></tr>
+							</thead>
+							<tbody>
+								<tr><td>@Lang.GuideTotalScore</td><td>@Html.Raw(Lang.GuideTotalScoreCondition)</td></tr>
+							</tbody>
+						</table>
+					</div>
+					<p>@Lang.GuideMonthlyRewardChance</p>
+					<p><strong>Monthly Prize Table</strong></p>
+					<div class="guide-table-wrap">
+						<table class="guide-table is-reward">
+							<thead>
+								<tr><th>Prize</th><th>Quantity</th><th>@Lang.Rewards</th></tr>
+							</thead>
+							<tbody>
+								<tr><td>@Lang.FirstPrize</td><td>1</td><td>20,000 HTG</td></tr>
+								<tr><td>@Lang.SecondPrize</td><td>1</td><td>10,000 HTG</td></tr>
+								<tr><td>@Lang.ThirdPrize</td><td>2</td><td>5,000 HTG</td></tr>
+								<tr><td>Fourth Prize</td><td>5</td><td>2,000 HTG</td></tr>
+								<tr><td>Fifth Prize</td><td>10</td><td>1,000 HTG</td></tr>
+							</tbody>
+						</table>
+					</div>
+					<ul>
+						<li>@Lang.GuideRewardDistributionMechanism</li>
+						<li>@Lang.GuideEndOfMonth</li>
+						<li>@Lang.GuideCollectEligibleUsers</li>
+						<li>@Lang.GuideRandomDraw</li>
+						<li>@Lang.GuideTicketsByPoint</li>
+					</ul>
 
-                    <div class="guide-winner-card">
-                        <img src="~/kitty/assets/kitty-fall/winner-monthly.png" alt="">
-                        <div>
-                            <h4>@Lang.MonthlyWinners</h4>
-                            <p>@Lang.MonthlyWinnersDescription</p>
-                            <a href="/MonthlyWinners">@Lang.ViewMonthlyWinners</a>
-                        </div>
-                    </div>
+					<div class="guide-winner-card">
+						<img src="~/kitty/assets/kitty-fall/winner-monthly.png" alt="">
+						<div>
+							<h4>@Lang.MonthlyWinners</h4>
+							<p>@Lang.MonthlyWinnersDescription</p>
+							<a href="/MonthlyWinners">@Lang.ViewMonthlyWinners</a>
+						</div>
+					</div>
 
-                    <p><strong>@Lang.GuideDrawTicketsBasedOnPoints</strong></p>
-                    <div class="guide-table-wrap">
-                        <table class="guide-table is-reward">
-                            <thead>
-                                <tr><th>@Lang.GuideTotalScoreMonth</th><th>@Lang.GuideNumberOfTicket</th></tr>
-                            </thead>
-                            <tbody>
-                                <tr><td>5,000 - 5,999</td><td>@Lang.GuideOneTicket</td></tr>
-                                <tr><td>6,000 - 6,999</td><td>@Lang.GuideTwoTickets</td></tr>
-                                <tr><td>7,000 - 7,999</td><td>@Lang.GuideThreeTickets</td></tr>
-                                <tr><td>8,000 - 8,999</td><td>@Lang.GuideFiveTickets</td></tr>
-                                <tr><td>9,000 - 9,999</td><td>@Lang.GuideSevenTickets</td></tr>
-                                <tr><td>&ge; 10,000</td><td>@Lang.GuideTenTickets</td></tr>
-                            </tbody>
-                        </table>
-                    </div>
-                    <p class="guide-note">@Lang.GuideMonthlyAnnouncement</p>
-                    <p class="guide-note">@Lang.GuideMonthlyPrizeLimit</p>
-                </section>
-            </div>
-        </article>
-    </section>
+					<p><strong>@Lang.GuideDrawTicketsBasedOnPoints</strong></p>
+					<div class="guide-table-wrap">
+						<table class="guide-table is-reward">
+							<thead>
+								<tr><th>@Lang.GuideTotalScoreMonth</th><th>@Lang.GuideNumberOfTicket</th></tr>
+							</thead>
+							<tbody>
+								<tr><td>5,000 - 5,999</td><td>@Lang.GuideOneTicket</td></tr>
+								<tr><td>6,000 - 6,999</td><td>@Lang.GuideTwoTickets</td></tr>
+								<tr><td>7,000 - 7,999</td><td>@Lang.GuideThreeTickets</td></tr>
+								<tr><td>8,000 - 8,999</td><td>@Lang.GuideFiveTickets</td></tr>
+								<tr><td>9,000 - 9,999</td><td>@Lang.GuideSevenTickets</td></tr>
+								<tr><td>&ge; 10,000</td><td>@Lang.GuideTenTickets</td></tr>
+							</tbody>
+						</table>
+					</div>
+					<p class="guide-note">@Lang.GuideMonthlyAnnouncement</p>
+					<p class="guide-note">@Lang.GuideMonthlyPrizeLimit</p>
+				</section>
+			</div>
+		</article>
+	</section>
 
 </main>

+ 115 - 115
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/History/Index.cshtml

@@ -1,128 +1,128 @@
 @model Common.Http.Apis.Request.UserGameActivityHistoryData
 @{
-    ViewData["Title"] = "Kitty Fall - History";
-    var profile = Model?.profile ?? new Common.Http.Apis.Request.UserGameActivityProfile();
-    var profilePicture = string.IsNullOrWhiteSpace(profile.picture)
-        ? Url.Content("~/kitty/assets/kitty-fall/default.png")
-        : Url.Content("~/kitty/assets/kitty-fall/") + profile.picture;
-    var pageNumber = Model?.pageNumber > 0 ? Model.pageNumber : 1;
-    var totalPage = Model?.totalPage ?? 0;
-    var fromDate = Model?.fromDate ?? "";
-    var toDate = Model?.toDate ?? "";
-    var minHistoryDate = DateTime.Now.Date.AddDays(-30).ToString("yyyy-MM-dd");
-    var maxHistoryDate = DateTime.Now.Date.ToString("yyyy-MM-dd");
-    var startPage = totalPage <= 5 ? 1 : Math.Max(1, Math.Min(pageNumber - 2, totalPage - 4));
-    var endPage = totalPage <= 5 ? totalPage : Math.Min(totalPage, startPage + 4);
+	ViewData["Title"] = "Kitty Fall - History";
+	var profile = Model?.profile ?? new Common.Http.Apis.Request.UserGameActivityProfile();
+	var profilePicture = string.IsNullOrWhiteSpace(profile.picture)
+		? Url.Content("~/kitty/assets/kitty-fall/default.png")
+		: Url.Content("~/kitty/assets/kitty-fall/") + profile.picture;
+	var pageNumber = Model?.pageNumber > 0 ? Model.pageNumber : 1;
+	var totalPage = Model?.totalPage ?? 0;
+	var fromDate = Model?.fromDate ?? "";
+	var toDate = Model?.toDate ?? "";
+	var minHistoryDate = DateTime.Now.Date.AddDays(-30).ToString("yyyy-MM-dd");
+	var maxHistoryDate = DateTime.Now.Date.ToString("yyyy-MM-dd");
+	var startPage = totalPage <= 5 ? 1 : Math.Max(1, Math.Min(pageNumber - 2, totalPage - 4));
+	var endPage = totalPage <= 5 ? totalPage : Math.Min(totalPage, startPage + 4);
 }
 
 <main class="kitty-app is-winner-screen is-history-screen" aria-label="Kitty Fall history screen">
-    <img class="kitty-bg" src="~/kitty/assets/kitty-fall/bg.png" alt="">
-    <div class="kitty-bg-fade"></div>
+	<img class="kitty-bg" src="~/kitty/assets/kitty-fall/bg.png" alt="">
+	<div class="kitty-bg-fade"></div>
 
-    <header class="topbar">
-        <div class="profile">
-            <img class="profile-avatar" src="@profilePicture" alt="Player avatar">
-            <div class="profile-copy">
-                <strong>@Lang.Hello</strong>
-                <span>@profile.msisdn</span>
-            </div>
-        </div>
-        <div class="play-top-stats" aria-label="Player status">
-            <span class="play-top-chip">
-                <img src="~/kitty/assets/kitty-fall/coin.png" alt="">
-                <strong>@profile.totalPoint.ToString("N0")</strong>
-            </span>
-            <span class="play-top-chip">
-                <img src="~/kitty/assets/kitty-fall/ticket.png" alt="">
-                <strong>@profile.totalTurn.ToString("N0")</strong>
-                <b>+</b>
-            </span>
-        </div>
-        <button class="menu-button" aria-label="Open menu">
-            <span></span>
-            <span></span>
-            <span></span>
-        </button>
-    </header>
+	<header class="topbar">
+		<div class="profile">
+			<img class="profile-avatar" src="@profilePicture" alt="Player avatar">
+			<div class="profile-copy">
+				<strong>@Lang.Hello</strong>
+				<span>@profile.msisdn</span>
+			</div>
+		</div>
+		<div class="play-top-stats" aria-label="Player status">
+			<span class="play-top-chip">
+				<img src="~/kitty/assets/kitty-fall/coin.png" alt="">
+				<strong>@profile.totalPoint.ToString("N0")</strong>
+			</span>
+			<span class="play-top-chip">
+				<img src="~/kitty/assets/kitty-fall/ticket.png" alt="">
+				<strong>@profile.totalTurn.ToString("N0")</strong>
+				<b>+</b>
+			</span>
+		</div>
+		<button class="menu-button" aria-label="Open menu">
+			<span></span>
+			<span></span>
+			<span></span>
+		</button>
+	</header>
 
-    <section class="history-screen" aria-label="Game activity history">
-        <div class="history-panel">
-            <h1 class="history-title">
-                <img src="~/kitty/assets/kitty-fall/nav-history.png" alt="">
-                <span>@Lang.YourGameActivity</span>
-            </h1>
+	<section class="history-screen" aria-label="Game activity history">
+		<div class="history-panel">
+			<h1 class="history-title">
+				<img src="~/kitty/assets/kitty-fall/nav-history.png" alt="">
+				<span>@Lang.YourGameActivity</span>
+			</h1>
 
-            <form class="history-filter" aria-label="History date range" method="get" action="@Url.Action("Index", "History")">
-                <label data-calendar-picker data-selected-date="@fromDate" data-min-date="@minHistoryDate" data-max-date="@toDate">
-                    <img src="~/kitty/assets/kitty-fall/icon-calendar.png" alt="">
-                    <strong>@Model?.fromDateText</strong>
-                    <input type="hidden" name="fromDate" value="@fromDate" data-calendar-value>
-                    <img src="~/kitty/assets/kitty-fall/icon-chevron-down.png" alt="">
-                </label>
-                <span>to</span>
-                <label data-calendar-picker data-selected-date="@toDate" data-min-date="@fromDate" data-max-date="@maxHistoryDate">
-                    <img src="~/kitty/assets/kitty-fall/icon-calendar.png" alt="">
-                    <strong>@Model?.toDateText</strong>
-                    <input type="hidden" name="toDate" value="@toDate" data-calendar-value>
-                    <img src="~/kitty/assets/kitty-fall/icon-chevron-down.png" alt="">
-                </label>
-                <input type="hidden" name="pageNumber" value="1">
-            </form>
+			<form class="history-filter" aria-label="History date range" method="get" action="@Url.Action("Index", "History")">
+				<label data-calendar-picker data-selected-date="@fromDate" data-min-date="@minHistoryDate" data-max-date="@toDate">
+					<img src="~/kitty/assets/kitty-fall/icon-calendar.png" alt="">
+					<strong>@Model?.fromDateText</strong>
+					<input type="hidden" name="fromDate" value="@fromDate" data-calendar-value>
+					<img src="~/kitty/assets/kitty-fall/icon-chevron-down.png" alt="">
+				</label>
+				<span>to</span>
+				<label data-calendar-picker data-selected-date="@toDate" data-min-date="@fromDate" data-max-date="@maxHistoryDate">
+					<img src="~/kitty/assets/kitty-fall/icon-calendar.png" alt="">
+					<strong>@Model?.toDateText</strong>
+					<input type="hidden" name="toDate" value="@toDate" data-calendar-value>
+					<img src="~/kitty/assets/kitty-fall/icon-chevron-down.png" alt="">
+				</label>
+				<input type="hidden" name="pageNumber" value="1">
+			</form>
 
-            <div class="history-table">
-                <div class="history-table-scroll">
-                    <div class="history-table-inner">
-                        <div class="history-table-head">
-                            <span>@Lang.Date</span>
-                            <span>@Lang.Activity</span>
-                            <span>@Lang.Detail</span>
-                            <span><img src="~/kitty/assets/kitty-fall/nav-winner.png" alt="">@Lang.Prize</span>
-                        </div>
+			<div class="history-table">
+				<div class="history-table-scroll">
+					<div class="history-table-inner">
+						<div class="history-table-head">
+							<span>@Lang.Date</span>
+							<span>@Lang.Activity</span>
+							<span>@Lang.Detail</span>
+							<span><img src="~/kitty/assets/kitty-fall/nav-winner.png" alt="">@Lang.Prize</span>
+						</div>
 
-                        <div class="history-table-body">
-                            @if (Model?.items != null && Model.items.Count > 0)
-                            {
-                                foreach (var item in Model.items)
-                                {
-                                    <div class="history-row">
-                                        <span><b>@item.dateText</b><small>@item.timeText</small></span>
-                                        <span><b>@item.activity</b><small class="@item.modeCssClass">@item.modeName</small></span>
-                                        <span>@item.detail</span>
-                                        <div class="history-reward-summary" aria-label="@item.moneyText HTG, @item.scoreText score">
-                                            <span class="history-reward-value is-money">
-                                                <img src="~/kitty/assets/kitty-fall/coin.png" alt="">
-                                                <b>@item.moneyText</b>
-                                                <small>HTG</small>
-                                            </span>
-                                            <span class="history-reward-value is-score">
-                                                <img src="~/kitty/assets/kitty-fall/nav-winner.png" alt="">
-                                                <b>@item.scoreText</b>
-                                                <small>@Lang.Score</small>
-                                            </span>
-                                        </div>
-                                    </div>
-                                }
-                            }
-                        </div>
-                    </div>
-                </div>
+						<div class="history-table-body">
+							@if (Model?.items != null && Model.items.Count > 0)
+							{
+								foreach (var item in Model.items)
+								{
+									<div class="history-row">
+										<span><b>@item.dateText</b><small>@item.timeText</small></span>
+										<span><b>@item.activity</b><small class="@item.modeCssClass">@item.modeName</small></span>
+										<span>@item.detail</span>
+										<div class="history-reward-summary" aria-label="@item.moneyText HTG, @item.scoreText score">
+											<span class="history-reward-value is-money">
+												<img src="~/kitty/assets/kitty-fall/coin.png" alt="">
+												<b>@item.moneyText</b>
+												<small>HTG</small>
+											</span>
+											<span class="history-reward-value is-score">
+												<img src="~/kitty/assets/kitty-fall/nav-winner.png" alt="">
+												<b>@item.scoreText</b>
+												<small>@Lang.Score</small>
+											</span>
+										</div>
+									</div>
+								}
+							}
+						</div>
+					</div>
+				</div>
 
-                <p class="history-note"><span>i</span>@Lang.HistoryStoredLast30Days</p>
+				<p class="history-note"><span>i</span>@Lang.HistoryStoredLast30Days</p>
 
-                <div class="history-pages" aria-label="History pages">
-                    <a class="history-page-arrow is-prev @(pageNumber <= 1 ? "is-disabled" : "")"
-                       href="@Url.Action("Index", "History", new { fromDate, toDate, pageNumber = Math.Max(1, pageNumber - 1) })"
-                       aria-label="Previous page"></a>
-                    @for (var pageNo = startPage; pageNo <= endPage; pageNo++)
-                    {
-                        <a class="@(pageNo == pageNumber ? "active" : "")"
-                           href="@Url.Action("Index", "History", new { fromDate, toDate, pageNumber = pageNo })">@pageNo</a>
-                    }
-                    <a class="history-page-arrow is-next @(totalPage == 0 || pageNumber >= totalPage ? "is-disabled" : "")"
-                       href="@Url.Action("Index", "History", new { fromDate, toDate, pageNumber = totalPage == 0 ? 1 : Math.Min(totalPage, pageNumber + 1) })"
-                       aria-label="Next page"></a>
-                </div>
-            </div>
-        </div>
-    </section>
+				<div class="history-pages" aria-label="History pages">
+					<a class="history-page-arrow is-prev @(pageNumber <= 1 ? "is-disabled" : "")"
+					   href="@Url.Action("Index", "History", new { fromDate, toDate, pageNumber = Math.Max(1, pageNumber - 1) })"
+					   aria-label="Previous page"></a>
+					@for (var pageNo = startPage; pageNo <= endPage; pageNo++)
+					{
+						<a class="@(pageNo == pageNumber ? "active" : "")"
+						   href="@Url.Action("Index", "History", new { fromDate, toDate, pageNumber = pageNo })">@pageNo</a>
+					}
+					<a class="history-page-arrow is-next @(totalPage == 0 || pageNumber >= totalPage ? "is-disabled" : "")"
+					   href="@Url.Action("Index", "History", new { fromDate, toDate, pageNumber = totalPage == 0 ? 1 : Math.Min(totalPage, pageNumber + 1) })"
+					   aria-label="Next page"></a>
+				</div>
+			</div>
+		</div>
+	</section>
 </main>

+ 401 - 401
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Home/Index.cshtml

@@ -1,409 +1,409 @@
 @{
-    ViewData["Title"] = "Kitty Fall";
-    var msisdn = ViewBag.Msisdn as string;
-    var profilePicture = ViewBag.ProfilePicture as string;
-    var avatarUrl = string.IsNullOrWhiteSpace(profilePicture) ? Url.Content("~/kitty/assets/kitty-fall/default.png") : Url.Content("~/kitty/assets/kitty-fall/") + profilePicture;
-    var totalPoint = ViewBag.TotalPoint is null ? 0L : Convert.ToInt64(ViewBag.TotalPoint);
-    var totalTurn = ViewBag.TotalTurn is null ? 0L : Convert.ToInt64(ViewBag.TotalTurn);
-    var packageFee = ViewBag.PackageFee is null ? 179M : Convert.ToDecimal(ViewBag.PackageFee);
-    var packageTurns = ViewBag.PackageTurns is null ? 5 : Convert.ToInt32(ViewBag.PackageTurns);
-    var packageExpireDays = ViewBag.PackageExpireDays is null ? 1 : Convert.ToInt32(ViewBag.PackageExpireDays);
-    var packageProductName = ViewBag.PackageProductName as string ?? "";
-    var packagePeriodText = packageExpireDays <= 1 ? "Day" : $"{packageExpireDays} Days";
-    var packageTurnText = packageTurns <= 1 ? "Turn" : "Turns";
-    var isSubscriber = Context.Session.IsSubscriber();
-    var packageAction = isSubscriber ? "buy-turn" : "subscribe";
-    var autoOpenSubscriptionConfirm = (ViewBag.AutoOpenSubscriptionConfirm as bool?) == true && !isSubscriber;
-    // CHARGE is a one-time turn purchase, so it must be available even when
-    // the player does not have an active DAILY/WEEKLY subscription.
-    var autoOpenBuyTurn = (ViewBag.AutoOpenBuyTurn as bool?) == true;
-    var subscriptionPackages = ViewBag.SubscriptionPackages as List<Dictionary<string, object?>> ?? new List<Dictionary<string, object?>>();
-    var subscriptionFees = subscriptionPackages
-        .Select(p => p.TryGetValue("fee", out var value) && decimal.TryParse(value?.ToString(), out var fee) ? fee : 0M)
-        .Where(fee => fee > 0)
-        .ToList();
-    var lowestSubscriptionFee = subscriptionFees.Count > 0 ? subscriptionFees.Min() : packageFee;
-    var turnPackages = ViewBag.TurnPackages as List<Dictionary<string, object?>> ?? new List<Dictionary<string, object?>>();
-    var firstTurnPackage = turnPackages.FirstOrDefault();
-    var buyTurnFee = firstTurnPackage != null && firstTurnPackage.TryGetValue("fee", out var buyTurnFeeValue) && decimal.TryParse(buyTurnFeeValue?.ToString(), out var parsedBuyTurnFee)
-        ? parsedBuyTurnFee
-        : packageFee;
-    var buyTurnCount = firstTurnPackage != null && firstTurnPackage.TryGetValue("numberSpin", out var buyTurnCountValue) && int.TryParse(buyTurnCountValue?.ToString(), out var parsedBuyTurnCount)
-        ? parsedBuyTurnCount
-        : packageTurns;
-    var buyTurnText = buyTurnCount <= 1 ? "Turn" : "Turns";
+	ViewData["Title"] = "Kitty Fall";
+	var msisdn = ViewBag.Msisdn as string;
+	var profilePicture = ViewBag.ProfilePicture as string;
+	var avatarUrl = string.IsNullOrWhiteSpace(profilePicture) ? Url.Content("~/kitty/assets/kitty-fall/default.png") : Url.Content("~/kitty/assets/kitty-fall/") + profilePicture;
+	var totalPoint = ViewBag.TotalPoint is null ? 0L : Convert.ToInt64(ViewBag.TotalPoint);
+	var totalTurn = ViewBag.TotalTurn is null ? 0L : Convert.ToInt64(ViewBag.TotalTurn);
+	var packageFee = ViewBag.PackageFee is null ? 179M : Convert.ToDecimal(ViewBag.PackageFee);
+	var packageTurns = ViewBag.PackageTurns is null ? 5 : Convert.ToInt32(ViewBag.PackageTurns);
+	var packageExpireDays = ViewBag.PackageExpireDays is null ? 1 : Convert.ToInt32(ViewBag.PackageExpireDays);
+	var packageProductName = ViewBag.PackageProductName as string ?? "";
+	var packagePeriodText = packageExpireDays <= 1 ? "Day" : $"{packageExpireDays} Days";
+	var packageTurnText = packageTurns <= 1 ? "Turn" : "Turns";
+	var isSubscriber = Context.Session.IsSubscriber();
+	var packageAction = isSubscriber ? "buy-turn" : "subscribe";
+	var autoOpenSubscriptionConfirm = (ViewBag.AutoOpenSubscriptionConfirm as bool?) == true && !isSubscriber;
+	// CHARGE is a one-time turn purchase, so it must be available even when
+	// the player does not have an active DAILY/WEEKLY subscription.
+	var autoOpenBuyTurn = (ViewBag.AutoOpenBuyTurn as bool?) == true;
+	var subscriptionPackages = ViewBag.SubscriptionPackages as List<Dictionary<string, object?>> ?? new List<Dictionary<string, object?>>();
+	var subscriptionFees = subscriptionPackages
+		.Select(p => p.TryGetValue("fee", out var value) && decimal.TryParse(value?.ToString(), out var fee) ? fee : 0M)
+		.Where(fee => fee > 0)
+		.ToList();
+	var lowestSubscriptionFee = subscriptionFees.Count > 0 ? subscriptionFees.Min() : packageFee;
+	var turnPackages = ViewBag.TurnPackages as List<Dictionary<string, object?>> ?? new List<Dictionary<string, object?>>();
+	var firstTurnPackage = turnPackages.FirstOrDefault();
+	var buyTurnFee = firstTurnPackage != null && firstTurnPackage.TryGetValue("fee", out var buyTurnFeeValue) && decimal.TryParse(buyTurnFeeValue?.ToString(), out var parsedBuyTurnFee)
+		? parsedBuyTurnFee
+		: packageFee;
+	var buyTurnCount = firstTurnPackage != null && firstTurnPackage.TryGetValue("numberSpin", out var buyTurnCountValue) && int.TryParse(buyTurnCountValue?.ToString(), out var parsedBuyTurnCount)
+		? parsedBuyTurnCount
+		: packageTurns;
+	var buyTurnText = buyTurnCount <= 1 ? "Turn" : "Turns";
 }
 
 <main class="kitty-app" aria-label="Kitty Fall home screen">
-    <img class="kitty-bg" src="~/kitty/assets/kitty-fall/bg.png" alt="">
-    <div class="kitty-bg-fade"></div>
-
-    <header class="topbar">
-        <div class="profile">
-            <button class="home-back-button" type="button" aria-label="Back" onclick="logoutAndBack()">
-                <span></span>
-            </button>
-            <img class="profile-avatar" src="@avatarUrl" alt="Player avatar">
-            <div class="profile-copy">
-                <strong>@Lang.Hello</strong>
-                <span>@(string.IsNullOrWhiteSpace(msisdn) ? "-" : msisdn)</span>
-            </div>
-        </div>
-        <div class="topbar-actions">
-            <button class="menu-button" type="button" aria-label="Open menu">
-                <span></span>
-                <span></span>
-                <span></span>
-            </button>
-        </div>
-    </header>
-
-    <section class="stats-shell home-screen scale-floor" data-scale-floor="368" data-scale-height="80" data-scale-keep-height="true" aria-label="Player status">
-        <div class="stats-card scale-floor-inner">
-            <div class="stat-item">
-                <img class="stat-icon coin" src="~/kitty/assets/kitty-fall/coin.png" alt="">
-                <div>
-                    <p>@Lang.Points</p>
-                    <strong class="points">@totalPoint.ToString("N0")</strong>
-                </div>
-            </div>
-            <span class="stat-divider"></span>
-            <div class="stat-item">
-                <img class="stat-icon ticket" src="~/kitty/assets/kitty-fall/ticket.png" alt="">
-                <div>
-                    <p>@Lang.PlayTurns</p>
-                    <strong class="turns">@totalTurn.ToString("N0")</strong>
-                </div>
-            </div>
-        </div>
-    </section>
-
-    <section class="hero home-screen" aria-label="Kitty Fall">
-        <img class="kitty-logo" src="~/kitty/assets/kitty-fall/logo.png" alt="Kitty Fall">
-        <img class="kitty-cat" src="~/kitty/assets/kitty-fall/cat.png" alt="Smiling kitten">
-    </section>
-
-    <a class="play-button home-screen" href="/Play" aria-label="@Lang.PlayNow">
-        <span class="play-icon">
-            <img src="~/kitty/assets/kitty-fall/play-icon.svg" alt="">
-        </span>
-        <span>@Lang.PlayNow</span>
-    </a>
-
-    <section class="package-shell home-screen scale-floor" data-scale-floor="368" data-scale-height="100" data-scale-keep-height="true" aria-label="@(isSubscriber ? Lang.BuyTurn : "Subscription packages")">
-        @if (isSubscriber)
-        {
-            <div class="package-card scale-floor-inner">
-                <img class="package-icon" src="~/kitty/assets/kitty-fall/gift.png" alt="">
-                <div class="package-copy">
-                    <div class="package-title"><h2>@Lang.BuyTurn</h2></div>
-                    <p>@Lang.SelectTurnPackage</p>
-                    <p>@buyTurnCount @buyTurnText available from @buyTurnFee.ToString("N0") HTG</p>
-                </div>
-                <div class="package-action">
-                    <p><strong>@buyTurnFee.ToString("N0")</strong> HTG/@buyTurnCount @buyTurnText</p>
-                    <button class="subscribe-trigger" type="button" data-open-buy-turn>@Lang.BuyTurn</button>
-                </div>
-            </div>
-        }
-        else
-        {
-            <div class="package-card scale-floor-inner">
-                <img class="package-icon" src="~/kitty/assets/kitty-fall/gift.png" alt="">
-                <div class="package-copy">
-                    <div class="package-title"><h2>Daily / Weekly</h2></div>
-                    <p>Choose your package</p>
-                    <p>Daily or weekly auto renewal</p>
-                </div>
-                <div class="package-action">
-                    <p><span>From</span> <strong>@lowestSubscriptionFee.ToString("N0")</strong> HTG</p>
-                    <button class="subscribe-trigger" type="button" data-open-subscription-options>@Lang.SubscribeNow</button>
-                </div>
-            </div>
-        }
-    </section>
-
-    <div class="subscription-modal" data-subscription-modal
-         data-auto-open-subscription="@(autoOpenSubscriptionConfirm ? "true" : "false")"
-        data-auto-open-buy-turn="@(autoOpenBuyTurn ? "true" : "false")" aria-hidden="true">
-        <button class="subscription-backdrop" type="button" data-close-subscription aria-label="@Lang.ConfirmSubscription"></button>
-
-        <section class="confirm-dialog-shell buy-turn-dialog-shell scale-floor modal-panel" data-modal-panel="subscription-options" data-scale-floor="361" data-scale-height="460" role="dialog" aria-modal="true" aria-label="Select subscription package">
-            <div class="confirm-dialog buy-turn-dialog scale-floor-inner">
-                <div class="confirm-card buy-turn-card">
-                    <div class="confirm-ribbon">
-                        <img src="~/kitty/assets/kitty-fall/confirm-ribbon.png" alt="">
-                        <svg class="confirm-ribbon-title" viewBox="-20 0 360 75" aria-hidden="true">
-                            <defs><path id="subscription-options-title-path" d="M 20 55 Q 160 18 300 55" /></defs>
-                            <text font-size="27" font-weight="900" fill="#fff" text-anchor="middle" textLength="258" lengthAdjust="spacingAndGlyphs">
-                                <textPath href="#subscription-options-title-path" startOffset="50%">SELECT PACKAGE</textPath>
-                            </text>
-                        </svg>
-                    </div>
-                    <button class="confirm-close" type="button" data-close-subscription aria-label="Close">
-                        <img src="~/kitty/assets/kitty-fall/confirm-close.png" alt="">
-                    </button>
-                    <p class="confirm-intro buy-turn-intro">Choose your subscription package</p>
-                    <div class="buy-turn-options">
-                        @foreach (var subscriptionPackage in subscriptionPackages)
-                        {
-                            var optionProductName = subscriptionPackage.TryGetValue("productName", out var optionProductValue) ? optionProductValue?.ToString() ?? "" : "";
-                            var optionFee = subscriptionPackage.TryGetValue("fee", out var optionFeeValue) && decimal.TryParse(optionFeeValue?.ToString(), out var parsedOptionFee) ? parsedOptionFee : 0M;
-                            var optionTurns = subscriptionPackage.TryGetValue("numberSpin", out var optionTurnsValue) && int.TryParse(optionTurnsValue?.ToString(), out var parsedOptionTurns) ? parsedOptionTurns : 0;
-                            var optionExpireDays = subscriptionPackage.TryGetValue("expireDays", out var optionDaysValue) && int.TryParse(optionDaysValue?.ToString(), out var parsedOptionDays) ? parsedOptionDays : 1;
-                            var optionIsWeekly = optionProductName.Contains("WEEKLY", StringComparison.OrdinalIgnoreCase) || optionExpireDays == 7;
-                            var optionTitle = optionIsWeekly ? "Weekly Package" : Lang.DailyPackage;
-                            var optionPeriod = optionIsWeekly ? "week" : "day";
-                            var optionTurnText = optionTurns <= 1 ? "Turn" : "Turns";
-                            var optionRenewal = optionIsWeekly ? "Auto renewed weekly" : Lang.AutoRenewedDaily;
-
-                            <button class="buy-turn-option" type="button"
-                                    data-select-subscription-package
-                                    data-product-name="@optionProductName"
-                                    data-package-fee="@optionFee.ToString("N0")"
-                                    data-package-turns="@optionTurns"
-                                    data-package-turn-text="@optionTurnText"
-                                    data-package-renewal="@optionRenewal">
-                                <span>
-                                    <img src="~/kitty/assets/kitty-fall/gift.png" alt="">
-                                    <strong>@optionTitle</strong>
-                                </span>
-                                <b>@optionFee.ToString("N0") HTG/@optionPeriod</b>
-                                <small>@optionTurns @optionTurnText</small>
-                            </button>
-                        }
-                    </div>
-                </div>
-            </div>
-        </section>
-
-        <section class="confirm-dialog-shell scale-floor modal-panel is-active" data-modal-panel="confirm" data-scale-floor="361" data-scale-height="540" role="dialog" aria-modal="true" aria-label="@Lang.ConfirmSubscription">
-            <div class="confirm-dialog scale-floor-inner">
-                <img class="confirm-cat" src="~/kitty/assets/kitty-fall/confirm-cat.png" alt="">
-                <div class="confirm-card">
-                    <div class="confirm-ribbon">
-                        <img src="~/kitty/assets/kitty-fall/confirm-ribbon.png" alt="">
-                        <svg class="confirm-ribbon-title" viewBox="-20 0 360 75" aria-hidden="true">
-                            <defs>
-                                <path id="confirm-ribbon-title-path" d="M 20 55 Q 160 18 300 55" />
-                            </defs>
-                            <text font-size="30" font-weight="900" fill="#fff" text-anchor="middle" textLength="258" lengthAdjust="spacingAndGlyphs">
-                                <textPath href="#confirm-ribbon-title-path" startOffset="50%">@Lang.ConfirmSubscription</textPath>
-                            </text>
-                        </svg>
-                        <span id="subscription-title" class="sr-only">@Lang.ConfirmSubscription</span>
-                    </div>
-                    <button class="confirm-close" type="button" data-close-subscription aria-label="@Lang.ConfirmSubscription">
-                        <img src="~/kitty/assets/kitty-fall/confirm-close.png" alt="">
-                    </button>
-
-                    <p class="confirm-intro">@Lang.SubscriptionIntro</p>
-
-                    <div class="confirm-price">
-                        <strong data-subscription-fee>@packageFee.ToString("N0")</strong>
-                        <span data-subscription-turns>HTG/@packageTurns @packageTurnText</span>
-                    </div>
-
-                    <img class="confirm-tickets" src="~/kitty/assets/kitty-fall/confirm-tickets.png" alt="">
-
-                    <p class="confirm-note">@Lang.MoreTurnsAndMoreFun</p>
-
-                    <div class="renewal-note">
-                        <span class="renewal-icon" aria-hidden="true"></span>
-                        <p>@Lang.ThisPackageWillBe <strong data-subscription-renewal>@Lang.AutoRenewedDaily</strong></p>
-                    </div>
-
-                    <button class="confirm-button" type="button" data-confirm-subscription data-package-action="@packageAction" data-product-name="@packageProductName">
-                        <span class="confirm-button-icon" aria-hidden="true"></span>
-                        <span>@Lang.Confirm</span>
-                    </button>
-                </div>
-            </div>
-        </section>
-
-        <section class="confirm-dialog-shell buy-turn-dialog-shell scale-floor modal-panel" data-modal-panel="buy-turn" data-scale-floor="361" data-scale-height="460" role="dialog" aria-modal="true" aria-label="@Lang.SelectTurnPackage">
-            <div class="confirm-dialog buy-turn-dialog scale-floor-inner">
-                <div class="confirm-card buy-turn-card">
-                    <div class="confirm-ribbon">
-                        <img src="~/kitty/assets/kitty-fall/confirm-ribbon.png" alt="">
-                        <svg class="confirm-ribbon-title" viewBox="-20 0 360 75" aria-hidden="true">
-                            <defs>
-                                <path id="buy-turn-ribbon-title-path" d="M 20 55 Q 160 18 300 55" />
-                            </defs>
-                            <text font-size="30" font-weight="900" fill="#fff" text-anchor="middle" textLength="258" lengthAdjust="spacingAndGlyphs">
-                                <textPath href="#buy-turn-ribbon-title-path" startOffset="50%">@Lang.BuyTurn</textPath>
-                            </text>
-                        </svg>
-                        <span class="sr-only">@Lang.SelectTurnPackage</span>
-                    </div>
-                    <button class="confirm-close" type="button" data-close-subscription aria-label="@Lang.SelectTurnPackage">
-                        <img src="~/kitty/assets/kitty-fall/confirm-close.png" alt="">
-                    </button>
-
-                    <p class="confirm-intro buy-turn-intro">@Lang.SelectTurnPackage</p>
-
-                    <div class="buy-turn-options">
-                        @foreach (var turnPackage in turnPackages)
-                        {
-                            var turnProductName = turnPackage.TryGetValue("productName", out var productNameValue) ? productNameValue?.ToString() ?? "" : "";
-                            var turnFee = turnPackage.TryGetValue("fee", out var feeValue) && decimal.TryParse(feeValue?.ToString(), out var parsedFee) ? parsedFee : 0M;
-                            var turnCount = turnPackage.TryGetValue("numberSpin", out var turnValue) && int.TryParse(turnValue?.ToString(), out var parsedTurn) ? parsedTurn : 0;
-                            var turnText = turnCount <= 1 ? "Turn" : "Turns";
-
-                            if (string.IsNullOrWhiteSpace(turnProductName))
-                            {
-                                continue;
-                            }
-
-                            <button class="buy-turn-option" type="button" data-select-buy-turn-package data-product-name="@turnProductName" data-turn-count="@turnCount" data-turn-fee="@turnFee.ToString("N0")">
-                                <span>
-                                    <img src="~/kitty/assets/kitty-fall/ticket.png" alt="">
-                                    <strong>@turnCount @turnText</strong>
-                                </span>
-                                <b>@turnFee.ToString("N0") HTG</b>
-                                @* <small>@turnProductName</small> *@
-                            </button>
-                        }
-                    </div>
-                </div>
-            </div>
-        </section>
-
-        <section class="confirm-dialog-shell buy-turn-confirm-dialog-shell scale-floor modal-panel" data-modal-panel="confirm-buy-turn" data-scale-floor="361" data-scale-height="430" role="dialog" aria-modal="true" aria-label="@Lang.ConfirmBuyTurn">
-            <div class="confirm-dialog buy-turn-confirm-dialog scale-floor-inner">
-                <div class="confirm-card buy-turn-confirm-card">
-                    <div class="confirm-ribbon">
-                        <img src="~/kitty/assets/kitty-fall/confirm-ribbon.png" alt="">
-                        <svg class="confirm-ribbon-title" viewBox="-20 0 360 75" aria-hidden="true">
-                            <defs>
-                                <path id="confirm-buy-turn-ribbon-title-path" d="M 20 55 Q 160 18 300 55" />
-                            </defs>
-                            <text font-size="30" font-weight="900" fill="#fff" text-anchor="middle" textLength="258" lengthAdjust="spacingAndGlyphs">
-                                <textPath href="#confirm-buy-turn-ribbon-title-path" startOffset="50%">@Lang.ConfirmBuyTurn</textPath>
-                            </text>
-                        </svg>
-                        <span class="sr-only">@Lang.ConfirmBuyTurn</span>
-                    </div>
-                    <button class="confirm-close" type="button" data-close-subscription aria-label="@Lang.ConfirmBuyTurn">
-                        <img src="~/kitty/assets/kitty-fall/confirm-close.png" alt="">
-                    </button>
-
-                    <p class="confirm-intro buy-turn-confirm-intro">@Lang.ConfirmBuyTurnQuestion</p>
-
-                    <div class="selected-buy-turn-package">
-                        <span>
-                            <img src="~/kitty/assets/kitty-fall/ticket.png" alt="">
-                            <strong data-selected-turn-count>-</strong>
-                        </span>
-                        <b><span data-selected-turn-fee>-</span> HTG</b>
-                    </div>
-
-                    <button class="confirm-button buy-turn-confirm-button" type="button" data-confirm-subscription data-package-action="buy-turn" data-product-name="" data-turn-count="">
-                        <span class="confirm-button-icon" aria-hidden="true"></span>
-                        <span>@Lang.Confirm</span>
-                    </button>
-                </div>
-            </div>
-        </section>
-
-        <section class="result-dialog-shell result-dialog-shell-success scale-floor modal-panel" data-modal-panel="success" data-scale-floor="361" data-scale-height="608" role="dialog" aria-modal="true" aria-label="@Lang.Successful">
-            <div class="result-dialog scale-floor-inner">
-                <img class="result-cat result-cat-success" src="~/kitty/assets/kitty-fall/result-success-cat.png" alt="">
-                <div class="result-card result-card-success">
-                    <div class="confirm-ribbon result-ribbon">
-                        <img src="~/kitty/assets/kitty-fall/confirm-ribbon.png" alt="">
-                        <svg class="confirm-ribbon-title result-ribbon-title" viewBox="-20 0 360 75" aria-hidden="true">
-                            <defs>
-                                <path id="result-success-title-path" d="M 20 55 Q 160 18 300 55" />
-                            </defs>
-                            <text font-size="34" font-weight="900" fill="#fff" text-anchor="middle" textLength="210" lengthAdjust="spacingAndGlyphs">
-                                <textPath href="#result-success-title-path" startOffset="50%">@Lang.Successful</textPath>
-                            </text>
-                        </svg>
-                        <span class="sr-only">@Lang.Successful</span>
-                    </div>
-                    <button class="confirm-close" type="button" data-close-subscription aria-label="@Lang.Successful">
-                        <img src="~/kitty/assets/kitty-fall/confirm-close.png" alt="">
-                    </button>
-
-                    <img class="result-status-icon result-status-success" src="~/kitty/assets/kitty-fall/result-success-icon.png" alt="">
-                    <p class="result-title result-title-success" data-success-title
-                       data-subscription-title="@Lang.SubscriptionSuccessMessage"
-                       data-buy-turn-template="@Lang.BuyTurnSuccessMessage">@Lang.SubscriptionSuccessMessage</p>
-
-                    <div class="result-benefits" data-success-benefits>
-                        <div class="result-benefit">
-                            <span class="result-ticket-icon">
-                                <img src="~/kitty/assets/kitty-fall/ticket.png" alt="">
-                                @* <strong>x@packageTurns</strong> *@
-                            </span>
-                            <p><strong data-result-turn-count>@packageTurns</strong> @Lang.TurnsHaveBeenAdded</p>
-                        </div>
-                        <div class="result-benefit" data-subscription-result-only>
-                            <span class="renewal-icon" aria-hidden="true"></span>
-                            <p>@Lang.EnjoyMoreRewards</p>
-                        </div>
-                        <div class="result-benefit" data-subscription-result-only>
-                            <span class="renewal-icon" aria-hidden="true"></span>
-                            <p>@Lang.ThisPackageWillBe <strong>@Lang.AutoRenewedDaily</strong></p>
-                        </div>
-                    </div>
-
-                    <button class="result-button confirm-button" type="button" data-close-subscription
-                            data-success-action-button data-confirm-label="@Lang.Confirm"
-                            data-play-label="@Lang.PlayNow" data-play-url="@Url.Action("Index", "Play")">
-                        <span class="confirm-button-icon" aria-hidden="true"></span>
-                        <span data-success-action-label>@Lang.Confirm</span>
-                    </button>
-                </div>
-            </div>
-        </section>
-
-        <section class="result-dialog-shell result-dialog-shell-fail scale-floor modal-panel" data-modal-panel="fail" data-scale-floor="361" data-scale-height="452" role="dialog" aria-modal="true" aria-label="@Lang.SubscriptionFailed">
-            <div class="result-dialog scale-floor-inner">
-                <img class="result-cat result-cat-fail" src="~/kitty/assets/kitty-fall/result-fail-cat.png" alt="">
-                <div class="result-card result-card-fail">
-                    <div class="confirm-ribbon result-ribbon">
-                        <img src="~/kitty/assets/kitty-fall/confirm-ribbon.png" alt="">
-                        <svg class="confirm-ribbon-title result-ribbon-title" viewBox="-20 0 360 75" aria-hidden="true">
-                            <defs>
-                                <path id="result-fail-title-path" d="M 20 55 Q 160 18 300 55" />
-                            </defs>
-                            <text font-size="30" font-weight="900" fill="#fff" text-anchor="middle" textLength="258" lengthAdjust="spacingAndGlyphs">
-                                <textPath href="#result-fail-title-path" startOffset="50%">@Lang.SubscriptionFailed</textPath>
-                            </text>
-                        </svg>
-                        <span class="sr-only">@Lang.SubscriptionFailed</span>
-                    </div>
-                    <button class="confirm-close" type="button" data-close-subscription aria-label="@Lang.SubscriptionFailed">
-                        <img src="~/kitty/assets/kitty-fall/confirm-close.png" alt="">
-                    </button>
-
-                    <img class="result-status-icon result-status-fail" src="~/kitty/assets/kitty-fall/result-fail-icon.png" alt="">
-                    <p class="result-title result-title-fail">@Lang.SubscriptionFailedTitle</p>
-                    <p class="result-copy-fail" data-fallback-message="@Lang.SubscriptionFailedCopy @Lang.PleaseTryAgain">@Lang.SubscriptionFailedCopy<br>@Lang.PleaseTryAgain</p>
-
-                    <button class="result-button result-button-fail confirm-button" type="button" data-close-subscription>
-                        <span class="confirm-button-icon" aria-hidden="true"></span>
-                        <span>@Lang.Confirm</span>
-                    </button>
-                </div>
-            </div>
-        </section>
-    </div>
+	<img class="kitty-bg" src="~/kitty/assets/kitty-fall/bg.png" alt="">
+	<div class="kitty-bg-fade"></div>
+
+	<header class="topbar">
+		<div class="profile">
+			<button class="home-back-button" type="button" aria-label="Back" onclick="logoutAndBack()">
+				<span></span>
+			</button>
+			<img class="profile-avatar" src="@avatarUrl" alt="Player avatar">
+			<div class="profile-copy">
+				<strong>@Lang.Hello</strong>
+				<span>@(string.IsNullOrWhiteSpace(msisdn) ? "-" : msisdn)</span>
+			</div>
+		</div>
+		<div class="topbar-actions">
+			<button class="menu-button" type="button" aria-label="Open menu">
+				<span></span>
+				<span></span>
+				<span></span>
+			</button>
+		</div>
+	</header>
+
+	<section class="stats-shell home-screen scale-floor" data-scale-floor="368" data-scale-height="80" data-scale-keep-height="true" aria-label="Player status">
+		<div class="stats-card scale-floor-inner">
+			<div class="stat-item">
+				<img class="stat-icon coin" src="~/kitty/assets/kitty-fall/coin.png" alt="">
+				<div>
+					<p>@Lang.Points</p>
+					<strong class="points">@totalPoint.ToString("N0")</strong>
+				</div>
+			</div>
+			<span class="stat-divider"></span>
+			<div class="stat-item">
+				<img class="stat-icon ticket" src="~/kitty/assets/kitty-fall/ticket.png" alt="">
+				<div>
+					<p>@Lang.PlayTurns</p>
+					<strong class="turns">@totalTurn.ToString("N0")</strong>
+				</div>
+			</div>
+		</div>
+	</section>
+
+	<section class="hero home-screen" aria-label="Kitty Fall">
+		<img class="kitty-logo" src="~/kitty/assets/kitty-fall/logo.png" alt="Kitty Fall">
+		<img class="kitty-cat" src="~/kitty/assets/kitty-fall/cat.png" alt="Smiling kitten">
+	</section>
+
+	<a class="play-button home-screen" href="/Play" aria-label="@Lang.PlayNow">
+		<span class="play-icon">
+			<img src="~/kitty/assets/kitty-fall/play-icon.svg" alt="">
+		</span>
+		<span>@Lang.PlayNow</span>
+	</a>
+
+	<section class="package-shell home-screen scale-floor" data-scale-floor="368" data-scale-height="100" data-scale-keep-height="true" aria-label="@(isSubscriber? Lang.BuyTurn: "Subscription packages")">
+		@if (isSubscriber)
+		{
+			<div class="package-card scale-floor-inner">
+				<img class="package-icon" src="~/kitty/assets/kitty-fall/gift.png" alt="">
+				<div class="package-copy">
+					<div class="package-title"><h2>@Lang.BuyTurn</h2></div>
+					<p>@Lang.SelectTurnPackage</p>
+					<p>@buyTurnCount @buyTurnText available from @buyTurnFee.ToString("N0") HTG</p>
+				</div>
+				<div class="package-action">
+					<p><strong>@buyTurnFee.ToString("N0")</strong> HTG/@buyTurnCount @buyTurnText</p>
+					<button class="subscribe-trigger" type="button" data-open-buy-turn>@Lang.BuyTurn</button>
+				</div>
+			</div>
+		}
+		else
+		{
+			<div class="package-card scale-floor-inner">
+				<img class="package-icon" src="~/kitty/assets/kitty-fall/gift.png" alt="">
+				<div class="package-copy">
+					<div class="package-title"><h2>Daily / Weekly</h2></div>
+					<p>Choose your package</p>
+					<p>Daily or weekly auto renewal</p>
+				</div>
+				<div class="package-action">
+					<p><span>From</span> <strong>@lowestSubscriptionFee.ToString("N0")</strong> HTG</p>
+					<button class="subscribe-trigger" type="button" data-open-subscription-options>@Lang.SubscribeNow</button>
+				</div>
+			</div>
+		}
+	</section>
+
+	<div class="subscription-modal" data-subscription-modal
+		 data-auto-open-subscription="@(autoOpenSubscriptionConfirm ? "true" : "false")"
+		 data-auto-open-buy-turn="@(autoOpenBuyTurn ? "true" : "false")" aria-hidden="true">
+		<button class="subscription-backdrop" type="button" data-close-subscription aria-label="@Lang.ConfirmSubscription"></button>
+
+		<section class="confirm-dialog-shell buy-turn-dialog-shell scale-floor modal-panel" data-modal-panel="subscription-options" data-scale-floor="361" data-scale-height="460" role="dialog" aria-modal="true" aria-label="Select subscription package">
+			<div class="confirm-dialog buy-turn-dialog scale-floor-inner">
+				<div class="confirm-card buy-turn-card">
+					<div class="confirm-ribbon">
+						<img src="~/kitty/assets/kitty-fall/confirm-ribbon.png" alt="">
+						<svg class="confirm-ribbon-title" viewBox="-20 0 360 75" aria-hidden="true">
+							<defs><path id="subscription-options-title-path" d="M 20 55 Q 160 18 300 55" /></defs>
+							<text font-size="27" font-weight="900" fill="#fff" text-anchor="middle" textLength="258" lengthAdjust="spacingAndGlyphs">
+								<textPath href="#subscription-options-title-path" startOffset="50%">SELECT PACKAGE</textPath>
+							</text>
+						</svg>
+					</div>
+					<button class="confirm-close" type="button" data-close-subscription aria-label="Close">
+						<img src="~/kitty/assets/kitty-fall/confirm-close.png" alt="">
+					</button>
+					<p class="confirm-intro buy-turn-intro">Choose your subscription package</p>
+					<div class="buy-turn-options">
+						@foreach (var subscriptionPackage in subscriptionPackages)
+						{
+							var optionProductName = subscriptionPackage.TryGetValue("productName", out var optionProductValue) ? optionProductValue?.ToString() ?? "" : "";
+							var optionFee = subscriptionPackage.TryGetValue("fee", out var optionFeeValue) && decimal.TryParse(optionFeeValue?.ToString(), out var parsedOptionFee) ? parsedOptionFee : 0M;
+							var optionTurns = subscriptionPackage.TryGetValue("numberSpin", out var optionTurnsValue) && int.TryParse(optionTurnsValue?.ToString(), out var parsedOptionTurns) ? parsedOptionTurns : 0;
+							var optionExpireDays = subscriptionPackage.TryGetValue("expireDays", out var optionDaysValue) && int.TryParse(optionDaysValue?.ToString(), out var parsedOptionDays) ? parsedOptionDays : 1;
+							var optionIsWeekly = optionProductName.Contains("WEEKLY", StringComparison.OrdinalIgnoreCase) || optionExpireDays == 7;
+							var optionTitle = optionIsWeekly ? "Weekly Package" : Lang.DailyPackage;
+							var optionPeriod = optionIsWeekly ? "week" : "day";
+							var optionTurnText = optionTurns <= 1 ? "Turn" : "Turns";
+							var optionRenewal = optionIsWeekly ? "Auto renewed weekly" : Lang.AutoRenewedDaily;
+
+							<button class="buy-turn-option" type="button"
+									data-select-subscription-package
+									data-product-name="@optionProductName"
+									data-package-fee="@optionFee.ToString("N0")"
+									data-package-turns="@optionTurns"
+									data-package-turn-text="@optionTurnText"
+									data-package-renewal="@optionRenewal">
+								<span>
+									<img src="~/kitty/assets/kitty-fall/gift.png" alt="">
+									<strong>@optionTitle</strong>
+								</span>
+								<b>@optionFee.ToString("N0") HTG/@optionPeriod</b>
+								<small>@optionTurns @optionTurnText</small>
+							</button>
+						}
+					</div>
+				</div>
+			</div>
+		</section>
+
+		<section class="confirm-dialog-shell scale-floor modal-panel is-active" data-modal-panel="confirm" data-scale-floor="361" data-scale-height="540" role="dialog" aria-modal="true" aria-label="@Lang.ConfirmSubscription">
+			<div class="confirm-dialog scale-floor-inner">
+				<img class="confirm-cat" src="~/kitty/assets/kitty-fall/confirm-cat.png" alt="">
+				<div class="confirm-card">
+					<div class="confirm-ribbon">
+						<img src="~/kitty/assets/kitty-fall/confirm-ribbon.png" alt="">
+						<svg class="confirm-ribbon-title" viewBox="-20 0 360 75" aria-hidden="true">
+							<defs>
+								<path id="confirm-ribbon-title-path" d="M 20 55 Q 160 18 300 55" />
+							</defs>
+							<text font-size="30" font-weight="900" fill="#fff" text-anchor="middle" textLength="258" lengthAdjust="spacingAndGlyphs">
+								<textPath href="#confirm-ribbon-title-path" startOffset="50%">@Lang.ConfirmSubscription</textPath>
+							</text>
+						</svg>
+						<span id="subscription-title" class="sr-only">@Lang.ConfirmSubscription</span>
+					</div>
+					<button class="confirm-close" type="button" data-close-subscription aria-label="@Lang.ConfirmSubscription">
+						<img src="~/kitty/assets/kitty-fall/confirm-close.png" alt="">
+					</button>
+
+					<p class="confirm-intro">@Lang.SubscriptionIntro</p>
+
+					<div class="confirm-price">
+						<strong data-subscription-fee>@packageFee.ToString("N0")</strong>
+						<span data-subscription-turns>HTG/@packageTurns @packageTurnText</span>
+					</div>
+
+					<img class="confirm-tickets" src="~/kitty/assets/kitty-fall/confirm-tickets.png" alt="">
+
+					<p class="confirm-note">@Lang.MoreTurnsAndMoreFun</p>
+
+					<div class="renewal-note">
+						<span class="renewal-icon" aria-hidden="true"></span>
+						<p>@Lang.ThisPackageWillBe <strong data-subscription-renewal>@Lang.AutoRenewedDaily</strong></p>
+					</div>
+
+					<button class="confirm-button" type="button" data-confirm-subscription data-package-action="@packageAction" data-product-name="@packageProductName">
+						<span class="confirm-button-icon" aria-hidden="true"></span>
+						<span>@Lang.Confirm</span>
+					</button>
+				</div>
+			</div>
+		</section>
+
+		<section class="confirm-dialog-shell buy-turn-dialog-shell scale-floor modal-panel" data-modal-panel="buy-turn" data-scale-floor="361" data-scale-height="460" role="dialog" aria-modal="true" aria-label="@Lang.SelectTurnPackage">
+			<div class="confirm-dialog buy-turn-dialog scale-floor-inner">
+				<div class="confirm-card buy-turn-card">
+					<div class="confirm-ribbon">
+						<img src="~/kitty/assets/kitty-fall/confirm-ribbon.png" alt="">
+						<svg class="confirm-ribbon-title" viewBox="-20 0 360 75" aria-hidden="true">
+							<defs>
+								<path id="buy-turn-ribbon-title-path" d="M 20 55 Q 160 18 300 55" />
+							</defs>
+							<text font-size="30" font-weight="900" fill="#fff" text-anchor="middle" textLength="258" lengthAdjust="spacingAndGlyphs">
+								<textPath href="#buy-turn-ribbon-title-path" startOffset="50%">@Lang.BuyTurn</textPath>
+							</text>
+						</svg>
+						<span class="sr-only">@Lang.SelectTurnPackage</span>
+					</div>
+					<button class="confirm-close" type="button" data-close-subscription aria-label="@Lang.SelectTurnPackage">
+						<img src="~/kitty/assets/kitty-fall/confirm-close.png" alt="">
+					</button>
+
+					<p class="confirm-intro buy-turn-intro">@Lang.SelectTurnPackage</p>
+
+					<div class="buy-turn-options">
+						@foreach (var turnPackage in turnPackages)
+						{
+							var turnProductName = turnPackage.TryGetValue("productName", out var productNameValue) ? productNameValue?.ToString() ?? "" : "";
+							var turnFee = turnPackage.TryGetValue("fee", out var feeValue) && decimal.TryParse(feeValue?.ToString(), out var parsedFee) ? parsedFee : 0M;
+							var turnCount = turnPackage.TryGetValue("numberSpin", out var turnValue) && int.TryParse(turnValue?.ToString(), out var parsedTurn) ? parsedTurn : 0;
+							var turnText = turnCount <= 1 ? "Turn" : "Turns";
+
+							if (string.IsNullOrWhiteSpace(turnProductName))
+							{
+								continue;
+							}
+
+							<button class="buy-turn-option" type="button" data-select-buy-turn-package data-product-name="@turnProductName" data-turn-count="@turnCount" data-turn-fee="@turnFee.ToString("N0")">
+								<span>
+									<img src="~/kitty/assets/kitty-fall/ticket.png" alt="">
+									<strong>@turnCount @turnText</strong>
+								</span>
+								<b>@turnFee.ToString("N0") HTG</b>
+								@* <small>@turnProductName</small> *@
+							</button>
+						}
+					</div>
+				</div>
+			</div>
+		</section>
+
+		<section class="confirm-dialog-shell buy-turn-confirm-dialog-shell scale-floor modal-panel" data-modal-panel="confirm-buy-turn" data-scale-floor="361" data-scale-height="430" role="dialog" aria-modal="true" aria-label="@Lang.ConfirmBuyTurn">
+			<div class="confirm-dialog buy-turn-confirm-dialog scale-floor-inner">
+				<div class="confirm-card buy-turn-confirm-card">
+					<div class="confirm-ribbon">
+						<img src="~/kitty/assets/kitty-fall/confirm-ribbon.png" alt="">
+						<svg class="confirm-ribbon-title" viewBox="-20 0 360 75" aria-hidden="true">
+							<defs>
+								<path id="confirm-buy-turn-ribbon-title-path" d="M 20 55 Q 160 18 300 55" />
+							</defs>
+							<text font-size="30" font-weight="900" fill="#fff" text-anchor="middle" textLength="258" lengthAdjust="spacingAndGlyphs">
+								<textPath href="#confirm-buy-turn-ribbon-title-path" startOffset="50%">@Lang.ConfirmBuyTurn</textPath>
+							</text>
+						</svg>
+						<span class="sr-only">@Lang.ConfirmBuyTurn</span>
+					</div>
+					<button class="confirm-close" type="button" data-close-subscription aria-label="@Lang.ConfirmBuyTurn">
+						<img src="~/kitty/assets/kitty-fall/confirm-close.png" alt="">
+					</button>
+
+					<p class="confirm-intro buy-turn-confirm-intro">@Lang.ConfirmBuyTurnQuestion</p>
+
+					<div class="selected-buy-turn-package">
+						<span>
+							<img src="~/kitty/assets/kitty-fall/ticket.png" alt="">
+							<strong data-selected-turn-count>-</strong>
+						</span>
+						<b><span data-selected-turn-fee>-</span> HTG</b>
+					</div>
+
+					<button class="confirm-button buy-turn-confirm-button" type="button" data-confirm-subscription data-package-action="buy-turn" data-product-name="" data-turn-count="">
+						<span class="confirm-button-icon" aria-hidden="true"></span>
+						<span>@Lang.Confirm</span>
+					</button>
+				</div>
+			</div>
+		</section>
+
+		<section class="result-dialog-shell result-dialog-shell-success scale-floor modal-panel" data-modal-panel="success" data-scale-floor="361" data-scale-height="608" role="dialog" aria-modal="true" aria-label="@Lang.Successful">
+			<div class="result-dialog scale-floor-inner">
+				<img class="result-cat result-cat-success" src="~/kitty/assets/kitty-fall/result-success-cat.png" alt="">
+				<div class="result-card result-card-success">
+					<div class="confirm-ribbon result-ribbon">
+						<img src="~/kitty/assets/kitty-fall/confirm-ribbon.png" alt="">
+						<svg class="confirm-ribbon-title result-ribbon-title" viewBox="-20 0 360 75" aria-hidden="true">
+							<defs>
+								<path id="result-success-title-path" d="M 20 55 Q 160 18 300 55" />
+							</defs>
+							<text font-size="34" font-weight="900" fill="#fff" text-anchor="middle" textLength="210" lengthAdjust="spacingAndGlyphs">
+								<textPath href="#result-success-title-path" startOffset="50%">@Lang.Successful</textPath>
+							</text>
+						</svg>
+						<span class="sr-only">@Lang.Successful</span>
+					</div>
+					<button class="confirm-close" type="button" data-close-subscription aria-label="@Lang.Successful">
+						<img src="~/kitty/assets/kitty-fall/confirm-close.png" alt="">
+					</button>
+
+					<img class="result-status-icon result-status-success" src="~/kitty/assets/kitty-fall/result-success-icon.png" alt="">
+					<p class="result-title result-title-success" data-success-title
+					   data-subscription-title="@Lang.SubscriptionSuccessMessage"
+					   data-buy-turn-template="@Lang.BuyTurnSuccessMessage">@Lang.SubscriptionSuccessMessage</p>
+
+					<div class="result-benefits" data-success-benefits>
+						<div class="result-benefit">
+							<span class="result-ticket-icon">
+								<img src="~/kitty/assets/kitty-fall/ticket.png" alt="">
+								@* <strong>x@packageTurns</strong> *@
+							</span>
+							<p><strong data-result-turn-count>@packageTurns</strong> @Lang.TurnsHaveBeenAdded</p>
+						</div>
+						<div class="result-benefit" data-subscription-result-only>
+							<span class="renewal-icon" aria-hidden="true"></span>
+							<p>@Lang.EnjoyMoreRewards</p>
+						</div>
+						<div class="result-benefit" data-subscription-result-only>
+							<span class="renewal-icon" aria-hidden="true"></span>
+							<p>@Lang.ThisPackageWillBe <strong>@Lang.AutoRenewedDaily</strong></p>
+						</div>
+					</div>
+
+					<button class="result-button confirm-button" type="button" data-close-subscription
+							data-success-action-button data-confirm-label="@Lang.Confirm"
+							data-play-label="@Lang.PlayNow" data-play-url="@Url.Action("Index", "Play")">
+						<span class="confirm-button-icon" aria-hidden="true"></span>
+						<span data-success-action-label>@Lang.Confirm</span>
+					</button>
+				</div>
+			</div>
+		</section>
+
+		<section class="result-dialog-shell result-dialog-shell-fail scale-floor modal-panel" data-modal-panel="fail" data-scale-floor="361" data-scale-height="452" role="dialog" aria-modal="true" aria-label="@Lang.SubscriptionFailed">
+			<div class="result-dialog scale-floor-inner">
+				<img class="result-cat result-cat-fail" src="~/kitty/assets/kitty-fall/result-fail-cat.png" alt="">
+				<div class="result-card result-card-fail">
+					<div class="confirm-ribbon result-ribbon">
+						<img src="~/kitty/assets/kitty-fall/confirm-ribbon.png" alt="">
+						<svg class="confirm-ribbon-title result-ribbon-title" viewBox="-20 0 360 75" aria-hidden="true">
+							<defs>
+								<path id="result-fail-title-path" d="M 20 55 Q 160 18 300 55" />
+							</defs>
+							<text font-size="30" font-weight="900" fill="#fff" text-anchor="middle" textLength="258" lengthAdjust="spacingAndGlyphs">
+								<textPath href="#result-fail-title-path" startOffset="50%">@Lang.SubscriptionFailed</textPath>
+							</text>
+						</svg>
+						<span class="sr-only">@Lang.SubscriptionFailed</span>
+					</div>
+					<button class="confirm-close" type="button" data-close-subscription aria-label="@Lang.SubscriptionFailed">
+						<img src="~/kitty/assets/kitty-fall/confirm-close.png" alt="">
+					</button>
+
+					<img class="result-status-icon result-status-fail" src="~/kitty/assets/kitty-fall/result-fail-icon.png" alt="">
+					<p class="result-title result-title-fail">@Lang.SubscriptionFailedTitle</p>
+					<p class="result-copy-fail" data-fallback-message="@Lang.SubscriptionFailedCopy @Lang.PleaseTryAgain">@Lang.SubscriptionFailedCopy<br>@Lang.PleaseTryAgain</p>
+
+					<button class="result-button result-button-fail confirm-button" type="button" data-close-subscription>
+						<span class="confirm-button-icon" aria-hidden="true"></span>
+						<span>@Lang.Confirm</span>
+					</button>
+				</div>
+			</div>
+		</section>
+	</div>
 </main>
 
 @section Scripts {
-    <script>
-        function logoutAndBack() {
-            $.ajax({
-                url: "/Login/LogoutBackAction",
-                type: "POST",
-                complete: function () {
-                    //window.location.href = "mytel://back";
-                    window.location.href = "Login/index";
-                }
-            });
-        }
-    </script>
+	<script>
+		function logoutAndBack() {
+			$.ajax({
+				url: "/Login/LogoutBackAction",
+				type: "POST",
+				complete: function () {
+					//window.location.href = "mytel://back";
+					window.location.href = "Login/index";
+				}
+			});
+		}
+	</script>
 }

+ 82 - 82
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Leaderboard/Index.cshtml

@@ -1,94 +1,94 @@
 @model Common.Http.Apis.Request.UserLeaderboardData
 @{
-    ViewData["Title"] = "Kitty Fall - Leaderboard";
-    var profile = Model?.profile ?? new Common.Http.Apis.Request.UserGameActivityProfile();
-    var currentUser = Model?.currentUser ?? new Common.Http.Apis.Request.UserLeaderboardCurrentUser();
-    var profilePicture = string.IsNullOrWhiteSpace(profile.picture)
-        ? Url.Content("~/kitty/assets/kitty-fall/default.png")
-        : Url.Content("~/kitty/assets/kitty-fall/") + profile.picture;
-    var pageNumber = Model?.pageNumber > 0 ? Model.pageNumber : 1;
-    var totalPage = Model?.totalPage ?? 0;
-    var startPage = totalPage <= 5 ? 1 : Math.Max(1, Math.Min(pageNumber - 2, totalPage - 4));
-    var endPage = totalPage <= 5 ? totalPage : Math.Min(totalPage, startPage + 4);
+	ViewData["Title"] = "Kitty Fall - Leaderboard";
+	var profile = Model?.profile ?? new Common.Http.Apis.Request.UserGameActivityProfile();
+	var currentUser = Model?.currentUser ?? new Common.Http.Apis.Request.UserLeaderboardCurrentUser();
+	var profilePicture = string.IsNullOrWhiteSpace(profile.picture)
+		? Url.Content("~/kitty/assets/kitty-fall/default.png")
+		: Url.Content("~/kitty/assets/kitty-fall/") + profile.picture;
+	var pageNumber = Model?.pageNumber > 0 ? Model.pageNumber : 1;
+	var totalPage = Model?.totalPage ?? 0;
+	var startPage = totalPage <= 5 ? 1 : Math.Max(1, Math.Min(pageNumber - 2, totalPage - 4));
+	var endPage = totalPage <= 5 ? totalPage : Math.Min(totalPage, startPage + 4);
 }
 
 <main class="kitty-app is-winner-screen is-leaderboard-screen" aria-label="Kitty Fall leaderboard screen">
-    <img class="kitty-bg" src="~/kitty/assets/kitty-fall/bg.png" alt="">
-    <div class="kitty-bg-fade"></div>
+	<img class="kitty-bg" src="~/kitty/assets/kitty-fall/bg.png" alt="">
+	<div class="kitty-bg-fade"></div>
 
-    <header class="topbar">
-        <div class="profile">
-            <img class="profile-avatar" src="@profilePicture" alt="Player avatar">
-            <div class="profile-copy">
-                <strong>@Lang.Hello</strong>
-                <span>@profile.msisdn</span>
-            </div>
-        </div>
-        <button class="menu-button" aria-label="Open menu">
-            <span></span>
-            <span></span>
-            <span></span>
-        </button>
-    </header>
+	<header class="topbar">
+		<div class="profile">
+			<img class="profile-avatar" src="@profilePicture" alt="Player avatar">
+			<div class="profile-copy">
+				<strong>@Lang.Hello</strong>
+				<span>@profile.msisdn</span>
+			</div>
+		</div>
+		<button class="menu-button" aria-label="Open menu">
+			<span></span>
+			<span></span>
+			<span></span>
+		</button>
+	</header>
 
-    <section class="leaderboard-screen" aria-label="Leaderboard">
-        <div class="leaderboard-content">
-            <img class="leaderboard-hero" src="~/kitty/assets/kitty-fall/leaderboard-hero.png" alt="Leaderboard">
+	<section class="leaderboard-screen" aria-label="Leaderboard">
+		<div class="leaderboard-content">
+			<img class="leaderboard-hero" src="~/kitty/assets/kitty-fall/leaderboard-hero.png" alt="Leaderboard">
 
-            <div class="leaderboard-panel">
-                <div class="leaderboard-head">
-                    <span>@Lang.Rank</span>
-                    <span>@Lang.PhoneNumber</span>
-                    <span><img src="~/kitty/assets/kitty-fall/leaderboard-score-icon.png" alt="">@Lang.Score</span>
-                </div>
+			<div class="leaderboard-panel">
+				<div class="leaderboard-head">
+					<span>@Lang.Rank</span>
+					<span>@Lang.PhoneNumber</span>
+					<span><img src="~/kitty/assets/kitty-fall/leaderboard-score-icon.png" alt="">@Lang.Score</span>
+				</div>
 
-                <div class="leaderboard-rows">
-                    @if (Model?.items != null && Model.items.Count > 0)
-                    {
-                        foreach (var item in Model.items)
-                        {
-                            <div class="leaderboard-row @(item.isCurrentUser ? "is-current" : "")" data-rank-row="@item.rankNo">
-                                <span class="leaderboard-rank @(item.rankNo > 3 ? "is-number" : "")">
-                                    @if (item.rankNo >= 1 && item.rankNo <= 3)
-                                    {
-                                        <img src="~/kitty/assets/kitty-fall/rankmedal@(item.rankNo).png" alt="@item.rankNo">
-                                    }
-                                    else
-                                    {
-                                        @item.rankText
-                                    }
-                                </span>
-                                <span class="leaderboard-phone">@item.phoneText</span>
-                                <strong><img src="~/kitty/assets/kitty-fall/leaderboard-score-icon.png" alt=""><b>@item.scoreText</b></strong>
-                            </div>
-                        }
-                    }
-                </div>
+				<div class="leaderboard-rows">
+					@if (Model?.items != null && Model.items.Count > 0)
+					{
+						foreach (var item in Model.items)
+						{
+							<div class="leaderboard-row @(item.isCurrentUser ? "is-current" : "")" data-rank-row="@item.rankNo">
+								<span class="leaderboard-rank @(item.rankNo > 3 ? "is-number" : "")">
+									@if (item.rankNo >= 1 && item.rankNo <= 3)
+									{
+										<img src="~/kitty/assets/kitty-fall/rankmedal@(item.rankNo).png" alt="@item.rankNo">
+									}
+									else
+									{
+										@item.rankText
+									}
+								</span>
+								<span class="leaderboard-phone">@item.phoneText</span>
+								<strong><img src="~/kitty/assets/kitty-fall/leaderboard-score-icon.png" alt=""><b>@item.scoreText</b></strong>
+							</div>
+						}
+					}
+				</div>
 
-                <div class="leaderboard-pages" aria-label="Leaderboard pages">
-                    @for (var pageNo = startPage; pageNo <= endPage; pageNo++)
-                    {
-                        <a class="@(pageNo == pageNumber ? "active" : "")"
-                           href="@Url.Action("Index", "Leaderboard", new { pageNumber = pageNo })">@pageNo</a>
-                    }
-                </div>
+				<div class="leaderboard-pages" aria-label="Leaderboard pages">
+					@for (var pageNo = startPage; pageNo <= endPage; pageNo++)
+					{
+						<a class="@(pageNo == pageNumber ? "active" : "")"
+						   href="@Url.Action("Index", "Leaderboard", new { pageNumber = pageNo })">@pageNo</a>
+					}
+				</div>
 
-                <div class="leaderboard-my-rank">
-                    <div class="leaderboard-my-badge">
-                        <span>@Lang.MyRank</span>
-                        @if (currentUser.rankNo >= 1 && currentUser.rankNo <= 3)
-                        {
-                            <b class="has-medal"><img src="~/kitty/assets/kitty-fall/rankmedal@(currentUser.rankNo).png" alt="@currentUser.rankNo"></b>
-                        }
-                        else
-                        {
-                            <b>@currentUser.rankText</b>
-                        }
-                    </div>
-                    <span>@currentUser.phoneText</span>
-                    <strong><img src="~/kitty/assets/kitty-fall/leaderboard-score-icon.png" alt=""><b>@currentUser.scoreText</b></strong>
-                </div>
-            </div>
-        </div>
-    </section>
+				<div class="leaderboard-my-rank">
+					<div class="leaderboard-my-badge">
+						<span>@Lang.MyRank</span>
+						@if (currentUser.rankNo >= 1 && currentUser.rankNo <= 3)
+						{
+							<b class="has-medal"><img src="~/kitty/assets/kitty-fall/rankmedal@(currentUser.rankNo).png" alt="@currentUser.rankNo"></b>
+						}
+						else
+						{
+							<b>@currentUser.rankText</b>
+						}
+					</div>
+					<span>@currentUser.phoneText</span>
+					<strong><img src="~/kitty/assets/kitty-fall/leaderboard-score-icon.png" alt=""><b>@currentUser.scoreText</b></strong>
+				</div>
+			</div>
+		</div>
+	</section>
 </main>

+ 224 - 224
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Login/Index.cshtml

@@ -1,257 +1,257 @@
 @{
-    ViewData["Title"] = "Login";
-    var currentMsisdn = ViewBag.CurrentMsisdn as string;
+	ViewData["Title"] = "Login";
+	var currentMsisdn = ViewBag.CurrentMsisdn as string;
 }
 
 <section class="kitty-app login-screen" aria-label="login">
-    <img class="kitty-bg" src="~/kitty/assets/kitty-fall/bg.png" alt="">
-    <div class="kitty-bg-fade"></div>
+	<img class="kitty-bg" src="~/kitty/assets/kitty-fall/bg.png" alt="">
+	<div class="kitty-bg-fade"></div>
 
-    <div class="login-panel">
-        <img class="login-logo" src="~/kitty/assets/kitty-fall/logo.png" alt="Kitty Fall">
-        <img class="login-cat" src="~/kitty/assets/kitty-fall/cat.png" alt="">
+	<div class="login-panel">
+		<img class="login-logo" src="~/kitty/assets/kitty-fall/logo.png" alt="Kitty Fall">
+		<img class="login-cat" src="~/kitty/assets/kitty-fall/cat.png" alt="">
 
-        <div class="login-card" data-login-card data-login-step="msisdn">
-            <div class="login-card-head">
-                <strong>Login</strong>
-            </div>
+		<div class="login-card" data-login-card data-login-step="msisdn">
+			<div class="login-card-head">
+				<strong>Login</strong>
+			</div>
 
-            @if (!string.IsNullOrWhiteSpace(currentMsisdn))
-            {
-                <div class="login-session">
-                    <span>Logged in</span>
-                    <strong>@currentMsisdn</strong>
-                    <form asp-controller="Login" asp-action="Logout" method="post">
-                        @Html.AntiForgeryToken()
-                        <button type="submit">Logout</button>
-                    </form>
-                </div>
-            }
+			@if (!string.IsNullOrWhiteSpace(currentMsisdn))
+			{
+				<div class="login-session">
+					<span>Logged in</span>
+					<strong>@currentMsisdn</strong>
+					<form asp-controller="Login" asp-action="Logout" method="post">
+						@Html.AntiForgeryToken()
+						<button type="submit">Logout</button>
+					</form>
+				</div>
+			}
 
-            <div id="loginMessage" class="login-alert login-hidden" data-login-message></div>
+			<div id="loginMessage" class="login-alert login-hidden" data-login-message></div>
 
-            <div class="login-form" data-login-form>
-                <div id="loginSendPanel" class="login-step-panel login-send-step">
-                    <label class="login-field">
-                        <span>MSISDN</span>
-                        <input id="phonenumber" type="text" autocomplete="tel" placeholder="509xxxxxxxx">
-                    </label>
-                </div>
+			<div class="login-form" data-login-form>
+				<div id="loginSendPanel" class="login-step-panel login-send-step">
+					<label class="login-field">
+						<span>MSISDN</span>
+						<input id="phonenumber" type="text" autocomplete="tel" placeholder="509xxxxxxxx">
+					</label>
+				</div>
 
-                <div id="loginOtpPanel" class="login-step-panel login-otp-step" style="display: none;">
-                    <label class="login-field">
-                        <span>MSISDN</span>
-                        <input id="otp_phonenumber" type="text" autocomplete="tel" placeholder="509xxxxxxxx" readonly>
-                    </label>
+				<div id="loginOtpPanel" class="login-step-panel login-otp-step" style="display: none;">
+					<label class="login-field">
+						<span>MSISDN</span>
+						<input id="otp_phonenumber" type="text" autocomplete="tel" placeholder="509xxxxxxxx" readonly>
+					</label>
 
-                    <label class="login-field">
-                        <span>OTP</span>
-                        <input id="otp" type="text" inputmode="numeric" autocomplete="one-time-code" placeholder="Enter OTP">
-                    </label>
-                </div>
+					<label class="login-field">
+						<span>OTP</span>
+						<input id="otp" type="text" inputmode="numeric" autocomplete="one-time-code" placeholder="Enter OTP">
+					</label>
+				</div>
 
-                <button type="button" id="btnLoginConfirm" class="login-submit">
-                    <img src="~/kitty/assets/kitty-fall/play-icon.svg" alt="">
-                    <span>Send OTP</span>
-                </button>
+				<button type="button" id="btnLoginConfirm" class="login-submit">
+					<img src="~/kitty/assets/kitty-fall/play-icon.svg" alt="">
+					<span>Send OTP</span>
+				</button>
 
-                <div id="changePhoneWrapper" class="change-phone-wrapper" style="display: none; text-align: center; margin-top: 15px;">
-                    <button type="button" id="btnChangePhone" style="background: none; border: none; color: #5721ea; text-decoration: underline; cursor: pointer; font-size: 14px; font-weight: 800;">
-                        Change phone number
-                    </button>
-                </div>
-            </div>
+				<div id="changePhoneWrapper" class="change-phone-wrapper" style="display: none; text-align: center; margin-top: 15px;">
+					<button type="button" id="btnChangePhone" style="background: none; border: none; color: #5721ea; text-decoration: underline; cursor: pointer; font-size: 14px; font-weight: 800;">
+						Change phone number
+					</button>
+				</div>
+			</div>
 
-            <div class="login-loading login-hidden" data-login-loading>
-                <span class="login-spinner" aria-hidden="true"></span>
-            </div>
-        </div>
-    </div>
+			<div class="login-loading login-hidden" data-login-loading>
+				<span class="login-spinner" aria-hidden="true"></span>
+			</div>
+		</div>
+	</div>
 </section>
 
 <script>
-    (function() {
-        var currentStep = "msisdn";
-        var transactionId = "";
-        var loginBusy = false;
+	(function() {
+		var currentStep = "msisdn";
+		var transactionId = "";
+		var loginBusy = false;
 
-        var btnConfirm = document.getElementById("btnLoginConfirm");
-        var btnChangePhone = document.getElementById("btnChangePhone");
-        var changePhoneWrapper = document.getElementById("changePhoneWrapper");
-        var inputPhone = document.getElementById("phonenumber");
-        var inputOtpPhone = document.getElementById("otp_phonenumber");
-        var inputOtp = document.getElementById("otp");
-        var panelSend = document.getElementById("loginSendPanel");
-        var panelOtp = document.getElementById("loginOtpPanel");
-        var alertMessage = document.getElementById("loginMessage");
-        var loadingSpinner = document.querySelector("[data-login-loading]");
-        var card = document.querySelector("[data-login-card]");
+		var btnConfirm = document.getElementById("btnLoginConfirm");
+		var btnChangePhone = document.getElementById("btnChangePhone");
+		var changePhoneWrapper = document.getElementById("changePhoneWrapper");
+		var inputPhone = document.getElementById("phonenumber");
+		var inputOtpPhone = document.getElementById("otp_phonenumber");
+		var inputOtp = document.getElementById("otp");
+		var panelSend = document.getElementById("loginSendPanel");
+		var panelOtp = document.getElementById("loginOtpPanel");
+		var alertMessage = document.getElementById("loginMessage");
+		var loadingSpinner = document.querySelector("[data-login-loading]");
+		var card = document.querySelector("[data-login-card]");
 
-        function showMessage(text, isInfo) {
-            if (!alertMessage) return;
-            if (text) {
-                alertMessage.textContent = text;
-                alertMessage.className = "login-alert" + (isInfo ? " login-alert-info" : "");
-                alertMessage.style.display = "block";
-            } else {
-                alertMessage.style.display = "none";
-            }
-        }
+		function showMessage(text, isInfo) {
+			if (!alertMessage) return;
+			if (text) {
+				alertMessage.textContent = text;
+				alertMessage.className = "login-alert" + (isInfo ? " login-alert-info" : "");
+				alertMessage.style.display = "block";
+			} else {
+				alertMessage.style.display = "none";
+			}
+		}
 
-        function setLoading(isLoading, text) {
-            loginBusy = isLoading;
-            if (btnConfirm) {
-                btnConfirm.disabled = isLoading;
-                var span = btnConfirm.querySelector("span");
-                if (span && text) span.textContent = text;
-            }
-            if (loadingSpinner) {
-                loadingSpinner.classList.toggle("login-hidden", !isLoading);
-            }
-        }
+		function setLoading(isLoading, text) {
+			loginBusy = isLoading;
+			if (btnConfirm) {
+				btnConfirm.disabled = isLoading;
+				var span = btnConfirm.querySelector("span");
+				if (span && text) span.textContent = text;
+			}
+			if (loadingSpinner) {
+				loadingSpinner.classList.toggle("login-hidden", !isLoading);
+			}
+		}
 
-        function setStep(step) {
-            currentStep = step;
-            if (card) {
-                card.setAttribute("data-login-step", step);
-            }
-            if (panelSend) {
-                panelSend.style.display = step === "otp" ? "none" : "grid";
-            }
-            if (panelOtp) {
-                panelOtp.style.display = step === "otp" ? "grid" : "none";
-            }
-            if (changePhoneWrapper) {
-                changePhoneWrapper.style.display = step === "otp" ? "block" : "none";
-            }
-            if (btnConfirm) {
-                var span = btnConfirm.querySelector("span");
-                if (span) {
-                    span.textContent = step === "otp" ? "Confirm OTP" : "Send OTP";
-                }
-            }
-            if (step === "otp" && inputOtp) {
-                inputOtp.value = "";
-                setTimeout(function() { inputOtp.focus(); }, 50);
-            }
-        }
+		function setStep(step) {
+			currentStep = step;
+			if (card) {
+				card.setAttribute("data-login-step", step);
+			}
+			if (panelSend) {
+				panelSend.style.display = step === "otp" ? "none" : "grid";
+			}
+			if (panelOtp) {
+				panelOtp.style.display = step === "otp" ? "grid" : "none";
+			}
+			if (changePhoneWrapper) {
+				changePhoneWrapper.style.display = step === "otp" ? "block" : "none";
+			}
+			if (btnConfirm) {
+				var span = btnConfirm.querySelector("span");
+				if (span) {
+					span.textContent = step === "otp" ? "Confirm OTP" : "Send OTP";
+				}
+			}
+			if (step === "otp" && inputOtp) {
+				inputOtp.value = "";
+				setTimeout(function() { inputOtp.focus(); }, 50);
+			}
+		}
 
-        function handleSendOtp() {
-            var msisdn = (inputPhone ? inputPhone.value : "").trim();
-            if (!msisdn) {
-                showMessage("Please enter MSISDN.", false);
-                if (inputPhone) inputPhone.focus();
-                return;
-            }
+		function handleSendOtp() {
+			var msisdn = (inputPhone ? inputPhone.value : "").trim();
+			if (!msisdn) {
+				showMessage("Please enter MSISDN.", false);
+				if (inputPhone) inputPhone.focus();
+				return;
+			}
 
-            showMessage("Sending OTP...", true);
-            setLoading(true, "Sending...");
+			showMessage("Sending OTP...", true);
+			setLoading(true, "Sending...");
 
-            $.ajax({
-                url: "/Login/SendOtpAction",
-                type: "POST",
-                data: { msisdn: msisdn },
-                success: function(res) {
-                    setLoading(false);
-                    if (typeof res === "string") {
-                        try { res = JSON.parse(res); } catch(e) {}
-                    }
-                    
-                    var errorCode = res && (res.errorCode ?? res.ErrorCode);
-                    if (errorCode === 0 || errorCode === 200) {
-                        transactionId = res.transactionId || res.TransactionId;
-                        if (inputOtpPhone) inputOtpPhone.value = res.msisdn || res.Msisdn || msisdn;
-                        setStep("otp");
-                        showMessage(res.message || "Enter OTP from message.", true);
-                    } else {
-                        showMessage(res.message || "Send OTP failed.", false);
-                        setStep("msisdn");
-                    }
-                },
-                error: function() {
-                    setLoading(false);
-                    showMessage("Send OTP failed. Please check network connection.", false);
-                }
-            });
-        }
+			$.ajax({
+				url: "/Login/SendOtpAction",
+				type: "POST",
+				data: { msisdn: msisdn },
+				success: function(res) {
+					setLoading(false);
+					if (typeof res === "string") {
+						try { res = JSON.parse(res); } catch(e) {}
+					}
 
-        function handleConfirmOtp() {
-            var msisdn = (inputOtpPhone ? inputOtpPhone.value : "").trim();
-            var otp = (inputOtp ? inputOtp.value : "").trim();
+					var errorCode = res && (res.errorCode ?? res.ErrorCode);
+					if (errorCode === 0 || errorCode === 200) {
+						transactionId = res.transactionId || res.TransactionId;
+						if (inputOtpPhone) inputOtpPhone.value = res.msisdn || res.Msisdn || msisdn;
+						setStep("otp");
+						showMessage(res.message || "Enter OTP from message.", true);
+					} else {
+						showMessage(res.message || "Send OTP failed.", false);
+						setStep("msisdn");
+					}
+				},
+				error: function() {
+					setLoading(false);
+					showMessage("Send OTP failed. Please check network connection.", false);
+				}
+			});
+		}
 
-            if (!otp) {
-                showMessage("Please enter OTP.", false);
-                if (inputOtp) inputOtp.focus();
-                return;
-            }
+		function handleConfirmOtp() {
+			var msisdn = (inputOtpPhone ? inputOtpPhone.value : "").trim();
+			var otp = (inputOtp ? inputOtp.value : "").trim();
 
-            showMessage("Checking OTP...", true);
-            setLoading(true, "Checking...");
+			if (!otp) {
+				showMessage("Please enter OTP.", false);
+				if (inputOtp) inputOtp.focus();
+				return;
+			}
 
-            $.ajax({
-                url: "/Login/ConfirmOtpAction",
-                type: "POST",
-                data: {
-                    msisdn: msisdn,
-                    transactionId: transactionId,
-                    otp: otp
-                },
-                success: function(res) {
-                    if (typeof res === "string") {
-                        try { res = JSON.parse(res); } catch(e) {}
-                    }
+			showMessage("Checking OTP...", true);
+			setLoading(true, "Checking...");
 
-                    var errorCode = res && (res.errorCode ?? res.ErrorCode);
-                    if (errorCode === 0 || errorCode === 200) {
-                        showMessage("Login successful! Redirecting...", true);
-                        window.location.href = res.redirectUrl || "/";
-                    } else {
-                        setLoading(false);
-                        showMessage(res.message || "Confirm OTP failed.", false);
-                    }
-                },
-                error: function() {
-                    setLoading(false);
-                    showMessage("Confirm OTP failed. Please check network connection.", false);
-                }
-            });
-        }
+			$.ajax({
+				url: "/Login/ConfirmOtpAction",
+				type: "POST",
+				data: {
+					msisdn: msisdn,
+					transactionId: transactionId,
+					otp: otp
+				},
+				success: function(res) {
+					if (typeof res === "string") {
+						try { res = JSON.parse(res); } catch(e) {}
+					}
 
-        if (btnConfirm) {
-            btnConfirm.addEventListener("click", function(e) {
-                e.preventDefault();
-                if (loginBusy) return;
-                if (currentStep === "otp") {
-                    handleConfirmOtp();
-                } else {
-                    handleSendOtp();
-                }
-            });
-        }
+					var errorCode = res && (res.errorCode ?? res.ErrorCode);
+					if (errorCode === 0 || errorCode === 200) {
+						showMessage("Login successful! Redirecting...", true);
+						window.location.href = res.redirectUrl || "/";
+					} else {
+						setLoading(false);
+						showMessage(res.message || "Confirm OTP failed.", false);
+					}
+				},
+				error: function() {
+					setLoading(false);
+					showMessage("Confirm OTP failed. Please check network connection.", false);
+				}
+			});
+		}
 
-        if (btnChangePhone) {
-            btnChangePhone.addEventListener("click", function(e) {
-                e.preventDefault();
-                setStep("msisdn");
-                showMessage("");
-            });
-        }
+		if (btnConfirm) {
+			btnConfirm.addEventListener("click", function(e) {
+				e.preventDefault();
+				if (loginBusy) return;
+				if (currentStep === "otp") {
+					handleConfirmOtp();
+				} else {
+					handleSendOtp();
+				}
+			});
+		}
 
-        if (inputPhone) {
-            inputPhone.addEventListener("keypress", function(e) {
-                if (e.key === "Enter") {
-                    e.preventDefault();
-                    if (!loginBusy) handleSendOtp();
-                }
-            });
-        }
-        if (inputOtp) {
-            inputOtp.addEventListener("keypress", function(e) {
-                if (e.key === "Enter") {
-                    e.preventDefault();
-                    if (!loginBusy) handleConfirmOtp();
-                }
-            });
-        }
-    })();
+		if (btnChangePhone) {
+			btnChangePhone.addEventListener("click", function(e) {
+				e.preventDefault();
+				setStep("msisdn");
+				showMessage("");
+			});
+		}
+
+		if (inputPhone) {
+			inputPhone.addEventListener("keypress", function(e) {
+				if (e.key === "Enter") {
+					e.preventDefault();
+					if (!loginBusy) handleSendOtp();
+				}
+			});
+		}
+		if (inputOtp) {
+			inputOtp.addEventListener("keypress", function(e) {
+				if (e.key === "Enter") {
+					e.preventDefault();
+					if (!loginBusy) handleConfirmOtp();
+				}
+			});
+		}
+	})();
 </script>

+ 58 - 58
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/MonthlyWinners/Index.cshtml

@@ -1,68 +1,68 @@
 @model Common.Http.Apis.Request.UserMonthlyWinnersData
 @{
-    ViewData["Title"] = $"Kitty Fall - {Lang.MonthlyWinners}";
-    var profile = Model?.profile ?? new Common.Http.Apis.Request.UserGameActivityProfile();
-    var profilePicture = string.IsNullOrWhiteSpace(profile.picture)
-        ? Url.Content("~/kitty/assets/kitty-fall/default.png")
-        : Url.Content("~/kitty/assets/kitty-fall/") + profile.picture;
+	ViewData["Title"] = $"Kitty Fall - {Lang.MonthlyWinners}";
+	var profile = Model?.profile ?? new Common.Http.Apis.Request.UserGameActivityProfile();
+	var profilePicture = string.IsNullOrWhiteSpace(profile.picture)
+		? Url.Content("~/kitty/assets/kitty-fall/default.png")
+		: Url.Content("~/kitty/assets/kitty-fall/") + profile.picture;
 }
 
 <main class="kitty-app is-winner-screen is-monthly-winner-screen" aria-label="@Lang.MonthlyWinnerList">
-    <img class="kitty-bg" src="~/kitty/assets/kitty-fall/bg.png" alt="">
-    <div class="kitty-bg-fade"></div>
+	<img class="kitty-bg" src="~/kitty/assets/kitty-fall/bg.png" alt="">
+	<div class="kitty-bg-fade"></div>
 
-    <header class="topbar">
-        <div class="profile">
-            <img class="profile-avatar" src="@profilePicture" alt="@Lang.PlayerAvatar">
-            <div class="profile-copy">
-                <strong>@Lang.Hello</strong>
-                <span>@profile.msisdn</span>
-            </div>
-        </div>
-        <button class="menu-button" aria-label="@Lang.OpenMenu">
-            <span></span>
-            <span></span>
-            <span></span>
-        </button>
-    </header>
+	<header class="topbar">
+		<div class="profile">
+			<img class="profile-avatar" src="@profilePicture" alt="@Lang.PlayerAvatar">
+			<div class="profile-copy">
+				<strong>@Lang.Hello</strong>
+				<span>@profile.msisdn</span>
+			</div>
+		</div>
+		<button class="menu-button" aria-label="@Lang.OpenMenu">
+			<span></span>
+			<span></span>
+			<span></span>
+		</button>
+	</header>
 
-    <section class="monthly-winner-screen" aria-label="@Lang.MonthlyWinnerList">
-        <div class="monthly-winner-content">
-            <img class="monthly-hero" src="~/kitty/assets/kitty-fall/monthly-winner-hero.png" alt="@Lang.MonthlyWinnerList">
+	<section class="monthly-winner-screen" aria-label="@Lang.MonthlyWinnerList">
+		<div class="monthly-winner-content">
+			<img class="monthly-hero" src="~/kitty/assets/kitty-fall/monthly-winner-hero.png" alt="@Lang.MonthlyWinnerList">
 
-            <form class="winner-date-picker monthly-date-picker" method="get" action="@Url.Action("Index", "MonthlyWinners")"
-                  aria-label="@Lang.SelectMonth" data-month-picker data-selected-month="@Model?.monthKey"
-                  data-min-month="@Model?.minMonthKey" data-max-month="@Model?.maxMonthKey">
-                <img class="date-calendar" src="~/kitty/assets/kitty-fall/icon-calendar.png" alt="">
-                <strong>@Model?.monthText</strong>
-                <input type="hidden" name="monthKey" value="@Model?.monthKey" data-month-value>
-                <img class="date-chevron" src="~/kitty/assets/kitty-fall/icon-chevron-down.png" alt="">
-            </form>
+			<form class="winner-date-picker monthly-date-picker" method="get" action="@Url.Action("Index", "MonthlyWinners")"
+				  aria-label="@Lang.SelectMonth" data-month-picker data-selected-month="@Model?.monthKey"
+				  data-min-month="@Model?.minMonthKey" data-max-month="@Model?.maxMonthKey">
+				<img class="date-calendar" src="~/kitty/assets/kitty-fall/icon-calendar.png" alt="">
+				<strong>@Model?.monthText</strong>
+				<input type="hidden" name="monthKey" value="@Model?.monthKey" data-month-value>
+				<img class="date-chevron" src="~/kitty/assets/kitty-fall/icon-chevron-down.png" alt="">
+			</form>
 
-            <div class="monthly-prize-list">
-                @if (Model?.items != null && Model.items.Count > 0)
-                {
-                    foreach (var item in Model.items)
-                    {
-                        <article class="monthly-prize-card @item.prizeClass">
-                            <img class="monthly-medal" src="@item.medalIcon" alt="">
-                            <div class="monthly-prize-copy">
-                                <h2>@item.prizeName</h2>
-                                <strong>@item.phoneText</strong>
-                                <p><span class="monthly-time-icon" aria-hidden="true"></span>@item.timeText</p>
-                            </div>
-                            <div class="monthly-reward">
-                                <img src="@item.rewardIcon" alt="">
-                                <span>@item.prizeValueText<br>@item.prizeUnit</span>
-                            </div>
-                        </article>
-                    }
-                }
-                else
-                {
-                    <div class="monthly-empty">@((ViewBag.MonthlyWinnersError as string) ?? Lang.NoMonthlyWinnersFound)</div>
-                }
-            </div>
-        </div>
-    </section>
+			<div class="monthly-prize-list">
+				@if (Model?.items != null && Model.items.Count > 0)
+				{
+					foreach (var item in Model.items)
+					{
+						<article class="monthly-prize-card @item.prizeClass">
+							<img class="monthly-medal" src="@item.medalIcon" alt="">
+							<div class="monthly-prize-copy">
+								<h2>@item.prizeName</h2>
+								<strong>@item.phoneText</strong>
+								<p><span class="monthly-time-icon" aria-hidden="true"></span>@item.timeText</p>
+							</div>
+							<div class="monthly-reward">
+								<img src="@item.rewardIcon" alt="">
+								<span>@item.prizeValueText<br>@item.prizeUnit</span>
+							</div>
+						</article>
+					}
+				}
+				else
+				{
+					<div class="monthly-empty">@((ViewBag.MonthlyWinnersError as string) ?? Lang.NoMonthlyWinnersFound)</div>
+				}
+			</div>
+		</div>
+	</section>
 </main>

+ 142 - 142
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Play/Index.cshtml

@@ -1,158 +1,158 @@
 @model Kitty_Fall.Website.Models.PlayGameViewModel
 @{
-    ViewData["Title"] = "Kitty Fall - Play";
-    var avatarUrl = string.IsNullOrWhiteSpace(Model.ProfilePicture)
-        ? Url.Content("~/kitty/assets/kitty-fall/default.png")
-        : Url.Content("~/kitty/assets/kitty-fall/") + Model.ProfilePicture;
+	ViewData["Title"] = "Kitty Fall - Play";
+	var avatarUrl = string.IsNullOrWhiteSpace(Model.ProfilePicture)
+		? Url.Content("~/kitty/assets/kitty-fall/default.png")
+		: Url.Content("~/kitty/assets/kitty-fall/") + Model.ProfilePicture;
 }
 
 <main class="kitty-app is-play-screen" aria-label="Kitty Fall play screen"
-      data-play-game data-msisdn="@Model.Msisdn" data-token="@Model.Token"
-      data-trans-id="@Model.TransId" data-root="@Model.Root" data-game-url="@Model.GameUrl"
-      data-total-turn="@Model.TotalTurn">
-    <img class="kitty-bg" src="~/kitty/assets/kitty-fall/bg.png" alt="">
-    <div class="kitty-bg-fade"></div>
+	  data-play-game data-msisdn="@Model.Msisdn" data-token="@Model.Token"
+	  data-trans-id="@Model.TransId" data-root="@Model.Root" data-game-url="@Model.GameUrl"
+	  data-total-turn="@Model.TotalTurn">
+	<img class="kitty-bg" src="~/kitty/assets/kitty-fall/bg.png" alt="">
+	<div class="kitty-bg-fade"></div>
 
-    <header class="topbar">
-        <div class="profile">
-            <img class="profile-avatar" src="@avatarUrl" alt="@Lang.PlayerAvatar">
-            <div class="profile-copy">
-                <strong>@Lang.Hello</strong>
-                <span>@(string.IsNullOrWhiteSpace(Model.Msisdn) ? "-" : Model.Msisdn)</span>
-            </div>
-        </div>
-        <div class="play-top-stats" aria-label="Player status">
-            <span class="play-top-chip">
-                <img src="~/kitty/assets/kitty-fall/coin.png" alt="">
-                <strong>@Model.TotalPoint.ToString("N0")</strong>
-            </span>
-            <span class="play-top-chip">
-                <img src="~/kitty/assets/kitty-fall/ticket.png" alt="">
-                <strong>@Model.TotalTurn.ToString("N0")</strong>
-                <b>+</b>
-            </span>
-        </div>
-        <button class="menu-button" aria-label="@Lang.OpenMenu">
-            <span></span>
-            <span></span>
-            <span></span>
-        </button>
-    </header>
+	<header class="topbar">
+		<div class="profile">
+			<img class="profile-avatar" src="@avatarUrl" alt="@Lang.PlayerAvatar">
+			<div class="profile-copy">
+				<strong>@Lang.Hello</strong>
+				<span>@(string.IsNullOrWhiteSpace(Model.Msisdn) ? "-" : Model.Msisdn)</span>
+			</div>
+		</div>
+		<div class="play-top-stats" aria-label="Player status">
+			<span class="play-top-chip">
+				<img src="~/kitty/assets/kitty-fall/coin.png" alt="">
+				<strong>@Model.TotalPoint.ToString("N0")</strong>
+			</span>
+			<span class="play-top-chip">
+				<img src="~/kitty/assets/kitty-fall/ticket.png" alt="">
+				<strong>@Model.TotalTurn.ToString("N0")</strong>
+				<b>+</b>
+			</span>
+		</div>
+		<button class="menu-button" aria-label="@Lang.OpenMenu">
+			<span></span>
+			<span></span>
+			<span></span>
+		</button>
+	</header>
 
-    <section class="play-screen" aria-label="Choose your challenge" aria-hidden="false">
-        <div class="challenge-content">
-            <img class="challenge-title" src="~/kitty/assets/kitty-fall/challenge-title.png" alt="Choose your challenge">
+	<section class="play-screen" aria-label="Choose your challenge" aria-hidden="false">
+		<div class="challenge-content">
+			<img class="challenge-title" src="~/kitty/assets/kitty-fall/challenge-title.png" alt="Choose your challenge">
 
-            <p class="challenge-hint">@Lang.SelectDifficultyLevelAndStartAdventure</p>
+			<p class="challenge-hint">@Lang.SelectDifficultyLevelAndStartAdventure</p>
 
-            <div class="level-list">
-                <button class="level-card is-easy is-selected" type="button" data-level="Easy" aria-pressed="true">
-                    <span class="level-image">
-                        <img src="~/kitty/assets/kitty-fall/cat.png" alt="">
-                    </span>
-                    <span class="level-copy">
-                        <strong><span class="level-paw" aria-hidden="true"></span>@Lang.Easy</strong>
-                        <span>@Lang.SuitableForBeginners</span>
-                        <span>@Lang.FewerObstacles</span>
-                        <span>@Lang.EasierToCompleteLevels</span>
-                        <span>@Lang.StandardScoreRewards</span>
-                        <em>@Lang.Rewards: <b>+10-50</b> @Lang.Points</em>
-                    </span>
-                </button>
+			<div class="level-list">
+				<button class="level-card is-easy is-selected" type="button" data-level="Easy" aria-pressed="true">
+					<span class="level-image">
+						<img src="~/kitty/assets/kitty-fall/cat.png" alt="">
+					</span>
+					<span class="level-copy">
+						<strong><span class="level-paw" aria-hidden="true"></span>@Lang.Easy</strong>
+						<span>@Lang.SuitableForBeginners</span>
+						<span>@Lang.FewerObstacles</span>
+						<span>@Lang.EasierToCompleteLevels</span>
+						<span>@Lang.StandardScoreRewards</span>
+						<em>@Lang.Rewards: <b>+10-50</b> @Lang.Points</em>
+					</span>
+				</button>
 
-                <button class="level-card is-medium" type="button" data-level="Medium" aria-pressed="false">
-                    <span class="level-image">
-                        <img src="~/kitty/assets/kitty-fall/cat.png" alt="">
-                    </span>
-                    <span class="level-copy">
-                        <strong><span class="level-paw" aria-hidden="true"></span>@Lang.Medium</strong>
-                        <span>@Lang.BalancedChallenge</span>
-                        <span>@Lang.MoreObstacles</span>
-                        <span>@Lang.RequiresBetterSkills</span>
-                        <span>@Lang.HigherScoreRewards</span>
-                        <em>@Lang.Rewards: <b>+20-100</b> @Lang.Points</em>
-                    </span>
-                </button>
+				<button class="level-card is-medium" type="button" data-level="Medium" aria-pressed="false">
+					<span class="level-image">
+						<img src="~/kitty/assets/kitty-fall/cat.png" alt="">
+					</span>
+					<span class="level-copy">
+						<strong><span class="level-paw" aria-hidden="true"></span>@Lang.Medium</strong>
+						<span>@Lang.BalancedChallenge</span>
+						<span>@Lang.MoreObstacles</span>
+						<span>@Lang.RequiresBetterSkills</span>
+						<span>@Lang.HigherScoreRewards</span>
+						<em>@Lang.Rewards: <b>+20-100</b> @Lang.Points</em>
+					</span>
+				</button>
 
-                <button class="level-card is-hard" type="button" data-level="Hard" aria-pressed="false">
-                    <span class="level-image">
-                        <img src="~/kitty/assets/kitty-fall/cat.png" alt="">
-                    </span>
-                    <span class="level-copy">
-                        <strong><span class="level-paw" aria-hidden="true"></span>@Lang.Hard</strong>
-                        <span>@Lang.ForExperiencedPlayers</span>
-                        <span>@Lang.MostChallengingLevels</span>
-                        <span>@Lang.MaximumToCompleteLevels</span>
-                        <span>@Lang.ClimbToLeaderboard</span>
-                        <em>@Lang.Rewards: <b>+100-200</b> @Lang.Points</em>
-                    </span>
-                </button>
-            </div>
-        </div>
-    </section>
+				<button class="level-card is-hard" type="button" data-level="Hard" aria-pressed="false">
+					<span class="level-image">
+						<img src="~/kitty/assets/kitty-fall/cat.png" alt="">
+					</span>
+					<span class="level-copy">
+						<strong><span class="level-paw" aria-hidden="true"></span>@Lang.Hard</strong>
+						<span>@Lang.ForExperiencedPlayers</span>
+						<span>@Lang.MostChallengingLevels</span>
+						<span>@Lang.MaximumToCompleteLevels</span>
+						<span>@Lang.ClimbToLeaderboard</span>
+						<em>@Lang.Rewards: <b>+100-200</b> @Lang.Points</em>
+					</span>
+				</button>
+			</div>
+		</div>
+	</section>
 
-    <section class="embedded-game-screen" data-game-shell aria-label="Kitty Fall game" aria-hidden="true">
-        <iframe class="embedded-game-frame" id="gameFrame" data-game-frame title="Kitty Fall game"></iframe>
-    </section>
+	<section class="embedded-game-screen" data-game-shell aria-label="Kitty Fall game" aria-hidden="true">
+		<iframe class="embedded-game-frame" id="gameFrame" data-game-frame title="Kitty Fall game"></iframe>
+	</section>
 
-    <div class="play-action-bar">
-        <a class="play-back-button" href="/">
-            <span aria-hidden="true"></span>
-            <strong>@Lang.Back</strong>
-        </a>
-        <button class="play-start-button" type="button" data-start-game>
-            <span class="play-icon">
-                <img src="~/kitty/assets/kitty-fall/play-icon.svg" alt="">
-            </span>
-            <strong>@Lang.PlayNow</strong>
-        </button>
-    </div>
+	<div class="play-action-bar">
+		<a class="play-back-button" href="/">
+			<span aria-hidden="true"></span>
+			<strong>@Lang.Back</strong>
+		</a>
+		<button class="play-start-button" type="button" data-start-game>
+			<span class="play-icon">
+				<img src="~/kitty/assets/kitty-fall/play-icon.svg" alt="">
+			</span>
+			<strong>@Lang.PlayNow</strong>
+		</button>
+	</div>
 
-    <section class="game-over-modal" data-game-over-modal aria-hidden="true">
-        <div class="game-over-backdrop"></div>
-        <div class="game-over-card">
-            <button class="game-over-close" type="button" data-game-over-close aria-label="Close">×</button>
-            <img class="game-over-cat" src="~/kitty/assets/kitty-fall/cat.png" alt="">
-            <h2>Game Over</h2>
-            <p>@Lang.Rewards</p>
-            <div class="game-over-summary">
-                <div>
-                    <span>@Lang.AccountPoints</span>
-                    <strong data-game-over-score>0</strong>
-                </div>
-                <div>
-                    <span>@Lang.AccountMoney</span>
-                    <strong data-game-over-money>0</strong>
-                </div>
-            </div>
-            <a class="game-over-play-button" href="@Url.Action("Index", "Play")">
-                <span class="play-icon">
-                    <img src="~/kitty/assets/kitty-fall/play-icon.svg" alt="">
-                </span>
-                <strong>@Lang.PlayNow</strong>
-            </a>
-        </div>
-    </section>
+	<section class="game-over-modal" data-game-over-modal aria-hidden="true">
+		<div class="game-over-backdrop"></div>
+		<div class="game-over-card">
+			<button class="game-over-close" type="button" data-game-over-close aria-label="Close">×</button>
+			<img class="game-over-cat" src="~/kitty/assets/kitty-fall/cat.png" alt="">
+			<h2>Game Over</h2>
+			<p>@Lang.Rewards</p>
+			<div class="game-over-summary">
+				<div>
+					<span>@Lang.AccountPoints</span>
+					<strong data-game-over-score>0</strong>
+				</div>
+				<div>
+					<span>@Lang.AccountMoney</span>
+					<strong data-game-over-money>0</strong>
+				</div>
+			</div>
+			<a class="game-over-play-button" href="@Url.Action("Index", "Play")">
+				<span class="play-icon">
+					<img src="~/kitty/assets/kitty-fall/play-icon.svg" alt="">
+				</span>
+				<strong>@Lang.PlayNow</strong>
+			</a>
+		</div>
+	</section>
 
-    <section class="game-over-modal" data-turn-over-modal aria-hidden="true">
-        <div class="game-over-backdrop" data-turn-over-close></div>
-        <div class="game-over-card turn-over-card" role="dialog" aria-modal="true" aria-labelledby="turn-over-title">
-            <button class="game-over-close" type="button" data-turn-over-close aria-label="@Lang.Close">×</button>
-            <div class="turn-over-hero" aria-hidden="true">
-                <img class="game-over-cat" src="~/kitty/assets/kitty-fall/turn-over-sad-cat.png" alt="">
-                <span class="turn-over-zero">0</span>
-            </div>
-            <h2 id="turn-over-title">@Lang.TurnOverTitle</h2>
-            <p class="turn-over-copy">
-                <strong>@Lang.TurnOverPrimary</strong>
-                <span>@Lang.TurnOverSecondary</span>
-            </p>
-            <a class="game-over-play-button" href="@Url.Action("Index", "Home", new { buyturn = true })">
-                <span class="play-icon">
-                    <img src="~/kitty/assets/kitty-fall/ticket.png" alt="">
-                </span>
-                <strong>@Lang.BuyMoreTurns</strong>
-            </a>
-        </div>
-    </section>
+	<section class="game-over-modal" data-turn-over-modal aria-hidden="true">
+		<div class="game-over-backdrop" data-turn-over-close></div>
+		<div class="game-over-card turn-over-card" role="dialog" aria-modal="true" aria-labelledby="turn-over-title">
+			<button class="game-over-close" type="button" data-turn-over-close aria-label="@Lang.Close">×</button>
+			<div class="turn-over-hero" aria-hidden="true">
+				<img class="game-over-cat" src="~/kitty/assets/kitty-fall/turn-over-sad-cat.png" alt="">
+				<span class="turn-over-zero">0</span>
+			</div>
+			<h2 id="turn-over-title">@Lang.TurnOverTitle</h2>
+			<p class="turn-over-copy">
+				<strong>@Lang.TurnOverPrimary</strong>
+				<span>@Lang.TurnOverSecondary</span>
+			</p>
+			<a class="game-over-play-button" href="@Url.Action("Index", "Home", new { buyturn = true })">
+				<span class="play-icon">
+					<img src="~/kitty/assets/kitty-fall/ticket.png" alt="">
+				</span>
+				<strong>@Lang.BuyMoreTurns</strong>
+			</a>
+		</div>
+	</section>
 </main>

+ 196 - 193
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Shared/_Layout.cshtml

@@ -5,215 +5,218 @@
 <!DOCTYPE html>
 <html lang="en">
 <head>
-    <meta charset="utf-8" />
-    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
-    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>@ViewData["Title"]</title>
-    <link rel="stylesheet" href="~/kitty/css/all.min.css" asp-append-version="true" />
+	<meta charset="utf-8" />
+	<meta http-equiv="X-UA-Compatible" content="IE=edge" />
+	<meta name="viewport" content="width=device-width, initial-scale=1.0" />
+	<title>@ViewData["Title"]</title>
+	<link rel="stylesheet" href="~/kitty/css/all.min.css" asp-append-version="true" />
 </head>
 <body>
-@{
-    var controller = ViewContext.RouteData.Values["controller"]?.ToString() ?? "";
-    var isHome = string.Equals(controller, "Home", StringComparison.OrdinalIgnoreCase);
-    var isWinner = string.Equals(controller, "Winner", StringComparison.OrdinalIgnoreCase)
-        || string.Equals(controller, "DailyWinners", StringComparison.OrdinalIgnoreCase)
-        || string.Equals(controller, "MonthlyWinners", StringComparison.OrdinalIgnoreCase);
-    var isLeaderboard = string.Equals(controller, "Leaderboard", StringComparison.OrdinalIgnoreCase);
-    var isHistory = string.Equals(controller, "History", StringComparison.OrdinalIgnoreCase);
-    var isGuide = string.Equals(controller, "Guide", StringComparison.OrdinalIgnoreCase);
-    var showBottomNav = isHome || isWinner || isLeaderboard || isHistory || isGuide;
-    var navShellClass = isHome
-        ? "bottom-nav-shell home-screen scale-floor"
-        : "bottom-nav-shell scale-floor";
-    var currentCulture = System.Globalization.CultureInfo.CurrentUICulture.Name.ToLower().StartsWith("vi") ? "vi" : "en";
-    var accountMsisdn = Context.Session.GetComplexData<string>("msisdn") ?? "";
-    var accountProfilePicture = Context.Session.GetComplexData<string>("profilePicture") ?? "";
-    var accountAvatar = string.IsNullOrWhiteSpace(accountProfilePicture)
-        ? Url.Content("~/kitty/assets/kitty-fall/default.png")
-        : Url.Content("~/kitty/assets/kitty-fall/") + accountProfilePicture;
-    var accountScore = Context.Session.GetComplexData<long?>("totalPoint") ?? 0;
-    var accountMoney = Context.Session.GetComplexData<decimal?>("totalMoney") ?? 0;
-    var regInfos = Context.Session.GetComplexData<JArray>("regInfos");
-    var activeReg = regInfos?.FirstOrDefault(x =>
-    {
-        var statusText = (x["status"] ?? x["Status"])?.ToString() ?? "";
-        var isActive = bool.TryParse(statusText, out var activeFlag)
-            ? activeFlag
-            : decimal.TryParse(statusText, out var activeNumber) && activeNumber != 0;
-        var product = (x["productName"] ?? x["ProductName"])?.ToString() ?? "";
-        var isSubscription = product.Contains("_DAILY", StringComparison.OrdinalIgnoreCase)
-            || product.Contains("_DAYLY", StringComparison.OrdinalIgnoreCase)
-            || product.Contains("_WEEKLY", StringComparison.OrdinalIgnoreCase);
-        return isActive && isSubscription;
-    });
-    var packageNameRaw = (activeReg?["productName"] ?? activeReg?["ProductName"])?.ToString() ?? "";
-    var packageName = string.IsNullOrWhiteSpace(packageNameRaw)
-        ? Lang.AccountNoActivePackage
-        : packageNameRaw.Contains("WEEKLY", StringComparison.OrdinalIgnoreCase)
-            ? "Weekly Package"
-            : packageNameRaw.Contains("DAYLY", StringComparison.OrdinalIgnoreCase)
-                || packageNameRaw.Contains("DAILY", StringComparison.OrdinalIgnoreCase)
-                    ? Lang.AccountDailyPackage
-                    : packageNameRaw.Replace("_", " ");
-    var cancelKeyword = Configuration["AccountMenu:CancelKeyword"] ?? "OFF";
-    var cancelShortCode = Configuration["AccountMenu:CancelShortCode"] ?? "562";
-    var monthStart = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);
-    var monthEnd = monthStart.AddMonths(1).AddDays(-1);
-}
-    @RenderBody()
+	@{
+		var controller = ViewContext.RouteData.Values["controller"]?.ToString() ?? "";
+		var isHome = string.Equals(controller, "Home", StringComparison.OrdinalIgnoreCase);
+		var isWinner = string.Equals(controller, "Winner", StringComparison.OrdinalIgnoreCase)
+		|| string.Equals(controller, "DailyWinners", StringComparison.OrdinalIgnoreCase)
+		|| string.Equals(controller, "MonthlyWinners", StringComparison.OrdinalIgnoreCase);
+		var isLeaderboard = string.Equals(controller, "Leaderboard", StringComparison.OrdinalIgnoreCase);
+		var isHistory = string.Equals(controller, "History", StringComparison.OrdinalIgnoreCase);
+		var isGuide = string.Equals(controller, "Guide", StringComparison.OrdinalIgnoreCase);
+		var showBottomNav = isHome || isWinner || isLeaderboard || isHistory || isGuide;
+		var navShellClass = isHome
+		? "bottom-nav-shell home-screen scale-floor"
+		: "bottom-nav-shell scale-floor";
+		var currentCulture = System.Globalization.CultureInfo.CurrentUICulture.Name.ToLower().StartsWith("vi") ? "vi" : "en";
+		var accountMsisdn = Context.Session.GetComplexData<string>("msisdn") ?? "";
+		var accountProfilePicture = Context.Session.GetComplexData<string>("profilePicture") ?? "";
+		var accountAvatar = string.IsNullOrWhiteSpace(accountProfilePicture)
+		? Url.Content("~/kitty/assets/kitty-fall/default.png")
+		: Url.Content("~/kitty/assets/kitty-fall/") + accountProfilePicture;
+		var accountScore = Context.Session.GetComplexData<long?>("totalPoint") ?? 0;
+		var accountMoney = Context.Session.GetComplexData<decimal?>("totalMoney") ?? 0;
+		var regInfos = Context.Session.GetComplexData<JArray>("regInfos");
+		var activeReg = regInfos?.FirstOrDefault(x =>
+		{
+			var statusText = (x["status"] ?? x["Status"])?.ToString() ?? "";
+			var isActive = bool.TryParse(statusText, out var activeFlag)
+			? activeFlag
+			: decimal.TryParse(statusText, out var activeNumber) && activeNumber != 0;
+			var product = (x["productName"] ?? x["ProductName"])?.ToString() ?? "";
+			var isSubscription = product.Contains("_DAILY", StringComparison.OrdinalIgnoreCase)
+			|| product.Contains("_DAYLY", StringComparison.OrdinalIgnoreCase)
+			|| product.Contains("_WEEKLY", StringComparison.OrdinalIgnoreCase);
+			return isActive && isSubscription;
+		});
+		var packageNameRaw = (activeReg?["productName"] ?? activeReg?["ProductName"])?.ToString() ?? "";
+		var packageName = string.IsNullOrWhiteSpace(packageNameRaw)
+		? Lang.AccountNoActivePackage
+		: packageNameRaw.Contains("WEEKLY", StringComparison.OrdinalIgnoreCase)
+		? "Weekly Package"
+		: packageNameRaw.Contains("DAYLY", StringComparison.OrdinalIgnoreCase)
+		|| packageNameRaw.Contains("DAILY", StringComparison.OrdinalIgnoreCase)
+		? Lang.AccountDailyPackage
+		: packageNameRaw.Replace("_", " ");
+		var cancelKeyword = Configuration["AccountMenu:CancelKeyword"] ?? "OFF";
+		var cancelShortCode = Configuration["AccountMenu:CancelShortCode"] ?? "562";
+		var monthStart = new DateTime(DateTime.Now.Year, DateTime.Now.Month, 1);
+		var monthEnd = monthStart.AddMonths(1).AddDays(-1);
+	}
+	@RenderBody()
 
-    <div class="account-menu-modal" data-account-menu aria-hidden="true">
-        <button class="account-menu-backdrop" type="button" data-account-close aria-label="@Lang.AccountCloseMenu"></button>
-        <section class="account-menu-card" role="dialog" aria-modal="true" aria-label="@Lang.AccountTitle">
-            <div class="account-menu-ribbon"><span>@Lang.AccountTitle</span></div>
-            <button class="account-menu-close" type="button" data-account-close aria-label="@Lang.AccountCloseMenu">×</button>
+	<div class="account-menu-modal" data-account-menu aria-hidden="true">
+		<button class="account-menu-backdrop" type="button" data-account-close aria-label="@Lang.AccountCloseMenu"></button>
+		<section class="account-menu-card" role="dialog" aria-modal="true" aria-label="@Lang.AccountTitle">
+			<div class="account-menu-ribbon"><span>@Lang.AccountTitle</span></div>
+			<button class="account-menu-close" type="button" data-account-close aria-label="@Lang.AccountCloseMenu">×</button>
 
-            <div class="account-menu-content">
-                <section class="account-profile-card">
-                    <img class="account-profile-avatar" src="@accountAvatar" alt="@Lang.AccountAvatarAlt">
-                    <div>
-                        <small>@Lang.AccountPhoneNumber</small>
-                        <strong>@accountMsisdn</strong>
-                        <p>@Lang.AccountDefaultDescription</p>
-                    </div>
-                </section>
+			<div class="account-menu-content">
+				<section class="account-profile-card">
+					<img class="account-profile-avatar" src="@accountAvatar" alt="@Lang.AccountAvatarAlt">
+					<div>
+						<small>@Lang.AccountPhoneNumber</small>
+						<strong>@accountMsisdn</strong>
+						<p>@Lang.AccountDefaultDescription</p>
+					</div>
+				</section>
 
-                <section class="account-summary-card">
-                    <h2><span>▣</span> @Lang.AccountTotalThisMonth</h2>
-                    <div class="account-summary-grid">
-                        <div class="account-summary-item is-score">
-                            <img src="~/kitty/assets/kitty-fall/coin.png" alt="">
-                            <span><small>@Lang.AccountPoints</small><strong>@accountScore.ToString("N0")</strong><em>@Lang.AccountTotalPoints</em></span>
-                        </div>
-                        <div class="account-summary-item is-money">
-                            <img src="~/kitty/assets/kitty-fall/nav-winner.png" alt="">
-                            <span><small>@Lang.AccountMoney</small><strong>@accountMoney.ToString("N0")</strong><em>@Lang.AccountTotalMoneyWon</em></span>
-                        </div>
-                    </div>
-                    <p class="account-summary-note">
-                        <span>i</span> @string.Format(Lang.AccountMonthlySummary, monthStart.ToString("dd/MM"), monthEnd.ToString("dd/MM"))
-                    </p>
-                </section>
+				<section class="account-summary-card">
+					<h2><span>▣</span> @Lang.AccountTotalThisMonth</h2>
+					<div class="account-summary-grid">
+						<div class="account-summary-item is-score">
+							<img src="~/kitty/assets/kitty-fall/coin.png" alt="">
+							<span><small>@Lang.AccountPoints</small><strong>@accountScore.ToString("N0")</strong><em>@Lang.AccountTotalPoints</em></span>
+						</div>
+						<div class="account-summary-item is-money">
+							<img src="~/kitty/assets/kitty-fall/nav-winner.png" alt="">
+							<span><small>@Lang.AccountMoney</small><strong>@accountMoney.ToString("N0")</strong><em>@Lang.AccountTotalMoneyWon</em></span>
+						</div>
+					</div>
+					<p class="account-summary-note">
+						<span>i</span> @string.Format(Lang.AccountMonthlySummary, monthStart.ToString("dd/MM"), monthEnd.ToString("dd/MM"))
+					</p>
+				</section>
 
-                <section class="account-package-card">
-                    <span class="account-section-icon">★</span>
-                    <div class="account-package-info">
-                        <small>
-                            @Lang.AccountCurrentPackage
-                            @if (activeReg != null) { <b>@Lang.AccountActiveSubscription</b> }
-                        </small>
-                        <strong>@packageName</strong>
-                    </div>
-                </section>
+				<section class="account-package-card">
+					<span class="account-section-icon">★</span>
+					<div class="account-package-info">
+						<small>
+							@Lang.AccountCurrentPackage
+							@if (activeReg != null)
+							{
+								<b>@Lang.AccountActiveSubscription</b>
+							}
+						</small>
+						<strong>@packageName</strong>
+					</div>
+				</section>
 
-                <section class="account-cancel-card">
-                    <span class="account-section-icon">☏</span>
-                    <div>
-                        <small>@Lang.AccountHowToCancel</small>
-                        <p>@Lang.AccountCancelInstruction</p>
-                        <strong>@string.Format(Lang.AccountCancelCommand, cancelKeyword, cancelShortCode)</strong>
-                        <p>@Lang.AccountFollowInstructions</p>
-                    </div>
-                    <img src="~/kitty/assets/kitty-fall/account-cat-cancel.png" alt="@Lang.AccountCancelCatAlt">
-                </section>
+				<section class="account-cancel-card">
+					<span class="account-section-icon">☏</span>
+					<div>
+						<small>@Lang.AccountHowToCancel</small>
+						<p>@Lang.AccountCancelInstruction</p>
+						<strong>@string.Format(Lang.AccountCancelCommand, cancelKeyword, cancelShortCode)</strong>
+						<p>@Lang.AccountFollowInstructions</p>
+					</div>
+					<img src="~/kitty/assets/kitty-fall/account-cat-cancel.png" alt="@Lang.AccountCancelCatAlt">
+				</section>
 
-                <section class="account-language-card">
-                    <span class="account-section-icon">◎</span>
-                    <div class="account-language-main">
-                        <small>@Lang.AccountLanguage</small>
-                        <div class="account-language-options">
-                            <button class="account-language-option @(currentCulture == "en" ? "active" : null)" type="button" onclick="changeLang('en')">
-                                <img src="~/kitty/assets/kitty-fall/flag-gb.svg" alt=""><strong>@Lang.AccountEnglish</strong><b>✓</b>
-                            </button>
-                            <button class="account-language-option @(currentCulture == "vi" ? "active" : null)" type="button" onclick="changeLang('vi')">
-                                <img src="~/kitty/assets/kitty-fall/flag-ht.svg" alt=""><strong>@Lang.AccountMyanmar</strong><b>✓</b>
-                            </button>
-                        </div>
-                    </div>
-                </section>
+				<section class="account-language-card">
+					<span class="account-section-icon">◎</span>
+					<div class="account-language-main">
+						<small>@Lang.AccountLanguage</small>
+						<div class="account-language-options">
+							<button class="account-language-option @(currentCulture == "en" ? "active" : null)" type="button" onclick="changeLang('en')">
+								<img src="~/kitty/assets/kitty-fall/flag-gb.svg" alt=""><strong>@Lang.AccountEnglish</strong><b>✓</b>
+							</button>
+							<button class="account-language-option @(currentCulture == "vi" ? "active" : null)" type="button" onclick="changeLang('vi')">
+								<img src="~/kitty/assets/kitty-fall/flag-ht.svg" alt=""><strong>@Lang.AccountMyanmar</strong><b>✓</b>
+							</button>
+						</div>
+					</div>
+				</section>
 
-                <p class="account-security-note"><span>×</span>@Lang.AccountSecurityNote</p>
-            </div>
-        </section>
-    </div>
+				<p class="account-security-note"><span>×</span>@Lang.AccountSecurityNote</p>
+			</div>
+		</section>
+	</div>
 
-    @if (showBottomNav)
-    {
-        <nav class="@navShellClass" data-scale-floor="368" data-scale-height="60" data-scale-var="--bottom-nav-height" aria-label="Main navigation">
-            <div class="bottom-nav scale-floor-inner">
-                <a class="@(isHome ? "active" : null)" href="/">
-                    <img src="~/kitty/assets/kitty-fall/nav-home.png" alt="">
-                    <span>@Lang.Home</span>
-                </a>
-                <a class="@(isWinner ? "active" : null)" href="/Winner">
-                    <img src="~/kitty/assets/kitty-fall/nav-winner.png" alt="">
-                    <span>@Lang.WinnerList</span>
-                </a>
-                <a class="@(isLeaderboard ? "active" : null)" href="/Leaderboard">
-                    <img src="~/kitty/assets/kitty-fall/nav-leaderboard.png" alt="">
-                    <span>@Lang.Leaderboard</span>
-                </a>
-                <a class="@(isHistory ? "active" : null)" href="/History">
-                    <img src="~/kitty/assets/kitty-fall/nav-history.png" alt="">
-                    <span>@Lang.History</span>
-                </a>
-                <a class="@(isGuide ? "active" : null)" href="/Guide">
-                    <img src="~/kitty/assets/kitty-fall/nav-guide.png" alt="">
-                    <span>@Lang.Guide</span>
-                </a>
-            </div>
-        </nav>
-    }
+	@if (showBottomNav)
+	{
+		<nav class="@navShellClass" data-scale-floor="368" data-scale-height="60" data-scale-var="--bottom-nav-height" aria-label="Main navigation">
+			<div class="bottom-nav scale-floor-inner">
+				<a class="@(isHome ? "active" : null)" href="/">
+					<img src="~/kitty/assets/kitty-fall/nav-home.png" alt="">
+					<span>@Lang.Home</span>
+				</a>
+				<a class="@(isWinner ? "active" : null)" href="/Winner">
+					<img src="~/kitty/assets/kitty-fall/nav-winner.png" alt="">
+					<span>@Lang.WinnerList</span>
+				</a>
+				<a class="@(isLeaderboard ? "active" : null)" href="/Leaderboard">
+					<img src="~/kitty/assets/kitty-fall/nav-leaderboard.png" alt="">
+					<span>@Lang.Leaderboard</span>
+				</a>
+				<a class="@(isHistory ? "active" : null)" href="/History">
+					<img src="~/kitty/assets/kitty-fall/nav-history.png" alt="">
+					<span>@Lang.History</span>
+				</a>
+				<a class="@(isGuide ? "active" : null)" href="/Guide">
+					<img src="~/kitty/assets/kitty-fall/nav-guide.png" alt="">
+					<span>@Lang.Guide</span>
+				</a>
+			</div>
+		</nav>
+	}
 
-    <script src="~/kitty/lib/jquery/jquery.min.js" asp-append-version="true"></script>
-    <script src="~/kitty/js/all.min.js" asp-append-version="true"></script>
-    <script>
-        function changeLang(lang) {
-            $.ajax({
-                url: "/Home/ChangeLanguage",
-                data: { lang: lang },
-                success: function () { location.reload(); }
-            });
-        }
+	<script src="~/kitty/lib/jquery/jquery.min.js" asp-append-version="true"></script>
+	<script src="~/kitty/js/all.min.js" asp-append-version="true"></script>
+	<script>
+		function changeLang(lang) {
+			$.ajax({
+				url: "/Home/ChangeLanguage",
+				data: { lang: lang },
+				success: function () { location.reload(); }
+			});
+		}
 
-        (function () {
-            const accountMenu = document.querySelector("[data-account-menu]");
-            if (!accountMenu) return;
+		(function () {
+			const accountMenu = document.querySelector("[data-account-menu]");
+			if (!accountMenu) return;
 
-            const openAccountMenu = function (event) {
-                event.preventDefault();
-                event.stopPropagation();
-                accountMenu.classList.add("is-open");
-                accountMenu.setAttribute("aria-hidden", "false");
-                document.body.classList.add("has-account-menu");
-                requestAnimationFrame(function () {
-                    accountMenu.querySelector(".account-menu-close")?.focus({ preventScroll: true });
-                });
-            };
+			const openAccountMenu = function (event) {
+				event.preventDefault();
+				event.stopPropagation();
+				accountMenu.classList.add("is-open");
+				accountMenu.setAttribute("aria-hidden", "false");
+				document.body.classList.add("has-account-menu");
+				requestAnimationFrame(function () {
+					accountMenu.querySelector(".account-menu-close")?.focus({ preventScroll: true });
+				});
+			};
 
-            const closeAccountMenu = function (event) {
-                event?.preventDefault();
-                event?.stopPropagation();
-                accountMenu.classList.remove("is-open");
-                accountMenu.setAttribute("aria-hidden", "true");
-                document.body.classList.remove("has-account-menu");
-            };
+			const closeAccountMenu = function (event) {
+				event?.preventDefault();
+				event?.stopPropagation();
+				accountMenu.classList.remove("is-open");
+				accountMenu.setAttribute("aria-hidden", "true");
+				document.body.classList.remove("has-account-menu");
+			};
 
-            document.querySelectorAll(".menu-button").forEach(function (button) {
-                button.addEventListener("click", openAccountMenu);
-            });
+			document.querySelectorAll(".menu-button").forEach(function (button) {
+				button.addEventListener("click", openAccountMenu);
+			});
 
-            accountMenu.querySelectorAll("[data-account-close]").forEach(function (button) {
-                button.addEventListener("click", closeAccountMenu);
-            });
+			accountMenu.querySelectorAll("[data-account-close]").forEach(function (button) {
+				button.addEventListener("click", closeAccountMenu);
+			});
 
-            document.addEventListener("keydown", function (event) {
-                if (event.key === "Escape" && accountMenu.classList.contains("is-open")) {
-                    closeAccountMenu(event);
-                }
-            });
-        })();
-    </script>
-    @await RenderSectionAsync("Scripts", required: false)
+			document.addEventListener("keydown", function (event) {
+				if (event.key === "Escape" && accountMenu.classList.contains("is-open")) {
+					closeAccountMenu(event);
+				}
+			});
+		})();
+	</script>
+	@await RenderSectionAsync("Scripts", required: false)
 </body>
 </html>

+ 62 - 62
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Winner/Index.cshtml

@@ -1,72 +1,72 @@
 @model Common.Http.Apis.Request.UserGameActivityProfile
 @{
-    ViewData["Title"] = $"Kitty Fall - {Lang.WinnerList}";
-    var profile = Model ?? new Common.Http.Apis.Request.UserGameActivityProfile();
-    var profilePicture = string.IsNullOrWhiteSpace(profile.picture)
-        ? Url.Content("~/kitty/assets/kitty-fall/default.png")
-        : Url.Content("~/kitty/assets/kitty-fall/") + profile.picture ;
+	ViewData["Title"] = $"Kitty Fall - {Lang.WinnerList}";
+	var profile = Model ?? new Common.Http.Apis.Request.UserGameActivityProfile();
+	var profilePicture = string.IsNullOrWhiteSpace(profile.picture)
+		? Url.Content("~/kitty/assets/kitty-fall/default.png")
+		: Url.Content("~/kitty/assets/kitty-fall/") + profile.picture;
 }
 
 <main class="kitty-app is-winner-screen" aria-label="@Lang.WinnerList">
-    <img class="kitty-bg" src="~/kitty/assets/kitty-fall/bg.png" alt="">
-    <div class="kitty-bg-fade"></div>
+	<img class="kitty-bg" src="~/kitty/assets/kitty-fall/bg.png" alt="">
+	<div class="kitty-bg-fade"></div>
 
-    <header class="topbar">
-        <div class="profile">
-            <img class="profile-avatar" src="@profilePicture" alt="@Lang.PlayerAvatar">
-            <div class="profile-copy">
-                <strong>@Lang.Hello</strong>
-                <span>@profile.msisdn</span>
-            </div>
-        </div>
-        <button class="menu-button" aria-label="@Lang.OpenMenu">
-            <span></span>
-            <span></span>
-            <span></span>
-        </button>
-    </header>
+	<header class="topbar">
+		<div class="profile">
+			<img class="profile-avatar" src="@profilePicture" alt="@Lang.PlayerAvatar">
+			<div class="profile-copy">
+				<strong>@Lang.Hello</strong>
+				<span>@profile.msisdn</span>
+			</div>
+		</div>
+		<button class="menu-button" aria-label="@Lang.OpenMenu">
+			<span></span>
+			<span></span>
+			<span></span>
+		</button>
+	</header>
 
-    <section class="winner-screen" aria-label="@Lang.WinnerList">
-        <div class="winner-content">
-            <img class="winner-hero" src="~/kitty/assets/kitty-fall/winner-hero.png" alt="@Lang.WinnerList">
+	<section class="winner-screen" aria-label="@Lang.WinnerList">
+		<div class="winner-content">
+			<img class="winner-hero" src="~/kitty/assets/kitty-fall/winner-hero.png" alt="@Lang.WinnerList">
 
-            <div class="winner-cards">
-                <article class="winner-card is-daily">
-                    <img class="winner-card-image" src="~/kitty/assets/kitty-fall/winner-daily.png" alt="">
-                    <div class="winner-card-copy">
-                        <h2><span aria-hidden="true"></span>@Lang.DailyWinners<span aria-hidden="true"></span></h2>
-                        <p>@Lang.DailyWinnersDescription</p>
-                        <div class="winner-table-head">
-                            <span>@Lang.PhoneNumber</span>
-                            <span>@Lang.Time</span>
-                            <span>@Lang.Rewards</span>
-                        </div>
-                        <div class="winner-divider" aria-hidden="true"></div>
-                        <a class="winner-card-button" href="/DailyWinners">
-                            <span>@Lang.ViewDailyWinners</span>
-                            <b aria-hidden="true"></b>
-                        </a>
-                    </div>
-                </article>
+			<div class="winner-cards">
+				<article class="winner-card is-daily">
+					<img class="winner-card-image" src="~/kitty/assets/kitty-fall/winner-daily.png" alt="">
+					<div class="winner-card-copy">
+						<h2><span aria-hidden="true"></span>@Lang.DailyWinners<span aria-hidden="true"></span></h2>
+						<p>@Lang.DailyWinnersDescription</p>
+						<div class="winner-table-head">
+							<span>@Lang.PhoneNumber</span>
+							<span>@Lang.Time</span>
+							<span>@Lang.Rewards</span>
+						</div>
+						<div class="winner-divider" aria-hidden="true"></div>
+						<a class="winner-card-button" href="/DailyWinners">
+							<span>@Lang.ViewDailyWinners</span>
+							<b aria-hidden="true"></b>
+						</a>
+					</div>
+				</article>
 
-                <article class="winner-card is-monthly">
-                    <img class="winner-card-image" src="~/kitty/assets/kitty-fall/winner-monthly.png" alt="">
-                    <div class="winner-card-copy">
-                        <h2><span aria-hidden="true"></span>@Lang.MonthlyWinners</h2>
-                        <p>@Lang.MonthlyWinnersDescription</p>
-                        <div class="winner-table-head">
-                            <span>@Lang.FirstPrize</span>
-                            <span>@Lang.SecondPrize</span>
-                            <span>@Lang.ThirdPrize</span>
-                        </div>
-                        <div class="winner-divider" aria-hidden="true"></div>
-                        <a class="winner-card-button" href="/MonthlyWinners">
-                            <span>@Lang.ViewMonthlyWinners</span>
-                            <b aria-hidden="true"></b>
-                        </a>
-                    </div>
-                </article>
-            </div>
-        </div>
-    </section>
+				<article class="winner-card is-monthly">
+					<img class="winner-card-image" src="~/kitty/assets/kitty-fall/winner-monthly.png" alt="">
+					<div class="winner-card-copy">
+						<h2><span aria-hidden="true"></span>@Lang.MonthlyWinners</h2>
+						<p>@Lang.MonthlyWinnersDescription</p>
+						<div class="winner-table-head">
+							<span>@Lang.FirstPrize</span>
+							<span>@Lang.SecondPrize</span>
+							<span>@Lang.ThirdPrize</span>
+						</div>
+						<div class="winner-divider" aria-hidden="true"></div>
+						<a class="winner-card-button" href="/MonthlyWinners">
+							<span>@Lang.ViewMonthlyWinners</span>
+							<b aria-hidden="true"></b>
+						</a>
+					</div>
+				</article>
+			</div>
+		</div>
+	</section>
 </main>

+ 0 - 72
Kitty_Fall/Kitty_Fall/sql/audit_game_reward_type.sql

@@ -1,72 +0,0 @@
--- Chi kiem tra, khong cap nhat du lieu.
-
--- 1. Tim reward log bi khac loai so voi ket qua random da luu tren attempt.
-SELECT
-    a.ATTEMPT_ID,
-    a.MSISDN,
-    a.ROOT_TRANS_ID,
-    a.CHILD_TRANS_ID,
-    a.MODE_ID,
-    a.LEVEL_NO,
-    a.REWARD_OPTION_ID,
-    a.REWARD_TYPE AS RANDOM_REWARD_TYPE,
-    a.REWARD_VALUE AS RANDOM_REWARD_VALUE,
-    a.REWARD_UNIT AS RANDOM_REWARD_UNIT,
-    r.REWARD_LOG_ID,
-    r.REWARD_TYPE AS LOG_REWARD_TYPE,
-    r.REWARD_VALUE AS LOG_REWARD_VALUE,
-    r.REWARD_UNIT AS LOG_REWARD_UNIT,
-    r.STATUS AS LOG_STATUS,
-    r.RESPONSE_CODE,
-    r.RESPONSE_MESSAGE,
-    r.CREATED_TIME,
-    r.UPDATED_TIME
-FROM GAME_LEVEL_ATTEMPT a
-JOIN GAME_REWARD_LOG r ON r.ATTEMPT_ID = a.ATTEMPT_ID
-WHERE a.REWARD_TYPE IS NOT NULL
-  AND (
-      UPPER(TRIM(a.REWARD_TYPE)) <> UPPER(TRIM(r.REWARD_TYPE))
-      OR NVL(a.REWARD_VALUE, -1) <> NVL(r.REWARD_VALUE, -1)
-      OR NVL(UPPER(TRIM(a.REWARD_UNIT)), '-') <> NVL(UPPER(TRIM(r.REWARD_UNIT)), '-')
-  )
-ORDER BY r.CREATED_TIME DESC, r.REWARD_LOG_ID DESC;
-
--- 2. Tim attempt khac loai so voi option DB da duoc random.
-SELECT
-    a.ATTEMPT_ID,
-    a.MSISDN,
-    a.ROOT_TRANS_ID,
-    a.LEVEL_NO,
-    a.REWARD_OPTION_ID,
-    a.REWARD_TYPE AS RANDOM_REWARD_TYPE,
-    o.REWARD_TYPE AS CONFIG_REWARD_TYPE,
-    a.REWARD_VALUE AS RANDOM_REWARD_VALUE,
-    o.REWARD_VALUE AS CONFIG_REWARD_VALUE,
-    a.CREATED_TIME
-FROM GAME_LEVEL_ATTEMPT a
-JOIN GAME_LEVEL_REWARD_OPTION o ON o.REWARD_OPTION_ID = a.REWARD_OPTION_ID
-WHERE UPPER(TRIM(a.REWARD_TYPE)) <> UPPER(TRIM(o.REWARD_TYPE))
-   OR NVL(a.REWARD_VALUE, -1) <> NVL(o.REWARD_VALUE, -1)
-   OR NVL(UPPER(TRIM(a.REWARD_UNIT)), '-') <> NVL(UPPER(TRIM(o.REWARD_UNIT)), '-')
-ORDER BY a.CREATED_TIME DESC, a.ATTEMPT_ID DESC;
-
--- 3. Tim cau hinh han muc dang cho phep doi cheo loai reward.
-SELECT
-    LIMIT_CONFIG_ID,
-    LIMIT_CODE,
-    LIMIT_NAME,
-    REWARD_TYPE,
-    REWARD_UNIT,
-    EXCEED_ACTION,
-    FALLBACK_REWARD_TYPE,
-    FALLBACK_REWARD_VALUE,
-    FALLBACK_REWARD_UNIT,
-    EFFECTIVE_FROM,
-    EFFECTIVE_TO,
-    STATUS
-FROM GAME_REWARD_LIMIT_CONFIG
-WHERE STATUS = 1
-  AND UPPER(TRIM(EXCEED_ACTION)) = 'FALLBACK'
-  AND FALLBACK_REWARD_TYPE IS NOT NULL
-  AND UPPER(TRIM(REWARD_TYPE)) <> UPPER(TRIM(FALLBACK_REWARD_TYPE))
-ORDER BY PRIORITY DESC, LIMIT_CONFIG_ID;

+ 0 - 91
Kitty_Fall/Kitty_Fall/sql/monthly_winner_one_user_per_prize.sql

@@ -1,91 +0,0 @@
--- Nang cap online:
--- 1. So winner cua moi giai lay theo GAME_MONTHLY_PRIZE_CONFIG.QUANTITY.
--- 2. Mot user chi duoc trung mot giai trong cung mot ky quay.
--- Chay thu cong bang user schema dang so huu cac bang GAME_*.
-
-WHENEVER SQLERROR EXIT SQL.SQLCODE ROLLBACK;
-
--- Dung ngay neu du lieu hien tai co mot user trung nhieu hon mot giai trong cung draw.
-DECLARE
-    V_DUPLICATE_USER NUMBER;
-BEGIN
-    SELECT COUNT(*)
-      INTO V_DUPLICATE_USER
-      FROM (
-          SELECT DRAW_ID, MSISDN
-            FROM GAME_MONTHLY_WINNER
-           GROUP BY DRAW_ID, MSISDN
-          HAVING COUNT(*) > 1
-      );
-
-    IF V_DUPLICATE_USER > 0 THEN
-        RAISE_APPLICATION_ERROR(
-            -20001,
-            'GAME_MONTHLY_WINNER co user trung nhieu giai trong cung draw. duplicateUser=' ||
-            V_DUPLICATE_USER || '. Can doi soat du lieu truoc khi nang cap.'
-        );
-    END IF;
-END;
-/
-
--- Bo constraint sai neu da tung chay ban SQL gioi han mot winner cho moi PrizeCode.
-DECLARE
-    V_COUNT NUMBER;
-BEGIN
-    SELECT COUNT(*)
-      INTO V_COUNT
-      FROM USER_CONSTRAINTS
-     WHERE TABLE_NAME = 'GAME_MONTHLY_WINNER'
-       AND CONSTRAINT_NAME = 'UK_GMW_DRAW_PRIZE';
-
-    IF V_COUNT > 0 THEN
-        EXECUTE IMMEDIATE
-            'ALTER TABLE GAME_MONTHLY_WINNER DROP CONSTRAINT UK_GMW_DRAW_PRIZE';
-    END IF;
-END;
-/
-
--- Giu rang buoc cu: khong ghi trung cung user vao cung PrizeCode trong mot draw.
-DECLARE
-    V_COUNT NUMBER;
-BEGIN
-    SELECT COUNT(*)
-      INTO V_COUNT
-      FROM USER_CONSTRAINTS
-     WHERE TABLE_NAME = 'GAME_MONTHLY_WINNER'
-       AND CONSTRAINT_NAME = 'UK_GMW_DRAW_PRIZE_MSISDN';
-
-    IF V_COUNT = 0 THEN
-        EXECUTE IMMEDIATE
-            'ALTER TABLE GAME_MONTHLY_WINNER ADD CONSTRAINT UK_GMW_DRAW_PRIZE_MSISDN UNIQUE (DRAW_ID, PRIZE_CODE, MSISDN)';
-    END IF;
-END;
-/
-
--- Khoa cung o DB: mot user khong duoc trung hai giai trong cung draw.
-DECLARE
-    V_COUNT NUMBER;
-BEGIN
-    SELECT COUNT(*)
-      INTO V_COUNT
-      FROM USER_CONSTRAINTS
-     WHERE TABLE_NAME = 'GAME_MONTHLY_WINNER'
-       AND CONSTRAINT_NAME = 'UK_GMW_DRAW_MSISDN';
-
-    IF V_COUNT = 0 THEN
-        EXECUTE IMMEDIATE
-            'ALTER TABLE GAME_MONTHLY_WINNER ADD CONSTRAINT UK_GMW_DRAW_MSISDN UNIQUE (DRAW_ID, MSISDN)';
-    END IF;
-END;
-/
-
--- Kiem tra cau hinh so winner va constraint sau nang cap.
-SELECT PRIZE_CODE, QUANTITY, STATUS
-  FROM GAME_MONTHLY_PRIZE_CONFIG
- ORDER BY DISPLAY_ORDER, PRIZE_CONFIG_ID;
-
-SELECT CONSTRAINT_NAME, STATUS
-  FROM USER_CONSTRAINTS
- WHERE TABLE_NAME = 'GAME_MONTHLY_WINNER'
-   AND CONSTRAINT_NAME IN ('UK_GMW_DRAW_PRIZE_MSISDN', 'UK_GMW_DRAW_MSISDN')
- ORDER BY CONSTRAINT_NAME;

+ 0 - 72
Kitty_Fall/Kitty_Fall/sql/sync_game_sequences.sql

@@ -1,72 +0,0 @@
--- Dong bo sequence voi ID lon nhat dang co trong cac bang game.
--- Script chi tien sequence ve phia truoc, khong sua/xoa du lieu nghiep vu.
--- Nen chay bang schema KITTY_FALL truoc khi deploy API/Worker moi.
-
-SET SERVEROUTPUT ON;
-
-DECLARE
-    PROCEDURE sync_sequence(
-        p_sequence_name IN VARCHAR2,
-        p_table_name    IN VARCHAR2,
-        p_id_column     IN VARCHAR2
-    ) IS
-        v_sequence_name VARCHAR2(128);
-        v_table_name    VARCHAR2(128);
-        v_id_column     VARCHAR2(128);
-        v_max_id        NUMBER;
-        v_current_id    NUMBER;
-        v_increment     NUMBER;
-    BEGIN
-        v_sequence_name := DBMS_ASSERT.SIMPLE_SQL_NAME(UPPER(p_sequence_name));
-        v_table_name := DBMS_ASSERT.SIMPLE_SQL_NAME(UPPER(p_table_name));
-        v_id_column := DBMS_ASSERT.SIMPLE_SQL_NAME(UPPER(p_id_column));
-
-        EXECUTE IMMEDIATE
-            'SELECT NVL(MAX(' || v_id_column || '), 0) FROM ' || v_table_name
-            INTO v_max_id;
-
-        EXECUTE IMMEDIATE
-            'SELECT ' || v_sequence_name || '.NEXTVAL FROM DUAL'
-            INTO v_current_id;
-
-        IF v_current_id < v_max_id THEN
-            v_increment := v_max_id - v_current_id;
-            EXECUTE IMMEDIATE
-                'ALTER SEQUENCE ' || v_sequence_name || ' INCREMENT BY ' || TO_CHAR(v_increment);
-            EXECUTE IMMEDIATE
-                'SELECT ' || v_sequence_name || '.NEXTVAL FROM DUAL'
-                INTO v_current_id;
-            EXECUTE IMMEDIATE
-                'ALTER SEQUENCE ' || v_sequence_name || ' INCREMENT BY 1';
-        END IF;
-
-        -- Khong dung DBMS_OUTPUT: mot so schema production khong duoc cap
-        -- EXECUTE tren package nay. Ket qua duoc xac nhan bang cac SELECT ben duoi.
-    EXCEPTION
-        WHEN OTHERS THEN
-            BEGIN
-                EXECUTE IMMEDIATE
-                    'ALTER SEQUENCE ' || v_sequence_name || ' INCREMENT BY 1';
-            EXCEPTION
-                WHEN OTHERS THEN NULL;
-            END;
-            RAISE;
-    END;
-BEGIN
-    sync_sequence('SEQ_GAME_PLAY_SESSION',        'GAME_PLAY_SESSION',        'SESSION_ID');
-    sync_sequence('SEQ_GAME_LEVEL_ATTEMPT',       'GAME_LEVEL_ATTEMPT',       'ATTEMPT_ID');
-    sync_sequence('SEQ_GAME_REWARD_LOG',          'GAME_REWARD_LOG',          'REWARD_LOG_ID');
-    sync_sequence('SEQ_GAME_SCORE_LEDGER',        'GAME_SCORE_LEDGER',        'LEDGER_ID');
-    sync_sequence('SEQ_GAME_MONTHLY_RANKING',     'GAME_MONTHLY_RANKING',     'RANKING_ID');
-    sync_sequence('SEQ_GAME_REWARD_LIMIT_USAGE',  'GAME_REWARD_LIMIT_USAGE',  'LIMIT_USAGE_ID');
-    sync_sequence('SEQ_GAME_MONTHLY_DRAW',        'GAME_MONTHLY_DRAW',        'DRAW_ID');
-    sync_sequence('SEQ_GAME_MONTHLY_DRAW_TICKET', 'GAME_MONTHLY_DRAW_TICKET', 'TICKET_ID');
-    sync_sequence('SEQ_GAME_MONTHLY_WINNER',      'GAME_MONTHLY_WINNER',      'WINNER_ID');
-END;
-/
-
--- Xac nhan rieng loi PK_GAME_LEVEL_ATTEMPT vua gap:
-SELECT MAX(ATTEMPT_ID) AS MAX_ATTEMPT_ID FROM GAME_LEVEL_ATTEMPT;
-SELECT SEQUENCE_NAME, LAST_NUMBER, INCREMENT_BY, CYCLE_FLAG
-FROM USER_SEQUENCES
-WHERE SEQUENCE_NAME = 'SEQ_GAME_LEVEL_ATTEMPT';