Removes poc-booking.html from wwwroot

Cleans up redundant HTML file from project directory

Eliminates an unused proof-of-concept booking page template
This commit is contained in:
Janus C. H. Knudsen 2026-01-05 17:38:39 +01:00
parent a4fc822229
commit 35b15294e3
5 changed files with 1105 additions and 5702 deletions

File diff suppressed because it is too large Load diff

View file

@ -1033,6 +1033,326 @@
color: white; color: white;
} }
/* ==========================================
RECEIPT
========================================== */
.receipt-container {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.3);
z-index: 10;
display: flex;
justify-content: center;
padding-top: 20px;
opacity: 0;
visibility: hidden;
transition: opacity 300ms ease, visibility 300ms ease;
overflow-y: auto;
}
.receipt-container.visible {
opacity: 1;
visibility: visible;
}
.receipt {
width: 280px;
background: #fff;
font-family: 'Courier New', monospace;
font-size: 12px;
color: #000;
padding: 20px 16px;
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
transform: translateY(-100%);
transition: transform 500ms cubic-bezier(0.34, 1.56, 0.64, 1);
position: relative;
margin-bottom: 20px;
}
.receipt-container.visible .receipt {
transform: translateY(0);
}
/* Torn paper effect at bottom */
.receipt::after {
content: '';
position: absolute;
bottom: -10px;
left: 0;
right: 0;
height: 10px;
background: linear-gradient(135deg, #fff 25%, transparent 25%),
linear-gradient(-135deg, #fff 25%, transparent 25%);
background-size: 10px 10px;
}
.receipt-header {
text-align: center;
padding-bottom: 12px;
border-bottom: 1px dashed #000;
margin-bottom: 12px;
}
.receipt-logo {
font-size: 18px;
font-weight: bold;
letter-spacing: 2px;
margin-bottom: 4px;
}
.receipt-address {
font-size: 10px;
line-height: 1.4;
}
.receipt-info {
display: flex;
justify-content: space-between;
font-size: 10px;
padding: 8px 0;
border-bottom: 1px dashed #000;
margin-bottom: 12px;
}
.receipt-items {
margin-bottom: 12px;
}
.receipt-item {
display: flex;
justify-content: space-between;
padding: 4px 0;
font-size: 11px;
}
.receipt-item-name {
flex: 1;
padding-right: 8px;
}
.receipt-item-qty {
width: 30px;
text-align: center;
}
.receipt-item-price {
width: 70px;
text-align: right;
font-family: 'JetBrains Mono', monospace;
}
.receipt-divider {
border-top: 1px dashed #000;
margin: 8px 0;
}
.receipt-totals {
margin-bottom: 12px;
}
.receipt-total-row {
display: flex;
justify-content: space-between;
padding: 3px 0;
font-size: 11px;
}
.receipt-total-row.grand {
font-size: 14px;
font-weight: bold;
padding: 8px 0;
border-top: 2px solid #000;
border-bottom: 2px solid #000;
margin: 8px 0;
}
.receipt-payment {
font-size: 10px;
padding: 8px 0;
border-bottom: 1px dashed #000;
}
.receipt-payment-row {
display: flex;
justify-content: space-between;
padding: 2px 0;
}
.receipt-footer {
text-align: center;
padding-top: 12px;
font-size: 10px;
}
.receipt-footer-msg {
font-size: 12px;
font-weight: bold;
margin-bottom: 8px;
}
.receipt-qr {
margin: 12px auto 8px;
display: flex;
justify-content: center;
}
.receipt-qr svg {
border: 2px solid #000;
}
.receipt-id {
font-size: 9px;
letter-spacing: 1px;
}
.receipt-close {
position: absolute;
top: 8px;
right: 8px;
width: 24px;
height: 24px;
border: none;
background: #f0f0f0;
border-radius: 50%;
cursor: pointer;
font-size: 14px;
display: flex;
align-items: center;
justify-content: center;
}
.receipt-close:hover {
background: #e0e0e0;
}
.receipt-actions {
display: flex;
gap: 8px;
margin-top: 16px;
padding-top: 12px;
border-top: 1px solid #e0e0e0;
}
.receipt-actions button {
flex: 1;
padding: 8px;
font-size: 11px;
border-radius: 4px;
cursor: pointer;
font-family: var(--font-family);
}
.receipt-actions .btn-print {
background: #000;
color: #fff;
border: none;
}
.receipt-actions .btn-close {
background: #fff;
border: 1px solid #ccc;
color: #333;
}
/* ==========================================
PRINT STYLES
========================================== */
@media print {
/* White background */
html, body {
background: white !important;
-webkit-print-color-adjust: exact;
print-color-adjust: exact;
}
/* Hide everything by default */
.demo-trigger,
.overlay,
.header,
.sidebar,
.cart-area,
.payment-total,
.payment-methods,
.payment-input-section,
.registered-payments,
.payment-footer {
display: none !important;
}
/* Reset panel positioning */
.panel {
position: static !important;
width: 100% !important;
transform: none !important;
box-shadow: none !important;
background: white !important;
}
.main {
display: block !important;
}
.payment-panel {
display: block !important;
position: static !important;
border: none !important;
background: white !important;
}
/* Show receipt */
.receipt-container {
display: flex !important;
justify-content: center !important;
visibility: visible !important;
opacity: 1 !important;
position: static !important;
background: white !important;
padding: 0 !important;
}
.receipt {
display: block !important;
margin: 0 !important;
box-shadow: none !important;
transform: none !important;
text-align: left !important;
}
/* Preserve item layout */
.receipt-item {
display: flex !important;
justify-content: space-between !important;
text-align: left !important;
}
.receipt-item-name {
text-align: left !important;
}
.receipt-item-price {
text-align: right !important;
}
.receipt-total-row {
display: flex !important;
justify-content: space-between !important;
}
/* Hide buttons in receipt */
.receipt-close,
.receipt-actions {
display: none !important;
}
/* Remove torn paper effect */
.receipt::after {
display: none;
}
}
</style> </style>
</head> </head>
<body> <body>
@ -1303,7 +1623,178 @@
</div> </div>
<!-- Payment Panel --> <!-- Payment Panel -->
<div class="payment-panel"> <div class="payment-panel" style="position: relative;">
<!-- Receipt overlay -->
<div class="receipt-container" id="receiptContainer">
<div class="receipt">
<button class="receipt-close" onclick="hideReceipt()"></button>
<div class="receipt-header">
<div class="receipt-logo">SALON BELLEZZA</div>
<div class="receipt-address">
Østergade 42, 1. sal<br>
1100 København K<br>
Tlf: 33 12 34 56<br>
CVR: 12345678
</div>
</div>
<div class="receipt-info">
<div>
<div>Dato: 16-12-2025</div>
<div>Tid: 14:32</div>
</div>
<div style="text-align: right;">
<div>Kvit: #4521</div>
<div>Kasse: 1</div>
</div>
</div>
<div class="receipt-items">
<div class="receipt-item" style="font-weight: bold; border-bottom: 1px solid #000; padding-bottom: 4px; margin-bottom: 4px;">
<span class="receipt-item-name">Vare</span>
<span class="receipt-item-qty">Antal</span>
<span class="receipt-item-price">Pris</span>
</div>
<div class="receipt-item">
<span class="receipt-item-name">Dameklip</span>
<span class="receipt-item-qty">1</span>
<span class="receipt-item-price">725,00</span>
</div>
<div class="receipt-item">
<span class="receipt-item-name">Gloss langt hår</span>
<span class="receipt-item-qty">1</span>
<span class="receipt-item-price">900,00</span>
</div>
<div class="receipt-item">
<span class="receipt-item-name">Olaplex No. 3</span>
<span class="receipt-item-qty">1</span>
<span class="receipt-item-price">300,00</span>
</div>
<div class="receipt-item">
<span class="receipt-item-name">India mask 250ml</span>
<span class="receipt-item-qty">2</span>
<span class="receipt-item-price">350,00</span>
</div>
</div>
<div class="receipt-divider"></div>
<div class="receipt-totals">
<div class="receipt-total-row">
<span>Subtotal</span>
<span>1.820,00</span>
</div>
<div class="receipt-total-row">
<span>Moms (25%)</span>
<span>455,00</span>
</div>
<div class="receipt-total-row grand">
<span>TOTAL</span>
<span>2.275,00 DKK</span>
</div>
</div>
<div class="receipt-payment">
<div class="receipt-payment-row">
<span>Betalt med Kort</span>
<span>2.275,00</span>
</div>
</div>
<div class="receipt-footer">
<div class="receipt-footer-msg">TAK FOR BESØGET!</div>
<div>Kunde: Sofie Nielsen</div>
<div>Betjent af: Emma Larsen</div>
<div class="receipt-qr">
<svg viewBox="0 0 29 29" width="80" height="80">
<!-- QR Code pattern -->
<rect width="29" height="29" fill="white"/>
<!-- Position patterns (corners) -->
<rect x="0" y="0" width="7" height="7" fill="black"/>
<rect x="1" y="1" width="5" height="5" fill="white"/>
<rect x="2" y="2" width="3" height="3" fill="black"/>
<rect x="22" y="0" width="7" height="7" fill="black"/>
<rect x="23" y="1" width="5" height="5" fill="white"/>
<rect x="24" y="2" width="3" height="3" fill="black"/>
<rect x="0" y="22" width="7" height="7" fill="black"/>
<rect x="1" y="23" width="5" height="5" fill="white"/>
<rect x="2" y="24" width="3" height="3" fill="black"/>
<!-- Timing patterns -->
<rect x="8" y="6" width="1" height="1" fill="black"/>
<rect x="10" y="6" width="1" height="1" fill="black"/>
<rect x="12" y="6" width="1" height="1" fill="black"/>
<rect x="6" y="8" width="1" height="1" fill="black"/>
<rect x="6" y="10" width="1" height="1" fill="black"/>
<rect x="6" y="12" width="1" height="1" fill="black"/>
<!-- Data modules (random pattern) -->
<rect x="8" y="8" width="1" height="1" fill="black"/>
<rect x="9" y="9" width="1" height="1" fill="black"/>
<rect x="10" y="8" width="1" height="1" fill="black"/>
<rect x="11" y="10" width="1" height="1" fill="black"/>
<rect x="12" y="9" width="1" height="1" fill="black"/>
<rect x="13" y="8" width="1" height="1" fill="black"/>
<rect x="8" y="11" width="1" height="1" fill="black"/>
<rect x="10" y="11" width="1" height="1" fill="black"/>
<rect x="12" y="11" width="1" height="1" fill="black"/>
<rect x="9" y="12" width="1" height="1" fill="black"/>
<rect x="11" y="12" width="1" height="1" fill="black"/>
<rect x="13" y="12" width="1" height="1" fill="black"/>
<rect x="15" y="8" width="1" height="1" fill="black"/>
<rect x="16" y="9" width="1" height="1" fill="black"/>
<rect x="17" y="8" width="1" height="1" fill="black"/>
<rect x="18" y="10" width="1" height="1" fill="black"/>
<rect x="19" y="9" width="1" height="1" fill="black"/>
<rect x="20" y="8" width="1" height="1" fill="black"/>
<rect x="15" y="11" width="1" height="1" fill="black"/>
<rect x="17" y="11" width="1" height="1" fill="black"/>
<rect x="19" y="11" width="1" height="1" fill="black"/>
<rect x="16" y="12" width="1" height="1" fill="black"/>
<rect x="18" y="12" width="1" height="1" fill="black"/>
<rect x="20" y="12" width="1" height="1" fill="black"/>
<rect x="8" y="14" width="1" height="1" fill="black"/>
<rect x="10" y="14" width="1" height="1" fill="black"/>
<rect x="12" y="15" width="1" height="1" fill="black"/>
<rect x="9" y="16" width="1" height="1" fill="black"/>
<rect x="11" y="16" width="1" height="1" fill="black"/>
<rect x="13" y="17" width="1" height="1" fill="black"/>
<rect x="8" y="18" width="1" height="1" fill="black"/>
<rect x="10" y="18" width="1" height="1" fill="black"/>
<rect x="12" y="19" width="1" height="1" fill="black"/>
<rect x="15" y="14" width="1" height="1" fill="black"/>
<rect x="17" y="15" width="1" height="1" fill="black"/>
<rect x="19" y="14" width="1" height="1" fill="black"/>
<rect x="16" y="16" width="1" height="1" fill="black"/>
<rect x="18" y="17" width="1" height="1" fill="black"/>
<rect x="20" y="16" width="1" height="1" fill="black"/>
<rect x="22" y="8" width="1" height="1" fill="black"/>
<rect x="24" y="9" width="1" height="1" fill="black"/>
<rect x="23" y="10" width="1" height="1" fill="black"/>
<rect x="25" y="11" width="1" height="1" fill="black"/>
<rect x="22" y="12" width="1" height="1" fill="black"/>
<rect x="24" y="13" width="1" height="1" fill="black"/>
<rect x="8" y="22" width="1" height="1" fill="black"/>
<rect x="10" y="23" width="1" height="1" fill="black"/>
<rect x="9" y="24" width="1" height="1" fill="black"/>
<rect x="11" y="25" width="1" height="1" fill="black"/>
<rect x="12" y="22" width="1" height="1" fill="black"/>
<rect x="13" y="24" width="1" height="1" fill="black"/>
<!-- Alignment pattern -->
<rect x="20" y="20" width="5" height="5" fill="black"/>
<rect x="21" y="21" width="3" height="3" fill="white"/>
<rect x="22" y="22" width="1" height="1" fill="black"/>
</svg>
</div>
<div class="receipt-id">4521-161225-1432</div>
</div>
<div class="receipt-actions">
<button class="btn-print" onclick="window.print()">🖨️ Print</button>
<button class="btn-close" onclick="hideReceipt()">Luk</button>
</div>
</div>
</div>
<div class="payment-total"> <div class="payment-total">
<div class="payment-total-amount" id="totalAmount">2.275 kr</div> <div class="payment-total-amount" id="totalAmount">2.275 kr</div>
<div class="payment-total-label">Total at betale</div> <div class="payment-total-label">Total at betale</div>
@ -1369,7 +1860,7 @@
</div> </div>
<button class="btn-complete" id="btnComplete" disabled>Afslut salg</button> <button class="btn-complete" id="btnComplete" disabled>Afslut salg</button>
<div class="payment-actions"> <div class="payment-actions">
<button class="btn-secondary">Print kvittering</button> <button class="btn-secondary" onclick="showReceipt()">Print kvittering</button>
<button class="btn-secondary">Send på mail</button> <button class="btn-secondary">Send på mail</button>
</div> </div>
</div> </div>
@ -1978,6 +2469,31 @@
} }
`; `;
document.head.appendChild(scannerStyle); document.head.appendChild(scannerStyle);
// ==========================================
// RECEIPT
// ==========================================
function showReceipt() {
const receiptContainer = document.getElementById('receiptContainer');
receiptContainer.classList.add('visible');
}
function hideReceipt() {
const receiptContainer = document.getElementById('receiptContainer');
receiptContainer.classList.remove('visible');
}
// Close receipt on Escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
const receiptContainer = document.getElementById('receiptContainer');
if (receiptContainer.classList.contains('visible')) {
hideReceipt();
e.stopPropagation();
}
}
});
</script> </script>
</body> </body>
</html> </html>

File diff suppressed because it is too large Load diff

View file

@ -1684,6 +1684,181 @@
swp-btn.primary.purple:hover { swp-btn.primary.purple:hover {
background: color-mix(in srgb, var(--color-purple) 85%, black); background: color-mix(in srgb, var(--color-purple) 85%, black);
} }
/* ==========================================
TRACKING TAB
========================================== */
swp-tab-content[data-tab="tracking"] {
display: none;
}
swp-tab-content[data-tab="tracking"].active {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.tracking-card {
margin-bottom: 0;
}
.tracking-card.full-width,
.code-preview-card {
grid-column: 1 / -1;
}
.tracking-card swp-card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.tracking-card[data-tracker] swp-card-content {
transition: opacity 200ms ease, max-height 300ms ease;
}
.tracking-card[data-tracker].disabled swp-card-content {
opacity: 0.5;
pointer-events: none;
}
swp-tracking-hint {
display: flex;
align-items: center;
gap: 8px;
margin-top: 12px;
padding: 10px 12px;
background: color-mix(in srgb, var(--color-blue) 8%, transparent);
border-radius: 6px;
font-size: 12px;
color: var(--color-text-secondary);
}
swp-tracking-hint i {
font-size: 16px;
color: var(--color-blue);
}
swp-tracking-hint.privacy {
background: color-mix(in srgb, var(--color-green) 8%, transparent);
}
swp-tracking-hint.privacy i {
color: var(--color-green);
}
swp-script-section {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 16px;
}
swp-script-section:last-child {
margin-bottom: 0;
}
swp-script-label {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
font-weight: 500;
color: var(--color-text);
}
swp-script-label i {
font-size: 16px;
color: var(--color-text-secondary);
}
.script-textarea {
width: 100%;
min-height: 100px;
padding: 12px;
font-family: var(--font-mono);
font-size: 12px;
line-height: 1.5;
color: var(--color-text);
background: var(--color-background);
border: 1px solid var(--color-border);
border-radius: 6px;
resize: vertical;
}
.script-textarea::placeholder {
color: var(--color-text-secondary);
}
.script-textarea:focus {
outline: none;
border-color: var(--color-teal);
}
.code-preview-card {
margin-top: 24px;
border: 2px dashed var(--color-teal);
background: color-mix(in srgb, var(--color-teal) 3%, var(--color-surface));
}
.code-preview-card swp-card-header {
display: flex;
justify-content: space-between;
align-items: center;
}
.code-preview-card swp-card-title {
color: var(--color-teal);
}
swp-code-info {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 12px;
font-size: 12px;
color: var(--color-text-secondary);
}
swp-code-info i {
font-size: 16px;
color: var(--color-teal);
}
.code-preview {
margin: 0;
padding: 16px;
background: var(--color-background);
border: 1px solid var(--color-border);
border-radius: 6px;
overflow-x: auto;
max-height: 400px;
overflow-y: auto;
}
.code-preview code {
font-family: var(--font-mono);
font-size: 11px;
line-height: 1.6;
color: var(--color-text);
white-space: pre;
}
.code-preview .comment {
color: var(--color-green);
}
.code-preview .tag {
color: var(--color-blue);
}
.code-preview .attr {
color: var(--color-purple);
}
.code-preview .string {
color: var(--color-teal);
}
</style> </style>
</head> </head>
<body> <body>
@ -1735,6 +1910,10 @@
<i class="ph ph-puzzle-piece"></i> <i class="ph ph-puzzle-piece"></i>
Moduler Moduler
</swp-tab> </swp-tab>
<swp-tab data-tab="tracking">
<i class="ph ph-chart-line-up"></i>
Tracking
</swp-tab>
</swp-tab-bar> </swp-tab-bar>
<!-- ========================================== <!-- ==========================================
@ -3193,6 +3372,214 @@ Tak for din handel!</swp-edit-textarea>
</swp-modules-section> </swp-modules-section>
</swp-tab-content> </swp-tab-content>
<!-- ==========================================
TAB: TRACKING
========================================== -->
<swp-tab-content data-tab="tracking">
<!-- Meta Pixel -->
<swp-card class="tracking-card" data-tracker="meta">
<swp-card-header>
<swp-card-title>
<i class="ph ph-meta-logo"></i>
Meta Pixel (Facebook)
</swp-card-title>
<swp-toggle-slider data-value="yes">
<swp-toggle-option>Ja</swp-toggle-option>
<swp-toggle-option>Nej</swp-toggle-option>
</swp-toggle-slider>
</swp-card-header>
<swp-card-content>
<swp-edit-section>
<swp-edit-row>
<swp-edit-label>Pixel ID</swp-edit-label>
<swp-edit-value contenteditable="true" class="mono" id="metaPixelId" oninput="updateTrackingPreview()">123456789012345</swp-edit-value>
</swp-edit-row>
</swp-edit-section>
<swp-tracking-hint>
<i class="ph ph-info"></i>
Bruges til Facebook og Instagram annoncering og remarketing
</swp-tracking-hint>
</swp-card-content>
</swp-card>
<!-- Google Analytics -->
<swp-card class="tracking-card" data-tracker="ga4">
<swp-card-header>
<swp-card-title>
<i class="ph ph-chart-line"></i>
Google Analytics (GA4)
</swp-card-title>
<swp-toggle-slider data-value="yes">
<swp-toggle-option>Ja</swp-toggle-option>
<swp-toggle-option>Nej</swp-toggle-option>
</swp-toggle-slider>
</swp-card-header>
<swp-card-content>
<swp-edit-section>
<swp-edit-row>
<swp-edit-label>Measurement ID</swp-edit-label>
<swp-edit-value contenteditable="true" class="mono" id="ga4Id" oninput="updateTrackingPreview()">G-ABC123XYZ</swp-edit-value>
</swp-edit-row>
</swp-edit-section>
<swp-tracking-hint>
<i class="ph ph-info"></i>
Google Analytics 4 til website trafik og brugeradfærd
</swp-tracking-hint>
</swp-card-content>
</swp-card>
<!-- Google Tag Manager -->
<swp-card class="tracking-card" data-tracker="gtm">
<swp-card-header>
<swp-card-title>
<i class="ph ph-code"></i>
Google Tag Manager
</swp-card-title>
<swp-toggle-slider data-value="no">
<swp-toggle-option>Ja</swp-toggle-option>
<swp-toggle-option>Nej</swp-toggle-option>
</swp-toggle-slider>
</swp-card-header>
<swp-card-content>
<swp-edit-section>
<swp-edit-row>
<swp-edit-label>Container ID</swp-edit-label>
<swp-edit-value contenteditable="true" class="mono" id="gtmId" oninput="updateTrackingPreview()">GTM-XXXXXXX</swp-edit-value>
</swp-edit-row>
</swp-edit-section>
<swp-tracking-hint>
<i class="ph ph-info"></i>
Central styring af alle tracking-tags
</swp-tracking-hint>
</swp-card-content>
</swp-card>
<!-- Plausible -->
<swp-card class="tracking-card" data-tracker="plausible">
<swp-card-header>
<swp-card-title>
<i class="ph ph-leaf"></i>
Plausible Analytics
</swp-card-title>
<swp-toggle-slider data-value="no">
<swp-toggle-option>Ja</swp-toggle-option>
<swp-toggle-option>Nej</swp-toggle-option>
</swp-toggle-slider>
</swp-card-header>
<swp-card-content>
<swp-edit-section>
<swp-edit-row>
<swp-edit-label>Domæne</swp-edit-label>
<swp-edit-value contenteditable="true" class="mono" id="plausibleDomain" oninput="updateTrackingPreview()">minside.dk</swp-edit-value>
</swp-edit-row>
</swp-edit-section>
<swp-tracking-hint class="privacy">
<i class="ph ph-shield-check"></i>
Privacy-venlig analytics uden cookies - GDPR compliant
</swp-tracking-hint>
</swp-card-content>
</swp-card>
<!-- Fathom -->
<swp-card class="tracking-card" data-tracker="fathom">
<swp-card-header>
<swp-card-title>
<i class="ph ph-eye"></i>
Fathom Analytics
</swp-card-title>
<swp-toggle-slider data-value="no">
<swp-toggle-option>Ja</swp-toggle-option>
<swp-toggle-option>Nej</swp-toggle-option>
</swp-toggle-slider>
</swp-card-header>
<swp-card-content>
<swp-edit-section>
<swp-edit-row>
<swp-edit-label>Site ID</swp-edit-label>
<swp-edit-value contenteditable="true" class="mono" id="fathomSiteId" oninput="updateTrackingPreview()">ABCDEFGH</swp-edit-value>
</swp-edit-row>
</swp-edit-section>
<swp-tracking-hint class="privacy">
<i class="ph ph-shield-check"></i>
Privacy-venlig analytics uden cookies - GDPR compliant
</swp-tracking-hint>
</swp-card-content>
</swp-card>
<!-- Matomo -->
<swp-card class="tracking-card" data-tracker="matomo">
<swp-card-header>
<swp-card-title>
<i class="ph ph-database"></i>
Matomo
</swp-card-title>
<swp-toggle-slider data-value="no">
<swp-toggle-option>Ja</swp-toggle-option>
<swp-toggle-option>Nej</swp-toggle-option>
</swp-toggle-slider>
</swp-card-header>
<swp-card-content>
<swp-edit-section>
<swp-edit-row>
<swp-edit-label>Server URL</swp-edit-label>
<swp-edit-value contenteditable="true" class="mono" id="matomoUrl" oninput="updateTrackingPreview()">https://matomo.minside.dk</swp-edit-value>
</swp-edit-row>
<swp-edit-row>
<swp-edit-label>Site ID</swp-edit-label>
<swp-edit-value contenteditable="true" class="mono" id="matomoSiteId" oninput="updateTrackingPreview()">1</swp-edit-value>
</swp-edit-row>
</swp-edit-section>
<swp-tracking-hint class="privacy">
<i class="ph ph-shield-check"></i>
Self-hosted analytics - fuld kontrol over dine data
</swp-tracking-hint>
</swp-card-content>
</swp-card>
<!-- Custom Scripts -->
<swp-card class="tracking-card full-width">
<swp-card-header>
<swp-card-title>
<i class="ph ph-code-block"></i>
Brugerdefinerede Scripts
</swp-card-title>
</swp-card-header>
<swp-card-content>
<swp-script-section>
<swp-script-label>
<i class="ph ph-file-code"></i>
Scripts i &lt;head&gt;
</swp-script-label>
<textarea class="script-textarea" id="customHeadScripts" placeholder="<!-- Indsæt custom tracking kode her -->" oninput="updateTrackingPreview()"></textarea>
</swp-script-section>
<swp-script-section>
<swp-script-label>
<i class="ph ph-file-code"></i>
Scripts før &lt;/body&gt;
</swp-script-label>
<textarea class="script-textarea" id="customBodyScripts" placeholder="<!-- Indsæt custom kode her -->"></textarea>
</swp-script-section>
</swp-card-content>
</swp-card>
<!-- Generated Code Preview -->
<swp-card class="code-preview-card">
<swp-card-header>
<swp-card-title>
<i class="ph ph-terminal"></i>
Genereret Kode
</swp-card-title>
</swp-card-header>
<swp-card-content>
<swp-code-info>
<i class="ph ph-info"></i>
Denne kode indsættes automatisk i &lt;head&gt; på din online booking side
</swp-code-info>
<pre class="code-preview" id="trackingCodePreview"><code><!-- Ingen aktive tracking-koder --></code></pre>
</swp-card-content>
</swp-card>
</swp-tab-content>
<!-- ========================================== <!-- ==========================================
DRAWER: REDIGER BESKED DRAWER: REDIGER BESKED
========================================== --> ========================================== -->
@ -3699,6 +4086,206 @@ Vil du ændre din tid? {booking_link}</swp-message-editor>
// Initial state // Initial state
updateOnlinePaymentVisibility(); updateOnlinePaymentVisibility();
} }
// ==========================================
// TRACKING TAB
// ==========================================
function initTrackingToggles() {
document.querySelectorAll('.tracking-card swp-toggle-slider').forEach(slider => {
slider.querySelectorAll('swp-toggle-option').forEach((option, index) => {
option.addEventListener('click', () => {
slider.dataset.value = index === 0 ? 'yes' : 'no';
const card = slider.closest('.tracking-card');
if (slider.dataset.value === 'no') {
card.classList.add('disabled');
} else {
card.classList.remove('disabled');
}
updateTrackingPreview();
});
});
});
}
function updateTrackingPreview() {
const preview = document.getElementById('trackingCodePreview');
if (!preview) return;
let code = '';
// Meta Pixel
const metaCard = document.querySelector('[data-tracker="meta"]');
if (metaCard && metaCard.querySelector('swp-toggle-slider').dataset.value === 'yes') {
const pixelId = document.getElementById('metaPixelId')?.textContent?.trim() || '';
if (pixelId) {
code += `<span class="comment">&lt;!-- Meta Pixel --&gt;</span>
<span class="tag">&lt;script&gt;</span>
!function(f,b,e,v,n,t,s)
{if(f.fbq)return;n=f.fbq=function(){n.callMethod?
n.callMethod.apply(n,arguments):n.queue.push(arguments)};
if(!f._fbq)f._fbq=n;n.push=n;n.loaded=!0;n.version='2.0';
n.queue=[];t=b.createElement(e);t.async=!0;
t.src=v;s=b.getElementsByTagName(e)[0];
s.parentNode.insertBefore(t,s)}(window, document,'script',
'https://connect.facebook.net/en_US/fbevents.js');
fbq(<span class="string">'init'</span>, <span class="string">'${pixelId}'</span>);
fbq(<span class="string">'track'</span>, <span class="string">'PageView'</span>);
<span class="tag">&lt;/script&gt;</span>
`;
}
}
// Google Analytics
const ga4Card = document.querySelector('[data-tracker="ga4"]');
if (ga4Card && ga4Card.querySelector('swp-toggle-slider').dataset.value === 'yes') {
const measurementId = document.getElementById('ga4Id')?.textContent?.trim() || '';
if (measurementId) {
code += `<span class="comment">&lt;!-- Google Analytics (GA4) --&gt;</span>
<span class="tag">&lt;script</span> <span class="attr">async</span> <span class="attr">src</span>=<span class="string">"https://www.googletagmanager.com/gtag/js?id=${measurementId}"</span><span class="tag">&gt;&lt;/script&gt;</span>
<span class="tag">&lt;script&gt;</span>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag(<span class="string">'js'</span>, new Date());
gtag(<span class="string">'config'</span>, <span class="string">'${measurementId}'</span>);
<span class="tag">&lt;/script&gt;</span>
`;
}
}
// Google Tag Manager
const gtmCard = document.querySelector('[data-tracker="gtm"]');
if (gtmCard && gtmCard.querySelector('swp-toggle-slider').dataset.value === 'yes') {
const containerId = document.getElementById('gtmId')?.textContent?.trim() || '';
if (containerId) {
code += `<span class="comment">&lt;!-- Google Tag Manager --&gt;</span>
<span class="tag">&lt;script&gt;</span>
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer',<span class="string">'${containerId}'</span>);
<span class="tag">&lt;/script&gt;</span>
`;
}
}
// Plausible
const plausibleCard = document.querySelector('[data-tracker="plausible"]');
if (plausibleCard && plausibleCard.querySelector('swp-toggle-slider').dataset.value === 'yes') {
const domain = document.getElementById('plausibleDomain')?.textContent?.trim() || '';
if (domain) {
code += `<span class="comment">&lt;!-- Plausible Analytics --&gt;</span>
<span class="tag">&lt;script</span> <span class="attr">defer</span> <span class="attr">data-domain</span>=<span class="string">"${domain}"</span> <span class="attr">src</span>=<span class="string">"https://plausible.io/js/script.js"</span><span class="tag">&gt;&lt;/script&gt;</span>
`;
}
}
// Fathom
const fathomCard = document.querySelector('[data-tracker="fathom"]');
if (fathomCard && fathomCard.querySelector('swp-toggle-slider').dataset.value === 'yes') {
const siteId = document.getElementById('fathomSiteId')?.textContent?.trim() || '';
if (siteId) {
code += `<span class="comment">&lt;!-- Fathom Analytics --&gt;</span>
<span class="tag">&lt;script</span> <span class="attr">src</span>=<span class="string">"https://cdn.usefathom.com/script.js"</span> <span class="attr">data-site</span>=<span class="string">"${siteId}"</span> <span class="attr">defer</span><span class="tag">&gt;&lt;/script&gt;</span>
`;
}
}
// Matomo
const matomoCard = document.querySelector('[data-tracker="matomo"]');
if (matomoCard && matomoCard.querySelector('swp-toggle-slider').dataset.value === 'yes') {
const serverUrl = document.getElementById('matomoUrl')?.textContent?.trim() || '';
const siteId = document.getElementById('matomoSiteId')?.textContent?.trim() || '';
if (serverUrl && siteId) {
code += `<span class="comment">&lt;!-- Matomo Analytics --&gt;</span>
<span class="tag">&lt;script&gt;</span>
var _paq = window._paq = window._paq || [];
_paq.push([<span class="string">'trackPageView'</span>]);
_paq.push([<span class="string">'enableLinkTracking'</span>]);
(function() {
var u=<span class="string">"${serverUrl}/"</span>;
_paq.push([<span class="string">'setTrackerUrl'</span>, u+<span class="string">'matomo.php'</span>]);
_paq.push([<span class="string">'setSiteId'</span>, <span class="string">'${siteId}'</span>]);
var d=document, g=d.createElement(<span class="string">'script'</span>), s=d.getElementsByTagName(<span class="string">'script'</span>)[0];
g.async=true; g.src=u+<span class="string">'matomo.js'</span>; s.parentNode.insertBefore(g,s);
})();
<span class="tag">&lt;/script&gt;</span>
`;
}
}
// Custom Head Scripts
const customHead = document.getElementById('customHeadScripts')?.value?.trim() || '';
if (customHead) {
code += `<span class="comment">&lt;!-- Custom Scripts --&gt;</span>
${escapeHtml(customHead)}
`;
}
if (code) {
preview.querySelector('code').innerHTML = code.trim();
} else {
preview.querySelector('code').innerHTML = '<span class="comment">&lt;!-- Ingen aktive tracking-koder --&gt;</span>';
}
}
function escapeHtml(text) {
return text
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
}
function copyTrackingCode() {
const preview = document.getElementById('trackingCodePreview');
if (!preview) return;
// Get plain text version (strip HTML tags)
const code = preview.querySelector('code').innerHTML
.replace(/<span[^>]*>/g, '')
.replace(/<\/span>/g, '')
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>')
.replace(/&amp;/g, '&');
navigator.clipboard.writeText(code).then(() => {
const btn = document.querySelector('.code-preview-card swp-btn');
const originalText = btn.innerHTML;
btn.innerHTML = '<i class="ph ph-check"></i> Kopieret!';
btn.style.color = 'var(--color-green)';
setTimeout(() => {
btn.innerHTML = originalText;
btn.style.color = '';
}, 2000);
});
}
// Initialize tracking preview on page load
document.addEventListener('DOMContentLoaded', () => {
// Initialize tracking toggles
initTrackingToggles();
// Update preview
updateTrackingPreview();
// Set initial disabled state for cards
document.querySelectorAll('.tracking-card[data-tracker]').forEach(card => {
const toggle = card.querySelector('swp-toggle-slider');
if (toggle && toggle.dataset.value === 'no') {
card.classList.add('disabled');
}
});
});
</script> </script>
</body> </body>

File diff suppressed because it is too large Load diff