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
+ Betalt
+
+
+
+
+
+ #1846
+
+
+
+ 6. jan 2025
+ 13:15
+
+
+
+
+ Peter Sørensen
+ +45 30 12 34 56
+
+
+ Anna J.
+
+
+ Herreklip
+
+
+ 295 kr
+ MobilePay
+ Betalt
+
+
+
+
+
+ #1845
+
+
+
+ 6. jan 2025
+ 11:45
+
+
+
+
+ Lise Andersen
+ +45 42 56 78 90
+
+
+ Louise P.
+
+
+ Dameklip, Balayage
+ + 2 produkter
+
+
+ 2.350 kr
+ Kort
+ Betalt
+
+
+
+
+
+ #1844
+
+
+
+ 5. jan 2025
+ 16:20
+
+
+
+
+ Thomas Nielsen
+ +45 51 23 45 67
+
+
+ Mikkel H.
+
+
+ Herreklip, Skægtrim
+
+
+ 395 kr
+ Kontant
+ Betalt
+
+
+
+
+
+ #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
+ Betalt
+
+
+
+
+
+ #1840
+
+
+
+ 4. jan 2025
+ 10:00
+
+
+
+
+ Mads Christensen
+ +45 93 45 67 89
+
+
+ Anna J.
+
+
+ Herreklip
+
+
+ 275 kr
+ MobilePay
+ Betalt
+
+
+
+
+ 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
+
+
+
+
+
+
+
+
+
+
+
+ Udført
+ 3
+
+
+
+
+
+
+
+ Send faktura til kunde
+
+
+
+
+
+
+
+ Opdater åbningstider på Google
+
+
+
+
+
+
+
+ Bestil nye håndklæder
+
+
+
+
+
+
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;
+ }
+}