diff --git a/wwwroot/icons/booking.svg b/wwwroot/icons/booking.svg new file mode 100644 index 0000000..7a65614 --- /dev/null +++ b/wwwroot/icons/booking.svg @@ -0,0 +1,4 @@ + + + + diff --git a/wwwroot/icons/check-in-calendar.svg b/wwwroot/icons/check-in-calendar.svg new file mode 100644 index 0000000..a47968f --- /dev/null +++ b/wwwroot/icons/check-in-calendar.svg @@ -0,0 +1,4 @@ + + + + diff --git a/wwwroot/icons/comment-sms.svg b/wwwroot/icons/comment-sms.svg new file mode 100644 index 0000000..f6e7a17 --- /dev/null +++ b/wwwroot/icons/comment-sms.svg @@ -0,0 +1,2 @@ + + diff --git a/wwwroot/icons/created.svg b/wwwroot/icons/created.svg new file mode 100644 index 0000000..663e635 --- /dev/null +++ b/wwwroot/icons/created.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/wwwroot/icons/unlock.svg b/wwwroot/icons/unlock.svg new file mode 100644 index 0000000..35971b9 --- /dev/null +++ b/wwwroot/icons/unlock.svg @@ -0,0 +1,2 @@ + + diff --git a/wwwroot/poc-customer-detail.html b/wwwroot/poc-customer-detail.html index fce68e1..56146e1 100644 --- a/wwwroot/poc-customer-detail.html +++ b/wwwroot/poc-customer-detail.html @@ -1633,6 +1633,16 @@ font-size: 11px; } + swp-activity-filter .filter-icon img { + width: 12px; + height: 12px; + filter: invert(45%) sepia(0%) saturate(0%) brightness(90%); + } + + swp-activity-filter.active .filter-icon img { + filter: brightness(0) invert(1); + } + /* Activity Timeline */ swp-activity-timeline { display: block; @@ -1687,39 +1697,65 @@ flex-shrink: 0; } + swp-activity-icon img { + width: 16px; + height: 16px; + } + swp-activity-icon.system { background: var(--color-background); - color: var(--color-text-secondary); + } + + swp-activity-icon.system img { + filter: invert(45%) sepia(0%) saturate(0%) brightness(90%); } swp-activity-icon.customer { background: color-mix(in srgb, var(--color-blue) 15%, white); - color: var(--color-blue); + } + + swp-activity-icon.customer img { + filter: invert(32%) sepia(98%) saturate(1234%) hue-rotate(196deg) brightness(93%) contrast(92%); } swp-activity-icon.employee { background: color-mix(in srgb, var(--color-teal) 15%, white); - color: var(--color-teal); + } + + swp-activity-icon.employee img { + filter: invert(32%) sepia(98%) saturate(1234%) hue-rotate(152deg) brightness(93%) contrast(92%); } swp-activity-icon.booking { background: color-mix(in srgb, var(--color-purple) 15%, white); - color: var(--color-purple); + } + + swp-activity-icon.booking img { + filter: invert(45%) sepia(70%) saturate(2000%) hue-rotate(235deg) brightness(90%) contrast(95%); } swp-activity-icon.communication { background: color-mix(in srgb, var(--color-amber) 15%, white); - color: #b8860b; + } + + swp-activity-icon.communication img { + filter: invert(55%) sepia(80%) saturate(500%) hue-rotate(10deg) brightness(95%) contrast(95%); } swp-activity-icon.payment { background: color-mix(in srgb, var(--color-green) 15%, white); - color: var(--color-green); + } + + swp-activity-icon.payment img { + filter: invert(45%) sepia(70%) saturate(500%) hue-rotate(90deg) brightness(95%) contrast(90%); } swp-activity-icon.warning { background: color-mix(in srgb, var(--color-red) 15%, white); - color: var(--color-red); + } + + swp-activity-icon.warning img { + filter: invert(30%) sepia(90%) saturate(2000%) hue-rotate(340deg) brightness(90%) contrast(95%); } swp-activity-content { @@ -1834,6 +1870,94 @@ swp-activity-load-more:hover { text-decoration: underline; } + + /* ========================================== + DRAG & DROP CARDS + ========================================== */ + + /* Draggable Card */ + swp-card[draggable="true"] { + position: relative; + transition: transform 200ms ease, box-shadow 200ms ease, opacity 200ms ease; + } + + swp-card[draggable="true"]:hover { + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1); + } + + swp-card[draggable="true"].dragging { + opacity: 0.5; + transform: scale(0.98); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15); + } + + /* Drag Handle */ + swp-drag-handle { + position: absolute; + top: 8px; + right: 8px; + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; + color: var(--color-text-secondary); + opacity: 0; + transition: opacity 150ms ease, background 150ms ease; + cursor: move; + } + + swp-drag-handle svg { + width: 14px; + height: 14px; + fill: currentColor; + } + + swp-card[draggable="true"]:hover swp-drag-handle { + opacity: 0.5; + } + + swp-drag-handle:hover { + opacity: 1 !important; + background: var(--color-background); + } + + /* Column Drop Zones */ + swp-card-column { + display: flex; + flex-direction: column; + min-height: 200px; + position: relative; + } + + /* Drop zone indicator element - dynamically inserted */ + swp-drop-indicator { + display: block; + height: 60px; + background: color-mix(in srgb, var(--color-teal) 8%, transparent); + border: 2px dashed var(--color-teal); + border-radius: 8px; + margin: 8px 0; + transition: opacity 150ms ease; + } + + /* Column empty state drop zone */ + swp-card-column.drag-over-empty { + background: color-mix(in srgb, var(--color-teal) 5%, white); + border: 2px dashed var(--color-teal); + border-radius: 8px; + min-height: 100px; + } + + /* Cards have no gap by default */ + swp-card-column swp-card { + margin-bottom: 16px; + } + + swp-card-column swp-card:last-child { + margin-bottom: 0; + } @@ -1912,8 +2036,9 @@
-
- + + + Kontaktoplysninger @@ -1935,7 +2060,8 @@ - + + Profil @@ -1956,11 +2082,12 @@ -
+ -
- + + + Marketing Email marketing @@ -1978,7 +2105,8 @@ - + + Præferencer @@ -1996,7 +2124,8 @@ - + + Advarsler @@ -2006,7 +2135,8 @@ - + + Kundegruppe & Relationer @@ -2056,7 +2186,7 @@ -
+
@@ -2621,11 +2751,11 @@ Alle - 📅 Bookinger - 💬 Kommunikation - ✏️ Ændringer - 💳 Betalinger - 🔐 Login + Bookinger + Kommunikation + Ændringer + Betalinger + Login @@ -2636,7 +2766,7 @@ I dag - 💬 + SMS påmindelse sendt om aftale i morgen @@ -2650,7 +2780,7 @@ - 👤 + Kunde loggede ind via online booking @@ -2669,7 +2799,7 @@ 9. december 2025 - 💳 + Betaling modtaget1.799 kr (Dankort) @@ -2682,7 +2812,7 @@ - 📅 + Aftale gennemført — Klip + Farve hos Emma L. @@ -2695,7 +2825,7 @@ - 📅 + Check-in — Kunden er ankommet @@ -2708,7 +2838,7 @@ - ✏️ + Journal opdateret — Ny farveformel tilføjet @@ -2726,7 +2856,7 @@ 5. december 2025 - 📅 + Ny booking oprettet — 9. dec kl. 10:00, Klip + Farve @@ -2740,7 +2870,7 @@ - 👤 + Kunde loggede ind via online booking @@ -2759,7 +2889,7 @@ 28. november 2025 - ✏️ + Telefon ændret+45 12 34 56 78+45 23 45 67 89 @@ -2775,7 +2905,7 @@ - ✏️ + Tag tilføjetVIP @@ -2793,7 +2923,7 @@ 12. november 2025 - ⚠️ + Booking aflyst — Af kunden med 2 dages varsel @@ -2809,7 +2939,7 @@ - 💬 + Email sendt — Booking bekræftelse @@ -2828,7 +2958,7 @@ 15. marts 2024 - + Kunde oprettet @@ -3048,6 +3178,149 @@ // In a real app, this would filter the activity items }); }); + + // ========================================== + // CARD DRAG & DROP + // ========================================== + let draggedCard = null; + let dropIndicator = null; + + // Create drop indicator element + function createDropIndicator() { + if (!dropIndicator) { + dropIndicator = document.createElement('swp-drop-indicator'); + } + return dropIndicator; + } + + function removeDropIndicator() { + if (dropIndicator && dropIndicator.parentNode) { + dropIndicator.remove(); + } + } + + function clearDropIndicators() { + removeDropIndicator(); + document.querySelectorAll('.drag-over-empty').forEach(el => { + el.classList.remove('drag-over-empty'); + }); + } + + // Find the closest card and position based on mouse Y + function findDropPosition(column, mouseY) { + const cards = Array.from(column.querySelectorAll('swp-card[draggable="true"]:not(.dragging)')); + if (cards.length === 0) return { card: null, position: null }; + + for (let i = 0; i < cards.length; i++) { + const card = cards[i]; + const rect = card.getBoundingClientRect(); + const cardMiddle = rect.top + rect.height / 2; + + // If mouse is above the middle of this card, insert before it + if (mouseY < cardMiddle) { + return { card, position: 'before' }; + } + } + + // Mouse is below all cards, insert after the last one + return { card: cards[cards.length - 1], position: 'after' }; + } + + // Setup draggable cards + document.querySelectorAll('swp-card[draggable="true"]').forEach(card => { + card.addEventListener('dragstart', (e) => { + draggedCard = card; + card.classList.add('dragging'); + e.dataTransfer.effectAllowed = 'move'; + e.dataTransfer.setData('text/plain', card.dataset.cardId); + + setTimeout(() => { + card.style.opacity = '0.4'; + }, 0); + }); + + card.addEventListener('dragend', () => { + card.classList.remove('dragging'); + card.style.opacity = ''; + draggedCard = null; + clearDropIndicators(); + }); + }); + + // Handle all dragover at column level for seamless detection + document.querySelectorAll('swp-card-column').forEach(column => { + column.addEventListener('dragover', (e) => { + e.preventDefault(); + if (!draggedCard) return; + + e.dataTransfer.dropEffect = 'move'; + + const cards = column.querySelectorAll('swp-card[draggable="true"]:not(.dragging)'); + + if (cards.length === 0) { + // Empty column + column.classList.add('drag-over-empty'); + removeDropIndicator(); + return; + } + + column.classList.remove('drag-over-empty'); + + // Find where to show the indicator + const { card, position } = findDropPosition(column, e.clientY); + + if (card) { + const indicator = createDropIndicator(); + + if (position === 'before') { + if (indicator.nextElementSibling !== card) { + card.before(indicator); + } + } else { + if (indicator.previousElementSibling !== card) { + card.after(indicator); + } + } + } + }); + + column.addEventListener('dragleave', (e) => { + if (!column.contains(e.relatedTarget)) { + column.classList.remove('drag-over-empty'); + removeDropIndicator(); + } + }); + + column.addEventListener('drop', (e) => { + e.preventDefault(); + if (!draggedCard) return; + + // Insert dragged card where the indicator is, or at end of empty column + if (dropIndicator && dropIndicator.parentNode) { + dropIndicator.before(draggedCard); + } else if (column.classList.contains('drag-over-empty')) { + column.appendChild(draggedCard); + } + + clearDropIndicators(); + }); + }); + + // Also handle drop on the indicator itself + document.addEventListener('dragover', (e) => { + if (e.target.tagName === 'SWP-DROP-INDICATOR') { + e.preventDefault(); + e.dataTransfer.dropEffect = 'move'; + } + }); + + document.addEventListener('drop', (e) => { + if (e.target.tagName === 'SWP-DROP-INDICATOR' && draggedCard) { + e.preventDefault(); + dropIndicator.before(draggedCard); + clearDropIndicators(); + } + }); diff --git a/wwwroot/poc-service-detail.html b/wwwroot/poc-service-detail.html new file mode 100644 index 0000000..1dc3bf2 --- /dev/null +++ b/wwwroot/poc-service-detail.html @@ -0,0 +1,2244 @@ + + + + + + Service Detaljer - Klip & Farve + + + + + + + + + + + Tilbage til services + + Service detaljer + + + Duplikér + Gem ændringer + + + + + + + + Klip & Farve + + Populær + Kombi + Farve + + Tilføj tag + + + + Aktiv + + + + + 60-120 + min varighed + + + 795 kr + fra pris + + + 4 + medarbejdere + + + 156 + bookinger i år + + + + + + + + Generelt + Priser + Varighed + Medarbejdere + Tilvalg + Regler + + + + +
+
+ + Grundlæggende + + + Servicenavn + Klip & Farve + + + Kategori + + +
+ Kombi-behandlinger + Klip + Farve + Behandlinger + Styling +
+
+
+ + Farve i kalenderen + + +
+ Rød + Pink + Magenta + Lilla + Violet + Mørk lilla + Indigo + Blå + Lyseblå + Cyan + Teal + Grøn + Lysegrøn + Lime + Gul + Amber + Orange + Mørk orange +
+
+
+ + Service aktiv + + Ja + Nej + + +
+ + Interne noter + Komplet farvebehandling med klip. Husk konsultation ved første besøg. Anbefal Olaplex til kemisk behandlet hår. +
+ + + Bookingtype + +
+ Kan bookes som hovedservice + Vises i servicelisten og kan bookes selvstændigt +
+ + Ja + Nej + +
+ +
+ Kan bookes som tilvalg + Kan tilføjes som ekstra ydelse til andre services +
+ + Ja + Nej + +
+
+
+ +
+ + Online booking + +
+ Vis i online booking + Synlig for kunder i online booking +
+ + Ja + Nej + +
+ +
+ Fremhævet service + Vises øverst med fremhævet styling +
+ + Ja + Nej + +
+ + Beskrivelse + Forkæl dig selv med en komplet forvandling! Vores Klip & Farve behandling inkluderer professionel farverådgivning, farvning tilpasset din hudtone, præcisionsklip og styling. Perfekt til dig der ønsker et helt nyt look. + + Billede + + Upload billede +
+ +
+
+
+ + + + + Prisstruktur + + + Simpel pris + Matrix-pris + + + + + + + Pris + 995 kr + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NiveauKort hårMellem hårLangt hårEkstra langt
Junior795 kr895 kr995 kr1.095 kr
Senior895 kr995 kr1.095 kr1.195 kr
Master995 kr1.095 kr1.195 kr1.295 kr
+ + + Tilføj niveau eller hårlængde +
+
+ +
+ + Økonomi + + + Momssats + + + + + + Produktomkostning + 85 kr + + + Provision + + + + + + + + + Rabatter & Loyalitet + + Medlemsrabat (10%) + + Ja + Nej + + + + Kan betales med gavekort + + Ja + Nej + + + + Optjen loyalitetspoint + + Ja + Nej + + + +
+
+ + + +
+ + Varighedsvarianter + + + + Kort hår + + 60 + min + + + + + + Mellem hår + + 90 + min + + + + + + Langt hår + + 120 + min + + + + + + Ekstra langt hår + + 150 + min + + + + + + + Tilføj variant + + + + Buffer-tider + + + Buffer før aftale + + + + + + Buffer efter aftale + + + + + + Oprydningstid + + + + + + +
+
+ + + + + Medarbejdere der udfører denne service + + + + AS + + Anna Sørensen + Master Stylist + + + Pris: Standard + Varighed: Standard + + + Ja + Nej + + + + + MJ + + Mette Jensen + Senior Stylist + + + Pris: Standard + Varighed: +15 min + + + Ja + Nej + + + + + LN + + Louise Nielsen + Senior Stylist + + + Pris: Standard + Varighed: Standard + + + Ja + Nej + + + + + KP + + Katrine Pedersen + Stylist + + + Pris: Standard + Varighed: +15 min + + + Ja + Nej + + + + + SA + + Sofie Andersen⚠ Ikke certificeret + Junior Stylist + + + Pris: + Varighed: + + + Ja + Nej + + + + + Vælg alle / Fravælg alle + + + + + + + Tilvalg til denne service + + + + + + + + Olaplex Behandling + + +250 kr + +15 min + Valgfri + + + + + + + + + + Kerastase Hårkur + + +195 kr + +10 min + Valgfri + + + + + + + + + + Styling / Curls + + +150 kr + +20 min + Valgfri + + + + + + + + + + Hovedbundsmassage + + +75 kr + +5 min + Valgfri + + + + + + + + + + Patch Test + + Gratis + 48t før + Påkrævet (nye) + + + + + + + Tilføj eksisterende tilvalg + + + + + +
+ + Booking-regler + + + Minimum varsel + + + + + + Maks. forudbooking + + + + + + Afbestillingsfrist + + + + + + No-show gebyr + + + + + + + + + Tilgængelighed + + + Mandag + + + + + Ja + Nej + + + + Tirsdag + + + + + Ja + Nej + + + + Onsdag + + + + + Ja + Nej + + + + Torsdag + + + + + Ja + Nej + + + + Fredag + + + + + Ja + Nej + + + + Lørdag + + + + + Ja + Nej + + + + Søndag + + + + + Ja + Nej + + + + + + + Krav & Forberedelse + +
+ Konsultation påkrævet + Kunde skal have konsultation før første booking +
+ + Ja + Nej + +
+ +
+ Patch test påkrævet + Allergitest 48 timer før farvebehandling (nye kunder) +
+ + Ja + Nej + +
+ +
+ Aldersbegrænsning + Minimum alder for booking af denne service +
+ + Ja + Nej + +
+ +
+ Samtykke-formular + Kunde skal underskrive samtykke før behandling +
+ + Ja + Nej + +
+
+
+ + + Online booking indstillinger + + Vis i online booking + + Ja + Nej + + + + Tillad valg af medarbejder + + Ja + Nej + + + + Vis pris + + Ja + Nej + + + + Vis varighed + + Ja + Nej + + + + +
+ + + + +