Moving away from Azure Devops #1
5 changed files with 1105 additions and 5702 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -1033,6 +1033,326 @@
|
|||
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>
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -1303,7 +1623,178 @@
|
|||
</div>
|
||||
|
||||
<!-- 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-amount" id="totalAmount">2.275 kr</div>
|
||||
<div class="payment-total-label">Total at betale</div>
|
||||
|
|
@ -1369,7 +1860,7 @@
|
|||
</div>
|
||||
<button class="btn-complete" id="btnComplete" disabled>Afslut salg</button>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -1978,6 +2469,31 @@
|
|||
}
|
||||
`;
|
||||
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>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1684,6 +1684,181 @@
|
|||
swp-btn.primary.purple:hover {
|
||||
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>
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -1735,6 +1910,10 @@
|
|||
<i class="ph ph-puzzle-piece"></i>
|
||||
Moduler
|
||||
</swp-tab>
|
||||
<swp-tab data-tab="tracking">
|
||||
<i class="ph ph-chart-line-up"></i>
|
||||
Tracking
|
||||
</swp-tab>
|
||||
</swp-tab-bar>
|
||||
|
||||
<!-- ==========================================
|
||||
|
|
@ -3193,6 +3372,214 @@ Tak for din handel!</swp-edit-textarea>
|
|||
</swp-modules-section>
|
||||
</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 <head>
|
||||
</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 </body>
|
||||
</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 <head> 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
|
||||
========================================== -->
|
||||
|
|
@ -3699,6 +4086,206 @@ Vil du ændre din tid? {booking_link}</swp-message-editor>
|
|||
// Initial state
|
||||
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"><!-- Meta Pixel --></span>
|
||||
<span class="tag"><script></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"></script></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"><!-- Google Analytics (GA4) --></span>
|
||||
<span class="tag"><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">></script></span>
|
||||
<span class="tag"><script></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"></script></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"><!-- Google Tag Manager --></span>
|
||||
<span class="tag"><script></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"></script></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"><!-- Plausible Analytics --></span>
|
||||
<span class="tag"><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">></script></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"><!-- Fathom Analytics --></span>
|
||||
<span class="tag"><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">></script></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"><!-- Matomo Analytics --></span>
|
||||
<span class="tag"><script></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"></script></span>
|
||||
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
// Custom Head Scripts
|
||||
const customHead = document.getElementById('customHeadScripts')?.value?.trim() || '';
|
||||
if (customHead) {
|
||||
code += `<span class="comment"><!-- Custom Scripts --></span>
|
||||
${escapeHtml(customHead)}
|
||||
|
||||
`;
|
||||
}
|
||||
|
||||
if (code) {
|
||||
preview.querySelector('code').innerHTML = code.trim();
|
||||
} else {
|
||||
preview.querySelector('code').innerHTML = '<span class="comment"><!-- Ingen aktive tracking-koder --></span>';
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
return text
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>');
|
||||
}
|
||||
|
||||
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(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/&/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>
|
||||
|
||||
</body>
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
Loading…
Add table
Add a link
Reference in a new issue