Calendar/wwwroot/poc-detail-drawer.html

2031 lines
59 KiB
HTML
Raw Normal View History

2025-12-18 06:58:42 +01:00
<!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">
2025-12-18 06:58:42 +01:00
<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;
2025-12-18 06:58:42 +01:00
/* 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;
2025-12-18 06:58:42 +01:00
color: var(--color-text-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 10px;
2025-12-18 06:58:42 +01:00
}
/* 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;
}
2025-12-18 06:58:42 +01:00
swp-customer-name {
font-size: 18px;
font-weight: 500;
color: var(--color-text);
}
swp-customer-phone {
font-size: 14px;
color: var(--color-teal);
2025-12-18 06:58:42 +01:00
}
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;
2025-12-18 06:58:42 +01:00
align-items: center;
gap: 4px;
font-size: 13px;
2025-12-18 06:58:42 +01:00
color: var(--color-text-secondary);
cursor: pointer;
margin-top: 6px;
transition: color 150ms ease;
2025-12-18 06:58:42 +01:00
}
swp-customer-details-link:hover {
color: var(--color-teal);
2025-12-18 06:58:42 +01:00
}
/* 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);
2025-12-18 06:58:42 +01:00
}
/* 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;
2025-12-18 06:58:42 +01:00
}
swp-customer-header-contact-item {
font-size: 14px;
2025-12-18 06:58:42 +01:00
}
swp-customer-header-contact-item a {
color: var(--color-teal);
text-decoration: none;
2025-12-18 06:58:42 +01:00
}
swp-customer-header-contact-item a:hover {
text-decoration: underline;
2025-12-18 06:58:42 +01:00
}
swp-customer-since {
font-size: 12px;
2025-12-18 06:58:42 +01:00
color: var(--color-text-secondary);
}
/* Fact boxes grid */
swp-fact-boxes {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 16px;
2025-12-18 06:58:42 +01:00
}
swp-fact-box {
padding: 16px;
background: var(--color-background-alt);
border-radius: 8px;
border: 1px solid var(--color-border);
text-align: center;
2025-12-18 06:58:42 +01:00
}
swp-fact-box-value {
2025-12-18 06:58:42 +01:00
display: block;
font-family: var(--font-mono);
font-size: 20px;
font-weight: 700;
2025-12-18 06:58:42 +01:00
color: var(--color-text);
margin-bottom: 4px;
2025-12-18 06:58:42 +01:00
}
swp-fact-box-label {
2025-12-18 06:58:42 +01:00
display: block;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
2025-12-18 06:58:42 +01:00
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 {
2025-12-18 06:58:42 +01:00
display: block;
}
swp-chart-header {
2025-12-18 06:58:42 +01:00
display: flex;
align-items: center;
2025-12-18 06:58:42 +01:00
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;
2025-12-18 06:58:42 +01:00
align-items: center;
gap: 6px;
font-size: 12px;
color: var(--color-text-secondary);
2025-12-18 06:58:42 +01:00
}
swp-chart-legend-dot {
width: 8px;
height: 8px;
border-radius: 50%;
2025-12-18 06:58:42 +01:00
}
swp-chart-legend-dot.services {
background: var(--color-teal);
2025-12-18 06:58:42 +01:00
}
swp-chart-legend-dot.products {
background: var(--b-color-blue);
2025-12-18 06:58:42 +01:00
}
swp-chart-container {
display: block;
background: var(--color-background-alt);
border-radius: 8px;
2025-12-18 06:58:42 +01:00
border: 1px solid var(--color-border);
}
/* Products list */
swp-products-section {
display: block;
2025-12-18 06:58:42 +01:00
}
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);
2025-12-18 06:58:42 +01:00
}
swp-product-price {
font-family: var(--font-mono);
2025-12-18 06:58:42 +01:00
font-size: 14px;
font-weight: 500;
2025-12-18 06:58:42 +01:00
color: var(--color-text);
}
/* Preferences section */
swp-preferences-section {
display: block;
2025-12-18 06:58:42 +01:00
}
swp-preferences-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
swp-preference-item {
padding: 12px 14px;
2025-12-18 06:58:42 +01:00
background: var(--color-background-alt);
border-radius: 6px;
border: 1px solid var(--color-border);
2025-12-18 06:58:42 +01:00
}
swp-preference-label {
display: block;
font-size: 11px;
text-transform: uppercase;
letter-spacing: 0.5px;
2025-12-18 06:58:42 +01:00
color: var(--color-text-secondary);
margin-bottom: 4px;
2025-12-18 06:58:42 +01:00
}
swp-preference-value {
font-size: 14px;
color: var(--color-text);
2025-12-18 06:58:42 +01:00
}
/* 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);
}
2025-12-18 06:58:42 +01:00
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);
}
2025-12-18 06:58:42 +01:00
/* 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);
2025-12-18 06:58:42 +01:00
color: white;
border-color: var(--color-teal);
2025-12-18 06:58:42 +01:00
}
swp-footer-btn.btn-primary:hover {
background: #00796b;
2025-12-18 06:58:42 +01:00
}
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;
2025-12-18 06:58:42 +01:00
}
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>
2025-12-18 06:58:42 +01:00
<!-- 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>
2025-12-18 06:58:42 +01:00
<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>
2025-12-18 06:58:42 +01:00
</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>
2025-12-18 06:58:42 +01:00
<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>
2025-12-18 06:58:42 +01:00
<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>
2025-12-18 06:58:42 +01:00
</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 -->
2025-12-18 06:58:42 +01:00
<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>
2025-12-18 06:58:42 +01:00
</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="">';
2025-12-18 06:58:42 +01:00
if (hasJournal) {
journalLink.classList.remove('no-journal');
journalLink.innerHTML = 'Se journal ' + chevronImg;
2025-12-18 06:58:42 +01:00
} else {
journalLink.classList.add('no-journal');
journalLink.innerHTML = '+ Opret journal';
2025-12-18 06:58:42 +01:00
}
// 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');
2025-12-18 06:58:42 +01:00
}
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>
2025-12-18 06:58:42 +01:00
</body>
</html>