Adds interactive cart item editing with expandable sections Introduces dynamic discount calculation and display Implements giftcard lookup and balance tracking functionality Improves user experience with smooth transitions and more flexible item management
1629 lines
50 KiB
HTML
1629 lines
50 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="da">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>Checkout POC</title>
|
||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600;700&display=swap" rel="stylesheet">
|
||
<style>
|
||
:root {
|
||
--color-border: #e0e0e0;
|
||
--color-surface: #fff;
|
||
--color-background: #f5f5f5;
|
||
--color-background-hover: #f0f0f0;
|
||
--color-background-alt: #fafafa;
|
||
--color-text: #333;
|
||
--color-text-secondary: #666;
|
||
--color-teal: #00897b;
|
||
--color-red: #e53935;
|
||
--transition-fast: 150ms ease;
|
||
--font-mono: 'JetBrains Mono', monospace;
|
||
}
|
||
|
||
* { box-sizing: border-box; margin: 0; padding: 0; }
|
||
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||
background: var(--color-background);
|
||
font-size: 14px;
|
||
color: var(--color-text);
|
||
}
|
||
|
||
.demo-trigger { padding: 20px; }
|
||
|
||
.demo-btn {
|
||
padding: 12px 24px;
|
||
background: var(--color-teal);
|
||
color: white;
|
||
border: none;
|
||
border-radius: 4px;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
}
|
||
|
||
/* Overlay & Panel */
|
||
.overlay {
|
||
position: fixed;
|
||
inset: 0;
|
||
background: rgba(0,0,0,0.25);
|
||
opacity: 0;
|
||
visibility: hidden;
|
||
transition: opacity 200ms, visibility 200ms;
|
||
z-index: 100;
|
||
}
|
||
.overlay.open { opacity: 1; visibility: visible; }
|
||
|
||
.panel {
|
||
position: fixed;
|
||
top: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
width: 80%;
|
||
background: var(--color-background);
|
||
transform: translateX(100%);
|
||
transition: transform 200ms ease;
|
||
display: flex;
|
||
flex-direction: column;
|
||
z-index: 101;
|
||
box-shadow: -4px 0 20px rgba(0,0,0,0.15);
|
||
}
|
||
.panel.open { transform: translateX(0); }
|
||
|
||
/* Header */
|
||
.header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 40px;
|
||
padding: 20px 28px;
|
||
background: var(--color-surface);
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
|
||
.header-field { display: flex; align-items: center; gap: 10px; }
|
||
.header-label { color: var(--color-text-secondary); font-size: 13px; }
|
||
.header-value { font-weight: 500; font-size: 15px; }
|
||
.header-link { color: var(--color-teal); cursor: pointer; font-size: 13px; }
|
||
|
||
.header-select {
|
||
padding: 6px 28px 6px 10px;
|
||
border: 1px solid var(--color-border);
|
||
border-radius: 4px;
|
||
font-size: 14px;
|
||
background: var(--color-surface) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%23666' stroke-width='2'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E") no-repeat right 8px center;
|
||
appearance: none;
|
||
}
|
||
|
||
.header-close {
|
||
margin-left: auto;
|
||
background: none;
|
||
border: none;
|
||
font-size: 20px;
|
||
color: var(--color-text-secondary);
|
||
cursor: pointer;
|
||
padding: 4px 8px;
|
||
}
|
||
|
||
/* Main layout */
|
||
.main {
|
||
flex: 1;
|
||
display: grid;
|
||
grid-template-columns: 20% 1fr 35%;
|
||
overflow: hidden;
|
||
}
|
||
|
||
/* Sidebar */
|
||
.sidebar {
|
||
border-right: 1px solid var(--color-border);
|
||
display: flex;
|
||
flex-direction: column;
|
||
background: var(--color-surface);
|
||
}
|
||
|
||
.search-box {
|
||
padding: 20px;
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
|
||
.search-input {
|
||
width: 100%;
|
||
padding: 12px 14px 12px 40px;
|
||
border: 1px solid var(--color-border);
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
background: var(--color-surface) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23999' stroke-width='2'%3E%3Ccircle cx='11' cy='11' r='8'/%3E%3Cpath d='m21 21-4.35-4.35'/%3E%3C/svg%3E") no-repeat 12px center;
|
||
}
|
||
|
||
.menu-section {
|
||
border-bottom: 1px solid var(--color-border);
|
||
padding: 12px 0;
|
||
}
|
||
|
||
.menu-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 12px 20px;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
}
|
||
.menu-item:hover { background: var(--color-background-hover); }
|
||
.menu-item.active { color: var(--color-teal); font-weight: 500; }
|
||
|
||
.categories {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding: 12px 0;
|
||
}
|
||
|
||
.category {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 12px 20px;
|
||
font-size: 14px;
|
||
cursor: pointer;
|
||
color: var(--color-text-secondary);
|
||
}
|
||
.category:hover { background: var(--color-background-hover); color: var(--color-text); }
|
||
|
||
/* Cart area */
|
||
.cart-area {
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
background: var(--color-background);
|
||
}
|
||
|
||
.cart-list {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding: 20px 24px;
|
||
}
|
||
|
||
.cart-section {
|
||
margin-bottom: 24px;
|
||
}
|
||
|
||
.cart-section:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.cart-section-header {
|
||
font-size: 11px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
color: var(--color-text-secondary);
|
||
padding: 0 4px 10px;
|
||
border-bottom: 1px solid var(--color-border);
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.cart-section-items {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
/* Cart footer with totals */
|
||
.cart-footer {
|
||
padding: 20px 24px;
|
||
background: var(--color-surface);
|
||
border-top: 1px solid var(--color-border);
|
||
}
|
||
|
||
.cart-totals {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 8px;
|
||
}
|
||
|
||
.cart-total-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.cart-total-row .label {
|
||
color: var(--color-text-secondary);
|
||
}
|
||
|
||
.cart-total-row .value {
|
||
font-family: var(--font-mono);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.cart-total-row.grand {
|
||
padding-top: 12px;
|
||
border-top: 1px solid var(--color-border);
|
||
margin-top: 4px;
|
||
}
|
||
|
||
.cart-total-row.grand .label {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: var(--color-text);
|
||
}
|
||
|
||
.cart-total-row.grand .value {
|
||
font-family: var(--font-mono);
|
||
font-size: 24px;
|
||
font-weight: 700;
|
||
}
|
||
|
||
/* Cart item card */
|
||
.cart-item {
|
||
background: var(--color-surface);
|
||
border-radius: 6px;
|
||
overflow: hidden;
|
||
transition: box-shadow 200ms ease;
|
||
}
|
||
|
||
.cart-item.expanded {
|
||
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||
}
|
||
|
||
.cart-item-main {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
padding: 12px 16px;
|
||
cursor: pointer;
|
||
transition: background 150ms ease;
|
||
}
|
||
|
||
.cart-item-main:hover {
|
||
background: var(--color-background-hover);
|
||
}
|
||
|
||
.cart-item.expanded .cart-item-main {
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
|
||
.cart-item-main::after {
|
||
content: '';
|
||
width: 8px;
|
||
height: 8px;
|
||
border-right: 2px solid var(--color-text-secondary);
|
||
border-bottom: 2px solid var(--color-text-secondary);
|
||
transform: rotate(45deg);
|
||
transition: transform 200ms ease;
|
||
margin-left: 4px;
|
||
opacity: 0.5;
|
||
}
|
||
|
||
.cart-item:hover .cart-item-main::after {
|
||
opacity: 1;
|
||
}
|
||
|
||
.cart-item.expanded .cart-item-main::after {
|
||
transform: rotate(-135deg);
|
||
opacity: 1;
|
||
}
|
||
|
||
/* Expandable edit section */
|
||
.cart-item-edit {
|
||
max-height: 0;
|
||
opacity: 0;
|
||
overflow: hidden;
|
||
transition: max-height 300ms cubic-bezier(0.4, 0, 0.2, 1),
|
||
opacity 250ms ease,
|
||
padding 300ms ease;
|
||
padding: 0 16px;
|
||
background: var(--color-background-alt);
|
||
}
|
||
|
||
.cart-item.expanded .cart-item-edit {
|
||
max-height: 200px;
|
||
opacity: 1;
|
||
padding: 16px;
|
||
}
|
||
|
||
.edit-row {
|
||
display: flex;
|
||
gap: 12px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.edit-row:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.edit-field {
|
||
flex: 1;
|
||
}
|
||
|
||
.edit-field-label {
|
||
font-size: 11px;
|
||
color: var(--color-text-secondary);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
margin-bottom: 6px;
|
||
}
|
||
|
||
.edit-field-input {
|
||
width: 100%;
|
||
padding: 8px 10px;
|
||
border: 1px solid var(--color-border);
|
||
border-radius: 4px;
|
||
font-size: 13px;
|
||
transition: border-color 150ms ease;
|
||
}
|
||
|
||
.edit-field-input:focus {
|
||
outline: none;
|
||
border-color: var(--color-teal);
|
||
}
|
||
|
||
.edit-field-input.mono {
|
||
font-family: var(--font-mono);
|
||
text-align: right;
|
||
}
|
||
|
||
.edit-field-select {
|
||
width: 100%;
|
||
padding: 8px 10px;
|
||
border: 1px solid var(--color-border);
|
||
border-radius: 4px;
|
||
font-size: 13px;
|
||
background: var(--color-surface);
|
||
cursor: pointer;
|
||
}
|
||
|
||
.edit-field-select:focus {
|
||
outline: none;
|
||
border-color: var(--color-teal);
|
||
}
|
||
|
||
.discount-row {
|
||
display: flex;
|
||
gap: 8px;
|
||
align-items: center;
|
||
}
|
||
|
||
.discount-type {
|
||
display: flex;
|
||
gap: 4px;
|
||
}
|
||
|
||
.discount-type-btn {
|
||
padding: 6px 10px;
|
||
border: 1px solid var(--color-border);
|
||
background: var(--color-surface);
|
||
font-size: 12px;
|
||
cursor: pointer;
|
||
transition: all 150ms ease;
|
||
}
|
||
|
||
.discount-type-btn:first-child {
|
||
border-radius: 4px 0 0 4px;
|
||
}
|
||
|
||
.discount-type-btn:last-child {
|
||
border-radius: 0 4px 4px 0;
|
||
}
|
||
|
||
.discount-type-btn.active {
|
||
background: var(--color-teal);
|
||
border-color: var(--color-teal);
|
||
color: white;
|
||
}
|
||
|
||
.item-qty {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
}
|
||
|
||
.qty-btn {
|
||
width: 26px;
|
||
height: 26px;
|
||
border: 1px solid var(--color-border);
|
||
background: var(--color-surface);
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
color: var(--color-text-secondary);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.qty-btn:hover { background: var(--color-background-hover); }
|
||
|
||
.qty-val {
|
||
width: 24px;
|
||
text-align: center;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.item-info {
|
||
flex: 1;
|
||
}
|
||
|
||
.item-name {
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.item-meta {
|
||
font-size: 12px;
|
||
color: var(--color-text-secondary);
|
||
}
|
||
|
||
.item-price {
|
||
text-align: right;
|
||
}
|
||
|
||
.item-original-price {
|
||
font-family: var(--font-mono);
|
||
font-size: 12px;
|
||
color: var(--color-text-secondary);
|
||
text-decoration: line-through;
|
||
max-height: 0;
|
||
opacity: 0;
|
||
overflow: hidden;
|
||
transition: max-height 200ms ease, opacity 150ms ease;
|
||
}
|
||
|
||
.item-original-price.visible {
|
||
max-height: 20px;
|
||
opacity: 1;
|
||
}
|
||
|
||
.item-total {
|
||
font-family: var(--font-mono);
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
transition: color 200ms ease;
|
||
}
|
||
|
||
.cart-item:has(.item-discount.visible) .item-total {
|
||
color: var(--color-teal);
|
||
}
|
||
|
||
.item-discount {
|
||
font-size: 11px;
|
||
color: var(--color-teal);
|
||
font-weight: 500;
|
||
max-height: 0;
|
||
opacity: 0;
|
||
overflow: hidden;
|
||
transition: max-height 200ms ease, opacity 150ms ease;
|
||
}
|
||
|
||
.item-discount.visible {
|
||
max-height: 20px;
|
||
opacity: 1;
|
||
}
|
||
|
||
.item-remove {
|
||
background: none;
|
||
border: none;
|
||
font-size: 16px;
|
||
color: var(--color-text-secondary);
|
||
cursor: pointer;
|
||
opacity: 0.3;
|
||
padding: 4px;
|
||
}
|
||
.item-remove:hover { opacity: 1; color: var(--color-red); }
|
||
|
||
/* Payment Panel */
|
||
.payment-panel {
|
||
background: var(--color-surface);
|
||
border-left: 1px solid var(--color-border);
|
||
display: flex;
|
||
flex-direction: column;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.payment-total {
|
||
padding: 24px 28px;
|
||
text-align: center;
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
|
||
.payment-total-amount {
|
||
font-family: var(--font-mono);
|
||
font-size: 48px;
|
||
font-weight: 700;
|
||
color: var(--color-text);
|
||
letter-spacing: -2px;
|
||
}
|
||
|
||
.payment-total-label {
|
||
font-size: 12px;
|
||
color: var(--color-text-secondary);
|
||
margin-top: 4px;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
}
|
||
|
||
.payment-methods {
|
||
display: flex;
|
||
gap: 8px;
|
||
padding: 16px 24px;
|
||
border-bottom: 1px solid var(--color-border);
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.method-btn {
|
||
flex: 1 1 auto;
|
||
min-width: 70px;
|
||
padding: 12px 8px;
|
||
border: 2px solid var(--color-border);
|
||
border-radius: 6px;
|
||
background: var(--color-surface);
|
||
cursor: pointer;
|
||
text-align: center;
|
||
transition: all var(--transition-fast);
|
||
}
|
||
|
||
.method-btn:hover {
|
||
border-color: #ccc;
|
||
}
|
||
|
||
.method-btn.active {
|
||
border-color: var(--color-teal);
|
||
background: rgba(0, 137, 123, 0.05);
|
||
}
|
||
|
||
.method-btn-icon {
|
||
width: 24px;
|
||
height: 24px;
|
||
display: block;
|
||
margin: 0 auto 4px;
|
||
filter: invert(22%) sepia(14%) saturate(1042%) hue-rotate(164deg) brightness(102%) contrast(85%);
|
||
}
|
||
|
||
.method-btn-label {
|
||
font-size: 11px;
|
||
color: var(--color-text-secondary);
|
||
}
|
||
|
||
.method-btn.active .method-btn-label {
|
||
color: var(--color-teal);
|
||
font-weight: 500;
|
||
}
|
||
|
||
.payment-input-section {
|
||
padding: 16px 24px;
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
|
||
.payment-input-label {
|
||
font-size: 11px;
|
||
color: var(--color-text-secondary);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.payment-input-row {
|
||
display: flex;
|
||
gap: 10px;
|
||
}
|
||
|
||
.payment-input {
|
||
flex: 1;
|
||
padding: 12px 16px;
|
||
border: 1px solid var(--color-border);
|
||
border-radius: 6px;
|
||
font-family: var(--font-mono);
|
||
font-size: 22px;
|
||
font-weight: 600;
|
||
text-align: right;
|
||
}
|
||
|
||
.payment-input:focus {
|
||
outline: none;
|
||
border-color: var(--color-teal);
|
||
}
|
||
|
||
.btn-pay-rest {
|
||
padding: 10px 12px;
|
||
border: 1px solid var(--color-border);
|
||
border-radius: 6px;
|
||
background: var(--color-surface);
|
||
font-size: 11px;
|
||
color: var(--color-text-secondary);
|
||
cursor: pointer;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.btn-pay-rest:hover {
|
||
border-color: var(--color-teal);
|
||
color: var(--color-teal);
|
||
}
|
||
|
||
.btn-add-payment {
|
||
width: 100%;
|
||
padding: 12px;
|
||
margin-top: 12px;
|
||
border: none;
|
||
border-radius: 6px;
|
||
background: var(--color-teal);
|
||
color: white;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: background 200ms ease, opacity 200ms ease, transform 150ms ease;
|
||
}
|
||
|
||
.btn-add-payment:hover:not(:disabled) {
|
||
background: #00796b;
|
||
}
|
||
|
||
.btn-add-payment:active:not(:disabled) {
|
||
transform: scale(0.98);
|
||
}
|
||
|
||
.btn-add-payment:disabled {
|
||
background: #ccc;
|
||
cursor: not-allowed;
|
||
opacity: 0.7;
|
||
}
|
||
|
||
/* Giftcard lookup */
|
||
.giftcard-lookup {
|
||
max-height: 0;
|
||
opacity: 0;
|
||
overflow: hidden;
|
||
transition: max-height 300ms cubic-bezier(0.4, 0, 0.2, 1),
|
||
opacity 250ms ease,
|
||
margin-bottom 300ms ease,
|
||
padding-bottom 300ms ease;
|
||
margin-bottom: 0;
|
||
padding-bottom: 0;
|
||
border-bottom: 1px solid transparent;
|
||
}
|
||
|
||
.giftcard-lookup.visible {
|
||
max-height: 150px;
|
||
opacity: 1;
|
||
margin-bottom: 16px;
|
||
padding-bottom: 16px;
|
||
border-bottom-color: var(--color-border);
|
||
}
|
||
|
||
.giftcard-lookup-label {
|
||
font-size: 11px;
|
||
color: var(--color-text-secondary);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.giftcard-lookup-row {
|
||
display: flex;
|
||
gap: 10px;
|
||
}
|
||
|
||
.giftcard-input {
|
||
flex: 1;
|
||
padding: 10px 12px;
|
||
border: 1px solid var(--color-border);
|
||
border-radius: 6px;
|
||
font-family: var(--font-mono);
|
||
font-size: 14px;
|
||
letter-spacing: 1px;
|
||
}
|
||
|
||
.giftcard-input:focus {
|
||
outline: none;
|
||
border-color: var(--color-teal);
|
||
}
|
||
|
||
.btn-lookup {
|
||
padding: 10px 16px;
|
||
border: 1px solid var(--color-teal);
|
||
border-radius: 6px;
|
||
background: var(--color-surface);
|
||
color: var(--color-teal);
|
||
font-size: 13px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: background 150ms ease, transform 150ms ease;
|
||
}
|
||
|
||
.btn-lookup:hover {
|
||
background: rgba(0, 137, 123, 0.08);
|
||
}
|
||
|
||
.btn-lookup:active {
|
||
transform: scale(0.97);
|
||
}
|
||
|
||
.giftcard-result {
|
||
margin-top: 0;
|
||
padding: 0 12px;
|
||
border-radius: 6px;
|
||
font-size: 13px;
|
||
max-height: 0;
|
||
opacity: 0;
|
||
overflow: hidden;
|
||
transition: max-height 250ms ease,
|
||
opacity 200ms ease,
|
||
margin-top 250ms ease,
|
||
padding 250ms ease;
|
||
}
|
||
|
||
.giftcard-result.visible {
|
||
max-height: 50px;
|
||
opacity: 1;
|
||
margin-top: 10px;
|
||
padding: 10px 12px;
|
||
}
|
||
|
||
.giftcard-result.success {
|
||
background: rgba(0, 137, 123, 0.1);
|
||
color: var(--color-teal);
|
||
}
|
||
|
||
.giftcard-result.error {
|
||
background: rgba(229, 57, 53, 0.1);
|
||
color: var(--color-red);
|
||
}
|
||
|
||
.giftcard-balance {
|
||
font-family: var(--font-mono);
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* Registered payments */
|
||
.registered-payments {
|
||
flex: 1;
|
||
overflow-y: auto;
|
||
padding: 16px 24px;
|
||
}
|
||
|
||
.registered-payments-label {
|
||
font-size: 11px;
|
||
color: var(--color-text-secondary);
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.5px;
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.payment-entry {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 12px;
|
||
padding: 10px 0;
|
||
border-bottom: 1px solid var(--color-border);
|
||
}
|
||
|
||
.payment-entry-icon {
|
||
width: 20px;
|
||
height: 20px;
|
||
filter: invert(22%) sepia(14%) saturate(1042%) hue-rotate(164deg) brightness(102%) contrast(85%);
|
||
}
|
||
|
||
.payment-entry-info {
|
||
flex: 1;
|
||
}
|
||
|
||
.payment-entry-method {
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.payment-entry-detail {
|
||
font-size: 12px;
|
||
color: var(--color-text-secondary);
|
||
}
|
||
|
||
.payment-entry-amount {
|
||
font-family: var(--font-mono);
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: var(--color-teal);
|
||
}
|
||
|
||
.payment-entry-remove {
|
||
background: none;
|
||
border: none;
|
||
color: var(--color-text-secondary);
|
||
cursor: pointer;
|
||
font-size: 16px;
|
||
opacity: 0.5;
|
||
padding: 4px;
|
||
}
|
||
|
||
.payment-entry-remove:hover {
|
||
opacity: 1;
|
||
color: var(--color-red);
|
||
}
|
||
|
||
.no-payments {
|
||
color: var(--color-text-secondary);
|
||
font-size: 13px;
|
||
text-align: center;
|
||
padding: 24px;
|
||
}
|
||
|
||
/* Payment footer */
|
||
.payment-footer {
|
||
padding: 16px 24px;
|
||
border-top: 1px solid var(--color-border);
|
||
background: var(--color-background-alt);
|
||
}
|
||
|
||
.remaining-row {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 16px;
|
||
}
|
||
|
||
.remaining-label {
|
||
font-size: 14px;
|
||
color: var(--color-text-secondary);
|
||
}
|
||
|
||
.remaining-value {
|
||
font-family: var(--font-mono);
|
||
font-size: 28px;
|
||
font-weight: 700;
|
||
color: var(--color-red);
|
||
}
|
||
|
||
.remaining-value.zero {
|
||
color: var(--color-teal);
|
||
}
|
||
|
||
.btn-complete {
|
||
width: 100%;
|
||
padding: 16px;
|
||
border: none;
|
||
border-radius: 6px;
|
||
background: var(--color-teal);
|
||
color: white;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
}
|
||
|
||
.btn-complete:hover {
|
||
background: #00796b;
|
||
}
|
||
|
||
.btn-complete:disabled {
|
||
background: #ccc;
|
||
cursor: not-allowed;
|
||
}
|
||
|
||
.payment-actions {
|
||
display: flex;
|
||
gap: 12px;
|
||
margin-top: 16px;
|
||
}
|
||
|
||
.btn-secondary {
|
||
flex: 1;
|
||
padding: 10px;
|
||
border: 1px solid var(--color-border);
|
||
border-radius: 6px;
|
||
background: var(--color-surface);
|
||
font-size: 13px;
|
||
color: var(--color-text-secondary);
|
||
cursor: pointer;
|
||
}
|
||
|
||
.btn-secondary:hover {
|
||
border-color: #ccc;
|
||
}
|
||
|
||
</style>
|
||
</head>
|
||
<body>
|
||
<div class="demo-trigger">
|
||
<button class="demo-btn" onclick="openPanel()">Gå til betaling</button>
|
||
</div>
|
||
|
||
<div class="overlay" id="overlay" onclick="closePanel()"></div>
|
||
|
||
<div class="panel" id="panel">
|
||
<div class="header">
|
||
<div class="header-field">
|
||
<span class="header-label">Kunde</span>
|
||
<span class="header-value">Sofie Nielsen</span>
|
||
<span class="header-link">Fjern</span>
|
||
</div>
|
||
<div class="header-field">
|
||
<span class="header-label">Dato</span>
|
||
<span class="header-value">16. dec 2025</span>
|
||
</div>
|
||
<div class="header-field">
|
||
<span class="header-label">Betjent af</span>
|
||
<select class="header-select">
|
||
<option>Emma Larsen</option>
|
||
<option>Anett Davidsson</option>
|
||
</select>
|
||
</div>
|
||
<button class="header-close" onclick="closePanel()">✕</button>
|
||
</div>
|
||
|
||
<div class="main">
|
||
<div class="sidebar">
|
||
<div class="search-box">
|
||
<input type="text" class="search-input" placeholder="Søg varer...">
|
||
</div>
|
||
<div class="menu-section">
|
||
<div class="menu-item"><span>Brugerdefineret linje</span></div>
|
||
<div class="menu-item"><span>Tidligere salg</span></div>
|
||
<div class="menu-item"><span>Refunder</span></div>
|
||
<div class="menu-item active"><span>Services</span></div>
|
||
</div>
|
||
<div class="categories">
|
||
<div class="category"><span>Acidic Bonding</span><span>›</span></div>
|
||
<div class="category"><span>Amino Mint</span><span>›</span></div>
|
||
<div class="category"><span>Color Gloss</span><span>›</span></div>
|
||
<div class="category"><span>Conditioner</span><span>›</span></div>
|
||
<div class="category"><span>Styling</span><span>›</span></div>
|
||
<div class="category"><span>Olaplex</span><span>›</span></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="cart-area">
|
||
<div class="cart-list">
|
||
<!-- Services -->
|
||
<div class="cart-section">
|
||
<div class="cart-section-header">Services</div>
|
||
<div class="cart-section-items">
|
||
<div class="cart-item" onclick="toggleCartItem(this)" data-base-price="725" data-duration="45 min">
|
||
<div class="cart-item-main">
|
||
<div class="item-qty" onclick="event.stopPropagation()">
|
||
<button class="qty-btn">−</button>
|
||
<span class="qty-val">1</span>
|
||
<button class="qty-btn">+</button>
|
||
</div>
|
||
<div class="item-info">
|
||
<div class="item-name">Dameklip</div>
|
||
<div class="item-meta"><span class="item-employee">Emma Larsen</span> · 45 min</div>
|
||
</div>
|
||
<div class="item-price">
|
||
<div class="item-original-price">725 kr</div>
|
||
<div class="item-total">725 kr</div>
|
||
<div class="item-discount">-0 kr rabat</div>
|
||
</div>
|
||
<button class="item-remove" onclick="event.stopPropagation()">✕</button>
|
||
</div>
|
||
<div class="cart-item-edit" onclick="event.stopPropagation()">
|
||
<div class="edit-row">
|
||
<div class="edit-field">
|
||
<div class="edit-field-label">Pris</div>
|
||
<input type="text" class="edit-field-input mono edit-price" value="725" oninput="updateCartItemDisplay(this)">
|
||
</div>
|
||
<div class="edit-field">
|
||
<div class="edit-field-label">Udført af</div>
|
||
<select class="edit-field-select edit-employee" onchange="updateCartItemDisplay(this)">
|
||
<option selected>Emma Larsen</option>
|
||
<option>Anett Davidsson</option>
|
||
<option>Maria Jensen</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="edit-row">
|
||
<div class="edit-field">
|
||
<div class="edit-field-label">Rabat</div>
|
||
<div class="discount-row">
|
||
<input type="text" class="edit-field-input mono edit-discount" value="0" style="width: 80px;" oninput="updateCartItemDisplay(this)">
|
||
<div class="discount-type">
|
||
<button class="discount-type-btn active" data-type="kr">kr</button>
|
||
<button class="discount-type-btn" data-type="pct">%</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="cart-item" onclick="toggleCartItem(this)" data-base-price="900" data-duration="90 min">
|
||
<div class="cart-item-main">
|
||
<div class="item-qty" onclick="event.stopPropagation()">
|
||
<button class="qty-btn">−</button>
|
||
<span class="qty-val">1</span>
|
||
<button class="qty-btn">+</button>
|
||
</div>
|
||
<div class="item-info">
|
||
<div class="item-name">Gloss extra langt/tykt hår</div>
|
||
<div class="item-meta"><span class="item-employee">Emma Larsen</span> · 90 min</div>
|
||
</div>
|
||
<div class="item-price">
|
||
<div class="item-original-price">900 kr</div>
|
||
<div class="item-total">900 kr</div>
|
||
<div class="item-discount">-0 kr rabat</div>
|
||
</div>
|
||
<button class="item-remove" onclick="event.stopPropagation()">✕</button>
|
||
</div>
|
||
<div class="cart-item-edit" onclick="event.stopPropagation()">
|
||
<div class="edit-row">
|
||
<div class="edit-field">
|
||
<div class="edit-field-label">Pris</div>
|
||
<input type="text" class="edit-field-input mono edit-price" value="900" oninput="updateCartItemDisplay(this)">
|
||
</div>
|
||
<div class="edit-field">
|
||
<div class="edit-field-label">Udført af</div>
|
||
<select class="edit-field-select edit-employee" onchange="updateCartItemDisplay(this)">
|
||
<option selected>Emma Larsen</option>
|
||
<option>Anett Davidsson</option>
|
||
<option>Maria Jensen</option>
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<div class="edit-row">
|
||
<div class="edit-field">
|
||
<div class="edit-field-label">Rabat</div>
|
||
<div class="discount-row">
|
||
<input type="text" class="edit-field-input mono edit-discount" value="0" style="width: 80px;" oninput="updateCartItemDisplay(this)">
|
||
<div class="discount-type">
|
||
<button class="discount-type-btn active" data-type="kr">kr</button>
|
||
<button class="discount-type-btn" data-type="pct">%</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Produkter -->
|
||
<div class="cart-section">
|
||
<div class="cart-section-header">Produkter</div>
|
||
<div class="cart-section-items">
|
||
<div class="cart-item" onclick="toggleCartItem(this)" data-base-price="300">
|
||
<div class="cart-item-main">
|
||
<div class="item-qty" onclick="event.stopPropagation()">
|
||
<button class="qty-btn">−</button>
|
||
<span class="qty-val">1</span>
|
||
<button class="qty-btn">+</button>
|
||
</div>
|
||
<div class="item-info">
|
||
<div class="item-name">Olaplex No. 3</div>
|
||
<div class="item-meta">100ml</div>
|
||
</div>
|
||
<div class="item-price">
|
||
<div class="item-original-price">300 kr</div>
|
||
<div class="item-total">300 kr</div>
|
||
<div class="item-discount">-0 kr rabat</div>
|
||
</div>
|
||
<button class="item-remove" onclick="event.stopPropagation()">✕</button>
|
||
</div>
|
||
<div class="cart-item-edit" onclick="event.stopPropagation()">
|
||
<div class="edit-row">
|
||
<div class="edit-field">
|
||
<div class="edit-field-label">Pris</div>
|
||
<input type="text" class="edit-field-input mono edit-price" value="300" oninput="updateCartItemDisplay(this)">
|
||
</div>
|
||
<div class="edit-field">
|
||
<div class="edit-field-label">Rabat</div>
|
||
<div class="discount-row">
|
||
<input type="text" class="edit-field-input mono edit-discount" value="0" style="width: 80px;" oninput="updateCartItemDisplay(this)">
|
||
<div class="discount-type">
|
||
<button class="discount-type-btn active" data-type="kr">kr</button>
|
||
<button class="discount-type-btn" data-type="pct">%</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="cart-item" onclick="toggleCartItem(this)" data-base-price="175">
|
||
<div class="cart-item-main">
|
||
<div class="item-qty" onclick="event.stopPropagation()">
|
||
<button class="qty-btn">−</button>
|
||
<span class="qty-val">2</span>
|
||
<button class="qty-btn">+</button>
|
||
</div>
|
||
<div class="item-info">
|
||
<div class="item-name">India supercharged mask</div>
|
||
<div class="item-meta">250ml</div>
|
||
</div>
|
||
<div class="item-price">
|
||
<div class="item-original-price">350 kr</div>
|
||
<div class="item-total">350 kr</div>
|
||
<div class="item-discount">-0 kr rabat</div>
|
||
</div>
|
||
<button class="item-remove" onclick="event.stopPropagation()">✕</button>
|
||
</div>
|
||
<div class="cart-item-edit" onclick="event.stopPropagation()">
|
||
<div class="edit-row">
|
||
<div class="edit-field">
|
||
<div class="edit-field-label">Pris</div>
|
||
<input type="text" class="edit-field-input mono edit-price" value="175" oninput="updateCartItemDisplay(this)">
|
||
</div>
|
||
<div class="edit-field">
|
||
<div class="edit-field-label">Rabat</div>
|
||
<div class="discount-row">
|
||
<input type="text" class="edit-field-input mono edit-discount" value="0" style="width: 80px;" oninput="updateCartItemDisplay(this)">
|
||
<div class="discount-type">
|
||
<button class="discount-type-btn active" data-type="kr">kr</button>
|
||
<button class="discount-type-btn" data-type="pct">%</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="cart-footer">
|
||
<div class="cart-totals">
|
||
<div class="cart-total-row">
|
||
<span class="label">Subtotal</span>
|
||
<span class="value">1.820 kr</span>
|
||
</div>
|
||
<div class="cart-total-row">
|
||
<span class="label">Moms (25%)</span>
|
||
<span class="value">455 kr</span>
|
||
</div>
|
||
<div class="cart-total-row grand">
|
||
<span class="label">Total</span>
|
||
<span class="value">2.275 kr</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Payment Panel -->
|
||
<div class="payment-panel">
|
||
<div class="payment-total">
|
||
<div class="payment-total-amount" id="totalAmount">2.275 kr</div>
|
||
<div class="payment-total-label">Total at betale</div>
|
||
</div>
|
||
|
||
<div class="payment-methods">
|
||
<button class="method-btn active" onclick="selectMethod('kort')">
|
||
<img class="method-btn-icon" src="icons/credit-card.svg" alt="">
|
||
<span class="method-btn-label">Kort</span>
|
||
</button>
|
||
<button class="method-btn" onclick="selectMethod('kontant')">
|
||
<img class="method-btn-icon" src="icons/coins.svg" alt="">
|
||
<span class="method-btn-label">Kontant</span>
|
||
</button>
|
||
<button class="method-btn" onclick="selectMethod('mobilepay')">
|
||
<img class="method-btn-icon" src="icons/mobilepay.svg" alt="">
|
||
<span class="method-btn-label">MobilePay</span>
|
||
</button>
|
||
<button class="method-btn" onclick="selectMethod('bank')">
|
||
<img class="method-btn-icon" src="icons/bank.svg" alt="">
|
||
<span class="method-btn-label">Bank</span>
|
||
</button>
|
||
<button class="method-btn" onclick="selectMethod('gavekort')">
|
||
<img class="method-btn-icon" src="icons/gift-card.svg" alt="">
|
||
<span class="method-btn-label">Gavekort</span>
|
||
</button>
|
||
<button class="method-btn" onclick="selectMethod('tilgode')">
|
||
<img class="method-btn-icon" src="icons/loan.svg" alt="">
|
||
<span class="method-btn-label">Tilgode</span>
|
||
</button>
|
||
</div>
|
||
|
||
<div class="payment-input-section">
|
||
<!-- Gavekort lookup -->
|
||
<div class="giftcard-lookup" id="giftcardLookup">
|
||
<div class="giftcard-lookup-label">Indtast gavekortnummer</div>
|
||
<div class="giftcard-lookup-row">
|
||
<input type="text" class="giftcard-input" id="giftcardInput" placeholder="XXXX-XXXX-XXXX" onkeydown="if(event.key==='Enter')lookupGiftcard()">
|
||
<button class="btn-lookup" onclick="lookupGiftcard()">Slå op</button>
|
||
</div>
|
||
<div class="giftcard-result" id="giftcardResult"></div>
|
||
</div>
|
||
|
||
<div class="payment-input-label" id="inputLabel">Beløb at betale med Kort</div>
|
||
<div class="payment-input-row">
|
||
<input type="text" class="payment-input" id="paymentInput" value="2.275" inputmode="decimal">
|
||
<button class="btn-pay-rest" onclick="payRest()">Betal rest<br><span id="restAmount">(2.275 kr)</span></button>
|
||
</div>
|
||
<button class="btn-add-payment" id="btnAddPayment" onclick="addPayment()">+ Tilføj betaling</button>
|
||
</div>
|
||
|
||
<div class="registered-payments">
|
||
<div class="registered-payments-label">Registrerede betalinger</div>
|
||
<div id="paymentsList">
|
||
<div class="no-payments">Ingen betalinger registreret</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="payment-footer">
|
||
<div class="remaining-row">
|
||
<span class="remaining-label">Restbeløb</span>
|
||
<span class="remaining-value" id="remainingAmount">2.275 kr</span>
|
||
</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">Send på mail</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
const total = 2275;
|
||
const payments = [];
|
||
let currentMethod = 'kort';
|
||
let currentGiftcard = null; // { number, balance }
|
||
|
||
const methodLabels = {
|
||
'kort': 'Kort',
|
||
'kontant': 'Kontant',
|
||
'mobilepay': 'MobilePay',
|
||
'bank': 'Bank',
|
||
'gavekort': 'Gavekort',
|
||
'tilgode': 'Tilgode'
|
||
};
|
||
|
||
const methodIcons = {
|
||
'kort': 'icons/credit-card.svg',
|
||
'kontant': 'icons/coins.svg',
|
||
'mobilepay': 'icons/mobilepay.svg',
|
||
'bank': 'icons/bank.svg',
|
||
'gavekort': 'icons/gift-card.svg',
|
||
'tilgode': 'icons/loan.svg'
|
||
};
|
||
|
||
function openPanel() {
|
||
document.getElementById('overlay').classList.add('open');
|
||
document.getElementById('panel').classList.add('open');
|
||
}
|
||
|
||
function closePanel() {
|
||
document.getElementById('overlay').classList.remove('open');
|
||
document.getElementById('panel').classList.remove('open');
|
||
}
|
||
|
||
function toggleCartItem(item) {
|
||
// Close other expanded items
|
||
document.querySelectorAll('.cart-item.expanded').forEach(el => {
|
||
if (el !== item) el.classList.remove('expanded');
|
||
});
|
||
// Toggle this item
|
||
item.classList.toggle('expanded');
|
||
}
|
||
|
||
function toggleDiscountType(btn) {
|
||
const container = btn.closest('.discount-type');
|
||
const previousType = container.querySelector('.discount-type-btn.active')?.dataset.type;
|
||
const newType = btn.dataset.type;
|
||
|
||
// Don't do anything if clicking the same button
|
||
if (previousType === newType) return;
|
||
|
||
container.querySelectorAll('.discount-type-btn').forEach(b => b.classList.remove('active'));
|
||
btn.classList.add('active');
|
||
|
||
// Convert discount value
|
||
const cartItem = btn.closest('.cart-item');
|
||
const discountInput = cartItem.querySelector('.edit-discount');
|
||
const priceInput = cartItem.querySelector('.edit-price');
|
||
|
||
if (discountInput && priceInput) {
|
||
const currentValue = parseFloat(discountInput.value.replace(/\./g, '').replace(',', '.')) || 0;
|
||
const basePrice = parseFloat(priceInput.value.replace(/\./g, '').replace(',', '.')) || 0;
|
||
|
||
if (basePrice > 0 && currentValue > 0) {
|
||
let newValue;
|
||
if (previousType === 'pct' && newType === 'kr') {
|
||
// Converting from % to kr
|
||
newValue = Math.round(basePrice * currentValue / 100);
|
||
} else if (previousType === 'kr' && newType === 'pct') {
|
||
// Converting from kr to %
|
||
newValue = Math.round((currentValue / basePrice) * 100 * 10) / 10; // 1 decimal
|
||
}
|
||
discountInput.value = newValue;
|
||
}
|
||
|
||
updateCartItemDisplay(discountInput);
|
||
}
|
||
}
|
||
|
||
function updateCartItemDisplay(inputEl) {
|
||
const cartItem = inputEl.closest('.cart-item');
|
||
if (!cartItem) return;
|
||
|
||
// Get elements
|
||
const priceInput = cartItem.querySelector('.edit-price');
|
||
const employeeSelect = cartItem.querySelector('.edit-employee');
|
||
const discountInput = cartItem.querySelector('.edit-discount');
|
||
const discountTypeBtn = cartItem.querySelector('.discount-type-btn.active');
|
||
|
||
const itemTotal = cartItem.querySelector('.item-total');
|
||
const itemOriginalPrice = cartItem.querySelector('.item-original-price');
|
||
const itemDiscount = cartItem.querySelector('.item-discount');
|
||
const itemEmployee = cartItem.querySelector('.item-employee');
|
||
|
||
// Get values
|
||
const basePrice = parseFloat(priceInput?.value.replace(/\./g, '').replace(',', '.')) || 0;
|
||
const discountValue = parseFloat(discountInput?.value.replace(/\./g, '').replace(',', '.')) || 0;
|
||
const discountType = discountTypeBtn?.dataset.type || 'kr';
|
||
const qty = parseInt(cartItem.querySelector('.qty-val')?.textContent) || 1;
|
||
|
||
// Calculate discount
|
||
let discountAmount = 0;
|
||
if (discountType === 'kr') {
|
||
discountAmount = discountValue;
|
||
} else {
|
||
discountAmount = (basePrice * discountValue / 100);
|
||
}
|
||
|
||
const finalPrice = Math.max(0, (basePrice - discountAmount) * qty);
|
||
const totalBeforeDiscount = basePrice * qty;
|
||
|
||
// Update display
|
||
itemTotal.textContent = formatNumber(finalPrice) + ' kr';
|
||
|
||
// Show original price and discount if there's a discount
|
||
if (discountAmount > 0) {
|
||
itemOriginalPrice.textContent = formatNumber(totalBeforeDiscount) + ' kr';
|
||
itemOriginalPrice.classList.add('visible');
|
||
|
||
if (discountType === 'pct') {
|
||
itemDiscount.textContent = `-${discountValue}% rabat`;
|
||
} else {
|
||
itemDiscount.textContent = `-${formatNumber(discountAmount * qty)} kr rabat`;
|
||
}
|
||
itemDiscount.classList.add('visible');
|
||
} else {
|
||
itemOriginalPrice.classList.remove('visible');
|
||
itemDiscount.classList.remove('visible');
|
||
}
|
||
|
||
// Update employee if exists
|
||
if (employeeSelect && itemEmployee) {
|
||
itemEmployee.textContent = employeeSelect.value;
|
||
}
|
||
}
|
||
|
||
// Setup discount type button handlers
|
||
document.addEventListener('DOMContentLoaded', () => {
|
||
document.querySelectorAll('.discount-type-btn').forEach(btn => {
|
||
btn.addEventListener('click', (e) => {
|
||
e.stopPropagation();
|
||
toggleDiscountType(btn);
|
||
});
|
||
});
|
||
});
|
||
|
||
function selectMethod(method) {
|
||
currentMethod = method;
|
||
|
||
// Update active button
|
||
document.querySelectorAll('.method-btn').forEach(btn => btn.classList.remove('active'));
|
||
event.currentTarget.classList.add('active');
|
||
|
||
// Update label
|
||
document.getElementById('inputLabel').textContent = `Beløb at betale med ${methodLabels[method]}`;
|
||
|
||
// Show/hide giftcard lookup
|
||
const giftcardLookup = document.getElementById('giftcardLookup');
|
||
const btnAddPayment = document.getElementById('btnAddPayment');
|
||
|
||
if (method === 'gavekort') {
|
||
giftcardLookup.classList.add('visible');
|
||
// Disable add payment until giftcard is looked up
|
||
if (!currentGiftcard) {
|
||
btnAddPayment.disabled = true;
|
||
}
|
||
} else {
|
||
giftcardLookup.classList.remove('visible');
|
||
currentGiftcard = null;
|
||
resetGiftcardLookup();
|
||
btnAddPayment.disabled = false;
|
||
}
|
||
|
||
// Set input to remaining amount
|
||
payRest();
|
||
}
|
||
|
||
function lookupGiftcard() {
|
||
const input = document.getElementById('giftcardInput');
|
||
const result = document.getElementById('giftcardResult');
|
||
const cardNumber = input.value.trim();
|
||
|
||
if (!cardNumber) {
|
||
result.className = 'giftcard-result visible error';
|
||
result.innerHTML = 'Indtast venligst et gavekortnummer';
|
||
return;
|
||
}
|
||
|
||
// Simulate lookup - in real app this would be an API call
|
||
// For demo: simulate different balances based on card number
|
||
const simulatedBalance = simulateGiftcardBalance(cardNumber);
|
||
|
||
if (simulatedBalance === null) {
|
||
result.className = 'giftcard-result visible error';
|
||
result.innerHTML = 'Gavekort ikke fundet';
|
||
currentGiftcard = null;
|
||
document.getElementById('btnAddPayment').disabled = true;
|
||
return;
|
||
}
|
||
|
||
// Success - card found
|
||
currentGiftcard = {
|
||
number: cardNumber,
|
||
balance: simulatedBalance
|
||
};
|
||
|
||
result.className = 'giftcard-result visible success';
|
||
result.innerHTML = `Gavekort fundet! Saldo: <span class="giftcard-balance">${formatNumber(simulatedBalance)} kr</span>`;
|
||
|
||
// Enable payment button
|
||
document.getElementById('btnAddPayment').disabled = false;
|
||
|
||
// Set payment input to min of remaining and giftcard balance
|
||
const remaining = getRemaining();
|
||
const maxPayment = Math.min(remaining, simulatedBalance);
|
||
const paymentInput = document.getElementById('paymentInput');
|
||
paymentInput.value = formatNumber(maxPayment);
|
||
|
||
// Focus payment input and select the value
|
||
setTimeout(() => {
|
||
paymentInput.focus();
|
||
paymentInput.select();
|
||
}, 50);
|
||
}
|
||
|
||
function simulateGiftcardBalance(cardNumber) {
|
||
// Demo simulation: different cards have different balances
|
||
// Cards starting with "1" = 500 kr, "2" = 1000 kr, "3" = 1500 kr
|
||
// Any other = 750 kr, empty/invalid = not found
|
||
|
||
if (cardNumber.length < 4) return null;
|
||
|
||
const firstChar = cardNumber.charAt(0);
|
||
switch (firstChar) {
|
||
case '1': return 500;
|
||
case '2': return 1000;
|
||
case '3': return 1500;
|
||
case '0': return null; // Not found
|
||
default: return 750;
|
||
}
|
||
}
|
||
|
||
function resetGiftcardLookup() {
|
||
document.getElementById('giftcardInput').value = '';
|
||
const result = document.getElementById('giftcardResult');
|
||
result.className = 'giftcard-result';
|
||
result.innerHTML = '';
|
||
}
|
||
|
||
function payRest() {
|
||
const remaining = getRemaining();
|
||
let maxPayment = remaining;
|
||
|
||
// For giftcard, limit to available balance
|
||
if (currentMethod === 'gavekort' && currentGiftcard) {
|
||
maxPayment = Math.min(remaining, currentGiftcard.balance);
|
||
}
|
||
|
||
document.getElementById('paymentInput').value = formatNumber(maxPayment);
|
||
}
|
||
|
||
function addPayment() {
|
||
let amount = parseAmount(document.getElementById('paymentInput').value);
|
||
if (amount <= 0) return;
|
||
|
||
// For giftcard payments, validate and limit to balance
|
||
if (currentMethod === 'gavekort') {
|
||
if (!currentGiftcard) {
|
||
alert('Slå gavekortet op først');
|
||
return;
|
||
}
|
||
// Limit to available balance
|
||
if (amount > currentGiftcard.balance) {
|
||
amount = currentGiftcard.balance;
|
||
}
|
||
}
|
||
|
||
const payment = {
|
||
method: currentMethod,
|
||
amount: amount
|
||
};
|
||
|
||
// Add giftcard details if applicable
|
||
if (currentMethod === 'gavekort' && currentGiftcard) {
|
||
payment.giftcardNumber = currentGiftcard.number;
|
||
payment.giftcardOriginalBalance = currentGiftcard.balance;
|
||
|
||
// Reduce the giftcard balance (simulate using it)
|
||
currentGiftcard.balance -= amount;
|
||
|
||
// If balance is 0, reset the giftcard lookup for next use
|
||
if (currentGiftcard.balance <= 0) {
|
||
currentGiftcard = null;
|
||
resetGiftcardLookup();
|
||
document.getElementById('btnAddPayment').disabled = true;
|
||
} else {
|
||
// Update displayed balance
|
||
const result = document.getElementById('giftcardResult');
|
||
result.innerHTML = `Gavekort fundet! Resterende saldo: <span class="giftcard-balance">${formatNumber(currentGiftcard.balance)} kr</span>`;
|
||
}
|
||
}
|
||
|
||
payments.push(payment);
|
||
|
||
updatePaymentsList();
|
||
updateTotals();
|
||
|
||
// Reset input to new remaining
|
||
payRest();
|
||
}
|
||
|
||
function removePayment(index) {
|
||
payments.splice(index, 1);
|
||
updatePaymentsList();
|
||
updateTotals();
|
||
payRest();
|
||
}
|
||
|
||
function updatePaymentsList() {
|
||
const container = document.getElementById('paymentsList');
|
||
|
||
if (payments.length === 0) {
|
||
container.innerHTML = '<div class="no-payments">Ingen betalinger registreret</div>';
|
||
return;
|
||
}
|
||
|
||
container.innerHTML = payments.map((p, i) => {
|
||
const detail = p.giftcardNumber ? `Kort: ${p.giftcardNumber}` : '';
|
||
return `
|
||
<div class="payment-entry">
|
||
<img class="payment-entry-icon" src="${methodIcons[p.method]}" alt="">
|
||
<div class="payment-entry-info">
|
||
<div class="payment-entry-method">${methodLabels[p.method]}</div>
|
||
${detail ? `<div class="payment-entry-detail">${detail}</div>` : ''}
|
||
</div>
|
||
<span class="payment-entry-amount">+${formatNumber(p.amount)} kr</span>
|
||
<button class="payment-entry-remove" onclick="removePayment(${i})">✕</button>
|
||
</div>
|
||
`;
|
||
}).join('');
|
||
}
|
||
|
||
function updateTotals() {
|
||
const remaining = getRemaining();
|
||
|
||
// Update remaining display
|
||
const remainingEl = document.getElementById('remainingAmount');
|
||
remainingEl.textContent = formatNumber(remaining) + ' kr';
|
||
|
||
// Update rest button
|
||
document.getElementById('restAmount').textContent = `(${formatNumber(remaining)} kr)`;
|
||
|
||
// Toggle styling and button state
|
||
const btnComplete = document.getElementById('btnComplete');
|
||
if (remaining <= 0) {
|
||
remainingEl.classList.add('zero');
|
||
btnComplete.disabled = false;
|
||
} else {
|
||
remainingEl.classList.remove('zero');
|
||
btnComplete.disabled = true;
|
||
}
|
||
}
|
||
|
||
function getRemaining() {
|
||
const paid = payments.reduce((sum, p) => sum + p.amount, 0);
|
||
return Math.max(0, total - paid);
|
||
}
|
||
|
||
function formatNumber(num) {
|
||
return num.toLocaleString('da-DK');
|
||
}
|
||
|
||
function parseAmount(str) {
|
||
if (!str) return 0;
|
||
const cleaned = str.replace(/\./g, '').replace(',', '.').replace(/[^0-9.]/g, '');
|
||
return parseFloat(cleaned) || 0;
|
||
}
|
||
|
||
document.addEventListener('keydown', e => {
|
||
if (e.key === 'Escape') closePanel();
|
||
});
|
||
</script>
|
||
</body>
|
||
</html>
|