Enhance POC dashboard with dynamic UI components

Adds comprehensive dashboard HTML with responsive design and interactive elements

Implements:
- Dark/light mode color variables
- Responsive grid layout
- Interactive booking and notification sections
- Dynamic time and status tracking
- Custom web components for dashboard functionality
This commit is contained in:
Janus C. H. Knudsen 2025-12-30 20:20:57 +01:00
parent 196129b74a
commit 0fa5b60a6b
2 changed files with 1714 additions and 17 deletions

1385
wwwroot/poc-dashboard.html Normal file

File diff suppressed because it is too large Load diff

View file

@ -898,6 +898,172 @@
min-height: 100px;
}
/* ==========================================
ADD VALUE DRAWER
========================================== */
swp-balance-info-box {
display: block;
padding: 16px;
background: var(--color-background-alt);
border-radius: 6px;
}
swp-balance-info-box swp-current-amount {
display: block;
font-size: 24px;
font-weight: 700;
font-family: var(--font-mono);
color: var(--color-text);
}
swp-balance-info-box swp-original-amount {
display: block;
font-size: 13px;
color: var(--color-text-secondary);
margin-top: 4px;
}
swp-new-balance-box {
display: block;
padding: 16px;
background: color-mix(in srgb, var(--color-green) 10%, transparent);
border: 1px solid color-mix(in srgb, var(--color-green) 30%, transparent);
border-radius: 6px;
}
swp-new-balance-box swp-new-amount {
display: block;
font-size: 24px;
font-weight: 700;
font-family: var(--font-mono);
color: var(--color-green);
}
swp-new-balance-box swp-new-label {
display: block;
font-size: 13px;
color: var(--color-text-secondary);
margin-top: 4px;
}
swp-quick-amounts {
display: flex;
flex-wrap: wrap;
gap: 8px;
margin-top: 8px;
}
swp-quick-amount {
padding: 8px 16px;
border: 1px solid var(--color-border);
border-radius: 6px;
font-size: 13px;
font-family: var(--font-mono);
background: var(--color-surface);
color: var(--color-text);
cursor: pointer;
transition: all 150ms ease;
}
swp-quick-amount:hover {
background: var(--color-background-hover);
border-color: var(--color-teal);
}
swp-quick-amount.selected {
background: color-mix(in srgb, var(--color-teal) 10%, transparent);
border-color: var(--color-teal);
color: var(--color-teal);
}
/* Toggle Slider (Ja/Nej) */
swp-toggle-slider {
display: inline-flex;
width: fit-content;
background: var(--color-background);
border-radius: 6px;
border: 1px solid var(--color-border);
overflow: hidden;
position: relative;
}
swp-toggle-slider::before {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: calc(50% - 4px);
height: calc(100% - 4px);
background: color-mix(in srgb, var(--color-green) 18%, white);
border-radius: 4px;
transition: transform 200ms ease, background 200ms ease;
}
swp-toggle-slider[data-value="no"]::before {
transform: translateX(100%);
background: color-mix(in srgb, var(--color-red) 18%, white);
}
swp-toggle-option {
position: relative;
z-index: 1;
padding: 5px 16px;
font-size: 12px;
font-weight: 500;
color: var(--color-text-secondary);
cursor: pointer;
transition: color 150ms ease;
user-select: none;
}
swp-toggle-slider[data-value="yes"] swp-toggle-option:first-child {
color: var(--color-green);
font-weight: 600;
}
swp-toggle-slider[data-value="no"] swp-toggle-option:last-child {
color: var(--color-red);
font-weight: 600;
}
swp-date-extend {
display: flex;
flex-direction: column;
gap: 10px;
}
swp-current-expiry {
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
color: var(--color-text-secondary);
}
swp-current-expiry i {
font-size: 16px;
}
swp-current-expiry strong {
color: var(--color-text);
}
swp-date-extend input[type="date"] {
padding: 10px 12px;
border: 1px solid var(--color-border);
border-radius: 6px;
font-size: 14px;
font-family: var(--font-family);
background: var(--color-surface);
color: var(--color-text);
transition: border-color 150ms ease;
}
swp-date-extend input[type="date"]:focus {
outline: none;
border-color: var(--color-teal);
}
/* ==========================================
RESPONSIVE
========================================== */
@ -948,20 +1114,12 @@
<swp-expiry-label>Udløbsdato</swp-expiry-label>
<swp-expiry-date>31. december 2025</swp-expiry-date>
</swp-expiry-info>
<swp-btn class="secondary" style="padding: 8px 12px; font-size: 12px;">
<i class="ph ph-calendar-plus"></i>
Forlæng
</swp-btn>
<swp-btn class="danger" style="padding: 8px 12px; font-size: 12px;">
<i class="ph ph-x-circle"></i>
Deaktiver
</swp-btn>
</swp-expiry-box>
<swp-actions-row>
<swp-btn class="secondary">
<i class="ph ph-plus-circle"></i>
Tilføj værdi
<swp-btn class="secondary" id="openAddValueDrawer">
<i class="ph ph-sliders-horizontal"></i>
Juster
</swp-btn>
<swp-btn class="secondary" id="openEmailDrawer">
<i class="ph ph-envelope"></i>
@ -997,6 +1155,15 @@
<swp-kv-label>Original værdi</swp-kv-label>
<swp-kv-value class="mono">500 DKK</swp-kv-value>
</swp-kv-row>
<swp-kv-row>
<swp-kv-label>Aktiv</swp-kv-label>
<swp-kv-value>
<swp-toggle-slider data-value="yes" id="activeToggle">
<swp-toggle-option>Ja</swp-toggle-option>
<swp-toggle-option>Nej</swp-toggle-option>
</swp-toggle-slider>
</swp-kv-value>
</swp-kv-row>
</swp-kv-list>
</swp-card>
@ -1141,6 +1308,69 @@
</swp-drawer-footer>
</swp-drawer>
<!-- Add Value Drawer -->
<swp-drawer id="addValueDrawer">
<swp-drawer-header>
<swp-drawer-title>Juster gavekort</swp-drawer-title>
<swp-drawer-close id="closeAddValueDrawer">
<i class="ph ph-x"></i>
</swp-drawer-close>
</swp-drawer-header>
<swp-drawer-content>
<swp-form-field>
<swp-form-label>Nuværende saldo</swp-form-label>
<swp-balance-info-box>
<swp-current-amount>350 DKK</swp-current-amount>
<swp-original-amount>af 500 DKK original værdi</swp-original-amount>
</swp-balance-info-box>
</swp-form-field>
<swp-form-field>
<swp-form-label>Beløb at tilføje</swp-form-label>
<input type="text" id="addValueAmount" class="mono" placeholder="Indtast beløb" value="100 DKK" />
<swp-quick-amounts>
<swp-quick-amount data-value="50">50 DKK</swp-quick-amount>
<swp-quick-amount data-value="100" class="selected">100 DKK</swp-quick-amount>
<swp-quick-amount data-value="200">200 DKK</swp-quick-amount>
<swp-quick-amount data-value="500">500 DKK</swp-quick-amount>
</swp-quick-amounts>
</swp-form-field>
<swp-form-field>
<swp-form-label>Ny saldo</swp-form-label>
<swp-new-balance-box>
<swp-new-amount id="newBalanceAmount">450 DKK</swp-new-amount>
<swp-new-label>Efter tilføjelse</swp-new-label>
</swp-new-balance-box>
</swp-form-field>
<swp-form-field>
<swp-form-label>Forlæng udløbsdato (valgfri)</swp-form-label>
<swp-date-extend>
<swp-current-expiry>
<i class="ph ph-calendar"></i>
<span>Nuværende: <strong>31. december 2025</strong></span>
</swp-current-expiry>
<input type="date" id="newExpiryDate" value="2026-12-31" />
</swp-date-extend>
</swp-form-field>
<swp-form-field>
<swp-form-label>Intern note</swp-form-label>
<textarea id="addValueNote" placeholder="Beskriv hvorfor værdi tilføjes..."></textarea>
</swp-form-field>
</swp-drawer-content>
<swp-drawer-footer>
<swp-btn class="secondary" id="cancelAddValue">Annuller</swp-btn>
<swp-btn class="primary" id="confirmAddValue">
<i class="ph ph-check"></i>
Gem ændringer
</swp-btn>
</swp-drawer-footer>
</swp-drawer>
<script>
// Elements
const overlay = document.getElementById('overlay');
@ -1197,7 +1427,6 @@
openEmailBtnAlt.addEventListener('click', openEmailDrawerFn);
closeEmailBtn.addEventListener('click', closeEmailDrawerFn);
cancelEmailBtn.addEventListener('click', closeEmailDrawerFn);
overlay.addEventListener('click', closeEmailDrawerFn);
// Real-time preview updates
personalMessageInput.addEventListener('input', updatePreview);
@ -1209,13 +1438,96 @@
closeEmailDrawerFn();
});
// Close on Escape
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') closeEmailDrawerFn();
});
// Initialize preview
updatePreview();
// ==========================================
// ADD VALUE DRAWER
// ==========================================
const addValueDrawer = document.getElementById('addValueDrawer');
const openAddValueBtn = document.getElementById('openAddValueDrawer');
const closeAddValueBtn = document.getElementById('closeAddValueDrawer');
const cancelAddValueBtn = document.getElementById('cancelAddValue');
const confirmAddValueBtn = document.getElementById('confirmAddValue');
const addValueAmountInput = document.getElementById('addValueAmount');
const newBalanceAmount = document.getElementById('newBalanceAmount');
const addValueQuickAmounts = document.querySelectorAll('#addValueDrawer swp-quick-amount');
const currentBalance = 350; // Demo value
// Open add value drawer
function openAddValueDrawerFn() {
overlay.classList.add('open');
addValueDrawer.classList.add('open');
document.body.style.overflow = 'hidden';
updateNewBalance();
}
// Close add value drawer
function closeAddValueDrawerFn() {
overlay.classList.remove('open');
addValueDrawer.classList.remove('open');
document.body.style.overflow = '';
}
// Parse amount from string
function parseAmount(str) {
const num = parseInt(str.replace(/[^\d]/g, ''), 10);
return isNaN(num) ? 0 : num;
}
// Update new balance calculation
function updateNewBalance() {
const addAmount = parseAmount(addValueAmountInput.value);
const newBalance = currentBalance + addAmount;
newBalanceAmount.textContent = newBalance.toLocaleString('da-DK') + ' DKK';
}
// Event listeners
openAddValueBtn.addEventListener('click', openAddValueDrawerFn);
closeAddValueBtn.addEventListener('click', closeAddValueDrawerFn);
cancelAddValueBtn.addEventListener('click', closeAddValueDrawerFn);
// Quick amount selection
addValueQuickAmounts.forEach(btn => {
btn.addEventListener('click', () => {
addValueQuickAmounts.forEach(b => b.classList.remove('selected'));
btn.classList.add('selected');
const value = btn.dataset.value;
addValueAmountInput.value = Number(value).toLocaleString('da-DK') + ' DKK';
updateNewBalance();
});
});
// Live update on input
addValueAmountInput.addEventListener('input', updateNewBalance);
// Confirm add value (demo)
confirmAddValueBtn.addEventListener('click', () => {
alert('Værdi tilføjet! (Demo)');
closeAddValueDrawerFn();
});
// Update overlay click to close any open drawer
overlay.addEventListener('click', () => {
closeEmailDrawerFn();
closeAddValueDrawerFn();
});
// Update Escape to close any drawer
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
closeEmailDrawerFn();
closeAddValueDrawerFn();
}
});
// Toggle sliders (Ja/Nej)
document.querySelectorAll('swp-toggle-slider').forEach(slider => {
slider.addEventListener('click', () => {
slider.dataset.value = slider.dataset.value === 'yes' ? 'no' : 'yes';
});
});
</script>
</body>
</html>