Calendar/wwwroot/poc-customer-detail.html
Janus C. H. Knudsen 9f46ff8824 Enhances UI with drag-and-drop card functionality
Adds drag and drop interaction for rearranging cards on the customer and service detail pages

Improves user experience by allowing flexible card positioning
Implements dynamic drop indicators and column management
Supports intuitive card movement between columns
2025-12-23 09:32:49 +01:00

3327 lines
98 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="da">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kundedetaljer - Sofie Nielsen</title>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
/* ==========================================
CSS VARIABLES (Design System)
========================================== */
:root {
/* Colors */
--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: #ffb300;
--color-purple: #8b5cf6;
--color-green: #43a047;
/* Typography */
--font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
--font-mono: 'JetBrains Mono', monospace;
}
/* ==========================================
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;
}
/* ==========================================
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-back-link {
display: flex;
align-items: center;
gap: 6px;
color: var(--color-text-secondary);
text-decoration: none;
font-size: 13px;
cursor: pointer;
transition: color 150ms ease;
}
swp-back-link:hover {
color: var(--color-teal);
}
swp-back-link img {
width: 16px;
height: 16px;
opacity: 0.6;
}
swp-topbar-title {
font-size: 16px;
font-weight: 600;
color: var(--color-text);
}
swp-topbar-actions {
display: flex;
align-items: center;
gap: 12px;
}
swp-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 8px 16px;
font-size: 13px;
font-weight: 500;
border-radius: 6px;
border: none;
cursor: pointer;
transition: all 150ms ease;
}
swp-btn.primary {
background: var(--color-teal);
color: white;
}
swp-btn.primary:hover {
background: #00796b;
}
swp-btn.secondary {
background: var(--color-surface);
color: var(--color-text);
border: 1px solid var(--color-border);
}
swp-btn.secondary:hover {
background: var(--color-background);
}
/* ==========================================
STICKY HEADER
========================================== */
swp-customer-header {
display: flex;
gap: 24px;
padding: 24px;
background: var(--color-surface);
border-bottom: 1px solid var(--color-border);
position: sticky;
top: 49px;
z-index: 90;
}
swp-customer-avatar {
width: 80px;
height: 80px;
border-radius: 50%;
background: linear-gradient(135deg, var(--color-teal) 0%, #00695c 100%);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 28px;
font-weight: 600;
flex-shrink: 0;
}
swp-customer-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 8px;
}
swp-customer-name-row {
display: flex;
align-items: center;
gap: 16px;
}
swp-customer-name {
font-size: 24px;
font-weight: 600;
color: var(--color-text);
}
swp-customer-name[contenteditable="true"] {
outline: none;
border-bottom: 1px dashed var(--color-border);
padding-bottom: 2px;
}
swp-customer-name[contenteditable="true"]:focus {
border-bottom-color: var(--color-teal);
}
/* Tags Editor */
swp-tags-editor {
display: flex;
align-items: center;
gap: 8px;
flex-wrap: wrap;
}
swp-tag {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 4px 10px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.3px;
border-radius: 4px;
background: var(--color-background);
color: var(--color-text-secondary);
}
swp-tag.vip {
background: color-mix(in srgb, var(--color-amber) 20%, white);
color: #b8860b;
}
swp-tag.stamkunde {
background: color-mix(in srgb, var(--color-teal) 15%, white);
color: var(--color-teal);
}
swp-tag.ny {
background: color-mix(in srgb, var(--color-blue) 15%, white);
color: var(--color-blue);
}
swp-tag.allergi {
background: color-mix(in srgb, var(--color-red) 15%, white);
color: var(--color-red);
}
swp-tag.sensitiv {
background: color-mix(in srgb, var(--color-purple) 15%, white);
color: var(--color-purple);
}
swp-tag .remove {
cursor: pointer;
opacity: 0.6;
margin-left: 2px;
}
swp-tag .remove:hover {
opacity: 1;
}
swp-tag-add {
display: inline-flex;
align-items: center;
padding: 4px 10px;
font-size: 11px;
font-weight: 500;
color: var(--color-text-secondary);
border: 1px dashed var(--color-border);
border-radius: 4px;
cursor: pointer;
transition: all 150ms ease;
}
swp-tag-add:hover {
border-color: var(--color-teal);
color: var(--color-teal);
}
/* Booking Exclusion Toggle */
swp-booking-exclusion {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 6px 12px;
font-size: 12px;
font-weight: 500;
border-radius: 6px;
cursor: pointer;
transition: all 150ms ease;
margin-left: auto;
}
swp-booking-exclusion[data-excluded="false"] {
background: var(--color-background);
color: var(--color-text-secondary);
border: 1px solid var(--color-border);
}
swp-booking-exclusion[data-excluded="true"] {
background: color-mix(in srgb, var(--color-red) 12%, white);
color: var(--color-red);
border: 1px solid color-mix(in srgb, var(--color-red) 30%, white);
}
swp-booking-exclusion .icon {
font-size: 14px;
}
/* Customer Group Selector */
swp-customer-group {
display: inline-flex;
align-items: center;
gap: 8px;
position: relative;
}
swp-customer-group-label {
font-size: 12px;
color: var(--color-text-secondary);
}
swp-customer-group-select {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 5px 10px;
font-size: 13px;
font-weight: 500;
color: var(--color-text);
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 6px;
cursor: pointer;
transition: all 150ms ease;
}
swp-customer-group-select:hover {
border-color: var(--color-teal);
}
swp-customer-group-select .arrow {
font-size: 10px;
color: var(--color-text-secondary);
}
swp-customer-group-dropdown {
position: absolute;
top: 100%;
left: 0;
margin-top: 4px;
min-width: 180px;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
z-index: 200;
display: none;
}
swp-customer-group-dropdown.open {
display: block;
}
swp-customer-group-option {
display: block;
padding: 10px 14px;
font-size: 13px;
color: var(--color-text);
cursor: pointer;
transition: background 100ms ease;
}
swp-customer-group-option:first-child {
border-radius: 7px 7px 0 0;
}
swp-customer-group-option:last-child {
border-radius: 0 0 7px 7px;
}
swp-customer-group-option:hover {
background: var(--color-background);
}
swp-customer-group-option.selected {
background: color-mix(in srgb, var(--color-teal) 10%, white);
color: var(--color-teal);
font-weight: 500;
}
/* Relation Items */
swp-relations-list {
display: flex;
flex-direction: column;
gap: 8px;
}
swp-relation-item {
display: flex;
align-items: center;
gap: 12px;
padding: 10px 12px;
background: var(--color-background-alt);
border: 1px solid var(--color-border);
border-radius: 6px;
}
swp-relation-avatar {
width: 36px;
height: 36px;
border-radius: 50%;
background: var(--color-purple);
color: white;
display: flex;
align-items: center;
justify-content: center;
font-size: 13px;
font-weight: 600;
flex-shrink: 0;
}
swp-relation-info {
flex: 1;
}
swp-relation-name {
display: block;
font-size: 14px;
font-weight: 500;
color: var(--color-text);
}
swp-relation-type {
display: block;
font-size: 12px;
color: var(--color-text-secondary);
}
swp-relation-actions {
display: flex;
gap: 8px;
}
swp-relation-link {
font-size: 12px;
color: var(--color-teal);
cursor: pointer;
}
swp-relation-link:hover {
text-decoration: underline;
}
swp-relation-remove {
font-size: 14px;
color: var(--color-text-secondary);
cursor: pointer;
opacity: 0.6;
}
swp-relation-remove:hover {
opacity: 1;
color: var(--color-red);
}
swp-add-relation {
display: flex;
align-items: center;
gap: 8px;
padding: 10px 12px;
border: 1px dashed var(--color-border);
border-radius: 6px;
color: var(--color-text-secondary);
font-size: 13px;
cursor: pointer;
transition: all 150ms ease;
}
swp-add-relation:hover {
border-color: var(--color-teal);
color: var(--color-teal);
}
/* Customer Group in Card */
swp-customer-group-row {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 16px;
padding-bottom: 16px;
border-bottom: 1px solid var(--color-border);
}
/* Contact Line */
swp-contact-line {
display: flex;
align-items: center;
gap: 16px;
font-size: 13px;
color: var(--color-text-secondary);
}
swp-contact-line a {
color: var(--color-teal);
text-decoration: none;
}
swp-contact-line a:hover {
text-decoration: underline;
}
swp-contact-line span.separator {
color: var(--color-border);
}
/* Fact Boxes Inline */
swp-fact-boxes-inline {
display: flex;
gap: 24px;
margin-top: 4px;
}
swp-fact-inline {
display: flex;
align-items: baseline;
gap: 6px;
}
swp-fact-inline-value {
font-size: 18px;
font-weight: 600;
font-family: var(--font-mono);
color: var(--color-text);
}
swp-fact-inline-label {
font-size: 12px;
color: var(--color-text-secondary);
}
/* ==========================================
TAB BAR
========================================== */
swp-tab-bar {
display: flex;
gap: 0;
background: var(--color-surface);
border-bottom: 1px solid var(--color-border);
padding: 0 24px;
position: sticky;
top: 193px;
z-index: 80;
}
swp-tab {
padding: 14px 24px;
font-size: 14px;
font-weight: 500;
color: var(--color-text-secondary);
cursor: pointer;
border-bottom: 2px solid transparent;
margin-bottom: -1px;
transition: all 150ms ease;
}
swp-tab:hover {
color: var(--color-text);
background: var(--color-background-alt);
}
swp-tab.active {
color: var(--color-teal);
border-bottom-color: var(--color-teal);
}
/* ==========================================
TAB CONTENT
========================================== */
swp-tab-content {
display: none;
padding: 24px;
max-width: 1200px;
margin: 0 auto;
}
swp-tab-content.active {
display: block;
}
/* ==========================================
COMMON COMPONENTS
========================================== */
/* Section Label */
swp-section-label {
display: block;
font-size: 11px;
font-weight: 600;
color: var(--color-text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 12px;
}
/* Card */
swp-card {
display: block;
background: var(--color-surface);
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
padding: 20px;
margin-bottom: 20px;
}
/* Grid Layouts */
.grid-2 {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
.grid-4 {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
}
/* Edit Section */
swp-edit-section {
display: flex;
flex-direction: column;
gap: 12px;
}
swp-edit-row {
display: grid;
grid-template-columns: 120px 1fr;
align-items: center;
gap: 12px;
}
swp-edit-label {
font-size: 13px;
color: var(--color-text-secondary);
}
swp-edit-value {
font-size: 14px;
color: var(--color-text);
padding: 8px 12px;
border: 1px solid transparent;
border-radius: 6px;
cursor: text;
transition: all 150ms ease;
}
swp-edit-value:hover {
background: var(--color-background-alt);
}
swp-edit-value:focus {
outline: none;
border-color: var(--color-teal);
background: var(--color-surface);
}
swp-edit-value[contenteditable="true"] {
cursor: text;
}
/* Toggle Slider (Ja/Nej) */
swp-toggle-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 0;
border-bottom: 1px solid var(--color-border);
}
swp-toggle-row:last-child {
border-bottom: none;
}
swp-toggle-label {
font-size: 14px;
color: var(--color-text);
}
swp-toggle-slider {
display: flex;
position: relative;
background: var(--color-background);
border-radius: 6px;
padding: 3px;
gap: 0;
}
swp-toggle-slider::before {
content: '';
position: absolute;
top: 3px;
left: 3px;
width: calc(50% - 3px);
height: calc(100% - 6px);
background: color-mix(in srgb, var(--color-green) 18%, white);
border-radius: 4px;
transition: transform 200ms ease, background 200ms ease;
z-index: 0;
}
swp-toggle-slider[data-value="no"]::before {
transform: translateX(100%);
background: color-mix(in srgb, var(--color-red) 18%, white);
}
swp-toggle-option {
position: relative;
z-index: 1;
padding: 5px 16px;
font-size: 12px;
font-weight: 500;
color: var(--color-text-secondary);
cursor: pointer;
transition: color 200ms ease;
user-select: none;
}
swp-toggle-slider[data-value="yes"] swp-toggle-option:first-child {
color: var(--color-green);
font-weight: 600;
}
swp-toggle-slider[data-value="no"] swp-toggle-option:last-child {
color: var(--color-red);
font-weight: 600;
}
/* Profile Boxes */
swp-profile-boxes {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
}
swp-profile-box {
padding: 12px 14px;
background: var(--color-background-alt);
border-radius: 6px;
border: 1px solid var(--color-border);
}
swp-profile-box.warning {
background: color-mix(in srgb, var(--color-red) 8%, white);
border-color: color-mix(in srgb, var(--color-red) 30%, white);
}
swp-profile-box.full-width {
grid-column: span 2;
}
swp-profile-box-label {
display: block;
font-size: 11px;
color: var(--color-text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 4px;
}
swp-profile-box-value {
font-size: 14px;
color: var(--color-text);
}
swp-profile-box.warning swp-profile-box-value {
color: var(--color-red);
}
/* Stat Cards */
swp-stat-card {
background: var(--color-surface);
border-radius: 8px;
padding: 16px 20px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
text-align: center;
}
swp-stat-value {
display: block;
font-size: 24px;
font-weight: 600;
color: var(--color-text);
font-family: var(--font-mono);
}
swp-stat-card.highlight swp-stat-value {
color: var(--color-teal);
}
swp-stat-label {
display: block;
font-size: 12px;
color: var(--color-text-secondary);
margin-top: 4px;
}
/* Chart Container */
swp-chart-container {
display: block;
background: var(--color-background-alt);
border-radius: 8px;
border: 1px solid var(--color-border);
min-height: 200px;
}
swp-chart-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
swp-chart-legend {
display: flex;
gap: 16px;
}
swp-chart-legend-item {
display: flex;
align-items: center;
gap: 6px;
font-size: 12px;
color: var(--color-text-secondary);
}
swp-chart-legend-dot {
width: 8px;
height: 8px;
border-radius: 50%;
}
swp-chart-legend-dot.services {
background: var(--color-teal);
}
swp-chart-legend-dot.products {
background: var(--color-blue);
}
/* Table */
swp-table {
display: block;
width: 100%;
}
swp-table-header {
display: grid;
padding: 12px 16px;
background: var(--color-background);
border-radius: 6px 6px 0 0;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--color-text-secondary);
}
swp-table-row {
display: grid;
padding: 12px 16px;
border-bottom: 1px solid var(--color-border);
font-size: 13px;
align-items: center;
}
swp-table-row:last-child {
border-bottom: none;
}
swp-table-row:hover {
background: var(--color-background-alt);
}
/* Journal Entry */
swp-journal-entry {
display: block;
padding: 16px;
background: var(--color-surface);
border-radius: 8px;
border: 1px solid var(--color-border);
margin-bottom: 12px;
}
swp-journal-entry:last-child {
margin-bottom: 0;
}
swp-journal-meta {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
swp-journal-type {
display: inline-block;
padding: 3px 8px;
font-size: 10px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.3px;
border-radius: 4px;
}
swp-journal-type.farveformel {
background: color-mix(in srgb, var(--color-teal) 15%, white);
color: var(--color-teal);
}
swp-journal-type.analyse {
background: color-mix(in srgb, var(--color-purple) 15%, white);
color: var(--color-purple);
}
swp-journal-type.note {
background: color-mix(in srgb, var(--color-blue) 15%, white);
color: var(--color-blue);
}
swp-journal-type.advarsel {
background: color-mix(in srgb, var(--color-red) 15%, white);
color: var(--color-red);
}
swp-journal-date {
font-size: 12px;
color: var(--color-text-secondary);
}
swp-journal-content {
font-size: 14px;
color: var(--color-text);
line-height: 1.6;
}
swp-journal-author {
font-size: 12px;
color: var(--color-text-secondary);
margin-top: 8px;
}
/* Appointment Card */
swp-appointment-card {
display: block;
padding: 16px;
background: color-mix(in srgb, var(--color-teal) 5%, white);
border: 1px solid color-mix(in srgb, var(--color-teal) 20%, white);
border-radius: 8px;
margin-bottom: 12px;
}
swp-appointment-date {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
font-weight: 600;
color: var(--color-teal);
margin-bottom: 6px;
}
swp-appointment-details {
font-size: 13px;
color: var(--color-text);
margin-bottom: 12px;
}
swp-appointment-actions {
display: flex;
gap: 8px;
}
swp-appointment-actions swp-btn {
padding: 6px 12px;
font-size: 12px;
}
/* Giftcard */
swp-giftcard {
display: block;
padding: 16px;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 8px;
margin-bottom: 12px;
}
swp-giftcard-header {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
font-weight: 600;
color: var(--color-text);
margin-bottom: 8px;
}
swp-giftcard-balance {
font-size: 13px;
color: var(--color-text);
margin-bottom: 4px;
}
swp-giftcard-balance strong {
font-family: var(--font-mono);
color: var(--color-teal);
}
swp-giftcard-expires {
font-size: 12px;
color: var(--color-text-secondary);
}
/* Progress Bar */
swp-progress-bar {
display: block;
height: 8px;
background: var(--color-background);
border-radius: 4px;
overflow: hidden;
margin: 8px 0;
}
swp-progress-fill {
display: block;
height: 100%;
background: var(--color-teal);
border-radius: 4px;
transition: width 300ms ease;
}
/* See All Link */
swp-see-all {
display: inline-block;
margin-top: 12px;
font-size: 13px;
color: var(--color-teal);
cursor: pointer;
}
swp-see-all:hover {
text-decoration: underline;
}
/* Add Button */
swp-add-btn {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 10px 16px;
font-size: 13px;
font-weight: 500;
color: var(--color-teal);
background: color-mix(in srgb, var(--color-teal) 10%, white);
border: 1px solid color-mix(in srgb, var(--color-teal) 30%, white);
border-radius: 6px;
cursor: pointer;
transition: all 150ms ease;
margin-bottom: 16px;
}
swp-add-btn:hover {
background: color-mix(in srgb, var(--color-teal) 15%, white);
}
/* Empty State */
swp-empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 40px;
text-align: center;
color: var(--color-text-secondary);
}
swp-empty-state p {
font-size: 14px;
}
/* ==========================================
STAT CARD VARIANTS
========================================== */
swp-stat-card.warning swp-stat-value {
color: var(--color-amber);
}
swp-stat-card.danger swp-stat-value {
color: var(--color-red);
}
swp-stat-card.success swp-stat-value {
color: var(--color-green);
}
/* ==========================================
KEY-VALUE LIST
========================================== */
swp-kv-list {
display: flex;
flex-direction: column;
}
swp-kv-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
border-bottom: 1px solid var(--color-border);
}
swp-kv-row:last-child {
border-bottom: none;
}
swp-kv-label {
font-size: 13px;
color: var(--color-text-secondary);
}
swp-kv-value {
font-size: 14px;
font-weight: 500;
font-family: var(--font-mono);
color: var(--color-text);
}
/* ==========================================
RATIO BAR
========================================== */
swp-ratio-container {
display: block;
margin-top: 16px;
}
swp-ratio-labels {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
font-size: 12px;
color: var(--color-text-secondary);
}
swp-ratio-bar {
display: flex;
height: 10px;
border-radius: 5px;
overflow: hidden;
background: var(--color-background);
}
swp-ratio-segment {
height: 100%;
transition: width 300ms ease;
}
swp-ratio-segment.services {
background: var(--color-teal);
}
swp-ratio-segment.products {
background: var(--color-blue);
}
/* ==========================================
RISK INDICATOR
========================================== */
swp-risk-indicator {
display: inline-flex;
align-items: center;
gap: 6px;
}
swp-risk-dot {
width: 10px;
height: 10px;
border-radius: 50%;
}
swp-risk-dot.low {
background: var(--color-green);
}
swp-risk-dot.medium {
background: var(--color-amber);
}
swp-risk-dot.high {
background: var(--color-red);
}
swp-risk-text {
font-size: 14px;
font-weight: 500;
}
swp-risk-text.low {
color: var(--color-green);
}
swp-risk-text.medium {
color: var(--color-amber);
}
swp-risk-text.high {
color: var(--color-red);
}
/* ==========================================
TOP LIST
========================================== */
swp-top-list {
display: flex;
flex-direction: column;
gap: 8px;
}
swp-top-item {
display: flex;
align-items: center;
gap: 12px;
font-size: 13px;
}
swp-top-rank {
width: 24px;
height: 24px;
border-radius: 50%;
background: var(--color-background);
color: var(--color-text-secondary);
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: 600;
}
swp-top-item:first-child swp-top-rank {
background: color-mix(in srgb, var(--color-teal) 15%, white);
color: var(--color-teal);
}
swp-top-name {
flex: 1;
color: var(--color-text);
}
swp-top-count {
font-family: var(--font-mono);
font-size: 12px;
color: var(--color-text-secondary);
}
/* ==========================================
ATTENDANCE BAR
========================================== */
swp-attendance-bar {
display: flex;
height: 24px;
border-radius: 6px;
overflow: hidden;
margin-top: 12px;
}
swp-attendance-segment {
display: flex;
align-items: center;
justify-content: center;
font-size: 11px;
font-weight: 600;
color: white;
}
swp-attendance-segment.attended {
background: var(--color-teal);
}
swp-attendance-segment.cancelled {
background: var(--color-amber);
}
swp-attendance-segment.noshow {
background: var(--color-red);
}
/* ==========================================
JOURNAL - NEW LAYOUT
========================================== */
/* Journal Mini Tabs */
swp-journal-mini-tabs {
display: flex;
gap: 8px;
margin-bottom: 20px;
}
swp-journal-mini-tab {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 10px 16px;
font-size: 13px;
font-weight: 500;
color: var(--color-text-secondary);
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 8px;
cursor: pointer;
transition: all 150ms ease;
}
swp-journal-mini-tab:hover {
border-color: var(--color-text-secondary);
}
swp-journal-mini-tab.active {
border-color: var(--color-teal);
color: var(--color-teal);
background: color-mix(in srgb, var(--color-teal) 5%, white);
}
swp-journal-mini-tab .tab-dot {
width: 8px;
height: 8px;
border-radius: 50%;
}
swp-journal-mini-tab .tab-dot.blue { background: var(--color-blue); }
swp-journal-mini-tab .tab-dot.amber { background: var(--color-amber); }
swp-journal-mini-tab .tab-dot.purple { background: var(--color-purple); }
swp-journal-mini-tab .tab-count {
font-size: 11px;
padding: 2px 6px;
background: var(--color-background);
border-radius: 10px;
color: var(--color-text-secondary);
}
/* Journal Columns */
swp-journal-columns {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
}
swp-journal-column {
display: flex;
flex-direction: column;
}
swp-journal-column-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 12px;
padding-bottom: 12px;
border-bottom: 1px solid var(--color-border);
}
swp-journal-column-title {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
font-weight: 600;
color: var(--color-text);
}
swp-journal-column-title .col-dot {
width: 8px;
height: 8px;
border-radius: 50%;
}
swp-journal-column-title .col-dot.amber { background: var(--color-amber); }
swp-journal-column-title .col-dot.purple { background: var(--color-purple); }
swp-journal-column-add {
font-size: 12px;
color: var(--color-teal);
cursor: pointer;
}
swp-journal-column-add:hover {
text-decoration: underline;
}
/* Journal Entry - New Style */
swp-journal-entry-new {
display: block;
padding: 14px;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 8px;
margin-bottom: 10px;
transition: border-color 150ms ease;
}
swp-journal-entry-new:hover {
border-color: var(--color-text-secondary);
}
swp-journal-entry-new:last-child {
margin-bottom: 0;
}
swp-journal-entry-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
}
swp-journal-entry-type-new {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 4px 10px;
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.3px;
border-radius: 4px;
}
swp-journal-entry-type-new.farveformel {
background: color-mix(in srgb, var(--color-amber) 15%, white);
color: #b8860b;
}
swp-journal-entry-type-new.analyse {
background: color-mix(in srgb, var(--color-purple) 15%, white);
color: var(--color-purple);
}
swp-journal-entry-type-new.note {
background: color-mix(in srgb, var(--color-blue) 15%, white);
color: var(--color-blue);
}
swp-journal-entry-type-new.advarsel {
background: color-mix(in srgb, var(--color-red) 15%, white);
color: var(--color-red);
}
swp-journal-entry-tags-new {
display: flex;
gap: 6px;
}
swp-journal-tag-new {
display: inline-block;
padding: 3px 8px;
font-size: 10px;
font-weight: 500;
border-radius: 4px;
background: var(--color-background);
color: var(--color-text-secondary);
}
swp-journal-tag-new.sensitiv {
background: color-mix(in srgb, var(--color-purple) 12%, white);
color: var(--color-purple);
}
swp-journal-tag-new.allergi {
background: color-mix(in srgb, var(--color-red) 12%, white);
color: var(--color-red);
}
swp-journal-entry-delete-new {
font-size: 14px;
color: var(--color-text-secondary);
cursor: pointer;
opacity: 0;
transition: opacity 150ms ease;
}
swp-journal-entry-new:hover swp-journal-entry-delete-new {
opacity: 0.6;
}
swp-journal-entry-delete-new:hover {
opacity: 1;
color: var(--color-red);
}
swp-journal-entry-body {
font-size: 13px;
line-height: 1.6;
color: var(--color-text);
}
swp-journal-entry-body .mono {
font-family: var(--font-mono);
font-size: 12px;
background: var(--color-background);
padding: 1px 4px;
border-radius: 3px;
}
swp-journal-entry-body .label {
color: var(--color-text-secondary);
}
swp-journal-entry-footer-new {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 12px;
padding-top: 10px;
border-top: 1px solid var(--color-border);
}
swp-journal-entry-date-new {
font-size: 12px;
color: var(--color-text-secondary);
}
swp-journal-entry-icons-new {
display: flex;
align-items: center;
gap: 12px;
}
swp-journal-entry-icon {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 11px;
color: var(--color-text-secondary);
}
swp-journal-entry-icon img {
width: 14px;
height: 14px;
opacity: 0.5;
}
swp-journal-entry-icon.warning {
color: var(--color-amber);
}
swp-journal-entry-icon.warning img {
opacity: 1;
}
/* Noter Section (full width above columns) */
swp-journal-noter-section {
display: block;
margin-bottom: 24px;
}
swp-journal-noter-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
}
/* Kundeprofil i Journal */
swp-journal-profile {
display: block;
margin-top: 24px;
padding-top: 20px;
border-top: 1px solid var(--color-border);
}
swp-journal-profile-header {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
font-weight: 600;
color: var(--color-text-secondary);
margin-bottom: 12px;
cursor: pointer;
}
swp-journal-profile-header .arrow {
transition: transform 150ms ease;
}
swp-journal-profile-header.collapsed .arrow {
transform: rotate(-90deg);
}
swp-journal-profile swp-profile-boxes {
transition: all 200ms ease;
}
swp-journal-profile swp-profile-boxes.collapsed {
display: none;
}
/* ==========================================
ACTIVITY / AUDIT LOG
========================================== */
/* Activity Filters */
swp-activity-filters {
display: flex;
gap: 8px;
margin-bottom: 20px;
flex-wrap: wrap;
}
swp-activity-filter {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 6px 12px;
font-size: 12px;
font-weight: 500;
color: var(--color-text-secondary);
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 20px;
cursor: pointer;
transition: all 150ms ease;
}
swp-activity-filter:hover {
border-color: var(--color-text-secondary);
}
swp-activity-filter.active {
background: var(--color-teal);
color: white;
border-color: var(--color-teal);
}
swp-activity-filter .filter-icon {
font-size: 11px;
}
swp-activity-filter .filter-icon img {
width: 12px;
height: 12px;
filter: invert(45%) sepia(0%) saturate(0%) brightness(90%);
}
swp-activity-filter.active .filter-icon img {
filter: brightness(0) invert(1);
}
/* Activity Timeline */
swp-activity-timeline {
display: block;
position: relative;
}
/* Date Group */
swp-activity-date-group {
display: block;
margin-bottom: 24px;
}
swp-activity-date-header {
display: flex;
align-items: center;
gap: 12px;
margin-bottom: 12px;
font-size: 12px;
font-weight: 600;
color: var(--color-text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
}
swp-activity-date-header::after {
content: '';
flex: 1;
height: 1px;
background: var(--color-border);
}
/* Activity Item */
swp-activity-item {
display: flex;
gap: 12px;
padding: 12px 0;
border-bottom: 1px solid var(--color-border);
}
swp-activity-item:last-child {
border-bottom: none;
}
swp-activity-icon {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
flex-shrink: 0;
}
swp-activity-icon img {
width: 16px;
height: 16px;
}
swp-activity-icon.system {
background: var(--color-background);
}
swp-activity-icon.system img {
filter: invert(45%) sepia(0%) saturate(0%) brightness(90%);
}
swp-activity-icon.customer {
background: color-mix(in srgb, var(--color-blue) 15%, white);
}
swp-activity-icon.customer img {
filter: invert(32%) sepia(98%) saturate(1234%) hue-rotate(196deg) brightness(93%) contrast(92%);
}
swp-activity-icon.employee {
background: color-mix(in srgb, var(--color-teal) 15%, white);
}
swp-activity-icon.employee img {
filter: invert(32%) sepia(98%) saturate(1234%) hue-rotate(152deg) brightness(93%) contrast(92%);
}
swp-activity-icon.booking {
background: color-mix(in srgb, var(--color-purple) 15%, white);
}
swp-activity-icon.booking img {
filter: invert(45%) sepia(70%) saturate(2000%) hue-rotate(235deg) brightness(90%) contrast(95%);
}
swp-activity-icon.communication {
background: color-mix(in srgb, var(--color-amber) 15%, white);
}
swp-activity-icon.communication img {
filter: invert(55%) sepia(80%) saturate(500%) hue-rotate(10deg) brightness(95%) contrast(95%);
}
swp-activity-icon.payment {
background: color-mix(in srgb, var(--color-green) 15%, white);
}
swp-activity-icon.payment img {
filter: invert(45%) sepia(70%) saturate(500%) hue-rotate(90deg) brightness(95%) contrast(90%);
}
swp-activity-icon.warning {
background: color-mix(in srgb, var(--color-red) 15%, white);
}
swp-activity-icon.warning img {
filter: invert(30%) sepia(90%) saturate(2000%) hue-rotate(340deg) brightness(90%) contrast(95%);
}
swp-activity-content {
flex: 1;
min-width: 0;
}
swp-activity-title {
display: block;
font-size: 13px;
color: var(--color-text);
margin-bottom: 2px;
}
swp-activity-title strong {
font-weight: 600;
}
swp-activity-title .highlight {
color: var(--color-teal);
font-weight: 500;
}
swp-activity-title .old-value {
color: var(--color-text-secondary);
text-decoration: line-through;
}
swp-activity-title .new-value {
color: var(--color-teal);
}
swp-activity-meta {
display: flex;
align-items: center;
gap: 8px;
font-size: 11px;
color: var(--color-text-secondary);
}
swp-activity-time {
font-family: var(--font-mono);
}
swp-activity-actor {
display: inline-flex;
align-items: center;
gap: 4px;
}
swp-activity-actor::before {
content: '·';
}
swp-activity-badge {
display: inline-block;
padding: 2px 6px;
font-size: 9px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.3px;
border-radius: 3px;
margin-left: 6px;
}
swp-activity-badge.auto {
background: var(--color-background);
color: var(--color-text-secondary);
}
swp-activity-badge.online {
background: color-mix(in srgb, var(--color-blue) 15%, white);
color: var(--color-blue);
}
swp-activity-badge.manual {
background: color-mix(in srgb, var(--color-teal) 15%, white);
color: var(--color-teal);
}
/* Activity Details (expandable) */
swp-activity-details {
display: block;
margin-top: 8px;
padding: 10px 12px;
background: var(--color-background-alt);
border-radius: 6px;
font-size: 12px;
color: var(--color-text-secondary);
}
swp-activity-details code {
font-family: var(--font-mono);
font-size: 11px;
background: var(--color-background);
padding: 1px 4px;
border-radius: 3px;
}
/* Load More */
swp-activity-load-more {
display: block;
text-align: center;
padding: 16px;
font-size: 13px;
color: var(--color-teal);
cursor: pointer;
border-top: 1px solid var(--color-border);
margin-top: 12px;
}
swp-activity-load-more:hover {
text-decoration: underline;
}
/* ==========================================
DRAG & DROP CARDS
========================================== */
/* Draggable Card */
swp-card[draggable="true"] {
position: relative;
transition: transform 200ms ease, box-shadow 200ms ease, opacity 200ms ease;
}
swp-card[draggable="true"]:hover {
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
swp-card[draggable="true"].dragging {
opacity: 0.5;
transform: scale(0.98);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15);
}
/* Drag Handle */
swp-drag-handle {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 4px;
color: var(--color-text-secondary);
opacity: 0;
transition: opacity 150ms ease, background 150ms ease;
cursor: move;
}
swp-drag-handle svg {
width: 14px;
height: 14px;
fill: currentColor;
}
swp-card[draggable="true"]:hover swp-drag-handle {
opacity: 0.5;
}
swp-drag-handle:hover {
opacity: 1 !important;
background: var(--color-background);
}
/* Column Drop Zones */
swp-card-column {
display: flex;
flex-direction: column;
min-height: 200px;
position: relative;
}
/* Drop zone indicator element - dynamically inserted */
swp-drop-indicator {
display: block;
height: 60px;
background: color-mix(in srgb, var(--color-teal) 8%, transparent);
border: 2px dashed var(--color-teal);
border-radius: 8px;
margin: 8px 0;
transition: opacity 150ms ease;
}
/* Column empty state drop zone */
swp-card-column.drag-over-empty {
background: color-mix(in srgb, var(--color-teal) 5%, white);
border: 2px dashed var(--color-teal);
border-radius: 8px;
min-height: 100px;
}
/* Cards have no gap by default */
swp-card-column swp-card {
margin-bottom: 16px;
}
swp-card-column swp-card:last-child {
margin-bottom: 0;
}
</style>
</head>
<body>
<!-- Topbar -->
<swp-topbar>
<swp-topbar-left>
<swp-back-link onclick="history.back()">
<img src="icons/angle-small-left.svg" alt="">
Tilbage til kunder
</swp-back-link>
</swp-topbar-left>
<swp-topbar-actions>
<swp-btn class="secondary">Slet kunde</swp-btn>
<swp-btn class="primary" id="saveBtn">Gem</swp-btn>
</swp-topbar-actions>
</swp-topbar>
<!-- Sticky Header -->
<swp-customer-header>
<swp-customer-avatar>SN</swp-customer-avatar>
<swp-customer-info>
<swp-customer-name-row>
<swp-customer-name contenteditable="true">Sofie Nielsen</swp-customer-name>
<swp-tags-editor>
<swp-tag class="vip">VIP <span class="remove">×</span></swp-tag>
<swp-tag-add>+ Tilføj tag</swp-tag-add>
</swp-tags-editor>
<swp-booking-exclusion data-excluded="false" id="bookingExclusion">
<span class="icon"></span>
<span class="text">Booking tilladt</span>
</swp-booking-exclusion>
</swp-customer-name-row>
<swp-contact-line>
<a href="tel:+4523456789">+45 23 45 67 89</a>
<span class="separator">|</span>
<a href="mailto:sofie@email.dk">sofie@email.dk</a>
<span class="separator">|</span>
<span>Kunde siden marts 2024</span>
</swp-contact-line>
<swp-fact-boxes-inline>
<swp-fact-inline>
<swp-fact-inline-value>14</swp-fact-inline-value>
<swp-fact-inline-label>besøg</swp-fact-inline-label>
</swp-fact-inline>
<swp-fact-inline>
<swp-fact-inline-value>32</swp-fact-inline-value>
<swp-fact-inline-label>dage interval</swp-fact-inline-label>
</swp-fact-inline>
<swp-fact-inline>
<swp-fact-inline-value>Emma L.</swp-fact-inline-value>
<swp-fact-inline-label>foretrukken frisør</swp-fact-inline-label>
</swp-fact-inline>
<swp-fact-inline>
<swp-fact-inline-value>12.450 kr</swp-fact-inline-value>
<swp-fact-inline-label>total omsætning</swp-fact-inline-label>
</swp-fact-inline>
</swp-fact-boxes-inline>
</swp-customer-info>
</swp-customer-header>
<!-- Tab Bar -->
<swp-tab-bar>
<swp-tab class="active" data-tab="overview">Oversigt</swp-tab>
<swp-tab data-tab="economy">Økonomi</swp-tab>
<swp-tab data-tab="statistics">Statistik</swp-tab>
<swp-tab data-tab="journal">Journal</swp-tab>
<swp-tab data-tab="appointments">Aftaler</swp-tab>
<swp-tab data-tab="giftcards">Gavekort</swp-tab>
<swp-tab data-tab="activity">Aktivitet</swp-tab>
</swp-tab-bar>
<!-- Tab Contents -->
<!-- OVERSIGT TAB -->
<swp-tab-content class="active" data-tab="overview">
<div class="grid-2">
<!-- Left Column -->
<swp-card-column data-column="left">
<swp-card draggable="true" data-card-id="kontakt">
<swp-drag-handle><svg viewBox="0 0 24 24"><path d="M13 6v5h5V8l4 4-4 4v-3h-5v5h3l-4 4-4-4h3v-5H6v3l-4-4 4-4v3h5V6H8l4-4 4 4h-3z"/></svg></swp-drag-handle>
<swp-section-label>Kontaktoplysninger</swp-section-label>
<swp-edit-section>
<swp-edit-row>
<swp-edit-label>Telefon</swp-edit-label>
<swp-edit-value contenteditable="true">+45 23 45 67 89</swp-edit-value>
</swp-edit-row>
<swp-edit-row>
<swp-edit-label>Email</swp-edit-label>
<swp-edit-value contenteditable="true">sofie@email.dk</swp-edit-value>
</swp-edit-row>
<swp-edit-row>
<swp-edit-label>Adresse</swp-edit-label>
<swp-edit-value contenteditable="true">Hovedgaden 12</swp-edit-value>
</swp-edit-row>
<swp-edit-row>
<swp-edit-label>Postnr + By</swp-edit-label>
<swp-edit-value contenteditable="true">2100 København Ø</swp-edit-value>
</swp-edit-row>
</swp-edit-section>
</swp-card>
<swp-card draggable="true" data-card-id="profil">
<swp-drag-handle><svg viewBox="0 0 24 24"><path d="M13 6v5h5V8l4 4-4 4v-3h-5v5h3l-4 4-4-4h3v-5H6v3l-4-4 4-4v3h5V6H8l4-4 4 4h-3z"/></svg></swp-drag-handle>
<swp-section-label>Profil</swp-section-label>
<swp-profile-boxes>
<swp-profile-box>
<swp-profile-box-label>Hårtype</swp-profile-box-label>
<swp-profile-box-value>Medium · Bølget</swp-profile-box-value>
</swp-profile-box>
<swp-profile-box>
<swp-profile-box-label>Porøsitet</swp-profile-box-label>
<swp-profile-box-value>Medium</swp-profile-box-value>
</swp-profile-box>
<swp-profile-box>
<swp-profile-box-label>Hovedbund</swp-profile-box-label>
<swp-profile-box-value>Normal</swp-profile-box-value>
</swp-profile-box>
<swp-profile-box>
<swp-profile-box-label>Naturlig farve</swp-profile-box-label>
<swp-profile-box-value>Mørkblond (6)</swp-profile-box-value>
</swp-profile-box>
</swp-profile-boxes>
</swp-card>
</swp-card-column>
<!-- Right Column -->
<swp-card-column data-column="right">
<swp-card draggable="true" data-card-id="marketing">
<swp-drag-handle><svg viewBox="0 0 24 24"><path d="M13 6v5h5V8l4 4-4 4v-3h-5v5h3l-4 4-4-4h3v-5H6v3l-4-4 4-4v3h5V6H8l4-4 4 4h-3z"/></svg></swp-drag-handle>
<swp-section-label>Marketing</swp-section-label>
<swp-toggle-row>
<swp-toggle-label>Email marketing</swp-toggle-label>
<swp-toggle-slider data-value="yes">
<swp-toggle-option>Ja</swp-toggle-option>
<swp-toggle-option>Nej</swp-toggle-option>
</swp-toggle-slider>
</swp-toggle-row>
<swp-toggle-row>
<swp-toggle-label>SMS marketing</swp-toggle-label>
<swp-toggle-slider data-value="no">
<swp-toggle-option>Ja</swp-toggle-option>
<swp-toggle-option>Nej</swp-toggle-option>
</swp-toggle-slider>
</swp-toggle-row>
</swp-card>
<swp-card draggable="true" data-card-id="praeferencer">
<swp-drag-handle><svg viewBox="0 0 24 24"><path d="M13 6v5h5V8l4 4-4 4v-3h-5v5h3l-4 4-4-4h3v-5H6v3l-4-4 4-4v3h5V6H8l4-4 4 4h-3z"/></svg></swp-drag-handle>
<swp-section-label>Præferencer</swp-section-label>
<swp-profile-boxes>
<swp-profile-box>
<swp-profile-box-label>Foretrukken frisør</swp-profile-box-label>
<swp-profile-box-value>Emma L.</swp-profile-box-value>
</swp-profile-box>
<swp-profile-box>
<swp-profile-box-label>Foretrukken dag</swp-profile-box-label>
<swp-profile-box-value>Tirsdag/Torsdag</swp-profile-box-value>
</swp-profile-box>
<swp-profile-box class="full-width">
<swp-profile-box-label>Specielle ønsker</swp-profile-box-label>
<swp-profile-box-value>Foretrækker kold tone, ikke for mørk</swp-profile-box-value>
</swp-profile-box>
</swp-profile-boxes>
</swp-card>
<swp-card draggable="true" data-card-id="advarsler">
<swp-drag-handle><svg viewBox="0 0 24 24"><path d="M13 6v5h5V8l4 4-4 4v-3h-5v5h3l-4 4-4-4h3v-5H6v3l-4-4 4-4v3h5V6H8l4-4 4 4h-3z"/></svg></swp-drag-handle>
<swp-section-label>Advarsler</swp-section-label>
<swp-profile-boxes>
<swp-profile-box class="warning full-width">
<swp-profile-box-label>Allergier / Følsomhed</swp-profile-box-label>
<swp-profile-box-value>Parfumeallergi - brug uparfumerede produkter</swp-profile-box-value>
</swp-profile-box>
</swp-profile-boxes>
</swp-card>
<swp-card draggable="true" data-card-id="relationer">
<swp-drag-handle><svg viewBox="0 0 24 24"><path d="M13 6v5h5V8l4 4-4 4v-3h-5v5h3l-4 4-4-4h3v-5H6v3l-4-4 4-4v3h5V6H8l4-4 4 4h-3z"/></svg></swp-drag-handle>
<swp-section-label>Kundegruppe & Relationer</swp-section-label>
<swp-customer-group-row>
<swp-customer-group-label>Kundegruppe:</swp-customer-group-label>
<swp-customer-group>
<swp-customer-group-select id="customerGroupSelect">
<span class="value">Standard</span>
<span class="arrow"></span>
</swp-customer-group-select>
<swp-customer-group-dropdown id="customerGroupDropdown">
<swp-customer-group-option class="selected" data-value="standard">Standard</swp-customer-group-option>
<swp-customer-group-option data-value="premium">Premium</swp-customer-group-option>
<swp-customer-group-option data-value="erhverv">Erhverv</swp-customer-group-option>
<swp-customer-group-option data-value="medarbejder">Medarbejder</swp-customer-group-option>
<swp-customer-group-option data-value="familie">Familie & Venner</swp-customer-group-option>
</swp-customer-group-dropdown>
</swp-customer-group>
</swp-customer-group-row>
<swp-relations-list>
<swp-relation-item>
<swp-relation-avatar>EN</swp-relation-avatar>
<swp-relation-info>
<swp-relation-name>Emil Nielsen</swp-relation-name>
<swp-relation-type>Barn</swp-relation-type>
</swp-relation-info>
<swp-relation-actions>
<swp-relation-link>Åbn</swp-relation-link>
<swp-relation-remove>×</swp-relation-remove>
</swp-relation-actions>
</swp-relation-item>
<swp-relation-item>
<swp-relation-avatar>LN</swp-relation-avatar>
<swp-relation-info>
<swp-relation-name>Luna Nielsen</swp-relation-name>
<swp-relation-type>Barn</swp-relation-type>
</swp-relation-info>
<swp-relation-actions>
<swp-relation-link>Åbn</swp-relation-link>
<swp-relation-remove>×</swp-relation-remove>
</swp-relation-actions>
</swp-relation-item>
<swp-add-relation>
<span>+</span> Tilføj relation
</swp-add-relation>
</swp-relations-list>
</swp-card>
</swp-card-column>
</div>
</swp-tab-content>
<!-- ØKONOMI TAB -->
<swp-tab-content data-tab="economy">
<div class="grid-4" style="margin-bottom: 24px;">
<swp-stat-card class="highlight">
<swp-stat-value>12.450 kr</swp-stat-value>
<swp-stat-label>I år (2025)</swp-stat-label>
</swp-stat-card>
<swp-stat-card>
<swp-stat-value>9.800 kr</swp-stat-value>
<swp-stat-label>Sidste år</swp-stat-label>
</swp-stat-card>
<swp-stat-card>
<swp-stat-value>889 kr</swp-stat-value>
<swp-stat-label>Gns. pr. besøg</swp-stat-label>
</swp-stat-card>
<swp-stat-card>
<swp-stat-value>1.038 kr</swp-stat-value>
<swp-stat-label>Gns. pr. måned</swp-stat-label>
</swp-stat-card>
</div>
<swp-card>
<swp-chart-header>
<swp-section-label style="margin-bottom: 0;">Omsætning over tid</swp-section-label>
<swp-chart-legend>
<swp-chart-legend-item>
<swp-chart-legend-dot class="services"></swp-chart-legend-dot>
<span>Services</span>
</swp-chart-legend-item>
<swp-chart-legend-item>
<swp-chart-legend-dot class="products"></swp-chart-legend-dot>
<span>Produkter</span>
</swp-chart-legend-item>
</swp-chart-legend>
</swp-chart-header>
<swp-chart-container id="revenueChart"></swp-chart-container>
</swp-card>
<swp-card>
<swp-section-label>Købshistorik</swp-section-label>
<swp-table>
<swp-table-header style="grid-template-columns: 100px 80px 1fr 100px;">
<span>Dato</span>
<span>Type</span>
<span>Beskrivelse</span>
<span style="text-align: right;">Beløb</span>
</swp-table-header>
<swp-table-row style="grid-template-columns: 100px 80px 1fr 100px;">
<span>9. dec 2025</span>
<span><swp-journal-type class="farveformel" style="font-size: 9px;">Service</swp-journal-type></span>
<span>Klip + Farve</span>
<span style="text-align: right; font-family: var(--font-mono);">1.450 kr</span>
</swp-table-row>
<swp-table-row style="grid-template-columns: 100px 80px 1fr 100px;">
<span>9. dec 2025</span>
<span><swp-journal-type class="analyse" style="font-size: 9px;">Produkt</swp-journal-type></span>
<span>Olaplex No. 3</span>
<span style="text-align: right; font-family: var(--font-mono);">349 kr</span>
</swp-table-row>
<swp-table-row style="grid-template-columns: 100px 80px 1fr 100px;">
<span>12. nov 2025</span>
<span><swp-journal-type class="farveformel" style="font-size: 9px;">Service</swp-journal-type></span>
<span>Farve</span>
<span style="text-align: right; font-family: var(--font-mono);">1.200 kr</span>
</swp-table-row>
<swp-table-row style="grid-template-columns: 100px 80px 1fr 100px;">
<span>15. okt 2025</span>
<span><swp-journal-type class="farveformel" style="font-size: 9px;">Service</swp-journal-type></span>
<span>Klip</span>
<span style="text-align: right; font-family: var(--font-mono);">550 kr</span>
</swp-table-row>
</swp-table>
<swp-see-all>Se alle transaktioner →</swp-see-all>
</swp-card>
</swp-tab-content>
<!-- STATISTIK TAB -->
<swp-tab-content data-tab="statistics">
<!-- Fremmøde & Pålidelighed -->
<swp-card>
<swp-section-label>Fremmøde & Pålidelighed</swp-section-label>
<div class="grid-4">
<swp-stat-card class="highlight">
<swp-stat-value>47</swp-stat-value>
<swp-stat-label>Fremmøder</swp-stat-label>
</swp-stat-card>
<swp-stat-card class="warning">
<swp-stat-value>3</swp-stat-value>
<swp-stat-label>Aflysninger</swp-stat-label>
</swp-stat-card>
<swp-stat-card class="danger">
<swp-stat-value>1</swp-stat-value>
<swp-stat-label>No-shows</swp-stat-label>
</swp-stat-card>
<swp-stat-card class="success">
<swp-stat-value>92%</swp-stat-value>
<swp-stat-label>Pålidelighed</swp-stat-label>
</swp-stat-card>
</div>
<swp-attendance-bar>
<swp-attendance-segment class="attended" style="width: 92%;">47</swp-attendance-segment>
<swp-attendance-segment class="cancelled" style="width: 6%;">3</swp-attendance-segment>
<swp-attendance-segment class="noshow" style="width: 2%;">1</swp-attendance-segment>
</swp-attendance-bar>
</swp-card>
<div class="grid-2">
<!-- Booking-adfærd -->
<swp-card>
<swp-section-label>Booking-adfærd</swp-section-label>
<swp-kv-list>
<swp-kv-row>
<swp-kv-label>Gns. bookingvarsel</swp-kv-label>
<swp-kv-value>5 dage</swp-kv-value>
</swp-kv-row>
<swp-kv-row>
<swp-kv-label>Foretrukken dag</swp-kv-label>
<swp-kv-value>Tirsdag</swp-kv-value>
</swp-kv-row>
<swp-kv-row>
<swp-kv-label>Foretrukken tid</swp-kv-label>
<swp-kv-value>10:00 - 12:00</swp-kv-value>
</swp-kv-row>
<swp-kv-row>
<swp-kv-label>Online booking rate</swp-kv-label>
<swp-kv-value>78%</swp-kv-value>
</swp-kv-row>
<swp-kv-row>
<swp-kv-label>Gns. aflysningsvarsel</swp-kv-label>
<swp-kv-value>2 dage</swp-kv-value>
</swp-kv-row>
</swp-kv-list>
</swp-card>
<!-- Loyalitet -->
<swp-card>
<swp-section-label>Loyalitet</swp-section-label>
<div class="grid-2" style="gap: 12px;">
<swp-stat-card>
<swp-stat-value style="font-size: 20px;">1,8 år</swp-stat-value>
<swp-stat-label>Kunde siden</swp-stat-label>
</swp-stat-card>
<swp-stat-card class="success">
<swp-stat-value style="font-size: 20px;">13</swp-stat-value>
<swp-stat-label>Dage siden sidst</swp-stat-label>
</swp-stat-card>
<swp-stat-card>
<swp-stat-value style="font-size: 20px;">
<swp-risk-indicator>
<swp-risk-dot class="low"></swp-risk-dot>
<swp-risk-text class="low">Lav</swp-risk-text>
</swp-risk-indicator>
</swp-stat-value>
<swp-stat-label>Churn-risiko</swp-stat-label>
</swp-stat-card>
<swp-stat-card>
<swp-stat-value style="font-size: 20px;">32 dage</swp-stat-value>
<swp-stat-label>Gns. interval</swp-stat-label>
</swp-stat-card>
</div>
</swp-card>
</div>
<!-- Service-mønstre -->
<swp-card>
<swp-section-label>Service-mønstre</swp-section-label>
<div class="grid-2">
<div>
<swp-section-label style="font-size: 10px; margin-bottom: 10px;">Top 3 Services</swp-section-label>
<swp-top-list>
<swp-top-item>
<swp-top-rank>1</swp-top-rank>
<swp-top-name>Klip + Farve</swp-top-name>
<swp-top-count>12×</swp-top-count>
</swp-top-item>
<swp-top-item>
<swp-top-rank>2</swp-top-rank>
<swp-top-name>Farve</swp-top-name>
<swp-top-count>8×</swp-top-count>
</swp-top-item>
<swp-top-item>
<swp-top-rank>3</swp-top-rank>
<swp-top-name>Klip</swp-top-name>
<swp-top-count>6×</swp-top-count>
</swp-top-item>
</swp-top-list>
</div>
<div>
<swp-section-label style="font-size: 10px; margin-bottom: 10px;">Top 3 Produkter</swp-section-label>
<swp-top-list>
<swp-top-item>
<swp-top-rank>1</swp-top-rank>
<swp-top-name>Olaplex No. 3</swp-top-name>
<swp-top-count>5×</swp-top-count>
</swp-top-item>
<swp-top-item>
<swp-top-rank>2</swp-top-rank>
<swp-top-name>Shampoo</swp-top-name>
<swp-top-count>3×</swp-top-count>
</swp-top-item>
<swp-top-item>
<swp-top-rank>3</swp-top-rank>
<swp-top-name>Hårkur</swp-top-name>
<swp-top-count>2×</swp-top-count>
</swp-top-item>
</swp-top-list>
</div>
</div>
<swp-ratio-container>
<swp-ratio-labels>
<span>Services (85%)</span>
<span>Produkter (15%)</span>
</swp-ratio-labels>
<swp-ratio-bar>
<swp-ratio-segment class="services" style="width: 85%;"></swp-ratio-segment>
<swp-ratio-segment class="products" style="width: 15%;"></swp-ratio-segment>
</swp-ratio-bar>
</swp-ratio-container>
</swp-card>
</swp-tab-content>
<!-- JOURNAL TAB -->
<swp-tab-content data-tab="journal">
<!-- Mini Tabs for quick filter -->
<swp-journal-mini-tabs>
<swp-journal-mini-tab class="active">
<span class="tab-dot blue"></span>
Alle
<span class="tab-count">5</span>
</swp-journal-mini-tab>
<swp-journal-mini-tab>
<span class="tab-dot blue"></span>
Noter
<span class="tab-count">2</span>
</swp-journal-mini-tab>
<swp-journal-mini-tab>
<span class="tab-dot amber"></span>
Farveformler
<span class="tab-count">2</span>
</swp-journal-mini-tab>
<swp-journal-mini-tab>
<span class="tab-dot purple"></span>
Analyser
<span class="tab-count">1</span>
</swp-journal-mini-tab>
</swp-journal-mini-tabs>
<!-- Noter Section - full width -->
<swp-journal-noter-section>
<swp-card>
<swp-journal-column-header>
<swp-journal-column-title>
<span class="col-dot" style="background: var(--color-blue);"></span>
Noter
</swp-journal-column-title>
<swp-journal-column-add>+ Tilføj note</swp-journal-column-add>
</swp-journal-column-header>
<swp-journal-noter-grid>
<swp-journal-entry-new>
<swp-journal-entry-header>
<swp-journal-entry-type-new class="note">Note</swp-journal-entry-type-new>
<swp-journal-entry-delete-new>🗑</swp-journal-entry-delete-new>
</swp-journal-entry-header>
<swp-journal-entry-body>
Kunden foretrækker naturlige farver og ønsker lidt ekstra tid til konsultation. Husk at tjekke allergistatus inden farvebehandling.
</swp-journal-entry-body>
<swp-journal-entry-footer-new>
<swp-journal-entry-date-new>9. dec 2025 · Af: Emma</swp-journal-entry-date-new>
<swp-journal-entry-icons-new>
<swp-journal-entry-icon>
<img src="icons/eye.svg" alt="">
<span>Alle</span>
</swp-journal-entry-icon>
</swp-journal-entry-icons-new>
</swp-journal-entry-footer-new>
</swp-journal-entry-new>
<swp-journal-entry-new>
<swp-journal-entry-header>
<div style="display: flex; align-items: center; gap: 8px;">
<swp-journal-entry-type-new class="advarsel">Advarsel</swp-journal-entry-type-new>
<swp-journal-entry-tags-new>
<swp-journal-tag-new class="allergi">Allergi</swp-journal-tag-new>
</swp-journal-entry-tags-new>
</div>
<swp-journal-entry-delete-new>🗑</swp-journal-entry-delete-new>
</swp-journal-entry-header>
<swp-journal-entry-body>
<strong>PARFUMEALLERGI</strong> — Brug kun uparfumerede produkter. Havde reaktion på standard shampoo ved første besøg.
</swp-journal-entry-body>
<swp-journal-entry-footer-new>
<swp-journal-entry-date-new>15. mar 2024 · Af: Nina</swp-journal-entry-date-new>
<swp-journal-entry-icons-new>
<swp-journal-entry-icon>
<img src="icons/eye.svg" alt="">
<span>Alle</span>
</swp-journal-entry-icon>
<swp-journal-entry-icon class="warning">
<img src="icons/warning.svg" alt="">
<span>Advarsel</span>
</swp-journal-entry-icon>
</swp-journal-entry-icons-new>
</swp-journal-entry-footer-new>
</swp-journal-entry-new>
</swp-journal-noter-grid>
</swp-card>
</swp-journal-noter-section>
<!-- Two Column Layout: Farveformler + Analyser -->
<swp-journal-columns>
<!-- Farveformler Column -->
<swp-journal-column>
<swp-card style="height: 100%;">
<swp-journal-column-header>
<swp-journal-column-title>
<span class="col-dot amber"></span>
Farveformler
</swp-journal-column-title>
<swp-journal-column-add>+ Tilføj</swp-journal-column-add>
</swp-journal-column-header>
<swp-journal-entry-new>
<swp-journal-entry-header>
<swp-journal-entry-type-new class="farveformel">Farveformel</swp-journal-entry-type-new>
<swp-journal-entry-delete-new>🗑</swp-journal-entry-delete-new>
</swp-journal-entry-header>
<swp-journal-entry-body>
<span class="label">Mål/tone:</span> Kold<br>
<span class="label">Oxidant:</span> <span class="mono">6%</span><br>
<span class="label">Formel:</span> <span class="mono">7/1 + 7/0 (1:1)</span><br>
<span class="label">Virketid:</span> <span class="mono">35 min</span><br>
<span class="label">Placering:</span> Hele håret<br><br>
Resultat: Flot ensartet farve, kunden meget tilfreds
</swp-journal-entry-body>
<swp-journal-entry-footer-new>
<swp-journal-entry-date-new>12. nov 2025 · Af: Emma</swp-journal-entry-date-new>
<swp-journal-entry-icons-new>
<swp-journal-entry-icon>
<img src="icons/eye.svg" alt="">
<span>Alle</span>
</swp-journal-entry-icon>
</swp-journal-entry-icons-new>
</swp-journal-entry-footer-new>
</swp-journal-entry-new>
<swp-journal-entry-new>
<swp-journal-entry-header>
<swp-journal-entry-type-new class="farveformel">Farveformel</swp-journal-entry-type-new>
<swp-journal-entry-delete-new>🗑</swp-journal-entry-delete-new>
</swp-journal-entry-header>
<swp-journal-entry-body>
<span class="label">Mål/tone:</span> Varm<br>
<span class="label">Oxidant:</span> <span class="mono">6%</span><br>
<span class="label">Formel:</span> <span class="mono">8/0 + 7/1 (2:1)</span><br>
<span class="label">Virketid:</span> <span class="mono">30 min</span><br>
<span class="label">Placering:</span> Rødder<br><br>
Resultat: Lidt for varm, juster næste gang
</swp-journal-entry-body>
<swp-journal-entry-footer-new>
<swp-journal-entry-date-new>15. sep 2025 · Af: Emma</swp-journal-entry-date-new>
<swp-journal-entry-icons-new>
<swp-journal-entry-icon>
<img src="icons/eye.svg" alt="">
<span>Alle</span>
</swp-journal-entry-icon>
</swp-journal-entry-icons-new>
</swp-journal-entry-footer-new>
</swp-journal-entry-new>
</swp-card>
</swp-journal-column>
<!-- Analyser Column -->
<swp-journal-column>
<swp-card style="height: 100%;">
<swp-journal-column-header>
<swp-journal-column-title>
<span class="col-dot purple"></span>
Hår & Hovedbund
</swp-journal-column-title>
<swp-journal-column-add>+ Tilføj</swp-journal-column-add>
</swp-journal-column-header>
<swp-journal-entry-new>
<swp-journal-entry-header>
<div style="display: flex; align-items: center; gap: 8px;">
<swp-journal-entry-type-new class="analyse">Analyse</swp-journal-entry-type-new>
<swp-journal-entry-tags-new>
<swp-journal-tag-new class="sensitiv">Sensitiv</swp-journal-tag-new>
</swp-journal-entry-tags-new>
</div>
<swp-journal-entry-delete-new>🗑</swp-journal-entry-delete-new>
</swp-journal-entry-header>
<swp-journal-entry-body>
<span class="label">Hovedbund:</span> Normal<br>
<span class="label">Hår:</span> <span class="mono">Medium · Bølget</span><br>
<span class="label">Porøsitet:</span> <span class="mono">Medium</span><br>
<span class="label">Elasticitet:</span> <span class="mono">Normal</span><br>
<span class="label">Tilstand:</span> Let tørt i spidserne<br><br>
Anbefaling: Fugtgivende behandling hver 6. uge
</swp-journal-entry-body>
<swp-journal-entry-footer-new>
<swp-journal-entry-date-new>10. aug 2025 · Af: Nina</swp-journal-entry-date-new>
<swp-journal-entry-icons-new>
<swp-journal-entry-icon>
<img src="icons/eye.svg" alt="">
<span>Alle</span>
</swp-journal-entry-icon>
</swp-journal-entry-icons-new>
</swp-journal-entry-footer-new>
</swp-journal-entry-new>
</swp-card>
</swp-journal-column>
</swp-journal-columns>
<!-- Kundeprofil Section -->
<swp-journal-profile>
<swp-journal-profile-header onclick="this.classList.toggle('collapsed'); this.nextElementSibling.classList.toggle('collapsed');">
<span class="arrow"></span>
Vis "Kundeprofil" (hurtige facts)
</swp-journal-profile-header>
<swp-profile-boxes id="journalProfileBoxes">
<swp-profile-box>
<swp-profile-box-label>Hårtype</swp-profile-box-label>
<swp-profile-box-value>Medium · Bølget</swp-profile-box-value>
</swp-profile-box>
<swp-profile-box>
<swp-profile-box-label>Porøsitet</swp-profile-box-label>
<swp-profile-box-value>Medium</swp-profile-box-value>
</swp-profile-box>
<swp-profile-box>
<swp-profile-box-label>Præference</swp-profile-box-label>
<swp-profile-box-value>Kold tone, ikke for mørk</swp-profile-box-value>
</swp-profile-box>
<swp-profile-box class="warning">
<swp-profile-box-label>Advarsler</swp-profile-box-label>
<swp-profile-box-value>Parfumeallergi · Sensitiv hovedbund</swp-profile-box-value>
</swp-profile-box>
</swp-profile-boxes>
</swp-journal-profile>
</swp-tab-content>
<!-- AFTALER TAB -->
<swp-tab-content data-tab="appointments">
<swp-card>
<swp-section-label>Kommende aftaler</swp-section-label>
<swp-appointment-card>
<swp-appointment-date>
Tirsdag 14. januar 2026 kl. 10:00
</swp-appointment-date>
<swp-appointment-details>
Klip + Farve · Emma L. · 2 timer
</swp-appointment-details>
<swp-appointment-actions>
<swp-btn class="secondary">Flyt</swp-btn>
<swp-btn class="secondary">Aflys</swp-btn>
</swp-appointment-actions>
</swp-appointment-card>
</swp-card>
<swp-card>
<swp-section-label>Tidligere aftaler</swp-section-label>
<swp-table>
<swp-table-header style="grid-template-columns: 120px 1fr 100px 80px 100px;">
<span>Dato</span>
<span>Service</span>
<span>Frisør</span>
<span>Varighed</span>
<span style="text-align: right;">Pris</span>
</swp-table-header>
<swp-table-row style="grid-template-columns: 120px 1fr 100px 80px 100px;">
<span>9. dec 2025</span>
<span>Klip + Farve</span>
<span>Emma L.</span>
<span>2 timer</span>
<span style="text-align: right; font-family: var(--font-mono);">1.450 kr</span>
</swp-table-row>
<swp-table-row style="grid-template-columns: 120px 1fr 100px 80px 100px;">
<span>12. nov 2025</span>
<span>Farve</span>
<span>Emma L.</span>
<span>1t 30m</span>
<span style="text-align: right; font-family: var(--font-mono);">1.200 kr</span>
</swp-table-row>
<swp-table-row style="grid-template-columns: 120px 1fr 100px 80px 100px;">
<span>15. okt 2025</span>
<span>Klip</span>
<span>Emma L.</span>
<span>45 min</span>
<span style="text-align: right; font-family: var(--font-mono);">550 kr</span>
</swp-table-row>
<swp-table-row style="grid-template-columns: 120px 1fr 100px 80px 100px;">
<span>20. sep 2025</span>
<span>Klip + Behandling</span>
<span>Nina K.</span>
<span>1t 15m</span>
<span style="text-align: right; font-family: var(--font-mono);">750 kr</span>
</swp-table-row>
<swp-table-row style="grid-template-columns: 120px 1fr 100px 80px 100px;">
<span>15. aug 2025</span>
<span>Farve + Klip</span>
<span>Emma L.</span>
<span>2t 15m</span>
<span style="text-align: right; font-family: var(--font-mono);">1.600 kr</span>
</swp-table-row>
</swp-table>
<swp-see-all>Se alle aftaler →</swp-see-all>
</swp-card>
</swp-tab-content>
<!-- GAVEKORT TAB -->
<swp-tab-content data-tab="giftcards">
<swp-card>
<swp-section-label>Aktive gavekort</swp-section-label>
<swp-giftcard>
<swp-giftcard-header>
Gavekort #GK-2024-0892
</swp-giftcard-header>
<swp-giftcard-balance>
Saldo: <strong>350 kr</strong> (af 500 kr)
</swp-giftcard-balance>
<swp-progress-bar>
<swp-progress-fill style="width: 70%;"></swp-progress-fill>
</swp-progress-bar>
<swp-giftcard-expires>Udløber: 15. marts 2026</swp-giftcard-expires>
</swp-giftcard>
</swp-card>
<swp-card>
<swp-section-label>Klippekort</swp-section-label>
<swp-giftcard>
<swp-giftcard-header>
10-klip kort
</swp-giftcard-header>
<swp-giftcard-balance>
Brugt: <strong>7 af 10</strong> klip
</swp-giftcard-balance>
<swp-progress-bar>
<swp-progress-fill style="width: 70%;"></swp-progress-fill>
</swp-progress-bar>
<swp-giftcard-expires>Udløber aldrig</swp-giftcard-expires>
</swp-giftcard>
</swp-card>
<swp-card>
<swp-section-label>Udløbne / Brugte</swp-section-label>
<swp-empty-state>
<p>Ingen udløbne eller brugte kort</p>
</swp-empty-state>
</swp-card>
</swp-tab-content>
<!-- AKTIVITET TAB -->
<swp-tab-content data-tab="activity">
<!-- Filters -->
<swp-activity-filters>
<swp-activity-filter class="active">Alle</swp-activity-filter>
<swp-activity-filter><span class="filter-icon"><img src="icons/booking.svg" alt=""></span> Bookinger</swp-activity-filter>
<swp-activity-filter><span class="filter-icon"><img src="icons/envelope.svg" alt=""></span> Kommunikation</swp-activity-filter>
<swp-activity-filter><span class="filter-icon"><img src="icons/journal-alt.svg" alt=""></span> Ændringer</swp-activity-filter>
<swp-activity-filter><span class="filter-icon"><img src="icons/credit-card.svg" alt=""></span> Betalinger</swp-activity-filter>
<swp-activity-filter><span class="filter-icon"><img src="icons/unlock.svg" alt=""></span> Login</swp-activity-filter>
</swp-activity-filters>
<swp-card>
<swp-activity-timeline>
<!-- I dag -->
<swp-activity-date-group>
<swp-activity-date-header>I dag</swp-activity-date-header>
<swp-activity-item>
<swp-activity-icon class="communication"><img src="icons/comment-sms.svg" alt=""></swp-activity-icon>
<swp-activity-content>
<swp-activity-title>
<strong>SMS påmindelse</strong> sendt om aftale i morgen
<swp-activity-badge class="auto">Auto</swp-activity-badge>
</swp-activity-title>
<swp-activity-meta>
<swp-activity-time>14:00</swp-activity-time>
<swp-activity-actor>System</swp-activity-actor>
</swp-activity-meta>
</swp-activity-content>
</swp-activity-item>
<swp-activity-item>
<swp-activity-icon class="customer"><img src="icons/unlock.svg" alt=""></swp-activity-icon>
<swp-activity-content>
<swp-activity-title>
Kunde <strong>loggede ind</strong> via online booking
<swp-activity-badge class="online">Online</swp-activity-badge>
</swp-activity-title>
<swp-activity-meta>
<swp-activity-time>11:23</swp-activity-time>
<swp-activity-actor>Sofie Nielsen</swp-activity-actor>
</swp-activity-meta>
</swp-activity-content>
</swp-activity-item>
</swp-activity-date-group>
<!-- 9. december -->
<swp-activity-date-group>
<swp-activity-date-header>9. december 2025</swp-activity-date-header>
<swp-activity-item>
<swp-activity-icon class="payment"><img src="icons/credit-card.svg" alt=""></swp-activity-icon>
<swp-activity-content>
<swp-activity-title>
<strong>Betaling modtaget</strong><span class="highlight">1.799 kr</span> (Dankort)
</swp-activity-title>
<swp-activity-meta>
<swp-activity-time>12:45</swp-activity-time>
<swp-activity-actor>Emma L.</swp-activity-actor>
</swp-activity-meta>
</swp-activity-content>
</swp-activity-item>
<swp-activity-item>
<swp-activity-icon class="booking"><img src="icons/booking.svg" alt=""></swp-activity-icon>
<swp-activity-content>
<swp-activity-title>
<strong>Aftale gennemført</strong> — Klip + Farve hos Emma L.
</swp-activity-title>
<swp-activity-meta>
<swp-activity-time>12:30</swp-activity-time>
<swp-activity-actor>Emma L.</swp-activity-actor>
</swp-activity-meta>
</swp-activity-content>
</swp-activity-item>
<swp-activity-item>
<swp-activity-icon class="booking"><img src="icons/check-in-calendar.svg" alt=""></swp-activity-icon>
<swp-activity-content>
<swp-activity-title>
<strong>Check-in</strong> — Kunden er ankommet
</swp-activity-title>
<swp-activity-meta>
<swp-activity-time>10:02</swp-activity-time>
<swp-activity-actor>Reception</swp-activity-actor>
</swp-activity-meta>
</swp-activity-content>
</swp-activity-item>
<swp-activity-item>
<swp-activity-icon class="employee"><img src="icons/journal-alt.svg" alt=""></swp-activity-icon>
<swp-activity-content>
<swp-activity-title>
<strong>Journal opdateret</strong> — Ny farveformel tilføjet
</swp-activity-title>
<swp-activity-meta>
<swp-activity-time>12:35</swp-activity-time>
<swp-activity-actor>Emma L.</swp-activity-actor>
</swp-activity-meta>
</swp-activity-content>
</swp-activity-item>
</swp-activity-date-group>
<!-- 5. december -->
<swp-activity-date-group>
<swp-activity-date-header>5. december 2025</swp-activity-date-header>
<swp-activity-item>
<swp-activity-icon class="booking"><img src="icons/created.svg" alt=""></swp-activity-icon>
<swp-activity-content>
<swp-activity-title>
<strong>Ny booking</strong> oprettet — 9. dec kl. 10:00, Klip + Farve
<swp-activity-badge class="online">Online</swp-activity-badge>
</swp-activity-title>
<swp-activity-meta>
<swp-activity-time>19:42</swp-activity-time>
<swp-activity-actor>Sofie Nielsen</swp-activity-actor>
</swp-activity-meta>
</swp-activity-content>
</swp-activity-item>
<swp-activity-item>
<swp-activity-icon class="customer"><img src="icons/unlock.svg" alt=""></swp-activity-icon>
<swp-activity-content>
<swp-activity-title>
Kunde <strong>loggede ind</strong> via online booking
<swp-activity-badge class="online">Online</swp-activity-badge>
</swp-activity-title>
<swp-activity-meta>
<swp-activity-time>19:40</swp-activity-time>
<swp-activity-actor>Sofie Nielsen</swp-activity-actor>
</swp-activity-meta>
</swp-activity-content>
</swp-activity-item>
</swp-activity-date-group>
<!-- 28. november -->
<swp-activity-date-group>
<swp-activity-date-header>28. november 2025</swp-activity-date-header>
<swp-activity-item>
<swp-activity-icon class="employee"><img src="icons/journal-alt.svg" alt=""></swp-activity-icon>
<swp-activity-content>
<swp-activity-title>
<strong>Telefon ændret</strong><span class="old-value">+45 12 34 56 78</span><span class="new-value">+45 23 45 67 89</span>
</swp-activity-title>
<swp-activity-meta>
<swp-activity-time>14:15</swp-activity-time>
<swp-activity-actor>Emma L.</swp-activity-actor>
</swp-activity-meta>
<swp-activity-details>
Kunden oplyste nyt nummer under besøg.
</swp-activity-details>
</swp-activity-content>
</swp-activity-item>
<swp-activity-item>
<swp-activity-icon class="employee"><img src="icons/journal-alt.svg" alt=""></swp-activity-icon>
<swp-activity-content>
<swp-activity-title>
<strong>Tag tilføjet</strong><span class="highlight">VIP</span>
</swp-activity-title>
<swp-activity-meta>
<swp-activity-time>14:10</swp-activity-time>
<swp-activity-actor>Emma L.</swp-activity-actor>
</swp-activity-meta>
</swp-activity-content>
</swp-activity-item>
</swp-activity-date-group>
<!-- 12. november -->
<swp-activity-date-group>
<swp-activity-date-header>12. november 2025</swp-activity-date-header>
<swp-activity-item>
<swp-activity-icon class="warning"><img src="icons/warning.svg" alt=""></swp-activity-icon>
<swp-activity-content>
<swp-activity-title>
<strong>Booking aflyst</strong> — Af kunden med 2 dages varsel
</swp-activity-title>
<swp-activity-meta>
<swp-activity-time>09:15</swp-activity-time>
<swp-activity-actor>Sofie Nielsen</swp-activity-actor>
</swp-activity-meta>
<swp-activity-details>
Årsag: "Kan desværre ikke den dag alligevel"
</swp-activity-details>
</swp-activity-content>
</swp-activity-item>
<swp-activity-item>
<swp-activity-icon class="communication"><img src="icons/envelope.svg" alt=""></swp-activity-icon>
<swp-activity-content>
<swp-activity-title>
<strong>Email sendt</strong> — Booking bekræftelse
<swp-activity-badge class="auto">Auto</swp-activity-badge>
</swp-activity-title>
<swp-activity-meta>
<swp-activity-time>08:00</swp-activity-time>
<swp-activity-actor>System</swp-activity-actor>
</swp-activity-meta>
</swp-activity-content>
</swp-activity-item>
</swp-activity-date-group>
<!-- Marts 2024 -->
<swp-activity-date-group>
<swp-activity-date-header>15. marts 2024</swp-activity-date-header>
<swp-activity-item>
<swp-activity-icon class="system"><img src="icons/created.svg" alt=""></swp-activity-icon>
<swp-activity-content>
<swp-activity-title>
<strong>Kunde oprettet</strong>
</swp-activity-title>
<swp-activity-meta>
<swp-activity-time>10:30</swp-activity-time>
<swp-activity-actor>Nina K.</swp-activity-actor>
</swp-activity-meta>
<swp-activity-details>
Oprettet manuelt ved første besøg. Kilde: Walk-in
</swp-activity-details>
</swp-activity-content>
</swp-activity-item>
</swp-activity-date-group>
</swp-activity-timeline>
<swp-activity-load-more>Indlæs ældre aktivitet...</swp-activity-load-more>
</swp-card>
</swp-tab-content>
<script>
// ==========================================
// TAB SWITCHING
// ==========================================
document.querySelectorAll('swp-tab').forEach(tab => {
tab.addEventListener('click', () => {
const tabName = tab.dataset.tab;
// Update tab active state
document.querySelectorAll('swp-tab').forEach(t => t.classList.remove('active'));
tab.classList.add('active');
// Update content visibility
document.querySelectorAll('swp-tab-content').forEach(content => {
content.classList.remove('active');
if (content.dataset.tab === tabName) {
content.classList.add('active');
}
});
});
});
// ==========================================
// TOGGLE SLIDERS (Ja/Nej)
// ==========================================
document.querySelectorAll('swp-toggle-slider').forEach(slider => {
const options = slider.querySelectorAll('swp-toggle-option');
options.forEach((option, index) => {
option.addEventListener('click', () => {
slider.dataset.value = index === 0 ? 'yes' : 'no';
});
});
});
// ==========================================
// TAG REMOVAL
// ==========================================
document.querySelectorAll('swp-tag .remove').forEach(btn => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
btn.closest('swp-tag').remove();
});
});
// ==========================================
// BOOKING EXCLUSION TOGGLE
// ==========================================
const bookingExclusion = document.getElementById('bookingExclusion');
bookingExclusion.addEventListener('click', () => {
const isExcluded = bookingExclusion.dataset.excluded === 'true';
bookingExclusion.dataset.excluded = isExcluded ? 'false' : 'true';
const icon = bookingExclusion.querySelector('.icon');
const text = bookingExclusion.querySelector('.text');
if (isExcluded) {
icon.textContent = '✓';
text.textContent = 'Booking tilladt';
} else {
icon.textContent = '✕';
text.textContent = 'Udelukket fra booking';
}
});
// ==========================================
// CUSTOMER GROUP DROPDOWN
// ==========================================
const customerGroupSelect = document.getElementById('customerGroupSelect');
const customerGroupDropdown = document.getElementById('customerGroupDropdown');
customerGroupSelect.addEventListener('click', (e) => {
e.stopPropagation();
customerGroupDropdown.classList.toggle('open');
});
document.querySelectorAll('swp-customer-group-option').forEach(option => {
option.addEventListener('click', () => {
// Update selection
document.querySelectorAll('swp-customer-group-option').forEach(o => o.classList.remove('selected'));
option.classList.add('selected');
// Update display
customerGroupSelect.querySelector('.value').textContent = option.textContent;
// Close dropdown
customerGroupDropdown.classList.remove('open');
});
});
// Close dropdown when clicking outside
document.addEventListener('click', () => {
customerGroupDropdown.classList.remove('open');
});
// ==========================================
// RELATION REMOVE
// ==========================================
document.querySelectorAll('swp-relation-remove').forEach(btn => {
btn.addEventListener('click', () => {
btn.closest('swp-relation-item').remove();
});
});
// ==========================================
// SAVE BUTTON
// ==========================================
const saveBtn = document.getElementById('saveBtn');
saveBtn.addEventListener('click', () => {
// Simulate save
saveBtn.textContent = 'Gemt!';
saveBtn.style.background = 'var(--color-green)';
setTimeout(() => {
saveBtn.textContent = 'Gem';
saveBtn.style.background = '';
}, 2000);
});
// ==========================================
// CHART (lazy load)
// ==========================================
let chartLoaded = false;
function loadChart() {
if (chartLoaded) return;
import('https://unpkg.com/@sevenweirdpeople/swp-charting@latest/dist/index.js')
.then(module => {
const { createChart } = module;
createChart(document.getElementById('revenueChart'), {
width: 1100,
height: 200,
xAxis: {
categories: ['Jan', 'Feb', 'Mar', 'Apr', 'Maj', 'Jun', 'Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec']
},
series: [
{
name: 'Services',
color: '#00897b',
data: [
{ x: 'Mar', y: 1200 },
{ x: 'Apr', y: 800 },
{ x: 'Jun', y: 1500 },
{ x: 'Aug', y: 1600 },
{ x: 'Sep', y: 750 },
{ x: 'Okt', y: 550 },
{ x: 'Nov', y: 1200 },
{ x: 'Dec', y: 1450 }
]
},
{
name: 'Produkter',
color: '#1976d2',
data: [
{ x: 'Apr', y: 250 },
{ x: 'Jun', y: 180 },
{ x: 'Sep', y: 320 },
{ x: 'Dec', y: 349 }
]
}
],
legend: false
});
chartLoaded = true;
});
}
// Load chart when Økonomi tab is shown
document.querySelector('swp-tab[data-tab="economy"]').addEventListener('click', loadChart);
// ==========================================
// JOURNAL MINI TABS
// ==========================================
document.querySelectorAll('swp-journal-mini-tab').forEach(tab => {
tab.addEventListener('click', () => {
document.querySelectorAll('swp-journal-mini-tab').forEach(t => t.classList.remove('active'));
tab.classList.add('active');
// In a real app, this would filter the entries
});
});
// ==========================================
// JOURNAL ENTRY DELETE
// ==========================================
document.querySelectorAll('swp-journal-entry-delete-new').forEach(btn => {
btn.addEventListener('click', () => {
btn.closest('swp-journal-entry-new').remove();
});
});
// ==========================================
// ACTIVITY FILTERS
// ==========================================
document.querySelectorAll('swp-activity-filter').forEach(filter => {
filter.addEventListener('click', () => {
document.querySelectorAll('swp-activity-filter').forEach(f => f.classList.remove('active'));
filter.classList.add('active');
// In a real app, this would filter the activity items
});
});
// ==========================================
// CARD DRAG & DROP
// ==========================================
let draggedCard = null;
let dropIndicator = null;
// Create drop indicator element
function createDropIndicator() {
if (!dropIndicator) {
dropIndicator = document.createElement('swp-drop-indicator');
}
return dropIndicator;
}
function removeDropIndicator() {
if (dropIndicator && dropIndicator.parentNode) {
dropIndicator.remove();
}
}
function clearDropIndicators() {
removeDropIndicator();
document.querySelectorAll('.drag-over-empty').forEach(el => {
el.classList.remove('drag-over-empty');
});
}
// Find the closest card and position based on mouse Y
function findDropPosition(column, mouseY) {
const cards = Array.from(column.querySelectorAll('swp-card[draggable="true"]:not(.dragging)'));
if (cards.length === 0) return { card: null, position: null };
for (let i = 0; i < cards.length; i++) {
const card = cards[i];
const rect = card.getBoundingClientRect();
const cardMiddle = rect.top + rect.height / 2;
// If mouse is above the middle of this card, insert before it
if (mouseY < cardMiddle) {
return { card, position: 'before' };
}
}
// Mouse is below all cards, insert after the last one
return { card: cards[cards.length - 1], position: 'after' };
}
// Setup draggable cards
document.querySelectorAll('swp-card[draggable="true"]').forEach(card => {
card.addEventListener('dragstart', (e) => {
draggedCard = card;
card.classList.add('dragging');
e.dataTransfer.effectAllowed = 'move';
e.dataTransfer.setData('text/plain', card.dataset.cardId);
setTimeout(() => {
card.style.opacity = '0.4';
}, 0);
});
card.addEventListener('dragend', () => {
card.classList.remove('dragging');
card.style.opacity = '';
draggedCard = null;
clearDropIndicators();
});
});
// Handle all dragover at column level for seamless detection
document.querySelectorAll('swp-card-column').forEach(column => {
column.addEventListener('dragover', (e) => {
e.preventDefault();
if (!draggedCard) return;
e.dataTransfer.dropEffect = 'move';
const cards = column.querySelectorAll('swp-card[draggable="true"]:not(.dragging)');
if (cards.length === 0) {
// Empty column
column.classList.add('drag-over-empty');
removeDropIndicator();
return;
}
column.classList.remove('drag-over-empty');
// Find where to show the indicator
const { card, position } = findDropPosition(column, e.clientY);
if (card) {
const indicator = createDropIndicator();
if (position === 'before') {
if (indicator.nextElementSibling !== card) {
card.before(indicator);
}
} else {
if (indicator.previousElementSibling !== card) {
card.after(indicator);
}
}
}
});
column.addEventListener('dragleave', (e) => {
if (!column.contains(e.relatedTarget)) {
column.classList.remove('drag-over-empty');
removeDropIndicator();
}
});
column.addEventListener('drop', (e) => {
e.preventDefault();
if (!draggedCard) return;
// Insert dragged card where the indicator is, or at end of empty column
if (dropIndicator && dropIndicator.parentNode) {
dropIndicator.before(draggedCard);
} else if (column.classList.contains('drag-over-empty')) {
column.appendChild(draggedCard);
}
clearDropIndicators();
});
});
// Also handle drop on the indicator itself
document.addEventListener('dragover', (e) => {
if (e.target.tagName === 'SWP-DROP-INDICATOR') {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
}
});
document.addEventListener('drop', (e) => {
if (e.target.tagName === 'SWP-DROP-INDICATOR' && draggedCard) {
e.preventDefault();
dropIndicator.before(draggedCard);
clearDropIndicators();
}
});
</script>
</body>
</html>