From 405dabeb34e6dbd09c4bac96ac202c46b2246fbb Mon Sep 17 00:00:00 2001 From: "Janus C. H. Knudsen" Date: Wed, 21 Jan 2026 21:37:09 +0100 Subject: [PATCH] Add reports page with sales analytics and UI components Introduces comprehensive reports feature with interactive sales dashboard Includes dynamic data tables, charts, and filtering capabilities Enhances application with new statistics and reporting functionality --- .claude/settings.local.json | 3 +- .gitignore | 10 +- .../Features/Menu/Services/MockMenuService.cs | 4 +- .../Features/Reports/Pages/Index.cshtml | 622 +++++++++++++++ .../Features/Reports/Pages/Index.cshtml.cs | 10 + .../Features/Shared/_NewTodoDrawer.cshtml | 64 ++ .../Features/Shared/_ProfileDrawer.cshtml | 87 ++- .../Features/Shared/_TodoDrawer.cshtml | 141 ++++ .../Features/_Shared/Pages/_Layout.cshtml | 3 + .../_Shared/Pages/_ProfileDrawer.cshtml | 49 -- PlanTempus.Application/package-lock.json | 7 + PlanTempus.Application/package.json | 1 + .../wwwroot/css/app-layout.css | 15 +- .../wwwroot/css/drawers.css | 710 +++++++++++++++--- .../wwwroot/css/reports.css | 395 ++++++++++ 15 files changed, 1909 insertions(+), 212 deletions(-) create mode 100644 PlanTempus.Application/Features/Reports/Pages/Index.cshtml create mode 100644 PlanTempus.Application/Features/Reports/Pages/Index.cshtml.cs create mode 100644 PlanTempus.Application/Features/Shared/_NewTodoDrawer.cshtml create mode 100644 PlanTempus.Application/Features/Shared/_TodoDrawer.cshtml delete mode 100644 PlanTempus.Application/Features/_Shared/Pages/_ProfileDrawer.cshtml create mode 100644 PlanTempus.Application/wwwroot/css/reports.css diff --git a/.claude/settings.local.json b/.claude/settings.local.json index b4ee63a..77b9f5f 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -9,7 +9,8 @@ "Bash(npm run analyze-css:*)", "Bash(node:*)", "Bash(npx esbuild:*)", - "mcp__ide__getDiagnostics" + "mcp__ide__getDiagnostics", + "Bash(grep:*)" ] } } diff --git a/.gitignore b/.gitignore index b009ebb..686d1cb 100644 --- a/.gitignore +++ b/.gitignore @@ -366,7 +366,9 @@ nul tmpclaude* PlanTempus.Application/tmpclaude* - -PlanTempus.Application/wwwroot/js/app.js - -PlanTempus.Application/wwwroot/js/app.js.map + +PlanTempus.Application/wwwroot/js/app.js + +PlanTempus.Application/wwwroot/js/app.js.map + +PlanTempus.Application/wwwroot/lib/* diff --git a/PlanTempus.Application/Features/Menu/Services/MockMenuService.cs b/PlanTempus.Application/Features/Menu/Services/MockMenuService.cs index d40a310..229a7ff 100644 --- a/PlanTempus.Application/Features/Menu/Services/MockMenuService.cs +++ b/PlanTempus.Application/Features/Menu/Services/MockMenuService.cs @@ -155,8 +155,8 @@ public class MockMenuService : IMenuService { Id = "reports", Label = "Statistik & Rapporter", - Icon = "ph-chart-bar", - Url = "/reports", + Icon = "ph-chart-line-up", + Url = "/rapporter", MinimumRole = UserRole.Manager, SortOrder = 1 } diff --git a/PlanTempus.Application/Features/Reports/Pages/Index.cshtml b/PlanTempus.Application/Features/Reports/Pages/Index.cshtml new file mode 100644 index 0000000..81776df --- /dev/null +++ b/PlanTempus.Application/Features/Reports/Pages/Index.cshtml @@ -0,0 +1,622 @@ +@page "/rapporter" +@model PlanTempus.Application.Features.Reports.Pages.IndexModel +@{ + ViewData["Title"] = "Statistik og Rapporter"; +} + + + + + + + + Statistik og Rapporter + + + + + Eksporter + + + + + + + + + + Salgsrapport + + + + Timerapport + + + + + + + + + + + 12.450 kr + Omsætning i dag + + + 187.230 kr + Omsætning denne måned + + + 18 + Antal salg i dag + + + 692 kr + Gns. ordreværdi + + + + + + + + Omsætning pr. måned + Sidste 12 måneder + + + + + + Betalingsmetoder + Fordeling + + + + + + + + + + + + + Fra + + + + Til + + + + Status + + + + Betaling + + + + + + + + + Faktura + Dato/tid + Kunde + Medarbejder + Ydelser + Beløb + Betaling + Status + + + + + + #1847 + + + + 6. jan 2025 + 14:32 + + + + + Maria Hansen + +45 23 45 67 89 + + + Louise P. + + + Dameklip, Farve + + 1 produkt + + + 1.450 kr + Kort + + + + + + + #1846 + + + + 6. jan 2025 + 13:15 + + + + + Peter Sørensen + +45 30 12 34 56 + + + Anna J. + + + Herreklip + + + 295 kr + MobilePay + + + + + + + #1845 + + + + 6. jan 2025 + 11:45 + + + + + Lise Andersen + +45 42 56 78 90 + + + Louise P. + + + Dameklip, Balayage + + 2 produkter + + + 2.350 kr + Kort + + + + + + + #1844 + + + + 5. jan 2025 + 16:20 + + + + + Thomas Nielsen + +45 51 23 45 67 + + + Mikkel H. + + + Herreklip, Skægtrim + + + 395 kr + Kontant + + + + + + + #1843 + + + + 5. jan 2025 + 14:00 + + + + + Sofia Madsen + +45 60 78 90 12 + + + Anna J. + + + Extensions + + 1 produkt + + + 4.500 kr + Faktura + Afventer + + + + + + #1842 + + + + 5. jan 2025 + 11:30 + + + + + Emma Jensen + +45 71 23 45 67 + + + Louise P. + + + Dameklip + + + -450 kr + Kort + Krediteret + + + + + + #1841 + + + + 4. jan 2025 + 15:45 + + + + + Katrine Olsen + +45 82 34 56 78 + + + Mikkel H. + + + Dameklip, Highlights + + 3 produkter + + + 1.895 kr + Fordelskort + + + + + + + #1840 + + + + 4. jan 2025 + 10:00 + + + + + Mads Christensen + +45 93 45 67 89 + + + Anna J. + + + Herreklip + + + 275 kr + MobilePay + + + + + + Viser 1-8 af 1.847 fakturaer + + + 1 + 2 + 3 + ... + 231 + + + + + + + + + + + + + + + Timerapport + + + + + Timerapport kommer snart + + + + + +@section Scripts { + +} diff --git a/PlanTempus.Application/Features/Reports/Pages/Index.cshtml.cs b/PlanTempus.Application/Features/Reports/Pages/Index.cshtml.cs new file mode 100644 index 0000000..f134d7b --- /dev/null +++ b/PlanTempus.Application/Features/Reports/Pages/Index.cshtml.cs @@ -0,0 +1,10 @@ +using Microsoft.AspNetCore.Mvc.RazorPages; + +namespace PlanTempus.Application.Features.Reports.Pages; + +public class IndexModel : PageModel +{ + public void OnGet() + { + } +} diff --git a/PlanTempus.Application/Features/Shared/_NewTodoDrawer.cshtml b/PlanTempus.Application/Features/Shared/_NewTodoDrawer.cshtml new file mode 100644 index 0000000..d40e8dd --- /dev/null +++ b/PlanTempus.Application/Features/Shared/_NewTodoDrawer.cshtml @@ -0,0 +1,64 @@ + + + + + + Ny opgave + + + + + + Opgave + + + + + + + Dato + + + + Tid + + + + + + + Prioritet + + + + + + Synlighed + + + + Kun mig + + + + Alle + + + + + + + Noter + + + + + + Annuller + Gem opgave + + diff --git a/PlanTempus.Application/Features/Shared/_ProfileDrawer.cshtml b/PlanTempus.Application/Features/Shared/_ProfileDrawer.cshtml index dd9c3bb..c12c2aa 100644 --- a/PlanTempus.Application/Features/Shared/_ProfileDrawer.cshtml +++ b/PlanTempus.Application/Features/Shared/_ProfileDrawer.cshtml @@ -1,49 +1,74 @@ - Profil - + Min profil + - - MJ - Maria Jensen - maria@salon.dk - + + MJ + Maria Jensen + Administrator + maria@salon.dk + - + + Konto + + + Rediger profil + + + + + Skift adgangskode + + + + + Notifikationer + 3 ulæste + + + + Mine opgaver + 2 i dag + + - - - - Min profil - - - - Indstillinger - - + + Udseende + + + + + + + + + - - - - - - Mørk tilstand - - - - - - + + Support + + + Hjælp & Support + + + + + Om PlanTempus + v2.1.0 + + - Log ud + Log ud diff --git a/PlanTempus.Application/Features/Shared/_TodoDrawer.cshtml b/PlanTempus.Application/Features/Shared/_TodoDrawer.cshtml new file mode 100644 index 0000000..8f8e5aa --- /dev/null +++ b/PlanTempus.Application/Features/Shared/_TodoDrawer.cshtml @@ -0,0 +1,141 @@ + + + + + + Mine opgaver + + + + Ny opgave + + + + + + + + + + I dag + 3 + + + + + + + + Ring til leverandør om ordre + + + + 10:00 + + + + + + + + + + Bestil shampoo fra Wella + + + + + + + + Opdater prisliste for 2025 + + + + Høj + + + + + + + + + + + + Denne uge + 2 + + + + + + + + Rengør og vedligehold udstyr + + + + Onsdag + + + + + + + + + + Medarbejdersamtale med Jonas + + + + Fredag + + + + 14:00 + + + + + + + + + + + diff --git a/PlanTempus.Application/Features/_Shared/Pages/_Layout.cshtml b/PlanTempus.Application/Features/_Shared/Pages/_Layout.cshtml index 955ff6a..e4bd330 100644 --- a/PlanTempus.Application/Features/_Shared/Pages/_Layout.cshtml +++ b/PlanTempus.Application/Features/_Shared/Pages/_Layout.cshtml @@ -35,6 +35,7 @@ + @await RenderSectionAsync("Styles", required: false) @@ -62,6 +63,8 @@ + + diff --git a/PlanTempus.Application/Features/_Shared/Pages/_ProfileDrawer.cshtml b/PlanTempus.Application/Features/_Shared/Pages/_ProfileDrawer.cshtml deleted file mode 100644 index 5c44e33..0000000 --- a/PlanTempus.Application/Features/_Shared/Pages/_ProfileDrawer.cshtml +++ /dev/null @@ -1,49 +0,0 @@ - - - Profil - - - - - - - - MJ - Maria Jensen - maria@salon.dk - - - - - - - - Min profil - - - - Indstillinger - - - - - - - - - Mørk tilstand - - - - - - - - - - - - Log ud - - - diff --git a/PlanTempus.Application/package-lock.json b/PlanTempus.Application/package-lock.json index 0ad315d..e546fd1 100644 --- a/PlanTempus.Application/package-lock.json +++ b/PlanTempus.Application/package-lock.json @@ -5,6 +5,7 @@ "packages": { "": { "dependencies": { + "@sevenweirdpeople/swp-charting": "^0.2.2", "fuse.js": "^7.1.0" }, "devDependencies": { @@ -483,6 +484,12 @@ "node": ">=14" } }, + "node_modules/@sevenweirdpeople/swp-charting": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@sevenweirdpeople/swp-charting/-/swp-charting-0.2.2.tgz", + "integrity": "sha512-q9p7TOSMAq6I0t6jGEWpmjR7l2H8q8G0TnXbIpDutCz5a2JEqMDFe0NGBGcCwze2rvvRnRvCz8P2zGMQlHmphw==", + "license": "MIT" + }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", diff --git a/PlanTempus.Application/package.json b/PlanTempus.Application/package.json index d415797..ce08276 100644 --- a/PlanTempus.Application/package.json +++ b/PlanTempus.Application/package.json @@ -8,6 +8,7 @@ "analyze-css": "node analyze-css.js" }, "dependencies": { + "@sevenweirdpeople/swp-charting": "^0.2.2", "fuse.js": "^7.1.0" } } diff --git a/PlanTempus.Application/wwwroot/css/app-layout.css b/PlanTempus.Application/wwwroot/css/app-layout.css index 2f57b74..2016369 100644 --- a/PlanTempus.Application/wwwroot/css/app-layout.css +++ b/PlanTempus.Application/wwwroot/css/app-layout.css @@ -33,18 +33,5 @@ swp-main-content { /* =========================================== DRAWER OVERLAY + Styles moved to drawers.css for consistency with calpoc =========================================== */ -swp-drawer-overlay { - position: fixed; - inset: 0; - background: rgba(0, 0, 0, 0.5); - z-index: var(--z-overlay); - opacity: 0; - visibility: hidden; - transition: opacity var(--transition-normal), visibility var(--transition-normal); -} - -swp-drawer-overlay.active { - opacity: 1; - visibility: visible; -} diff --git a/PlanTempus.Application/wwwroot/css/drawers.css b/PlanTempus.Application/wwwroot/css/drawers.css index bd9888c..fa5c4ff 100644 --- a/PlanTempus.Application/wwwroot/css/drawers.css +++ b/PlanTempus.Application/wwwroot/css/drawers.css @@ -2,8 +2,27 @@ * Drawers - Slide-in Panels * * Profile drawer, notifications drawer, etc. + * Matches calpoc pattern from Calendar/wwwroot/poc-layout.html */ +/* =========================================== + DRAWER OVERLAY + =========================================== */ +swp-drawer-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.2); + opacity: 0; + visibility: hidden; + transition: opacity 200ms ease, visibility 200ms ease; + z-index: 900; +} + +swp-drawer-overlay.active { + opacity: 1; + visibility: visible; +} + /* =========================================== BASE DRAWER (Generic) =========================================== */ @@ -15,12 +34,12 @@ height: 100vh; background: var(--color-surface); border-left: 1px solid var(--color-border); - box-shadow: var(--shadow-lg); + box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1); z-index: var(--z-drawer); display: flex; flex-direction: column; transform: translateX(100%); - transition: transform var(--transition-normal); + transition: transform 200ms ease; } [data-drawer].active, @@ -46,12 +65,12 @@ swp-todo-drawer { height: 100vh; background: var(--color-surface); border-left: 1px solid var(--color-border); - box-shadow: var(--shadow-lg); - z-index: var(--z-drawer); + box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1); + z-index: 1000; display: flex; flex-direction: column; transform: translateX(100%); - transition: transform var(--transition-normal); + transition: transform 200ms ease; } swp-profile-drawer.active, @@ -67,17 +86,15 @@ swp-drawer-header { display: flex; align-items: center; justify-content: space-between; - padding: var(--spacing-10) var(--spacing-12); + padding: 14px 16px; border-bottom: 1px solid var(--color-border); + background: var(--color-background-alt); flex-shrink: 0; } swp-drawer-title { - display: flex; - align-items: center; - gap: var(--spacing-2); - font-size: var(--font-size-lg); - font-weight: var(--font-weight-semibold); + font-size: 14px; + font-weight: 600; color: var(--color-text); } @@ -94,11 +111,11 @@ swp-drawer-close { align-items: center; justify-content: center; border: none; - background: var(--color-background-alt); - border-radius: var(--radius-md); + background: transparent; + border-radius: 6px; cursor: pointer; color: var(--color-text-secondary); - transition: all var(--transition-fast); + transition: all 150ms ease; } swp-drawer-close:hover { @@ -110,6 +127,12 @@ swp-drawer-close i { font-size: 20px; } +swp-drawer-header-actions { + display: flex; + align-items: center; + gap: var(--spacing-3); +} + /* =========================================== DRAWER CONTENT / BODY =========================================== */ @@ -117,10 +140,10 @@ swp-drawer-content, swp-drawer-body { flex: 1; overflow-y: auto; - padding: var(--spacing-8); + padding: 20px 16px; display: flex; flex-direction: column; - gap: var(--spacing-5); + gap: 24px; } swp-drawer-divider { @@ -130,134 +153,142 @@ swp-drawer-divider { } /* =========================================== - PROFILE SECTION + PROFILE SECTION (Centered in drawer) =========================================== */ -swp-profile-section { +swp-drawer-profile { display: flex; flex-direction: column; align-items: center; text-align: center; - padding: var(--spacing-4) 0; + padding-bottom: 20px; + border-bottom: 1px solid var(--color-border); } -swp-profile-avatar-large { - width: 64px; - height: 64px; - border-radius: var(--radius-full); - background: var(--color-teal); +swp-drawer-avatar { + width: 72px; + height: 72px; + border-radius: 50%; + background: var(--color-purple); color: white; - font-size: var(--font-size-3xl); - font-weight: var(--font-weight-semibold); display: flex; align-items: center; justify-content: center; - margin-bottom: var(--spacing-3); + font-size: 24px; + font-weight: 600; + margin-bottom: 12px; } -swp-profile-name-large { - font-size: var(--font-size-lg); - font-weight: var(--font-weight-semibold); +swp-drawer-name { + font-size: 18px; + font-weight: 600; color: var(--color-text); - margin-bottom: var(--spacing-1); + margin-bottom: 2px; } -swp-profile-email { - font-size: var(--font-size-sm); +swp-drawer-role { + font-size: 13px; color: var(--color-text-secondary); + margin-bottom: 8px; +} + +swp-drawer-email { + font-size: 13px; + color: var(--color-teal); } /* =========================================== - DRAWER MENU + DRAWER SECTIONS =========================================== */ -swp-drawer-menu { +swp-drawer-section { display: flex; flex-direction: column; - gap: var(--spacing-1); + gap: 8px; } -swp-drawer-menu-item { +swp-drawer-section-label { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + color: var(--color-text-secondary); + margin-bottom: 4px; +} + +/* =========================================== + DRAWER ITEMS (Menu items) + =========================================== */ +swp-drawer-item { display: flex; align-items: center; - gap: var(--spacing-3); - padding: var(--spacing-3) var(--spacing-3); - border-radius: var(--border-radius); + gap: 12px; + padding: 12px; + background: var(--color-background-alt); + border-radius: 8px; cursor: pointer; - transition: background var(--transition-fast); - color: var(--color-text); + transition: background 150ms ease; } -swp-drawer-menu-item:hover { +swp-drawer-item:hover { background: var(--color-background-hover); } -swp-drawer-menu-item i { +swp-drawer-item i { font-size: 20px; color: var(--color-text-secondary); } +swp-drawer-item-text { + flex: 1; + font-size: 14px; + color: var(--color-text); +} + +swp-drawer-item-hint { + font-size: 12px; + color: var(--color-text-secondary); +} + /* =========================================== - THEME TOGGLE + THEME TOGGLE (Two icon buttons) =========================================== */ swp-theme-toggle { display: flex; - align-items: center; - justify-content: space-between; - padding: var(--spacing-3); - border-radius: var(--border-radius); - background: var(--color-background); + border: 1px solid var(--color-border); + border-radius: 8px; + overflow: hidden; } -swp-theme-label { +swp-theme-option { + flex: 1; display: flex; align-items: center; - gap: var(--spacing-3); - color: var(--color-text); + justify-content: center; + padding: 12px; + background: var(--color-background-alt); + cursor: pointer; + transition: all 150ms ease; } -swp-theme-label i { +swp-theme-option:first-child { + border-right: 1px solid var(--color-border); +} + +swp-theme-option i { font-size: 20px; color: var(--color-text-secondary); + transition: color 150ms ease; } -swp-toggle-switch { - position: relative; - width: 44px; - height: 24px; +swp-theme-option:hover { + background: var(--color-background-hover); } -swp-toggle-switch input { - opacity: 0; - width: 0; - height: 0; +swp-theme-option.active { + background: color-mix(in srgb, var(--color-teal) 12%, transparent); } -swp-toggle-track { - position: absolute; - cursor: pointer; - inset: 0; - background: var(--color-border); - border-radius: 12px; - transition: background var(--transition-fast); -} - -swp-toggle-track::before { - content: ''; - position: absolute; - width: 18px; - height: 18px; - left: 3px; - bottom: 3px; - background: white; - border-radius: 50%; - transition: transform var(--transition-fast); -} - -swp-toggle-switch input:checked + swp-toggle-track { - background: var(--color-teal); -} - -swp-toggle-switch input:checked + swp-toggle-track::before { - transform: translateX(20px); +swp-theme-option.active i { + color: var(--color-teal); } /* =========================================== @@ -265,9 +296,9 @@ swp-toggle-switch input:checked + swp-toggle-track::before { =========================================== */ swp-drawer-footer { display: flex; - justify-content: flex-end; - gap: var(--spacing-3); - padding: var(--spacing-4) var(--spacing-5); + flex-direction: column; + gap: 8px; + padding: 16px; border-top: 1px solid var(--color-border); flex-shrink: 0; } @@ -276,28 +307,485 @@ swp-drawer-action { display: flex; align-items: center; justify-content: center; - gap: var(--spacing-2); - width: 100%; - padding: var(--spacing-3); - font-size: var(--font-size-base); + gap: 8px; + padding: 12px; + font-size: 14px; font-family: var(--font-family); color: var(--color-text-secondary); background: transparent; border: 1px solid var(--color-border); - border-radius: var(--border-radius); + border-radius: 8px; cursor: pointer; - transition: all var(--transition-fast); - - & i { - font-size: 18px; - } - - &:hover { - background: var(--color-background-hover); - } - - &.logout:hover { - color: var(--color-red); - border-color: var(--color-red); - } + transition: all 150ms ease; +} + +swp-drawer-action i { + font-size: 18px; +} + +swp-drawer-action:hover { + background: var(--color-background-hover); +} + +swp-drawer-action.logout:hover { + color: var(--color-red); + border-color: var(--color-red); + background: var(--bg-red-subtle); +} + +/* =========================================== + MARK ALL READ BUTTON (Notification drawer) + =========================================== */ +swp-mark-read-btn { + font-size: 12px; + color: var(--color-teal); + cursor: pointer; + transition: opacity 150ms ease; +} + +swp-mark-read-btn:hover { + opacity: 0.8; +} + +/* =========================================== + TODO DRAWER (slides out to the left of profile drawer) + =========================================== */ +swp-todo-drawer { + position: fixed; + top: 0; + right: 320px; /* Position to the left of profile drawer (320px wide) */ + bottom: 0; + width: 380px; + background: var(--color-surface); + border-left: 1px solid var(--color-border); + box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1); + transform: translateX(100%); + opacity: 0; + pointer-events: none; + transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1), + opacity 200ms ease; + display: flex; + flex-direction: column; + z-index: 999; /* Below profile drawer */ +} + +swp-todo-drawer.active { + transform: translateX(0); + opacity: 1; + pointer-events: auto; +} + +swp-todo-drawer swp-drawer-header { + display: flex; + align-items: center; + gap: 8px; + padding: 14px 16px; + border-bottom: 1px solid var(--color-border); + background: var(--color-background-alt); +} + +swp-todo-drawer swp-drawer-title { + flex: 1; +} + +swp-drawer-back { + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + border: none; + background: transparent; + border-radius: 6px; + cursor: pointer; + color: var(--color-text-secondary); + transition: all 150ms ease; +} + +swp-drawer-back:hover { + background: var(--color-background-hover); + color: var(--color-text); +} + +swp-drawer-back i { + font-size: 20px; +} + +/* Header button with text */ +swp-todo-drawer swp-btn.primary.small { + display: flex; + align-items: center; + gap: 6px; + padding: 6px 12px; + font-size: 13px; + font-family: var(--font-family); + font-weight: 500; + background: var(--color-teal); + color: white; + border: none; + border-radius: 6px; + cursor: pointer; + transition: all 150ms ease; +} + +swp-todo-drawer swp-btn.primary.small:hover { + background: color-mix(in srgb, var(--color-teal) 85%, black); +} + +swp-todo-drawer swp-btn.primary.small i { + font-size: 16px; +} + +swp-todo-drawer swp-drawer-content { + flex: 1; + overflow-y: auto; + padding: 16px; + display: flex; + flex-direction: column; + gap: 20px; +} + +swp-todo-section { + display: flex; + flex-direction: column; + gap: 8px; +} + +swp-todo-section-header { + display: flex; + align-items: center; + gap: 8px; + padding: 4px 0; + cursor: pointer; +} + +swp-todo-section-header i { + font-size: 14px; + color: var(--color-text-secondary); + transition: transform 200ms ease; +} + +swp-todo-section.collapsed swp-todo-section-header i { + transform: rotate(-90deg); +} + +swp-todo-section.collapsed swp-todo-items { + display: none; +} + +swp-todo-section-title { + font-size: 12px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + color: var(--color-text-secondary); +} + +swp-todo-section-count { + font-size: 11px; + padding: 2px 6px; + background: var(--color-background); + border-radius: 10px; + color: var(--color-text-secondary); +} + +swp-todo-items { + display: flex; + flex-direction: column; + gap: 6px; +} + +swp-todo-item { + display: flex; + align-items: flex-start; + gap: 12px; + padding: 10px 12px; + background: var(--color-background-alt); + border-radius: 8px; + cursor: pointer; + transition: all 150ms ease; +} + +swp-todo-item:hover { + background: var(--color-background-hover); +} + +swp-todo-item[data-completed="true"] { + opacity: 0.6; +} + +swp-todo-item[data-completed="true"] swp-todo-title { + text-decoration: line-through; + color: var(--color-text-secondary); +} + +swp-todo-checkbox { + width: 20px; + height: 20px; + border: 2px solid var(--color-border); + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + margin-top: 1px; + transition: all 150ms ease; +} + +swp-todo-checkbox i { + font-size: 14px; + color: white; + opacity: 0; +} + +swp-todo-item[data-completed="true"] swp-todo-checkbox { + background: var(--color-teal); + border-color: var(--color-teal); +} + +swp-todo-item[data-completed="true"] swp-todo-checkbox i { + opacity: 1; +} + +swp-todo-content { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + gap: 4px; +} + +swp-todo-title { + font-size: 14px; + color: var(--color-text); +} + +swp-todo-meta { + display: flex; + align-items: center; + gap: 8px; + font-size: 12px; + color: var(--color-text-secondary); +} + +swp-todo-meta i { + font-size: 14px; +} + +swp-todo-time { + display: flex; + align-items: center; + gap: 4px; +} + +swp-todo-priority { + display: flex; + align-items: center; + gap: 4px; +} + +swp-todo-priority.high { + color: var(--color-red); +} + +swp-todo-priority.low { + color: var(--color-text-secondary); + opacity: 0.7; +} + +swp-todo-date { + display: flex; + align-items: center; + gap: 4px; +} + +/* =========================================== + NEW TODO DRAWER (slides out to the left of todo drawer) + =========================================== */ +swp-new-todo-drawer { + position: fixed; + top: 0; + right: 700px; /* 320px (profile) + 380px (todo) */ + bottom: 0; + width: 340px; + background: var(--color-surface); + border-left: 1px solid var(--color-border); + box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1); + transform: translateX(100%); + opacity: 0; + pointer-events: none; + transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1), + opacity 200ms ease; + display: flex; + flex-direction: column; + z-index: 998; /* Below todo drawer */ +} + +swp-new-todo-drawer.active { + transform: translateX(0); + opacity: 1; + pointer-events: auto; +} + +swp-new-todo-drawer swp-drawer-header { + display: flex; + align-items: center; + gap: 8px; + padding: 14px 16px; + border-bottom: 1px solid var(--color-border); + background: var(--color-background-alt); +} + +swp-new-todo-drawer swp-drawer-title { + flex: 1; +} + +swp-new-todo-drawer swp-drawer-content { + flex: 1; + overflow-y: auto; + padding: 16px; + display: flex; + flex-direction: column; + gap: 16px; +} + +swp-new-todo-drawer swp-drawer-footer { + flex-direction: row; + justify-content: flex-end; +} + +/* Form Elements */ +swp-new-todo-drawer swp-section-label { + display: block; + font-size: 11px; + font-weight: 400; + color: var(--color-text-secondary); + text-transform: uppercase; + letter-spacing: 0.5px; + margin-bottom: 8px; +} + +swp-new-todo-drawer swp-form-field { + display: block; +} + +swp-new-todo-drawer swp-form-row { + display: flex; + gap: 12px; +} + +swp-new-todo-drawer swp-form-row swp-form-field { + flex: 1; +} + +swp-new-todo-drawer input[type="text"], +swp-new-todo-drawer input[type="date"], +swp-new-todo-drawer input[type="time"], +swp-new-todo-drawer select, +swp-new-todo-drawer textarea { + width: 100%; + padding: 10px 12px; + font-size: 14px; + font-family: var(--font-family); + color: var(--color-text); + background: var(--color-background-alt); + border: 1px solid var(--color-border); + border-radius: 4px; + outline: none; + transition: border-color 150ms ease; +} + +swp-new-todo-drawer input:focus, +swp-new-todo-drawer select:focus, +swp-new-todo-drawer textarea:focus { + border-color: var(--color-teal); +} + +swp-new-todo-drawer input::placeholder, +swp-new-todo-drawer textarea::placeholder { + color: var(--color-text-secondary); +} + +swp-new-todo-drawer select { + cursor: pointer; + appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 256 256'%3E%3Cpath fill='%23666' d='M213.66 101.66l-80 80a8 8 0 0 1-11.32 0l-80-80a8 8 0 0 1 11.32-11.32L128 164.69l74.34-74.35a8 8 0 0 1 11.32 11.32Z'/%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 10px center; + padding-right: 36px; +} + +swp-new-todo-drawer textarea { + resize: none; + min-height: 80px; +} + +/* Visibility toggle */ +swp-visibility-toggle { + display: flex; + border: 1px solid var(--color-border); + border-radius: 4px; + overflow: hidden; +} + +swp-visibility-option { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + gap: 6px; + padding: 10px 12px; + font-size: 13px; + color: var(--color-text-secondary); + background: var(--color-background-alt); + cursor: pointer; + transition: all 150ms ease; + border: none; +} + +swp-visibility-option:first-child { + border-right: 1px solid var(--color-border); +} + +swp-visibility-option i { + font-size: 16px; +} + +swp-visibility-option:hover { + background: var(--color-background-hover); +} + +swp-visibility-option.active { + background: color-mix(in srgb, var(--color-teal) 12%, transparent); + color: var(--color-teal); +} + +/* Footer buttons */ +swp-new-todo-drawer swp-btn { + padding: 10px 16px; + font-size: 14px; + font-family: var(--font-family); + border-radius: 6px; + cursor: pointer; + transition: all 150ms ease; +} + +swp-new-todo-drawer swp-btn.secondary { + background: transparent; + border: 1px solid var(--color-border); + color: var(--color-text-secondary); +} + +swp-new-todo-drawer swp-btn.secondary:hover { + background: var(--color-background-hover); +} + +swp-new-todo-drawer swp-btn.primary { + background: var(--color-teal); + border: 1px solid var(--color-teal); + color: white; +} + +swp-new-todo-drawer swp-btn.primary:hover { + background: color-mix(in srgb, var(--color-teal) 85%, black); } diff --git a/PlanTempus.Application/wwwroot/css/reports.css b/PlanTempus.Application/wwwroot/css/reports.css new file mode 100644 index 0000000..9d22215 --- /dev/null +++ b/PlanTempus.Application/wwwroot/css/reports.css @@ -0,0 +1,395 @@ +/** + * Reports - Statistik og Rapporter + * + * Feature-specific styling for reports pages. + * Reuses: swp-stats-row (stats.css), swp-stat-card (stats.css), + * swp-tab-bar (tabs.css), swp-data-table (components.css) + */ + +/* =========================================== + CHARTS GRID (2-column layout) + =========================================== */ +swp-charts-grid { + display: grid; + grid-template-columns: 2fr 1fr; + gap: var(--card-gap); + margin-bottom: var(--section-gap); +} + +@media (max-width: 1200px) { + swp-charts-grid { + grid-template-columns: 1fr; + } +} + +/* =========================================== + CHART CARD + =========================================== */ +swp-chart-card { + display: block; + background: var(--color-surface); + border-radius: var(--border-radius-lg); + border: 1px solid var(--color-border); + overflow: hidden; +} + +swp-chart-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--spacing-6) var(--card-padding); + border-bottom: 1px solid var(--color-border); +} + +swp-chart-title { + font-size: var(--font-size-base); + font-weight: var(--font-weight-semibold); + color: var(--color-text); +} + +swp-chart-hint { + font-size: var(--font-size-sm); + color: var(--color-text-secondary); +} + +swp-chart-container { + display: block; + height: 240px; + position: relative; +} + +/* =========================================== + FILTER BAR + =========================================== */ +swp-filter-bar { + display: flex; + align-items: center; + gap: var(--spacing-6); + padding: var(--spacing-6) var(--card-padding); + background: var(--color-surface); + border-radius: var(--border-radius-lg); + margin-bottom: var(--section-gap); + flex-wrap: wrap; +} + +swp-search-input { + display: flex; + align-items: center; + gap: var(--spacing-3); + padding: var(--spacing-3) var(--spacing-4); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + background: var(--color-surface); + flex: 1; + max-width: 350px; + + & i { + color: var(--color-text-secondary); + font-size: 18px; + } + + & input { + border: none; + outline: none; + font-size: var(--font-size-md); + font-family: var(--font-family); + width: 100%; + background: transparent; + color: var(--color-text); + + &::placeholder { + color: var(--color-text-muted); + } + } +} + +swp-filter-group { + display: flex; + align-items: center; + gap: var(--spacing-3); +} + +swp-filter-label { + font-size: var(--font-size-sm); + color: var(--color-text-secondary); +} + +swp-filter-bar select, +swp-filter-bar input[type="date"] { + padding: var(--spacing-3) var(--spacing-4); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + font-size: var(--font-size-md); + font-family: var(--font-family); + background: var(--color-surface); + color: var(--color-text); + cursor: pointer; +} + +swp-filter-bar input[type="date"] { + font-family: var(--font-mono); +} + +/* =========================================== + SALES TABLE - Grid columns + =========================================== */ +swp-card.sales-table { + padding: 0; + overflow: hidden; +} + +swp-card.sales-table swp-data-table { + grid-template-columns: 100px 140px minmax(120px, 1fr) 120px minmax(140px, 1.2fr) 100px 120px 100px 40px; +} + +swp-card.sales-table swp-data-table-header { + padding: var(--spacing-4) var(--card-padding); +} + +swp-card.sales-table swp-data-table-row { + padding: var(--spacing-5) var(--card-padding); + cursor: pointer; +} + +/* =========================================== + INVOICE CELL + =========================================== */ +swp-invoice-cell { + font-family: var(--font-mono); + font-weight: var(--font-weight-medium); + font-size: var(--font-size-md); + color: var(--color-teal); +} + +/* =========================================== + DATETIME CELL + =========================================== */ +swp-datetime-cell { + display: flex; + flex-direction: column; + gap: 2px; + + & .date { + font-size: var(--font-size-md); + color: var(--color-text); + } + + & .time { + font-size: var(--font-size-sm); + color: var(--color-text-secondary); + font-family: var(--font-mono); + } +} + +/* =========================================== + CUSTOMER CELL + =========================================== */ +swp-customer-cell { + display: flex; + flex-direction: column; + gap: 2px; + + & .name { + font-weight: var(--font-weight-medium); + color: var(--color-text); + } + + & .phone { + font-size: var(--font-size-sm); + color: var(--color-text-secondary); + font-family: var(--font-mono); + } +} + +/* =========================================== + SERVICES CELL + =========================================== */ +swp-services-cell { + display: flex; + flex-direction: column; + gap: 2px; + + & .main { + font-size: var(--font-size-md); + color: var(--color-text); + } + + & .more { + font-size: var(--font-size-sm); + color: var(--color-text-secondary); + } +} + +/* =========================================== + AMOUNT CELL + =========================================== */ +swp-amount-cell { + font-family: var(--font-mono); + font-weight: var(--font-weight-semibold); + font-size: var(--font-size-base); + text-align: right; + display: block; +} + +/* =========================================== + PAYMENT BADGE + =========================================== */ +swp-payment-badge { + display: inline-flex; + align-items: center; + gap: var(--spacing-2); + padding: var(--spacing-2) var(--spacing-4); + font-size: var(--font-size-sm); + font-weight: var(--font-weight-medium); + border-radius: var(--radius-md); + background: var(--color-background-alt); + color: var(--color-text-secondary); + + & i { + font-size: 14px; + } + + &.card { + background: color-mix(in srgb, var(--color-blue) 12%, transparent); + color: var(--color-blue); + } + + &.cash { + background: color-mix(in srgb, var(--color-green) 12%, transparent); + color: var(--color-green); + } + + &.mobilepay { + background: color-mix(in srgb, var(--color-blue) 12%, transparent); + color: var(--color-blue); + } + + &.invoice { + background: color-mix(in srgb, var(--color-amber) 12%, transparent); + color: var(--color-amber); + } + + &.giftcard { + background: color-mix(in srgb, var(--color-purple) 12%, transparent); + color: var(--color-purple); + } +} + +/* =========================================== + STATUS BADGE ADDITIONS (paid, credited) + =========================================== */ +swp-status-badge.paid { + background: var(--bg-green-strong); + color: var(--color-green); +} + +swp-status-badge.credited { + background: var(--bg-purple-strong); + color: var(--color-purple); +} + +/* =========================================== + ROW ARROW + =========================================== */ +swp-row-arrow { + display: flex; + align-items: center; + justify-content: flex-end; + + & i { + font-size: 18px; + color: var(--color-text-secondary); + transition: transform var(--transition-fast), color var(--transition-fast); + } +} + +swp-data-table-row:hover swp-row-arrow i { + transform: translateX(4px); + color: var(--color-teal); +} + +/* =========================================== + TABLE FOOTER + PAGINATION + =========================================== */ +swp-table-footer { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--spacing-5) var(--card-padding); + background: var(--color-background-alt); + border-top: 1px solid var(--color-border); + font-size: var(--font-size-md); + color: var(--color-text-secondary); +} + +swp-pagination { + display: flex; + align-items: center; + gap: var(--spacing-1); +} + +swp-page-btn { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: var(--radius-md); + font-size: var(--font-size-md); + font-weight: var(--font-weight-medium); + cursor: pointer; + transition: all var(--transition-fast); + background: transparent; + color: var(--color-text-secondary); + border: 1px solid transparent; + + &:hover { + background: var(--color-background-hover); + color: var(--color-text); + } + + &.active { + background: var(--color-teal); + color: white; + border-color: var(--color-teal); + } + + & i { + font-size: 16px; + } +} + +/* =========================================== + RESPONSIVE + =========================================== */ +@media (max-width: 1200px) { + swp-card.sales-table swp-data-table { + grid-template-columns: 100px 130px 1fr 100px 100px 100px 40px; + } + + /* Hide employee and services columns */ + swp-card.sales-table swp-data-table-cell:nth-child(4), + swp-card.sales-table swp-data-table-cell:nth-child(5) { + display: none; + } +} + +@media (max-width: 900px) { + swp-card.sales-table swp-data-table { + grid-template-columns: 100px 1fr 100px 100px 40px; + } + + /* Hide customer column */ + swp-card.sales-table swp-data-table-cell:nth-child(3) { + display: none; + } + + swp-filter-bar { + flex-direction: column; + align-items: stretch; + } + + swp-search-input { + max-width: none; + } +}