Enhances cart and payment interaction UI

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
This commit is contained in:
Janus C. H. Knudsen 2025-12-19 18:21:55 +01:00
parent e09048742c
commit 1b0ef74551
4 changed files with 759 additions and 69 deletions

View file

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 24 24">
<path d="m15,2.766v-1.766c0-.553-.448-1-1-1s-1,.447-1,1v1h-2v-1c0-.553-.448-1-1-1s-1,.447-1,1v1h-2v-1c0-.553-.448-1-1-1s-1,.447-1,1v1h-2v-1c0-.553-.448-1-1-1s-1,.447-1,1v1.766c-.613.55-1,1.347-1,2.234v14c0,2.757,2.243,5,5,5h6c2.757,0,5-2.243,5-5V5c0-.886-.387-1.684-1-2.234Zm-1,16.234c0,1.654-1.346,3-3,3h-6c-1.654,0-3-1.346-3-3V5c0-.552.449-1,1-1h10c.551,0,1,.448,1,1v14Zm-2-11c0,.553-.448,1-1,1h-6c-.552,0-1-.447-1-1s.448-1,1-1h6c.552,0,1,.447,1,1Zm0,4c0,.553-.448,1-1,1h-6c-.552,0-1-.447-1-1s.448-1,1-1h6c.552,0,1,.447,1,1Zm-3,4c0,.553-.448,1-1,1h-3c-.552,0-1-.447-1-1s.448-1,1-1h3c.552,0,1,.447,1,1ZM21,0c-1.654,0-3,1.346-3,3v16.758c0,1.054.427,2.084,1.172,2.828l1.121,1.121c.195.195.451.293.707.293s.512-.098.707-.293l1.121-1.121c.745-.744,1.172-1.774,1.172-2.828V3c0-1.654-1.346-3-3-3Zm1,19.758c0,.526-.213,1.042-.586,1.414l-.414.414-.414-.414c-.373-.372-.586-.888-.586-1.414V3c0-.552.449-1,1-1s1,.448,1,1v16.758Z"/>
</svg>

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 24 24" width="512" height="512"><path d="M17,12c0,.553-.448,1-1,1h-3v3c0,.553-.448,1-1,1s-1-.447-1-1v-3h-3c-.552,0-1-.447-1-1s.448-1,1-1h3v-3c0-.553,.448-1,1-1s1,.447,1,1v3h3c.552,0,1,.447,1,1Zm7-7v14c0,2.757-2.243,5-5,5H5c-2.757,0-5-2.243-5-5V5C0,2.243,2.243,0,5,0h14c2.757,0,5,2.243,5,5Zm-2,0c0-1.654-1.346-3-3-3H5c-1.654,0-3,1.346-3,3v14c0,1.654,1.346,3,3,3h14c1.654,0,3-1.346,3-3V5Z"/></svg>

After

Width:  |  Height:  |  Size: 521 B

View file

@ -253,12 +253,160 @@
/* Cart item card */ /* Cart item card */
.cart-item { .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; display: flex;
align-items: center; align-items: center;
gap: 16px; gap: 16px;
padding: 12px 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); background: var(--color-surface);
border-radius: 6px; 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 { .item-qty {
@ -307,10 +455,46 @@
text-align: right; 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 { .item-total {
font-family: var(--font-mono); font-family: var(--font-mono);
font-size: 15px; font-size: 15px;
font-weight: 600; 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 { .item-remove {
@ -463,15 +647,127 @@
font-size: 14px; font-size: 14px;
font-weight: 500; font-weight: 500;
cursor: pointer; cursor: pointer;
transition: background 200ms ease, opacity 200ms ease, transform 150ms ease;
} }
.btn-add-payment:hover { .btn-add-payment:hover:not(:disabled) {
background: #00796b; background: #00796b;
} }
.btn-add-payment:active:not(:disabled) {
transform: scale(0.98);
}
.btn-add-payment:disabled { .btn-add-payment:disabled {
background: #ccc; background: #ccc;
cursor: not-allowed; 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 */
@ -675,35 +971,99 @@
<div class="cart-section"> <div class="cart-section">
<div class="cart-section-header">Services</div> <div class="cart-section-header">Services</div>
<div class="cart-section-items"> <div class="cart-section-items">
<div class="cart-item"> <div class="cart-item" onclick="toggleCartItem(this)" data-base-price="725" data-duration="45 min">
<div class="item-qty"> <div class="cart-item-main">
<button class="qty-btn"></button> <div class="item-qty" onclick="event.stopPropagation()">
<span class="qty-val">1</span> <button class="qty-btn"></button>
<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>
<div class="item-info"> <div class="cart-item-edit" onclick="event.stopPropagation()">
<div class="item-name">Dameklip</div> <div class="edit-row">
<div class="item-meta">Emma Larsen · 45 min</div> <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="item-price">
<div class="item-total">725 kr</div>
</div>
<button class="item-remove"></button>
</div> </div>
<div class="cart-item"> <div class="cart-item" onclick="toggleCartItem(this)" data-base-price="900" data-duration="90 min">
<div class="item-qty"> <div class="cart-item-main">
<button class="qty-btn"></button> <div class="item-qty" onclick="event.stopPropagation()">
<span class="qty-val">1</span> <button class="qty-btn"></button>
<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>
<div class="item-info"> <div class="cart-item-edit" onclick="event.stopPropagation()">
<div class="item-name">Gloss extra langt/tykt hår</div> <div class="edit-row">
<div class="item-meta">Emma Larsen · 90 min</div> <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 class="item-price">
<div class="item-total">900 kr</div>
</div>
<button class="item-remove"></button>
</div> </div>
</div> </div>
</div> </div>
@ -712,35 +1072,79 @@
<div class="cart-section"> <div class="cart-section">
<div class="cart-section-header">Produkter</div> <div class="cart-section-header">Produkter</div>
<div class="cart-section-items"> <div class="cart-section-items">
<div class="cart-item"> <div class="cart-item" onclick="toggleCartItem(this)" data-base-price="300">
<div class="item-qty"> <div class="cart-item-main">
<button class="qty-btn"></button> <div class="item-qty" onclick="event.stopPropagation()">
<span class="qty-val">1</span> <button class="qty-btn"></button>
<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>
<div class="item-info"> <div class="cart-item-edit" onclick="event.stopPropagation()">
<div class="item-name">Olaplex No. 3</div> <div class="edit-row">
<div class="item-meta">100ml</div> <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="item-price">
<div class="item-total">300 kr</div>
</div>
<button class="item-remove"></button>
</div> </div>
<div class="cart-item"> <div class="cart-item" onclick="toggleCartItem(this)" data-base-price="175">
<div class="item-qty"> <div class="cart-item-main">
<button class="qty-btn"></button> <div class="item-qty" onclick="event.stopPropagation()">
<span class="qty-val">2</span> <button class="qty-btn"></button>
<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>
<div class="item-info"> <div class="cart-item-edit" onclick="event.stopPropagation()">
<div class="item-name">India supercharged mask</div> <div class="edit-row">
<div class="item-meta">250ml</div> <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 class="item-price">
<div class="item-total">350 kr</div>
</div>
<button class="item-remove"></button>
</div> </div>
</div> </div>
</div> </div>
@ -799,12 +1203,22 @@
</div> </div>
<div class="payment-input-section"> <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-label" id="inputLabel">Beløb at betale med Kort</div>
<div class="payment-input-row"> <div class="payment-input-row">
<input type="text" class="payment-input" id="paymentInput" value="2.275" inputmode="decimal"> <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> <button class="btn-pay-rest" onclick="payRest()">Betal rest<br><span id="restAmount">(2.275 kr)</span></button>
</div> </div>
<button class="btn-add-payment" onclick="addPayment()">+ Tilføj betaling</button> <button class="btn-add-payment" id="btnAddPayment" onclick="addPayment()">+ Tilføj betaling</button>
</div> </div>
<div class="registered-payments"> <div class="registered-payments">
@ -833,6 +1247,7 @@
const total = 2275; const total = 2275;
const payments = []; const payments = [];
let currentMethod = 'kort'; let currentMethod = 'kort';
let currentGiftcard = null; // { number, balance }
const methodLabels = { const methodLabels = {
'kort': 'Kort', 'kort': 'Kort',
@ -862,6 +1277,118 @@
document.getElementById('panel').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) { function selectMethod(method) {
currentMethod = method; currentMethod = method;
@ -872,23 +1399,153 @@
// Update label // Update label
document.getElementById('inputLabel').textContent = `Beløb at betale med ${methodLabels[method]}`; 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 // Set input to remaining amount
payRest(); 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() { function payRest() {
const remaining = getRemaining(); const remaining = getRemaining();
document.getElementById('paymentInput').value = formatNumber(remaining); 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() { function addPayment() {
const amount = parseAmount(document.getElementById('paymentInput').value); let amount = parseAmount(document.getElementById('paymentInput').value);
if (amount <= 0) return; if (amount <= 0) return;
payments.push({ // 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, method: currentMethod,
amount: amount 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(); updatePaymentsList();
updateTotals(); updateTotals();
@ -912,16 +1569,20 @@
return; return;
} }
container.innerHTML = payments.map((p, i) => ` container.innerHTML = payments.map((p, i) => {
<div class="payment-entry"> const detail = p.giftcardNumber ? `Kort: ${p.giftcardNumber}` : '';
<img class="payment-entry-icon" src="${methodIcons[p.method]}" alt=""> return `
<div class="payment-entry-info"> <div class="payment-entry">
<div class="payment-entry-method">${methodLabels[p.method]}</div> <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> </div>
<span class="payment-entry-amount">+${formatNumber(p.amount)} kr</span> `;
<button class="payment-entry-remove" onclick="removePayment(${i})"></button> }).join('');
</div>
`).join('');
} }
function updateTotals() { function updateTotals() {

View file

@ -442,7 +442,30 @@
} }
swp-journal-icon { swp-journal-icon {
font-size: 18px; width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
}
swp-journal-icon img {
width: 20px;
height: 20px;
filter: invert(22%) sepia(14%) saturate(1042%) hue-rotate(164deg) brightness(102%) contrast(85%);
transition: filter 150ms ease;
}
swp-journal-link:hover swp-journal-icon img {
filter: invert(32%) sepia(98%) saturate(1234%) hue-rotate(196deg) brightness(93%) contrast(92%);
}
swp-journal-link.no-journal swp-journal-icon img {
filter: invert(45%) sepia(8%) saturate(0%) hue-rotate(0deg) brightness(95%) contrast(90%);
}
swp-journal-link.no-journal:hover swp-journal-icon img {
filter: invert(32%) sepia(98%) saturate(1234%) hue-rotate(196deg) brightness(93%) contrast(92%);
} }
swp-journal-text { swp-journal-text {
@ -827,7 +850,7 @@
<swp-drawer-section> <swp-drawer-section>
<swp-section-label>Journal</swp-section-label> <swp-section-label>Journal</swp-section-label>
<swp-journal-link id="journalLink"> <swp-journal-link id="journalLink">
<swp-journal-icon>📋</swp-journal-icon> <swp-journal-icon><img id="journalIcon" src="icons/journal-alt.svg" alt=""></swp-journal-icon>
<swp-journal-text>Åbn journal</swp-journal-text> <swp-journal-text>Åbn journal</swp-journal-text>
<swp-journal-arrow></swp-journal-arrow> <swp-journal-arrow></swp-journal-arrow>
</swp-journal-link> </swp-journal-link>
@ -898,15 +921,15 @@
// Update journal link // Update journal link
const journalLink = document.getElementById('journalLink'); const journalLink = document.getElementById('journalLink');
const journalText = journalLink.querySelector('swp-journal-text'); const journalText = journalLink.querySelector('swp-journal-text');
const journalIcon = journalLink.querySelector('swp-journal-icon'); const journalIcon = document.getElementById('journalIcon');
if (hasJournal) { if (hasJournal) {
journalLink.classList.remove('no-journal'); journalLink.classList.remove('no-journal');
journalIcon.textContent = '📋'; journalIcon.src = 'icons/journal-alt.svg';
journalText.textContent = 'Åbn journal'; journalText.textContent = 'Åbn journal';
} else { } else {
journalLink.classList.add('no-journal'); journalLink.classList.add('no-journal');
journalIcon.textContent = ''; journalIcon.src = 'icons/square-plus.svg';
journalText.textContent = 'Opret journal'; journalText.textContent = 'Opret journal';
} }