Explorar el Código

thêm báo cáo cho cms

student hace 2 semanas
padre
commit
109ed751e2

+ 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.Bonus)
+                .HasDefaultValueSql("0")
+                .HasColumnType("NUMBER")
+                .HasColumnName("BONUS");
             entity.Property(e => e.DailyPrize)
                 .HasDefaultValueSql("0")
                 .HasColumnType("NUMBER")
@@ -1843,6 +1847,10 @@ public partial class ModelContext : DbContext
                 .HasDefaultValueSql("0")
                 .HasColumnType("NUMBER")
                 .HasColumnName("USER_ACTIVE");
+            entity.Property(e => e.UserActiveTotal)
+                .HasDefaultValueSql("0")
+                .HasColumnType("NUMBER")
+                .HasColumnName("USER_ACTIVE_TOTAL");
             entity.Property(e => e.UserInactive)
                 .HasDefaultValueSql("0")
                 .HasColumnType("NUMBER")

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

@@ -21,10 +21,14 @@ public partial class ReportByTime
 
     public decimal UserActive { get; set; }
 
+    public decimal? UserActiveTotal { get; set; }
+
     public decimal UserInactive { get; set; }
 
     public decimal Revenue { get; set; }
 
+    public decimal? Bonus { get; set; }
+
     public string Type { get; set; } = null!;
 
     public decimal DailyPrize { get; set; }

+ 100 - 0
Kitty_Fall/Kitty_Fall/Kitty_Fall.Cms/Controllers/DailyReportController.cs

@@ -0,0 +1,100 @@
+using Database.Database;
+using Kitty_Fall.Cms.Helpers;
+using Kitty_Fall.Cms.Models;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.EntityFrameworkCore;
+
+namespace Kitty_Fall.Cms.Controllers;
+
+public class DailyReportController : CmsAuthorizedController
+{
+    private const string DailyReportType = "DAILY";
+
+    private readonly ModelContext _db;
+    private readonly IReadOnlyList<ServiceFilterOptionViewModel> _serviceFilters;
+    private readonly HashSet<string> _supportedServiceFilters;
+    private readonly string _defaultServiceFilter;
+
+    public DailyReportController(ModelContext db, IConfiguration configuration)
+    {
+        _db = db;
+
+        _serviceFilters = configuration
+            .GetSection("RevenueReport:Services")
+            .Get<List<ServiceFilterOptionViewModel>>()?
+            .Where(x => !string.IsNullOrWhiteSpace(x.Value) && !string.IsNullOrWhiteSpace(x.Name))
+            .Select(x => new ServiceFilterOptionViewModel
+            {
+                Value = x.Value.Trim(),
+                Name = x.Name.Trim()
+            })
+            .DistinctBy(x => x.Value, StringComparer.OrdinalIgnoreCase)
+            .ToList()
+            ?? new List<ServiceFilterOptionViewModel>();
+
+        if (_serviceFilters.Count == 0)
+        {
+            throw new InvalidOperationException("RevenueReport:Services must contain at least one service.");
+        }
+
+        _supportedServiceFilters = _serviceFilters
+            .Select(x => x.Value)
+            .ToHashSet(StringComparer.OrdinalIgnoreCase);
+
+        var configuredDefault = configuration["RevenueReport:DefaultService"]?.Trim();
+        _defaultServiceFilter = !string.IsNullOrWhiteSpace(configuredDefault)
+            && _supportedServiceFilters.Contains(configuredDefault)
+                ? _serviceFilters.First(x => string.Equals(x.Value, configuredDefault, StringComparison.OrdinalIgnoreCase)).Value
+                : _serviceFilters[0].Value;
+    }
+
+    [HttpGet("/DailyReport")]
+    [HttpGet("/DailyReport/Index")]
+    [HttpGet("/Admin/DailyReport")]
+    public async Task<IActionResult> Index(DateTime? dateData = null, string? serviceFilter = null)
+    {
+        if (!CheckLogin()) return RedirectToLogin();
+        if (!HasMenuPermission(CmsSecurityHelper.DailyReportsMenuKey)) return RedirectNoPermission();
+
+        var normalizedDate = dateData?.Date;
+        var normalizedService = NormalizeServiceFilter(serviceFilter);
+        var query = _db.ReportByTimes
+            .AsNoTracking()
+            .Where(x => x.Type == DailyReportType && x.ServiceName == normalizedService);
+
+        if (normalizedDate.HasValue)
+        {
+            var nextDate = normalizedDate.Value.AddDays(1);
+            query = query.Where(x => x.TimeReport >= normalizedDate.Value && x.TimeReport < nextDate);
+        }
+
+        var items = await query
+            .OrderByDescending(x => x.TimeReport)
+            .Select(x => new DailyReportItemViewModel
+            {
+                Date = x.TimeReport,
+                TotalSubscribers = x.UserActiveTotal ?? 0,
+                Registered = x.UserActive,
+                Cancelled = x.UserInactive,
+                Revenue = x.Revenue,
+                Bonus = x.Bonus ?? 0
+            })
+            .ToListAsync();
+
+        return View(new DailyReportViewModel
+        {
+            DateData = normalizedDate,
+            ServiceFilter = normalizedService,
+            ServiceFilters = _serviceFilters,
+            Items = items
+        });
+    }
+
+    private string NormalizeServiceFilter(string? serviceFilter)
+    {
+        var normalized = (serviceFilter ?? string.Empty).Trim();
+        return _supportedServiceFilters.Contains(normalized)
+            ? _serviceFilters.First(x => string.Equals(x.Value, normalized, StringComparison.OrdinalIgnoreCase)).Value
+            : _defaultServiceFilter;
+    }
+}

+ 39 - 14
Kitty_Fall/Kitty_Fall/Kitty_Fall.Cms/Controllers/RevenueReportController.cs

@@ -9,19 +9,42 @@ namespace Kitty_Fall.Cms.Controllers;
 
 public class RevenueReportController : CmsAuthorizedController
 {
-    private const string DefaultServiceFilter = "MYJOB";
-    private static readonly HashSet<string> SupportedServiceFilters = new(StringComparer.OrdinalIgnoreCase)
-    {
-        "MYJOB",
-        "MYJOB_DAILY",
-        "MYJOB_MONTHLY"
-    };
-
     private readonly ModelContext _db;
+    private readonly IReadOnlyList<ServiceFilterOptionViewModel> _serviceFilters;
+    private readonly HashSet<string> _supportedServiceFilters;
+    private readonly string _defaultServiceFilter;
 
-    public RevenueReportController(ModelContext db)
+    public RevenueReportController(ModelContext db, IConfiguration configuration)
     {
         _db = db;
+
+        _serviceFilters = configuration
+            .GetSection("RevenueReport:Services")
+            .Get<List<ServiceFilterOptionViewModel>>()?
+            .Where(x => !string.IsNullOrWhiteSpace(x.Value) && !string.IsNullOrWhiteSpace(x.Name))
+            .Select(x => new ServiceFilterOptionViewModel
+            {
+                Value = x.Value.Trim(),
+                Name = x.Name.Trim()
+            })
+            .DistinctBy(x => x.Value, StringComparer.OrdinalIgnoreCase)
+            .ToList()
+            ?? new List<ServiceFilterOptionViewModel>();
+
+        if (_serviceFilters.Count == 0)
+        {
+            throw new InvalidOperationException("RevenueReport:Services must contain at least one service.");
+        }
+
+        _supportedServiceFilters = _serviceFilters
+            .Select(x => x.Value)
+            .ToHashSet(StringComparer.OrdinalIgnoreCase);
+
+        var configuredDefault = configuration["RevenueReport:DefaultService"]?.Trim();
+        _defaultServiceFilter = !string.IsNullOrWhiteSpace(configuredDefault)
+            && _supportedServiceFilters.Contains(configuredDefault)
+                ? _serviceFilters.First(x => string.Equals(x.Value, configuredDefault, StringComparison.OrdinalIgnoreCase)).Value
+                : _serviceFilters[0].Value;
     }
 
     [HttpGet("/RevenueReport")]
@@ -59,6 +82,7 @@ public class RevenueReportController : CmsAuthorizedController
             FromDate = normalizedFromDate,
             ToDate = normalizedToDate,
             ServiceFilter = normalizedServiceFilter,
+            ServiceFilters = _serviceFilters,
             TotalRows = items.Count,
             TotalRevenue = items.Sum(x => x.Revenue),
             AverageRevenue = items.Count == 0 ? 0 : items.Average(x => x.Revenue),
@@ -94,6 +118,7 @@ public class RevenueReportController : CmsAuthorizedController
             FromDate = normalizedFromDate,
             ToDate = normalizedToDate,
             ServiceFilter = normalizedServiceFilter,
+            ServiceFilters = _serviceFilters,
             TotalColumns = items.Count,
             TotalRevenue = items.Sum(x => x.Revenue),
             AverageRevenue = items.Count == 0 ? 0 : items.Average(x => x.Revenue),
@@ -125,12 +150,12 @@ public class RevenueReportController : CmsAuthorizedController
         return (normalizedFromDate, normalizedToDate);
     }
 
-    private static string NormalizeServiceFilter(string? serviceFilter)
+    private string NormalizeServiceFilter(string? serviceFilter)
     {
-        var normalizedServiceFilter = (serviceFilter ?? string.Empty).Trim().ToUpperInvariant();
-        return SupportedServiceFilters.Contains(normalizedServiceFilter)
-            ? normalizedServiceFilter
-            : DefaultServiceFilter;
+        var normalizedServiceFilter = (serviceFilter ?? string.Empty).Trim();
+        return _supportedServiceFilters.Contains(normalizedServiceFilter)
+            ? _serviceFilters.First(x => string.Equals(x.Value, normalizedServiceFilter, StringComparison.OrdinalIgnoreCase)).Value
+            : _defaultServiceFilter;
     }
 
     private IQueryable<ReportByTime> BuildBaseQuery(string reportType, DateTime? fromDate, DateTime? toDate, string serviceFilter)

+ 18 - 0
Kitty_Fall/Kitty_Fall/Kitty_Fall.Cms/Helpers/CmsSecurityHelper.cs

@@ -15,6 +15,7 @@ public static class CmsSecurityHelper
     public const string CmsJobPostingMenuKey = "cms-job-posting";
     public const string JobsMenuKey = "jobs";
     public const string RevenueReportsMenuKey = "revenue-reports";
+    public const string DailyReportsMenuKey = "daily-reports";
     public const string SubscriptionChargeReportsMenuKey = "subscription-charge-reports";
     public const string MonthlyWinnersMenuKey = "monthly-winners";
     public const string EmployersMenuKey = "employers";
@@ -170,6 +171,23 @@ public static class CmsSecurityHelper
             }
         },
         new()
+        {
+            Key = DailyReportsMenuKey,
+            Title = "Daily Report",
+            Url = "/DailyReport",
+            IconClass = "fas fa-calendar-day",
+            PermissionPaths = new List<string>
+            {
+                "/dailyreport",
+                "/dailyreport/index",
+                "/admin/dailyreport",
+                // Tai khoan dang co quyen Revenue Reports duoc xem menu bao cao ngay.
+                "/revenuereport",
+                "/revenuereport/index",
+                "/admin/revenuereport"
+            }
+        },
+        new()
         {
             Key = SubscriptionChargeReportsMenuKey,
             Title = "Subscription & Charge",

+ 27 - 0
Kitty_Fall/Kitty_Fall/Kitty_Fall.Cms/Models/DailyReportViewModel.cs

@@ -0,0 +1,27 @@
+namespace Kitty_Fall.Cms.Models;
+
+public class DailyReportViewModel
+{
+    public DateTime? DateData { get; set; }
+
+    public string ServiceFilter { get; set; } = string.Empty;
+
+    public IReadOnlyList<ServiceFilterOptionViewModel> ServiceFilters { get; set; } = Array.Empty<ServiceFilterOptionViewModel>();
+
+    public List<DailyReportItemViewModel> Items { get; set; } = new();
+}
+
+public class DailyReportItemViewModel
+{
+    public DateTime Date { get; set; }
+
+    public decimal TotalSubscribers { get; set; }
+
+    public decimal Registered { get; set; }
+
+    public decimal Cancelled { get; set; }
+
+    public decimal Revenue { get; set; }
+
+    public decimal Bonus { get; set; }
+}

+ 11 - 0
Kitty_Fall/Kitty_Fall/Kitty_Fall.Cms/Models/RevenueReportViewModel.cs

@@ -10,6 +10,8 @@ public class RevenueReportViewModel
 
     public string? ServiceFilter { get; set; }
 
+    public IReadOnlyList<ServiceFilterOptionViewModel> ServiceFilters { get; set; } = Array.Empty<ServiceFilterOptionViewModel>();
+
     public int TotalRows { get; set; }
 
     public decimal TotalRevenue { get; set; }
@@ -29,6 +31,8 @@ public class RevenueTrendReportViewModel
 
     public string? ServiceFilter { get; set; }
 
+    public IReadOnlyList<ServiceFilterOptionViewModel> ServiceFilters { get; set; } = Array.Empty<ServiceFilterOptionViewModel>();
+
     public int TotalColumns { get; set; }
 
     public decimal TotalRevenue { get; set; }
@@ -45,6 +49,13 @@ public class RevenueTrendItemViewModel
     public decimal Revenue { get; set; }
 }
 
+public class ServiceFilterOptionViewModel
+{
+    public string Value { get; set; } = string.Empty;
+
+    public string Name { get; set; } = string.Empty;
+}
+
 public class RevenueReportItemViewModel
 {
     public DateTime TimeReport { get; set; }

+ 92 - 0
Kitty_Fall/Kitty_Fall/Kitty_Fall.Cms/Views/DailyReport/Index.cshtml

@@ -0,0 +1,92 @@
+@model Kitty_Fall.Cms.Models.DailyReportViewModel
+@{
+    ViewData["Title"] = "Daily Report";
+    var dateValue = Model.DateData?.ToString("yyyy-MM-dd");
+    string FormatNumber(decimal value) => value.ToString("#,##0.##");
+}
+
+<div class="py-6 sm:py-8">
+    <div class="rounded-3xl bg-white shadow-sm ring-1 ring-gray-100">
+        <div class="border-b border-gray-100 px-5 py-5 sm:px-6">
+            <div>
+                <h1 class="text-2xl font-bold text-gray-900">Daily Report</h1>
+                <p class="mt-1 text-sm text-gray-500">Daily subscriber, registration, cancellation, revenue and bonus report.</p>
+            </div>
+
+            <form method="get" action="@Url.Content("~/DailyReport")" class="mt-5 grid gap-3 md:grid-cols-[minmax(0,1fr)_minmax(0,1fr)_auto]">
+                <label class="block">
+                    <span class="mb-2 block text-sm font-semibold text-gray-700">Service</span>
+                    <select name="serviceFilter" class="w-full rounded-2xl border border-gray-200 px-4 py-3 text-sm text-gray-700 outline-none transition focus:border-admin-active focus:ring-2 focus:ring-purple-100">
+                        @foreach (var service in Model.ServiceFilters)
+                        {
+                            <option value="@service.Value" selected="@(string.Equals(Model.ServiceFilter, service.Value, StringComparison.OrdinalIgnoreCase) ? "selected" : null)">@service.Name</option>
+                        }
+                    </select>
+                </label>
+                <label class="block">
+                    <span class="mb-2 block text-sm font-semibold text-gray-700">Date</span>
+                    <input type="date" name="dateData" value="@dateValue" class="w-full rounded-2xl border border-gray-200 px-4 py-3 text-sm text-gray-700 outline-none transition focus:border-admin-active focus:ring-2 focus:ring-purple-100" />
+                </label>
+                <div class="flex items-end">
+                    <button type="submit" class="inline-flex w-full items-center justify-center rounded-2xl bg-admin-active px-4 py-3 text-sm font-semibold text-white transition hover:bg-purple-700 md:w-auto">
+                        Apply filter
+                    </button>
+                </div>
+            </form>
+        </div>
+
+        @if (Model.Items.Count == 0)
+        {
+            <div class="px-5 py-10 text-center sm:px-6">
+                <div class="text-base font-semibold text-gray-800">No daily report data found.</div>
+                <div class="mt-2 text-sm text-gray-500">Adjust the service or date and try again.</div>
+            </div>
+        }
+        else
+        {
+            <div class="space-y-3 px-5 py-6 md:hidden">
+                @foreach (var item in Model.Items)
+                {
+                    <div class="rounded-2xl border border-gray-100 bg-white p-4 shadow-sm">
+                        <div class="text-base font-bold text-gray-900">@item.Date.ToString("dd/MM/yyyy")</div>
+                        <div class="mt-4 grid grid-cols-2 gap-4 text-sm">
+                            <div><div class="text-xs font-bold uppercase text-gray-400">Total subscribers</div><div class="mt-1 font-semibold text-gray-800">@FormatNumber(item.TotalSubscribers)</div></div>
+                            <div><div class="text-xs font-bold uppercase text-gray-400">Registered</div><div class="mt-1 font-semibold text-gray-800">@FormatNumber(item.Registered)</div></div>
+                            <div><div class="text-xs font-bold uppercase text-gray-400">Cancelled</div><div class="mt-1 font-semibold text-gray-800">@FormatNumber(item.Cancelled)</div></div>
+                            <div><div class="text-xs font-bold uppercase text-gray-400">Revenue</div><div class="mt-1 font-semibold text-gray-800">@FormatNumber(item.Revenue)</div></div>
+                            <div><div class="text-xs font-bold uppercase text-gray-400">Bonus</div><div class="mt-1 font-semibold text-gray-800">@FormatNumber(item.Bonus)</div></div>
+                        </div>
+                    </div>
+                }
+            </div>
+
+            <div class="hidden overflow-x-auto md:block">
+                <table class="min-w-full divide-y divide-gray-100">
+                    <thead class="bg-gray-50">
+                        <tr class="text-left text-xs font-bold uppercase tracking-[0.14em] text-gray-500">
+                            <th class="px-5 py-4">Date</th>
+                            <th class="px-5 py-4 text-right">Total subscribers</th>
+                            <th class="px-5 py-4 text-right">Registered</th>
+                            <th class="px-5 py-4 text-right">Cancelled</th>
+                            <th class="px-5 py-4 text-right">Revenue</th>
+                            <th class="px-5 py-4 text-right">Bonus</th>
+                        </tr>
+                    </thead>
+                    <tbody class="divide-y divide-gray-100 bg-white text-sm text-gray-700">
+                        @foreach (var item in Model.Items)
+                        {
+                            <tr>
+                                <td class="whitespace-nowrap px-5 py-4 font-semibold text-gray-900">@item.Date.ToString("dd/MM/yyyy")</td>
+                                <td class="whitespace-nowrap px-5 py-4 text-right">@FormatNumber(item.TotalSubscribers)</td>
+                                <td class="whitespace-nowrap px-5 py-4 text-right">@FormatNumber(item.Registered)</td>
+                                <td class="whitespace-nowrap px-5 py-4 text-right">@FormatNumber(item.Cancelled)</td>
+                                <td class="whitespace-nowrap px-5 py-4 text-right font-semibold text-gray-900">@FormatNumber(item.Revenue)</td>
+                                <td class="whitespace-nowrap px-5 py-4 text-right">@FormatNumber(item.Bonus)</td>
+                            </tr>
+                        }
+                    </tbody>
+                </table>
+            </div>
+        }
+    </div>
+</div>

+ 4 - 3
Kitty_Fall/Kitty_Fall/Kitty_Fall.Cms/Views/Reports/Revenue.cshtml

@@ -80,9 +80,10 @@
                 <label class="block">
                     <span class="mb-2 block text-sm font-semibold text-gray-700">Service</span>
                     <select name="serviceFilter" class="w-full rounded-2xl border border-gray-200 px-4 py-3 text-sm text-gray-700 outline-none transition focus:border-admin-active focus:ring-2 focus:ring-purple-100">
-                        <option value="MYJOB" selected="@(Model.ServiceFilter == "MYJOB" ? "selected" : null)">MYJOB</option>
-                        <option value="MYJOB_DAILY" selected="@(Model.ServiceFilter == "MYJOB_DAILY" ? "selected" : null)">MYJOB_DAILY</option>
-                        <option value="MYJOB_MONTHLY" selected="@(Model.ServiceFilter == "MYJOB_MONTHLY" ? "selected" : null)">MYJOB_MONTHLY</option>
+                        @foreach (var service in Model.ServiceFilters)
+                        {
+                            <option value="@service.Value" selected="@(string.Equals(Model.ServiceFilter, service.Value, StringComparison.OrdinalIgnoreCase) ? "selected" : null)">@service.Name</option>
+                        }
                     </select>
                 </label>
                 <label class="block">

+ 4 - 3
Kitty_Fall/Kitty_Fall/Kitty_Fall.Cms/Views/Reports/RevenueTrend.cshtml

@@ -76,9 +76,10 @@
                 <label class="block">
                     <span class="mb-2 block text-sm font-semibold text-gray-700">Service</span>
                     <select name="serviceFilter" class="w-full rounded-2xl border border-gray-200 px-4 py-3 text-sm text-gray-700 outline-none transition focus:border-admin-active focus:ring-2 focus:ring-purple-100">
-                        <option value="MYJOB" selected="@(Model.ServiceFilter == "MYJOB" ? "selected" : null)">MYJOB</option>
-                        <option value="MYJOB_DAILY" selected="@(Model.ServiceFilter == "MYJOB_DAILY" ? "selected" : null)">MYJOB_DAILY</option>
-                        <option value="MYJOB_MONTHLY" selected="@(Model.ServiceFilter == "MYJOB_MONTHLY" ? "selected" : null)">MYJOB_MONTHLY</option>
+                        @foreach (var service in Model.ServiceFilters)
+                        {
+                            <option value="@service.Value" selected="@(string.Equals(Model.ServiceFilter, service.Value, StringComparison.OrdinalIgnoreCase) ? "selected" : null)">@service.Name</option>
+                        }
                     </select>
                 </label>
                 <label class="block">

+ 22 - 1
Kitty_Fall/Kitty_Fall/Kitty_Fall.Cms/Views/Shared/_Layout.cshtml

@@ -13,6 +13,7 @@
     var canViewCmsJobPosting = CmsSecurityHelper.HasMenuAccess(cmsSession, CmsSecurityHelper.CmsJobPostingMenuKey);
     var canViewJobs = CmsSecurityHelper.HasMenuAccess(cmsSession, CmsSecurityHelper.JobsMenuKey);
     var canViewRevenueReports = CmsSecurityHelper.HasMenuAccess(cmsSession, CmsSecurityHelper.RevenueReportsMenuKey);
+    var canViewDailyReports = CmsSecurityHelper.HasMenuAccess(cmsSession, CmsSecurityHelper.DailyReportsMenuKey);
     var canViewSubscriptionChargeReports = CmsSecurityHelper.HasMenuAccess(cmsSession, CmsSecurityHelper.SubscriptionChargeReportsMenuKey);
     var canViewMonthlyWinners = CmsSecurityHelper.HasMenuAccess(cmsSession, CmsSecurityHelper.MonthlyWinnersMenuKey);
     var canViewEmployers = CmsSecurityHelper.HasMenuAccess(cmsSession, CmsSecurityHelper.EmployersMenuKey);
@@ -50,7 +51,7 @@
 <head>
     <meta charset="utf-8" />
     <meta name="viewport" content="width=device-width, initial-scale=1.0" />
-    <title>@ViewData["Title"] - MyJob Admin</title>
+    <title>@ViewData["Title"] - CMS Admin</title>
     <script src="https://cdn.tailwindcss.com"></script>
     <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
     <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700&display=swap" rel="stylesheet">
@@ -192,6 +193,16 @@
                         </a>
                     </li>
                 }
+                @if (canViewDailyReports)
+                {
+                    <li class="relative px-6 py-3">
+                        <span class="@NavMarkerClass("/dailyreport", "/admin/dailyreport") absolute inset-y-0 left-0 w-1 bg-admin-active rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
+                        <a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 @NavLinkClass("/dailyreport", "/admin/dailyreport")" href="~/DailyReport" onclick="closeMobileSidebar()">
+                            <i class="fas fa-calendar-day w-5 h-5"></i>
+                            <span class="ml-4">Daily Report</span>
+                        </a>
+                    </li>
+                }
                 @if (canViewSubscriptionChargeReports)
                 {
                     <li class="relative px-6 py-3">
@@ -380,6 +391,16 @@
                             </a>
                         </li>
                     }
+                    @if (canViewDailyReports)
+                    {
+                        <li class="relative px-6 py-3">
+                            <span class="@NavMarkerClass("/dailyreport", "/admin/dailyreport") absolute inset-y-0 left-0 w-1 bg-admin-active rounded-tr-lg rounded-br-lg" aria-hidden="true"></span>
+                            <a class="inline-flex items-center w-full text-sm font-semibold transition-colors duration-150 @NavLinkClass("/dailyreport", "/admin/dailyreport")" href="~/DailyReport">
+                                <i class="fas fa-calendar-day w-5 h-5"></i>
+                                <span class="ml-4">Daily Report</span>
+                            </a>
+                        </li>
+                    }
                     @if (canViewSubscriptionChargeReports)
                     {
                         <li class="relative px-6 py-3">

+ 9 - 0
Kitty_Fall/Kitty_Fall/Kitty_Fall.Cms/appsettings.json

@@ -2,6 +2,15 @@
   "PathBase": "/cms",
   // "Connection": "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=1539))(CONNECT_DATA=(SERVICE_NAME=ORA12C)));User Id=myjob;Password=123456;Connection Timeout=120;",
   "Connection": "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=127.0.0.1)(PORT=1539))(CONNECT_DATA=(SERVICE_NAME=ORA12C)));User Id=Kitty_Fall;Password=123456;Connection Timeout=120;",
+  "RevenueReport": {
+    "DefaultService": "Kitty_Fall_DAYLY",
+    "Services": [
+      {
+        "Value": "Kitty_Fall_DAYLY",
+        "Name": "Kitty_Fall_DAYLY"
+      }
+    ]
+  },
   "Logging": {
     "LogLevel": {
       "Default": "Information",