Adds dark mode support and responsive design

Introduces system and manual dark mode toggles across multiple pages
Implements consistent theming with CSS variables
Enhances user experience with responsive layout adjustments
This commit is contained in:
Janus C. H. Knudsen 2025-12-30 00:36:01 +01:00
parent da5d9df274
commit 1718d4d5a1
4 changed files with 2977 additions and 0 deletions

790
wwwroot/poc-layout.html Normal file
View file

@ -0,0 +1,790 @@
<!DOCTYPE html>
<html lang="da">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Salon OS</title>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
/* ==========================================
FONT FACE (Poppins)
========================================== */
@font-face {
font-family: 'Poppins';
src: url('fonts/Poppins-Regular.woff') format('woff');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Poppins';
src: url('fonts/Poppins-Medium.woff') format('woff');
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Poppins';
src: url('fonts/Poppins-SemiBold.woff') format('woff');
font-weight: 600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Poppins';
src: url('fonts/Poppins-Bold.woff') format('woff');
font-weight: 700;
font-style: normal;
font-display: swap;
}
/* ==========================================
CSS VARIABLES (Design System)
========================================== */
:root {
--color-surface: #fff;
--color-background: #f5f5f5;
--color-background-hover: #f0f0f0;
--color-background-alt: #fafafa;
--color-border: #e0e0e0;
--color-text: #333;
--color-text-secondary: #666;
--color-teal: #00897b;
--color-blue: #1976d2;
--color-red: #e53935;
--color-amber: #f59e0b;
--color-purple: #8b5cf6;
--color-green: #43a047;
--font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--font-mono: 'JetBrains Mono', monospace;
/* Layout */
--side-menu-width: 240px;
--topbar-height: 56px;
}
/* Dark mode - system preference */
@media (prefers-color-scheme: dark) {
:root:not(.light-mode) {
--color-surface: #1e1e1e;
--color-background: #121212;
--color-background-hover: #2a2a2a;
--color-background-alt: #1a1a1a;
--color-border: #333;
--color-text: #e0e0e0;
--color-text-secondary: #999;
--color-teal: #26a69a;
--color-blue: #42a5f5;
--color-red: #ef5350;
--color-amber: #ffb74d;
--color-purple: #a78bfa;
--color-green: #66bb6a;
}
}
/* Manual dark mode override */
:root.dark-mode {
--color-surface: #1e1e1e;
--color-background: #121212;
--color-background-hover: #2a2a2a;
--color-background-alt: #1a1a1a;
--color-border: #333;
--color-text: #e0e0e0;
--color-text-secondary: #999;
--color-teal: #26a69a;
--color-blue: #42a5f5;
--color-red: #ef5350;
--color-amber: #ffb74d;
--color-purple: #a78bfa;
--color-green: #66bb6a;
}
/* ==========================================
RESET & BASE
========================================== */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html, body {
height: 100%;
}
body {
font-family: var(--font-family);
font-size: 14px;
color: var(--color-text);
background: var(--color-background);
line-height: 1.5;
}
a {
color: var(--color-teal);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* ==========================================
APP LAYOUT
========================================== */
swp-app-layout {
display: grid;
grid-template-columns: var(--side-menu-width) 1fr;
grid-template-rows: var(--topbar-height) 1fr;
height: 100vh;
}
/* ==========================================
SIDE MENU
========================================== */
swp-side-menu {
grid-row: 1 / -1;
display: flex;
flex-direction: column;
background: var(--color-surface);
border-right: 1px solid var(--color-border);
overflow-y: auto;
}
swp-side-menu-header {
display: flex;
align-items: center;
gap: 10px;
height: var(--topbar-height);
padding: 0 16px;
border-bottom: 1px solid var(--color-border);
}
swp-side-menu-header svg {
width: 28px;
height: 28px;
fill: var(--color-teal);
}
swp-side-menu-logo {
font-size: 16px;
font-weight: 600;
color: var(--color-text);
}
swp-side-menu-nav {
flex: 1;
padding: 12px 0;
overflow-y: auto;
}
swp-side-menu-group {
display: block;
margin-bottom: 8px;
}
swp-side-menu-label {
display: block;
padding: 8px 16px 6px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--color-text-secondary);
}
swp-side-menu-item {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 16px;
color: var(--color-text);
cursor: pointer;
transition: all 150ms ease;
border-left: 3px solid transparent;
text-decoration: none;
}
swp-side-menu-item:hover {
background: var(--color-background-hover);
text-decoration: none;
}
swp-side-menu-item[data-active="true"] {
background: color-mix(in srgb, var(--color-teal) 10%, transparent);
border-left-color: var(--color-teal);
color: var(--color-teal);
font-weight: 500;
}
swp-side-menu-item svg {
width: 20px;
height: 20px;
fill: currentColor;
flex-shrink: 0;
}
swp-side-menu-footer {
display: flex;
flex-direction: column;
gap: 8px;
padding: 12px 16px;
border-top: 1px solid var(--color-border);
margin-top: auto;
}
swp-side-menu-user {
display: flex;
align-items: center;
gap: 10px;
padding: 8px;
border-radius: 6px;
background: var(--color-background-alt);
}
swp-side-menu-user-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
background: var(--color-teal);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: 600;
}
swp-side-menu-user-info {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
}
swp-side-menu-user-name {
font-size: 13px;
font-weight: 500;
color: var(--color-text);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
swp-side-menu-user-role {
font-size: 11px;
color: var(--color-text-secondary);
}
swp-side-menu-logout {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
padding: 8px;
font-size: 13px;
color: var(--color-text-secondary);
background: transparent;
border: 1px solid var(--color-border);
border-radius: 6px;
cursor: pointer;
transition: all 150ms ease;
font-family: var(--font-family);
}
swp-side-menu-logout:hover {
background: var(--color-background-hover);
color: var(--color-red);
border-color: var(--color-red);
}
swp-side-menu-logout svg {
width: 16px;
height: 16px;
fill: currentColor;
}
/* ==========================================
TOP BAR
========================================== */
swp-app-topbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
background: var(--color-surface);
border-bottom: 1px solid var(--color-border);
}
swp-topbar-search {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 14px;
background: var(--color-background);
border: 1px solid var(--color-border);
border-radius: 6px;
width: 320px;
transition: border-color 150ms ease;
}
swp-topbar-search:focus-within {
border-color: var(--color-teal);
}
swp-topbar-search svg {
width: 18px;
height: 18px;
fill: var(--color-text-secondary);
flex-shrink: 0;
}
swp-topbar-search input {
flex: 1;
border: none;
outline: none;
background: transparent;
font-size: 13px;
font-family: var(--font-family);
color: var(--color-text);
}
swp-topbar-search input::placeholder {
color: var(--color-text-secondary);
}
swp-topbar-search kbd {
font-size: 11px;
font-family: var(--font-mono);
padding: 2px 6px;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 4px;
color: var(--color-text-secondary);
}
swp-topbar-actions {
display: flex;
align-items: center;
gap: 8px;
}
swp-topbar-btn {
display: flex;
align-items: center;
justify-content: center;
width: 38px;
height: 38px;
border: none;
background: transparent;
border-radius: 6px;
cursor: pointer;
transition: background 150ms ease;
position: relative;
}
swp-topbar-btn:hover {
background: var(--color-background-hover);
}
swp-topbar-btn svg {
width: 20px;
height: 20px;
fill: var(--color-text-secondary);
}
swp-notification-badge {
position: absolute;
top: 6px;
right: 6px;
min-width: 16px;
height: 16px;
padding: 0 4px;
font-size: 10px;
font-weight: 600;
background: var(--color-red);
color: white;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
}
swp-topbar-divider {
width: 1px;
height: 24px;
background: var(--color-border);
margin: 0 8px;
}
swp-topbar-profile {
display: flex;
align-items: center;
gap: 10px;
padding: 6px 12px 6px 6px;
background: transparent;
border: 1px solid transparent;
border-radius: 6px;
cursor: pointer;
transition: all 150ms ease;
position: relative;
}
swp-topbar-profile:hover {
background: var(--color-background-hover);
border-color: var(--color-border);
}
swp-profile-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
background: var(--color-purple);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: 600;
}
swp-profile-info {
display: flex;
flex-direction: column;
text-align: left;
}
swp-profile-name {
font-size: 13px;
font-weight: 500;
color: var(--color-text);
}
swp-profile-role {
font-size: 11px;
color: var(--color-text-secondary);
}
swp-topbar-profile svg {
width: 16px;
height: 16px;
fill: var(--color-text-secondary);
transition: transform 200ms ease;
}
swp-topbar-profile[aria-expanded="true"] svg {
transform: rotate(180deg);
}
/* Profile Dropdown */
swp-profile-dropdown {
position: absolute;
top: calc(100% + 8px);
right: 0;
width: 200px;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
opacity: 0;
visibility: hidden;
transform: translateY(-8px);
transition: all 200ms ease;
z-index: 100;
}
swp-topbar-profile[aria-expanded="true"] swp-profile-dropdown {
opacity: 1;
visibility: visible;
transform: translateY(0);
}
swp-profile-dropdown-item {
display: flex;
align-items: center;
gap: 10px;
padding: 10px 14px;
font-size: 13px;
color: var(--color-text);
cursor: pointer;
transition: background 150ms ease;
}
swp-profile-dropdown-item:first-child {
border-radius: 7px 7px 0 0;
}
swp-profile-dropdown-item:last-child {
border-radius: 0 0 7px 7px;
}
swp-profile-dropdown-item:hover {
background: var(--color-background-hover);
}
swp-profile-dropdown-item.danger {
color: var(--color-red);
}
swp-profile-dropdown-item svg {
width: 18px;
height: 18px;
fill: currentColor;
}
swp-profile-dropdown-divider {
height: 1px;
background: var(--color-border);
margin: 4px 0;
}
/* ==========================================
MAIN CONTENT
========================================== */
swp-main-content {
display: block;
overflow-y: auto;
padding: 24px;
background: var(--color-background);
}
/* Demo placeholder */
swp-content-placeholder {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
min-height: 400px;
background: var(--color-surface);
border: 2px dashed var(--color-border);
border-radius: 12px;
color: var(--color-text-secondary);
}
swp-content-placeholder svg {
width: 64px;
height: 64px;
fill: var(--color-border);
margin-bottom: 16px;
}
swp-content-placeholder-title {
font-size: 18px;
font-weight: 600;
color: var(--color-text);
margin-bottom: 8px;
}
swp-content-placeholder-text {
font-size: 14px;
text-align: center;
max-width: 400px;
}
</style>
</head>
<body>
<swp-app-layout>
<!-- Side Menu -->
<swp-side-menu>
<swp-side-menu-header>
<svg viewBox="0 0 24 24"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm-7 14c-3.31 0-6-2.69-6-6s2.69-6 6-6 6 2.69 6 6-2.69 6-6 6zm0-10c-2.21 0-4 1.79-4 4s1.79 4 4 4 4-1.79 4-4-1.79-4-4-4z"/></svg>
<swp-side-menu-logo>Salon OS</swp-side-menu-logo>
</swp-side-menu-header>
<swp-side-menu-nav>
<!-- DASHBOARD -->
<swp-side-menu-group>
<swp-side-menu-label>Dashboard</swp-side-menu-label>
<swp-side-menu-item data-active="true">
<svg viewBox="0 0 24 24"><path d="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z"/></svg>
Dashboard
</swp-side-menu-item>
<swp-side-menu-item onclick="location.href='poc-calendar.html'">
<svg viewBox="0 0 24 24"><path d="M19 3h-1V1h-2v2H8V1H6v2H5c-1.11 0-1.99.9-1.99 2L3 19c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zm0 16H5V8h14v11zM9 10H7v2h2v-2zm4 0h-2v2h2v-2zm4 0h-2v2h2v-2zm-8 4H7v2h2v-2zm4 0h-2v2h2v-2zm4 0h-2v2h2v-2z"/></svg>
Kalender
</swp-side-menu-item>
<swp-side-menu-item>
<svg viewBox="0 0 24 24"><path d="M17 2H7c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h10c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 18H7V4h10v16zm-4.5-1c.83 0 1.5-.67 1.5-1.5s-.67-1.5-1.5-1.5-1.5.67-1.5 1.5.67 1.5 1.5 1.5z"/></svg>
Kasse
</swp-side-menu-item>
</swp-side-menu-group>
<!-- DATA -->
<swp-side-menu-group>
<swp-side-menu-label>Data</swp-side-menu-label>
<swp-side-menu-item onclick="location.href='poc-produkter.html'">
<svg viewBox="0 0 24 24"><path d="M20 6h-4V4c0-1.11-.89-2-2-2h-4c-1.11 0-2 .89-2 2v2H4c-1.11 0-1.99.89-1.99 2L2 19c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V8c0-1.11-.89-2-2-2zm-6 0h-4V4h4v2z"/></svg>
Produkter & Lager
</swp-side-menu-item>
<swp-side-menu-item onclick="location.href='poc-leverandoerer.html'">
<svg viewBox="0 0 24 24"><path d="M20 8h-3V4H3c-1.1 0-2 .9-2 2v11h2c0 1.66 1.34 3 3 3s3-1.34 3-3h6c0 1.66 1.34 3 3 3s3-1.34 3-3h2v-5l-3-4zM6 18.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5zm13.5-9l1.96 2.5H17V9.5h2.5zm-1.5 9c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5z"/></svg>
Leverandører
</swp-side-menu-item>
<swp-side-menu-item>
<svg viewBox="0 0 24 24"><path d="M16 11c1.66 0 2.99-1.34 2.99-3S17.66 5 16 5c-1.66 0-3 1.34-3 3s1.34 3 3 3zm-8 0c1.66 0 2.99-1.34 2.99-3S9.66 5 8 5C6.34 5 5 6.34 5 8s1.34 3 3 3zm0 2c-2.33 0-7 1.17-7 3.5V19h14v-2.5c0-2.33-4.67-3.5-7-3.5zm8 0c-.29 0-.62.02-.97.05 1.16.84 1.97 1.97 1.97 3.45V19h6v-2.5c0-2.33-4.67-3.5-7-3.5z"/></svg>
Kunder
</swp-side-menu-item>
<swp-side-menu-item onclick="location.href='poc-employee.html'">
<svg viewBox="0 0 24 24"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg>
Medarbejdere
</swp-side-menu-item>
</swp-side-menu-group>
<!-- ANALYSE -->
<swp-side-menu-group>
<swp-side-menu-label>Analyse</swp-side-menu-label>
<swp-side-menu-item>
<svg viewBox="0 0 24 24"><path d="M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2zM9 17H7v-7h2v7zm4 0h-2V7h2v10zm4 0h-2v-4h2v4z"/></svg>
Statistik & Rapporter
</swp-side-menu-item>
</swp-side-menu-group>
<!-- SYSTEM -->
<swp-side-menu-group>
<swp-side-menu-label>System</swp-side-menu-label>
<swp-side-menu-item>
<svg viewBox="0 0 24 24"><path d="M19.14 12.94c.04-.31.06-.63.06-.94 0-.31-.02-.63-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/></svg>
Indstillinger
</swp-side-menu-item>
<swp-side-menu-item>
<svg viewBox="0 0 24 24"><path d="M20 4H4c-1.11 0-1.99.89-1.99 2L2 18c0 1.11.89 2 2 2h16c1.11 0 2-.89 2-2V6c0-1.11-.89-2-2-2zm0 14H4v-6h16v6zm0-10H4V6h16v2z"/></svg>
Abonnement & Konto
</swp-side-menu-item>
</swp-side-menu-group>
</swp-side-menu-nav>
<swp-side-menu-footer>
<swp-side-menu-user>
<swp-side-menu-user-avatar>MJ</swp-side-menu-user-avatar>
<swp-side-menu-user-info>
<swp-side-menu-user-name>Maria Jensen</swp-side-menu-user-name>
<swp-side-menu-user-role>Administrator</swp-side-menu-user-role>
</swp-side-menu-user-info>
</swp-side-menu-user>
<swp-side-menu-logout>
<svg viewBox="0 0 24 24"><path d="M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z"/></svg>
Log ud
</swp-side-menu-logout>
</swp-side-menu-footer>
</swp-side-menu>
<!-- Top Bar -->
<swp-app-topbar>
<swp-topbar-search>
<svg viewBox="0 0 24 24"><path d="M15.5 14h-.79l-.28-.27C15.41 12.59 16 11.11 16 9.5 16 5.91 13.09 3 9.5 3S3 5.91 3 9.5 5.91 16 9.5 16c1.61 0 3.09-.59 4.23-1.57l.27.28v.79l5 4.99L20.49 19l-4.99-5zm-6 0C7.01 14 5 11.99 5 9.5S7.01 5 9.5 5 14 7.01 14 9.5 11.99 14 9.5 14z"/></svg>
<input type="text" placeholder="Søg i Salon OS..." id="globalSearch">
<kbd>⌘K</kbd>
</swp-topbar-search>
<swp-topbar-actions>
<!-- Theme Toggle -->
<swp-topbar-btn id="themeToggle" title="Skift tema">
<svg id="sunIcon" viewBox="0 0 24 24"><path d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zM2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1zm18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1zM11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1zm0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1zM5.99 4.58c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0s.39-1.03 0-1.41L5.99 4.58zm12.37 12.37c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0 .39-.39.39-1.03 0-1.41l-1.06-1.06zm1.06-10.96c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06zM7.05 18.36c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06z"/></svg>
<svg id="moonIcon" viewBox="0 0 24 24" style="display:none"><path d="M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9c0-.46-.04-.92-.1-1.36-.98 1.37-2.58 2.26-4.4 2.26-2.98 0-5.4-2.42-5.4-5.4 0-1.81.89-3.42 2.26-4.4-.44-.06-.9-.1-1.36-.1z"/></svg>
</swp-topbar-btn>
<!-- Notifications -->
<swp-topbar-btn id="notificationsBtn" title="Notifikationer">
<svg viewBox="0 0 24 24"><path d="M12 22c1.1 0 2-.9 2-2h-4c0 1.1.89 2 2 2zm6-6v-5c0-3.07-1.64-5.64-4.5-6.32V4c0-.83-.67-1.5-1.5-1.5s-1.5.67-1.5 1.5v.68C7.63 5.36 6 7.92 6 11v5l-2 2v1h16v-1l-2-2z"/></svg>
<swp-notification-badge>3</swp-notification-badge>
</swp-topbar-btn>
<swp-topbar-divider></swp-topbar-divider>
<!-- Profile -->
<swp-topbar-profile id="profileDropdown" aria-expanded="false">
<swp-profile-avatar>MJ</swp-profile-avatar>
<swp-profile-info>
<swp-profile-name>Maria Jensen</swp-profile-name>
<swp-profile-role>Administrator</swp-profile-role>
</swp-profile-info>
<svg viewBox="0 0 24 24"><path d="M7.41 8.59L12 13.17l4.59-4.58L18 10l-6 6-6-6 1.41-1.41z"/></svg>
<swp-profile-dropdown>
<swp-profile-dropdown-item>
<svg viewBox="0 0 24 24"><path d="M12 12c2.21 0 4-1.79 4-4s-1.79-4-4-4-4 1.79-4 4 1.79 4 4 4zm0 2c-2.67 0-8 1.34-8 4v2h16v-2c0-2.66-5.33-4-8-4z"/></svg>
Min profil
</swp-profile-dropdown-item>
<swp-profile-dropdown-item>
<svg viewBox="0 0 24 24"><path d="M19.14 12.94c.04-.31.06-.63.06-.94 0-.31-.02-.63-.06-.94l2.03-1.58c.18-.14.23-.41.12-.61l-1.92-3.32c-.12-.22-.37-.29-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54c-.04-.24-.24-.41-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96c-.22-.08-.47 0-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.04.31-.06.63-.06.94s.02.63.06.94l-2.03 1.58c-.18.14-.23.41-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6c-1.98 0-3.6-1.62-3.6-3.6s1.62-3.6 3.6-3.6 3.6 1.62 3.6 3.6-1.62 3.6-3.6 3.6z"/></svg>
Indstillinger
</swp-profile-dropdown-item>
<swp-profile-dropdown-divider></swp-profile-dropdown-divider>
<swp-profile-dropdown-item class="danger">
<svg viewBox="0 0 24 24"><path d="M17 7l-1.41 1.41L18.17 11H8v2h10.17l-2.58 2.58L17 17l5-5zM4 5h8V3H4c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h8v-2H4V5z"/></svg>
Log ud
</swp-profile-dropdown-item>
</swp-profile-dropdown>
</swp-topbar-profile>
</swp-topbar-actions>
</swp-app-topbar>
<!-- Main Content -->
<swp-main-content>
<swp-content-placeholder>
<svg viewBox="0 0 24 24"><path d="M3 13h8V3H3v10zm0 8h8v-6H3v6zm10 0h8V11h-8v10zm0-18v6h8V3h-8z"/></svg>
<swp-content-placeholder-title>Dashboard</swp-content-placeholder-title>
<swp-content-placeholder-text>
Vælg et menupunkt i venstre side for at navigere til den ønskede sektion.
<br><br>
Prøv f.eks. <a href="poc-produkter.html">Produkter & Lager</a> eller <a href="poc-leverandoerer.html">Leverandører</a>.
</swp-content-placeholder-text>
</swp-content-placeholder>
</swp-main-content>
</swp-app-layout>
<script>
// Theme toggle
const themeToggle = document.getElementById('themeToggle');
const sunIcon = document.getElementById('sunIcon');
const moonIcon = document.getElementById('moonIcon');
const root = document.documentElement;
function updateIcons() {
const isDark = root.classList.contains('dark-mode') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches && !root.classList.contains('light-mode'));
sunIcon.style.display = isDark ? 'none' : 'block';
moonIcon.style.display = isDark ? 'block' : 'none';
}
updateIcons();
themeToggle.addEventListener('click', () => {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (prefersDark) {
root.classList.toggle('light-mode');
root.classList.remove('dark-mode');
} else {
root.classList.toggle('dark-mode');
root.classList.remove('light-mode');
}
updateIcons();
});
// Profile dropdown
const profileDropdown = document.getElementById('profileDropdown');
profileDropdown.addEventListener('click', (e) => {
e.stopPropagation();
const isExpanded = profileDropdown.getAttribute('aria-expanded') === 'true';
profileDropdown.setAttribute('aria-expanded', !isExpanded);
});
// Close dropdown when clicking outside
document.addEventListener('click', () => {
profileDropdown.setAttribute('aria-expanded', 'false');
});
// Keyboard shortcut for search (Cmd/Ctrl + K)
document.addEventListener('keydown', (e) => {
if ((e.metaKey || e.ctrlKey) && e.key === 'k') {
e.preventDefault();
document.getElementById('globalSearch').focus();
}
});
</script>
</body>
</html>

1376
wwwroot/poc-leverandoer.html Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,719 @@
<!DOCTYPE html>
<html lang="da">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Leverandører - Backend</title>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
/* ==========================================
FONT FACE (Poppins)
========================================== */
@font-face {
font-family: 'Poppins';
src: url('fonts/Poppins-Regular.woff') format('woff');
font-weight: 400;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Poppins';
src: url('fonts/Poppins-Medium.woff') format('woff');
font-weight: 500;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Poppins';
src: url('fonts/Poppins-SemiBold.woff') format('woff');
font-weight: 600;
font-style: normal;
font-display: swap;
}
@font-face {
font-family: 'Poppins';
src: url('fonts/Poppins-Bold.woff') format('woff');
font-weight: 700;
font-style: normal;
font-display: swap;
}
/* ==========================================
CSS VARIABLES (Design System)
========================================== */
:root {
--color-surface: #fff;
--color-background: #f5f5f5;
--color-background-hover: #f0f0f0;
--color-background-alt: #fafafa;
--color-border: #e0e0e0;
--color-text: #333;
--color-text-secondary: #666;
--color-teal: #00897b;
--color-blue: #1976d2;
--color-red: #e53935;
--color-amber: #f59e0b;
--color-purple: #8b5cf6;
--color-green: #43a047;
--font-family: 'Poppins', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--font-mono: 'JetBrains Mono', monospace;
}
/* Dark mode - følger system preference ELLER manuel toggle */
@media (prefers-color-scheme: dark) {
:root:not(.light-mode) {
--color-surface: #1e1e1e;
--color-background: #121212;
--color-background-hover: #2a2a2a;
--color-background-alt: #1a1a1a;
--color-border: #333;
--color-text: #e0e0e0;
--color-text-secondary: #999;
--color-teal: #26a69a;
--color-blue: #42a5f5;
--color-red: #ef5350;
--color-amber: #ffb74d;
--color-purple: #a78bfa;
--color-green: #66bb6a;
}
}
/* Manuel dark mode override */
:root.dark-mode {
--color-surface: #1e1e1e;
--color-background: #121212;
--color-background-hover: #2a2a2a;
--color-background-alt: #1a1a1a;
--color-border: #333;
--color-text: #e0e0e0;
--color-text-secondary: #999;
--color-teal: #26a69a;
--color-blue: #42a5f5;
--color-red: #ef5350;
--color-amber: #ffb74d;
--color-purple: #a78bfa;
--color-green: #66bb6a;
}
/* ==========================================
RESET & BASE
========================================== */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: var(--font-family);
font-size: 14px;
color: var(--color-text);
background: var(--color-background);
line-height: 1.5;
}
a {
color: var(--color-teal);
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* ==========================================
TOPBAR
========================================== */
swp-topbar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 24px;
background: var(--color-surface);
border-bottom: 1px solid var(--color-border);
position: sticky;
top: 0;
z-index: 100;
}
swp-topbar-left {
display: flex;
align-items: center;
gap: 16px;
}
swp-page-title {
font-size: 16px;
font-weight: 600;
}
swp-topbar-right {
display: flex;
align-items: center;
gap: 12px;
}
/* ==========================================
LAYOUT
========================================== */
swp-page-container {
display: block;
max-width: 1400px;
margin: 0 auto;
padding: 24px;
}
/* ==========================================
BUTTONS
========================================== */
swp-btn {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 18px;
font-size: 13px;
font-weight: 500;
font-family: var(--font-family);
border-radius: 6px;
cursor: pointer;
transition: all 150ms ease;
border: none;
}
swp-btn svg {
width: 16px;
height: 16px;
fill: currentColor;
}
swp-btn.primary {
background: var(--color-teal);
color: white;
}
swp-btn.primary:hover {
background: #00796b;
}
swp-btn.secondary {
background: var(--color-surface);
border: 1px solid var(--color-border);
color: var(--color-text);
}
swp-btn.secondary:hover {
background: var(--color-background-hover);
}
swp-btn.icon-only {
padding: 10px;
}
swp-btn.icon-only svg {
width: 18px;
height: 18px;
}
/* ==========================================
FILTER BAR
========================================== */
swp-filter-bar {
display: flex;
align-items: center;
gap: 16px;
padding: 16px 20px;
background: var(--color-surface);
border-radius: 8px;
margin-bottom: 20px;
flex-wrap: wrap;
}
swp-search-input {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
border: 1px solid var(--color-border);
border-radius: 6px;
background: var(--color-surface);
}
swp-search-input img {
width: 16px;
height: 16px;
opacity: 0.5;
}
@media (prefers-color-scheme: dark) {
:root:not(.light-mode) swp-search-input img {
filter: invert(1);
}
}
:root.dark-mode swp-search-input img {
filter: invert(1);
}
swp-search-input input {
border: none;
outline: none;
font-size: 13px;
font-family: var(--font-family);
width: 400px;
background: transparent;
color: var(--color-text);
}
swp-filter-spacer {
flex: 1;
}
/* ==========================================
STATS BAR
========================================== */
swp-stats-bar {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
margin-bottom: 20px;
}
swp-stat-card {
display: flex;
flex-direction: column;
gap: 4px;
padding: 16px 20px;
background: var(--color-surface);
border-radius: 8px;
border: 1px solid var(--color-border);
}
swp-stat-card swp-stat-value {
font-size: 22px;
font-weight: 600;
font-family: var(--font-mono);
color: var(--color-text);
}
swp-stat-card swp-stat-label {
font-size: 12px;
color: var(--color-text-secondary);
}
swp-stat-card.highlight swp-stat-value {
color: var(--color-teal);
}
swp-stat-card.warning swp-stat-value {
color: var(--color-amber);
}
/* ==========================================
TABLE
========================================== */
swp-table {
display: block;
background: var(--color-surface);
border-radius: 8px;
overflow: hidden;
border: 1px solid var(--color-border);
}
swp-table-header,
swp-table-row {
display: grid;
grid-template-columns: minmax(200px, 1fr) 150px 80px 140px 80px 40px;
align-items: center;
}
swp-table-header {
background: var(--color-background-alt);
border-bottom: 1px solid var(--color-border);
padding: 12px 20px;
}
swp-th {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--color-text-secondary);
}
swp-th.right {
text-align: right;
}
swp-th.center {
text-align: center;
}
swp-table-body {
display: block;
}
swp-table-row {
padding: 14px 20px;
border-bottom: 1px solid var(--color-border);
transition: background 150ms ease;
cursor: pointer;
}
swp-table-row:last-child {
border-bottom: none;
}
swp-table-row:hover {
background: var(--color-background-hover);
}
swp-td {
font-size: 14px;
color: var(--color-text);
}
swp-td.right {
text-align: right;
}
swp-td.center {
text-align: center;
}
swp-td.mono {
font-family: var(--font-mono);
font-size: 13px;
}
swp-td.muted {
color: var(--color-text-secondary);
}
/* ==========================================
SUPPLIER CELL
========================================== */
swp-supplier-cell {
display: flex;
flex-direction: column;
gap: 2px;
}
swp-supplier-name {
font-weight: 500;
color: var(--color-text);
}
swp-supplier-city {
font-size: 12px;
color: var(--color-text-secondary);
}
/* ==========================================
STATUS BADGE
========================================== */
swp-status-badge {
display: inline-flex;
align-items: center;
padding: 4px 10px;
font-size: 11px;
font-weight: 500;
border-radius: 12px;
}
swp-status-badge.active {
background: color-mix(in srgb, var(--color-green) 15%, white);
color: var(--color-green);
}
swp-status-badge.inactive {
background: color-mix(in srgb, var(--color-text-secondary) 15%, white);
color: var(--color-text-secondary);
}
/* ==========================================
ROW ARROW
========================================== */
swp-row-arrow {
display: flex;
align-items: center;
justify-content: flex-end;
}
swp-row-arrow svg {
width: 18px;
height: 18px;
fill: var(--color-text-secondary);
transition: transform 150ms ease;
}
swp-table-row:hover swp-row-arrow svg {
transform: translateX(4px);
fill: var(--color-teal);
}
/* ==========================================
TABLE FOOTER
========================================== */
swp-table-footer {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 20px;
background: var(--color-background-alt);
border-top: 1px solid var(--color-border);
font-size: 13px;
color: var(--color-text-secondary);
}
/* ==========================================
RESPONSIVE
========================================== */
@media (max-width: 1200px) {
swp-stats-bar {
grid-template-columns: repeat(2, 1fr);
}
}
@media (max-width: 900px) {
swp-table-header,
swp-table-row {
grid-template-columns: 1fr 80px 80px 40px;
}
swp-th:nth-child(2),
swp-td:nth-child(2),
swp-th:nth-child(3),
swp-td:nth-child(3),
swp-th:nth-child(4),
swp-td:nth-child(4) {
display: none;
}
}
</style>
</head>
<body>
<!-- Topbar -->
<swp-topbar>
<swp-topbar-left>
<swp-page-title>Leverandører</swp-page-title>
</swp-topbar-left>
<swp-topbar-right>
<swp-btn class="secondary icon-only" id="themeToggle" title="Skift tema">
<svg id="sunIcon" viewBox="0 0 24 24"><path d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zM2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1zm18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1zM11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1zm0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1zM5.99 4.58c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0s.39-1.03 0-1.41L5.99 4.58zm12.37 12.37c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0 .39-.39.39-1.03 0-1.41l-1.06-1.06zm1.06-10.96c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06zM7.05 18.36c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06z"/></svg>
<svg id="moonIcon" viewBox="0 0 24 24" style="display:none"><path d="M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9c0-.46-.04-.92-.1-1.36-.98 1.37-2.58 2.26-4.4 2.26-2.98 0-5.4-2.42-5.4-5.4 0-1.81.89-3.42 2.26-4.4-.44-.06-.9-.1-1.36-.1z"/></svg>
</swp-btn>
<swp-btn class="primary">
<svg viewBox="0 0 24 24"><path d="M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/></svg>
Ny leverandør
</swp-btn>
</swp-topbar-right>
</swp-topbar>
<swp-page-container>
<!-- Filter Bar -->
<swp-filter-bar>
<swp-search-input>
<img src="icons/search.svg" alt="Søg" />
<input type="search" id="searchInput" placeholder="Søg leverandør, kontaktperson..." />
</swp-search-input>
<swp-filter-spacer></swp-filter-spacer>
</swp-filter-bar>
<!-- Stats Bar -->
<swp-stats-bar>
<swp-stat-card class="highlight">
<swp-stat-value>12</swp-stat-value>
<swp-stat-label>Leverandører i alt</swp-stat-label>
</swp-stat-card>
<swp-stat-card>
<swp-stat-value>10</swp-stat-value>
<swp-stat-label>Aktive</swp-stat-label>
</swp-stat-card>
<swp-stat-card>
<swp-stat-value>45.230 kr</swp-stat-value>
<swp-stat-label>Indkøb denne måned</swp-stat-label>
</swp-stat-card>
<swp-stat-card class="warning">
<swp-stat-value>3</swp-stat-value>
<swp-stat-label>Afventende ordrer</swp-stat-label>
</swp-stat-card>
</swp-stats-bar>
<!-- Table -->
<swp-table>
<swp-table-header>
<swp-th>Leverandør</swp-th>
<swp-th>Kontakt</swp-th>
<swp-th class="center">Produkter</swp-th>
<swp-th>Sidste ordre</swp-th>
<swp-th>Status</swp-th>
<swp-th></swp-th>
</swp-table-header>
<swp-table-body>
<swp-table-row data-id="1" onclick="location.href='poc-leverandoer.html'">
<swp-td>
<swp-supplier-cell>
<swp-supplier-name>Beauty Group Denmark</swp-supplier-name>
<swp-supplier-city>København</swp-supplier-city>
</swp-supplier-cell>
</swp-td>
<swp-td>Lars Hansen</swp-td>
<swp-td class="center mono">24</swp-td>
<swp-td class="muted">15. december 2024</swp-td>
<swp-td><swp-status-badge class="active">Aktiv</swp-status-badge></swp-td>
<swp-td><swp-row-arrow><svg viewBox="0 0 24 24"><path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"/></svg></swp-row-arrow></swp-td>
</swp-table-row>
<swp-table-row data-id="2">
<swp-td>
<swp-supplier-cell>
<swp-supplier-name>Salon Supplies ApS</swp-supplier-name>
<swp-supplier-city>Aarhus</swp-supplier-city>
</swp-supplier-cell>
</swp-td>
<swp-td>Mette Nielsen</swp-td>
<swp-td class="center mono">18</swp-td>
<swp-td class="muted">22. december 2024</swp-td>
<swp-td><swp-status-badge class="active">Aktiv</swp-status-badge></swp-td>
<swp-td><swp-row-arrow><svg viewBox="0 0 24 24"><path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"/></svg></swp-row-arrow></swp-td>
</swp-table-row>
<swp-table-row data-id="3">
<swp-td>
<swp-supplier-cell>
<swp-supplier-name>Pro Hair Distribution</swp-supplier-name>
<swp-supplier-city>Odense</swp-supplier-city>
</swp-supplier-cell>
</swp-td>
<swp-td>Anders Sørensen</swp-td>
<swp-td class="center mono">32</swp-td>
<swp-td class="muted">10. december 2024</swp-td>
<swp-td><swp-status-badge class="active">Aktiv</swp-status-badge></swp-td>
<swp-td><swp-row-arrow><svg viewBox="0 0 24 24"><path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"/></svg></swp-row-arrow></swp-td>
</swp-table-row>
<swp-table-row data-id="4">
<swp-td>
<swp-supplier-cell>
<swp-supplier-name>Nordic Beauty Import</swp-supplier-name>
<swp-supplier-city>Aalborg</swp-supplier-city>
</swp-supplier-cell>
</swp-td>
<swp-td>Pia Kristensen</swp-td>
<swp-td class="center mono">15</swp-td>
<swp-td class="muted">28. november 2024</swp-td>
<swp-td><swp-status-badge class="active">Aktiv</swp-status-badge></swp-td>
<swp-td><swp-row-arrow><svg viewBox="0 0 24 24"><path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"/></svg></swp-row-arrow></swp-td>
</swp-table-row>
<swp-table-row data-id="5">
<swp-td>
<swp-supplier-cell>
<swp-supplier-name>Color World A/S</swp-supplier-name>
<swp-supplier-city>Vejle</swp-supplier-city>
</swp-supplier-cell>
</swp-td>
<swp-td>Thomas Berg</swp-td>
<swp-td class="center mono">8</swp-td>
<swp-td class="muted">5. december 2024</swp-td>
<swp-td><swp-status-badge class="inactive">Inaktiv</swp-status-badge></swp-td>
<swp-td><swp-row-arrow><svg viewBox="0 0 24 24"><path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"/></svg></swp-row-arrow></swp-td>
</swp-table-row>
<swp-table-row data-id="6">
<swp-td>
<swp-supplier-cell>
<swp-supplier-name>Tools & More</swp-supplier-name>
<swp-supplier-city>Roskilde</swp-supplier-city>
</swp-supplier-cell>
</swp-td>
<swp-td>Karen Olsen</swp-td>
<swp-td class="center mono">12</swp-td>
<swp-td class="muted">18. december 2024</swp-td>
<swp-td><swp-status-badge class="active">Aktiv</swp-status-badge></swp-td>
<swp-td><swp-row-arrow><svg viewBox="0 0 24 24"><path d="M8.59 16.59L13.17 12 8.59 7.41 10 6l6 6-6 6-1.41-1.41z"/></svg></swp-row-arrow></swp-td>
</swp-table-row>
</swp-table-body>
<swp-table-footer>
<span>Viser 6 af 12 leverandører</span>
<span>Side 1 af 2</span>
</swp-table-footer>
</swp-table>
</swp-page-container>
<script src="https://cdn.jsdelivr.net/npm/fuse.js@7.0.0"></script>
<script>
// Supplier data for search
const suppliers = [
{ id: 1, name: 'Beauty Group Denmark', contact: 'Lars Hansen', city: 'København' },
{ id: 2, name: 'Salon Supplies ApS', contact: 'Mette Nielsen', city: 'Aarhus' },
{ id: 3, name: 'Pro Hair Distribution', contact: 'Anders Sørensen', city: 'Odense' },
{ id: 4, name: 'Nordic Beauty Import', contact: 'Pia Kristensen', city: 'Aalborg' },
{ id: 5, name: 'Color World A/S', contact: 'Thomas Berg', city: 'Vejle' },
{ id: 6, name: 'Tools & More', contact: 'Karen Olsen', city: 'Roskilde' }
];
// Initialize Fuse.js
const fuse = new Fuse(suppliers, {
keys: ['name', 'contact', 'city'],
threshold: 0.3,
ignoreLocation: true
});
const searchInput = document.getElementById('searchInput');
const tableBody = document.querySelector('swp-table-body');
const allRows = Array.from(tableBody.querySelectorAll('swp-table-row'));
// Search handler
searchInput.addEventListener('input', (e) => {
const query = e.target.value.trim();
if (query === '') {
// Show all rows
allRows.forEach(row => row.style.display = '');
} else {
// Search with Fuse.js
const results = fuse.search(query);
const matchedIds = results.map(r => r.item.id);
allRows.forEach(row => {
const rowId = parseInt(row.dataset.id);
row.style.display = matchedIds.includes(rowId) ? '' : 'none';
});
}
});
// Theme toggle
const themeToggle = document.getElementById('themeToggle');
const sunIcon = document.getElementById('sunIcon');
const moonIcon = document.getElementById('moonIcon');
const root = document.documentElement;
function updateIcons() {
const isDark = root.classList.contains('dark-mode') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches && !root.classList.contains('light-mode'));
sunIcon.style.display = isDark ? 'none' : 'block';
moonIcon.style.display = isDark ? 'block' : 'none';
}
updateIcons();
themeToggle.addEventListener('click', () => {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (prefersDark) {
// System is dark - toggle to light or back to system
root.classList.toggle('light-mode');
root.classList.remove('dark-mode');
} else {
// System is light - toggle to dark or back to system
root.classList.toggle('dark-mode');
root.classList.remove('light-mode');
}
updateIcons();
});
</script>
</body>
</html>

View file

@ -59,6 +59,42 @@
--font-mono: 'JetBrains Mono', monospace; --font-mono: 'JetBrains Mono', monospace;
} }
/* Dark mode - følger system preference ELLER manuel toggle */
@media (prefers-color-scheme: dark) {
:root:not(.light-mode) {
--color-surface: #1e1e1e;
--color-background: #121212;
--color-background-hover: #2a2a2a;
--color-background-alt: #1a1a1a;
--color-border: #333;
--color-text: #e0e0e0;
--color-text-secondary: #999;
--color-teal: #26a69a;
--color-blue: #42a5f5;
--color-red: #ef5350;
--color-amber: #ffb74d;
--color-purple: #a78bfa;
--color-green: #66bb6a;
}
}
/* Manuel dark mode override */
:root.dark-mode {
--color-surface: #1e1e1e;
--color-background: #121212;
--color-background-hover: #2a2a2a;
--color-background-alt: #1a1a1a;
--color-border: #333;
--color-text: #e0e0e0;
--color-text-secondary: #999;
--color-teal: #26a69a;
--color-blue: #42a5f5;
--color-red: #ef5350;
--color-amber: #ffb74d;
--color-purple: #a78bfa;
--color-green: #66bb6a;
}
/* ========================================== /* ==========================================
RESET & BASE RESET & BASE
========================================== */ ========================================== */
@ -169,6 +205,15 @@
background: var(--color-background-hover); background: var(--color-background-hover);
} }
swp-btn.icon-only {
padding: 10px;
}
swp-btn.icon-only svg {
width: 18px;
height: 18px;
}
/* ========================================== /* ==========================================
FILTER BAR FILTER BAR
========================================== */ ========================================== */
@ -227,12 +272,24 @@
opacity: 0.5; opacity: 0.5;
} }
@media (prefers-color-scheme: dark) {
:root:not(.light-mode) swp-search-input img {
filter: invert(1);
}
}
:root.dark-mode swp-search-input img {
filter: invert(1);
}
swp-search-input input { swp-search-input input {
border: none; border: none;
outline: none; outline: none;
font-size: 13px; font-size: 13px;
font-family: var(--font-family); font-family: var(--font-family);
width: 400px; width: 400px;
background: transparent;
color: var(--color-text);
} }
swp-filter-spacer { swp-filter-spacer {
@ -488,6 +545,10 @@
<swp-page-title>Produkter</swp-page-title> <swp-page-title>Produkter</swp-page-title>
</swp-topbar-left> </swp-topbar-left>
<swp-topbar-right> <swp-topbar-right>
<swp-btn class="secondary icon-only" id="themeToggle" title="Skift tema">
<svg id="sunIcon" viewBox="0 0 24 24"><path d="M12 7c-2.76 0-5 2.24-5 5s2.24 5 5 5 5-2.24 5-5-2.24-5-5-5zM2 13h2c.55 0 1-.45 1-1s-.45-1-1-1H2c-.55 0-1 .45-1 1s.45 1 1 1zm18 0h2c.55 0 1-.45 1-1s-.45-1-1-1h-2c-.55 0-1 .45-1 1s.45 1 1 1zM11 2v2c0 .55.45 1 1 1s1-.45 1-1V2c0-.55-.45-1-1-1s-1 .45-1 1zm0 18v2c0 .55.45 1 1 1s1-.45 1-1v-2c0-.55-.45-1-1-1s-1 .45-1 1zM5.99 4.58c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0s.39-1.03 0-1.41L5.99 4.58zm12.37 12.37c-.39-.39-1.03-.39-1.41 0-.39.39-.39 1.03 0 1.41l1.06 1.06c.39.39 1.03.39 1.41 0 .39-.39.39-1.03 0-1.41l-1.06-1.06zm1.06-10.96c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06zM7.05 18.36c.39-.39.39-1.03 0-1.41-.39-.39-1.03-.39-1.41 0l-1.06 1.06c-.39.39-.39 1.03 0 1.41s1.03.39 1.41 0l1.06-1.06z"/></svg>
<svg id="moonIcon" viewBox="0 0 24 24" style="display:none"><path d="M12 3c-4.97 0-9 4.03-9 9s4.03 9 9 9 9-4.03 9-9c0-.46-.04-.92-.1-1.36-.98 1.37-2.58 2.26-4.4 2.26-2.98 0-5.4-2.42-5.4-5.4 0-1.81.89-3.42 2.26-4.4-.44-.06-.9-.1-1.36-.1z"/></svg>
</swp-btn>
<swp-btn class="secondary"> <swp-btn class="secondary">
<svg viewBox="0 0 24 24"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z"/></svg> <svg viewBox="0 0 24 24"><path d="M19.35 10.04C18.67 6.59 15.64 4 12 4 9.11 4 6.6 5.64 5.35 8.04 2.34 8.36 0 10.91 0 14c0 3.31 2.69 6 6 6h13c2.76 0 5-2.24 5-5 0-2.64-2.05-4.78-4.65-4.96zM14 13v4h-4v-4H7l5-5 5 5h-3z"/></svg>
Importer Importer
@ -721,6 +782,37 @@
}); });
} }
}); });
// Theme toggle
const themeToggle = document.getElementById('themeToggle');
const sunIcon = document.getElementById('sunIcon');
const moonIcon = document.getElementById('moonIcon');
const root = document.documentElement;
function updateIcons() {
const isDark = root.classList.contains('dark-mode') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches && !root.classList.contains('light-mode'));
sunIcon.style.display = isDark ? 'none' : 'block';
moonIcon.style.display = isDark ? 'block' : 'none';
}
updateIcons();
themeToggle.addEventListener('click', () => {
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
if (prefersDark) {
// System is dark - toggle to light or back to system
root.classList.toggle('light-mode');
root.classList.remove('dark-mode');
} else {
// System is light - toggle to dark or back to system
root.classList.toggle('dark-mode');
root.classList.remove('light-mode');
}
updateIcons();
});
</script> </script>
</body> </body>
</html> </html>