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; + } +}