Moving away from Azure Devops #1

Merged
Janus007 merged 113 commits from refac into master 2026-02-03 00:04:27 +01:00
4 changed files with 759 additions and 69 deletions
Showing only changes of commit 1b0ef74551 - Show all commits

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 {
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);
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 {
@ -307,10 +455,46 @@
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 {
@ -463,15 +647,127 @@
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: background 200ms ease, opacity 200ms ease, transform 150ms ease;
}
.btn-add-payment:hover {
.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 */
@ -675,35 +971,99 @@
<div class="cart-section">
<div class="cart-section-header">Services</div>
<div class="cart-section-items">
<div class="cart-item">
<div class="item-qty">
<button class="qty-btn"></button>
<span class="qty-val">1</span>
<button class="qty-btn">+</button>
<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="item-info">
<div class="item-name">Dameklip</div>
<div class="item-meta">Emma Larsen · 45 min</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 class="item-price">
<div class="item-total">725 kr</div>
</div>
<button class="item-remove"></button>
</div>
<div class="cart-item">
<div class="item-qty">
<button class="qty-btn"></button>
<span class="qty-val">1</span>
<button class="qty-btn">+</button>
<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="item-info">
<div class="item-name">Gloss extra langt/tykt hår</div>
<div class="item-meta">Emma Larsen · 90 min</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 class="item-price">
<div class="item-total">900 kr</div>
</div>
<button class="item-remove"></button>
</div>
</div>
</div>
@ -712,35 +1072,79 @@
<div class="cart-section">
<div class="cart-section-header">Produkter</div>
<div class="cart-section-items">
<div class="cart-item">
<div class="item-qty">
<button class="qty-btn"></button>
<span class="qty-val">1</span>
<button class="qty-btn">+</button>
<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="item-info">
<div class="item-name">Olaplex No. 3</div>
<div class="item-meta">100ml</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 class="item-price">
<div class="item-total">300 kr</div>
</div>
<button class="item-remove"></button>
</div>
<div class="cart-item">
<div class="item-qty">
<button class="qty-btn"></button>
<span class="qty-val">2</span>
<button class="qty-btn">+</button>
<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="item-info">
<div class="item-name">India supercharged mask</div>
<div class="item-meta">250ml</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 class="item-price">
<div class="item-total">350 kr</div>
</div>
<button class="item-remove"></button>
</div>
</div>
</div>
@ -799,12 +1203,22 @@
</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" onclick="addPayment()">+ Tilføj betaling</button>
<button class="btn-add-payment" id="btnAddPayment" onclick="addPayment()">+ Tilføj betaling</button>
</div>
<div class="registered-payments">
@ -833,6 +1247,7 @@
const total = 2275;
const payments = [];
let currentMethod = 'kort';
let currentGiftcard = null; // { number, balance }
const methodLabels = {
'kort': 'Kort',
@ -862,6 +1277,118 @@
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;
@ -872,23 +1399,153 @@
// 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();
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() {
const amount = parseAmount(document.getElementById('paymentInput').value);
let amount = parseAmount(document.getElementById('paymentInput').value);
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,
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();
@ -912,16 +1569,20 @@
return;
}
container.innerHTML = payments.map((p, i) => `
<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>
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>
<span class="payment-entry-amount">+${formatNumber(p.amount)} kr</span>
<button class="payment-entry-remove" onclick="removePayment(${i})"></button>
</div>
`).join('');
`;
}).join('');
}
function updateTotals() {

View file

@ -442,7 +442,30 @@
}
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 {
@ -827,7 +850,7 @@
<swp-drawer-section>
<swp-section-label>Journal</swp-section-label>
<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-arrow></swp-journal-arrow>
</swp-journal-link>
@ -898,15 +921,15 @@
// Update journal link
const journalLink = document.getElementById('journalLink');
const journalText = journalLink.querySelector('swp-journal-text');
const journalIcon = journalLink.querySelector('swp-journal-icon');
const journalIcon = document.getElementById('journalIcon');
if (hasJournal) {
journalLink.classList.remove('no-journal');
journalIcon.textContent = '📋';
journalIcon.src = 'icons/journal-alt.svg';
journalText.textContent = 'Åbn journal';
} else {
journalLink.classList.add('no-journal');
journalIcon.textContent = '';
journalIcon.src = 'icons/square-plus.svg';
journalText.textContent = 'Opret journal';
}