From 9f46ff8824d357098aaad662029653cb9fbb3fa5 Mon Sep 17 00:00:00 2001 From: "Janus C. H. Knudsen" Date: Tue, 23 Dec 2025 09:32:49 +0100 Subject: [PATCH] Enhances UI with drag-and-drop card functionality Adds drag and drop interaction for rearranging cards on the customer and service detail pages Improves user experience by allowing flexible card positioning Implements dynamic drop indicators and column management Supports intuitive card movement between columns --- wwwroot/icons/booking.svg | 4 + wwwroot/icons/check-in-calendar.svg | 4 + wwwroot/icons/comment-sms.svg | 2 + wwwroot/icons/created.svg | 1 + wwwroot/icons/unlock.svg | 2 + wwwroot/poc-customer-detail.html | 343 +++- wwwroot/poc-service-detail.html | 2244 +++++++++++++++++++++++++++ 7 files changed, 2565 insertions(+), 35 deletions(-) create mode 100644 wwwroot/icons/booking.svg create mode 100644 wwwroot/icons/check-in-calendar.svg create mode 100644 wwwroot/icons/comment-sms.svg create mode 100644 wwwroot/icons/created.svg create mode 100644 wwwroot/icons/unlock.svg create mode 100644 wwwroot/poc-service-detail.html 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 + + + + +
+ + + + +