Calendar/wwwroot/poc-detail-drawer.html
Janus C. H. Knudsen db4e8b7380 Enhances customer details and journal UI components
Adds new customer panel with detailed profile and revenue visualization
Implements journal panel with historical entries and interaction
Introduces new SVG icons for improved user interface

Adds @sevenweirdpeople/swp-charting for customer revenue chart
2025-12-21 12:23:49 +01:00

2030 lines
59 KiB
HTML

<!DOCTYPE html>
<html lang="da">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Detail Drawer POC</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
/* Import design tokens fra calendar-base.css */
:root {
--hour-height: 64px;
--time-axis-width: 60px;
--header-height: 70px;
/* Colors - UI */
--color-border: #e0e0e0;
--color-surface: #fff;
--color-background: #f5f5f5;
--color-background-hover: #f0f0f0;
--color-background-alt: #fafafa;
--color-text: #333333;
--color-text-secondary: #666;
--color-primary: #1976d2;
/* Named color palette */
--b-color-blue: #1e88e5;
--b-color-green: #43a047;
--b-color-red: #e53935;
--b-color-amber: #ffb300;
--b-color-purple: #8e24aa;
--b-color-teal: #00897b;
--b-mix: #fff;
/* Checkout style tokens */
--color-teal: #00897b;
--font-mono: 'JetBrains Mono', monospace;
/* Shadows */
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.1);
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1);
/* Transitions */
--transition-fast: 150ms ease;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
background: var(--color-background);
}
/* Demo layout */
.demo-container {
padding: 16px;
}
.demo-buttons {
display: flex;
gap: 8px;
margin-bottom: 16px;
flex-wrap: wrap;
}
.demo-btn {
padding: 6px 12px;
border: 1px solid var(--color-border);
border-radius: 4px;
cursor: pointer;
background: var(--color-surface);
font-size: 12px;
transition: background var(--transition-fast);
}
.demo-btn:hover {
background: var(--color-background-hover);
}
.demo-btn-primary {
background: var(--color-primary);
color: white;
border-color: var(--color-primary);
}
.demo-btn-primary:hover {
background: #1565c0;
}
/* Calendar mock */
.calendar-mock {
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 4px;
height: 500px;
display: grid;
grid-template-columns: repeat(5, 1fr);
}
.calendar-mock-column {
border-right: 1px solid var(--color-border);
padding: 8px;
}
.calendar-mock-column:last-child {
border-right: none;
}
.mock-event {
--b-primary: var(--b-color-blue);
background-color: color-mix(in srgb, var(--b-primary) 10%, var(--b-mix));
border-left: 4px solid var(--b-primary);
padding: 4px 6px;
margin-bottom: 8px;
border-radius: 3px;
cursor: pointer;
font-size: 12px;
transition: background-color 200ms ease;
}
.mock-event:hover {
background-color: color-mix(in srgb, var(--b-primary) 15%, var(--b-mix));
}
.mock-event-time {
font-size: 0.875rem;
font-weight: 500;
}
.mock-event-title {
font-size: 0.875rem;
}
/* ==========================================
DETAIL DRAWER
========================================== */
swp-detail-drawer-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.2);
opacity: 0;
visibility: hidden;
transition: opacity 200ms ease, visibility 200ms ease;
z-index: 1000;
}
swp-detail-drawer-overlay.open {
opacity: 1;
visibility: visible;
}
swp-detail-drawer {
position: fixed;
top: 0;
right: 0;
bottom: 0;
width: 340px;
background: var(--color-surface);
border-left: 1px solid var(--color-border);
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
transform: translateX(100%);
transition: transform 200ms ease;
display: flex;
flex-direction: column;
z-index: 1001;
}
swp-detail-drawer.open {
transform: translateX(0);
}
/* Drawer header */
swp-drawer-header {
display: flex;
flex-direction: column;
gap: 2px;
padding: 0;
border-bottom: 1px solid var(--color-border);
}
swp-drawer-title {
display: flex;
justify-content: center;
padding: 14px 16px;
font-size: 14px;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.5px;
background: var(--color-background);
color: var(--color-text-secondary);
}
swp-drawer-title.source-web {
--b-primary: var(--b-color-blue);
background: color-mix(in srgb, var(--b-primary) 15%, var(--b-mix));
color: var(--b-primary);
}
swp-drawer-title.source-recurring {
--b-primary: var(--b-color-purple);
background: color-mix(in srgb, var(--b-primary) 15%, var(--b-mix));
color: var(--b-primary);
}
swp-drawer-title.source-manual {
--b-primary: var(--b-color-teal);
background: color-mix(in srgb, var(--b-primary) 15%, var(--b-mix));
color: var(--b-primary);
}
swp-created-date {
font-size: 10px;
color: var(--color-text-secondary);
opacity: 0.8;
padding: 4px 16px 8px;
text-align: right;
background: var(--color-background-alt);
}
/* Drawer content */
swp-drawer-content {
flex: 1;
overflow-y: auto;
padding: 16px;
display: flex;
flex-direction: column;
gap: 16px;
}
/* Section styling */
swp-drawer-section {
display: block;
}
swp-section-label {
display: block;
font-size: 11px;
font-weight: 400;
color: var(--color-text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 10px;
}
/* Event time/date */
swp-event-info {
display: block;
padding-bottom: 16px;
border-bottom: 1px solid var(--color-border);
}
swp-event-time-display {
display: block;
font-size: 20px;
font-weight: 500;
color: var(--color-text);
}
swp-event-date-display {
display: block;
font-size: 14px;
color: var(--color-text-secondary);
margin-top: 2px;
}
/* Customer section */
swp-customer-info {
display: block;
}
swp-customer-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
}
swp-customer-name {
font-size: 18px;
font-weight: 500;
color: var(--color-text);
}
swp-customer-phone {
font-size: 14px;
color: var(--color-teal);
}
swp-customer-phone a {
color: var(--color-teal);
text-decoration: none;
}
swp-customer-phone a:hover {
text-decoration: underline;
}
swp-customer-details-link {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 13px;
color: var(--color-text-secondary);
cursor: pointer;
margin-top: 6px;
transition: color 150ms ease;
}
swp-customer-details-link:hover {
color: var(--color-teal);
}
/* Chevron icon styling */
.link-chevron {
width: 18px;
height: 18px;
transition: transform 200ms ease;
filter: invert(45%) sepia(0%) saturate(0%) brightness(90%);
}
swp-customer-details-link:hover .link-chevron,
swp-journal-details-link:hover .link-chevron {
filter: invert(32%) sepia(98%) saturate(1234%) hue-rotate(152deg) brightness(93%) contrast(92%);
}
swp-customer-details-link.panel-open .link-chevron,
swp-journal-details-link.panel-open .link-chevron {
transform: rotate(90deg);
}
/* Status */
swp-status-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 12px;
background: var(--color-background-alt);
border-radius: 4px;
border: 1px solid var(--color-border);
}
swp-status-label {
font-size: 14px;
color: var(--color-text-secondary);
}
swp-status-dot {
width: 6px;
height: 6px;
border-radius: 50%;
}
/* Status variants - using color-mix like events */
.status-badge.status-created {
--b-primary: var(--b-color-purple);
background: color-mix(in srgb, var(--b-primary) 15%, var(--b-mix));
color: var(--b-primary);
}
.status-badge.status-created swp-status-dot { background: var(--b-color-purple); }
.status-badge.status-arrived {
--b-primary: var(--b-color-green);
background: color-mix(in srgb, var(--b-primary) 15%, var(--b-mix));
color: var(--b-primary);
}
.status-badge.status-arrived swp-status-dot { background: var(--b-color-green); }
.status-badge.status-paid {
--b-primary: var(--b-color-blue);
background: color-mix(in srgb, var(--b-primary) 15%, var(--b-mix));
color: var(--b-primary);
}
.status-badge.status-paid swp-status-dot { background: var(--b-color-blue); }
.status-badge.status-noshow {
--b-primary: var(--b-color-red);
background: color-mix(in srgb, var(--b-primary) 15%, var(--b-mix));
color: var(--b-primary);
}
.status-badge.status-noshow swp-status-dot { background: var(--b-color-red); }
.status-badge.status-cancelled {
background: var(--color-background-alt);
color: var(--color-text-secondary);
border: 1px solid var(--color-border);
}
.status-badge.status-cancelled swp-status-dot { background: var(--color-text-secondary); }
/* Services list */
swp-services-list {
display: flex;
flex-direction: column;
gap: 8px;
}
swp-service-item {
--b-primary: var(--b-color-blue);
display: block;
padding: 10px 12px;
background: color-mix(in srgb, var(--b-primary) 5%, var(--b-mix));
border-left: 3px solid var(--b-primary);
border-radius: 3px;
}
swp-service-name {
display: block;
font-size: 15px;
font-weight: 500;
color: var(--color-text);
}
swp-service-details {
display: block;
font-size: 13px;
color: var(--color-text-secondary);
margin-top: 2px;
}
swp-service-price {
font-family: var(--font-mono);
font-weight: 500;
}
swp-service-resource {
display: block;
font-size: 13px;
color: var(--color-text-secondary);
margin-top: 4px;
opacity: 0.8;
}
/* Total */
swp-total-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
border-top: 1px solid var(--color-border);
border-bottom: 1px solid var(--color-border);
margin-top: 4px;
}
swp-total-label {
font-size: 16px;
font-weight: 600;
color: var(--color-text);
}
swp-total-amount {
font-family: var(--font-mono);
font-size: 24px;
font-weight: 700;
color: var(--color-text);
}
/* Notes */
swp-notes-box {
display: block;
padding: 10px 12px;
background: color-mix(in srgb, var(--b-color-amber) 10%, var(--b-mix));
border-left: 3px solid var(--b-color-amber);
border-radius: 3px;
font-size: 14px;
color: var(--color-text);
font-style: italic;
}
/* Journal link */
swp-journal-link {
display: flex;
align-items: center;
gap: 10px;
padding: 12px;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 4px;
cursor: pointer;
transition: all var(--transition-fast);
}
swp-journal-link:hover {
background: var(--color-background-hover);
border-color: var(--color-teal);
}
swp-journal-icon {
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
}
swp-journal-icon img {
width: 20px;
height: 20px;
filter: invert(22%) sepia(14%) saturate(1042%) hue-rotate(164deg) brightness(102%) contrast(85%);
transition: filter 150ms ease;
}
swp-journal-link:hover swp-journal-icon img {
filter: invert(32%) sepia(98%) saturate(1234%) hue-rotate(196deg) brightness(93%) contrast(92%);
}
swp-journal-link.no-journal swp-journal-icon img {
filter: invert(45%) sepia(8%) saturate(0%) hue-rotate(0deg) brightness(95%) contrast(90%);
}
swp-journal-link.no-journal:hover swp-journal-icon img {
filter: invert(32%) sepia(98%) saturate(1234%) hue-rotate(196deg) brightness(93%) contrast(92%);
}
swp-journal-text {
flex: 1;
font-size: 14px;
color: var(--color-text);
}
swp-journal-arrow {
font-size: 14px;
color: var(--color-text-secondary);
}
/* Journal not created state */
swp-journal-link.no-journal {
border-style: dashed;
background: var(--color-background-alt);
}
swp-journal-link.no-journal swp-journal-text {
color: var(--color-text-secondary);
}
swp-journal-link.no-journal:hover {
background: var(--color-surface);
border-color: var(--color-teal);
border-style: solid;
}
/* Journal Panel - slides out from left of drawer */
swp-journal-panel {
position: fixed;
top: 0;
right: 340px;
bottom: 0;
width: 440px;
background: var(--color-surface);
border-left: 1px solid var(--color-border);
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
transform: translateX(100%);
opacity: 0;
transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1),
opacity 200ms ease;
display: flex;
flex-direction: column;
z-index: 1000;
}
swp-journal-panel.open {
transform: translateX(0);
opacity: 1;
}
swp-journal-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 20px;
border-bottom: 1px solid var(--color-border);
background: var(--color-background-alt);
}
swp-journal-header-title {
font-size: 16px;
font-weight: 600;
color: var(--color-text);
}
swp-journal-close {
background: none;
border: none;
font-size: 18px;
color: var(--color-text-secondary);
cursor: pointer;
padding: 4px 8px;
border-radius: 4px;
transition: background 150ms ease;
}
swp-journal-close:hover {
background: var(--color-background-hover);
}
/* Journal Tabs - Traditional style */
swp-journal-tabs {
display: flex;
padding: 0 20px;
background: var(--color-background-alt);
border-bottom: 1px solid var(--color-border);
}
swp-journal-tab {
display: inline-flex;
align-items: center;
gap: 8px;
padding: 14px 18px;
font-size: 13px;
font-weight: 500;
color: var(--color-text-secondary);
background: transparent;
border: none;
border-bottom: 2px solid transparent;
margin-bottom: -1px;
cursor: pointer;
transition: all 150ms ease;
}
swp-journal-tab:hover:not(.disabled) {
color: var(--color-text);
background: var(--color-background-hover);
}
swp-journal-tab.active {
color: var(--color-text);
background: var(--color-surface);
border-bottom-color: var(--color-teal);
}
swp-journal-tab.disabled {
opacity: 0.5;
cursor: not-allowed;
}
.tab-indicator {
width: 8px;
height: 8px;
border-radius: 2px;
flex-shrink: 0;
}
.tab-indicator.blue {
background: var(--b-color-blue);
}
.tab-indicator.amber {
background: var(--b-color-amber);
}
.tab-indicator.gray {
background: var(--color-text-secondary);
}
/* Tab Content Header */
swp-journal-tab-header {
display: flex;
flex-direction: column;
gap: 8px;
padding: 16px 20px;
border-bottom: 1px solid var(--color-border);
}
swp-journal-tab-title {
font-size: 15px;
font-weight: 600;
color: var(--color-text);
}
swp-journal-tab-title span {
font-weight: 400;
color: var(--color-text-secondary);
}
swp-journal-tab-desc {
font-size: 12px;
color: var(--color-text-secondary);
line-height: 1.4;
}
swp-journal-add-btn {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 8px 14px;
font-size: 13px;
font-weight: 500;
color: white;
background: var(--color-teal);
border-radius: 6px;
cursor: pointer;
margin-top: 4px;
align-self: flex-start;
transition: background 150ms ease;
}
swp-journal-add-btn:hover {
background: #00796b;
}
/* Journal Content */
swp-journal-content {
flex: 1;
overflow-y: auto;
padding: 16px 20px;
display: flex;
flex-direction: column;
gap: 12px;
}
/* Journal Entry Card */
swp-journal-entry {
display: flex;
flex-direction: column;
gap: 10px;
padding: 14px 16px;
background: var(--color-background-alt);
border-radius: 8px;
border: 1px solid var(--color-border);
}
swp-journal-entry-meta {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px;
}
swp-journal-entry-type {
padding: 4px 10px;
font-size: 11px;
font-weight: 600;
color: #92400e;
background: color-mix(in srgb, var(--b-color-amber) 20%, var(--b-mix));
border-radius: 4px;
}
swp-journal-entry-date {
font-size: 12px;
color: var(--color-text-secondary);
}
swp-journal-entry-author {
font-size: 12px;
color: var(--color-text-secondary);
}
swp-journal-entry-tags {
display: flex;
flex-wrap: wrap;
gap: 6px;
margin-left: auto;
}
swp-journal-tag {
padding: 3px 10px;
font-size: 11px;
color: var(--color-text-secondary);
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 12px;
}
swp-journal-entry-delete {
padding: 4px 6px;
font-size: 14px;
color: var(--color-text-secondary);
cursor: pointer;
border-radius: 4px;
transition: all 150ms ease;
opacity: 0.6;
}
swp-journal-entry-delete:hover {
color: var(--b-color-red);
background: color-mix(in srgb, var(--b-color-red) 10%, var(--b-mix));
opacity: 1;
}
swp-journal-entry-text {
font-size: 13px;
color: var(--color-text);
line-height: 1.5;
}
/* Journal Profile Section */
swp-journal-profile-section {
padding: 16px 20px;
border-top: 1px solid var(--color-border);
background: var(--color-surface);
}
swp-journal-profile-toggle {
display: flex;
align-items: center;
gap: 6px;
font-size: 13px;
color: var(--color-teal);
cursor: pointer;
transition: color 150ms ease;
}
swp-journal-profile-toggle:hover {
color: #00796b;
}
swp-journal-profile-toggle .toggle-arrow {
font-size: 10px;
transition: transform 200ms ease;
}
swp-journal-profile-toggle.collapsed .toggle-arrow {
transform: rotate(-90deg);
}
swp-journal-profile-boxes {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
margin-top: 12px;
max-height: 200px;
overflow: hidden;
transition: max-height 200ms ease, margin-top 200ms ease, opacity 200ms ease;
}
swp-journal-profile-boxes.collapsed {
max-height: 0;
margin-top: 0;
opacity: 0;
}
/* Customer Details Panel - double width drawer, slides under like journal */
swp-customer-panel {
position: fixed;
top: 0;
right: 340px;
bottom: 0;
width: 680px;
background: var(--color-surface);
border-left: 1px solid var(--color-border);
box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);
transform: translateX(100%);
opacity: 0;
transition: transform 250ms cubic-bezier(0.4, 0, 0.2, 1),
opacity 200ms ease;
display: flex;
flex-direction: column;
z-index: 1000;
overflow: hidden;
}
swp-customer-panel.open {
transform: translateX(0);
opacity: 1;
}
swp-customer-panel-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 20px 24px;
border-bottom: 1px solid var(--color-border);
background: var(--color-background-alt);
}
swp-customer-panel-title {
display: flex;
align-items: center;
gap: 8px;
font-size: 18px;
font-weight: 600;
color: var(--color-text);
}
.panel-header-icon {
width: 20px;
height: 20px;
filter: invert(22%) sepia(14%) saturate(1042%) hue-rotate(164deg) brightness(102%) contrast(85%);
}
swp-customer-panel-close {
background: none;
border: none;
font-size: 20px;
color: var(--color-text-secondary);
cursor: pointer;
padding: 4px 8px;
border-radius: 4px;
transition: background 150ms ease;
}
swp-customer-panel-close:hover {
background: var(--color-background-hover);
}
swp-customer-panel-content {
flex: 1;
overflow-y: auto;
overflow-x: hidden;
padding: 24px;
display: flex;
flex-direction: column;
gap: 24px;
min-height: 0;
}
/* Customer info header */
swp-customer-header-section {
display: flex;
gap: 24px;
padding-bottom: 20px;
border-bottom: 1px solid var(--color-border);
}
swp-customer-avatar {
width: 80px;
height: 80px;
border-radius: 50%;
background: linear-gradient(135deg, var(--color-teal), #4db6ac);
display: flex;
align-items: center;
justify-content: center;
font-size: 32px;
font-weight: 600;
color: white;
flex-shrink: 0;
}
swp-customer-header-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
}
swp-customer-header-top {
display: flex;
justify-content: space-between;
align-items: flex-start;
}
swp-customer-header-left {
display: flex;
flex-direction: column;
gap: 4px;
}
swp-customer-header-name {
font-size: 24px;
font-weight: 600;
color: var(--color-text);
}
swp-customer-header-contact {
display: flex;
flex-direction: column;
gap: 6px;
align-items: flex-end;
}
swp-customer-header-contact-item {
font-size: 14px;
}
swp-customer-header-contact-item a {
color: var(--color-teal);
text-decoration: none;
}
swp-customer-header-contact-item a:hover {
text-decoration: underline;
}
swp-customer-since {
font-size: 12px;
color: var(--color-text-secondary);
}
/* Fact boxes grid */
swp-fact-boxes {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
}
swp-fact-box {
padding: 16px;
background: var(--color-background-alt);
border-radius: 8px;
border: 1px solid var(--color-border);
text-align: center;
}
swp-fact-box-value {
display: block;
font-family: var(--font-mono);
font-size: 20px;
font-weight: 700;
color: var(--color-text);
margin-bottom: 4px;
}
swp-fact-box-label {
display: block;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--color-text-secondary);
}
swp-fact-box.highlight {
background: linear-gradient(135deg, rgba(0, 137, 123, 0.1), rgba(0, 137, 123, 0.05));
border-color: var(--color-teal);
}
swp-fact-box.highlight swp-fact-box-value {
color: var(--color-teal);
}
/* Chart section */
swp-chart-section {
display: block;
}
swp-chart-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
}
swp-chart-title {
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--color-text-secondary);
}
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(--b-color-blue);
}
swp-chart-container {
display: block;
background: var(--color-background-alt);
border-radius: 8px;
border: 1px solid var(--color-border);
}
/* Products list */
swp-products-section {
display: block;
}
swp-products-list {
display: flex;
flex-direction: column;
gap: 8px;
}
swp-product-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 14px;
background: var(--color-background-alt);
border-radius: 6px;
border: 1px solid var(--color-border);
}
swp-product-info {
display: flex;
flex-direction: column;
gap: 2px;
}
swp-product-name {
font-size: 14px;
font-weight: 500;
color: var(--color-text);
}
swp-product-date {
font-size: 12px;
color: var(--color-text-secondary);
}
swp-product-price {
font-family: var(--font-mono);
font-size: 14px;
font-weight: 500;
color: var(--color-text);
}
/* Preferences section */
swp-preferences-section {
display: block;
}
swp-preferences-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
swp-preference-item {
padding: 12px 14px;
background: var(--color-background-alt);
border-radius: 6px;
border: 1px solid var(--color-border);
}
swp-preference-label {
display: block;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--color-text-secondary);
margin-bottom: 4px;
}
swp-preference-value {
font-size: 14px;
color: var(--color-text);
}
/* Customer stats */
swp-stats-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
swp-stat-item {
display: flex;
flex-direction: column;
padding: 10px 12px;
background: var(--color-background-alt);
border-radius: 4px;
border: 1px solid var(--color-border);
}
swp-stat-value {
font-size: 16px;
font-weight: 600;
color: var(--color-text);
}
swp-stat-value.mono {
font-family: var(--font-mono);
}
swp-stat-label {
font-size: 11px;
color: var(--color-text-secondary);
margin-top: 2px;
}
/* Profile boxes */
swp-profile-boxes {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 8px;
}
swp-profile-box {
padding: 10px 12px;
background: var(--color-background-alt);
border-radius: 4px;
border: 1px solid var(--color-border);
}
swp-profile-box.warning {
background: color-mix(in srgb, var(--b-color-amber) 10%, var(--b-mix));
border-color: var(--b-color-amber);
}
swp-profile-box-label {
display: block;
font-size: 10px;
text-transform: uppercase;
letter-spacing: 0.5px;
color: var(--color-text-secondary);
margin-bottom: 3px;
}
swp-profile-box.warning swp-profile-box-label {
color: #b8860b;
}
swp-profile-box-value {
display: block;
font-size: 12px;
color: var(--color-text);
line-height: 1.3;
}
swp-journal-details-link {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 13px;
color: var(--color-text-secondary);
cursor: pointer;
margin-top: 10px;
transition: color 150ms ease;
}
swp-journal-details-link:hover {
color: var(--color-teal);
}
swp-journal-details-link.no-journal {
color: var(--color-teal);
}
/* Drawer footer */
swp-drawer-footer {
display: flex;
gap: 8px;
padding: 16px;
border-top: 1px solid var(--color-border);
background: var(--color-surface);
}
swp-footer-btn {
flex: 1;
padding: 12px 16px;
border: 1px solid var(--color-border);
border-radius: 4px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all var(--transition-fast);
text-align: center;
}
swp-footer-btn.btn-primary {
background: var(--color-teal);
color: white;
border-color: var(--color-teal);
}
swp-footer-btn.btn-primary:hover {
background: #00796b;
}
swp-footer-btn.btn-secondary {
background: var(--color-surface);
color: var(--color-text);
}
swp-footer-btn.btn-secondary:hover {
background: var(--color-background-hover);
}
/* Footer states based on booking status */
swp-drawer-footer.status-paid swp-footer-btn.btn-primary {
background: var(--color-teal);
border-color: var(--color-teal);
opacity: 0.7;
cursor: default;
}
swp-drawer-footer.status-cancelled swp-footer-btn.btn-primary,
swp-drawer-footer.status-noshow swp-footer-btn.btn-primary {
background: var(--color-text-secondary);
border-color: var(--color-text-secondary);
cursor: not-allowed;
opacity: 0.6;
}
/* ==========================================
STATUS DROPDOWN (Popover API)
========================================== */
swp-status-row {
position: relative;
anchor-name: --status-row;
}
.status-badge {
display: inline-flex;
align-items: center;
gap: 6px;
padding: 3px 8px;
border: none;
border-radius: 3px;
font-size: 13px;
font-weight: 500;
font-family: inherit;
cursor: pointer;
transition: transform var(--transition-fast);
}
.status-badge:hover {
transform: scale(1.02);
}
#statusText {
margin-top: -2px;
}
swp-status-dropdown {
margin: 0;
padding: 4px;
background: var(--color-surface);
border: 1px solid var(--color-border);
border-radius: 4px;
box-shadow: var(--shadow-md);
display: flex;
flex-direction: column;
gap: 2px;
/* Animation */
opacity: 0;
transform: translateY(-8px);
transition: opacity 150ms ease, transform 150ms ease, display 150ms ease allow-discrete;
}
swp-status-dropdown:popover-open {
opacity: 1;
transform: translateY(0);
}
@starting-style {
swp-status-dropdown:popover-open {
opacity: 0;
transform: translateY(-8px);
}
}
swp-status-option {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 10px;
border-radius: 3px;
cursor: pointer;
font-size: 13px;
font-weight: 500;
opacity: 0;
transform: translateX(10px);
transition: background var(--transition-fast), opacity 150ms ease, transform 150ms ease;
}
swp-status-dropdown:popover-open swp-status-option {
opacity: 1;
transform: translateX(0);
}
/* Stagger animation delays */
swp-status-dropdown:popover-open swp-status-option:nth-child(1) { transition-delay: 0ms; }
swp-status-dropdown:popover-open swp-status-option:nth-child(2) { transition-delay: 30ms; }
swp-status-dropdown:popover-open swp-status-option:nth-child(3) { transition-delay: 60ms; }
swp-status-dropdown:popover-open swp-status-option:nth-child(4) { transition-delay: 90ms; }
swp-status-dropdown:popover-open swp-status-option:nth-child(5) { transition-delay: 120ms; }
swp-status-option:hover {
background: var(--color-background-hover);
}
swp-status-option swp-status-dot {
width: 8px;
height: 8px;
}
swp-status-option swp-status-check {
margin-left: auto;
font-size: 12px;
opacity: 0;
}
swp-status-option.selected swp-status-check {
opacity: 1;
}
/* Status option colors */
swp-status-option.opt-created { color: var(--b-color-purple); }
swp-status-option.opt-created swp-status-dot { background: var(--b-color-purple); }
swp-status-option.opt-arrived { color: var(--b-color-green); }
swp-status-option.opt-arrived swp-status-dot { background: var(--b-color-green); }
swp-status-option.opt-paid { color: var(--b-color-blue); }
swp-status-option.opt-paid swp-status-dot { background: var(--b-color-blue); }
swp-status-option.opt-noshow { color: var(--b-color-red); }
swp-status-option.opt-noshow swp-status-dot { background: var(--b-color-red); }
swp-status-option.opt-cancelled { color: var(--color-text-secondary); }
swp-status-option.opt-cancelled swp-status-dot { background: var(--color-text-secondary); }
</style>
</head>
<body>
<div class="demo-container">
<p style="font-size: 12px; color: var(--color-text-secondary); margin-bottom: 12px;">
Dobbeltklik på en event eller brug knapperne
</p>
<div class="demo-buttons">
<button class="demo-btn demo-btn-primary" onclick="openDrawer('arrived', 'web', true)">Online + journal</button>
<button class="demo-btn" onclick="openDrawer('created', 'manual', false)">Manuel</button>
<button class="demo-btn" onclick="openDrawer('paid', 'recurring', true)">Recurring</button>
<button class="demo-btn" onclick="openDrawer('noshow', 'web', false)">No-show</button>
<button class="demo-btn" onclick="openDrawer('cancelled', 'manual', true)">Cancelled</button>
</div>
<div class="calendar-mock">
<div class="calendar-mock-column">
<div class="mock-event" ondblclick="openDrawer('arrived')">
<div class="mock-event-time">10:00 - 11:00</div>
<div class="mock-event-title">Sofie Nielsen</div>
</div>
<div class="mock-event" ondblclick="openDrawer('created')">
<div class="mock-event-time">14:00 - 15:30</div>
<div class="mock-event-title">Lars Pedersen</div>
</div>
</div>
<div class="calendar-mock-column">
<div class="mock-event" ondblclick="openDrawer('paid')">
<div class="mock-event-time">09:00 - 10:00</div>
<div class="mock-event-title">Marie Hansen</div>
</div>
</div>
<div class="calendar-mock-column">
<div class="mock-event" ondblclick="openDrawer('noshow')">
<div class="mock-event-time">11:00 - 12:00</div>
<div class="mock-event-title">Peter Jensen</div>
</div>
</div>
<div class="calendar-mock-column"></div>
<div class="calendar-mock-column"></div>
</div>
</div>
<!-- Overlay -->
<swp-detail-drawer-overlay id="drawerOverlay" onclick="closeDrawer()"></swp-detail-drawer-overlay>
<!-- Journal Panel -->
<swp-journal-panel id="journalPanel">
<swp-journal-header>
<swp-journal-header-title>Journal</swp-journal-header-title>
<swp-journal-close onclick="closeJournal()"></swp-journal-close>
</swp-journal-header>
<!-- Tabs -->
<swp-journal-tabs>
<swp-journal-tab class="active"><span class="tab-indicator blue"></span>Noter</swp-journal-tab>
<swp-journal-tab class="disabled"><span class="tab-indicator amber"></span>Farveformler</swp-journal-tab>
<swp-journal-tab class="disabled"><span class="tab-indicator gray"></span>Analyse</swp-journal-tab>
</swp-journal-tabs>
<!-- Tab Content Header -->
<swp-journal-tab-header>
<swp-journal-tab-title>Kunde-journal <span>(langsigtet)</span></swp-journal-tab-title>
<swp-journal-tab-desc>Private noter om hår, formler, reaktioner og anbefalinger. Kunden ser det ikke.</swp-journal-tab-desc>
<swp-journal-add-btn>+ Tilføj note</swp-journal-add-btn>
</swp-journal-tab-header>
<!-- Journal Entries -->
<swp-journal-content>
<swp-journal-entry>
<swp-journal-entry-meta>
<swp-journal-entry-type>Journal</swp-journal-entry-type>
<swp-journal-entry-date>12. nov 2025</swp-journal-entry-date>
<swp-journal-entry-author>Af: Maja</swp-journal-entry-author>
<swp-journal-entry-tags>
<swp-journal-tag>Farveformel</swp-journal-tag>
<swp-journal-tag>Kold tone</swp-journal-tag>
</swp-journal-entry-tags>
<swp-journal-entry-delete title="Slet">🗑</swp-journal-entry-delete>
</swp-journal-entry-meta>
<swp-journal-entry-text>
Formel: 7/1 + 7/0 (1:1) + 1.9% • 15 min.<br>
Resultat: neutral/kold, god glans. Undgå varme toner næste gang.
</swp-journal-entry-text>
</swp-journal-entry>
<swp-journal-entry>
<swp-journal-entry-meta>
<swp-journal-entry-type>Journal</swp-journal-entry-type>
<swp-journal-entry-date>2. sep 2025</swp-journal-entry-date>
<swp-journal-entry-author>Af: Nina</swp-journal-entry-author>
<swp-journal-entry-tags>
<swp-journal-tag>Hovedbund</swp-journal-tag>
<swp-journal-tag>Sensitiv</swp-journal-tag>
</swp-journal-entry-tags>
<swp-journal-entry-delete title="Slet">🗑</swp-journal-entry-delete>
</swp-journal-entry-meta>
<swp-journal-entry-text>
Let irritation ved hårgrænsen. Anbefalet parfumefri shampoo + mild kur.<br>
Kunden reagerer på stærk parfume.
</swp-journal-entry-text>
</swp-journal-entry>
</swp-journal-content>
<!-- Kundeprofil Toggle -->
<swp-journal-profile-section>
<swp-journal-profile-toggle onclick="toggleJournalProfile()">
<span class="toggle-arrow"></span> Vis "Kundeprofil" (hurtige facts)
</swp-journal-profile-toggle>
<swp-journal-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>Parfume-allergi • Sensitiv hovedbund</swp-profile-box-value>
</swp-profile-box>
</swp-journal-profile-boxes>
</swp-journal-profile-section>
</swp-journal-panel>
<!-- Customer Details Panel -->
<swp-customer-panel id="customerPanel">
<swp-customer-panel-header>
<swp-customer-panel-title><img src="icons/user.svg" class="panel-header-icon" alt="">Kundedetaljer</swp-customer-panel-title>
<swp-customer-panel-close onclick="closeCustomerPanel()"></swp-customer-panel-close>
</swp-customer-panel-header>
<swp-customer-panel-content>
<!-- Customer header with avatar -->
<swp-customer-header-section>
<swp-customer-avatar>SN</swp-customer-avatar>
<swp-customer-header-info>
<swp-customer-header-top>
<swp-customer-header-left>
<swp-customer-header-name>Sofie Nielsen</swp-customer-header-name>
<swp-customer-since>Kunde siden marts 2024</swp-customer-since>
</swp-customer-header-left>
<swp-customer-header-contact>
<swp-customer-header-contact-item>
<a href="tel:+4523456789">+45 23 45 67 89</a>
</swp-customer-header-contact-item>
<swp-customer-header-contact-item>
<a href="mailto:sofie@email.dk">sofie@email.dk</a>
</swp-customer-header-contact-item>
</swp-customer-header-contact>
</swp-customer-header-top>
</swp-customer-header-info>
</swp-customer-header-section>
<!-- Fact boxes -->
<swp-fact-boxes>
<swp-fact-box class="highlight">
<swp-fact-box-value>12.450 kr</swp-fact-box-value>
<swp-fact-box-label>Total omsætning</swp-fact-box-label>
</swp-fact-box>
<swp-fact-box>
<swp-fact-box-value>14</swp-fact-box-value>
<swp-fact-box-label>Besøg</swp-fact-box-label>
</swp-fact-box>
<swp-fact-box>
<swp-fact-box-value>889 kr</swp-fact-box-value>
<swp-fact-box-label>Gns. pr. besøg</swp-fact-box-label>
</swp-fact-box>
<swp-fact-box>
<swp-fact-box-value>32 dage</swp-fact-box-value>
<swp-fact-box-label>Gns. interval</swp-fact-box-label>
</swp-fact-box>
</swp-fact-boxes>
<!-- Chart -->
<swp-chart-section>
<swp-chart-header>
<swp-chart-title>Omsætning (sidste 6 mdr)</swp-chart-title>
<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="customerRevenueChart"></swp-chart-container>
</swp-chart-section>
<!-- Last 5 products -->
<swp-products-section>
<swp-section-label>Seneste produktkøb</swp-section-label>
<swp-products-list>
<swp-product-item>
<swp-product-info>
<swp-product-name>Olaplex No. 3</swp-product-name>
<swp-product-date>9. dec 2025</swp-product-date>
</swp-product-info>
<swp-product-price>300 kr</swp-product-price>
</swp-product-item>
<swp-product-item>
<swp-product-info>
<swp-product-name>Kevin Murphy Hydrate-Me Wash</swp-product-name>
<swp-product-date>12. nov 2025</swp-product-date>
</swp-product-info>
<swp-product-price>275 kr</swp-product-price>
</swp-product-item>
<swp-product-item>
<swp-product-info>
<swp-product-name>Olaplex No. 4 Bond Shampoo</swp-product-name>
<swp-product-date>18. okt 2025</swp-product-date>
</swp-product-info>
<swp-product-price>320 kr</swp-product-price>
</swp-product-item>
<swp-product-item>
<swp-product-info>
<swp-product-name>Moroccanoil Treatment</swp-product-name>
<swp-product-date>5. sep 2025</swp-product-date>
</swp-product-info>
<swp-product-price>399 kr</swp-product-price>
</swp-product-item>
<swp-product-item>
<swp-product-info>
<swp-product-name>Olaplex No. 5 Conditioner</swp-product-name>
<swp-product-date>12. aug 2025</swp-product-date>
</swp-product-info>
<swp-product-price>320 kr</swp-product-price>
</swp-product-item>
</swp-products-list>
</swp-products-section>
<!-- Preferences -->
<swp-preferences-section>
<swp-section-label>Præferencer & noter</swp-section-label>
<swp-preferences-grid>
<swp-preference-item>
<swp-preference-label>Foretrukken frisør</swp-preference-label>
<swp-preference-value>Emma Larsen</swp-preference-value>
</swp-preference-item>
<swp-preference-item>
<swp-preference-label>Hårtype</swp-preference-label>
<swp-preference-value>Fint, medium tykkelse</swp-preference-value>
</swp-preference-item>
<swp-preference-item>
<swp-preference-label>Farvehistorik</swp-preference-label>
<swp-preference-value>7.3 + 7.4 blanding</swp-preference-value>
</swp-preference-item>
<swp-preference-item>
<swp-preference-label>Allergier</swp-preference-label>
<swp-preference-value>Ingen kendte</swp-preference-value>
</swp-preference-item>
</swp-preferences-grid>
</swp-preferences-section>
</swp-customer-panel-content>
</swp-customer-panel>
<!-- Detail Drawer -->
<swp-detail-drawer id="detailDrawer">
<swp-drawer-header>
<swp-drawer-title id="drawerTitle" class="source-web">Online Booking</swp-drawer-title>
<swp-created-date>oprettet 5. aug 2025, 14:32</swp-created-date>
</swp-drawer-header>
<swp-drawer-content>
<!-- Event time/date -->
<swp-event-info>
<swp-event-time-display>10:00 - 11:00</swp-event-time-display>
<swp-event-date-display>Mandag 9. december 2025</swp-event-date-display>
</swp-event-info>
<!-- Customer -->
<swp-drawer-section>
<swp-section-label>Kunde</swp-section-label>
<swp-customer-info>
<swp-customer-row>
<swp-customer-name>Sofie Nielsen</swp-customer-name>
<swp-customer-phone><a href="tel:+4523456789">+45 23 45 67 89</a></swp-customer-phone>
</swp-customer-row>
<swp-customer-details-link id="customerDetailsLink" onclick="openCustomerPanel()">Vis detaljer <img src="icons/angle-small-right.svg" class="link-chevron" alt=""></swp-customer-details-link>
</swp-customer-info>
</swp-drawer-section>
<!-- Status -->
<swp-drawer-section>
<swp-status-row>
<swp-status-label>Status</swp-status-label>
<button class="status-badge status-arrived" id="statusBadge" popovertarget="statusDropdown">
<swp-status-dot></swp-status-dot>
<span id="statusText">Ankommet</span>
</button>
<swp-status-dropdown id="statusDropdown" popover>
<swp-status-option class="opt-created" data-status="created" onclick="selectStatus('created')">
<swp-status-dot></swp-status-dot>
<span>Oprettet</span>
<swp-status-check></swp-status-check>
</swp-status-option>
<swp-status-option class="opt-arrived" data-status="arrived" onclick="selectStatus('arrived')">
<swp-status-dot></swp-status-dot>
<span>Ankommet</span>
<swp-status-check></swp-status-check>
</swp-status-option>
<swp-status-option class="opt-paid" data-status="paid" onclick="selectStatus('paid')">
<swp-status-dot></swp-status-dot>
<span>Betalt</span>
<swp-status-check></swp-status-check>
</swp-status-option>
<swp-status-option class="opt-noshow" data-status="noshow" onclick="selectStatus('noshow')">
<swp-status-dot></swp-status-dot>
<span>Udeblevet</span>
<swp-status-check></swp-status-check>
</swp-status-option>
<swp-status-option class="opt-cancelled" data-status="cancelled" onclick="selectStatus('cancelled')">
<swp-status-dot></swp-status-dot>
<span>Aflyst</span>
<swp-status-check></swp-status-check>
</swp-status-option>
</swp-status-dropdown>
</swp-status-row>
</swp-drawer-section>
<!-- Services -->
<swp-drawer-section>
<swp-section-label>Services</swp-section-label>
<swp-services-list>
<swp-service-item>
<swp-service-name>Klipning og styling</swp-service-name>
<swp-service-details>60 min · <swp-service-price>500 kr</swp-service-price></swp-service-details>
<swp-service-resource>Emma (EMP001)</swp-service-resource>
</swp-service-item>
<swp-service-item>
<swp-service-name>Bundfarve</swp-service-name>
<swp-service-details>90 min · <swp-service-price>800 kr</swp-service-price></swp-service-details>
<swp-service-resource>Emma (EMP001)</swp-service-resource>
</swp-service-item>
</swp-services-list>
</swp-drawer-section>
<!-- Total -->
<swp-drawer-section>
<swp-total-row>
<swp-total-label>Total</swp-total-label>
<swp-total-amount>1.300 kr</swp-total-amount>
</swp-total-row>
</swp-drawer-section>
<!-- Notes -->
<swp-drawer-section>
<swp-section-label>Noter</swp-section-label>
<swp-notes-box>
Kunden foretrækker naturlige farver og ønsker lidt ekstra tid til konsultation
</swp-notes-box>
</swp-drawer-section>
<!-- Customer profile boxes -->
<swp-drawer-section>
<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>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>Parfume-allergi • Sensitiv hovedbund</swp-profile-box-value>
</swp-profile-box>
</swp-profile-boxes>
<swp-journal-details-link id="journalLink" onclick="toggleJournal()">Se journal <img src="icons/angle-small-right.svg" class="link-chevron" alt=""></swp-journal-details-link>
</swp-drawer-section>
</swp-drawer-content>
<swp-drawer-footer id="drawerFooter">
<swp-footer-btn class="btn-primary" id="payBtn">Gå til betaling</swp-footer-btn>
</swp-drawer-footer>
</swp-detail-drawer>
<script>
const statusLabels = {
created: 'Oprettet',
arrived: 'Ankommet',
paid: 'Betalt',
noshow: 'Udeblevet',
cancelled: 'Aflyst'
};
const sourceTitles = {
manual: 'Manuel Booking',
web: 'Online Booking',
recurring: 'Gentagende Booking'
};
function openDrawer(status = 'arrived', source = 'web', hasJournal = true) {
document.getElementById('drawerOverlay').classList.add('open');
document.getElementById('detailDrawer').classList.add('open');
// Update current status for dropdown
currentStatus = status;
// Update status badge
const badge = document.getElementById('statusBadge');
const text = document.getElementById('statusText');
badge.className = 'status-badge status-' + status;
text.textContent = statusLabels[status];
// Update drawer title based on source
const title = document.getElementById('drawerTitle');
title.textContent = sourceTitles[source];
title.className = 'source-' + source;
// Update journal link
const journalLink = document.getElementById('journalLink');
const chevronImg = '<img src="icons/angle-small-right.svg" class="link-chevron" alt="">';
if (hasJournal) {
journalLink.classList.remove('no-journal');
journalLink.innerHTML = 'Se journal ' + chevronImg;
} else {
journalLink.classList.add('no-journal');
journalLink.innerHTML = '+ Opret journal';
}
// Update footer based on status
const footer = document.getElementById('drawerFooter');
const payBtn = document.getElementById('payBtn');
footer.className = 'status-' + status;
const btnLabels = {
created: 'Gå til betaling',
arrived: 'Gå til betaling',
paid: 'Betalt ✓',
noshow: 'Udeblevet',
cancelled: 'Aflyst'
};
payBtn.textContent = btnLabels[status];
}
function closeDrawer() {
document.getElementById('drawerOverlay').classList.remove('open');
document.getElementById('detailDrawer').classList.remove('open');
document.getElementById('journalPanel').classList.remove('open');
document.getElementById('customerPanel').classList.remove('open');
document.getElementById('journalLink').classList.remove('panel-open');
document.getElementById('customerDetailsLink').classList.remove('panel-open');
}
function toggleJournal() {
const journalLink = document.getElementById('journalLink');
// Don't open journal if it's the "create" state
if (journalLink.classList.contains('no-journal')) {
// Could show create journal flow here
return;
}
const journalPanel = document.getElementById('journalPanel');
journalPanel.classList.toggle('open');
journalLink.classList.toggle('panel-open', journalPanel.classList.contains('open'));
}
function closeJournal() {
document.getElementById('journalPanel').classList.remove('open');
document.getElementById('journalLink').classList.remove('panel-open');
}
function toggleJournalProfile() {
const toggle = document.querySelector('swp-journal-profile-toggle');
const boxes = document.getElementById('journalProfileBoxes');
toggle.classList.toggle('collapsed');
boxes.classList.toggle('collapsed');
}
function openCustomerPanel() {
const panel = document.getElementById('customerPanel');
const link = document.getElementById('customerDetailsLink');
const isOpen = panel.classList.contains('open');
if (isOpen) {
panel.classList.remove('open');
link.classList.remove('panel-open');
} else {
panel.classList.add('open');
link.classList.add('panel-open');
}
}
function closeCustomerPanel() {
document.getElementById('customerPanel').classList.remove('open');
document.getElementById('customerDetailsLink').classList.remove('panel-open');
}
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') closeDrawer();
});
// Status dropdown (Popover API)
let currentStatus = 'arrived';
const dropdown = document.getElementById('statusDropdown');
const badge = document.getElementById('statusBadge');
// Update selection when popover is about to open
dropdown.addEventListener('beforetoggle', (e) => {
if (e.newState === 'open') {
updateDropdownSelection();
}
});
// Position after popover opens
dropdown.addEventListener('toggle', (e) => {
if (e.newState === 'open') {
const rect = badge.getBoundingClientRect();
dropdown.style.position = 'fixed';
dropdown.style.top = `${rect.bottom + 4}px`;
dropdown.style.left = `${rect.right - dropdown.offsetWidth}px`;
}
});
function updateDropdownSelection() {
const options = document.querySelectorAll('swp-status-option');
options.forEach(opt => {
opt.classList.toggle('selected', opt.dataset.status === currentStatus);
});
}
function selectStatus(status) {
currentStatus = status;
// Update badge
const badge = document.getElementById('statusBadge');
const text = document.getElementById('statusText');
badge.className = 'status-badge status-' + status;
text.textContent = statusLabels[status];
// Update footer
const footer = document.getElementById('drawerFooter');
const payBtn = document.getElementById('payBtn');
footer.className = 'status-' + status;
const btnLabels = {
created: 'Gå til betaling',
arrived: 'Gå til betaling',
paid: 'Betalt ✓',
noshow: 'Udeblevet',
cancelled: 'Aflyst'
};
payBtn.textContent = btnLabels[status];
// Close popover
dropdown.hidePopover();
}
</script>
<!-- Chart initialization -->
<script type="module">
import { createChart } from 'https://unpkg.com/@sevenweirdpeople/swp-charting@latest/dist/index.js';
// Initialize chart when customer panel opens
let chartInitialized = false;
const originalOpenCustomerPanel = window.openCustomerPanel;
window.openCustomerPanel = function() {
originalOpenCustomerPanel();
if (!chartInitialized) {
setTimeout(() => {
createChart(document.getElementById('customerRevenueChart'), {
width: 620,
height: 215,
xAxis: {
categories: ['Jul', 'Aug', 'Sep', 'Okt', 'Nov', 'Dec']
},
series: [
{
name: 'Services',
color: '#00897b',
data: [
{ x: 'Jul', y: 1200 },
{ x: 'Sep', y: 1500 },
{ x: 'Okt', y: 2800 },
{ x: 'Dec', y: 1800 }
]
},
{
name: 'Produkter',
color: '#1e88e5',
data: [
{ x: 'Aug', y: 800 },
{ x: 'Okt', y: 900 },
{ x: 'Nov', y: 700 },
{ x: 'Dec', y: 950 }
]
}
],
legend: true
});
chartInitialized = true;
}, 100);
}
};
</script>
</body>
</html>