diff --git a/PlanTempus.Application/.claude/settings.local.json b/PlanTempus.Application/.claude/settings.local.json index 825d842..ac7fc6c 100644 --- a/PlanTempus.Application/.claude/settings.local.json +++ b/PlanTempus.Application/.claude/settings.local.json @@ -5,7 +5,9 @@ "Bash(npm run build:*)", "Bash(node -e:*)", "Bash(dir /s /b \"C:\\\\Users\\\\Janus Knudsen\\\\source\\\\swp-repos\\\\PlanTempus\")", - "Bash(findstr:*)" + "Bash(findstr:*)", + "Bash(dir \"C:\\\\Users\\\\Janus Knudsen\\\\source\\\\swp-repos\\\\PlanTempus\\\\PlanTempus.Application\\\\wwwroot\\\\*.html\")", + "Bash(dir /s /b \"C:\\\\Users\\\\Janus Knudsen\\\\source\\\\swp-repos\\\\PlanTempus\\\\PlanTempus.Application\\\\Features\\\\OnlineBooking\\\\*.cs\" \"C:\\\\Users\\\\Janus Knudsen\\\\source\\\\swp-repos\\\\PlanTempus\\\\PlanTempus.Application\\\\Features\\\\OnlineBooking\\\\*.cshtml\")" ] } } diff --git a/PlanTempus.Application/Features/Localization/Translations/da.json b/PlanTempus.Application/Features/Localization/Translations/da.json index be3d5cd..2ab8fc1 100644 --- a/PlanTempus.Application/Features/Localization/Translations/da.json +++ b/PlanTempus.Application/Features/Localization/Translations/da.json @@ -707,5 +707,60 @@ "noData": "Ingen økonomiske data tilgængelige" } } + }, + "onlineBooking": { + "title": "Online Booking", + "subtitle": "Konfigurer og preview din booking-side", + "status": { + "active": "Aktiv", + "inactive": "Inaktiv" + }, + "url": { + "title": "Booking URL", + "copy": "Kopiér", + "open": "Åbn i ny fane" + }, + "settings": { + "title": "Booking-indstillinger", + "enableBooking": "Aktivér online booking", + "enableBookingDesc": "Tillad kunder at booke tider online", + "bookAhead": "Book frem i tiden", + "minNotice": "Minimum varsel", + "cancelDeadline": "Aflysningsfrist" + }, + "company": { + "title": "Virksomhedsoplysninger", + "edit": "Rediger", + "name": "Navn", + "address": "Adresse", + "zipCity": "Postnr + By", + "phone": "Telefon", + "email": "Email" + }, + "hours": { + "title": "Åbningstider", + "edit": "Rediger", + "closed": "Lukket" + }, + "preview": { + "title": "Preview", + "desktop": "Desktop", + "tablet": "Tablet", + "mobile": "Mobil" + }, + "allSettings": "Alle indstillinger" + }, + "settings": { + "title": "Indstillinger", + "tabs": { + "company": "Virksomhed", + "calendar": "Kalender", + "booking": "Online Booking", + "billing": "Faktura & Kvittering", + "reminders": "Påmindelser", + "payments": "Betalinger", + "modules": "Moduler", + "tracking": "Tracking" + } } } diff --git a/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingCompany/Default.cshtml b/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingCompany/Default.cshtml new file mode 100644 index 0000000..7baca13 --- /dev/null +++ b/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingCompany/Default.cshtml @@ -0,0 +1,33 @@ + + + + + Virksomhedsoplysninger + + Rediger + + + + + Navn + Salon Beauty + + + Adresse + Hovedgaden 123 + + + Postnr + By + 2100 København Ø + + + Telefon + +45 12 34 56 78 + + + Email + kontakt@salonbeauty.dk + + + + diff --git a/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingCompany/OnlineBookingCompanyViewComponent.cs b/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingCompany/OnlineBookingCompanyViewComponent.cs new file mode 100644 index 0000000..be905df --- /dev/null +++ b/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingCompany/OnlineBookingCompanyViewComponent.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Mvc; + +namespace PlanTempus.Application.Features.OnlineBooking.Components; + +/// +/// ViewComponent for displaying company information used in booking. +/// +public class OnlineBookingCompanyViewComponent : ViewComponent +{ + public IViewComponentResult Invoke() + { + return View(); + } +} diff --git a/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingHours/Default.cshtml b/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingHours/Default.cshtml new file mode 100644 index 0000000..22eb848 --- /dev/null +++ b/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingHours/Default.cshtml @@ -0,0 +1,93 @@ + + + + + Åbningstider + + Rediger + + + + + Mandag + + Ja + Nej + + + + - + + + + + Tirsdag + + Ja + Nej + + + + - + + + + + Onsdag + + Ja + Nej + + + + - + + + + + Torsdag + + Ja + Nej + + + + - + + + + + Fredag + + Ja + Nej + + + + - + + + + + Lørdag + + Ja + Nej + + + + - + + + + + Søndag + + Ja + Nej + + Lukket + + + + diff --git a/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingHours/OnlineBookingHoursViewComponent.cs b/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingHours/OnlineBookingHoursViewComponent.cs new file mode 100644 index 0000000..967019c --- /dev/null +++ b/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingHours/OnlineBookingHoursViewComponent.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Mvc; + +namespace PlanTempus.Application.Features.OnlineBooking.Components; + +/// +/// ViewComponent for displaying opening hours used in booking. +/// +public class OnlineBookingHoursViewComponent : ViewComponent +{ + public IViewComponentResult Invoke() + { + return View(); + } +} diff --git a/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingPreview/Default.cshtml b/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingPreview/Default.cshtml new file mode 100644 index 0000000..1726e5d --- /dev/null +++ b/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingPreview/Default.cshtml @@ -0,0 +1,130 @@ + + + + + Preview + + + + + + + + + + + + + + + + + + + + SB + +

Salon Beauty

+

Hovedgaden 123, 2100 København Ø

+
+
+ + +

Vælg service

+ + + Dame klip + 45 min · 450 kr + + + Herre klip + 30 min · 350 kr + + + Farvning + 90 min · 850 kr + + + Balayage + 120 min · 1.450 kr + + +
+ + +

Vælg medarbejder

+ + + MJ + Maria Jensen + + + AH + Anna Hansen + + + LP + Louise Petersen + + +
+ + +

Vælg dato og tid

+ + Januar 2026 + + Ma + Ti + On + To + Fr + + + 27 + 28 + 29 + 30 + 31 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15 + 16 + + +
+ + + Book tid + +
+
+
+
+ + diff --git a/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingPreview/OnlineBookingPreviewViewComponent.cs b/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingPreview/OnlineBookingPreviewViewComponent.cs new file mode 100644 index 0000000..4c3202f --- /dev/null +++ b/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingPreview/OnlineBookingPreviewViewComponent.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Mvc; + +namespace PlanTempus.Application.Features.OnlineBooking.Components; + +/// +/// ViewComponent for the booking preview iframe with device toggle. +/// +public class OnlineBookingPreviewViewComponent : ViewComponent +{ + public IViewComponentResult Invoke() + { + return View(); + } +} diff --git a/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingSettings/Default.cshtml b/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingSettings/Default.cshtml new file mode 100644 index 0000000..01e1c2a --- /dev/null +++ b/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingSettings/Default.cshtml @@ -0,0 +1,61 @@ + + + + + Booking-indstillinger + + + + + + Aktivér online booking + Tillad kunder at booke tider online + + + Ja + Nej + + + + + + + + Book frem i tiden + + + + + + Minimum varsel + + + + + + Aflysningsfrist + + + + + + + diff --git a/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingSettings/OnlineBookingSettingsViewComponent.cs b/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingSettings/OnlineBookingSettingsViewComponent.cs new file mode 100644 index 0000000..f05ccde --- /dev/null +++ b/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingSettings/OnlineBookingSettingsViewComponent.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Mvc; + +namespace PlanTempus.Application.Features.OnlineBooking.Components; + +/// +/// ViewComponent for online booking settings (toggle, booking ahead, notice, cancellation). +/// +public class OnlineBookingSettingsViewComponent : ViewComponent +{ + public IViewComponentResult Invoke() + { + return View(); + } +} diff --git a/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingUrl/Default.cshtml b/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingUrl/Default.cshtml new file mode 100644 index 0000000..6b68c7e --- /dev/null +++ b/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingUrl/Default.cshtml @@ -0,0 +1,31 @@ + + + + + Booking URL + + + + + + + + + + + + + Åbn i ny fane + + + + + + diff --git a/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingUrl/OnlineBookingUrlViewComponent.cs b/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingUrl/OnlineBookingUrlViewComponent.cs new file mode 100644 index 0000000..4f7839d --- /dev/null +++ b/PlanTempus.Application/Features/OnlineBooking/Components/OnlineBookingUrl/OnlineBookingUrlViewComponent.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Mvc; + +namespace PlanTempus.Application.Features.OnlineBooking.Components; + +/// +/// ViewComponent for displaying the booking URL with copy and open actions. +/// +public class OnlineBookingUrlViewComponent : ViewComponent +{ + public IViewComponentResult Invoke() + { + return View(); + } +} diff --git a/PlanTempus.Application/Features/OnlineBooking/Pages/Index.cshtml b/PlanTempus.Application/Features/OnlineBooking/Pages/Index.cshtml new file mode 100644 index 0000000..7c98cb8 --- /dev/null +++ b/PlanTempus.Application/Features/OnlineBooking/Pages/Index.cshtml @@ -0,0 +1,42 @@ +@page "/online-booking" +@model PlanTempus.Application.Features.OnlineBooking.Pages.IndexModel +@{ + ViewData["Title"] = "Online Booking"; +} + + + + + +

Online Booking

+

Konfigurer og preview din booking-side

+
+ + + + Aktiv + + +
+
+
+ + + + + + @await Component.InvokeAsync("OnlineBookingUrl") + @await Component.InvokeAsync("OnlineBookingSettings") + @await Component.InvokeAsync("OnlineBookingCompany") + @await Component.InvokeAsync("OnlineBookingHours") + + + Alle indstillinger + + + + + + @await Component.InvokeAsync("OnlineBookingPreview") + + diff --git a/PlanTempus.Application/Features/OnlineBooking/Pages/Index.cshtml.cs b/PlanTempus.Application/Features/OnlineBooking/Pages/Index.cshtml.cs new file mode 100644 index 0000000..2686ad6 --- /dev/null +++ b/PlanTempus.Application/Features/OnlineBooking/Pages/Index.cshtml.cs @@ -0,0 +1,10 @@ +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace PlanTempus.Application.Features.OnlineBooking.Pages; + +public class IndexModel : PageModel +{ + public void OnGet() + { + } +} diff --git a/PlanTempus.Application/Features/Settings/Pages/Index.cshtml b/PlanTempus.Application/Features/Settings/Pages/Index.cshtml index 1ddb516..7b154bc 100644 --- a/PlanTempus.Application/Features/Settings/Pages/Index.cshtml +++ b/PlanTempus.Application/Features/Settings/Pages/Index.cshtml @@ -18,18 +18,10 @@ - - - Virksomhed - - + Kalender - - - Online Booking - Faktura & Kvittering @@ -53,27 +45,13 @@ - - - - @await Component.InvokeAsync("SettingsGeneral") - - - - + @await Component.InvokeAsync("SettingsCalendar") - - - - @await Component.InvokeAsync("SettingsBooking") - - - diff --git a/PlanTempus.Application/Features/_Shared/Pages/_Layout.cshtml b/PlanTempus.Application/Features/_Shared/Pages/_Layout.cshtml index a9d88df..399dbd9 100644 --- a/PlanTempus.Application/Features/_Shared/Pages/_Layout.cshtml +++ b/PlanTempus.Application/Features/_Shared/Pages/_Layout.cshtml @@ -37,6 +37,7 @@ + @await RenderSectionAsync("Styles", required: false) diff --git a/PlanTempus.Application/wwwroot/css/online-booking.css b/PlanTempus.Application/wwwroot/css/online-booking.css new file mode 100644 index 0000000..b108a3a --- /dev/null +++ b/PlanTempus.Application/wwwroot/css/online-booking.css @@ -0,0 +1,360 @@ +/** + * Online Booking - Preview and Settings page + * + * Feature-specific styling only. + * Reuses: swp-card, swp-card-header, swp-card-title, swp-edit-section, + * swp-toggle-row, swp-url-field, swp-hours-table (components.css, settings.css) + */ + +/* =========================================== + PAGE LAYOUT (Two-column) + =========================================== */ +swp-online-booking-layout { + display: grid; + grid-template-columns: 1fr 1fr; + gap: var(--card-gap); + align-items: start; +} + +@media (max-width: 1200px) { + swp-online-booking-layout { + grid-template-columns: 1fr; + } + + swp-online-booking-preview-container { + order: -1; + } +} + +/* =========================================== + SETTINGS COLUMN + =========================================== */ +swp-online-booking-settings { + display: flex; + flex-direction: column; + gap: var(--card-gap); +} + +/* URL Actions (below URL field) */ +swp-url-actions { + display: flex; + gap: var(--spacing-3); + margin-top: var(--spacing-4); +} + +/* All Settings Link */ +swp-all-settings-link { + display: flex; + align-items: center; + justify-content: center; + gap: var(--spacing-2); + padding: var(--spacing-4); + color: var(--color-teal); + font-size: var(--font-size-base); + font-weight: var(--font-weight-medium); + cursor: pointer; + transition: opacity var(--transition-fast); + text-decoration: none; + + &:hover { + opacity: 0.8; + } + + & i { + font-size: 18px; + } +} + +/* =========================================== + PREVIEW COLUMN + =========================================== */ +swp-online-booking-preview-container { + display: flex; + flex-direction: column; + position: sticky; + top: calc(var(--sticky-header-height, 120px) + var(--section-gap)); +} + +swp-preview-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--spacing-4) var(--spacing-5); + background: var(--color-surface); + border: 1px solid var(--color-border); + border-bottom: none; + border-radius: var(--radius-lg) var(--radius-lg) 0 0; +} + +swp-preview-title { + display: flex; + align-items: center; + gap: var(--spacing-3); + font-size: var(--font-size-sm); + font-weight: var(--font-weight-semibold); + text-transform: uppercase; + color: var(--color-text); + + & i { + font-size: 18px; + color: var(--color-teal); + } +} + +/* =========================================== + DEVICE TOGGLE + =========================================== */ +swp-device-toggle { + display: flex; + gap: var(--spacing-1); + padding: var(--spacing-1); + background: var(--color-background-alt); + border-radius: var(--radius-md); +} + +swp-device-btn { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + border-radius: var(--radius-sm); + background: transparent; + color: var(--color-text-secondary); + cursor: pointer; + transition: all var(--transition-fast); + + & i { + font-size: 18px; + } + + &:hover { + background: var(--color-background); + color: var(--color-text); + } + + &.active { + background: var(--color-teal); + color: white; + } +} + +/* =========================================== + PREVIEW FRAME + =========================================== */ +swp-preview-frame { + display: flex; + justify-content: center; + padding: var(--spacing-6); + background: var(--color-background-alt); + border: 1px solid var(--color-border); + border-radius: 0 0 var(--radius-lg) var(--radius-lg); + min-height: 600px; + overflow: hidden; +} + +swp-preview-content { + width: 100%; + max-width: 100%; + background: var(--color-surface); + border-radius: var(--radius-lg); + box-shadow: var(--shadow-lg); + overflow: hidden; + transition: all var(--transition-normal); +} + +/* Device variants */ +swp-preview-frame[data-device="desktop"] swp-preview-content { + max-width: 100%; +} + +swp-preview-frame[data-device="tablet"] swp-preview-content { + max-width: 768px; +} + +swp-preview-frame[data-device="mobile"] swp-preview-content { + max-width: 375px; +} + +/* =========================================== + BOOKING DEMO (Placeholder UI) + =========================================== */ +swp-booking-demo { + display: flex; + flex-direction: column; + height: 100%; +} + +swp-booking-demo-header { + display: flex; + align-items: center; + gap: var(--spacing-4); + padding: var(--spacing-6); + background: linear-gradient(135deg, var(--color-teal), #00796b); + color: white; +} + +swp-booking-demo-logo { + width: 48px; + height: 48px; + background: rgba(255, 255, 255, 0.2); + border-radius: var(--radius-md); + display: flex; + align-items: center; + justify-content: center; + font-size: var(--font-size-lg); + font-weight: var(--font-weight-bold); +} + +swp-booking-demo-info { + & h2 { + font-size: var(--font-size-lg); + font-weight: var(--font-weight-semibold); + margin: 0 0 var(--spacing-1) 0; + } + + & p { + font-size: var(--font-size-sm); + opacity: 0.9; + margin: 0; + } +} + +swp-booking-demo-section { + padding: var(--spacing-5); + border-bottom: 1px solid var(--color-border); + + & h3 { + font-size: var(--font-size-sm); + font-weight: var(--font-weight-semibold); + text-transform: uppercase; + color: var(--color-text-secondary); + margin: 0 0 var(--spacing-4) 0; + } +} + +/* Services list */ +swp-booking-demo-services { + display: flex; + flex-direction: column; + gap: var(--spacing-2); +} + +swp-booking-demo-service { + display: flex; + justify-content: space-between; + align-items: center; + padding: var(--spacing-3) var(--spacing-4); + background: var(--color-background-alt); + border-radius: var(--radius-md); + cursor: pointer; + transition: all var(--transition-fast); + + &:hover { + background: var(--color-background-hover); + } + + &.selected { + background: var(--bg-teal-strong); + border: 1px solid var(--color-teal); + } + + & .name { + font-size: var(--font-size-base); + font-weight: var(--font-weight-medium); + color: var(--color-text); + } + + & .meta { + font-size: var(--font-size-sm); + color: var(--color-text-secondary); + } +} + +/* Employees list */ +swp-booking-demo-employees { + display: flex; + gap: var(--spacing-3); + flex-wrap: wrap; +} + +swp-booking-demo-employee { + display: flex; + align-items: center; + gap: var(--spacing-2); + padding: var(--spacing-2) var(--spacing-4) var(--spacing-2) var(--spacing-2); + background: var(--color-background-alt); + border-radius: var(--radius-pill); + cursor: pointer; + transition: all var(--transition-fast); + + &:hover { + background: var(--color-background-hover); + } + + &.selected { + background: var(--bg-teal-strong); + border: 1px solid var(--color-teal); + } + + & span { + font-size: var(--font-size-sm); + color: var(--color-text); + } +} + +/* Calendar */ +swp-booking-demo-calendar { + background: var(--color-background-alt); + border-radius: var(--radius-md); + padding: var(--spacing-4); +} + +swp-booking-demo-month { + display: block; + text-align: center; + font-size: var(--font-size-base); + font-weight: var(--font-weight-semibold); + color: var(--color-text); + margin-bottom: var(--spacing-4); +} + +swp-booking-demo-days { + display: grid; + grid-template-columns: repeat(7, 1fr); + gap: var(--spacing-1); + text-align: center; + + & .day-header { + font-size: var(--font-size-xs); + font-weight: var(--font-weight-medium); + color: var(--color-text-secondary); + padding: var(--spacing-2); + } + + & .day { + font-size: var(--font-size-sm); + padding: var(--spacing-2); + border-radius: var(--radius-sm); + cursor: pointer; + transition: all var(--transition-fast); + + &:hover { + background: var(--color-background-hover); + } + + &.selected { + background: var(--color-teal); + color: white; + } + + &.other { + color: var(--color-text-muted); + } + } +} + +swp-booking-demo-footer { + padding: var(--spacing-5); + margin-top: auto; +}