From abcf8ee75e1fc71f900cefbb5f0fffe4302cbbc5 Mon Sep 17 00:00:00 2001 From: "Janus C. H. Knudsen" Date: Sun, 11 Jan 2026 13:11:55 +0100 Subject: [PATCH] Adds dashboard booking and notification components Introduces reusable view components for bookings and notifications Implements dynamic rendering of booking items and lists Adds corresponding CSS styles for new dashboard components Enhances dashboard user interface with interactive elements --- .workbench/image.png | Bin 0 -> 41466 bytes .../Components/BookingItem/Default.cshtml | 17 ++ .../Components/BookingItemViewComponent.cs | 120 +++++++++++ .../Components/BookingList/Default.cshtml | 23 +++ .../Components/BookingListViewComponent.cs | 42 ++++ .../NotificationItem/Default.cshtml | 13 ++ .../NotificationItemViewComponent.cs | 75 +++++++ .../NotificationList/Default.cshtml | 19 ++ .../NotificationListViewComponent.cs | 42 ++++ .../Features/Dashboard/Pages/Index.cshtml | 18 +- .../Features/_Shared/Pages/_Layout.cshtml | 2 + .../wwwroot/css/bookings.css | 194 ++++++++++++++++++ .../wwwroot/css/design-tokens.css | 3 + .../wwwroot/css/notifications.css | 92 +++++++++ PlanTempus.Application/wwwroot/css/page.css | 2 +- 15 files changed, 648 insertions(+), 14 deletions(-) create mode 100644 .workbench/image.png create mode 100644 PlanTempus.Application/Features/Dashboard/Components/BookingItem/Default.cshtml create mode 100644 PlanTempus.Application/Features/Dashboard/Components/BookingItemViewComponent.cs create mode 100644 PlanTempus.Application/Features/Dashboard/Components/BookingList/Default.cshtml create mode 100644 PlanTempus.Application/Features/Dashboard/Components/BookingListViewComponent.cs create mode 100644 PlanTempus.Application/Features/Dashboard/Components/NotificationItem/Default.cshtml create mode 100644 PlanTempus.Application/Features/Dashboard/Components/NotificationItemViewComponent.cs create mode 100644 PlanTempus.Application/Features/Dashboard/Components/NotificationList/Default.cshtml create mode 100644 PlanTempus.Application/Features/Dashboard/Components/NotificationListViewComponent.cs create mode 100644 PlanTempus.Application/wwwroot/css/bookings.css create mode 100644 PlanTempus.Application/wwwroot/css/notifications.css diff --git a/.workbench/image.png b/.workbench/image.png new file mode 100644 index 0000000000000000000000000000000000000000..e1594c1f301653a77439bd5ecbd8cc50cb39e0ce GIT binary patch literal 41466 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 + + @Model.TimeStart + @Model.TimeEnd + + + + @Model.Service + @Model.CustomerName + + + @Model.EmployeeInitials + + @Model.StatusText + diff --git a/PlanTempus.Application/Features/Dashboard/Components/BookingItemViewComponent.cs b/PlanTempus.Application/Features/Dashboard/Components/BookingItemViewComponent.cs new file mode 100644 index 0000000..4e172f3 --- /dev/null +++ b/PlanTempus.Application/Features/Dashboard/Components/BookingItemViewComponent.cs @@ -0,0 +1,120 @@ +using Microsoft.AspNetCore.Mvc; + +namespace PlanTempus.Application.Features.Dashboard.Components; + +public class BookingItemViewComponent : ViewComponent +{ + public IViewComponentResult Invoke(string key) + { + var model = BookingItemCatalog.Get(key); + return View(model); + } +} + +public class BookingItemViewModel +{ + 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 string StatusText => Status switch + { + "completed" => "Gennemført", + "inprogress" => "I gang", + "confirmed" => "Bekræftet", + "pending" => "Afventer", + _ => Status + }; +} + +public static class BookingItemCatalog +{ + private static readonly Dictionary Bookings = new() + { + ["booking-1"] = new BookingItemViewModel + { + Key = "booking-1", + TimeStart = "08:00", + TimeEnd = "08:30", + Service = "Herreklip", + CustomerName = "Thomas Berg", + EmployeeInitials = "MH", + EmployeeName = "Maria Hansen", + Status = "completed" + }, + ["booking-2"] = new BookingItemViewModel + { + Key = "booking-2", + TimeStart = "08:30", + TimeEnd = "09:00", + Service = "Dameklip", + CustomerName = "Katrine Holm", + EmployeeInitials = "AS", + EmployeeName = "Anna Sørensen", + Status = "completed" + }, + ["booking-3"] = new BookingItemViewModel + { + Key = "booking-3", + TimeStart = "09:00", + TimeEnd = "09:30", + Service = "Skægtrimning", + CustomerName = "Mikkel Skov", + EmployeeInitials = "PK", + EmployeeName = "Peter Kristensen", + Status = "completed" + }, + ["booking-4"] = new BookingItemViewModel + { + Key = "booking-4", + TimeStart = "10:30", + TimeEnd = "11:00", + Service = "Herreklip", + CustomerName = "Jonas Petersen", + EmployeeInitials = "MH", + EmployeeName = "Maria Hansen", + Status = "inprogress", + IndicatorColor = "blue" + }, + ["booking-5"] = new BookingItemViewModel + { + Key = "booking-5", + TimeStart = "10:00", + TimeEnd = "11:00", + Service = "Føn + Styling", + CustomerName = "Rikke Dam", + EmployeeInitials = "LJ", + EmployeeName = "Louise Jensen", + Status = "inprogress", + IndicatorColor = "purple" + }, + ["booking-6"] = new BookingItemViewModel + { + Key = "booking-6", + TimeStart = "11:00", + TimeEnd = "12:00", + Service = "Balayage", + CustomerName = "Emma Christensen", + EmployeeInitials = "AS", + EmployeeName = "Anna Sørensen", + Status = "confirmed", + IndicatorColor = "teal" + } + }; + + public static BookingItemViewModel Get(string key) + { + if (Bookings.TryGetValue(key, out var booking)) + return booking; + + throw new KeyNotFoundException($"BookingItem with key '{key}' not found"); + } + + public static IEnumerable AllKeys => Bookings.Keys; +} diff --git a/PlanTempus.Application/Features/Dashboard/Components/BookingList/Default.cshtml b/PlanTempus.Application/Features/Dashboard/Components/BookingList/Default.cshtml new file mode 100644 index 0000000..878b7fd --- /dev/null +++ b/PlanTempus.Application/Features/Dashboard/Components/BookingList/Default.cshtml @@ -0,0 +1,23 @@ +@model PlanTempus.Application.Features.Dashboard.Components.BookingListViewModel + + + + + + @Model.Title + + Se alle + + + + Nu: @Model.CurrentTime + + + + @foreach (var bookingKey in Model.BookingKeys) + { + @await Component.InvokeAsync("BookingItem", bookingKey) + } + + + diff --git a/PlanTempus.Application/Features/Dashboard/Components/BookingListViewComponent.cs b/PlanTempus.Application/Features/Dashboard/Components/BookingListViewComponent.cs new file mode 100644 index 0000000..7c99560 --- /dev/null +++ b/PlanTempus.Application/Features/Dashboard/Components/BookingListViewComponent.cs @@ -0,0 +1,42 @@ +using Microsoft.AspNetCore.Mvc; + +namespace PlanTempus.Application.Features.Dashboard.Components; + +public class BookingListViewComponent : ViewComponent +{ + public IViewComponentResult Invoke(string key) + { + var model = BookingListCatalog.Get(key); + return View(model); + } +} + +public class BookingListViewModel +{ + public required string Key { get; init; } + public required string Title { get; init; } + public required string CurrentTime { get; init; } + public required IReadOnlyList BookingKeys { get; init; } +} + +public static class BookingListCatalog +{ + private static readonly Dictionary Lists = new() + { + ["todays-bookings"] = new BookingListViewModel + { + Key = "todays-bookings", + Title = "Dagens bookinger", + CurrentTime = "10:45", + BookingKeys = ["booking-1", "booking-2", "booking-3", "booking-4", "booking-5", "booking-6"] + } + }; + + public static BookingListViewModel Get(string key) + { + if (Lists.TryGetValue(key, out var list)) + return list; + + throw new KeyNotFoundException($"BookingList with key '{key}' not found"); + } +} diff --git a/PlanTempus.Application/Features/Dashboard/Components/NotificationItem/Default.cshtml b/PlanTempus.Application/Features/Dashboard/Components/NotificationItem/Default.cshtml new file mode 100644 index 0000000..6372dfd --- /dev/null +++ b/PlanTempus.Application/Features/Dashboard/Components/NotificationItem/Default.cshtml @@ -0,0 +1,13 @@ +@model PlanTempus.Application.Features.Dashboard.Components.NotificationItemViewModel + + + + + + + + @Model.Title @Model.Text + + @Model.Time + + diff --git a/PlanTempus.Application/Features/Dashboard/Components/NotificationItemViewComponent.cs b/PlanTempus.Application/Features/Dashboard/Components/NotificationItemViewComponent.cs new file mode 100644 index 0000000..5372aa9 --- /dev/null +++ b/PlanTempus.Application/Features/Dashboard/Components/NotificationItemViewComponent.cs @@ -0,0 +1,75 @@ +using Microsoft.AspNetCore.Mvc; + +namespace PlanTempus.Application.Features.Dashboard.Components; + +public class NotificationItemViewComponent : ViewComponent +{ + public IViewComponentResult Invoke(string key) + { + var model = NotificationItemCatalog.Get(key); + return View(model); + } +} + +public class NotificationItemViewModel +{ + public required string Key { get; init; } + public required string Icon { get; init; } + public required string Title { get; init; } + public required string Text { get; init; } + public required string Time { get; init; } + public bool IsUnread { get; init; } +} + +public static class NotificationItemCatalog +{ + private static readonly Dictionary Notifications = new() + { + ["notif-1"] = new NotificationItemViewModel + { + Key = "notif-1", + Icon = "calendar-plus", + Title = "Ny booking", + Text = "fra Emma Christensen til Balayage", + Time = "For 15 min. siden", + IsUnread = true + }, + ["notif-2"] = new NotificationItemViewModel + { + Key = "notif-2", + Icon = "star", + Title = "Ny anmeldelse", + Text = "5 stjerner fra Sofie Nielsen", + Time = "For 1 time siden", + IsUnread = true + }, + ["notif-3"] = new NotificationItemViewModel + { + Key = "notif-3", + Icon = "x", + Title = "Aflysning", + Text = "Mette Hansen aflyste kl. 15:00", + Time = "For 2 timer siden", + IsUnread = false + }, + ["notif-4"] = new NotificationItemViewModel + { + Key = "notif-4", + Icon = "check", + Title = "Bekræftet", + Text = "Louise Andersen bekræftede kl. 13:00", + Time = "I går kl. 18:30", + IsUnread = false + } + }; + + public static NotificationItemViewModel Get(string key) + { + if (Notifications.TryGetValue(key, out var notification)) + return notification; + + throw new KeyNotFoundException($"NotificationItem with key '{key}' not found"); + } + + public static IEnumerable AllKeys => Notifications.Keys; +} diff --git a/PlanTempus.Application/Features/Dashboard/Components/NotificationList/Default.cshtml b/PlanTempus.Application/Features/Dashboard/Components/NotificationList/Default.cshtml new file mode 100644 index 0000000..9b5b5ab --- /dev/null +++ b/PlanTempus.Application/Features/Dashboard/Components/NotificationList/Default.cshtml @@ -0,0 +1,19 @@ +@model PlanTempus.Application.Features.Dashboard.Components.NotificationListViewModel + + + + + + @Model.Title + + @Model.ActionText + + + + @foreach (var notificationKey in Model.NotificationKeys) + { + @await Component.InvokeAsync("NotificationItem", notificationKey) + } + + + diff --git a/PlanTempus.Application/Features/Dashboard/Components/NotificationListViewComponent.cs b/PlanTempus.Application/Features/Dashboard/Components/NotificationListViewComponent.cs new file mode 100644 index 0000000..b2596f7 --- /dev/null +++ b/PlanTempus.Application/Features/Dashboard/Components/NotificationListViewComponent.cs @@ -0,0 +1,42 @@ +using Microsoft.AspNetCore.Mvc; + +namespace PlanTempus.Application.Features.Dashboard.Components; + +public class NotificationListViewComponent : ViewComponent +{ + public IViewComponentResult Invoke(string key) + { + var model = NotificationListCatalog.Get(key); + return View(model); + } +} + +public class NotificationListViewModel +{ + public required string Key { get; init; } + public required string Title { get; init; } + public required string ActionText { get; init; } + public required IReadOnlyList NotificationKeys { get; init; } +} + +public static class NotificationListCatalog +{ + private static readonly Dictionary Lists = new() + { + ["recent-notifications"] = new NotificationListViewModel + { + Key = "recent-notifications", + Title = "Notifikationer", + ActionText = "Marker alle som læst", + NotificationKeys = ["notif-1", "notif-2", "notif-3", "notif-4"] + } + }; + + public static NotificationListViewModel Get(string key) + { + if (Lists.TryGetValue(key, out var list)) + return list; + + throw new KeyNotFoundException($"NotificationList with key '{key}' not found"); + } +} diff --git a/PlanTempus.Application/Features/Dashboard/Pages/Index.cshtml b/PlanTempus.Application/Features/Dashboard/Pages/Index.cshtml index 92c24f0..5c78b61 100644 --- a/PlanTempus.Application/Features/Dashboard/Pages/Index.cshtml +++ b/PlanTempus.Application/Features/Dashboard/Pages/Index.cshtml @@ -31,22 +31,14 @@ - - - - - - Dagens bookinger - - Se alle - - -

Booking oversigt kommer her...

-
-
+ + @await Component.InvokeAsync("BookingList", "todays-bookings") + + @await Component.InvokeAsync("NotificationList", "recent-notifications") + diff --git a/PlanTempus.Application/Features/_Shared/Pages/_Layout.cshtml b/PlanTempus.Application/Features/_Shared/Pages/_Layout.cshtml index bc1b7d0..36be511 100644 --- a/PlanTempus.Application/Features/_Shared/Pages/_Layout.cshtml +++ b/PlanTempus.Application/Features/_Shared/Pages/_Layout.cshtml @@ -18,6 +18,8 @@ + + @await RenderSectionAsync("Styles", required: false) diff --git a/PlanTempus.Application/wwwroot/css/bookings.css b/PlanTempus.Application/wwwroot/css/bookings.css new file mode 100644 index 0000000..695efd9 --- /dev/null +++ b/PlanTempus.Application/wwwroot/css/bookings.css @@ -0,0 +1,194 @@ +/** + * Bookings - Booking List & Items + * + * Styling for booking components on dashboard + */ + +/* =========================================== + BOOKING LIST + =========================================== */ +swp-booking-list { + display: grid; + grid-template-columns: 50px 4px 1fr auto auto; + gap: var(--spacing-4); + padding: 0 var(--card-body-padding); +} + +/* =========================================== + BOOKING ITEM + =========================================== */ +swp-booking-item { + display: grid; + grid-column: 1 / -1; + grid-template-columns: subgrid; + align-items: center; + padding: var(--spacing-4); + background: var(--color-background-alt); + border-radius: var(--radius-lg); + cursor: pointer; + transition: background var(--transition-fast); +} + +swp-booking-item:hover { + background: var(--color-background-hover); +} + +swp-booking-item.completed { + opacity: 0.6; +} + +swp-booking-item.completed swp-booking-indicator { + background: var(--color-border); +} + +swp-booking-item.inprogress { + background: color-mix(in srgb, var(--color-teal) 8%, var(--color-background-alt)); +} + +/* =========================================== + BOOKING TIME + =========================================== */ +swp-booking-time { + display: flex; + flex-direction: column; + align-items: flex-start; +} + +swp-booking-time swp-time-start { + font-size: var(--font-size-base); + font-weight: var(--font-weight-semibold); + font-family: var(--font-mono); + color: var(--color-text); +} + +swp-booking-time swp-time-end { + font-size: var(--font-size-xs); + color: var(--color-text-secondary); + font-family: var(--font-mono); +} + +/* =========================================== + BOOKING INDICATOR + =========================================== */ +swp-booking-indicator { + width: 4px; + height: 40px; + border-radius: 2px; +} + +swp-booking-indicator.teal { background: var(--color-teal); } +swp-booking-indicator.blue { background: var(--color-blue); } +swp-booking-indicator.purple { background: var(--color-purple); } +swp-booking-indicator.amber { background: var(--color-amber); } +swp-booking-indicator.green { background: var(--color-green); } + +/* =========================================== + BOOKING DETAILS + =========================================== */ +swp-booking-details { + min-width: 0; + overflow: hidden; +} + +swp-booking-service { + display: block; + font-size: var(--font-size-base); + font-weight: var(--font-weight-medium); + color: var(--color-text); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +swp-booking-customer { + display: block; + font-size: var(--font-size-sm); + color: var(--color-text-secondary); +} + +/* =========================================== + BOOKING EMPLOYEE + =========================================== */ +swp-booking-employee { + display: flex; + align-items: center; + gap: var(--spacing-3); +} + +swp-booking-employee swp-avatar-small { + width: 24px; + height: 24px; + border-radius: var(--radius-full); + background: var(--color-teal); + color: white; + display: flex; + align-items: center; + justify-content: center; + font-size: 10px; + font-weight: var(--font-weight-semibold); +} + +swp-booking-employee span { + font-size: var(--font-size-sm); + color: var(--color-text-secondary); +} + +/* =========================================== + BOOKING STATUS + =========================================== */ +swp-booking-status { + padding: var(--spacing-2) var(--spacing-4); + border-radius: var(--radius-md); + font-size: var(--font-size-xs); + font-weight: var(--font-weight-medium); +} + +swp-booking-status.confirmed { + background: color-mix(in srgb, var(--color-green) 15%, transparent); + color: var(--color-green); +} + +swp-booking-status.pending { + background: color-mix(in srgb, var(--color-amber) 15%, transparent); + color: var(--color-amber); +} + +swp-booking-status.inprogress, +swp-booking-status.in-progress { + background: color-mix(in srgb, var(--color-blue) 15%, transparent); + color: var(--color-blue); +} + +swp-booking-status.completed { + background: var(--color-background-hover); + color: var(--color-text-secondary); +} + +/* =========================================== + CURRENT TIME INDICATOR + =========================================== */ +swp-current-time { + display: flex; + align-items: center; + gap: var(--spacing-4); + padding: var(--spacing-4) var(--spacing-6); + background: color-mix(in srgb, var(--color-teal) 10%, transparent); + border-radius: var(--radius-lg); + margin: 0 var(--card-body-padding) var(--spacing-4) var(--card-body-padding); +} + +swp-current-time i { + font-size: var(--font-size-lg); + color: var(--color-teal); +} + +swp-current-time span { + font-size: var(--font-size-md); + color: var(--color-teal); + font-weight: var(--font-weight-medium); +} + +swp-current-time swp-time { + font-family: var(--font-mono); + font-weight: var(--font-weight-semibold); +} diff --git a/PlanTempus.Application/wwwroot/css/design-tokens.css b/PlanTempus.Application/wwwroot/css/design-tokens.css index 346e5a9..6718a60 100644 --- a/PlanTempus.Application/wwwroot/css/design-tokens.css +++ b/PlanTempus.Application/wwwroot/css/design-tokens.css @@ -189,6 +189,9 @@ --container-max-width-md: 1200px; --container-max-width-lg: 1400px; + /* -------- Card Spacing -------- */ + --card-body-padding: var(--spacing-5); + /* -------- Calendar Grid -------- */ --hour-height: 64px; --time-axis-width: 60px; diff --git a/PlanTempus.Application/wwwroot/css/notifications.css b/PlanTempus.Application/wwwroot/css/notifications.css new file mode 100644 index 0000000..f16bb76 --- /dev/null +++ b/PlanTempus.Application/wwwroot/css/notifications.css @@ -0,0 +1,92 @@ +/** + * Notifications CSS + * + * Styling for notification components on dashboard + */ + +/* =========================================== + NOTIFICATION LIST + =========================================== */ +swp-notification-list { + display: grid; + grid-template-columns: 50px 1fr; + gap: var(--spacing-4) var(--spacing-6); + padding: 0 var(--card-body-padding); +} + +/* =========================================== + NOTIFICATION ITEM + =========================================== */ +swp-notification-item { + display: grid; + grid-column: 1 / -1; + grid-template-columns: subgrid; + align-items: center; + padding: var(--spacing-5) var(--spacing-6); + background: var(--color-background-alt); + border-radius: var(--radius-xl); + cursor: pointer; + transition: background var(--transition-fast); +} + +swp-notification-item:hover { + background: var(--color-background-hover); +} + +swp-notification-item.unread { + background: color-mix(in srgb, var(--color-teal) 5%, var(--color-background-alt)); +} + +swp-notification-item.unread:hover { + background: var(--color-background-hover); +} + +/* =========================================== + NOTIFICATION ICON + =========================================== */ +swp-notification-icon { + display: flex; + align-items: center; + justify-content: center; + width: 40px; + height: 40px; + background: var(--color-background-hover); + border-radius: var(--radius-xl); + color: var(--color-text-secondary); + font-size: var(--font-size-xl); +} + +swp-notification-item.unread swp-notification-icon { + background: color-mix(in srgb, var(--color-teal) 15%, transparent); + color: var(--color-teal); +} + +/* =========================================== + NOTIFICATION CONTENT + =========================================== */ +swp-notification-content { + display: flex; + flex-direction: column; + min-width: 0; +} + +swp-notification-text { + display: block; + font-size: var(--font-size-md); + color: var(--color-text); + line-height: var(--line-height-snug); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +swp-notification-text strong { + font-weight: var(--font-weight-semibold); +} + +swp-notification-time { + display: block; + font-size: var(--font-size-xs); + color: var(--color-text-secondary); + margin-top: var(--spacing-1); +} diff --git a/PlanTempus.Application/wwwroot/css/page.css b/PlanTempus.Application/wwwroot/css/page.css index dceab74..4372d4f 100644 --- a/PlanTempus.Application/wwwroot/css/page.css +++ b/PlanTempus.Application/wwwroot/css/page.css @@ -94,7 +94,7 @@ swp-card-content { =========================================== */ swp-dashboard-grid { display: grid; - grid-template-columns: 1fr 350px; + grid-template-columns: 1fr 380px; gap: var(--spacing-5); }