diff --git a/PlanTempus.Application/Features/Account/Components/InvoiceHistory/Default.cshtml b/PlanTempus.Application/Features/Account/Components/InvoiceHistory/Default.cshtml index 9fd16d3..915919b 100644 --- a/PlanTempus.Application/Features/Account/Components/InvoiceHistory/Default.cshtml +++ b/PlanTempus.Application/Features/Account/Components/InvoiceHistory/Default.cshtml @@ -1,15 +1,15 @@ - Faktura-historik + Faktura-historik - Dato - Fakturanr. - Beløb - Status + Dato + Fakturanr. + Beløb + Status @@ -21,13 +21,13 @@ - PDF + PDF @@ -39,13 +39,13 @@ - PDF + PDF @@ -57,13 +57,13 @@ - PDF + PDF @@ -75,13 +75,13 @@ - PDF + PDF @@ -93,13 +93,13 @@ - PDF + PDF diff --git a/PlanTempus.Application/Features/Account/Components/PaymentMethod/Default.cshtml b/PlanTempus.Application/Features/Account/Components/PaymentMethod/Default.cshtml index d748aa1..1336b2d 100644 --- a/PlanTempus.Application/Features/Account/Components/PaymentMethod/Default.cshtml +++ b/PlanTempus.Application/Features/Account/Components/PaymentMethod/Default.cshtml @@ -9,32 +9,32 @@ - Skift + Skift - Betalingsfrekvens - Månedlig + Betalingsfrekvens + Månedlig - Næste betaling + Næste betaling 1. februar 2026 - Beløb + Beløb 599,00 kr - Kortudløb + Kortudløb 08/2027 - Skift til årlig betaling (spar 15%) + Skift til årlig betaling (spar 15%) diff --git a/PlanTempus.Application/Features/Account/Components/SubscriptionPlans/Default.cshtml b/PlanTempus.Application/Features/Account/Components/SubscriptionPlans/Default.cshtml index 7464bb3..fc0c4be 100644 --- a/PlanTempus.Application/Features/Account/Components/SubscriptionPlans/Default.cshtml +++ b/PlanTempus.Application/Features/Account/Components/SubscriptionPlans/Default.cshtml @@ -1,8 +1,10 @@ @using PlanTempus.Application.Features.Accounts.Models +@using PlanTempus.Application.Features.Localization.Services @model IEnumerable @{ var currentPlanKey = (string)ViewBag.CurrentPlanKey; + var L = (ILocalizationService)ViewBag.Localization; } @@ -16,14 +18,14 @@ }; var badgeClass = isCurrent ? "current" : plan.BadgeClass; - var badgeText = isCurrent ? "Nuværende plan" : plan.BadgeText; + var badgeText = isCurrent ? L.Get("account.subscription.currentPlan") : plan.BadgeText; var badgeIcon = isCurrent ? "ph-check" : plan.BadgeIcon; var buttonText = isCurrent - ? "Nuværende plan" + ? L.Get("account.subscription.currentPlan") : plan.IsContactSales - ? "Kontakt salg" - : $"Skift til {plan.Name}"; + ? L.Get("account.subscription.contactSales") + : L.Get("account.subscription.switchTo").Replace("{plan}", plan.Name); var buttonClass = isCurrent ? "secondary" @@ -42,11 +44,11 @@ @if (plan.PricePerMonth.HasValue) { @plan.PriceDisplay - kr/md + kr/md } else { - Kontakt os + Kontakt os } diff --git a/PlanTempus.Application/Features/Account/Components/SubscriptionPlans/SubscriptionPlansViewComponent.cs b/PlanTempus.Application/Features/Account/Components/SubscriptionPlans/SubscriptionPlansViewComponent.cs index c0ad730..1b09297 100644 --- a/PlanTempus.Application/Features/Account/Components/SubscriptionPlans/SubscriptionPlansViewComponent.cs +++ b/PlanTempus.Application/Features/Account/Components/SubscriptionPlans/SubscriptionPlansViewComponent.cs @@ -1,5 +1,6 @@ using Microsoft.AspNetCore.Mvc; using PlanTempus.Application.Features.Accounts.Models; +using PlanTempus.Application.Features.Localization.Services; namespace PlanTempus.Application.Features.Account.Components; @@ -9,6 +10,13 @@ namespace PlanTempus.Application.Features.Account.Components; /// public class SubscriptionPlansViewComponent : ViewComponent { + private readonly ILocalizationService _localization; + + public SubscriptionPlansViewComponent(ILocalizationService localization) + { + _localization = localization; + } + public IViewComponentResult Invoke() { var plans = PlanCatalog.GetAllPlans(); @@ -16,6 +24,7 @@ public class SubscriptionPlansViewComponent : ViewComponent var currentPlanKey = "pro"; ViewBag.CurrentPlanKey = currentPlanKey; + ViewBag.Localization = _localization; return View(plans); } } diff --git a/PlanTempus.Application/Features/Account/Pages/Index.cshtml b/PlanTempus.Application/Features/Account/Pages/Index.cshtml index 13b2920..e48143b 100644 --- a/PlanTempus.Application/Features/Account/Pages/Index.cshtml +++ b/PlanTempus.Application/Features/Account/Pages/Index.cshtml @@ -10,8 +10,8 @@ -

Abonnement & Konto

-

Administrer dit abonnement og betalingsinfo

+

Abonnement & Konto

+

Administrer dit abonnement og betalingsinfo

@@ -20,7 +20,7 @@ - Dit abonnement + Dit abonnement @@ -32,7 +32,7 @@ - Betaling & Fakturaer + Betaling & Fakturaer diff --git a/PlanTempus.Application/Features/CashRegister/Components/CashApproval/Default.cshtml b/PlanTempus.Application/Features/CashRegister/Components/CashApproval/Default.cshtml index 4f8ca39..1be609a 100644 --- a/PlanTempus.Application/Features/CashRegister/Components/CashApproval/Default.cshtml +++ b/PlanTempus.Application/Features/CashRegister/Components/CashApproval/Default.cshtml @@ -1,21 +1,21 @@ - Afslut dagen + Afslut dagen - Status + Status - Kladde + Kladde - Godkendt af (valgfrit) + Godkendt af (valgfrit) @@ -24,15 +24,15 @@ - + - Gem som kladde + Gem som kladde - Fortryd - Godkend & lås + Fortryd + Godkend & lås diff --git a/PlanTempus.Application/Features/CashRegister/Components/CashBalance/Default.cshtml b/PlanTempus.Application/Features/CashRegister/Components/CashBalance/Default.cshtml index 9b339fc..d6f3cc1 100644 --- a/PlanTempus.Application/Features/CashRegister/Components/CashBalance/Default.cshtml +++ b/PlanTempus.Application/Features/CashRegister/Components/CashBalance/Default.cshtml @@ -1,20 +1,20 @@ - Kontanter i kassen + Kontanter i kassen - Startbeholdning - Overført fra sidste afstemning + Startbeholdning + Overført fra sidste afstemning 2.000,00 - Udbetalinger / Bilag - Sammentæl bilag betalt kontant + Udbetalinger / Bilag + Sammentæl bilag betalt kontant @@ -23,8 +23,8 @@ - Udtaget til bank - Kontanter lagt til side + Udtaget til bank + Kontanter lagt til side @@ -33,15 +33,15 @@ - Forventet kontantbeholdning + Forventet kontantbeholdning 5.220,00 - Optalt kontantbeholdning * - Hvad ligger der faktisk i kassen? + Optalt kontantbeholdning * + Hvad ligger der faktisk i kassen? diff --git a/PlanTempus.Application/Features/CashRegister/Components/CashDifference/Default.cshtml b/PlanTempus.Application/Features/CashRegister/Components/CashDifference/Default.cshtml index b0a8b4d..c2520f9 100644 --- a/PlanTempus.Application/Features/CashRegister/Components/CashDifference/Default.cshtml +++ b/PlanTempus.Application/Features/CashRegister/Components/CashDifference/Default.cshtml @@ -1,6 +1,6 @@ - Kontant difference + Kassedifference Optalt minus forventet – kr diff --git a/PlanTempus.Application/Features/CashRegister/Components/PeriodInfo/Default.cshtml b/PlanTempus.Application/Features/CashRegister/Components/PeriodInfo/Default.cshtml index 94feb27..8956a97 100644 --- a/PlanTempus.Application/Features/CashRegister/Components/PeriodInfo/Default.cshtml +++ b/PlanTempus.Application/Features/CashRegister/Components/PeriodInfo/Default.cshtml @@ -1,11 +1,10 @@ - Periodeoplysninger - Identificér afstemningen + Periodeoplysninger - Periode + Periode 28. dec 2025 kl. 18:00 @@ -15,7 +14,7 @@ - Kassepunkt + Kassepunkt diff --git a/PlanTempus.Application/Features/CashRegister/Components/PeriodRevenue/Default.cshtml b/PlanTempus.Application/Features/CashRegister/Components/PeriodRevenue/Default.cshtml index 77e2348..aac8bde 100644 --- a/PlanTempus.Application/Features/CashRegister/Components/PeriodRevenue/Default.cshtml +++ b/PlanTempus.Application/Features/CashRegister/Components/PeriodRevenue/Default.cshtml @@ -1,7 +1,7 @@ - Periodens omsætning - Systemtal vs. kontrol + Periodens omsætning + Systemtal vs. kontrol @@ -12,7 +12,7 @@ - Kortbetalinger + Kortbetalinger 12.875,50 @@ -20,7 +20,7 @@ - MobilePay / Online + MobilePay / Online 2.450,00 @@ -28,12 +28,12 @@ - Kontantsalg + Kontantsalg 3.540,00 .. - Kort og MobilePay afstemmes mod bank/indløser. Kontanter tælles op nedenfor. + Kort og MobilePay afstemmes mod bank/indløser. Kontanter tælles op nedenfor. diff --git a/PlanTempus.Application/Features/CashRegister/Components/ReconciliationNote/Default.cshtml b/PlanTempus.Application/Features/CashRegister/Components/ReconciliationNote/Default.cshtml index 5d63a14..bda7473 100644 --- a/PlanTempus.Application/Features/CashRegister/Components/ReconciliationNote/Default.cshtml +++ b/PlanTempus.Application/Features/CashRegister/Components/ReconciliationNote/Default.cshtml @@ -1,12 +1,10 @@ - Note til difference - Valgfrit + Note til afstemning - + - Kan gøres obligatorisk ved difference over 100 kr. diff --git a/PlanTempus.Application/Features/CashRegister/Components/ReconciliationTable/Default.cshtml b/PlanTempus.Application/Features/CashRegister/Components/ReconciliationTable/Default.cshtml index 2675ca8..e75ddf8 100644 --- a/PlanTempus.Application/Features/CashRegister/Components/ReconciliationTable/Default.cshtml +++ b/PlanTempus.Application/Features/CashRegister/Components/ReconciliationTable/Default.cshtml @@ -1,11 +1,11 @@ - 0 valgt + 0 valgt - Eksporter SAF-T + Eksporter SAF-T @@ -13,14 +13,14 @@ - Dato - ID - Periode - Kassepunkt - Afsluttet af - Omsætning - Difference - Status + Dato + ID + Periode + Kassepunkt + Afsluttet af + Omsætning + Difference + Status @@ -39,7 +39,7 @@ 4.250 kr - Kladde + Kladde @@ -56,7 +56,7 @@ Anna Jensen 18.865 kr 0 kr - Godkendt + Godkendt @@ -64,15 +64,15 @@ - Download CSV + Download CSV - Download PDF + Download PDF - Se transaktioner + Se transaktioner @@ -91,7 +91,7 @@ Karina Knudsen 12.450 kr -25 kr - Godkendt + Godkendt @@ -99,15 +99,15 @@ - Download CSV + Download CSV - Download PDF + Download PDF - Se transaktioner + Se transaktioner @@ -126,7 +126,7 @@ Martin Nielsen 21.340 kr 0 kr - Godkendt + Godkendt @@ -134,15 +134,15 @@ - Download CSV + Download CSV - Download PDF + Download PDF - Se transaktioner + Se transaktioner @@ -161,7 +161,7 @@ Anna Jensen 28.750 kr -50 kr - Godkendt + Godkendt @@ -169,15 +169,15 @@ - Download CSV + Download CSV - Download PDF + Download PDF - Se transaktioner + Se transaktioner diff --git a/PlanTempus.Application/Features/CashRegister/Components/RegisterFilterBar/Default.cshtml b/PlanTempus.Application/Features/CashRegister/Components/RegisterFilterBar/Default.cshtml index 2371b88..ec7d328 100644 --- a/PlanTempus.Application/Features/CashRegister/Components/RegisterFilterBar/Default.cshtml +++ b/PlanTempus.Application/Features/CashRegister/Components/RegisterFilterBar/Default.cshtml @@ -1,28 +1,28 @@ - Fra + Fra - Til + Til - Kassepunkt + Kassepunkt - Status + Status - Nulstil + Nulstil diff --git a/PlanTempus.Application/Features/CashRegister/Pages/Index.cshtml b/PlanTempus.Application/Features/CashRegister/Pages/Index.cshtml index cec4339..4a0e869 100644 --- a/PlanTempus.Application/Features/CashRegister/Pages/Index.cshtml +++ b/PlanTempus.Application/Features/CashRegister/Pages/Index.cshtml @@ -13,19 +13,19 @@ 12 - Afstemninger i periode + Afstemninger i periode 186.450 kr - Total omsætning + Total omsætning 42.340 kr - Kontantsalg + Kontantsalg -75 kr - Samlet difference + Samlet difference @@ -33,19 +33,19 @@ 47 - Transaktioner i dag + Transaktioner i dag 18.865 kr - Omsætning i dag + Omsætning i dag 29. dec 17:45 - Sidste afstemning + Sidste afstemning Anna J. - Åbnede kassen 29. dec 09:05 + Åbnede kassen 29. dec 09:05 @@ -54,11 +54,11 @@ - Oversigt + Oversigt - Kasseafstemning + Kasseafstemning @@ -88,6 +88,6 @@ - Systemet gemmer hvornår og af hvem der er godkendt – enkelt kontrolspor. + Systemet gemmer hvornår og af hvem der er godkendt – enkelt kontrolspor. diff --git a/PlanTempus.Application/Features/Dashboard/Components/AttentionList/AttentionListViewComponent.cs b/PlanTempus.Application/Features/Dashboard/Components/AttentionList/AttentionListViewComponent.cs index 2892d22..e62bff8 100644 --- a/PlanTempus.Application/Features/Dashboard/Components/AttentionList/AttentionListViewComponent.cs +++ b/PlanTempus.Application/Features/Dashboard/Components/AttentionList/AttentionListViewComponent.cs @@ -1,12 +1,20 @@ using Microsoft.AspNetCore.Mvc; +using PlanTempus.Application.Features.Localization.Services; namespace PlanTempus.Application.Features.Dashboard.Components; public class AttentionListViewComponent : ViewComponent { + private readonly ILocalizationService _localization; + + public AttentionListViewComponent(ILocalizationService localization) + { + _localization = localization; + } + public IViewComponentResult Invoke(string key) { - var model = AttentionListCatalog.Get(key); + var model = AttentionListCatalog.Get(key, _localization); return View(model); } } @@ -18,23 +26,35 @@ public class AttentionListViewModel public required IReadOnlyList AttentionKeys { get; init; } } +internal class AttentionListData +{ + public required string Key { get; init; } + public required string TitleKey { get; init; } + public required IReadOnlyList AttentionKeys { get; init; } +} + public static class AttentionListCatalog { - private static readonly Dictionary Lists = new() + private static readonly Dictionary Lists = new() { - ["current-attentions"] = new AttentionListViewModel + ["current-attentions"] = new AttentionListData { Key = "current-attentions", - Title = "Opmærksomheder", + TitleKey = "dashboard.attentions.title", AttentionKeys = ["attention-1", "attention-2", "attention-3"] } }; - public static AttentionListViewModel Get(string key) + public static AttentionListViewModel Get(string key, ILocalizationService localization) { - if (Lists.TryGetValue(key, out var list)) - return list; + if (!Lists.TryGetValue(key, out var list)) + throw new KeyNotFoundException($"AttentionList with key '{key}' not found"); - throw new KeyNotFoundException($"AttentionList with key '{key}' not found"); + return new AttentionListViewModel + { + Key = list.Key, + Title = localization.Get(list.TitleKey), + AttentionKeys = list.AttentionKeys + }; } } diff --git a/PlanTempus.Application/Features/Dashboard/Components/BookingItem/BookingItemViewComponent.cs b/PlanTempus.Application/Features/Dashboard/Components/BookingItem/BookingItemViewComponent.cs index 4e172f3..3770e60 100644 --- a/PlanTempus.Application/Features/Dashboard/Components/BookingItem/BookingItemViewComponent.cs +++ b/PlanTempus.Application/Features/Dashboard/Components/BookingItem/BookingItemViewComponent.cs @@ -1,12 +1,20 @@ using Microsoft.AspNetCore.Mvc; +using PlanTempus.Application.Features.Localization.Services; namespace PlanTempus.Application.Features.Dashboard.Components; public class BookingItemViewComponent : ViewComponent { + private readonly ILocalizationService _localization; + + public BookingItemViewComponent(ILocalizationService localization) + { + _localization = localization; + } + public IViewComponentResult Invoke(string key) { - var model = BookingItemCatalog.Get(key); + var model = BookingItemCatalog.Get(key, _localization); return View(model); } } @@ -21,23 +29,36 @@ public class BookingItemViewModel public required string EmployeeInitials { get; init; } public required string EmployeeName { get; init; } public required string Status { get; init; } + public required string StatusText { get; init; } public string? IndicatorColor { get; init; } +} - public string StatusText => Status switch - { - "completed" => "Gennemført", - "inprogress" => "I gang", - "confirmed" => "Bekræftet", - "pending" => "Afventer", - _ => Status - }; +internal class BookingItemData +{ + public required string Key { get; init; } + public required string TimeStart { get; init; } + public required string TimeEnd { get; init; } + public required string Service { get; init; } + public required string CustomerName { get; init; } + public required string EmployeeInitials { get; init; } + public required string EmployeeName { get; init; } + public required string Status { get; init; } + public string? IndicatorColor { get; init; } } public static class BookingItemCatalog { - private static readonly Dictionary Bookings = new() + private static readonly Dictionary StatusKeys = new() { - ["booking-1"] = new BookingItemViewModel + ["completed"] = "dashboard.bookings.status.completed", + ["inprogress"] = "dashboard.bookings.status.inProgress", + ["confirmed"] = "dashboard.bookings.status.confirmed", + ["pending"] = "dashboard.bookings.status.pending" + }; + + private static readonly Dictionary Bookings = new() + { + ["booking-1"] = new BookingItemData { Key = "booking-1", TimeStart = "08:00", @@ -48,7 +69,7 @@ public static class BookingItemCatalog EmployeeName = "Maria Hansen", Status = "completed" }, - ["booking-2"] = new BookingItemViewModel + ["booking-2"] = new BookingItemData { Key = "booking-2", TimeStart = "08:30", @@ -59,7 +80,7 @@ public static class BookingItemCatalog EmployeeName = "Anna Sørensen", Status = "completed" }, - ["booking-3"] = new BookingItemViewModel + ["booking-3"] = new BookingItemData { Key = "booking-3", TimeStart = "09:00", @@ -70,7 +91,7 @@ public static class BookingItemCatalog EmployeeName = "Peter Kristensen", Status = "completed" }, - ["booking-4"] = new BookingItemViewModel + ["booking-4"] = new BookingItemData { Key = "booking-4", TimeStart = "10:30", @@ -82,7 +103,7 @@ public static class BookingItemCatalog Status = "inprogress", IndicatorColor = "blue" }, - ["booking-5"] = new BookingItemViewModel + ["booking-5"] = new BookingItemData { Key = "booking-5", TimeStart = "10:00", @@ -94,7 +115,7 @@ public static class BookingItemCatalog Status = "inprogress", IndicatorColor = "purple" }, - ["booking-6"] = new BookingItemViewModel + ["booking-6"] = new BookingItemData { Key = "booking-6", TimeStart = "11:00", @@ -108,12 +129,28 @@ public static class BookingItemCatalog } }; - public static BookingItemViewModel Get(string key) + public static BookingItemViewModel Get(string key, ILocalizationService localization) { - if (Bookings.TryGetValue(key, out var booking)) - return booking; + if (!Bookings.TryGetValue(key, out var booking)) + throw new KeyNotFoundException($"BookingItem with key '{key}' not found"); - throw new KeyNotFoundException($"BookingItem with key '{key}' not found"); + var statusText = StatusKeys.TryGetValue(booking.Status, out var statusKey) + ? localization.Get(statusKey) + : booking.Status; + + return new BookingItemViewModel + { + Key = booking.Key, + TimeStart = booking.TimeStart, + TimeEnd = booking.TimeEnd, + Service = booking.Service, + CustomerName = booking.CustomerName, + EmployeeInitials = booking.EmployeeInitials, + EmployeeName = booking.EmployeeName, + Status = booking.Status, + StatusText = statusText, + IndicatorColor = booking.IndicatorColor + }; } public static IEnumerable AllKeys => Bookings.Keys; diff --git a/PlanTempus.Application/Features/Dashboard/Components/BookingList/BookingListViewComponent.cs b/PlanTempus.Application/Features/Dashboard/Components/BookingList/BookingListViewComponent.cs index 7c99560..2e6305a 100644 --- a/PlanTempus.Application/Features/Dashboard/Components/BookingList/BookingListViewComponent.cs +++ b/PlanTempus.Application/Features/Dashboard/Components/BookingList/BookingListViewComponent.cs @@ -1,12 +1,20 @@ using Microsoft.AspNetCore.Mvc; +using PlanTempus.Application.Features.Localization.Services; namespace PlanTempus.Application.Features.Dashboard.Components; public class BookingListViewComponent : ViewComponent { + private readonly ILocalizationService _localization; + + public BookingListViewComponent(ILocalizationService localization) + { + _localization = localization; + } + public IViewComponentResult Invoke(string key) { - var model = BookingListCatalog.Get(key); + var model = BookingListCatalog.Get(key, _localization); return View(model); } } @@ -19,24 +27,38 @@ public class BookingListViewModel public required IReadOnlyList BookingKeys { get; init; } } +internal class BookingListData +{ + public required string Key { get; init; } + public required string TitleKey { get; init; } + public required string CurrentTime { get; init; } + public required IReadOnlyList BookingKeys { get; init; } +} + public static class BookingListCatalog { - private static readonly Dictionary Lists = new() + private static readonly Dictionary Lists = new() { - ["todays-bookings"] = new BookingListViewModel + ["todays-bookings"] = new BookingListData { Key = "todays-bookings", - Title = "Dagens bookinger", + TitleKey = "dashboard.bookings.title", CurrentTime = "10:45", BookingKeys = ["booking-1", "booking-2", "booking-3", "booking-4", "booking-5", "booking-6"] } }; - public static BookingListViewModel Get(string key) + public static BookingListViewModel Get(string key, ILocalizationService localization) { - if (Lists.TryGetValue(key, out var list)) - return list; + if (!Lists.TryGetValue(key, out var list)) + throw new KeyNotFoundException($"BookingList with key '{key}' not found"); - throw new KeyNotFoundException($"BookingList with key '{key}' not found"); + return new BookingListViewModel + { + Key = list.Key, + Title = localization.Get(list.TitleKey), + CurrentTime = list.CurrentTime, + BookingKeys = list.BookingKeys + }; } } diff --git a/PlanTempus.Application/Features/Dashboard/Components/BookingList/Default.cshtml b/PlanTempus.Application/Features/Dashboard/Components/BookingList/Default.cshtml index 878b7fd..71acecf 100644 --- a/PlanTempus.Application/Features/Dashboard/Components/BookingList/Default.cshtml +++ b/PlanTempus.Application/Features/Dashboard/Components/BookingList/Default.cshtml @@ -6,11 +6,11 @@ @Model.Title - Se alle + Se alle - Nu: @Model.CurrentTime + Nu: @Model.CurrentTime diff --git a/PlanTempus.Application/Features/Dashboard/Components/NotificationList/NotificationListViewComponent.cs b/PlanTempus.Application/Features/Dashboard/Components/NotificationList/NotificationListViewComponent.cs index b2596f7..ba7912f 100644 --- a/PlanTempus.Application/Features/Dashboard/Components/NotificationList/NotificationListViewComponent.cs +++ b/PlanTempus.Application/Features/Dashboard/Components/NotificationList/NotificationListViewComponent.cs @@ -1,12 +1,20 @@ using Microsoft.AspNetCore.Mvc; +using PlanTempus.Application.Features.Localization.Services; namespace PlanTempus.Application.Features.Dashboard.Components; public class NotificationListViewComponent : ViewComponent { + private readonly ILocalizationService _localization; + + public NotificationListViewComponent(ILocalizationService localization) + { + _localization = localization; + } + public IViewComponentResult Invoke(string key) { - var model = NotificationListCatalog.Get(key); + var model = NotificationListCatalog.Get(key, _localization); return View(model); } } @@ -19,24 +27,38 @@ public class NotificationListViewModel public required IReadOnlyList NotificationKeys { get; init; } } +internal class NotificationListData +{ + public required string Key { get; init; } + public required string TitleKey { get; init; } + public required string ActionTextKey { get; init; } + public required IReadOnlyList NotificationKeys { get; init; } +} + public static class NotificationListCatalog { - private static readonly Dictionary Lists = new() + private static readonly Dictionary Lists = new() { - ["recent-notifications"] = new NotificationListViewModel + ["recent-notifications"] = new NotificationListData { Key = "recent-notifications", - Title = "Notifikationer", - ActionText = "Marker alle som læst", + TitleKey = "dashboard.notifications.title", + ActionTextKey = "dashboard.notifications.markAllRead", NotificationKeys = ["notif-1", "notif-2", "notif-3", "notif-4"] } }; - public static NotificationListViewModel Get(string key) + public static NotificationListViewModel Get(string key, ILocalizationService localization) { - if (Lists.TryGetValue(key, out var list)) - return list; + if (!Lists.TryGetValue(key, out var list)) + throw new KeyNotFoundException($"NotificationList with key '{key}' not found"); - throw new KeyNotFoundException($"NotificationList with key '{key}' not found"); + return new NotificationListViewModel + { + Key = list.Key, + Title = localization.Get(list.TitleKey), + ActionText = localization.Get(list.ActionTextKey), + NotificationKeys = list.NotificationKeys + }; } } diff --git a/PlanTempus.Application/Features/Dashboard/Components/QuickStat/QuickStatViewComponent.cs b/PlanTempus.Application/Features/Dashboard/Components/QuickStat/QuickStatViewComponent.cs index eb16392..4d8c0fd 100644 --- a/PlanTempus.Application/Features/Dashboard/Components/QuickStat/QuickStatViewComponent.cs +++ b/PlanTempus.Application/Features/Dashboard/Components/QuickStat/QuickStatViewComponent.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Mvc; +using PlanTempus.Application.Features.Localization.Services; namespace PlanTempus.Application.Features.Dashboard.Components; @@ -7,9 +8,16 @@ namespace PlanTempus.Application.Features.Dashboard.Components; /// public class QuickStatViewComponent : ViewComponent { + private readonly ILocalizationService _localization; + + public QuickStatViewComponent(ILocalizationService localization) + { + _localization = localization; + } + public IViewComponentResult Invoke(string key) { - var model = QuickStatCatalog.Get(key); + var model = QuickStatCatalog.Get(key, _localization); return View(model); } } @@ -24,45 +32,57 @@ public class QuickStatViewModel public required string Label { get; init; } } +internal class QuickStatData +{ + public required string Key { get; init; } + public required string Value { get; init; } + public required string LabelKey { get; init; } +} + /// /// Catalog of available quick stats with their data. /// public static class QuickStatCatalog { - private static readonly Dictionary Stats = new() + private static readonly Dictionary Stats = new() { - ["bookings-week"] = new QuickStatViewModel + ["bookings-week"] = new QuickStatData { Key = "bookings-week", Value = "47", - Label = "Bookinger" + LabelKey = "dashboard.quickStats.bookings" }, - ["revenue-week"] = new QuickStatViewModel + ["revenue-week"] = new QuickStatData { Key = "revenue-week", Value = "38.200 kr", - Label = "Omsætning" + LabelKey = "dashboard.quickStats.revenue" }, - ["new-customers"] = new QuickStatViewModel + ["new-customers"] = new QuickStatData { Key = "new-customers", Value = "8", - Label = "Nye kunder" + LabelKey = "dashboard.quickStats.newCustomers" }, - ["avg-occupancy"] = new QuickStatViewModel + ["avg-occupancy"] = new QuickStatData { Key = "avg-occupancy", Value = "72%", - Label = "Gns. belægning" + LabelKey = "dashboard.quickStats.avgOccupancy" } }; - public static QuickStatViewModel Get(string key) + public static QuickStatViewModel Get(string key, ILocalizationService localization) { - if (Stats.TryGetValue(key, out var stat)) - return stat; + if (!Stats.TryGetValue(key, out var stat)) + throw new KeyNotFoundException($"QuickStat with key '{key}' not found"); - throw new KeyNotFoundException($"QuickStat with key '{key}' not found"); + return new QuickStatViewModel + { + Key = stat.Key, + Value = stat.Value, + Label = localization.Get(stat.LabelKey) + }; } public static IEnumerable AllKeys => Stats.Keys; diff --git a/PlanTempus.Application/Features/Dashboard/Components/QuickStatList/QuickStatListViewComponent.cs b/PlanTempus.Application/Features/Dashboard/Components/QuickStatList/QuickStatListViewComponent.cs index cef1f5e..53e39bd 100644 --- a/PlanTempus.Application/Features/Dashboard/Components/QuickStatList/QuickStatListViewComponent.cs +++ b/PlanTempus.Application/Features/Dashboard/Components/QuickStatList/QuickStatListViewComponent.cs @@ -1,12 +1,20 @@ using Microsoft.AspNetCore.Mvc; +using PlanTempus.Application.Features.Localization.Services; namespace PlanTempus.Application.Features.Dashboard.Components; public class QuickStatListViewComponent : ViewComponent { + private readonly ILocalizationService _localization; + + public QuickStatListViewComponent(ILocalizationService localization) + { + _localization = localization; + } + public IViewComponentResult Invoke(string key) { - var model = QuickStatListCatalog.Get(key); + var model = QuickStatListCatalog.Get(key, _localization); return View(model); } } @@ -19,24 +27,38 @@ public class QuickStatListViewModel public required IReadOnlyList StatKeys { get; init; } } +internal class QuickStatListData +{ + public required string Key { get; init; } + public required string TitleKey { get; init; } + public required string Icon { get; init; } + public required IReadOnlyList StatKeys { get; init; } +} + public static class QuickStatListCatalog { - private static readonly Dictionary Lists = new() + private static readonly Dictionary Lists = new() { - ["this-week"] = new QuickStatListViewModel + ["this-week"] = new QuickStatListData { Key = "this-week", - Title = "Denne uge", + TitleKey = "dashboard.quickStats.title", Icon = "chart-line-up", StatKeys = ["bookings-week", "revenue-week", "new-customers", "avg-occupancy"] } }; - public static QuickStatListViewModel Get(string key) + public static QuickStatListViewModel Get(string key, ILocalizationService localization) { - if (Lists.TryGetValue(key, out var list)) - return list; + if (!Lists.TryGetValue(key, out var list)) + throw new KeyNotFoundException($"QuickStatList with key '{key}' not found"); - throw new KeyNotFoundException($"QuickStatList with key '{key}' not found"); + return new QuickStatListViewModel + { + Key = list.Key, + Title = localization.Get(list.TitleKey), + Icon = list.Icon, + StatKeys = list.StatKeys + }; } } diff --git a/PlanTempus.Application/Features/Dashboard/Components/StatCard/StatCardViewComponent.cs b/PlanTempus.Application/Features/Dashboard/Components/StatCard/StatCardViewComponent.cs index ccac82f..53583e5 100644 --- a/PlanTempus.Application/Features/Dashboard/Components/StatCard/StatCardViewComponent.cs +++ b/PlanTempus.Application/Features/Dashboard/Components/StatCard/StatCardViewComponent.cs @@ -1,4 +1,5 @@ using Microsoft.AspNetCore.Mvc; +using PlanTempus.Application.Features.Localization.Services; namespace PlanTempus.Application.Features.Dashboard.Components; @@ -7,9 +8,16 @@ namespace PlanTempus.Application.Features.Dashboard.Components; /// public class StatCardViewComponent : ViewComponent { + private readonly ILocalizationService _localization; + + public StatCardViewComponent(ILocalizationService localization) + { + _localization = localization; + } + public IViewComponentResult Invoke(string key) { - var model = StatCardCatalog.Get(key); + var model = StatCardCatalog.Get(key, _localization); return View(model); } } @@ -29,61 +37,84 @@ public class StatCardViewModel public bool HasTrend => !string.IsNullOrEmpty(TrendText); } +/// +/// Internal data for stat cards (uses localization keys). +/// +internal class StatCardData +{ + public required string Key { get; init; } + public required string Value { get; init; } + public required string LabelKey { get; init; } + public string? TrendTextKey { get; init; } + public string? TrendIcon { get; init; } + public string? TrendDirection { get; init; } + public string? Variant { get; init; } +} + /// /// Catalog of available stat cards with their data. /// public static class StatCardCatalog { - private static readonly Dictionary Cards = new() + private static readonly Dictionary Cards = new() { - ["bookings-today"] = new StatCardViewModel + ["bookings-today"] = new StatCardData { Key = "bookings-today", Value = "12", - Label = "Bookinger i dag", - TrendText = "4 gennemført, 2 i gang", + LabelKey = "dashboard.stats.bookingsToday", + TrendTextKey = "dashboard.stats.bookingsTrend", TrendIcon = "ph-check-circle", TrendDirection = "up", Variant = "highlight" }, - ["expected-revenue"] = new StatCardViewModel + ["expected-revenue"] = new StatCardData { Key = "expected-revenue", Value = "8.450 kr", - Label = "Forventet omsætning", - TrendText = "+12% vs. gennemsnit", + LabelKey = "dashboard.stats.expectedRevenue", + TrendTextKey = "dashboard.stats.revenueTrend", TrendIcon = "ph-trend-up", TrendDirection = "up", Variant = "success" }, - ["occupancy-rate"] = new StatCardViewModel + ["occupancy-rate"] = new StatCardData { Key = "occupancy-rate", Value = "78%", - Label = "Belægningsgrad", - TrendText = "God kapacitet", + LabelKey = "dashboard.stats.occupancyRate", + TrendTextKey = "dashboard.stats.occupancyTrend", TrendIcon = "ph-trend-up", TrendDirection = "up", Variant = null }, - ["needs-attention"] = new StatCardViewModel + ["needs-attention"] = new StatCardData { Key = "needs-attention", Value = "4", - Label = "Kræver opmærksomhed", - TrendText = null, + LabelKey = "dashboard.stats.needsAttention", + TrendTextKey = null, TrendIcon = null, TrendDirection = null, Variant = "warning" } }; - public static StatCardViewModel Get(string key) + public static StatCardViewModel Get(string key, ILocalizationService localization) { - if (Cards.TryGetValue(key, out var card)) - return card; + if (!Cards.TryGetValue(key, out var card)) + throw new KeyNotFoundException($"StatCard with key '{key}' not found"); - throw new KeyNotFoundException($"StatCard with key '{key}' not found"); + return new StatCardViewModel + { + Key = card.Key, + Value = card.Value, + Label = localization.Get(card.LabelKey), + TrendText = card.TrendTextKey != null ? localization.Get(card.TrendTextKey) : null, + TrendIcon = card.TrendIcon, + TrendDirection = card.TrendDirection, + Variant = card.Variant + }; } public static IEnumerable AllKeys => Cards.Keys; diff --git a/PlanTempus.Application/Features/Dashboard/Pages/Index.cshtml b/PlanTempus.Application/Features/Dashboard/Pages/Index.cshtml index 1a746d3..ad4c940 100644 --- a/PlanTempus.Application/Features/Dashboard/Pages/Index.cshtml +++ b/PlanTempus.Application/Features/Dashboard/Pages/Index.cshtml @@ -22,7 +22,7 @@ - AI Analyse + AI Analyse Godt i gang! 4 af 12 bookinger er gennemført. 2 er i gang nu, og 6 venter. @@ -51,17 +51,17 @@ - Hurtige handlinger + Hurtige handlinger - Ny booking + Ny booking - Ny kunde + Ny kunde diff --git a/PlanTempus.Application/Features/Localization/Translations/da.json b/PlanTempus.Application/Features/Localization/Translations/da.json index c569ae6..2589a9c 100644 --- a/PlanTempus.Application/Features/Localization/Translations/da.json +++ b/PlanTempus.Application/Features/Localization/Translations/da.json @@ -24,10 +24,197 @@ "close": "Luk", "delete": "Slet", "edit": "Rediger", - "add": "Tilføj" + "add": "Tilføj", + "from": "Fra", + "to": "Til", + "all": "Alle", + "reset": "Nulstil", + "status": "Status" }, "sidebar": { "lockScreen": "Lås skærm", "appName": "Salon OS" + }, + "dashboard": { + "title": "Dashboard", + "subtitle": "Overblik over dagens aktivitet", + "stats": { + "bookingsToday": "Bookinger i dag", + "bookingsTrend": "4 gennemført, 2 i gang", + "expectedRevenue": "Forventet omsætning", + "revenueTrend": "+12% vs. gennemsnit", + "occupancyRate": "Belægningsgrad", + "occupancyTrend": "God kapacitet", + "needsAttention": "Kræver handling" + }, + "ai": { + "header": "AI Analyse" + }, + "bookings": { + "title": "Dagens bookinger", + "viewAll": "Se alle", + "currentTime": "Nu:", + "status": { + "confirmed": "Bekræftet", + "pending": "Afventer", + "inProgress": "I gang", + "completed": "Gennemført" + } + }, + "notifications": { + "title": "Notifikationer", + "viewAll": "Se alle", + "markAllRead": "Marker alle som læst" + }, + "attentions": { + "title": "Kræver opmærksomhed", + "viewAll": "Se alle" + }, + "quickStats": { + "title": "Denne uge", + "bookings": "Bookinger", + "revenue": "Omsætning", + "newCustomers": "Nye kunder", + "cancellations": "Aflysninger", + "avgOccupancy": "Gns. belægning" + }, + "quickActions": { + "title": "Hurtige handlinger", + "newBooking": "Ny booking", + "newCustomer": "Ny kunde" + }, + "waitlist": { + "title": "Venteliste", + "count": "{count} venter" + } + }, + "cash": { + "title": "Kasse", + "tabs": { + "overview": "Oversigt", + "reconciliation": "Kasseafstemning" + }, + "stats": { + "reconciliationsInPeriod": "Afstemninger i periode", + "totalRevenue": "Total omsætning", + "cashSales": "Kontantsalg", + "totalDifference": "Samlet difference", + "transactionsToday": "Transaktioner i dag", + "revenueToday": "Omsætning i dag", + "lastReconciliation": "Sidste afstemning", + "openedRegister": "Åbnede kassen" + }, + "filter": { + "register": "Kassepunkt", + "approved": "Godkendt", + "draft": "Kladde" + }, + "table": { + "date": "Dato", + "id": "ID", + "period": "Periode", + "register": "Kassepunkt", + "closedBy": "Afsluttet af", + "revenue": "Omsætning", + "difference": "Difference", + "selected": "{count} valgt", + "noneSelected": "0 valgt", + "showingCount": "Viser {count} afstemninger", + "exportSaft": "Eksporter SAF-T", + "downloadCsv": "Download CSV", + "downloadPdf": "Download PDF", + "viewTransactions": "Se transaktioner" + }, + "revenue": { + "title": "Periodens omsætning", + "subtitle": "Systemtal vs. kontrol", + "cardPayments": "Kortbetalinger", + "mobilePay": "MobilePay / Online", + "cashSales": "Kontantsalg", + "hint": "Kort og MobilePay afstemmes mod bank/indløser. Kontanter tælles op nedenfor." + }, + "balance": { + "title": "Kontanter i kassen", + "startBalance": "Startbeholdning", + "startHint": "Overført fra sidste afstemning", + "payouts": "Udbetalinger / Bilag", + "payoutsHint": "Sammentæl bilag betalt kontant", + "toBank": "Udtaget til bank", + "toBankHint": "Kontanter lagt til side", + "expected": "Forventet kontantbeholdning", + "counted": "Optalt kontantbeholdning", + "countedHint": "Hvad ligger der faktisk i kassen?" + }, + "difference": { + "title": "Kassedifference", + "match": "Kassen stemmer", + "over": "Overskud", + "under": "Underskud" + }, + "period": { + "title": "Periodeoplysninger", + "dateRange": "Periode", + "register": "Kassepunkt", + "employee": "Medarbejder" + }, + "note": { + "title": "Note til afstemning", + "placeholder": "Beskriv evt. årsag til difference..." + }, + "approval": { + "title": "Afslut dagen", + "approvedBy": "Godkendt af (valgfrit)", + "selectPlaceholder": "Vælg...", + "confirmation": "Jeg bekræfter, at kassen er talt op, og at tallene er indtastet efter bedste evne.", + "saveDraft": "Gem som kladde", + "approve": "Godkend & lås" + }, + "status": { + "draft": "Kladde", + "approved": "Godkendt" + }, + "systemNote": "Systemet gemmer hvornår og af hvem der er godkendt – enkelt kontrolspor." + }, + "profile": { + "title": "Profil", + "myProfile": "Min profil", + "settings": "Indstillinger", + "darkMode": "Mørk tilstand", + "logout": "Log ud" + }, + "account": { + "title": "Abonnement & Konto", + "subtitle": "Administrer dit abonnement og betalingsinfo", + "subscription": { + "title": "Dit abonnement", + "currentPlan": "Nuværende plan", + "switchTo": "Skift til {plan}", + "contactSales": "Kontakt salg", + "contactUs": "Kontakt os", + "pricePerMonth": "kr/md" + }, + "billing": { + "title": "Betaling & Fakturaer" + }, + "payment": { + "frequency": "Betalingsfrekvens", + "monthly": "Månedlig", + "yearly": "Årlig", + "nextPayment": "Næste betaling", + "amount": "Beløb", + "cardExpiry": "Kortudløb", + "change": "Skift", + "switchToYearly": "Skift til årlig betaling (spar 15%)" + }, + "invoices": { + "title": "Faktura-historik", + "date": "Dato", + "invoiceNumber": "Fakturanr.", + "amount": "Beløb", + "download": "PDF", + "paid": "Betalt", + "pending": "Afventer", + "overdue": "Forfalden" + } } } diff --git a/PlanTempus.Application/Features/Localization/Translations/en.json b/PlanTempus.Application/Features/Localization/Translations/en.json index a66a7b7..9738bcc 100644 --- a/PlanTempus.Application/Features/Localization/Translations/en.json +++ b/PlanTempus.Application/Features/Localization/Translations/en.json @@ -24,10 +24,197 @@ "close": "Close", "delete": "Delete", "edit": "Edit", - "add": "Add" + "add": "Add", + "from": "From", + "to": "To", + "all": "All", + "reset": "Reset", + "status": "Status" }, "sidebar": { "lockScreen": "Lock screen", "appName": "Salon OS" + }, + "dashboard": { + "title": "Dashboard", + "subtitle": "Overview of today's activity", + "stats": { + "bookingsToday": "Bookings today", + "bookingsTrend": "4 completed, 2 in progress", + "expectedRevenue": "Expected revenue", + "revenueTrend": "+12% vs. average", + "occupancyRate": "Occupancy rate", + "occupancyTrend": "Good capacity", + "needsAttention": "Needs attention" + }, + "ai": { + "header": "AI Analysis" + }, + "bookings": { + "title": "Today's bookings", + "viewAll": "View all", + "currentTime": "Now:", + "status": { + "confirmed": "Confirmed", + "pending": "Pending", + "inProgress": "In progress", + "completed": "Completed" + } + }, + "notifications": { + "title": "Notifications", + "viewAll": "View all", + "markAllRead": "Mark all as read" + }, + "attentions": { + "title": "Needs attention", + "viewAll": "View all" + }, + "quickStats": { + "title": "This week", + "bookings": "Bookings", + "revenue": "Revenue", + "newCustomers": "New customers", + "cancellations": "Cancellations", + "avgOccupancy": "Avg. occupancy" + }, + "quickActions": { + "title": "Quick actions", + "newBooking": "New booking", + "newCustomer": "New customer" + }, + "waitlist": { + "title": "Waitlist", + "count": "{count} waiting" + } + }, + "cash": { + "title": "Cash Register", + "tabs": { + "overview": "Overview", + "reconciliation": "Reconciliation" + }, + "stats": { + "reconciliationsInPeriod": "Reconciliations in period", + "totalRevenue": "Total revenue", + "cashSales": "Cash sales", + "totalDifference": "Total difference", + "transactionsToday": "Transactions today", + "revenueToday": "Revenue today", + "lastReconciliation": "Last reconciliation", + "openedRegister": "Opened register" + }, + "filter": { + "register": "Register", + "approved": "Approved", + "draft": "Draft" + }, + "table": { + "date": "Date", + "id": "ID", + "period": "Period", + "register": "Register", + "closedBy": "Closed by", + "revenue": "Revenue", + "difference": "Difference", + "selected": "{count} selected", + "noneSelected": "0 selected", + "showingCount": "Showing {count} reconciliations", + "exportSaft": "Export SAF-T", + "downloadCsv": "Download CSV", + "downloadPdf": "Download PDF", + "viewTransactions": "View transactions" + }, + "revenue": { + "title": "Period revenue", + "subtitle": "System vs. control", + "cardPayments": "Card payments", + "mobilePay": "MobilePay / Online", + "cashSales": "Cash sales", + "hint": "Card and MobilePay are reconciled against bank/acquirer. Cash is counted below." + }, + "balance": { + "title": "Cash in register", + "startBalance": "Starting balance", + "startHint": "Carried over from last reconciliation", + "payouts": "Payouts / Receipts", + "payoutsHint": "Total receipts paid in cash", + "toBank": "Withdrawn to bank", + "toBankHint": "Cash set aside", + "expected": "Expected cash balance", + "counted": "Counted cash balance", + "countedHint": "What is actually in the register?" + }, + "difference": { + "title": "Cash difference", + "match": "Register balanced", + "over": "Overage", + "under": "Shortage" + }, + "period": { + "title": "Period information", + "dateRange": "Period", + "register": "Register", + "employee": "Employee" + }, + "note": { + "title": "Reconciliation note", + "placeholder": "Describe reason for difference..." + }, + "approval": { + "title": "Close the day", + "approvedBy": "Approved by (optional)", + "selectPlaceholder": "Select...", + "confirmation": "I confirm that the register has been counted and the figures have been entered to the best of my ability.", + "saveDraft": "Save as draft", + "approve": "Approve & lock" + }, + "status": { + "draft": "Draft", + "approved": "Approved" + }, + "systemNote": "The system records when and by whom approval was made – simple audit trail." + }, + "profile": { + "title": "Profile", + "myProfile": "My profile", + "settings": "Settings", + "darkMode": "Dark mode", + "logout": "Log out" + }, + "account": { + "title": "Subscription & Account", + "subtitle": "Manage your subscription and payment info", + "subscription": { + "title": "Your subscription", + "currentPlan": "Current plan", + "switchTo": "Switch to {plan}", + "contactSales": "Contact sales", + "contactUs": "Contact us", + "pricePerMonth": "/mo" + }, + "billing": { + "title": "Payment & Invoices" + }, + "payment": { + "frequency": "Payment frequency", + "monthly": "Monthly", + "yearly": "Yearly", + "nextPayment": "Next payment", + "amount": "Amount", + "cardExpiry": "Card expiry", + "change": "Change", + "switchToYearly": "Switch to yearly billing (save 15%)" + }, + "invoices": { + "title": "Invoice history", + "date": "Date", + "invoiceNumber": "Invoice no.", + "amount": "Amount", + "download": "PDF", + "paid": "Paid", + "pending": "Pending", + "overdue": "Overdue" + } } } diff --git a/PlanTempus.Application/Features/Shared/_WaitlistDrawer.cshtml b/PlanTempus.Application/Features/Shared/_WaitlistDrawer.cshtml index bee4ac3..2ffc20c 100644 --- a/PlanTempus.Application/Features/Shared/_WaitlistDrawer.cshtml +++ b/PlanTempus.Application/Features/Shared/_WaitlistDrawer.cshtml @@ -3,7 +3,7 @@ - Venteliste (@WaitlistItemCatalog.AllKeys.Count()) + Venteliste (@WaitlistItemCatalog.AllKeys.Count()) diff --git a/PlanTempus.Application/Features/_Shared/Pages/_ProfileDrawer.cshtml b/PlanTempus.Application/Features/_Shared/Pages/_ProfileDrawer.cshtml index 9e42620..68f1020 100644 --- a/PlanTempus.Application/Features/_Shared/Pages/_ProfileDrawer.cshtml +++ b/PlanTempus.Application/Features/_Shared/Pages/_ProfileDrawer.cshtml @@ -1,6 +1,6 @@ - Profil + Profil @@ -18,11 +18,11 @@ - Min profil + Min profil - Indstillinger + Indstillinger @@ -31,7 +31,7 @@ - Mørk tilstand + Mørk tilstand @@ -43,7 +43,7 @@ - Log ud + Log ud diff --git a/PlanTempus.Application/wwwroot/css/account.css b/PlanTempus.Application/wwwroot/css/account.css index 908ab38..980e89c 100644 --- a/PlanTempus.Application/wwwroot/css/account.css +++ b/PlanTempus.Application/wwwroot/css/account.css @@ -10,14 +10,14 @@ =========================================== */ swp-account-section { display: block; - margin-bottom: var(--spacing-8); + margin-bottom: var(--section-gap); } swp-account-section-header { display: flex; align-items: center; justify-content: space-between; - margin-bottom: var(--spacing-4); + margin-bottom: var(--section-gap); } swp-account-section-title { @@ -40,7 +40,7 @@ swp-account-section-title i { swp-plan-grid { display: grid; grid-template-columns: repeat(3, 1fr); - gap: var(--spacing-5); + gap: var(--card-gap); } @media (max-width: 1024px) { @@ -82,7 +82,7 @@ swp-plan-badge.popular { /* Plan action buttons */ swp-plan-action { margin-top: auto; - padding-top: var(--spacing-5); + padding-top: var(--card-padding); } swp-plan-action swp-btn { @@ -96,7 +96,7 @@ swp-plan-action swp-btn { swp-billing-grid { display: grid; grid-template-columns: 380px 1fr; - gap: var(--spacing-6); + gap: var(--card-gap); } @media (max-width: 1024px) { @@ -111,18 +111,18 @@ swp-billing-grid { swp-payment-card { display: flex; flex-direction: column; - gap: var(--spacing-5); + gap: var(--card-padding); background: var(--color-surface); border-radius: var(--radius-lg); box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); - padding: var(--spacing-6); + padding: var(--card-padding); } swp-payment-method { display: flex; align-items: center; gap: var(--spacing-4); - padding: var(--spacing-4); + padding: var(--card-padding); background: var(--color-background-alt); border-radius: var(--radius-md); } @@ -168,7 +168,7 @@ swp-payment-detail { display: flex; justify-content: space-between; align-items: center; - padding: var(--spacing-3) 0; + padding: var(--spacing-4) 0; border-bottom: 1px solid var(--color-border); } @@ -206,7 +206,7 @@ swp-invoices-header { display: flex; align-items: center; justify-content: space-between; - padding: var(--spacing-4) var(--spacing-5); + padding: var(--card-padding); border-bottom: 1px solid var(--color-border); } @@ -255,17 +255,17 @@ swp-invoice-row:last-child { } swp-invoice-cell { - padding: var(--spacing-3) var(--spacing-4); + padding: var(--spacing-4) var(--spacing-4); font-size: var(--font-size-sm); color: var(--color-text); } swp-invoice-cell:first-child { - padding-left: var(--spacing-5); + padding-left: var(--card-padding); } swp-invoice-cell:last-child { - padding-right: var(--spacing-5); + padding-right: var(--card-padding); } /* Header cells */ @@ -275,8 +275,8 @@ swp-invoice-table-header swp-invoice-cell { text-transform: uppercase; letter-spacing: 0.5px; color: var(--color-text-secondary); - padding-top: var(--spacing-3); - padding-bottom: var(--spacing-3); + padding-top: var(--spacing-4); + padding-bottom: var(--spacing-4); } /* Invoice number mono font */ @@ -384,18 +384,18 @@ swp-btn.sm i { } swp-invoice-cell { - padding: var(--spacing-2) var(--spacing-3); + padding: var(--spacing-3) var(--spacing-3); } swp-invoice-cell:first-child { - padding-left: var(--spacing-4); + padding-left: var(--spacing-5); } swp-invoice-cell:last-child { - padding-right: var(--spacing-4); + padding-right: var(--spacing-5); } swp-payment-card { - padding: var(--spacing-4); + padding: var(--spacing-5); } } diff --git a/PlanTempus.Application/wwwroot/css/attentions.css b/PlanTempus.Application/wwwroot/css/attentions.css index 47d4738..b51693a 100644 --- a/PlanTempus.Application/wwwroot/css/attentions.css +++ b/PlanTempus.Application/wwwroot/css/attentions.css @@ -20,7 +20,7 @@ swp-attention-item { grid-template-columns: subgrid; align-items: center; gap: var(--spacing-8); - padding: var(--spacing-5) var(--spacing-6); + padding: var(--card-padding); background: var(--color-background-alt); border-radius: var(--radius-xl); border-left: 3px solid var(--color-border); diff --git a/PlanTempus.Application/wwwroot/css/bookings.css b/PlanTempus.Application/wwwroot/css/bookings.css index b04e98e..5c867f2 100644 --- a/PlanTempus.Application/wwwroot/css/bookings.css +++ b/PlanTempus.Application/wwwroot/css/bookings.css @@ -19,7 +19,7 @@ swp-booking-item { grid-column: 1 / -1; grid-template-columns: subgrid; align-items: center; - padding: var(--spacing-4); + padding: var(--card-padding); background: var(--color-background-alt); border-radius: var(--radius-lg); cursor: pointer; diff --git a/PlanTempus.Application/wwwroot/css/design-tokens.css b/PlanTempus.Application/wwwroot/css/design-tokens.css index 6718a60..8b3ff94 100644 --- a/PlanTempus.Application/wwwroot/css/design-tokens.css +++ b/PlanTempus.Application/wwwroot/css/design-tokens.css @@ -190,7 +190,10 @@ --container-max-width-lg: 1400px; /* -------- Card Spacing -------- */ - --card-body-padding: var(--spacing-5); + --card-padding: 12px; + --card-gap: 24px; + --section-gap: 24px; + --page-padding: 24px; /* -------- Calendar Grid -------- */ --hour-height: 64px; diff --git a/PlanTempus.Application/wwwroot/css/notifications.css b/PlanTempus.Application/wwwroot/css/notifications.css index 1f589f9..31fc996 100644 --- a/PlanTempus.Application/wwwroot/css/notifications.css +++ b/PlanTempus.Application/wwwroot/css/notifications.css @@ -19,7 +19,7 @@ swp-notification-item { grid-column: 1 / -1; grid-template-columns: subgrid; align-items: center; - padding: var(--spacing-5) var(--spacing-6); + padding: var(--card-padding); background: var(--color-background-alt); border-radius: var(--radius-xl); cursor: pointer; diff --git a/PlanTempus.Application/wwwroot/css/page.css b/PlanTempus.Application/wwwroot/css/page.css index c8263aa..3afe199 100644 --- a/PlanTempus.Application/wwwroot/css/page.css +++ b/PlanTempus.Application/wwwroot/css/page.css @@ -11,7 +11,7 @@ swp-page-container { display: block; max-width: var(--page-max-width); margin: 0 auto; - padding: var(--spacing-6); + padding: var(--page-padding); } /* =========================================== @@ -21,7 +21,7 @@ swp-page-header { display: flex; align-items: flex-start; justify-content: space-between; - margin-bottom: var(--spacing-6); + margin-bottom: var(--section-gap); } swp-page-title h1 { @@ -49,16 +49,15 @@ swp-card { background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--border-radius-lg); - padding: var(--spacing-5); - margin-bottom: var(--spacing-5); + padding: var(--card-padding); } swp-card-header { display: flex; align-items: center; justify-content: space-between; - margin: calc(-1 * var(--spacing-5)) calc(-1 * var(--spacing-5)) var(--spacing-4) calc(-1 * var(--spacing-5)); - padding: var(--spacing-4) var(--spacing-5); + margin: calc(-1 * var(--card-padding)) calc(-1 * var(--card-padding)) var(--spacing-5) calc(-1 * var(--card-padding)); + padding: var(--spacing-4) var(--card-padding); border-bottom: 1px solid var(--color-border); } @@ -96,7 +95,7 @@ swp-card-content:has(> swp-booking-list), swp-card-content:has(> swp-notification-list), swp-card-content:has(> swp-attention-list) { display: grid; - gap: var(--spacing-4); + gap: var(--spacing-5); } swp-card-content:has(> swp-booking-list) { @@ -134,13 +133,13 @@ swp-item-desc { swp-dashboard-grid { display: grid; grid-template-columns: 1fr 380px; - gap: var(--spacing-5); + gap: var(--card-gap); } swp-main-column, swp-side-column { display: grid; - gap: var(--spacing-5); + gap: var(--card-gap); align-content: start; } @@ -149,7 +148,7 @@ swp-side-column { =========================================== */ swp-ai-insight { display: block; - padding: var(--spacing-4) var(--spacing-5); + padding: var(--card-padding); background: linear-gradient(135deg, color-mix(in srgb, var(--color-purple) 8%, transparent), color-mix(in srgb, var(--color-teal) 8%, transparent) @@ -183,14 +182,14 @@ swp-ai-text { swp-quick-actions { display: flex; flex-direction: column; - gap: var(--spacing-2); + gap: var(--spacing-3); } swp-quick-action-btn { display: flex; align-items: center; - gap: var(--spacing-2); - padding: var(--spacing-3); + gap: var(--spacing-3); + padding: var(--card-padding); font-size: var(--font-size-base); font-family: var(--font-family); color: var(--color-text); @@ -226,7 +225,7 @@ swp-quick-action-btn i { @media (max-width: 768px) { swp-page-container { - padding: var(--spacing-4); + padding: var(--spacing-5); } swp-page-header { diff --git a/PlanTempus.Application/wwwroot/css/quick-stats.css b/PlanTempus.Application/wwwroot/css/quick-stats.css index ec1f626..b077e7d 100644 --- a/PlanTempus.Application/wwwroot/css/quick-stats.css +++ b/PlanTempus.Application/wwwroot/css/quick-stats.css @@ -10,7 +10,7 @@ swp-quick-stats { display: grid; grid-template-columns: repeat(2, 1fr); - gap: var(--spacing-3); + gap: var(--card-gap); } /* =========================================== @@ -19,8 +19,8 @@ swp-quick-stats { swp-quick-stat { display: flex; flex-direction: column; - gap: var(--spacing-1); - padding: var(--spacing-3); + gap: var(--spacing-2); + padding: var(--card-padding); background: var(--color-background-alt); border-radius: var(--radius-md); } diff --git a/PlanTempus.Application/wwwroot/css/stats.css b/PlanTempus.Application/wwwroot/css/stats.css index dd49a10..bf79a14 100644 --- a/PlanTempus.Application/wwwroot/css/stats.css +++ b/PlanTempus.Application/wwwroot/css/stats.css @@ -11,15 +11,15 @@ swp-stats-bar, swp-stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); - gap: var(--spacing-4); - margin-bottom: var(--spacing-5); + gap: var(--card-gap); + margin-bottom: var(--section-gap); } swp-stats-row { display: grid; grid-template-columns: repeat(3, 1fr); - gap: var(--spacing-4); - margin-bottom: var(--spacing-5); + gap: var(--card-gap); + margin-bottom: var(--section-gap); } /* =========================================== @@ -30,15 +30,15 @@ swp-stat-card { flex-direction: column; background: var(--color-surface); border-radius: var(--border-radius-lg); - padding: var(--spacing-4) var(--spacing-5); + padding: var(--card-padding); border: 1px solid var(--color-border); } swp-stat-box { display: flex; flex-direction: column; - gap: var(--spacing-1); - padding: var(--spacing-4); + gap: var(--spacing-2); + padding: var(--card-padding); background: var(--color-surface); border-radius: var(--border-radius); border: 1px solid var(--color-border); @@ -72,7 +72,7 @@ swp-stat-label { display: block; font-size: var(--font-size-sm); color: var(--color-text-secondary); - margin-top: var(--spacing-1); + margin-top: var(--spacing-2); } swp-stat-box swp-stat-label { diff --git a/PlanTempus.Application/wwwroot/css/waitlist.css b/PlanTempus.Application/wwwroot/css/waitlist.css index 8500654..792f774 100644 --- a/PlanTempus.Application/wwwroot/css/waitlist.css +++ b/PlanTempus.Application/wwwroot/css/waitlist.css @@ -10,8 +10,8 @@ swp-waitlist-card { display: flex; align-items: center; - gap: var(--spacing-3); - padding: var(--spacing-4); + gap: var(--spacing-4); + padding: var(--card-padding); background: var(--color-surface); border: 1px solid var(--color-border); border-radius: var(--radius-lg);