소스 검색

nâng cấp chức năng update avt và tên
thêm chức năng hủy trên website

student 1 주 전
부모
커밋
20f699e115
30개의 변경된 파일467개의 추가작업 그리고 70개의 파일을 삭제
  1. 1 1
      KittyFallWs/KittyFallWs/src/com/vas/webservices/KittyFallWs.java
  2. 1 0
      Kitty_Fall/Kitty_Fall/Common/CommonUrl.cs
  3. 10 1
      Kitty_Fall/Kitty_Fall/Common/Http/Apis/Request/UserInfo.cs
  4. 2 1
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Apis/Business/User/IUserBusiness.cs
  5. 118 41
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Apis/Business/User/UserBusinessImpl.cs
  6. 14 7
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Apis/Controllers/UserController.cs
  7. 156 0
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Controllers/HomeController.cs
  8. 2 0
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Helpers/LoginSessionHelper.cs
  9. 1 1
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/DailyWinners/Index.cshtml
  10. 1 1
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Guide/Index.cshtml
  11. 1 1
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/History/Index.cshtml
  12. 3 2
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Home/Index.cshtml
  13. 1 1
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Leaderboard/Index.cshtml
  14. 1 1
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/MonthlyWinners/Index.cshtml
  15. 1 1
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Play/Index.cshtml
  16. 76 6
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Shared/_Layout.cshtml
  17. 1 1
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Winner/Index.cshtml
  18. 0 0
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/catpuzzle/data.json
  19. BIN
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/catpuzzle/images/block1a-sheet0.webp
  20. BIN
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/catpuzzle/images/btn_lvl-sheet0.webp
  21. BIN
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/catpuzzle/images/cats-sheet1.webp
  22. BIN
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/catpuzzle/images/sprite3-sheet0.webp
  23. BIN
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/catpuzzle/images/sprite3-sheet1.webp
  24. BIN
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/catpuzzle/images/sprite3-sheet2.webp
  25. 1 1
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/catpuzzle/offline.json
  26. 1 2
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/catpuzzle/scripts/objRefTable.js
  27. 1 1
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/catpuzzle/scripts/project/main.js
  28. 37 0
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/kitty/css/all.min.css
  29. 37 0
      Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/kitty/css/input.css
  30. BIN
      Kitty_Fall/catpuzzle_aquang.zip

+ 1 - 1
KittyFallWs/KittyFallWs/src/com/vas/webservices/KittyFallWs.java

@@ -1721,7 +1721,7 @@ public class KittyFallWs extends WebserviceAbstract {
                                 response.setErrorCode(Common.ErrorCode.NOT_REGISTERED);
                                 response.setContent(message);
                             } else {
-                                rsCancelMps = ws.cancelService(msisdn, regInfo.get(0).getProductName());
+//                                rsCancelMps = ws.cancelService(msisdn, regInfo.get(0).getProductName());
                                 if (WSProcessor.LIST_CANCEL_SUCCESS_RES.contains(rsCancelMps)) {
                                     db.disableAutoExtend(regInfo.get(0).getRegisterId());
 

+ 1 - 0
Kitty_Fall/Kitty_Fall/Common/CommonUrl.cs

@@ -6,6 +6,7 @@ public static class UrlConstant
     public const String UserRegisterSubOtp = "/apis/user/register-sub-otp";
     public const String UserRegisterSubConfirm = "/apis/user/register-sub-confirm";
     public const String UserRegisterSubQuickUrl = "/apis/user/register-sub-quick";
+    public const String UserDisableRegisterUrl = "/apis/user/disable-register";
    
 
     public const String UserSendOtp = "/apis/user/send-otp";

+ 10 - 1
Kitty_Fall/Kitty_Fall/Common/Http/Apis/Request/UserInfo.cs

@@ -52,5 +52,14 @@ namespace Common.Http.Apis.Request
         }
     }
 
-   
+
+    public class UserUpdateProfileReq
+    {
+        [Required]
+        public string msisdn { get; set; }
+        public string? fullname { get; set; }
+        public string? picture { get; set; }
+    }
+
+
 }

+ 2 - 1
Kitty_Fall/Kitty_Fall/Kitty_Fall.Apis/Business/User/IUserBusiness.cs

@@ -28,11 +28,12 @@ namespace Kitty_Fall.Apis.Business.User
         Task<IActionResult> UserRegisterSubOtp(HttpRequest httpRequest, UserRegisterReq request);
         Task<IActionResult> UserRegisterSubConfirm(HttpRequest httpRequest, UserRegisterSubConfirmReq request);
         Task<IActionResult> UserRegisterSubQuick(HttpRequest httpRequest, UserRegisterReq request);
+        Task<IActionResult> UserDisableRegister(HttpRequest httpRequest, UserRegisterReq request);
         Task<IActionResult> UserSendOtp(HttpRequest httpRequest, UserSendOtpReq request);
         Task<IActionResult> UserResendOtp(HttpRequest httpRequest, UserSendOtpReq request);
         Task<IActionResult> UserOtpConfirm(HttpRequest httpRequest, UserSendOtpReq request);
         Task<IActionResult> MyIdAutoLogin(HttpRequest httpRequest, MyIdAutoLoginReq request);
-        //Task<IActionResult> UpdateProfile(HttpRequest httpRequest, UserUpdateProfileReq request);
+        Task<IActionResult> UpdateProfile(HttpRequest httpRequest, UserUpdateProfileReq request);
         Task<IActionResult> UserBuyTurn(HttpRequest httpRequest, UserBuyTurnReq request);
     }
 }

+ 118 - 41
Kitty_Fall/Kitty_Fall/Kitty_Fall.Apis/Business/User/UserBusinessImpl.cs

@@ -714,6 +714,83 @@ namespace Kitty_Fall.Apis.Business.User
             );
         }
 
+        public async Task<IActionResult> UserDisableRegister(HttpRequest httpRequest, UserRegisterReq request)
+        {
+            var url = httpRequest.Path;
+            var json = JsonConvert.SerializeObject(request);
+
+            try
+            {
+                Logic.ChangeLanguage(request.language);
+                log.Debug("URL: " + url + " => Request: " + json);
+
+                var msisdn = CommonLogic.ValidateMsisdn(
+                    GetParameter("CountryCode"),
+                    request.msisdn ?? ""
+                );
+                if (string.IsNullOrWhiteSpace(msisdn))
+                {
+                    return CommonLogic.BuildResponse(
+                        url, json, CommonErrorCode.Error, Lang.msisdnInvalid, new { }
+                    );
+                }
+
+                var packg = await dbContext.Packgs
+                    .AsNoTracking()
+                    .FirstOrDefaultAsync(x => x.ProductName == request.packageName);
+                if (packg == null)
+                {
+                    return CommonLogic.BuildResponse(
+                        url, json, CommonErrorCode.Error, Lang.packageInvalid, new { }
+                    );
+                }
+
+                var wsResponse = await wsClient.wsDisableRegisterAsync(
+                    wsUser,
+                    wsPass,
+                    msisdn,
+                    packg.ProductName
+                );
+                log.Info("Response: wsDisableRegisterAsync => data: " + JsonConvert.SerializeObject(wsResponse));
+
+                if (wsResponse?.@return == null)
+                {
+                    return CommonLogic.BuildResponse(
+                        url, json, CommonErrorCode.Error, Lang.badRequest, new { }
+                    );
+                }
+
+                var wsErrorCode = int.TryParse(wsResponse.@return.errorCode, out var parsedCode)
+                    ? parsedCode
+                    : CommonErrorCode.Error;
+                if (wsResponse.@return.errorCode == CommonErrorCode.Success)
+                {
+                    return CommonLogic.BuildResponse(
+                        url,
+                        json,
+                        CommonErrorCode.SUCCESS,
+                        wsResponse.@return.content,
+                        new { packageName = packg.ProductName }
+                    );
+                }
+
+                return CommonLogic.BuildResponse(
+                    url,
+                    json,
+                    wsErrorCode,
+                    wsResponse.@return.content?.Replace("%package%", packg.ProductName) ?? Lang.badRequest,
+                    new { }
+                );
+            }
+            catch (Exception exception)
+            {
+                log.Error("UserDisableRegister error: ", exception);
+                return CommonLogic.BuildResponse(
+                    url, json, CommonErrorCode.Error, Lang.badRequest, new { }
+                );
+            }
+        }
+
 
 
 
@@ -1403,46 +1480,46 @@ namespace Kitty_Fall.Apis.Business.User
             return GetHttpSendRequest;
         }
 
-        //public async Task<IActionResult> UpdateProfile(HttpRequest httpRequest, UserUpdateProfileReq request)
-        //{
-        //    var url = httpRequest.Path;
-        //    var json = JsonConvert.SerializeObject(request);
-        //    try
-        //    {
-        //        // Normalize msisdn để chấp nhận cả "9681454634" lẫn "959681454634"
-        //        string? msisdn = CommonLogic.ValidateMsisdn(GetParameter("CountryCode"), request.msisdn);
-        //        if (string.IsNullOrEmpty(msisdn))
-        //        {
-        //            return CommonLogic.BuildResponse(url, json, CommonErrorCode.Error, Lang.msisdnInvalid, new { });
-        //        }
-
-        //        var user = dbContext.AccountUsers.FirstOrDefault(x => x.Msisdn == msisdn);
-        //        if (user == null)
-        //        {
-        //            return CommonLogic.BuildResponse(url, json, CommonErrorCode.Error, "User not found", new { });
-        //        }
-
-        //        if (!string.IsNullOrEmpty(request.fullname)) user.Fullname = request.fullname;
-        //        if (!string.IsNullOrEmpty(request.email)) user.Email = request.email;
-        //        if (!string.IsNullOrEmpty(request.birthday))
-        //        {
-        //            if (DateTime.TryParse(request.birthday, out DateTime birthday))
-        //            {
-        //                user.Birthday = birthday;
-        //            }
-        //        }
-        //        if (!string.IsNullOrEmpty(request.picture)) user.Picture = request.picture;
-
-        //        user.UpdateDate = DateTime.Now;
-        //        await dbContext.SaveChangesAsync();
-
-        //        return CommonLogic.BuildResponse(url, json, CommonErrorCode.SUCCESS, "Profile updated successfully", user);
-        //    }
-        //    catch (Exception ex)
-        //    {
-        //        log.Error("UpdateProfile Error: ", ex);
-        //        return CommonLogic.BuildResponse(url, json, CommonErrorCode.Error, "Internal Server Error", new { });
-        //    }
-        //}
+        public async Task<IActionResult> UpdateProfile(HttpRequest httpRequest, UserUpdateProfileReq request)
+        {
+            var url = httpRequest.Path;
+            var json = JsonConvert.SerializeObject(request);
+            try
+            {
+                // Normalize msisdn để chấp nhận cả "9681454634" lẫn "959681454634"
+                string? msisdn = CommonLogic.ValidateMsisdn(GetParameter("CountryCode"), request.msisdn);
+                if (string.IsNullOrEmpty(msisdn))
+                {
+                    return CommonLogic.BuildResponse(url, json, CommonErrorCode.Error, Lang.msisdnInvalid, new { });
+                }
+
+                var user = dbContext.AccountUsers.FirstOrDefault(x => x.Msisdn == msisdn);
+                if (user == null)
+                {
+                    return CommonLogic.BuildResponse(url, json, CommonErrorCode.Error, "User not found", new { });
+                }
+
+                if (!string.IsNullOrEmpty(request.fullname)) user.Fullname = request.fullname;
+                //if (!string.IsNullOrEmpty(request.email)) user.Email = request.email;
+                //if (!string.IsNullOrEmpty(request.birthday))
+                //{
+                //    if (DateTime.TryParse(request.birthday, out DateTime birthday))
+                //    {
+                //        user.Birthday = birthday;
+                //    }
+                //}
+                if (!string.IsNullOrEmpty(request.picture)) user.Picture = request.picture;
+
+                user.UpdateDate = DateTime.Now;
+                await dbContext.SaveChangesAsync();
+
+                return CommonLogic.BuildResponse(url, json, CommonErrorCode.SUCCESS, "Profile updated successfully", user);
+            }
+            catch (Exception ex)
+            {
+                log.Error("UpdateProfile Error: ", ex);
+                return CommonLogic.BuildResponse(url, json, CommonErrorCode.Error, "Internal Server Error", new { });
+            }
+        }
     }
 }

+ 14 - 7
Kitty_Fall/Kitty_Fall/Kitty_Fall.Apis/Controllers/UserController.cs

@@ -94,6 +94,13 @@ namespace RevoSystem.Apis.Controllers
             return await userBusiness.UserRegisterSubQuick(HttpContext.Request, request);
         }
 
+        [HttpPost]
+        [Route(UrlConstant.UserDisableRegisterUrl)]
+        public async Task<IActionResult> UserDisableRegister([FromBody] UserRegisterReq request)
+        {
+            return await userBusiness.UserDisableRegister(HttpContext.Request, request);
+        }
+
 
         [HttpPost]
         [Route(UrlConstant.UserSendOtp)]
@@ -123,13 +130,13 @@ namespace RevoSystem.Apis.Controllers
             return await userBusiness.MyIdAutoLogin(HttpContext.Request, request);
         }
 
-      
-        // [HttpPost]
-        // [Route(UrlConstant.UserUpdateProfileUrl)]
-        // public async Task<IActionResult> UpdateProfile([FromBody] UserUpdateProfileReq request)
-        // {
-        //     return await userBusiness.UpdateProfile(HttpContext.Request, request);
-        // }
+
+        [HttpPost]
+        [Route(UrlConstant.UserUpdateProfileUrl)]
+        public async Task<IActionResult> UpdateProfile([FromBody] UserUpdateProfileReq request)
+        {
+            return await userBusiness.UpdateProfile(HttpContext.Request, request);
+        }
 
         [HttpPost]
         [Route(UrlConstant.UserBuyTurnUrl)]

+ 156 - 0
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Controllers/HomeController.cs

@@ -52,6 +52,86 @@ namespace Kitty_Fall.Website.Controllers
             return Json(new { success = true });
         }
 
+        [HttpPost]
+        [RequestSizeLimit(5 * 1024 * 1024)]
+        public async Task<IActionResult> UpdateProfile(string? fullName, IFormFile? avatar)
+        {
+            var msisdn = HttpContext.Session.GetComplexData<string>("msisdn") ?? "";
+            var apiToken = HttpContext.Session.GetString("apiToken");
+            var baseUrl = GetParameter("url");
+            var normalizedName = fullName?.Trim() ?? "";
+
+            if (string.IsNullOrWhiteSpace(msisdn) || string.IsNullOrWhiteSpace(apiToken))
+                return Unauthorized(new { success = false, message = "Please login again." });
+            if (normalizedName.Length > 100)
+                return BadRequest(new { success = false, message = "Full name must not exceed 100 characters." });
+            if (string.IsNullOrWhiteSpace(normalizedName) && avatar == null)
+                return BadRequest(new { success = false, message = "Please enter a full name or select an avatar." });
+
+            string? picture = null;
+            if (avatar is { Length: > 0 })
+            {
+                if (avatar.Length > 5 * 1024 * 1024)
+                    return BadRequest(new { success = false, message = "Avatar must not exceed 5 MB." });
+
+                var allowedTypes = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
+                {
+                    ["image/jpeg"] = ".jpg", ["image/png"] = ".png", ["image/webp"] = ".webp"
+                };
+                if (!allowedTypes.TryGetValue(avatar.ContentType, out var extension))
+                    return BadRequest(new { success = false, message = "Only JPG, PNG or WEBP images are supported." });
+
+                var avatarDirectory = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "kitty", "assets", "kitty-fall", "avatar");
+                Directory.CreateDirectory(avatarDirectory);
+                var fileName = $"{DateTime.UtcNow:yyyyMMddHHmmss}-{Guid.NewGuid():N}{extension}";
+                await using var stream = System.IO.File.Create(Path.Combine(avatarDirectory, fileName));
+                await avatar.CopyToAsync(stream);
+                picture = $"avatar/{fileName}";
+            }
+
+            try
+            {
+                var language = GetCurrentLanguage();
+                var postTask = DotnetLib.Rest.RestHandler.SendPostWithAuthen(
+                    log,
+                    baseUrl + UrlConstant.UserUpdateProfileUrl,
+                    new UserUpdateProfileReq
+                    {
+                        msisdn = msisdn,
+                        fullname = string.IsNullOrWhiteSpace(normalizedName) ? null : normalizedName,
+                        picture = picture
+                    },
+                    apiToken,
+                    "",
+                    "",
+                    language
+                );
+                if (postTask == null)
+                {
+                    return BadRequest(new { success = false, message = "Profile update API returned empty response." });
+                }
+
+                ReturnDataPost? response = await postTask;
+                if (response?.data == null)
+                {
+                    return BadRequest(new { success = false, message = "Profile update API returned empty response." });
+                }
+
+                var result = JObject.Parse(response.data);
+                if (ReadErrorCode(result) != CommonErrorCode.SUCCESS)
+                    return BadRequest(new { success = false, message = result["message"]?.ToString() ?? "Profile update failed." });
+
+                if (!string.IsNullOrWhiteSpace(normalizedName)) HttpContext.Session.SetComplexData("fullName", normalizedName);
+                if (!string.IsNullOrWhiteSpace(picture)) HttpContext.Session.SetComplexData("profilePicture", picture);
+                return Json(new { success = true, fullName = normalizedName, picture });
+            }
+            catch (Exception ex)
+            {
+                log.Error("UpdateProfile error: ", ex);
+                return StatusCode(500, new { success = false, message = "Profile update failed." });
+            }
+        }
+
         [HttpPost]
         public async Task<IActionResult> RegisterQAction(string? productName)
         {
@@ -136,6 +216,75 @@ namespace Kitty_Fall.Website.Controllers
             }
         }
 
+        [HttpPost]
+        public async Task<IActionResult> CancelPackage(string? packageName)
+        {
+            var msisdn = HttpContext.Session.GetComplexData<string>("msisdn") ?? "";
+            var apiToken = HttpContext.Session.GetString("apiToken");
+            var normalizedPackageName = packageName?.Trim() ?? "";
+
+            if (string.IsNullOrWhiteSpace(msisdn) || string.IsNullOrWhiteSpace(apiToken))
+                return Unauthorized(new { success = false, message = "Please login again." });
+            if (string.IsNullOrWhiteSpace(normalizedPackageName))
+                return BadRequest(new { success = false, message = "No active package was selected." });
+
+            try
+            {
+                var language = GetCurrentLanguage();
+                var postTask = DotnetLib.Rest.RestHandler.SendPostWithAuthen(
+                    log,
+                    GetParameter("url") + UrlConstant.UserDisableRegisterUrl,
+                    new UserRegisterReq
+                    {
+                        msisdn = msisdn,
+                        packageName = normalizedPackageName,
+                        language = language
+                    },
+                    apiToken,
+                    "",
+                    "",
+                    language
+                );
+                if (postTask == null)
+                    return BadRequest(new { success = false, message = "Cancel package API returned empty response." });
+
+                ReturnDataPost? response = await postTask;
+                if (response?.data == null)
+                    return BadRequest(new { success = false, message = "Cancel package API returned empty response." });
+
+                var result = JObject.Parse(response.data);
+                if (ReadErrorCode(result) != CommonErrorCode.SUCCESS)
+                    return BadRequest(new { success = false, message = result["message"]?.ToString() ?? "Package cancellation failed." });
+
+                var regInfos = HttpContext.Session.GetComplexData<JArray>("regInfos");
+                if (regInfos != null)
+                {
+                    foreach (var item in regInfos
+                        .Where(x => string.Equals(
+                            (x["productName"] ?? x["ProductName"])?.ToString(),
+                            normalizedPackageName,
+                            StringComparison.OrdinalIgnoreCase))
+                        .ToList())
+                    {
+                        item["status"] = false;
+                        item["renew"] = 0;
+                    }
+                    HttpContext.Session.SetComplexData("regInfos", regInfos);
+                }
+
+                return Json(new
+                {
+                    success = true,
+                    message = result["message"]?.ToString() ?? "Package cancelled successfully."
+                });
+            }
+            catch (Exception ex)
+            {
+                log.Error("CancelPackage error: ", ex);
+                return StatusCode(500, new { success = false, message = "Package cancellation failed." });
+            }
+        }
+
         [HttpPost]
         public async Task<IActionResult> BuyTurnQAction(string? productName)
         {
@@ -227,11 +376,13 @@ namespace Kitty_Fall.Website.Controllers
         {
             var msisdn = HttpContext.Session.GetComplexData<string>("msisdn") ?? "";
             var profilePicture = HttpContext.Session.GetComplexData<string>("profilePicture") ?? "";
+            var fullName = HttpContext.Session.GetComplexData<string>("fullName") ?? "";
             var totalTurn = HttpContext.Session.GetComplexData<long?>("totalTurn") ?? 0;
             var totalPoint = HttpContext.Session.GetComplexData<long?>("totalPoint") ?? 0;
 
             ViewBag.Msisdn = msisdn;
             ViewBag.ProfilePicture = profilePicture;
+            ViewBag.FullName = fullName;
             ViewBag.TotalTurn = totalTurn;
             ViewBag.TotalPoint = totalPoint;
 
@@ -285,6 +436,9 @@ namespace Kitty_Fall.Website.Controllers
                 var loadedPicture = accountUser?["picture"]?.ToString()
                     ?? accountUser?["Picture"]?.ToString()
                     ?? profilePicture;
+                var loadedFullName = accountUser?["fullname"]?.ToString()
+                    ?? accountUser?["Fullname"]?.ToString()
+                    ?? fullName;
                 var loadedTurn = ReadLong(data?["totalTurn"] ?? data?["TotalTurn"]);
                 var loadedPoint = ReadLong(
                     data?["totalPoint"]
@@ -299,6 +453,7 @@ namespace Kitty_Fall.Website.Controllers
 
                 HttpContext.Session.SetComplexData("msisdn", loadedMsisdn);
                 HttpContext.Session.SetComplexData("profilePicture", loadedPicture);
+                HttpContext.Session.SetComplexData("fullName", loadedFullName);
                 HttpContext.Session.SetComplexData("totalTurn", loadedTurn);
                 HttpContext.Session.SetComplexData("totalPoint", loadedPoint);
                 HttpContext.Session.SetComplexData("totalDataMb", loadedDataMb);
@@ -310,6 +465,7 @@ namespace Kitty_Fall.Website.Controllers
 
                 ViewBag.Msisdn = loadedMsisdn;
                 ViewBag.ProfilePicture = loadedPicture;
+                ViewBag.FullName = loadedFullName;
                 ViewBag.TotalTurn = loadedTurn;
                 ViewBag.TotalPoint = loadedPoint;
             }

+ 2 - 0
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Helpers/LoginSessionHelper.cs

@@ -25,6 +25,7 @@ public static class LoginSessionHelper
             context.Session.SetComplexData("userId", accountUser["id"] ?? accountUser["Id"] ?? "");
             context.Session.SetComplexData("role", role);
             context.Session.SetComplexData("profilePicture", accountUser["picture"]?.ToString() ?? accountUser["Picture"]?.ToString() ?? "");
+            context.Session.SetComplexData("fullName", accountUser["fullname"]?.ToString() ?? accountUser["Fullname"]?.ToString() ?? "");
         }
 
         var regInfos = data?["regInfos"] ?? data?["RegInfos"];
@@ -58,6 +59,7 @@ public static class LoginSessionHelper
         context.Session.Remove("userId");
         context.Session.Remove("role");
         context.Session.Remove("profilePicture");
+        context.Session.Remove("fullName");
         context.Session.Remove("regInfos");
         context.Session.Remove("hasAnyRegInfo");
         context.Session.Remove("canUseSubscriptionFeatures");

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

@@ -4,7 +4,7 @@
     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;
+        : Url.Content("~/kitty/assets/kitty-fall/avatar/") + 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");

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

@@ -4,7 +4,7 @@
     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;
+        : Url.Content("~/kitty/assets/kitty-fall/avatar/") + profile.picture;
 }
 
 <main class="kitty-app is-winner-screen is-guide-screen" aria-label="@Lang.GuideAriaLabel">

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

@@ -4,7 +4,7 @@
     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;
+        : Url.Content("~/kitty/assets/kitty-fall/avatar/") + profile.picture;
     var pageNumber = Model?.pageNumber > 0 ? Model.pageNumber : 1;
     var totalPage = Model?.totalPage ?? 0;
     var fromDate = Model?.fromDate ?? "";

+ 3 - 2
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Home/Index.cshtml

@@ -2,7 +2,8 @@
     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 fullName = ViewBag.FullName as string;
+    var avatarUrl = string.IsNullOrWhiteSpace(profilePicture) ? Url.Content("~/kitty/assets/kitty-fall/default.png") : Url.Content("~/kitty/assets/kitty-fall/avatar/") + 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);
@@ -32,7 +33,7 @@
             <img class="profile-avatar" src="@avatarUrl" alt="Player avatar">
             <div class="profile-copy">
                 <strong>@Lang.Hello</strong>
-                <span>@(string.IsNullOrWhiteSpace(msisdn) ? "-" : msisdn)</span>
+                <span><span>@(string.IsNullOrWhiteSpace(msisdn) ? "-" : msisdn)</span>@if (!string.IsNullOrWhiteSpace(fullName)) {<b class="profile-full-name">@fullName</b>}</span>
             </div>
         </div>
         <div class="topbar-actions">

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

@@ -5,7 +5,7 @@
     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;
+        : Url.Content("~/kitty/assets/kitty-fall/avatar/") + 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));

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

@@ -4,7 +4,7 @@
     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;
+        : Url.Content("~/kitty/assets/kitty-fall/avatar/") + profile.picture;
 }
 
 <main class="kitty-app is-winner-screen is-monthly-winner-screen" aria-label="@Lang.MonthlyWinnerList">

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

@@ -3,7 +3,7 @@
     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;
+        : Url.Content("~/kitty/assets/kitty-fall/avatar/") + Model.ProfilePicture;
 }
 
 <main class="kitty-app is-play-screen" aria-label="Kitty Fall play screen"

+ 76 - 6
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/Views/Shared/_Layout.cshtml

@@ -28,9 +28,10 @@
     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 accountFullName = Context.Session.GetComplexData<string>("fullName") ?? "";
     var accountAvatar = string.IsNullOrWhiteSpace(accountProfilePicture)
         ? Url.Content("~/kitty/assets/kitty-fall/default.png")
-        : Url.Content("~/kitty/assets/kitty-fall/") + accountProfilePicture;
+        : Url.Content("~/kitty/assets/kitty-fall/avatar/") + accountProfilePicture;
     var accountScore = Context.Session.GetComplexData<long?>("totalPoint") ?? 0;
     var accountDataMb = Context.Session.GetComplexData<decimal?>("totalDataMb") ?? 0;
     var accountLoyalty = Context.Session.GetComplexData<decimal?>("totalLoyaltyPoints") ?? 0;
@@ -67,14 +68,21 @@
             <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>
+                <form class="account-profile-card" data-profile-form enctype="multipart/form-data">
+                    <label class="account-avatar-editor" title="Change avatar">
+                        <img class="account-profile-avatar" data-profile-avatar src="@accountAvatar" alt="@Lang.AccountAvatarAlt">
+                        <span>✎</span>
+                        <input type="file" name="avatar" accept="image/jpeg,image/png,image/webp" data-avatar-input>
+                    </label>
+                    <div class="account-profile-fields">
                         <small>@Lang.AccountPhoneNumber</small>
-                        <strong>@accountMsisdn</strong>
+                        <strong><span>@accountMsisdn</span><span data-profile-name-display>@accountFullName</span></strong>
+                        <input name="fullName" maxlength="100" value="@accountFullName" placeholder="Full name" aria-label="Full name">
                         <p>@Lang.AccountDefaultDescription</p>
+                        <button type="submit" data-profile-save>Save profile</button>
+                        <em data-profile-message></em>
                     </div>
-                </section>
+                </form>
 
                 <section class="account-summary-card">
                     <h2><span>▣</span> @Lang.AccountTotalThisMonth</h2>
@@ -115,6 +123,16 @@
                         <p>@Lang.AccountCancelInstruction</p>
                         <strong>@string.Format(Lang.AccountCancelCommand, cancelKeyword, cancelShortCode)</strong>
                         <p>@Lang.AccountFollowInstructions</p>
+                        @if (activeReg != null)
+                        {
+                            <button type="button"
+                                    class="account-cancel-button"
+                                    data-cancel-package
+                                    data-package-name="@packageNameRaw">
+                                Cancel package
+                            </button>
+                            <em class="account-cancel-message" data-cancel-package-message></em>
+                        }
                     </div>
                     <img src="~/kitty/assets/kitty-fall/account-cat-cancel.png" alt="@Lang.AccountCancelCatAlt">
                 </section>
@@ -214,6 +232,58 @@
                     closeAccountMenu(event);
                 }
             });
+
+            const profileForm = accountMenu.querySelector("[data-profile-form]");
+            const avatarInput = profileForm?.querySelector("[data-avatar-input]");
+            avatarInput?.addEventListener("change", function () {
+                const file = avatarInput.files?.[0];
+                if (file) profileForm.querySelector("[data-profile-avatar]").src = URL.createObjectURL(file);
+            });
+            profileForm?.addEventListener("submit", async function (event) {
+                event.preventDefault();
+                const button = profileForm.querySelector("[data-profile-save]");
+                const message = profileForm.querySelector("[data-profile-message]");
+                button.disabled = true;
+                message.textContent = "Saving...";
+                try {
+                    const response = await fetch("/Home/UpdateProfile", { method: "POST", body: new FormData(profileForm) });
+                    const result = await response.json();
+                    if (!response.ok || !result.success) throw new Error(result.message || "Profile update failed.");
+                    profileForm.querySelector("[data-profile-name-display]").textContent = result.fullName || "";
+                    message.textContent = "Saved successfully.";
+                    if (result.picture) document.querySelectorAll(".profile-avatar").forEach(img => img.src = "/kitty/assets/kitty-fall/" + result.picture);
+                    window.setTimeout(() => location.reload(), 500);
+                } catch (error) {
+                    message.textContent = error.message;
+                } finally {
+                    button.disabled = false;
+                }
+            });
+
+            const cancelPackageButton = accountMenu.querySelector("[data-cancel-package]");
+            cancelPackageButton?.addEventListener("click", async function () {
+                const packageName = cancelPackageButton.dataset.packageName;
+                const message = accountMenu.querySelector("[data-cancel-package-message]");
+                if (!packageName || !window.confirm("Are you sure you want to cancel this package?")) return;
+
+                cancelPackageButton.disabled = true;
+                message.textContent = "Cancelling...";
+                try {
+                    const body = new URLSearchParams({ packageName: packageName });
+                    const response = await fetch("/Home/CancelPackage", {
+                        method: "POST",
+                        headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8" },
+                        body: body.toString()
+                    });
+                    const result = await response.json();
+                    if (!response.ok || !result.success) throw new Error(result.message || "Package cancellation failed.");
+                    message.textContent = result.message || "Package cancelled successfully.";
+                    window.setTimeout(() => location.reload(), 800);
+                } catch (error) {
+                    message.textContent = error.message;
+                    cancelPackageButton.disabled = false;
+                }
+            });
         })();
     </script>
     @await RenderSectionAsync("Scripts", required: false)

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

@@ -4,7 +4,7 @@
     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 ;
+        : Url.Content("~/kitty/assets/kitty-fall/avatar/") + profile.picture ;
 }
 
 <main class="kitty-app is-winner-screen" aria-label="@Lang.WinnerList">

파일 크기가 너무 크기때문에 변경 상태를 표시하지 않습니다.
+ 0 - 0
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/catpuzzle/data.json


BIN
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/catpuzzle/images/block1a-sheet0.webp


BIN
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/catpuzzle/images/btn_lvl-sheet0.webp


BIN
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/catpuzzle/images/cats-sheet1.webp


BIN
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/catpuzzle/images/sprite3-sheet0.webp


BIN
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/catpuzzle/images/sprite3-sheet1.webp


BIN
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/catpuzzle/images/sprite3-sheet2.webp


+ 1 - 1
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/catpuzzle/offline.json

@@ -1 +1 @@
-{"version":1784712078432,"fileList":["scripts/main.js","scripts/c3runtime.js","scripts/objRefTable.js","scripts/project/main.js","scripts/c3main.js","scripts/dispatchworker.js","scripts/jobworker.js","data.json","scripts/modernjscheck.js","scripts/supportcheck.js","scripts/offlineclient.js","icons/loading-logo.png","icons/icon-16.png","icons/icon-32.png","icons/icon-64.png","icons/icon-128.png","icons/icon-256.png","icons/icon-512.png","media/congratulations.webm","media/click.webm","media/Button03.webm","media/cute-cat-352656.webm","media/win2.webm","media/timeup_.webm","media/click_sound.webm","media/playground.webm","box2d.wasm.js","box2d.wasm","style.css","images/shared-0-sheet0.webp","images/shared-0-sheet1.webp","images/shared-0-sheet2.webp","images/shared-0-sheet3.webp","images/shared-0-sheet4.webp","images/shared-0-sheet5.webp","images/shared-0-sheet6.webp","images/block1a-sheet0.webp","images/block1a-sheet1.webp","images/cats-sheet0.webp","images/cats-sheet1.webp","images/cats-sheet2.webp","images/cats-sheet3.webp","images/cats-sheet4.webp","images/confettix-sheet0.webp","images/confettix-sheet1.webp","images/sprite3-sheet0.webp","images/sprite3-sheet1.webp","images/sprite3-sheet2.webp","images/scroll-sheet0.webp","images/btn_lvl-sheet0.webp","images/btn_lvl-sheet1.webp","scripts/register-sw.js"]}
+{"version":1784775413573,"fileList":["scripts/main.js","scripts/c3runtime.js","scripts/objRefTable.js","scripts/project/main.js","scripts/c3main.js","scripts/dispatchworker.js","scripts/jobworker.js","data.json","scripts/modernjscheck.js","scripts/supportcheck.js","scripts/offlineclient.js","icons/loading-logo.png","icons/icon-16.png","icons/icon-32.png","icons/icon-64.png","icons/icon-128.png","icons/icon-256.png","icons/icon-512.png","media/congratulations.webm","media/click.webm","media/Button03.webm","media/cute-cat-352656.webm","media/win2.webm","media/timeup_.webm","media/click_sound.webm","media/playground.webm","box2d.wasm.js","box2d.wasm","style.css","images/scroll-sheet0.webp","images/shared-0-sheet6.webp","images/shared-0-sheet3.webp","images/shared-0-sheet0.webp","images/shared-0-sheet5.webp","images/shared-0-sheet4.webp","images/shared-0-sheet2.webp","images/confettix-sheet0.webp","images/confettix-sheet1.webp","images/cats-sheet0.webp","images/shared-0-sheet1.webp","images/cats-sheet2.webp","images/btn_lvl-sheet0.webp","images/btn_lvl-sheet1.webp","images/cats-sheet1.webp","images/cats-sheet4.webp","images/cats-sheet3.webp","images/block1a-sheet1.webp","images/block1a-sheet0.webp","images/sprite3-sheet0.webp","images/sprite3-sheet1.webp","images/sprite3-sheet2.webp","scripts/register-sw.js"]}

+ 1 - 2
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/catpuzzle/scripts/objRefTable.js

@@ -113,8 +113,7 @@ self.C3_GetObjectRefTable = function () {
 		C3.Plugins.LocalStorage.Cnds.OnItemMissing,
 		C3.Plugins.LocalStorage.Cnds.OnItemGet,
 		C3.Plugins.LocalStorage.Exps.ItemValue,
-		C3.Plugins.Sprite.Acts.SetAnim,
-		C3.Plugins.LocalStorage.Acts.CheckItemExists
+		C3.Plugins.Sprite.Acts.SetAnim
 	];
 };
 self.C3_JsPropNameTable = [

+ 1 - 1
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/catpuzzle/scripts/project/main.js

@@ -316,7 +316,7 @@ async function SaveLevelResult(runtime)
 		else if (runtime.globalVars.data > 0)
 		{
 			runtime.globalVars.gGiftValues = runtime.globalVars.data;
-			runtime.globalVars.gGiftText = "Data";
+			runtime.globalVars.gGiftText = "M";
 		}
 		else if (runtime.globalVars.score > 0)
 		{

+ 37 - 0
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/kitty/css/all.min.css

@@ -4289,6 +4289,20 @@ body.has-account-menu {
     box-shadow: 0 3px 8px rgba(32, 104, 172, .2);
 }
 
+.profile-full-name { margin-left: 6px; font-weight: 800; color: #fff; }
+.profile-full-name::before { content: "•"; margin-right: 6px; opacity: .75; }
+.account-avatar-editor { position: relative; flex: 0 0 auto; cursor: pointer; }
+.account-avatar-editor > span { position: absolute; right: -2px; bottom: 0; display: grid; place-items: center; width: 23px; height: 23px; border: 2px solid #fff; border-radius: 50%; background: #0879cf; color: #fff; font-style: normal; }
+.account-avatar-editor input { position: absolute; width: 1px; height: 1px; opacity: 0; }
+.account-profile-fields { flex: 1; min-width: 0; }
+.account-profile-fields strong { display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px; }
+.account-profile-fields strong [data-profile-name-display]:not(:empty)::before { content: "•"; margin-right: 6px; color: #6e8494; }
+.account-profile-fields input { width: 100%; max-width: 220px; margin-top: 6px; padding: 7px 9px; border: 1px solid #bed7e5; border-radius: 8px; color: #203a68; font: inherit; font-weight: 700; outline: none; }
+.account-profile-fields input:focus { border-color: #078add; box-shadow: 0 0 0 2px rgba(7,138,221,.15); }
+.account-profile-fields button { margin-top: 7px; padding: 6px 12px; border: 0; border-radius: 8px; background: #087fd1; color: #fff; font-weight: 800; cursor: pointer; }
+.account-profile-fields button:disabled { opacity: .6; }
+.account-profile-fields em { display: block; min-height: 14px; margin-top: 4px; color: #268746; font-size: 10px; font-style: normal; }
+
 .account-profile-card small,
 .account-package-card small,
 .account-cancel-card small,
@@ -4492,6 +4506,29 @@ body.has-account-menu {
     object-fit: contain;
 }
 
+.account-cancel-button {
+    display: block;
+    margin-top: 7px;
+    border: 0;
+    border-radius: 8px;
+    padding: 6px 13px;
+    background: linear-gradient(#ff7168, #e73d37);
+    color: #fff;
+    font-size: 12px;
+    font-weight: 800;
+    cursor: pointer;
+    box-shadow: inset 0 -2px 0 rgba(0, 0, 0, .14);
+}
+
+.account-cancel-button:disabled { opacity: .65; cursor: wait; }
+.account-cancel-message {
+    display: block;
+    margin-top: 4px;
+    color: #c72f2a;
+    font-size: 11px;
+    font-style: normal;
+}
+
 .account-language-card {
     min-height: 76px;
     border-color: #ddd6ff;

+ 37 - 0
Kitty_Fall/Kitty_Fall/Kitty_Fall.Website/wwwroot/kitty/css/input.css

@@ -4289,6 +4289,20 @@ body.has-account-menu {
     box-shadow: 0 3px 8px rgba(32, 104, 172, .2);
 }
 
+.profile-full-name { margin-left: 6px; font-weight: 800; color: #fff; }
+.profile-full-name::before { content: "•"; margin-right: 6px; opacity: .75; }
+.account-avatar-editor { position: relative; flex: 0 0 auto; cursor: pointer; }
+.account-avatar-editor > span { position: absolute; right: -2px; bottom: 0; display: grid; place-items: center; width: 23px; height: 23px; border: 2px solid #fff; border-radius: 50%; background: #0879cf; color: #fff; font-style: normal; }
+.account-avatar-editor input { position: absolute; width: 1px; height: 1px; opacity: 0; }
+.account-profile-fields { flex: 1; min-width: 0; }
+.account-profile-fields strong { display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px; }
+.account-profile-fields strong [data-profile-name-display]:not(:empty)::before { content: "•"; margin-right: 6px; color: #6e8494; }
+.account-profile-fields input { width: 100%; max-width: 220px; margin-top: 6px; padding: 7px 9px; border: 1px solid #bed7e5; border-radius: 8px; color: #203a68; font: inherit; font-weight: 700; outline: none; }
+.account-profile-fields input:focus { border-color: #078add; box-shadow: 0 0 0 2px rgba(7,138,221,.15); }
+.account-profile-fields button { margin-top: 7px; padding: 6px 12px; border: 0; border-radius: 8px; background: #087fd1; color: #fff; font-weight: 800; cursor: pointer; }
+.account-profile-fields button:disabled { opacity: .6; }
+.account-profile-fields em { display: block; min-height: 14px; margin-top: 4px; color: #268746; font-size: 10px; font-style: normal; }
+
 .account-profile-card small,
 .account-package-card small,
 .account-cancel-card small,
@@ -4492,6 +4506,29 @@ body.has-account-menu {
     object-fit: contain;
 }
 
+.account-cancel-button {
+    display: block;
+    margin-top: 7px;
+    border: 0;
+    border-radius: 8px;
+    padding: 6px 13px;
+    background: linear-gradient(#ff7168, #e73d37);
+    color: #fff;
+    font-size: 12px;
+    font-weight: 800;
+    cursor: pointer;
+    box-shadow: inset 0 -2px 0 rgba(0, 0, 0, .14);
+}
+
+.account-cancel-button:disabled { opacity: .65; cursor: wait; }
+.account-cancel-message {
+    display: block;
+    margin-top: 4px;
+    color: #c72f2a;
+    font-size: 11px;
+    font-style: normal;
+}
+
 .account-language-card {
     min-height: 76px;
     border-color: #ddd6ff;

BIN
Kitty_Fall/catpuzzle_aquang.zip


이 변경점에서 너무 많은 파일들이 변경되어 몇몇 파일들은 표시되지 않았습니다.