student 4 روز پیش
والد
کامیت
dad496432a

+ 8 - 0
Kitty_Fall/Kitty_Fall/Database/Database/ModelContext.cs

@@ -1808,6 +1808,10 @@ public partial class ModelContext : DbContext
                 .HasDefaultValueSql("0")
                 .HasColumnType("NUMBER")
                 .HasColumnName("BUY_ON_WEB");
+            entity.Property(e => e.BuyTotal)
+                .HasDefaultValueSql("0")
+                .HasColumnType("NUMBER")
+                .HasColumnName("BUY_TOTAL");
             entity.Property(e => e.RevenueBuyMps)
                 .HasDefaultValueSql("0")
                 .HasColumnType("NUMBER")
@@ -1835,6 +1839,10 @@ public partial class ModelContext : DbContext
                 .HasDefaultValueSql("0")
                 .HasColumnType("NUMBER")
                 .HasColumnName("REGISTER_ON_WEB");
+            entity.Property(e => e.RegisterTotal)
+                .HasDefaultValueSql("0")
+                .HasColumnType("NUMBER")
+                .HasColumnName("REGISTER_TOTAL");
             entity.Property(e => e.Renew)
                 .HasDefaultValueSql("0")
                 .HasColumnType("NUMBER")

+ 4 - 0
Kitty_Fall/Kitty_Fall/Database/Database/ReportByTime.cs

@@ -13,12 +13,16 @@ public partial class ReportByTime
 
     public decimal? RegisterOnWeb { get; set; }
 
+    public decimal? RegisterTotal { get; set; }
+
     public decimal? Renew { get; set; }
 
     public decimal? BuyOnWeb { get; set; }
 
     public decimal? BuyOnMps { get; set; }
 
+    public decimal? BuyTotal { get; set; }
+
     public decimal? RevenueBuyMps { get; set; }
 
     public decimal? RevenueBuyWeb { get; set; }

+ 21 - 3
Kitty_Fall/Kitty_Fall/Kitty_Fall.Cms/Controllers/DailyReportController.cs

@@ -9,6 +9,7 @@ namespace Kitty_Fall.Cms.Controllers;
 public class DailyReportController : CmsAuthorizedController
 {
     private const string DailyReportType = "DAILY";
+    private const int PageSize = 10;
     private readonly ModelContext _db;
     private readonly IReadOnlyList<RevenueReportServiceOption> _services;
     private readonly string _defaultService;
@@ -42,7 +43,8 @@ public class DailyReportController : CmsAuthorizedController
     public async Task<IActionResult> Index(
         DateTime? fromDate = null,
         DateTime? toDate = null,
-        string? serviceFilter = null)
+        string? serviceFilter = null,
+        int page = 1)
     {
         if (!CheckLogin()) return RedirectToLogin();
         if (!HasMenuPermission(CmsSecurityHelper.DailyReportsMenuKey)) return RedirectNoPermission();
@@ -75,18 +77,30 @@ public class DailyReportController : CmsAuthorizedController
             query = query.Where(x => x.TimeReport < endDateExclusive);
         }
 
+        var totalRows = await query.CountAsync();
+        var totalPages = Math.Max(1, (int)Math.Ceiling(totalRows / (double)PageSize));
+        var currentPage = Math.Clamp(page, 1, totalPages);
+
         var items = await query
             .OrderByDescending(x => x.TimeReport)
+            .ThenByDescending(x => x.Id)
+            .Skip((currentPage - 1) * PageSize)
+            .Take(PageSize)
             .Select(x => new ReportByTimesItemViewModel
             {
                 Ngay = x.TimeReport,
-                TongSub = x.UserActiveTotal ?? 0,
-                DangKy = x.UserActive ?? 0,
+                TongSub = x.UserActive ?? 0,
+                DangKy = x.RegisterTotal ?? 0,
+                DangKyOnWeb = x.RegisterOnWeb ?? 0,
+                DangKyOnMps = x.RegisterOnMps ?? 0,
                 Huy = x.UserInactive ?? 0,
+                Renew = x.Renew ?? 0,
                 DoanhThu = x.Revenue ?? 0,
                 CongTien = x.Bonus ?? 0,
                 BuyOnWeb = x.BuyOnWeb ?? 0,
                 BuyOnMps = x.BuyOnMps ?? 0,
+                BuyTotal = x.BuyTotal ?? 0,
+                UserActiveTotal = x.UserActiveTotal ?? 0,
                 RevenueBuyMps = x.RevenueBuyMps ?? 0,
                 RevenueBuyWeb = x.RevenueBuyWeb ?? 0
             })
@@ -98,6 +112,10 @@ public class DailyReportController : CmsAuthorizedController
             ToDate = normalizedToDate,
             ServiceFilter = normalizedService,
             Services = _services.ToList(),
+            Page = currentPage,
+            PageSize = PageSize,
+            TotalPages = totalPages,
+            TotalRows = totalRows,
             Items = items
         });
     }

+ 18 - 0
Kitty_Fall/Kitty_Fall/Kitty_Fall.Cms/Models/ReportByTimesViewModel.cs

@@ -10,6 +10,14 @@ public class ReportByTimesViewModel
 
     public List<RevenueReportServiceOption> Services { get; set; } = new();
 
+    public int Page { get; set; } = 1;
+
+    public int PageSize { get; set; } = 10;
+
+    public int TotalPages { get; set; } = 1;
+
+    public int TotalRows { get; set; }
+
     public List<ReportByTimesItemViewModel> Items { get; set; } = new();
 }
 
@@ -21,6 +29,12 @@ public class ReportByTimesItemViewModel
 
     public decimal DangKy { get; set; }
 
+    public decimal DangKyOnWeb { get; set; }
+
+    public decimal DangKyOnMps { get; set; }
+
+    public decimal Renew { get; set; }
+
     public decimal Huy { get; set; }
 
     public decimal DoanhThu { get; set; }
@@ -31,6 +45,10 @@ public class ReportByTimesItemViewModel
 
     public decimal BuyOnMps { get; set; }
 
+    public decimal BuyTotal { get; set; }
+
+    public decimal UserActiveTotal { get; set; }
+
     public decimal RevenueBuyMps { get; set; }
 
     public decimal RevenueBuyWeb { get; set; }

+ 63 - 6
Kitty_Fall/Kitty_Fall/Kitty_Fall.Cms/Views/DailyReport/Index.cshtml

@@ -4,6 +4,17 @@
     var fromDateValue = Model.FromDate?.ToString("yyyy-MM-dd");
     var toDateValue = Model.ToDate?.ToString("yyyy-MM-dd");
     string FormatNumber(decimal value) => value.ToString("#,##0.##");
+    string PageUrl(int page)
+    {
+        var parameters = new List<string> { "page=" + page };
+        if (!string.IsNullOrWhiteSpace(Model.ServiceFilter))
+            parameters.Add("serviceFilter=" + Uri.EscapeDataString(Model.ServiceFilter));
+        if (fromDateValue != null)
+            parameters.Add("fromDate=" + fromDateValue);
+        if (toDateValue != null)
+            parameters.Add("toDate=" + toDateValue);
+        return Url.Content("~/DailyReport") + "?" + string.Join("&", parameters);
+    }
 }
 
 <div class="py-6 sm:py-8">
@@ -54,13 +65,17 @@
                         <div class="rounded-3xl border border-gray-100 bg-gray-50 p-4 shadow-sm">
                             <div class="text-lg font-bold text-gray-900">@item.Ngay.ToString("dd/MM/yyyy")</div>
                             <div class="mt-4 grid grid-cols-2 gap-3 text-sm">
-                                <div class="rounded-2xl bg-white px-3 py-2"><div class="text-gray-500">Total subscriptions</div><div class="mt-1 font-semibold text-gray-900">@FormatNumber(item.TongSub)</div></div>
-                                <div class="rounded-2xl bg-white px-3 py-2"><div class="text-gray-500">Registrations</div><div class="mt-1 font-semibold text-gray-900">@FormatNumber(item.DangKy)</div></div>
-                                <div class="rounded-2xl bg-white px-3 py-2"><div class="text-gray-500">Cancellations</div><div class="mt-1 font-semibold text-gray-900">@FormatNumber(item.Huy)</div></div>
+                                <div class="rounded-2xl bg-white px-3 py-2"><div class="text-gray-500">TotalSub</div><div class="mt-1 font-semibold text-gray-900">@FormatNumber(item.TongSub)</div></div>
+                                <div class="rounded-2xl bg-white px-3 py-2"><div class="text-gray-500">REGISTER_TOTAL</div><div class="mt-1 font-semibold text-gray-900">@FormatNumber(item.DangKy)</div></div>
+                                <div class="rounded-2xl bg-white px-3 py-2"><div class="text-gray-500">REGISTER_ON_WEB</div><div class="mt-1 font-semibold text-gray-900">@FormatNumber(item.DangKyOnWeb)</div></div>
+                                <div class="rounded-2xl bg-white px-3 py-2"><div class="text-gray-500">REGISTER_ON_MPS</div><div class="mt-1 font-semibold text-gray-900">@FormatNumber(item.DangKyOnMps)</div></div>
+                                <div class="rounded-2xl bg-white px-3 py-2"><div class="text-gray-500">Cancel</div><div class="mt-1 font-semibold text-gray-900">@FormatNumber(item.Huy)</div></div>
+                                <div class="rounded-2xl bg-white px-3 py-2"><div class="text-gray-500">RENEW</div><div class="mt-1 font-semibold text-gray-900">@FormatNumber(item.Renew)</div></div>
                                 <div class="rounded-2xl bg-white px-3 py-2"><div class="text-gray-500">Revenue</div><div class="mt-1 font-semibold text-gray-900">@FormatNumber(item.DoanhThu)</div></div>
                                 <div class="rounded-2xl bg-white px-3 py-2"><div class="text-gray-500">Bonus</div><div class="mt-1 font-semibold text-gray-900">@FormatNumber(item.CongTien)</div></div>
                                 <div class="rounded-2xl bg-white px-3 py-2"><div class="text-gray-500">BUY_ON_WEB</div><div class="mt-1 font-semibold text-gray-900">@FormatNumber(item.BuyOnWeb)</div></div>
                                 <div class="rounded-2xl bg-white px-3 py-2"><div class="text-gray-500">BUY_ON_MPS</div><div class="mt-1 font-semibold text-gray-900">@FormatNumber(item.BuyOnMps)</div></div>
+                                <div class="rounded-2xl bg-white px-3 py-2"><div class="text-gray-500">BUY_TOTAL</div><div class="mt-1 font-semibold text-gray-900">@FormatNumber(item.BuyTotal)</div></div>
                                 <div class="rounded-2xl bg-white px-3 py-2"><div class="text-gray-500">REVENUE_BUY_MPS</div><div class="mt-1 font-semibold text-gray-900">@FormatNumber(item.RevenueBuyMps)</div></div>
                                 <div class="rounded-2xl bg-white px-3 py-2"><div class="text-gray-500">REVENUE_BUY_WEB</div><div class="mt-1 font-semibold text-gray-900">@FormatNumber(item.RevenueBuyWeb)</div></div>
                             </div>
@@ -76,13 +91,17 @@
                             <thead class="bg-gray-50">
                                 <tr class="text-left text-xs font-bold uppercase tracking-[0.14em] text-gray-500">
                                     <th class="px-4 py-3">Date</th>
-                                    <th class="px-4 py-3">Total subscriptions</th>
-                                    <th class="px-4 py-3">Registrations</th>
-                                    <th class="px-4 py-3">Cancellations</th>
+                                    <th class="px-4 py-3">TotalSub</th>
+                                    <th class="px-4 py-3">REGISTER_TOTAL</th>
+                                    <th class="px-4 py-3">REGISTER_ON_WEB</th>
+                                    <th class="px-4 py-3">REGISTER_ON_MPS</th>
+                                    <th class="px-4 py-3">Cancel</th>
+                                    <th class="px-4 py-3">RENEW</th>
                                     <th class="px-4 py-3">Revenue</th>
                                     <th class="px-4 py-3">Bonus</th>
                                     <th class="px-4 py-3">BUY_ON_WEB</th>
                                     <th class="px-4 py-3">BUY_ON_MPS</th>
+                                    <th class="px-4 py-3">BUY_TOTAL</th>
                                     <th class="px-4 py-3">REVENUE_BUY_MPS</th>
                                     <th class="px-4 py-3">REVENUE_BUY_WEB</th>
                                 </tr>
@@ -94,11 +113,15 @@
                                         <td class="whitespace-nowrap px-4 py-4 font-semibold text-gray-900">@item.Ngay.ToString("dd/MM/yyyy")</td>
                                         <td class="whitespace-nowrap px-4 py-4">@FormatNumber(item.TongSub)</td>
                                         <td class="whitespace-nowrap px-4 py-4">@FormatNumber(item.DangKy)</td>
+                                        <td class="whitespace-nowrap px-4 py-4">@FormatNumber(item.DangKyOnWeb)</td>
+                                        <td class="whitespace-nowrap px-4 py-4">@FormatNumber(item.DangKyOnMps)</td>
                                         <td class="whitespace-nowrap px-4 py-4">@FormatNumber(item.Huy)</td>
+                                        <td class="whitespace-nowrap px-4 py-4">@FormatNumber(item.Renew)</td>
                                         <td class="whitespace-nowrap px-4 py-4 font-semibold text-gray-900">@FormatNumber(item.DoanhThu)</td>
                                         <td class="whitespace-nowrap px-4 py-4">@FormatNumber(item.CongTien)</td>
                                         <td class="whitespace-nowrap px-4 py-4">@FormatNumber(item.BuyOnWeb)</td>
                                         <td class="whitespace-nowrap px-4 py-4">@FormatNumber(item.BuyOnMps)</td>
+                                        <td class="whitespace-nowrap px-4 py-4">@FormatNumber(item.BuyTotal)</td>
                                         <td class="whitespace-nowrap px-4 py-4 font-semibold text-gray-900">@FormatNumber(item.RevenueBuyMps)</td>
                                         <td class="whitespace-nowrap px-4 py-4 font-semibold text-gray-900">@FormatNumber(item.RevenueBuyWeb)</td>
                                     </tr>
@@ -108,6 +131,40 @@
                     </div>
                 </div>
             </div>
+
+            <nav class="flex flex-wrap items-center justify-between gap-3 border-t border-gray-100 px-5 py-4 sm:px-6" aria-label="Daily report pagination">
+                <div class="text-sm text-gray-500">
+                    Showing @((Model.Page - 1) * Model.PageSize + 1)–@Math.Min(Model.Page * Model.PageSize, Model.TotalRows) of @Model.TotalRows rows
+                </div>
+                @if (Model.TotalPages > 1)
+                {
+                    var start = Math.Max(1, Model.Page - 2);
+                    var end = Math.Min(Model.TotalPages, start + 4);
+                    start = Math.Max(1, end - 4);
+
+                    <div class="flex items-center gap-1">
+                        @if (Model.Page > 1)
+                        {
+                            <a href="@PageUrl(Model.Page - 1)" class="rounded-lg px-3 py-2 text-sm font-semibold text-gray-600 hover:bg-gray-100" aria-label="Previous page">Previous</a>
+                        }
+                        @for (var p = start; p <= end; p++)
+                        {
+                            if (p == Model.Page)
+                            {
+                                <span class="min-w-[36px] rounded-lg bg-admin-active px-3 py-2 text-center text-sm font-bold text-white" aria-current="page">@p</span>
+                            }
+                            else
+                            {
+                                <a href="@PageUrl(p)" class="min-w-[36px] rounded-lg px-3 py-2 text-center text-sm font-semibold text-gray-600 hover:bg-gray-100">@p</a>
+                            }
+                        }
+                        @if (Model.Page < Model.TotalPages)
+                        {
+                            <a href="@PageUrl(Model.Page + 1)" class="rounded-lg px-3 py-2 text-sm font-semibold text-gray-600 hover:bg-gray-100" aria-label="Next page">Next</a>
+                        }
+                    </div>
+                }
+            </nav>
         }
     </div>
 </div>