Compare commits
29 Commits
5a90d454ce
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 1dffeb6be7 | |||
| 1f1ed2e4f3 | |||
| bc9eaf6706 | |||
| 549265cd34 | |||
| 989b18527e | |||
| 0cda37808e | |||
| b3c4761ae5 | |||
| dd65467573 | |||
| eb45e6b294 | |||
| 6553fcf685 | |||
| c2cf6b03a7 | |||
| 310ad7bc7f | |||
| fbdbd35813 | |||
| d6b94376b0 | |||
| 7a604f1250 | |||
| 13fd8f81c9 | |||
| 16cc26632e | |||
| 8ac2c4ba22 | |||
| 65e5d90fee | |||
| 5f55687239 | |||
| e30ce9ac30 | |||
| 61c024dbda | |||
| 2f73fce6f2 | |||
| 76ee5e97bf | |||
| fd38542863 | |||
| cc60cf2903 | |||
| 13b0d976ac | |||
| 77eabac038 | |||
| 0919134f88 |
@@ -1,3 +1,4 @@
|
|||||||
.layout {
|
.layout {
|
||||||
font-family: var(--typography-Body-Regular-fontFamily);
|
font-family:
|
||||||
|
var(--Body-Paragraph-Font-family), var(--Body-Paragraph-Font-fallback);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -158,6 +158,11 @@ body.partner-sas {
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
border-width: 0;
|
border-width: 0;
|
||||||
}
|
}
|
||||||
|
/* When a select in the booking widget is open, react-aria sets overflow:hidden
|
||||||
|
which breaks sticky positioning. Override with clip which doesn't break sticky. */
|
||||||
|
body:has([data-booking-widget-open] [data-open]) {
|
||||||
|
overflow: clip !important;
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
:root {
|
:root {
|
||||||
|
|||||||
@@ -231,6 +231,12 @@
|
|||||||
"value": "Særlige ønsker (valgfrit)"
|
"value": "Særlige ønsker (valgfrit)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"ancillaries.label.quantity": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Antal"
|
||||||
|
}
|
||||||
|
],
|
||||||
"ancillaries.unableToDisplayBreakfastPrices": [
|
"ancillaries.unableToDisplayBreakfastPrices": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -249,6 +255,18 @@
|
|||||||
"value": "Jeg accepterer booking- og annulleringsbetingelserne"
|
"value": "Jeg accepterer booking- og annulleringsbetingelserne"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"booking.alert.extraBeds.text": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Ved booking for mere end 2 gæster vil der blive opkrævet et ekstra gebyr pr. person. Se link for detaljer."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"booking.alert.extraguests": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Ekstra gæst(er)"
|
||||||
|
}
|
||||||
|
],
|
||||||
"booking.approx": [
|
"booking.approx": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -953,6 +971,16 @@
|
|||||||
"value": "Tilføj kode"
|
"value": "Tilføj kode"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.bookingCode.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Læs mere om brug af "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "codeVoucher"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.bookingCode.remember": [
|
"bookingWidget.bookingCode.remember": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -1061,6 +1089,16 @@
|
|||||||
"value": "details"
|
"value": "details"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.reward.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Læs mere om booking med "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "reward"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.reward.rewardNight": [
|
"bookingWidget.reward.rewardNight": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -2510,6 +2548,44 @@
|
|||||||
"value": "Datoer"
|
"value": "Datoer"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.currentCount": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Nuværende "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": " "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "count"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.decrease": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Reducer "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.increase": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Øg "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
"destination.backToCities": [
|
"destination.backToCities": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -5783,7 +5859,7 @@
|
|||||||
"myPages.myStay.ancillaries.reachedMaxItemsStepperMessage": [
|
"myPages.myStay.ancillaries.reachedMaxItemsStepperMessage": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Maksimal antal nået for denne vare."
|
"value": "Maksimalt antal nået for denne vare."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"myPages.myStay.ancillaries.reachedMaxPointsMessage": [
|
"myPages.myStay.ancillaries.reachedMaxPointsMessage": [
|
||||||
@@ -7569,6 +7645,58 @@
|
|||||||
"value": "Samlede ophold"
|
"value": "Samlede ophold"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"price.numberOfEuroBonusPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB-point"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB-point"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfEuroBonusPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"price.numberOfScandicPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Point"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Point"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfScandicPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
"price.numberOfVouchers": [
|
"price.numberOfVouchers": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
@@ -8629,6 +8757,12 @@
|
|||||||
"value": "). Se tilgængelige priser nedenfor."
|
"value": "). Se tilgængelige priser nedenfor."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"selectRate.alert.reservationPolicies": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Se prisdetaljer"
|
||||||
|
}
|
||||||
|
],
|
||||||
"selectRate.availableRooms.all": [
|
"selectRate.availableRooms.all": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
|
|||||||
@@ -231,6 +231,12 @@
|
|||||||
"value": "Sonderwünsche (optional)"
|
"value": "Sonderwünsche (optional)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"ancillaries.label.quantity": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Menge"
|
||||||
|
}
|
||||||
|
],
|
||||||
"ancillaries.unableToDisplayBreakfastPrices": [
|
"ancillaries.unableToDisplayBreakfastPrices": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -249,6 +255,18 @@
|
|||||||
"value": "Ich akzeptiere die Buchungs- und Stornierungsbedingungen"
|
"value": "Ich akzeptiere die Buchungs- und Stornierungsbedingungen"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"booking.alert.extraBeds.text": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Bei Buchungen für mehr als 2 Gäste fällt eine zusätzliche Gebühr pro Person an. Siehe Link für Details."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"booking.alert.extraguests": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Zusätzliche Gäste"
|
||||||
|
}
|
||||||
|
],
|
||||||
"booking.approx": [
|
"booking.approx": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -949,6 +967,16 @@
|
|||||||
"value": "Code hinzufügen"
|
"value": "Code hinzufügen"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.bookingCode.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Mehr erfahren über die Verwendung von "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "codeVoucher"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.bookingCode.remember": [
|
"bookingWidget.bookingCode.remember": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -1057,6 +1085,20 @@
|
|||||||
"value": "details"
|
"value": "details"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.reward.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Mehr über Buchungen mit "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "reward"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "erfahren"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.reward.rewardNight": [
|
"bookingWidget.reward.rewardNight": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -2522,6 +2564,44 @@
|
|||||||
"value": "Daten"
|
"value": "Daten"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.currentCount": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Aktuell "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": " "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "count"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.decrease": [
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "verringern"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.increase": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Erhöhen "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
"destination.backToCities": [
|
"destination.backToCities": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -7570,6 +7650,58 @@
|
|||||||
"value": "Aufenthalte insgesamt"
|
"value": "Aufenthalte insgesamt"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"price.numberOfEuroBonusPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB-Punkt"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB-Punkte"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfEuroBonusPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"price.numberOfScandicPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Punkt"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Punkte"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfScandicPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
"price.numberOfVouchers": [
|
"price.numberOfVouchers": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
@@ -8634,6 +8766,12 @@
|
|||||||
"value": "). Verfügbare Übernachtungspreise sind unten zu sehen."
|
"value": "). Verfügbare Übernachtungspreise sind unten zu sehen."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"selectRate.alert.reservationPolicies": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Tarifdetails anzeigen"
|
||||||
|
}
|
||||||
|
],
|
||||||
"selectRate.availableRooms.all": [
|
"selectRate.availableRooms.all": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
|
|||||||
@@ -231,6 +231,12 @@
|
|||||||
"value": "Special requests (optional)"
|
"value": "Special requests (optional)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"ancillaries.label.quantity": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Quantity"
|
||||||
|
}
|
||||||
|
],
|
||||||
"ancillaries.unableToDisplayBreakfastPrices": [
|
"ancillaries.unableToDisplayBreakfastPrices": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -249,6 +255,18 @@
|
|||||||
"value": "I accept the booking and cancellation terms"
|
"value": "I accept the booking and cancellation terms"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"booking.alert.extraBeds.text": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "When booking for more than 2 guests, an additional fee will apply per person. See link for details."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"booking.alert.extraguests": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Extra guest(s)"
|
||||||
|
}
|
||||||
|
],
|
||||||
"booking.approx": [
|
"booking.approx": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -337,6 +355,12 @@
|
|||||||
"value": "Change or cancel"
|
"value": "Change or cancel"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"booking.changeTitle": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Change"
|
||||||
|
}
|
||||||
|
],
|
||||||
"booking.codeVoucher": [
|
"booking.codeVoucher": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -953,6 +977,16 @@
|
|||||||
"value": "Add code"
|
"value": "Add code"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.bookingCode.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Read more about using "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "codeVoucher"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.bookingCode.remember": [
|
"bookingWidget.bookingCode.remember": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -1061,6 +1095,16 @@
|
|||||||
"value": "details"
|
"value": "details"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.reward.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Read more about booking with "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "reward"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.reward.rewardNight": [
|
"bookingWidget.reward.rewardNight": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -2198,6 +2242,16 @@
|
|||||||
"value": " points"
|
"value": " points"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"common.pointsInLine": [
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "points"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": " points"
|
||||||
|
}
|
||||||
|
],
|
||||||
"common.pointsToSpend": [
|
"common.pointsToSpend": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -2510,6 +2564,44 @@
|
|||||||
"value": "Dates"
|
"value": "Dates"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.currentCount": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Current "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": " "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "count"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.decrease": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Decrease "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.increase": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Increase "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
"destination.backToCities": [
|
"destination.backToCities": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -3060,6 +3152,12 @@
|
|||||||
"value": "When you confirm the booking the room will be guaranteed for late arrival. If you fail to arrive without cancelling in advance or if you cancel after 18:00 local time, you will be charged for one reward night."
|
"value": "When you confirm the booking the room will be guaranteed for late arrival. If you fail to arrive without cancelling in advance or if you cancel after 18:00 local time, you will be charged for one reward night."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"enterDetails.confirmBooking.rewardNightGuaranteeInfo": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "When you complete the booking the room will be guaranteed for late arrival. The hotel will hold your booking, even if you arrive after 18:00. In case of a no-show, you will be charged for one reward night."
|
||||||
|
}
|
||||||
|
],
|
||||||
"enterDetails.details.description": [
|
"enterDetails.details.description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -3344,6 +3442,40 @@
|
|||||||
"value": "Select payment method"
|
"value": "Select payment method"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"enterDetails.paymentStep.flexBookingTermsAndConditions": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "To complete your booking, please accept the general "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Booking & Cancellation Terms"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": 8,
|
||||||
|
"value": "termsAndConditionsLink"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": ", and acknowledge that your data will be processed in accordance with Scandic's "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Privacy policy"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": 8,
|
||||||
|
"value": "privacyPolicyLink"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "."
|
||||||
|
}
|
||||||
|
],
|
||||||
"enterDetails.priceChangeDialog.acceptButton": [
|
"enterDetails.priceChangeDialog.acceptButton": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -5669,6 +5801,24 @@
|
|||||||
"value": "!"
|
"value": "!"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"myPages.l6progress.modal.title": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Level upgrade and membership year"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPages.l6progress.modal.youCanAlsoReach": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "You can also reach Best Friend, our highest membership level, by staying 100 nights with us within a membership year."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPages.l6progress.modal.yourLevelDuring": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Your level during the current and next period is based on the points you earn during this 12-month period."
|
||||||
|
}
|
||||||
|
],
|
||||||
"myPages.leftToLevelUp": [
|
"myPages.leftToLevelUp": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -6057,6 +6207,88 @@
|
|||||||
"value": "Your membership"
|
"value": "Your membership"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"myPoints.pointTransactions.extrasToBooking": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Extras to your booking"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.formerScandicHotel": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Former Scandic Hotel"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.noTransactions": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "No transactions available"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.pointShop": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Scandic Friends Point Shop"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.pointsActivity": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Point activity"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.pointsEarnedPriorMay2021": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Points earned prior to May 1, 2021"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.redGift": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Reward Gift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.rewardNight": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Reward Night"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.scandicFriendsMastercard": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Scandic Friends Mastercard"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.showMoreTransactions": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Show more transactions"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.signUpBonus": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Sign up bonus"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.stayAt": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Stay at "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "hotelName"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.tuiPoints": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "TUI Points"
|
||||||
|
}
|
||||||
|
],
|
||||||
"myStay.accessDenied.bookingNotFound": [
|
"myStay.accessDenied.bookingNotFound": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -7546,6 +7778,58 @@
|
|||||||
"value": "Total stays"
|
"value": "Total stays"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"price.numberOfEuroBonusPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB Point"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB Points"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfEuroBonusPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"price.numberOfScandicPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Point"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Points"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfScandicPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
"price.numberOfVouchers": [
|
"price.numberOfVouchers": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
@@ -8610,6 +8894,12 @@
|
|||||||
"value": "). See available rates below."
|
"value": "). See available rates below."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"selectRate.alert.reservationPolicies": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "See rate details"
|
||||||
|
}
|
||||||
|
],
|
||||||
"selectRate.availableRooms.all": [
|
"selectRate.availableRooms.all": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
|
|||||||
@@ -231,6 +231,12 @@
|
|||||||
"value": "Erityistoiveet (valinnainen)"
|
"value": "Erityistoiveet (valinnainen)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"ancillaries.label.quantity": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Määrä"
|
||||||
|
}
|
||||||
|
],
|
||||||
"ancillaries.unableToDisplayBreakfastPrices": [
|
"ancillaries.unableToDisplayBreakfastPrices": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -249,6 +255,18 @@
|
|||||||
"value": "Hyväksyn varaus- ja peruutusehdot"
|
"value": "Hyväksyn varaus- ja peruutusehdot"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"booking.alert.extraBeds.text": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Kun varaat yli 2 vieraalle, lisämaksu veloitetaan per henkilö. Katso lisätietoja linkistä."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"booking.alert.extraguests": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Ylimääräinen vieras/vieraat"
|
||||||
|
}
|
||||||
|
],
|
||||||
"booking.approx": [
|
"booking.approx": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -953,6 +971,20 @@
|
|||||||
"value": "Lisää koodi"
|
"value": "Lisää koodi"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.bookingCode.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Lue lisää "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "codeVoucher"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": ":n käytöstä"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.bookingCode.remember": [
|
"bookingWidget.bookingCode.remember": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -1061,6 +1093,20 @@
|
|||||||
"value": "details"
|
"value": "details"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.reward.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Lue lisää "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "reward"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": " varaamisesta"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.reward.rewardNight": [
|
"bookingWidget.reward.rewardNight": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -2514,6 +2560,44 @@
|
|||||||
"value": "Päivämäärät"
|
"value": "Päivämäärät"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.currentCount": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Nykyinen "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": " "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "count"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.decrease": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Vähennä "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.increase": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Lisää "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
"destination.backToCities": [
|
"destination.backToCities": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -7586,6 +7670,58 @@
|
|||||||
"value": "Majoitukset yhteensä"
|
"value": "Majoitukset yhteensä"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"price.numberOfEuroBonusPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB-piste"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB-pistettä"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfEuroBonusPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"price.numberOfScandicPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Piste"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "pistettä"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfScandicPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
"price.numberOfVouchers": [
|
"price.numberOfVouchers": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
@@ -8658,6 +8794,12 @@
|
|||||||
"value": "). Katso saatavilla olevat hinnat alla."
|
"value": "). Katso saatavilla olevat hinnat alla."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"selectRate.alert.reservationPolicies": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Katso hinnan tiedot"
|
||||||
|
}
|
||||||
|
],
|
||||||
"selectRate.availableRooms.all": [
|
"selectRate.availableRooms.all": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
|
|||||||
@@ -231,6 +231,12 @@
|
|||||||
"value": "Spesielle ønsker (valgfritt)"
|
"value": "Spesielle ønsker (valgfritt)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"ancillaries.label.quantity": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Antall"
|
||||||
|
}
|
||||||
|
],
|
||||||
"ancillaries.unableToDisplayBreakfastPrices": [
|
"ancillaries.unableToDisplayBreakfastPrices": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -249,6 +255,18 @@
|
|||||||
"value": "Jeg godtar bestillings- og avbestillingsvilkårene"
|
"value": "Jeg godtar bestillings- og avbestillingsvilkårene"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"booking.alert.extraBeds.text": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Ved bestilling for mer enn 2 gjester vil det påløpe et tilleggsgebyr per person. Se lenke for detaljer."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"booking.alert.extraguests": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Ekstra gjest(er)"
|
||||||
|
}
|
||||||
|
],
|
||||||
"booking.approx": [
|
"booking.approx": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -953,6 +971,16 @@
|
|||||||
"value": "Legg til kode"
|
"value": "Legg til kode"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.bookingCode.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Les mer om bruk av "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "codeVoucher"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.bookingCode.remember": [
|
"bookingWidget.bookingCode.remember": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -1061,6 +1089,16 @@
|
|||||||
"value": "details"
|
"value": "details"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.reward.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Les mer om bestilling med "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "reward"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.reward.rewardNight": [
|
"bookingWidget.reward.rewardNight": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -2510,6 +2548,44 @@
|
|||||||
"value": "Datoer"
|
"value": "Datoer"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.currentCount": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Gjeldende "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": " "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "count"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.decrease": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Reduser "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.increase": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Øk "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
"destination.backToCities": [
|
"destination.backToCities": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -7582,6 +7658,58 @@
|
|||||||
"value": "Totalt antall opphold"
|
"value": "Totalt antall opphold"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"price.numberOfEuroBonusPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB-poeng"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB-poeng"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfEuroBonusPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"price.numberOfScandicPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Poeng"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Poeng"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfScandicPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
"price.numberOfVouchers": [
|
"price.numberOfVouchers": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
@@ -8654,6 +8782,12 @@
|
|||||||
"value": " ). Se tilgjengelige priser nedenfor."
|
"value": " ). Se tilgjengelige priser nedenfor."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"selectRate.alert.reservationPolicies": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Se prisdetaljer"
|
||||||
|
}
|
||||||
|
],
|
||||||
"selectRate.availableRooms.all": [
|
"selectRate.availableRooms.all": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
|
|||||||
@@ -231,6 +231,12 @@
|
|||||||
"value": "Särskilda önskemål (valfritt)"
|
"value": "Särskilda önskemål (valfritt)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"ancillaries.label.quantity": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Antal"
|
||||||
|
}
|
||||||
|
],
|
||||||
"ancillaries.unableToDisplayBreakfastPrices": [
|
"ancillaries.unableToDisplayBreakfastPrices": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -249,6 +255,18 @@
|
|||||||
"value": "Jag accepterar boknings- och avbokningsvillkoren"
|
"value": "Jag accepterar boknings- och avbokningsvillkoren"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"booking.alert.extraBeds.text": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "När du bokar för fler än 2 gäster tillkommer en extra avgift per person. Se länk för detaljer."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"booking.alert.extraguests": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Extra gäst(er)"
|
||||||
|
}
|
||||||
|
],
|
||||||
"booking.approx": [
|
"booking.approx": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -953,6 +971,16 @@
|
|||||||
"value": "Lägg till kod"
|
"value": "Lägg till kod"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.bookingCode.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Läs mer om att använda "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "codeVoucher"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.bookingCode.remember": [
|
"bookingWidget.bookingCode.remember": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -1061,6 +1089,16 @@
|
|||||||
"value": "details"
|
"value": "details"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.reward.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Läs mer om bokning med "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "reward"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.reward.rewardNight": [
|
"bookingWidget.reward.rewardNight": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -2510,6 +2548,44 @@
|
|||||||
"value": "Datum"
|
"value": "Datum"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.currentCount": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Aktuell "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": " "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "count"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.decrease": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Minska "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.increase": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Öka "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
"destination.backToCities": [
|
"destination.backToCities": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -3845,7 +3921,7 @@
|
|||||||
"findMyBooking.findYourStay": [
|
"findMyBooking.findYourStay": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Hitta ditt hotell"
|
"value": "Hitta din bokning"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"findMyBooking.manageBooking": [
|
"findMyBooking.manageBooking": [
|
||||||
@@ -7566,6 +7642,58 @@
|
|||||||
"value": "Totalt antal vistelser"
|
"value": "Totalt antal vistelser"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"price.numberOfEuroBonusPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB-poäng"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB-poäng"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfEuroBonusPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"price.numberOfScandicPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Punkt"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Poäng"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfScandicPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
"price.numberOfVouchers": [
|
"price.numberOfVouchers": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
@@ -8626,6 +8754,12 @@
|
|||||||
"value": "). Se tillgängliga priser nedan."
|
"value": "). Se tillgängliga priser nedan."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"selectRate.alert.reservationPolicies": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Se bokningsvillkor"
|
||||||
|
}
|
||||||
|
],
|
||||||
"selectRate.availableRooms.all": [
|
"selectRate.availableRooms.all": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
|
|||||||
@@ -22,13 +22,13 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@formatjs/intl": "^3.1.6",
|
"@formatjs/intl": "^3.1.6",
|
||||||
"@netlify/plugin-nextjs": "^5.15.1",
|
"@netlify/plugin-nextjs": "^5.15.7",
|
||||||
"@scandic-hotels/booking-flow": "workspace:*",
|
"@scandic-hotels/booking-flow": "workspace:*",
|
||||||
"@scandic-hotels/design-system": "workspace:*",
|
"@scandic-hotels/design-system": "workspace:*",
|
||||||
"@scandic-hotels/tracking": "workspace:*",
|
"@scandic-hotels/tracking": "workspace:*",
|
||||||
"@scandic-hotels/trpc": "workspace:*",
|
"@scandic-hotels/trpc": "workspace:*",
|
||||||
"@sentry/nextjs": "^10.33.0",
|
"@sentry/nextjs": "^10.33.0",
|
||||||
"@swc/plugin-formatjs": "^3.2.2",
|
"@swc/plugin-formatjs": "^8.1.0",
|
||||||
"@t3-oss/env-nextjs": "^0.13.4",
|
"@t3-oss/env-nextjs": "^0.13.4",
|
||||||
"@tanstack/react-query": "^5.75.5",
|
"@tanstack/react-query": "^5.75.5",
|
||||||
"@tanstack/react-query-devtools": "^5.75.5",
|
"@tanstack/react-query-devtools": "^5.75.5",
|
||||||
@@ -36,11 +36,11 @@
|
|||||||
"@trpc/server": "^11.1.2",
|
"@trpc/server": "^11.1.2",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"iron-session": "^8.0.4",
|
"iron-session": "^8.0.4",
|
||||||
"next": "16.0.10",
|
"next": "16.1.6",
|
||||||
"next-auth": "5.0.0-beta.29",
|
"next-auth": "5.0.0-beta.29",
|
||||||
"react": "19.2.1",
|
"react": "19.2.4",
|
||||||
"react-aria-components": "1.8.0",
|
"react-aria-components": "1.8.0",
|
||||||
"react-dom": "19.2.1",
|
"react-dom": "19.2.4",
|
||||||
"react-intl": "^7.1.11",
|
"react-intl": "^7.1.11",
|
||||||
"server-only": "^0.0.1",
|
"server-only": "^0.0.1",
|
||||||
"usehooks-ts": "3.1.1",
|
"usehooks-ts": "3.1.1",
|
||||||
|
|||||||
@@ -52,3 +52,4 @@ DTMC_ENTRA_ID_CLIENT=""
|
|||||||
DTMC_ENTRA_ID_ISSUER=""
|
DTMC_ENTRA_ID_ISSUER=""
|
||||||
DTMC_ENTRA_ID_SECRET=""
|
DTMC_ENTRA_ID_SECRET=""
|
||||||
|
|
||||||
|
NEXT_PUBLIC_NEW_POINTCLAIMS="true"
|
||||||
|
|||||||
@@ -16,6 +16,18 @@ yarn workspace @scandic-hotels/design-system build
|
|||||||
yarn dev
|
yarn dev
|
||||||
```
|
```
|
||||||
|
|
||||||
|
To run only scandic web
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn dev:web
|
||||||
|
```
|
||||||
|
|
||||||
|
To run only partner sas
|
||||||
|
|
||||||
|
```bash
|
||||||
|
yarn dev:sas
|
||||||
|
```
|
||||||
|
|
||||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
||||||
|
|
||||||
### Caching
|
### Caching
|
||||||
|
|||||||
@@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
.layout {
|
.layout {
|
||||||
display: grid;
|
display: grid;
|
||||||
font-family: var(--typography-Body-Regular-fontFamily);
|
font-family:
|
||||||
|
var(--Body-Paragraph-Font-family), var(--Body-Paragraph-Font-fallback);
|
||||||
grid-template-rows: auto 1fr;
|
grid-template-rows: auto 1fr;
|
||||||
min-height: 100dvh;
|
min-height: 100dvh;
|
||||||
max-width: var(--max-width-page);
|
max-width: var(--max-width-page);
|
||||||
|
|||||||
@@ -1,12 +1,7 @@
|
|||||||
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
|
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
|
||||||
import {
|
import { getEurobonusMembership } from "@scandic-hotels/trpc/routers/user/helpers"
|
||||||
getEurobonusMembership,
|
|
||||||
scandicMembershipTypes,
|
|
||||||
} from "@scandic-hotels/trpc/routers/user/helpers"
|
|
||||||
|
|
||||||
import { env } from "@/env/server"
|
|
||||||
import {
|
import {
|
||||||
getBasicProfileSafely,
|
|
||||||
getProfileSafely,
|
getProfileSafely,
|
||||||
getProfilingConsent,
|
getProfilingConsent,
|
||||||
} from "@/lib/trpc/memoizedRequests"
|
} from "@/lib/trpc/memoizedRequests"
|
||||||
@@ -26,15 +21,7 @@ type MyPagesLayoutProps = React.PropsWithChildren<{
|
|||||||
breadcrumbs: React.ReactNode
|
breadcrumbs: React.ReactNode
|
||||||
}>
|
}>
|
||||||
|
|
||||||
export default async function MyPagesLayout(props: MyPagesLayoutProps) {
|
export default async function MyPagesLayout({
|
||||||
if (env.ENABLE_PROFILE_CONSENT) {
|
|
||||||
return <MyPagesLayoutWithConsent {...props} />
|
|
||||||
}
|
|
||||||
|
|
||||||
return <MyPagesLayoutBase {...props} />
|
|
||||||
}
|
|
||||||
|
|
||||||
async function MyPagesLayoutWithConsent({
|
|
||||||
breadcrumbs,
|
breadcrumbs,
|
||||||
children,
|
children,
|
||||||
}: MyPagesLayoutProps) {
|
}: MyPagesLayoutProps) {
|
||||||
@@ -84,25 +71,3 @@ async function MyPagesLayoutWithConsent({
|
|||||||
</ProfilingConsentAlertProvider>
|
</ProfilingConsentAlertProvider>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
async function MyPagesLayoutBase({
|
|
||||||
breadcrumbs,
|
|
||||||
children,
|
|
||||||
}: MyPagesLayoutProps) {
|
|
||||||
const profile = await getBasicProfileSafely()
|
|
||||||
const eurobonusMembership = profile?.loyalty?.memberships?.find(
|
|
||||||
(m) => m.membershipType === scandicMembershipTypes.SAS_EB
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className={styles.container}>
|
|
||||||
<div className={styles.layout}>
|
|
||||||
{breadcrumbs}
|
|
||||||
<div className={styles.content}>{children}</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{eurobonusMembership && <SASLevelUpgradeCheck />}
|
|
||||||
<Surprises />
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,24 +1,13 @@
|
|||||||
import { redirect } from "next/navigation"
|
|
||||||
|
|
||||||
import { profile } from "@scandic-hotels/common/constants/routes/myPages"
|
|
||||||
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
|
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
|
||||||
|
|
||||||
import { env } from "@/env/server"
|
|
||||||
import { getProfile } from "@/lib/trpc/memoizedRequests"
|
import { getProfile } from "@/lib/trpc/memoizedRequests"
|
||||||
import { serverClient } from "@/lib/trpc/server"
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
import { ProfilingConsent } from "@/components/Forms/ProfilingConsent"
|
import { ProfilingConsent } from "@/components/Forms/ProfilingConsent"
|
||||||
import { getLang } from "@/i18n/serverContext"
|
|
||||||
|
|
||||||
import styles from "./page.module.css"
|
import styles from "./page.module.css"
|
||||||
|
|
||||||
export default async function ProfilingConsentSlot() {
|
export default async function ProfilingConsentSlot() {
|
||||||
const lang = await getLang()
|
|
||||||
|
|
||||||
if (!env.ENABLE_PROFILE_CONSENT) {
|
|
||||||
redirect(profile[lang])
|
|
||||||
}
|
|
||||||
|
|
||||||
const caller = await serverClient()
|
const caller = await serverClient()
|
||||||
const accountPage = await caller.contentstack.accountPage.get()
|
const accountPage = await caller.contentstack.accountPage.get()
|
||||||
const user = await getProfile()
|
const user = await getProfile()
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
.layout {
|
.layout {
|
||||||
display: grid;
|
display: grid;
|
||||||
font-family: var(--typography-Body-Regular-fontFamily);
|
font-family:
|
||||||
|
var(--Body-Paragraph-Font-family), var(--Body-Paragraph-Font-fallback);
|
||||||
grid-template-rows: auto 1fr;
|
grid-template-rows: auto 1fr;
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
.layout {
|
.layout {
|
||||||
font-family: var(--typography-Body-Regular-fontFamily);
|
font-family:
|
||||||
|
var(--Body-Paragraph-Font-family), var(--Body-Paragraph-Font-fallback);
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-2
@@ -5,6 +5,7 @@ import { useTransition } from "react"
|
|||||||
import { FormProvider, useForm } from "react-hook-form"
|
import { FormProvider, useForm } from "react-hook-form"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import { sasPartnershipTermsAndConditions } from "@scandic-hotels/common/constants/routes/customerService"
|
||||||
import { profileEdit } from "@scandic-hotels/common/constants/routes/myPages"
|
import { profileEdit } from "@scandic-hotels/common/constants/routes/myPages"
|
||||||
import { Button } from "@scandic-hotels/design-system/Button"
|
import { Button } from "@scandic-hotels/design-system/Button"
|
||||||
import Checkbox from "@scandic-hotels/design-system/Form/Checkbox"
|
import Checkbox from "@scandic-hotels/design-system/Form/Checkbox"
|
||||||
@@ -13,8 +14,6 @@ import Image from "@scandic-hotels/design-system/Image"
|
|||||||
import Link from "@scandic-hotels/design-system/OldDSLink"
|
import Link from "@scandic-hotels/design-system/OldDSLink"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import { sasPartnershipTermsAndConditions } from "@/constants/webHrefs"
|
|
||||||
|
|
||||||
import styles from "./link-sas.module.css"
|
import styles from "./link-sas.module.css"
|
||||||
|
|
||||||
import type { LangParams } from "@/types/params"
|
import type { LangParams } from "@/types/params"
|
||||||
|
|||||||
+19
-23
@@ -1,9 +1,8 @@
|
|||||||
import { redirect } from "next/navigation"
|
import { redirect } from "next/navigation"
|
||||||
import { z } from "zod"
|
import { z } from "zod"
|
||||||
|
|
||||||
import Footnote from "@scandic-hotels/design-system/Footnote"
|
|
||||||
import Image from "@scandic-hotels/design-system/Image"
|
import Image from "@scandic-hotels/design-system/Image"
|
||||||
import Link from "@scandic-hotels/design-system/OldDSLink"
|
import { TextLink } from "@scandic-hotels/design-system/TextLink"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import { env } from "@/env/server"
|
import { env } from "@/env/server"
|
||||||
@@ -94,27 +93,24 @@ export default async function SASxScandicLoginPage(
|
|||||||
{intentDescriptions[parsedParams.intent]}
|
{intentDescriptions[parsedParams.intent]}
|
||||||
</p>
|
</p>
|
||||||
</Typography>
|
</Typography>
|
||||||
<Footnote textAlign="center">
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
{intl.formatMessage(
|
<p style={{ textAlign: "center" }}>
|
||||||
{
|
{intl.formatMessage(
|
||||||
id: "linkEuroBonusAccount.manualRedirectLinkMessage",
|
{
|
||||||
defaultMessage:
|
id: "linkEuroBonusAccount.manualRedirectLinkMessage",
|
||||||
"If you are not redirected automatically, please <loginLink>click here</loginLink>.",
|
defaultMessage:
|
||||||
},
|
"If you are not redirected automatically, please <loginLink>click here</loginLink>.",
|
||||||
{
|
},
|
||||||
loginLink: (str) => (
|
{
|
||||||
<Link
|
loginLink: (str) => (
|
||||||
href={loginLink}
|
<TextLink typography="Link/sm" href={loginLink}>
|
||||||
color="red"
|
{str}
|
||||||
size="tiny"
|
</TextLink>
|
||||||
textDecoration="underline"
|
),
|
||||||
>
|
}
|
||||||
{str}
|
)}
|
||||||
</Link>
|
</p>
|
||||||
),
|
</Typography>
|
||||||
}
|
|
||||||
)}
|
|
||||||
</Footnote>
|
|
||||||
</SASModal>
|
</SASModal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -42,7 +42,8 @@
|
|||||||
width: 34px;
|
width: 34px;
|
||||||
height: 0px;
|
height: 0px;
|
||||||
padding: var(--Space-x3) 0;
|
padding: var(--Space-x3) 0;
|
||||||
font-family: var(--typography-Body-Regular-fontFamily);
|
font-family:
|
||||||
|
var(--Body-Paragraph-Font-family), var(--Body-Paragraph-Font-fallback);
|
||||||
border: 1px solid var(--Base-Border-Normal);
|
border: 1px solid var(--Base-Border-Normal);
|
||||||
border-radius: var(--Corner-Radius-md);
|
border-radius: var(--Corner-Radius-md);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
.layout {
|
.layout {
|
||||||
background-color: var(--Background-Primary);
|
background-color: var(--Background-Primary);
|
||||||
font-family: var(--typography-Body-Regular-fontFamily);
|
font-family:
|
||||||
|
var(--Body-Paragraph-Font-family), var(--Body-Paragraph-Font-fallback);
|
||||||
min-height: 100dvh;
|
min-height: 100dvh;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -88,6 +88,12 @@ body:has([data-booking-widget-open="true"]) #kindly-chat-api {
|
|||||||
z-index: -1 !important;
|
z-index: -1 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* When a select in the booking widget is open, react-aria sets overflow:hidden
|
||||||
|
which breaks sticky positioning. Override with clip which doesn't break sticky. */
|
||||||
|
body:has([data-booking-widget-open] [data-open]) {
|
||||||
|
overflow: clip !important;
|
||||||
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
:root {
|
:root {
|
||||||
--max-width-single-spacing: var(--Layout-Tablet-Margin-Margin-min);
|
--max-width-single-spacing: var(--Layout-Tablet-Margin-Margin-min);
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
.layout {
|
.layout {
|
||||||
font-family: var(--typography-Body-Regular-fontFamily);
|
font-family:
|
||||||
|
var(--Body-Paragraph-Font-family), var(--Body-Paragraph-Font-fallback);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,29 @@
|
|||||||
import { Alert } from "@scandic-hotels/design-system/Alert"
|
import { Alert } from "@scandic-hotels/design-system/Alert"
|
||||||
|
import { getAlertPhoneContactData } from "@scandic-hotels/trpc/routers/contentstack/base/utils"
|
||||||
|
|
||||||
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
import type { AlertBlock } from "@scandic-hotels/trpc/types/blocks"
|
import type { AlertBlock } from "@scandic-hotels/trpc/types/blocks"
|
||||||
|
|
||||||
interface AlertBlockProps extends Pick<AlertBlock, "alert"> {}
|
interface AlertBlockProps extends Pick<AlertBlock, "alert"> {}
|
||||||
|
|
||||||
export function AlertBlock({ alert }: AlertBlockProps) {
|
export async function AlertBlock({ alert }: AlertBlockProps) {
|
||||||
|
const caller = await serverClient()
|
||||||
|
const contactConfig = await caller.contentstack.base.contact()
|
||||||
if (!alert) {
|
if (!alert) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return <Alert {...alert} />
|
const phoneContact =
|
||||||
|
alert.phoneContact && contactConfig
|
||||||
|
? getAlertPhoneContactData(alert, contactConfig)
|
||||||
|
: null
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Alert
|
||||||
|
{...alert}
|
||||||
|
phoneContact={phoneContact}
|
||||||
|
sidepeekCtaText={alert.sidepeekButton?.cta_text}
|
||||||
|
/>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
-4
@@ -16,18 +16,14 @@
|
|||||||
|
|
||||||
.iconTh {
|
.iconTh {
|
||||||
padding: var(--Space-x5) var(--Space-x2) var(--Space-x2);
|
padding: var(--Space-x5) var(--Space-x2) var(--Space-x2);
|
||||||
font-weight: var(--typography-Caption-Regular-fontWeight);
|
|
||||||
vertical-align: bottom;
|
vertical-align: bottom;
|
||||||
}
|
}
|
||||||
|
|
||||||
.summaryTh {
|
.summaryTh {
|
||||||
font-size: var(--typography-Caption-Regular-fontSize);
|
|
||||||
font-weight: var(--typography-Caption-Regular-fontWeight);
|
|
||||||
padding: 0 var(--Space-x2) var(--Space-x2);
|
padding: 0 var(--Space-x2) var(--Space-x2);
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
}
|
}
|
||||||
|
|
||||||
.select {
|
.select {
|
||||||
font-weight: var(--typography-Caption-Regular-fontWeight);
|
|
||||||
padding: 0 var(--Space-x2) var(--Space-x2);
|
padding: 0 var(--Space-x2) var(--Space-x2);
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-5
@@ -1,3 +1,5 @@
|
|||||||
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import MembershipLevelIcon from "@/components/Levels/Icon"
|
import MembershipLevelIcon from "@/components/Levels/Icon"
|
||||||
|
|
||||||
import LevelSummary from "../../LevelSummary"
|
import LevelSummary from "../../LevelSummary"
|
||||||
@@ -37,12 +39,14 @@ export default function DesktopHeader({
|
|||||||
<th />
|
<th />
|
||||||
{levels.map((level, idx) => {
|
{levels.map((level, idx) => {
|
||||||
return (
|
return (
|
||||||
<th
|
<Typography
|
||||||
key={"summary" + level.level_id + idx}
|
variant="Body/Supporting text (caption)/smRegular"
|
||||||
className={styles.summaryTh}
|
key={"name" + level.level_id + idx}
|
||||||
>
|
>
|
||||||
<LevelSummary level={level} />
|
<th className={styles.summaryTh}>
|
||||||
</th>
|
<LevelSummary level={level} />
|
||||||
|
</th>
|
||||||
|
</Typography>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
+6
-4
@@ -82,10 +82,12 @@ function RewardTableHeader({ name, description }: RewardTableHeaderProps) {
|
|||||||
</span>
|
</span>
|
||||||
</hgroup>
|
</hgroup>
|
||||||
</summary>
|
</summary>
|
||||||
<p
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
className={styles.rewardDescription}
|
<p
|
||||||
dangerouslySetInnerHTML={{ __html: description }}
|
className={styles.rewardDescription}
|
||||||
/>
|
dangerouslySetInnerHTML={{ __html: description }}
|
||||||
|
/>
|
||||||
|
</Typography>
|
||||||
</details>
|
</details>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
-3
@@ -15,14 +15,11 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.td {
|
.td {
|
||||||
font-size: var(--typography-Footnote-Regular-fontSize);
|
|
||||||
text-align: center;
|
text-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rewardTh {
|
.rewardTh {
|
||||||
padding: var(--Space-x3) var(--Space-x2);
|
padding: var(--Space-x3) var(--Space-x2);
|
||||||
font-size: var(--typography-Caption-Regular-fontSize);
|
|
||||||
font-weight: var(--typography-Caption-Regular-fontWeight);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.details[open] .chevron {
|
.details[open] .chevron {
|
||||||
|
|||||||
+5
-1
@@ -1,5 +1,7 @@
|
|||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import styles from "./levelSummary.module.css"
|
import styles from "./levelSummary.module.css"
|
||||||
|
|
||||||
import type { LevelSummaryProps } from "@/types/components/overviewTable"
|
import type { LevelSummaryProps } from "@/types/components/overviewTable"
|
||||||
@@ -32,7 +34,9 @@ export default function LevelSummary({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.levelSummary}>
|
<div className={styles.levelSummary}>
|
||||||
<span className={styles.levelRequirements}>{pointsMsg}</span>
|
<Typography variant="Label/xsRegular">
|
||||||
|
<span className={styles.levelRequirements}>{pointsMsg}</span>
|
||||||
|
</Typography>
|
||||||
{showDescription && (
|
{showDescription && (
|
||||||
<p className={styles.levelSummaryText}>{level.description}</p>
|
<p className={styles.levelSummaryText}>{level.description}</p>
|
||||||
)}
|
)}
|
||||||
|
|||||||
+2
-13
@@ -8,16 +8,14 @@
|
|||||||
|
|
||||||
.levelRequirements {
|
.levelRequirements {
|
||||||
border-radius: var(--Corner-Radius-md);
|
border-radius: var(--Corner-Radius-md);
|
||||||
background-color: var(--Scandic-Brand-Pale-Peach);
|
background-color: var(--Surface-Brand-Primary-1-Default);
|
||||||
color: var(--Scandic-Peach-80);
|
color: var(--Text-Interactive-Secondary);
|
||||||
padding: var(--Space-x05) var(--Space-x1);
|
padding: var(--Space-x05) var(--Space-x1);
|
||||||
text-align: center;
|
text-align: center;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.levelSummaryText {
|
.levelSummaryText {
|
||||||
font-size: var(--typography-Caption-Regular-fontSize);
|
|
||||||
line-height: var(--typography-Body-Regular-lineHeight);
|
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -26,12 +24,3 @@
|
|||||||
padding: var(--Space-x05) var(--Space-x1);
|
padding: var(--Space-x05) var(--Space-x1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@media screen and (min-width: 1367px) {
|
|
||||||
.levelRequirements {
|
|
||||||
font-size: var(--typography-Footnote-Regular-fontSize);
|
|
||||||
}
|
|
||||||
|
|
||||||
.levelSummaryText {
|
|
||||||
font-size: var(--typography-Caption-Regular-fontSize);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
+6
-4
@@ -27,10 +27,12 @@ export default function RewardCard({
|
|||||||
</span>
|
</span>
|
||||||
</hgroup>
|
</hgroup>
|
||||||
</summary>
|
</summary>
|
||||||
<p
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
className={styles.rewardCardDescription}
|
<p
|
||||||
dangerouslySetInnerHTML={{ __html: description }}
|
className={styles.rewardCardDescription}
|
||||||
/>
|
dangerouslySetInnerHTML={{ __html: description }}
|
||||||
|
/>
|
||||||
|
</Typography>
|
||||||
</details>
|
</details>
|
||||||
</div>
|
</div>
|
||||||
<div className={styles.rewardComparison}>
|
<div className={styles.rewardComparison}>
|
||||||
|
|||||||
-2
@@ -12,8 +12,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.rewardCardDescription {
|
.rewardCardDescription {
|
||||||
font-size: var(--typography-Caption-Regular-fontSize);
|
|
||||||
line-height: 150%;
|
|
||||||
padding-right: var(--Space-x4);
|
padding-right: var(--Space-x4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-3
@@ -1,6 +1,7 @@
|
|||||||
import { Minus } from "react-feather"
|
import { Minus } from "react-feather"
|
||||||
|
|
||||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||||
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import styles from "./rewardValue.module.css"
|
import styles from "./rewardValue.module.css"
|
||||||
|
|
||||||
@@ -21,8 +22,8 @@ export default function RewardValue({ reward }: RewardValueProps) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className={styles.rewardValueContainer}>
|
<Typography variant="Body/Paragraph/mdBold">
|
||||||
<span className={styles.rewardValue}>{reward.value}</span>
|
<div className={styles.rewardValueContainer}>{reward.value}</div>
|
||||||
</div>
|
</Typography>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
-11
@@ -7,17 +7,6 @@
|
|||||||
text-wrap: balance;
|
text-wrap: balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
.rewardValue {
|
|
||||||
font-size: var(--typography-Body-Bold-fontSize);
|
|
||||||
font-weight: var(--typography-Body-Bold-fontWeight);
|
|
||||||
}
|
|
||||||
|
|
||||||
.rewardValueDetails {
|
|
||||||
font-size: var(--typography-Footnote-Regular-fontSize);
|
|
||||||
text-align: center;
|
|
||||||
color: var(--UI-Grey-80);
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkIcon {
|
.checkIcon {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
}
|
}
|
||||||
|
|||||||
+430
@@ -0,0 +1,430 @@
|
|||||||
|
/* eslint-disable formatjs/no-literal-string-in-jsx */
|
||||||
|
/* TODO remove disable and add i18n */
|
||||||
|
/* TODO add analytics */
|
||||||
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
|
import { cx } from "class-variance-authority"
|
||||||
|
import { useState } from "react"
|
||||||
|
import { FormProvider, useForm, useWatch } from "react-hook-form"
|
||||||
|
import { useIntl } from "react-intl"
|
||||||
|
import z from "zod"
|
||||||
|
|
||||||
|
import { dt } from "@scandic-hotels/common/dt"
|
||||||
|
import { Button } from "@scandic-hotels/design-system/Button"
|
||||||
|
import { FormInput } from "@scandic-hotels/design-system/Form/FormInput"
|
||||||
|
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||||
|
import { LoadingSpinner } from "@scandic-hotels/design-system/LoadingSpinner"
|
||||||
|
import { MessageBanner } from "@scandic-hotels/design-system/MessageBanner"
|
||||||
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
import { trpc } from "@scandic-hotels/trpc/client"
|
||||||
|
|
||||||
|
import useLang from "@/hooks/useLang"
|
||||||
|
|
||||||
|
import styles from "./claimPoints.module.css"
|
||||||
|
|
||||||
|
type PointClaimBookingInfo = {
|
||||||
|
from: string
|
||||||
|
to: string
|
||||||
|
city: string
|
||||||
|
hotel: string
|
||||||
|
}
|
||||||
|
export function ClaimPointsWizard({
|
||||||
|
onSuccess,
|
||||||
|
onClose,
|
||||||
|
}: {
|
||||||
|
onSuccess: () => void
|
||||||
|
onClose: () => void
|
||||||
|
}) {
|
||||||
|
const [state, setState] = useState<
|
||||||
|
"initial" | "loading" | "invalid" | "form"
|
||||||
|
>("initial")
|
||||||
|
const [bookingDetails, setBookingDetails] =
|
||||||
|
useState<PointClaimBookingInfo | null>(null)
|
||||||
|
|
||||||
|
const { data, isLoading } = trpc.user.getSafely.useQuery()
|
||||||
|
|
||||||
|
if (state === "invalid") {
|
||||||
|
return <InvalidBooking onClose={onClose} />
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state === "form") {
|
||||||
|
if (isLoading) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ClaimPointsForm
|
||||||
|
onSuccess={onSuccess}
|
||||||
|
initialData={{
|
||||||
|
...bookingDetails,
|
||||||
|
firstName: data?.firstName ?? "",
|
||||||
|
lastName: data?.lastName ?? "",
|
||||||
|
email: data?.email ?? "",
|
||||||
|
phone: data?.phoneNumber ?? "",
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleBookingNumberEvent = (event: BookingNumberEvent) => {
|
||||||
|
switch (event.type) {
|
||||||
|
case "submit":
|
||||||
|
setState("loading")
|
||||||
|
break
|
||||||
|
case "error":
|
||||||
|
setState("initial")
|
||||||
|
break
|
||||||
|
case "invalid":
|
||||||
|
setState("invalid")
|
||||||
|
break
|
||||||
|
case "success":
|
||||||
|
setBookingDetails(event.data)
|
||||||
|
setState("form")
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.introWrapper}>
|
||||||
|
{state === "loading" && (
|
||||||
|
<div
|
||||||
|
className={styles.spinner}
|
||||||
|
aria-live="polite"
|
||||||
|
aria-label="Loading booking details, please wait.."
|
||||||
|
>
|
||||||
|
<LoadingSpinner />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
<div
|
||||||
|
className={cx(styles.options, { [styles.hidden]: state === "loading" })}
|
||||||
|
>
|
||||||
|
<section className={styles.sectionCard}>
|
||||||
|
<div className={styles.sectionInfo}>
|
||||||
|
<Typography variant="Body/Paragraph/mdBold">
|
||||||
|
<h4>Claim points with booking number</h4>
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
|
<p>
|
||||||
|
Enter a valid booking number to load booking details
|
||||||
|
automatically.
|
||||||
|
</p>
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<BookingNumberInput onEvent={handleBookingNumberEvent} />
|
||||||
|
</section>
|
||||||
|
<Divider />
|
||||||
|
<section className={styles.sectionCard}>
|
||||||
|
<div className={styles.sectionInfo}>
|
||||||
|
<Typography variant="Body/Paragraph/mdBold">
|
||||||
|
<h4>Claim points without booking number</h4>
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
|
<p>You need to add booking details in a form.</p>
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
<Button variant="Secondary" onPress={() => setState("form")}>
|
||||||
|
Fill form to claim points
|
||||||
|
</Button>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
<MessageBanner
|
||||||
|
type="info"
|
||||||
|
text="Points can be claimed up to 6 months back if you were a member at the time of your stay."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type BookingNumberFormData = {
|
||||||
|
bookingNumber: string
|
||||||
|
}
|
||||||
|
type BookingNumberEvent =
|
||||||
|
| { type: "submit" }
|
||||||
|
| { type: "success"; data: PointClaimBookingInfo }
|
||||||
|
| { type: "error" }
|
||||||
|
| { type: "invalid" }
|
||||||
|
function BookingNumberInput({
|
||||||
|
onEvent,
|
||||||
|
}: {
|
||||||
|
onEvent: (event: BookingNumberEvent) => void
|
||||||
|
}) {
|
||||||
|
const lang = useLang()
|
||||||
|
const form = useForm<BookingNumberFormData>({
|
||||||
|
resolver: zodResolver(
|
||||||
|
z.object({
|
||||||
|
bookingNumber: z
|
||||||
|
.string()
|
||||||
|
// TODO Check UX for validation as different environments have different lengths
|
||||||
|
.min(9, { message: "Booking number must be 10 digits" })
|
||||||
|
.max(10, { message: "Booking number must be 10 digits" }),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
defaultValues: {
|
||||||
|
bookingNumber: "",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
const confirmationNumber = useWatch({
|
||||||
|
name: "bookingNumber",
|
||||||
|
control: form.control,
|
||||||
|
})
|
||||||
|
|
||||||
|
const { refetch, isFetching } =
|
||||||
|
trpc.booking.findBookingForCurrentUser.useQuery(
|
||||||
|
{
|
||||||
|
confirmationNumber,
|
||||||
|
lang,
|
||||||
|
},
|
||||||
|
{ enabled: false }
|
||||||
|
)
|
||||||
|
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
onEvent({ type: "submit" })
|
||||||
|
const result = await refetch()
|
||||||
|
if (!result.data) {
|
||||||
|
onEvent({ type: "error" })
|
||||||
|
form.setError("bookingNumber", {
|
||||||
|
type: "manual",
|
||||||
|
message:
|
||||||
|
"We could not find a booking with this number registered in your name.",
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = result.data
|
||||||
|
|
||||||
|
// TODO validate if this should be check out or check in date
|
||||||
|
const checkOutDate = dt(data.booking.checkOutDate)
|
||||||
|
const sixMonthsAgo = dt().subtract(6, "months")
|
||||||
|
if (checkOutDate.isBefore(sixMonthsAgo, "day")) {
|
||||||
|
onEvent({ type: "invalid" })
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
onEvent({
|
||||||
|
type: "success",
|
||||||
|
data: {
|
||||||
|
from: data.booking.checkInDate,
|
||||||
|
to: data.booking.checkOutDate,
|
||||||
|
city: data.hotel.cityName,
|
||||||
|
hotel: data.hotel.name,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormProvider {...form}>
|
||||||
|
<form onSubmit={form.handleSubmit(handleSubmit)}>
|
||||||
|
<FormInput
|
||||||
|
name="bookingNumber"
|
||||||
|
label="Booking number"
|
||||||
|
leftIcon={<MaterialIcon icon="edit_document" />}
|
||||||
|
description="Enter your 10-digit booking number"
|
||||||
|
maxLength={10}
|
||||||
|
showClearContentIcon
|
||||||
|
disabled={isFetching}
|
||||||
|
autoFocus
|
||||||
|
autoComplete="off"
|
||||||
|
onChange={(e) => {
|
||||||
|
const value = e.target.value
|
||||||
|
if (value.length !== 10) return
|
||||||
|
|
||||||
|
form.handleSubmit(handleSubmit)()
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</form>
|
||||||
|
</FormProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function InvalidBooking({ onClose }: { onClose: () => void }) {
|
||||||
|
return (
|
||||||
|
<div className={styles.invalidWrapper}>
|
||||||
|
<Typography variant="Body/Paragraph/mdRegular">
|
||||||
|
<p>
|
||||||
|
We can’t add these points to your account as it has been longer than 6
|
||||||
|
months since your stay.
|
||||||
|
</p>
|
||||||
|
</Typography>
|
||||||
|
<Button variant="Primary" fullWidth onPress={onClose}>
|
||||||
|
Close
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
type PointClaimUserInfo = {
|
||||||
|
firstName: string
|
||||||
|
lastName: string
|
||||||
|
email: string
|
||||||
|
phone: string
|
||||||
|
}
|
||||||
|
function ClaimPointsForm({
|
||||||
|
onSuccess,
|
||||||
|
initialData,
|
||||||
|
}: {
|
||||||
|
onSuccess: () => void
|
||||||
|
initialData: Partial<PointClaimBookingInfo & PointClaimUserInfo> | null
|
||||||
|
}) {
|
||||||
|
const form = useForm({
|
||||||
|
resolver: zodResolver(
|
||||||
|
z.object({
|
||||||
|
from: z.string().min(1, { message: "Arrival date is required" }),
|
||||||
|
to: z.string().min(1, { message: "Departure date is required" }),
|
||||||
|
city: z.string().min(1, { message: "City is required" }),
|
||||||
|
hotel: z.string().min(1, { message: "Hotel is required" }),
|
||||||
|
firstName: z.string().min(1, { message: "First name is required" }),
|
||||||
|
lastName: z.string().min(1, { message: "Last name is required" }),
|
||||||
|
email: z
|
||||||
|
.string()
|
||||||
|
.email("Enter a valid email")
|
||||||
|
.min(1, { message: "Email is required" }),
|
||||||
|
phone: z.string().min(1, { message: "Phone is required" }),
|
||||||
|
})
|
||||||
|
),
|
||||||
|
defaultValues: {
|
||||||
|
from: initialData?.from || "",
|
||||||
|
to: initialData?.to || "",
|
||||||
|
city: initialData?.city || "",
|
||||||
|
hotel: initialData?.hotel || "",
|
||||||
|
firstName: initialData?.firstName || "",
|
||||||
|
lastName: initialData?.lastName || "",
|
||||||
|
email: initialData?.email || "",
|
||||||
|
phone: initialData?.phone || "",
|
||||||
|
},
|
||||||
|
mode: "all",
|
||||||
|
})
|
||||||
|
|
||||||
|
const { mutate, isPending } = trpc.user.claimPoints.useMutation({
|
||||||
|
onSuccess,
|
||||||
|
})
|
||||||
|
|
||||||
|
const autoFocusField = getAutoFocus(initialData)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormProvider {...form}>
|
||||||
|
<form
|
||||||
|
className={styles.form}
|
||||||
|
onSubmit={form.handleSubmit((data) => mutate(data))}
|
||||||
|
>
|
||||||
|
<div className={styles.formInputs}>
|
||||||
|
{!initialData?.firstName && (
|
||||||
|
<FormInput
|
||||||
|
name="firstName"
|
||||||
|
label="First name"
|
||||||
|
autoFocus={autoFocusField === "firstName"}
|
||||||
|
readOnly={isPending}
|
||||||
|
registerOptions={{ required: true }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{!initialData?.lastName && (
|
||||||
|
<FormInput
|
||||||
|
name="lastName"
|
||||||
|
label="Last name"
|
||||||
|
autoFocus={autoFocusField === "lastName"}
|
||||||
|
readOnly={isPending}
|
||||||
|
registerOptions={{ required: true }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{!initialData?.email && (
|
||||||
|
<FormInput
|
||||||
|
name="email"
|
||||||
|
label="Email"
|
||||||
|
type="email"
|
||||||
|
autoFocus={autoFocusField === "email"}
|
||||||
|
readOnly={isPending}
|
||||||
|
registerOptions={{ required: true }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{!initialData?.phone && (
|
||||||
|
<FormInput
|
||||||
|
name="phone"
|
||||||
|
label="Phone"
|
||||||
|
type="tel"
|
||||||
|
autoFocus={autoFocusField === "phone"}
|
||||||
|
readOnly={isPending}
|
||||||
|
registerOptions={{ required: true }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<FormInput
|
||||||
|
name="from"
|
||||||
|
label="Arrival (YYYY-MM-DD)"
|
||||||
|
leftIcon={<MaterialIcon icon="calendar_today" />}
|
||||||
|
autoFocus={autoFocusField === "from"}
|
||||||
|
readOnly={isPending}
|
||||||
|
registerOptions={{ required: true }}
|
||||||
|
/>
|
||||||
|
<FormInput
|
||||||
|
name="to"
|
||||||
|
label="Departure (YYYY-MM-DD)"
|
||||||
|
leftIcon={<MaterialIcon icon="calendar_today" />}
|
||||||
|
readOnly={isPending}
|
||||||
|
registerOptions={{ required: true }}
|
||||||
|
/>
|
||||||
|
<FormInput
|
||||||
|
name="city"
|
||||||
|
label="City"
|
||||||
|
readOnly={isPending}
|
||||||
|
registerOptions={{ required: true }}
|
||||||
|
/>
|
||||||
|
<FormInput
|
||||||
|
name="hotel"
|
||||||
|
label="Hotel"
|
||||||
|
readOnly={isPending}
|
||||||
|
registerOptions={{ required: true }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<MessageBanner
|
||||||
|
type="info"
|
||||||
|
text="Points can be claimed up to 6 months back if you were a member at the time of your stay."
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
variant="Primary"
|
||||||
|
fullWidth
|
||||||
|
isDisabled={!form.formState.isValid}
|
||||||
|
isPending={isPending}
|
||||||
|
className={styles.formSubmit}
|
||||||
|
>
|
||||||
|
Send points claim
|
||||||
|
</Button>
|
||||||
|
</form>
|
||||||
|
</FormProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function getAutoFocus(userInfo: Partial<PointClaimUserInfo> | null) {
|
||||||
|
if (!userInfo?.firstName) {
|
||||||
|
return "firstName"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userInfo?.lastName) {
|
||||||
|
return "lastName"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userInfo?.email) {
|
||||||
|
return "email"
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!userInfo?.phone) {
|
||||||
|
return "phone"
|
||||||
|
}
|
||||||
|
|
||||||
|
return "from"
|
||||||
|
}
|
||||||
|
|
||||||
|
function Divider() {
|
||||||
|
const intl = useIntl()
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.divider}>
|
||||||
|
<Typography variant="Body/Paragraph/mdRegular">
|
||||||
|
<span>
|
||||||
|
{intl.formatMessage({
|
||||||
|
id: "common.or",
|
||||||
|
defaultMessage: "or",
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
+97
@@ -6,3 +6,100 @@
|
|||||||
gap: var(--Space-x2);
|
gap: var(--Space-x2);
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.dialog {
|
||||||
|
max-width: 560px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.introWrapper {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--Space-x3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.options {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--Space-x1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionCard {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--Space-x3);
|
||||||
|
padding: var(--Space-x2);
|
||||||
|
background-color: var(--Surface-Primary-OnSurface-Default);
|
||||||
|
border-radius: var(--Corner-Radius-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sectionInfo {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--Space-x1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||||
|
position: absolute;
|
||||||
|
inset: 0;
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bookingInputDescription {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--Space-x05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--Space-x3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.formInputs {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--Space-x2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.formSubmit {
|
||||||
|
margin-top: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.divider {
|
||||||
|
width: 100%;
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
& > span {
|
||||||
|
position: relative;
|
||||||
|
padding: 0 var(--Space-x2);
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
bottom: calc(50% - 1px);
|
||||||
|
content: "";
|
||||||
|
display: block;
|
||||||
|
height: 1px;
|
||||||
|
width: 100%;
|
||||||
|
background-color: var(--Border-Default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invalidWrapper {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--Space-x3);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,18 +1,110 @@
|
|||||||
|
/* eslint-disable formatjs/no-literal-string-in-jsx */
|
||||||
|
/* TODO remove disable and add i18n */
|
||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react"
|
||||||
|
import { Dialog } from "react-aria-components"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import { Button } from "@scandic-hotels/design-system/Button"
|
||||||
import ButtonLink from "@scandic-hotels/design-system/ButtonLink"
|
import ButtonLink from "@scandic-hotels/design-system/ButtonLink"
|
||||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||||
|
import Modal from "@scandic-hotels/design-system/Modal"
|
||||||
|
import { toast } from "@scandic-hotels/design-system/Toast"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import { missingPoints } from "@/constants/missingPointsHrefs"
|
import { missingPoints } from "@/constants/missingPointsHrefs"
|
||||||
|
import { env } from "@/env/client"
|
||||||
|
|
||||||
import useLang from "@/hooks/useLang"
|
import useLang from "@/hooks/useLang"
|
||||||
|
|
||||||
|
import { ClaimPointsWizard } from "./ClaimPointsWizard"
|
||||||
|
|
||||||
import styles from "./claimPoints.module.css"
|
import styles from "./claimPoints.module.css"
|
||||||
|
|
||||||
export default function ClaimPoints() {
|
export default function ClaimPoints() {
|
||||||
|
const intl = useIntl()
|
||||||
|
const [openModal, setOpenModal] = useLinkableModalState("claim-points")
|
||||||
|
|
||||||
|
const useNewFlow = env.NEXT_PUBLIC_NEW_POINTCLAIMS
|
||||||
|
if (!useNewFlow) {
|
||||||
|
return <OldClaimPointsLink />
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<div className={styles.claim}>
|
||||||
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
|
<p>
|
||||||
|
{intl.formatMessage({
|
||||||
|
id: "points.claimPoints.missingPreviousStay",
|
||||||
|
defaultMessage: "Missing a previous stay?",
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
</Typography>
|
||||||
|
<Button variant="Text" size="sm" onPress={() => setOpenModal(true)}>
|
||||||
|
{intl.formatMessage({
|
||||||
|
id: "points.claimPoints.cta",
|
||||||
|
defaultMessage: "Claim points",
|
||||||
|
})}
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<Modal
|
||||||
|
title="Add missing points"
|
||||||
|
isOpen={openModal}
|
||||||
|
onToggle={(open) => setOpenModal(open)}
|
||||||
|
>
|
||||||
|
<Dialog aria-label="TODO" className={styles.dialog}>
|
||||||
|
{({ close }) => (
|
||||||
|
<ClaimPointsWizard
|
||||||
|
onSuccess={() => {
|
||||||
|
toast.info(
|
||||||
|
<>
|
||||||
|
<Typography variant="Body/Paragraph/mdBold">
|
||||||
|
<p>We're on it!</p>
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="Body/Paragraph/mdRegular">
|
||||||
|
<p>
|
||||||
|
If your points have not been added to your account
|
||||||
|
within 2 weeks, please contact us.
|
||||||
|
</p>
|
||||||
|
</Typography>
|
||||||
|
</>,
|
||||||
|
{
|
||||||
|
duration: Infinity,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
close()
|
||||||
|
}}
|
||||||
|
onClose={close}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Dialog>
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function useLinkableModalState(target: string) {
|
||||||
|
const [openModal, setOpenModal] = useState(false)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const params = new URLSearchParams(window.location.search)
|
||||||
|
const claimPoints = params.get("target") === target
|
||||||
|
|
||||||
|
if (claimPoints) {
|
||||||
|
params.delete("target")
|
||||||
|
const newUrl = `${window.location.pathname}?${params.toString()}`
|
||||||
|
window.history.replaceState({}, "", newUrl)
|
||||||
|
// eslint-disable-next-line react-hooks/set-state-in-effect
|
||||||
|
setOpenModal(true)
|
||||||
|
}
|
||||||
|
}, [target])
|
||||||
|
|
||||||
|
return [openModal, setOpenModal] as const
|
||||||
|
}
|
||||||
|
|
||||||
|
function OldClaimPointsLink() {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const lang = useLang()
|
const lang = useLang()
|
||||||
|
|
||||||
|
|||||||
+49
-31
@@ -26,14 +26,14 @@ export function PointTransactionRow({
|
|||||||
focusRef: React.Ref<HTMLAnchorElement>
|
focusRef: React.Ref<HTMLAnchorElement>
|
||||||
}) {
|
}) {
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
|
const { confirmationNumber, bookingUrl, transactionDate, awardPoints } =
|
||||||
const { confirmationNumber, bookingUrl, checkinDate, awardPoints } =
|
|
||||||
transaction.attributes
|
transaction.attributes
|
||||||
const balfwd = confirmationNumber === BALFWD
|
const balfwd = confirmationNumber === BALFWD
|
||||||
const nonTransactional = confirmationNumber === NON_TRANSACTIONAL
|
const nonTransactional = confirmationNumber === NON_TRANSACTIONAL
|
||||||
|
|
||||||
const day = checkinDate.split("-")[2].replace(/^0/, "")
|
const date = dt.utc(transactionDate).locale(lang)
|
||||||
const month = dt(checkinDate.split("-")[1]).locale(lang).format("MMM")
|
const day = date.format("D")
|
||||||
|
const month = date.format("MMM")
|
||||||
|
|
||||||
const formattedPoints = intl.formatNumber(Math.abs(awardPoints))
|
const formattedPoints = intl.formatNumber(Math.abs(awardPoints))
|
||||||
const calculatedPoints =
|
const calculatedPoints =
|
||||||
@@ -41,7 +41,12 @@ export function PointTransactionRow({
|
|||||||
? formattedPoints
|
? formattedPoints
|
||||||
: `${awardPoints > 0 ? "+" : "-"} ${formattedPoints}`
|
: `${awardPoints > 0 ? "+" : "-"} ${formattedPoints}`
|
||||||
|
|
||||||
const canLinkBookingUrl = !balfwd && !nonTransactional
|
const canLinkBookingUrl =
|
||||||
|
!balfwd &&
|
||||||
|
!nonTransactional &&
|
||||||
|
!!transaction.attributes.bookingUrl &&
|
||||||
|
(transaction.type === Transactions.rewardType.stay ||
|
||||||
|
transaction.type === Transactions.rewardType.rewardNight)
|
||||||
|
|
||||||
const description = getDescription(transaction, intl)
|
const description = getDescription(transaction, intl)
|
||||||
|
|
||||||
@@ -93,26 +98,29 @@ export function PointTransactionRow({
|
|||||||
|
|
||||||
function getDescription(transaction: Transaction, intl: IntlShape) {
|
function getDescription(transaction: Transaction, intl: IntlShape) {
|
||||||
const hotelInformation = transaction.attributes.hotelInformation
|
const hotelInformation = transaction.attributes.hotelInformation
|
||||||
const balfwd = transaction.attributes.confirmationNumber === BALFWD
|
const isBalfwd = transaction.attributes.confirmationNumber === BALFWD
|
||||||
const nonTransactional =
|
const isNonTransactional =
|
||||||
transaction.attributes.confirmationNumber === NON_TRANSACTIONAL
|
transaction.attributes.confirmationNumber === NON_TRANSACTIONAL
|
||||||
|
|
||||||
|
if (isNonTransactional && transaction.attributes.nights === 0) {
|
||||||
|
return intl.formatMessage({
|
||||||
|
id: "earnAndBurn.journeyTable.pointsActivity",
|
||||||
|
defaultMessage: "Point activity",
|
||||||
|
})
|
||||||
|
}
|
||||||
switch (transaction.type) {
|
switch (transaction.type) {
|
||||||
case Transactions.rewardType.stay:
|
case Transactions.rewardType.stay:
|
||||||
return nonTransactional && transaction.attributes.nights === 0
|
if (hotelInformation?.name) {
|
||||||
? intl.formatMessage({
|
return intl.formatMessage(
|
||||||
id: "myPoints.pointTransactions.pointsActivity",
|
{
|
||||||
defaultMessage: "Point activity",
|
id: "earnAndBurn.journeyTable.stayAt",
|
||||||
})
|
defaultMessage: "Stay at {hotelName}",
|
||||||
: hotelInformation?.name
|
},
|
||||||
? intl.formatMessage(
|
{ hotelName: hotelInformation.name }
|
||||||
{
|
)
|
||||||
id: "myPoints.pointTransactions.stayAt",
|
} else {
|
||||||
defaultMessage: "Stay at {hotelName}",
|
return ""
|
||||||
},
|
}
|
||||||
{ hotelName: hotelInformation?.name }
|
|
||||||
)
|
|
||||||
: ""
|
|
||||||
|
|
||||||
case Transactions.rewardType.stayAdj:
|
case Transactions.rewardType.stayAdj:
|
||||||
if (transaction.attributes.hotelOperaId === "ORS") {
|
if (transaction.attributes.hotelOperaId === "ORS") {
|
||||||
return intl.formatMessage({
|
return intl.formatMessage({
|
||||||
@@ -120,42 +128,52 @@ function getDescription(transaction: Transaction, intl: IntlShape) {
|
|||||||
defaultMessage: "Former Scandic Hotel",
|
defaultMessage: "Former Scandic Hotel",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (balfwd) {
|
if (isBalfwd) {
|
||||||
return intl.formatMessage({
|
return intl.formatMessage({
|
||||||
id: "myPoints.pointTransactions.pointsEarnedPriorMay2021",
|
id: "earnAndBurn.journeyTable.pointsEarnedPriorMay2021",
|
||||||
defaultMessage: "Points earned prior to May 1, 2021",
|
defaultMessage: "Points earned prior to May 1, 2021",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
break
|
case Transactions.rewardType.redgift:
|
||||||
|
return intl.formatMessage({
|
||||||
|
id: "earnAndBurn.journeyTable.redGift",
|
||||||
|
defaultMessage: "Reward Gift",
|
||||||
|
})
|
||||||
|
case Transactions.rewardType.rewardNight:
|
||||||
|
return intl.formatMessage({
|
||||||
|
id: "earnAndBurn.journeyTable.rewardNight",
|
||||||
|
defaultMessage: "Reward Night",
|
||||||
|
})
|
||||||
case Transactions.rewardType.ancillary:
|
case Transactions.rewardType.ancillary:
|
||||||
return intl.formatMessage({
|
return intl.formatMessage({
|
||||||
id: "myPoints.pointTransactions.extrasToBooking",
|
id: "earnAndBurn.journeyTable.extrasToBooking",
|
||||||
defaultMessage: "Extras to your booking",
|
defaultMessage: "Extras to your booking",
|
||||||
})
|
})
|
||||||
|
|
||||||
case Transactions.rewardType.enrollment:
|
case Transactions.rewardType.enrollment:
|
||||||
return intl.formatMessage({
|
return intl.formatMessage({
|
||||||
id: "myPoints.pointTransactions.signUpBonus",
|
id: "earnAndBurn.journeyTable.signUpBonus",
|
||||||
defaultMessage: "Sign up bonus",
|
defaultMessage: "Sign up bonus",
|
||||||
})
|
})
|
||||||
|
|
||||||
case Transactions.rewardType.mastercard_points:
|
case Transactions.rewardType.mastercard_points:
|
||||||
return intl.formatMessage({
|
return intl.formatMessage({
|
||||||
id: "myPoints.pointTransactions.scandicFriendsMastercard",
|
id: "earnAndBurn.journeyTable.scandicFriendsMastercard",
|
||||||
defaultMessage: "Scandic Friends Mastercard",
|
defaultMessage: "Scandic Friends Mastercard",
|
||||||
})
|
})
|
||||||
|
|
||||||
case Transactions.rewardType.tui_points:
|
case Transactions.rewardType.tui_points:
|
||||||
return intl.formatMessage({
|
return intl.formatMessage({
|
||||||
id: "myPoints.pointTransactions.tuiPoints",
|
id: "earnAndBurn.journeyTable.tuiPoints",
|
||||||
defaultMessage: "TUI Points",
|
defaultMessage: "TUI Points",
|
||||||
})
|
})
|
||||||
|
|
||||||
case Transactions.rewardType.pointShop:
|
case Transactions.rewardType.pointShop:
|
||||||
return intl.formatMessage({
|
return intl.formatMessage({
|
||||||
id: "myPoints.pointTransactions.pointShop",
|
id: "earnAndBurn.journeyTable.pointShop",
|
||||||
defaultMessage: "Scandic Friends Point Shop",
|
defaultMessage: "Scandic Friends Point Shop",
|
||||||
})
|
})
|
||||||
|
default:
|
||||||
|
return undefined
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -2,6 +2,7 @@
|
|||||||
import { Fragment, useCallback, useRef } from "react"
|
import { Fragment, useCallback, useRef } from "react"
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import { dt } from "@scandic-hotels/common/dt"
|
||||||
import useStickyPosition from "@scandic-hotels/common/hooks/useStickyPosition"
|
import useStickyPosition from "@scandic-hotels/common/hooks/useStickyPosition"
|
||||||
import { StickyElementNameEnum } from "@scandic-hotels/common/stores/sticky-position"
|
import { StickyElementNameEnum } from "@scandic-hotels/common/stores/sticky-position"
|
||||||
import { Divider } from "@scandic-hotels/design-system/Divider"
|
import { Divider } from "@scandic-hotels/design-system/Divider"
|
||||||
@@ -40,9 +41,9 @@ export function PointTransactionList() {
|
|||||||
.flatMap((page) => page.data)
|
.flatMap((page) => page.data)
|
||||||
|
|
||||||
const groupedTransactions =
|
const groupedTransactions =
|
||||||
transactions?.reduce<Record<number, typeof transactions>>(
|
transactions?.reduce<Record<string, typeof transactions>>(
|
||||||
(acc, transaction) => {
|
(acc, transaction) => {
|
||||||
const year = new Date(transaction.attributes.checkinDate).getFullYear()
|
const year = dt.utc(transaction.attributes.transactionDate).year()
|
||||||
if (!acc[year]) acc[year] = []
|
if (!acc[year]) acc[year] = []
|
||||||
acc[year].push(transaction)
|
acc[year].push(transaction)
|
||||||
return acc
|
return acc
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { env } from "@/env/server"
|
|
||||||
|
|
||||||
import SignupForm from "@/components/Forms/Signup"
|
import SignupForm from "@/components/Forms/Signup"
|
||||||
|
|
||||||
import type { SignupFormWrapperProps } from "@/types/components/blocks/dynamicContent"
|
import type { SignupFormWrapperProps } from "@/types/components/blocks/dynamicContent"
|
||||||
@@ -7,10 +5,5 @@ import type { SignupFormWrapperProps } from "@/types/components/blocks/dynamicCo
|
|||||||
export default async function SignupFormWrapper({
|
export default async function SignupFormWrapper({
|
||||||
dynamic_content,
|
dynamic_content,
|
||||||
}: SignupFormWrapperProps) {
|
}: SignupFormWrapperProps) {
|
||||||
return (
|
return <SignupForm {...dynamic_content} />
|
||||||
<SignupForm
|
|
||||||
{...dynamic_content}
|
|
||||||
enableProfileConsent={env.ENABLE_PROFILE_CONSENT}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|||||||
-1
@@ -33,7 +33,6 @@
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Styles for new empty upcoming stays design */
|
|
||||||
.emptyUpcomingStaysContainer {
|
.emptyUpcomingStaysContainer {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: var(--Space-x6);
|
padding: var(--Space-x6);
|
||||||
|
|||||||
+5
-1
@@ -10,6 +10,7 @@ import { useMediaQuery } from "usehooks-ts"
|
|||||||
|
|
||||||
import { useMarkerHover } from "@scandic-hotels/common/hooks/map/useMarkerHover"
|
import { useMarkerHover } from "@scandic-hotels/common/hooks/map/useMarkerHover"
|
||||||
import { InfoWindow } from "@scandic-hotels/design-system/Map/InfoWindow"
|
import { InfoWindow } from "@scandic-hotels/design-system/Map/InfoWindow"
|
||||||
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import { useDestinationPageCitiesMapStore } from "@/stores/destination-page-cities-map"
|
import { useDestinationPageCitiesMapStore } from "@/stores/destination-page-cities-map"
|
||||||
|
|
||||||
@@ -79,7 +80,10 @@ export default function CityClusterMarker({
|
|||||||
})}
|
})}
|
||||||
anchorPoint={AdvancedMarkerAnchorPoint.CENTER}
|
anchorPoint={AdvancedMarkerAnchorPoint.CENTER}
|
||||||
>
|
>
|
||||||
<span className={styles.count}>{sizeAsText}</span>
|
<Typography variant="Title/Subtitle/md">
|
||||||
|
<span>{sizeAsText}</span>
|
||||||
|
</Typography>
|
||||||
|
|
||||||
{isDesktop && isHovered ? (
|
{isDesktop && isHovered ? (
|
||||||
<InfoWindow
|
<InfoWindow
|
||||||
position={position}
|
position={position}
|
||||||
|
|||||||
-6
@@ -20,9 +20,3 @@
|
|||||||
height: 46px !important;
|
height: 46px !important;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.count {
|
|
||||||
font-family: var(--typography-Body-Regular-fontFamily);
|
|
||||||
font-size: var(--typography-Subtitle-2-fontSize);
|
|
||||||
font-weight: var(--typography-Subtitle-2-fontWeight);
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -19,12 +19,13 @@ div.months {
|
|||||||
td.day,
|
td.day,
|
||||||
td.rangeEnd,
|
td.rangeEnd,
|
||||||
td.rangeStart {
|
td.rangeStart {
|
||||||
font-family: var(--typography-Body-Bold-fontFamily);
|
font-family:
|
||||||
font-size: var(--typography-Body-Bold-fontSize);
|
var(--Body-Paragraph-Font-family), var(--Body-Paragraph-Font-fallback);
|
||||||
font-weight: 500;
|
font-size: var(--Body-Paragraph-Size);
|
||||||
letter-spacing: var(--typography-Body-Bold-letterSpacing);
|
font-weight: var(--Body-Paragraph-Font-weight-2);
|
||||||
line-height: var(--typography-Body-Bold-lineHeight);
|
letter-spacing: var(--Body-Paragraph-Letter-spacing);
|
||||||
text-decoration: var(--typography-Body-Bold-textDecoration);
|
line-height: 1.5;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
td.rangeEnd,
|
td.rangeEnd,
|
||||||
@@ -90,14 +91,16 @@ td.day[data-outside="true"] ~ td.day[data-disabled="true"] button.dayButton,
|
|||||||
}
|
}
|
||||||
|
|
||||||
.weekDay {
|
.weekDay {
|
||||||
color: var(--UI-Text-Placeholder);
|
color: var(--Text-Tertiary);
|
||||||
font-family: var(--typography-Footnote-Labels-fontFamily);
|
font-family:
|
||||||
font-size: var(--typography-Footnote-Labels-fontSize);
|
var(--Title-Overline-sm-Font-family), var(--Title-Overline-sm-Font-fallback);
|
||||||
font-weight: var(--typography-Footnote-Labels-fontWeight);
|
font-size: var(--Title-Overline-sm-Size);
|
||||||
letter-spacing: var(--typography-Footnote-Labels-letterSpacing);
|
font-style: normal;
|
||||||
line-height: var(--typography-Footnote-Labels-lineHeight);
|
font-weight: var(--Title-Overline-sm-Font-weight);
|
||||||
text-decoration: var(--typography-Footnote-Labels-textDecoration);
|
line-height: 1.5;
|
||||||
text-transform: uppercase;
|
letter-spacing: var(--Title-Overline-sm-Letter-spacing);
|
||||||
|
text-transform: var(--Title-Overline-sm-Text-Transform);
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
|
|||||||
@@ -89,12 +89,13 @@ div.months {
|
|||||||
td.day,
|
td.day,
|
||||||
td.rangeEnd,
|
td.rangeEnd,
|
||||||
td.rangeStart {
|
td.rangeStart {
|
||||||
font-family: var(--typography-Body-Bold-fontFamily);
|
font-family:
|
||||||
font-size: var(--typography-Body-Bold-fontSize);
|
var(--Body-Paragraph-Font-family), var(--Body-Paragraph-Font-fallback);
|
||||||
font-weight: 500;
|
font-size: var(--Body-Paragraph-Size);
|
||||||
letter-spacing: var(--typography-Body-Bold-letterSpacing);
|
font-weight: var(--Body-Paragraph-Font-weight-2);
|
||||||
line-height: var(--typography-Body-Bold-lineHeight);
|
letter-spacing: var(--Body-Paragraph-Letter-spacing);
|
||||||
text-decoration: var(--typography-Body-Bold-textDecoration);
|
line-height: 1.5;
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
td.rangeEnd,
|
td.rangeEnd,
|
||||||
@@ -156,14 +157,16 @@ td.day[data-outside="true"] ~ td.day[data-disabled="true"] button.dayButton,
|
|||||||
}
|
}
|
||||||
|
|
||||||
.weekDay {
|
.weekDay {
|
||||||
color: var(--UI-Text-Placeholder);
|
color: var(--Text-Tertiary);
|
||||||
font-family: var(--typography-Caption-Labels-fontFamily);
|
font-family:
|
||||||
font-size: var(--typography-Caption-Labels-fontSize);
|
var(--Title-Overline-sm-Font-family), var(--Title-Overline-sm-Font-fallback);
|
||||||
font-weight: var(--typography-Caption-Labels-fontWeight);
|
font-size: var(--Title-Overline-sm-Size);
|
||||||
letter-spacing: var(--typography-Caption-Labels-letterSpacing);
|
font-style: normal;
|
||||||
line-height: var(--typography-Caption-Labels-lineHeight);
|
font-weight: var(--Title-Overline-sm-Font-weight);
|
||||||
text-decoration: var(--typography-Caption-Labels-textDecoration);
|
line-height: 1.5;
|
||||||
text-transform: uppercase;
|
letter-spacing: var(--Title-Overline-sm-Letter-spacing);
|
||||||
|
text-transform: var(--Title-Overline-sm-Text-Transform);
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 1367px) {
|
@media screen and (min-width: 1367px) {
|
||||||
|
|||||||
@@ -107,7 +107,6 @@ export default function Form({ user }: EditFormProps) {
|
|||||||
} else {
|
} else {
|
||||||
router.push(profile[lang])
|
router.push(profile[lang])
|
||||||
}
|
}
|
||||||
router.refresh() // Can be removed on NextJs 15
|
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,14 +48,9 @@ import styles from "./form.module.css"
|
|||||||
|
|
||||||
interface SignUpFormProps {
|
interface SignUpFormProps {
|
||||||
title: string
|
title: string
|
||||||
enableProfileConsent?: boolean
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SignupForm({
|
export default function SignupForm({ title }: SignUpFormProps) {
|
||||||
title,
|
|
||||||
// Handled as a prop rather than a client env var due to limits in Netlify env var size.
|
|
||||||
enableProfileConsent = false,
|
|
||||||
}: SignUpFormProps) {
|
|
||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const lang = useLang()
|
const lang = useLang()
|
||||||
@@ -140,7 +135,7 @@ export default function SignupForm({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.formWrapper}>
|
<div className={styles.formWrapper}>
|
||||||
{enableProfileConsent && <ProfilingConsentModalReadOnly />}
|
<ProfilingConsentModalReadOnly />
|
||||||
{title ? (
|
{title ? (
|
||||||
<Typography variant="Title/md">
|
<Typography variant="Title/md">
|
||||||
<h2>{title}</h2>
|
<h2>{title}</h2>
|
||||||
@@ -293,41 +288,39 @@ export default function SignupForm({
|
|||||||
/>
|
/>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{enableProfileConsent && (
|
<section className={styles.personalization}>
|
||||||
<section className={styles.personalization}>
|
<header>
|
||||||
<header>
|
<Typography variant="Title/Subtitle/md">
|
||||||
<Typography variant="Title/Subtitle/md">
|
<h3>
|
||||||
<h3>
|
{intl.formatMessage({
|
||||||
{intl.formatMessage({
|
id: "signup.UnlockYourPersonalizedExperience",
|
||||||
id: "signup.UnlockYourPersonalizedExperience",
|
defaultMessage: "Unlock your personalized experience!",
|
||||||
defaultMessage: "Unlock your personalized experience!",
|
})}
|
||||||
})}
|
</h3>
|
||||||
</h3>
|
</Typography>
|
||||||
</Typography>
|
</header>
|
||||||
</header>
|
<Checkbox
|
||||||
<Checkbox
|
name="profilingConsent"
|
||||||
name="profilingConsent"
|
registerOptions={{ required: true }}
|
||||||
registerOptions={{ required: true }}
|
>
|
||||||
>
|
{intl.formatMessage({
|
||||||
{intl.formatMessage({
|
id: "signup.yesConsent",
|
||||||
id: "signup.yesConsent",
|
defaultMessage:
|
||||||
defaultMessage:
|
"I consent to Scandic using my information to give me even more personalized travel inspiration and offers from Scandic and trusted Scandic Friends partners. This means Scandic may use information about my interactions with Scandic Friends partners, and share details of my interactions with Scandic with those partners, to make the experience even more relevant to me.",
|
||||||
"I consent to Scandic using my information to give me even more personalized travel inspiration and offers from Scandic and trusted Scandic Friends partners. This means Scandic may use information about my interactions with Scandic Friends partners, and share details of my interactions with Scandic with those partners, to make the experience even more relevant to me.",
|
})}
|
||||||
})}
|
</Checkbox>
|
||||||
</Checkbox>
|
<TextLinkButton
|
||||||
<TextLinkButton
|
typography="Link/sm"
|
||||||
typography="Link/sm"
|
color="Primary"
|
||||||
color="Primary"
|
className={styles.personalizationButton}
|
||||||
className={styles.personalizationButton}
|
onClick={openPersonalizationModal}
|
||||||
onClick={openPersonalizationModal}
|
>
|
||||||
>
|
{intl.formatMessage({
|
||||||
{intl.formatMessage({
|
id: "signup.ReadMoreAboutPersonalization",
|
||||||
id: "signup.ReadMoreAboutPersonalization",
|
defaultMessage: "Read more about personalization at Scandic",
|
||||||
defaultMessage: "Read more about personalization at Scandic",
|
})}
|
||||||
})}
|
</TextLinkButton>
|
||||||
</TextLinkButton>
|
</section>
|
||||||
</section>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<section className={styles.terms}>
|
<section className={styles.terms}>
|
||||||
<header>
|
<header>
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
.menuButton {
|
@layer component {
|
||||||
display: flex;
|
.menuButton {
|
||||||
align-items: center;
|
display: flex;
|
||||||
justify-content: center;
|
align-items: center;
|
||||||
gap: var(--Space-x05);
|
justify-content: center;
|
||||||
cursor: pointer;
|
gap: var(--Space-x05);
|
||||||
width: 100%;
|
cursor: pointer;
|
||||||
background-color: transparent;
|
width: 100%;
|
||||||
color: var(--Text-Interactive-Default);
|
background-color: transparent;
|
||||||
border-width: 0;
|
color: var(--Text-Interactive-Default);
|
||||||
padding: var(--Space-x05) 0;
|
border-width: 0;
|
||||||
|
padding: var(--Space-x05) 0;
|
||||||
|
|
||||||
&.loading {
|
&.loading {
|
||||||
cursor: progress;
|
cursor: progress;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
import { zodResolver } from "@hookform/resolvers/zod"
|
||||||
import { usePathname, useRouter } from "next/navigation"
|
import { usePathname, useRouter } from "next/navigation"
|
||||||
import { useState } from "react"
|
import { useState } from "react"
|
||||||
@@ -25,7 +26,7 @@ import ModifyContact from "../ModifyContact"
|
|||||||
|
|
||||||
import styles from "./guestDetails.module.css"
|
import styles from "./guestDetails.module.css"
|
||||||
|
|
||||||
import type { Guest } from "@scandic-hotels/trpc/routers/booking/output"
|
import type { BookingConfirmation } from "@scandic-hotels/trpc/types/bookingConfirmation"
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type ModifyContactSchema,
|
type ModifyContactSchema,
|
||||||
@@ -34,9 +35,9 @@ import {
|
|||||||
import { MODAL_STEPS } from "@/types/components/hotelReservation/myStay/myStay"
|
import { MODAL_STEPS } from "@/types/components/hotelReservation/myStay/myStay"
|
||||||
import type { SafeUser } from "@/types/user"
|
import type { SafeUser } from "@/types/user"
|
||||||
|
|
||||||
interface GuestDetailsProps {
|
type GuestDetailsProps = {
|
||||||
refId: string
|
refId: string
|
||||||
guest: Guest
|
guest: BookingConfirmation["booking"]["guest"]
|
||||||
isCancelled: boolean
|
isCancelled: boolean
|
||||||
user: SafeUser
|
user: SafeUser
|
||||||
}
|
}
|
||||||
@@ -76,6 +77,7 @@ export default function GuestDetails({
|
|||||||
const isFirstStep = currentStep === MODAL_STEPS.INITIAL
|
const isFirstStep = currentStep === MODAL_STEPS.INITIAL
|
||||||
|
|
||||||
const isMemberBooking =
|
const isMemberBooking =
|
||||||
|
!!user?.membership?.membershipNumber &&
|
||||||
guest.membershipNumber === user?.membership?.membershipNumber
|
guest.membershipNumber === user?.membership?.membershipNumber
|
||||||
|
|
||||||
const updateGuest = trpc.booking.update.useMutation({
|
const updateGuest = trpc.booking.update.useMutation({
|
||||||
@@ -196,7 +198,7 @@ export default function GuestDetails({
|
|||||||
{guest.firstName} {guest.lastName}
|
{guest.firstName} {guest.lastName}
|
||||||
</p>
|
</p>
|
||||||
</Typography>
|
</Typography>
|
||||||
{isMemberBooking && user.membership && (
|
{isMemberBooking && user?.membership && (
|
||||||
<Typography variant="Body/Paragraph/mdRegular">
|
<Typography variant="Body/Paragraph/mdRegular">
|
||||||
<p className={styles.memberNumber} data-hj-suppress>
|
<p className={styles.memberNumber} data-hj-suppress>
|
||||||
{intl.formatMessage(
|
{intl.formatMessage(
|
||||||
|
|||||||
+1
@@ -23,6 +23,7 @@
|
|||||||
text-decoration-skip-ink: none;
|
text-decoration-skip-ink: none;
|
||||||
text-decoration-thickness: auto;
|
text-decoration-thickness: auto;
|
||||||
text-underline-offset: auto;
|
text-underline-offset: auto;
|
||||||
|
text-align: center;
|
||||||
text-underline-position: from-font;
|
text-underline-position: from-font;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+8
-2
@@ -4,6 +4,7 @@ import { useIntl } from "react-intl"
|
|||||||
|
|
||||||
import { sumPackages } from "@scandic-hotels/booking-flow/utils/SelectRate"
|
import { sumPackages } from "@scandic-hotels/booking-flow/utils/SelectRate"
|
||||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||||
|
import { dt } from "@scandic-hotels/common/dt"
|
||||||
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
||||||
import { trpc } from "@scandic-hotels/trpc/client"
|
import { trpc } from "@scandic-hotels/trpc/client"
|
||||||
|
|
||||||
@@ -66,9 +67,14 @@ export default function Steps({ closeModal }: ChangeDatesStepsProps) {
|
|||||||
|
|
||||||
setDates({ fromDate, toDate })
|
setDates({ fromDate, toDate })
|
||||||
|
|
||||||
|
const numberOfNights = dt(toDate).diff(dt(fromDate), "days")
|
||||||
|
|
||||||
const pkgsSum = sumPackages(packages)
|
const pkgsSum = sumPackages(packages)
|
||||||
const extraPrice =
|
const breakfastPrice = !!breakfast
|
||||||
pkgsSum.price + ((breakfast && breakfast.localPrice.totalPrice) || 0)
|
? breakfast.localPrice.price * numberOfNights
|
||||||
|
: 0
|
||||||
|
|
||||||
|
const extraPrice = pkgsSum.price + breakfastPrice
|
||||||
if (isLoggedIn && "member" in data.product && data.product.member) {
|
if (isLoggedIn && "member" in data.product && data.product.member) {
|
||||||
const { currency, pricePerStay } = data.product.member.localPrice
|
const { currency, pricePerStay } = data.product.member.localPrice
|
||||||
setNewPrice(formatPrice(intl, pricePerStay + extraPrice, currency))
|
setNewPrice(formatPrice(intl, pricePerStay + extraPrice, currency))
|
||||||
|
|||||||
+22
-16
@@ -1,11 +1,11 @@
|
|||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
|
import { CancellationRuleEnum } from "@scandic-hotels/common/constants/booking"
|
||||||
import { changeOrCancelDateFormat } from "@scandic-hotels/common/constants/dateFormats"
|
import { changeOrCancelDateFormat } from "@scandic-hotels/common/constants/dateFormats"
|
||||||
import { dt } from "@scandic-hotels/common/dt"
|
import { dt } from "@scandic-hotels/common/dt"
|
||||||
|
|
||||||
import { useMyStayStore } from "@/stores/my-stay"
|
import { useMyStayStore } from "@/stores/my-stay"
|
||||||
|
|
||||||
import { hasModifiableRate } from "@/components/HotelReservation/MyStay/utils"
|
|
||||||
import useLang from "@/hooks/useLang"
|
import useLang from "@/hooks/useLang"
|
||||||
|
|
||||||
import Row from "./Row"
|
import Row from "./Row"
|
||||||
@@ -14,14 +14,19 @@ export default function ModifyBy() {
|
|||||||
const intl = useIntl()
|
const intl = useIntl()
|
||||||
const lang = useLang()
|
const lang = useLang()
|
||||||
|
|
||||||
const { checkInDate, isModifyable } = useMyStayStore((state) => ({
|
const { checkInDate, isFlexBooking, isChangeBooking } = useMyStayStore(
|
||||||
checkInDate: state.bookedRoom.checkInDate,
|
(state) => ({
|
||||||
isModifyable: hasModifiableRate(
|
checkInDate: state.bookedRoom.checkInDate,
|
||||||
state.bookedRoom.rateDefinition.cancellationRule
|
isFlexBooking:
|
||||||
),
|
state.bookedRoom.rateDefinition.cancellationRule ===
|
||||||
}))
|
CancellationRuleEnum.CancellableBefore6PM,
|
||||||
|
isChangeBooking:
|
||||||
|
state.bookedRoom.rateDefinition.cancellationRule ===
|
||||||
|
CancellationRuleEnum.Changeable,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
if (!isModifyable) {
|
if (!isFlexBooking && !isChangeBooking) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,14 +43,15 @@ export default function ModifyBy() {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
return (
|
const title = isChangeBooking
|
||||||
<Row
|
? intl.formatMessage({
|
||||||
icon="refresh"
|
id: "booking.changeTitle",
|
||||||
text={text}
|
defaultMessage: "Change",
|
||||||
title={intl.formatMessage({
|
})
|
||||||
|
: intl.formatMessage({
|
||||||
id: "booking.changeOrCancel",
|
id: "booking.changeOrCancel",
|
||||||
defaultMessage: "Change or cancel",
|
defaultMessage: "Change or cancel",
|
||||||
})}
|
})
|
||||||
/>
|
|
||||||
)
|
return <Row icon="refresh" text={text} title={title} />
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,16 +4,18 @@ import { useMyStayStore } from "@/stores/my-stay"
|
|||||||
|
|
||||||
import Price from "../PriceType/Price"
|
import Price from "../PriceType/Price"
|
||||||
|
|
||||||
import type { PriceType as _PriceType } from "@/types/components/hotelReservation/myStay/myStay"
|
|
||||||
|
|
||||||
export default function TotalPrice() {
|
export default function TotalPrice() {
|
||||||
const { bookedRoom, totalPrice } = useMyStayStore((state) => ({
|
const { bookedRoom, totalPrice, allRoomsAreCancelled } = useMyStayStore(
|
||||||
bookedRoom: state.bookedRoom,
|
(state) => ({
|
||||||
totalPrice: state.totalPrice,
|
bookedRoom: state.bookedRoom,
|
||||||
}))
|
totalPrice: state.totalPrice,
|
||||||
|
allRoomsAreCancelled: state.allRoomsAreCancelled,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Price
|
<Price
|
||||||
isCancelled={bookedRoom.isCancelled}
|
isCancelled={allRoomsAreCancelled}
|
||||||
isMember={bookedRoom.rateDefinition.isMemberRate}
|
isMember={bookedRoom.rateDefinition.isMemberRate}
|
||||||
price={totalPrice}
|
price={totalPrice}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ import accessBooking, {
|
|||||||
} from "./accessBooking"
|
} from "./accessBooking"
|
||||||
|
|
||||||
import type { AdditionalInfoCookieValue } from "@scandic-hotels/booking-flow/types/components/findMyBooking/additionalInfoCookieValue"
|
import type { AdditionalInfoCookieValue } from "@scandic-hotels/booking-flow/types/components/findMyBooking/additionalInfoCookieValue"
|
||||||
import type { Guest } from "@scandic-hotels/trpc/routers/booking/output"
|
import type { BookingConfirmation } from "@scandic-hotels/trpc/types/bookingConfirmation"
|
||||||
|
|
||||||
import type { SafeUser } from "@/types/user"
|
import type { SafeUser } from "@/types/user"
|
||||||
|
|
||||||
@@ -201,7 +201,7 @@ const badAuthenticatedUser: SafeUser = {
|
|||||||
profilingConsentUpdateDate: undefined,
|
profilingConsentUpdateDate: undefined,
|
||||||
}
|
}
|
||||||
|
|
||||||
const loggedOutGuest: Guest = {
|
const loggedOutGuest: BookingConfirmation["booking"]["guest"] = {
|
||||||
email: "logged+out@scandichotels.com",
|
email: "logged+out@scandichotels.com",
|
||||||
firstName: "Anonymous",
|
firstName: "Anonymous",
|
||||||
lastName: "Booking",
|
lastName: "Booking",
|
||||||
@@ -210,7 +210,7 @@ const loggedOutGuest: Guest = {
|
|||||||
countryCode: "SE",
|
countryCode: "SE",
|
||||||
}
|
}
|
||||||
|
|
||||||
const loggedInGuest: Guest = {
|
const loggedInGuest: BookingConfirmation["booking"]["guest"] = {
|
||||||
email: "logged+in@scandichotels.com",
|
email: "logged+in@scandichotels.com",
|
||||||
firstName: "Authenticated",
|
firstName: "Authenticated",
|
||||||
lastName: "Booking",
|
lastName: "Booking",
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { AdditionalInfoCookieValue } from "@scandic-hotels/booking-flow/types/components/findMyBooking/additionalInfoCookieValue"
|
import type { AdditionalInfoCookieValue } from "@scandic-hotels/booking-flow/types/components/findMyBooking/additionalInfoCookieValue"
|
||||||
import type { Guest } from "@scandic-hotels/trpc/routers/booking/output"
|
import type { BookingConfirmation } from "@scandic-hotels/trpc/types/bookingConfirmation"
|
||||||
|
|
||||||
import type { SafeUser } from "@/types/user"
|
import type { SafeUser } from "@/types/user"
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ export {
|
|||||||
* Whether a request can access a confirmed booking or not.
|
* Whether a request can access a confirmed booking or not.
|
||||||
*/
|
*/
|
||||||
function accessBooking(
|
function accessBooking(
|
||||||
guest: Guest,
|
guest: BookingConfirmation["booking"]["guest"],
|
||||||
lastName: string,
|
lastName: string,
|
||||||
user: SafeUser | null,
|
user: SafeUser | null,
|
||||||
cookie: string = ""
|
cookie: string = ""
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import AdditionalInfoForm from "@/components/HotelReservation/FindMyBooking/Addi
|
|||||||
import accessBooking, {
|
import accessBooking, {
|
||||||
ACCESS_GRANTED,
|
ACCESS_GRANTED,
|
||||||
ERROR_BAD_REQUEST,
|
ERROR_BAD_REQUEST,
|
||||||
|
ERROR_NOT_FOUND,
|
||||||
ERROR_UNAUTHORIZED,
|
ERROR_UNAUTHORIZED,
|
||||||
} from "@/components/HotelReservation/MyStay/accessBooking"
|
} from "@/components/HotelReservation/MyStay/accessBooking"
|
||||||
import { Ancillaries } from "@/components/HotelReservation/MyStay/Ancillaries"
|
import { Ancillaries } from "@/components/HotelReservation/MyStay/Ancillaries"
|
||||||
@@ -74,39 +75,23 @@ async function MyStay(props: {
|
|||||||
notFound()
|
notFound()
|
||||||
}
|
}
|
||||||
|
|
||||||
const { confirmationNumber, lastName } = parseRefId(refId)
|
|
||||||
|
|
||||||
const isLoggedIn = await isLoggedInUser()
|
|
||||||
|
|
||||||
const cookieStore = await cookies()
|
const cookieStore = await cookies()
|
||||||
const bv = cookieStore.get("bv")?.value
|
const bv = cookieStore.get("bv")?.value
|
||||||
let bookingConfirmation
|
|
||||||
if (isLoggedIn) {
|
|
||||||
bookingConfirmation = await getBookingConfirmation(refId)
|
|
||||||
} else if (bv) {
|
|
||||||
logger.debug(`MyStay: bv`, bv)
|
|
||||||
const {
|
|
||||||
firstName,
|
|
||||||
email,
|
|
||||||
confirmationNumber: bvConfirmationNo,
|
|
||||||
} = JSON.parse(bv) as AdditionalInfoCookieValue
|
|
||||||
|
|
||||||
if (firstName && email && bvConfirmationNo === confirmationNumber) {
|
const { confirmationNumber, lastName } = parseRefId(refId)
|
||||||
bookingConfirmation = await findBooking(
|
const isLoggedIn = await isLoggedInUser()
|
||||||
confirmationNumber,
|
const [{ error, bookingConfirmation }, user] = await Promise.all([
|
||||||
lastName,
|
getOrFindBookingConfirmation({
|
||||||
firstName,
|
refId,
|
||||||
email
|
isLoggedIn,
|
||||||
)
|
confirmationNumber,
|
||||||
} else {
|
lastName,
|
||||||
return (
|
bv,
|
||||||
<RenderAdditionalInfoForm
|
}),
|
||||||
confirmationNumber={confirmationNumber}
|
getProfileSafely(),
|
||||||
lastName={lastName}
|
])
|
||||||
/>
|
|
||||||
)
|
if (error === "MISSING_INFO") {
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return (
|
return (
|
||||||
<RenderAdditionalInfoForm
|
<RenderAdditionalInfoForm
|
||||||
confirmationNumber={confirmationNumber}
|
confirmationNumber={confirmationNumber}
|
||||||
@@ -121,208 +106,226 @@ async function MyStay(props: {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const { additionalData, booking, hotel, roomCategories } = bookingConfirmation
|
const { booking } = bookingConfirmation
|
||||||
|
|
||||||
const user = await getProfileSafely()
|
const { code } = accessBooking(booking.guest, lastName, user, bv)
|
||||||
|
|
||||||
const intl = await getIntl()
|
switch (code) {
|
||||||
|
case ACCESS_GRANTED.code:
|
||||||
|
return (
|
||||||
|
<MyStayPage
|
||||||
|
bookingConfirmation={bookingConfirmation}
|
||||||
|
user={user}
|
||||||
|
lang={lang}
|
||||||
|
isWebview={!!isWebview}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
case ERROR_NOT_FOUND.code:
|
||||||
|
return notFound()
|
||||||
|
case ERROR_BAD_REQUEST.code:
|
||||||
|
return (
|
||||||
|
<RenderAdditionalInfoForm
|
||||||
|
confirmationNumber={confirmationNumber}
|
||||||
|
lastName={lastName}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
case ERROR_UNAUTHORIZED.code: {
|
||||||
|
if (!bv) return notFound()
|
||||||
|
|
||||||
const access = accessBooking(booking.guest, lastName, user, bv)
|
return (
|
||||||
|
<RenderFindMyBookingForm
|
||||||
if (access === ACCESS_GRANTED) {
|
bv={bv}
|
||||||
const fromDate = dt(booking.checkInDate).format("YYYY-MM-DD")
|
lastName={lastName}
|
||||||
const toDate = dt(booking.checkOutDate).format("YYYY-MM-DD")
|
confirmationNumber={confirmationNumber}
|
||||||
|
/>
|
||||||
const linkedReservationsPromise = getLinkedReservations(booking.refId)
|
|
||||||
|
|
||||||
const packagesInput = {
|
|
||||||
adults: booking.adults,
|
|
||||||
children: booking.childrenAges.length,
|
|
||||||
endDate: toDate,
|
|
||||||
hotelId: hotel.operaId,
|
|
||||||
lang,
|
|
||||||
startDate: fromDate,
|
|
||||||
packageCodes: [
|
|
||||||
BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST,
|
|
||||||
BreakfastPackageEnum.ANCILLARY_CHILD_PAYING_BREAKFAST,
|
|
||||||
BreakfastPackageEnum.FREE_CHILD_BREAKFAST,
|
|
||||||
],
|
|
||||||
}
|
|
||||||
const supportedCards = hotel.merchantInformationData.cards
|
|
||||||
const savedPaymentCardsInput = { supportedCards }
|
|
||||||
|
|
||||||
const hasBreakfastPackage = booking.packages.find(
|
|
||||||
(pkg) => pkg.code === BreakfastPackageEnum.REGULAR_BREAKFAST
|
|
||||||
)
|
|
||||||
const breakfastIncluded = booking.rateDefinition.breakfastIncluded
|
|
||||||
const shouldFetchBreakfastPackages =
|
|
||||||
!hasBreakfastPackage && !breakfastIncluded
|
|
||||||
if (shouldFetchBreakfastPackages) {
|
|
||||||
void getPackages(packagesInput)
|
|
||||||
}
|
|
||||||
const isOwnBooking = user?.email === booking.guest.email
|
|
||||||
if (user && isOwnBooking) {
|
|
||||||
void getSavedPaymentCardsSafely(savedPaymentCardsInput)
|
|
||||||
}
|
|
||||||
|
|
||||||
let breakfastPackages = null
|
|
||||||
if (shouldFetchBreakfastPackages) {
|
|
||||||
breakfastPackages = await getPackages(packagesInput)
|
|
||||||
}
|
|
||||||
let savedCreditCards = null
|
|
||||||
if (user && isOwnBooking) {
|
|
||||||
savedCreditCards = await getSavedPaymentCardsSafely(
|
|
||||||
savedPaymentCardsInput
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
let ancillaryPackagesPromise = null
|
default:
|
||||||
if (booking.showAncillaries) {
|
const _exhaustiveCheck: never = code
|
||||||
ancillaryPackagesPromise = getAncillaryPackages({
|
throw new Error(`Unknown access code: ${code}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function MyStayPage({
|
||||||
|
bookingConfirmation,
|
||||||
|
user,
|
||||||
|
lang,
|
||||||
|
isWebview,
|
||||||
|
}: {
|
||||||
|
bookingConfirmation: BookingConfirmation
|
||||||
|
user: SafeUser | null
|
||||||
|
lang: Lang
|
||||||
|
isWebview: boolean
|
||||||
|
}) {
|
||||||
|
const intl = await getIntl()
|
||||||
|
|
||||||
|
const { additionalData, booking, hotel, roomCategories } = bookingConfirmation
|
||||||
|
|
||||||
|
const fromDate = dt(booking.checkInDate).format("YYYY-MM-DD")
|
||||||
|
const toDate = dt(booking.checkOutDate).format("YYYY-MM-DD")
|
||||||
|
|
||||||
|
const packagesInput = {
|
||||||
|
adults: booking.adults,
|
||||||
|
children: booking.childrenAges.length,
|
||||||
|
endDate: toDate,
|
||||||
|
hotelId: hotel.operaId,
|
||||||
|
lang,
|
||||||
|
startDate: fromDate,
|
||||||
|
packageCodes: [
|
||||||
|
BreakfastPackageEnum.ANCILLARY_REGULAR_BREAKFAST,
|
||||||
|
BreakfastPackageEnum.ANCILLARY_CHILD_PAYING_BREAKFAST,
|
||||||
|
BreakfastPackageEnum.FREE_CHILD_BREAKFAST,
|
||||||
|
],
|
||||||
|
}
|
||||||
|
const supportedCards = hotel.merchantInformationData.cards
|
||||||
|
const savedPaymentCardsInput = { supportedCards }
|
||||||
|
|
||||||
|
const hasBreakfastPackage = booking.packages.find(
|
||||||
|
(pkg) => pkg.code === BreakfastPackageEnum.REGULAR_BREAKFAST
|
||||||
|
)
|
||||||
|
const breakfastIncluded = booking.rateDefinition.breakfastIncluded
|
||||||
|
const shouldFetchBreakfastPackages =
|
||||||
|
!hasBreakfastPackage && !breakfastIncluded
|
||||||
|
|
||||||
|
const isOwnBooking = user?.email === booking.guest.email
|
||||||
|
const shouldGetCards = user && isOwnBooking
|
||||||
|
|
||||||
|
const [breakfastPackages, savedCreditCards] = await Promise.all([
|
||||||
|
shouldFetchBreakfastPackages ? getPackages(packagesInput) : noop(),
|
||||||
|
shouldGetCards
|
||||||
|
? getSavedPaymentCardsSafely(savedPaymentCardsInput)
|
||||||
|
: noop(),
|
||||||
|
])
|
||||||
|
|
||||||
|
const imageSrc =
|
||||||
|
hotel.hotelContent.images.src ||
|
||||||
|
additionalData.gallery?.heroImages[0]?.src ||
|
||||||
|
hotel.galleryImages[0]?.src
|
||||||
|
|
||||||
|
const baseUrl = env.PUBLIC_URL || "https://www.scandichotels.com"
|
||||||
|
const promoUrl = new URL(`${baseUrl}/${lang}/`)
|
||||||
|
const hotelUrl = new URL(`${baseUrl}${bookingConfirmation.url}/`)
|
||||||
|
promoUrl.searchParams.set("hotel", hotel.operaId)
|
||||||
|
|
||||||
|
const maskedBookingConfirmation = maskBookingConfirmation(bookingConfirmation)
|
||||||
|
const maskedUser = isOwnBooking ? maskUser(user) : null
|
||||||
|
|
||||||
|
const hotelWithFilteredAlerts = {
|
||||||
|
...hotel,
|
||||||
|
specialAlerts: filterOverlappingDates(
|
||||||
|
hotel.specialAlerts,
|
||||||
|
dt.utc(fromDate),
|
||||||
|
dt.utc(toDate)
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
const linkedReservationsPromise = getLinkedReservations(booking.refId)
|
||||||
|
const ancillaryPackagesPromise = booking.showAncillaries
|
||||||
|
? getAncillaryPackages({
|
||||||
fromDate,
|
fromDate,
|
||||||
hotelId: hotel.operaId,
|
hotelId: hotel.operaId,
|
||||||
toDate,
|
toDate,
|
||||||
})
|
})
|
||||||
}
|
: null
|
||||||
|
|
||||||
const imageSrc =
|
return (
|
||||||
hotel.hotelContent.images.src ||
|
<MyStayProvider
|
||||||
additionalData.gallery?.heroImages[0]?.src ||
|
bookingConfirmation={maskedBookingConfirmation}
|
||||||
hotel.galleryImages[0]?.src
|
breakfastPackages={breakfastPackages}
|
||||||
|
lang={lang}
|
||||||
const baseUrl = env.PUBLIC_URL || "https://www.scandichotels.com"
|
linkedReservationsPromise={linkedReservationsPromise}
|
||||||
const promoUrl = new URL(`${baseUrl}/${lang}/`)
|
refId={booking.refId}
|
||||||
const hotelUrl = new URL(`${baseUrl}${bookingConfirmation.url}/`)
|
roomCategories={roomCategories}
|
||||||
|
savedCreditCards={savedCreditCards}
|
||||||
promoUrl.searchParams.set("hotel", hotel.operaId)
|
isLoggedIn={!!user}
|
||||||
|
>
|
||||||
const maskedBookingConfirmation = {
|
<main className={styles.main}>
|
||||||
...bookingConfirmation,
|
<div className={styles.imageContainer}>
|
||||||
booking: {
|
<div className={styles.blurOverlay} />
|
||||||
...bookingConfirmation.booking,
|
{imageSrc && (
|
||||||
guest: {
|
<Image
|
||||||
...bookingConfirmation.booking.guest,
|
className={styles.image}
|
||||||
email: maskValue.email(bookingConfirmation.booking.guest.email),
|
src={imageSrc}
|
||||||
phoneNumber: maskValue.phone(
|
alt={hotelWithFilteredAlerts.name}
|
||||||
bookingConfirmation.booking.guest.phoneNumber ?? ""
|
fill
|
||||||
),
|
|
||||||
},
|
|
||||||
},
|
|
||||||
} satisfies BookingConfirmation
|
|
||||||
|
|
||||||
const maskedUser =
|
|
||||||
user && isOwnBooking
|
|
||||||
? ({
|
|
||||||
...user,
|
|
||||||
email: maskValue.email(user.email),
|
|
||||||
phoneNumber: maskValue.phone(user.phoneNumber ?? ""),
|
|
||||||
} satisfies SafeUser)
|
|
||||||
: null
|
|
||||||
|
|
||||||
hotel.specialAlerts = filterOverlappingDates(
|
|
||||||
hotel.specialAlerts,
|
|
||||||
dt.utc(fromDate),
|
|
||||||
dt.utc(toDate)
|
|
||||||
)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<MyStayProvider
|
|
||||||
bookingConfirmation={maskedBookingConfirmation}
|
|
||||||
breakfastPackages={breakfastPackages}
|
|
||||||
lang={lang}
|
|
||||||
linkedReservationsPromise={linkedReservationsPromise}
|
|
||||||
refId={booking.refId}
|
|
||||||
roomCategories={roomCategories}
|
|
||||||
savedCreditCards={savedCreditCards}
|
|
||||||
isLoggedIn={isLoggedIn}
|
|
||||||
>
|
|
||||||
<main className={styles.main}>
|
|
||||||
<div className={styles.imageContainer}>
|
|
||||||
<div className={styles.blurOverlay} />
|
|
||||||
{imageSrc && (
|
|
||||||
<Image
|
|
||||||
className={styles.image}
|
|
||||||
src={imageSrc}
|
|
||||||
alt={hotel.name}
|
|
||||||
fill
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className={styles.content}>
|
|
||||||
<div className={styles.headerContainer}>
|
|
||||||
<Header cityName={hotel.cityName} name={hotel.name} />
|
|
||||||
<ReferenceCard />
|
|
||||||
</div>
|
|
||||||
{booking.showAncillaries && ancillaryPackagesPromise && (
|
|
||||||
<Ancillaries
|
|
||||||
ancillariesPromise={ancillaryPackagesPromise}
|
|
||||||
packages={breakfastPackages}
|
|
||||||
user={maskedUser}
|
|
||||||
savedCreditCards={savedCreditCards}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<SingleRoom user={maskedUser} />
|
|
||||||
<MultiRoom user={maskedUser} />
|
|
||||||
|
|
||||||
<BookingSummary hotelUrl={hotelUrl.toString()} hotel={hotel} />
|
|
||||||
{!isWebview && (
|
|
||||||
<Promo
|
|
||||||
title={intl.formatMessage({
|
|
||||||
id: "booking.bookNextStay.title",
|
|
||||||
defaultMessage: "Book your next stay",
|
|
||||||
})}
|
|
||||||
text={intl.formatMessage({
|
|
||||||
id: "booking.bookAnotherStayDescription",
|
|
||||||
defaultMessage:
|
|
||||||
"Get inspired and start dreaming beyond your next trip. Explore more Scandic destinations.",
|
|
||||||
})}
|
|
||||||
buttonText={intl.formatMessage({
|
|
||||||
id: "myStay.promo.bookNextStay.buttonText",
|
|
||||||
defaultMessage: "Explore Scandic hotels",
|
|
||||||
})}
|
|
||||||
href={promoUrl.toString()}
|
|
||||||
image={hotel.hotelContent.images}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</MyStayProvider>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (access === ERROR_BAD_REQUEST) {
|
|
||||||
return (
|
|
||||||
<RenderAdditionalInfoForm
|
|
||||||
confirmationNumber={confirmationNumber}
|
|
||||||
lastName={lastName}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (access === ERROR_UNAUTHORIZED) {
|
|
||||||
if (bv) {
|
|
||||||
const { firstName, email } = JSON.parse(bv) as AdditionalInfoCookieValue
|
|
||||||
|
|
||||||
return (
|
|
||||||
<main className={styles.main}>
|
|
||||||
<div className={styles.form}>
|
|
||||||
<FindMyBooking
|
|
||||||
error={FindMyBookingErrorEnum.BOOKING_ACCESS_DENIED}
|
|
||||||
defaultValues={{
|
|
||||||
firstName,
|
|
||||||
lastName,
|
|
||||||
confirmationNumber,
|
|
||||||
email,
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<div className={styles.content}>
|
||||||
|
<div className={styles.headerContainer}>
|
||||||
|
<Header
|
||||||
|
cityName={hotelWithFilteredAlerts.cityName}
|
||||||
|
name={hotelWithFilteredAlerts.name}
|
||||||
|
/>
|
||||||
|
<ReferenceCard />
|
||||||
</div>
|
</div>
|
||||||
</main>
|
{booking.showAncillaries && ancillaryPackagesPromise && (
|
||||||
)
|
<Ancillaries
|
||||||
} else {
|
ancillariesPromise={ancillaryPackagesPromise}
|
||||||
}
|
packages={breakfastPackages}
|
||||||
}
|
user={maskedUser}
|
||||||
|
savedCreditCards={savedCreditCards}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
return notFound()
|
<SingleRoom user={maskedUser} />
|
||||||
|
<MultiRoom user={maskedUser} />
|
||||||
|
|
||||||
|
<BookingSummary
|
||||||
|
hotelUrl={hotelUrl.toString()}
|
||||||
|
hotel={hotelWithFilteredAlerts}
|
||||||
|
/>
|
||||||
|
{!isWebview && (
|
||||||
|
<Promo
|
||||||
|
title={intl.formatMessage({
|
||||||
|
id: "booking.bookNextStay.title",
|
||||||
|
defaultMessage: "Book your next stay",
|
||||||
|
})}
|
||||||
|
text={intl.formatMessage({
|
||||||
|
id: "booking.bookAnotherStayDescription",
|
||||||
|
defaultMessage:
|
||||||
|
"Get inspired and start dreaming beyond your next trip. Explore more Scandic destinations.",
|
||||||
|
})}
|
||||||
|
buttonText={intl.formatMessage({
|
||||||
|
id: "myStay.promo.bookNextStay.buttonText",
|
||||||
|
defaultMessage: "Explore Scandic hotels",
|
||||||
|
})}
|
||||||
|
href={promoUrl.toString()}
|
||||||
|
image={hotelWithFilteredAlerts.hotelContent.images}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</MyStayProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function RenderFindMyBookingForm({
|
||||||
|
bv,
|
||||||
|
lastName,
|
||||||
|
confirmationNumber,
|
||||||
|
}: {
|
||||||
|
bv: string
|
||||||
|
lastName: string
|
||||||
|
confirmationNumber: string
|
||||||
|
}) {
|
||||||
|
const { firstName, email } = JSON.parse(bv) as AdditionalInfoCookieValue
|
||||||
|
|
||||||
|
return (
|
||||||
|
<main className={styles.main}>
|
||||||
|
<div className={styles.form}>
|
||||||
|
<FindMyBooking
|
||||||
|
error={FindMyBookingErrorEnum.BOOKING_ACCESS_DENIED}
|
||||||
|
defaultValues={{
|
||||||
|
firstName,
|
||||||
|
lastName,
|
||||||
|
confirmationNumber,
|
||||||
|
email,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function RenderAdditionalInfoForm({
|
function RenderAdditionalInfoForm({
|
||||||
@@ -343,3 +346,75 @@ function RenderAdditionalInfoForm({
|
|||||||
</main>
|
</main>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function maskUser(user: SafeUser | null): SafeUser | null {
|
||||||
|
if (!user) return null
|
||||||
|
|
||||||
|
return {
|
||||||
|
...user,
|
||||||
|
email: maskValue.email(user.email),
|
||||||
|
phoneNumber: maskValue.phone(user.phoneNumber ?? ""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function maskBookingConfirmation(
|
||||||
|
bookingConfirmation: BookingConfirmation
|
||||||
|
): BookingConfirmation {
|
||||||
|
return {
|
||||||
|
...bookingConfirmation,
|
||||||
|
booking: {
|
||||||
|
...bookingConfirmation.booking,
|
||||||
|
guest: {
|
||||||
|
...bookingConfirmation.booking.guest,
|
||||||
|
email: maskValue.email(bookingConfirmation.booking.guest.email),
|
||||||
|
phoneNumber: maskValue.phone(
|
||||||
|
bookingConfirmation.booking.guest.phoneNumber ?? ""
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getOrFindBookingConfirmation({
|
||||||
|
refId,
|
||||||
|
confirmationNumber,
|
||||||
|
lastName,
|
||||||
|
isLoggedIn,
|
||||||
|
bv,
|
||||||
|
}: {
|
||||||
|
refId: string
|
||||||
|
confirmationNumber: string
|
||||||
|
lastName: string
|
||||||
|
isLoggedIn: boolean
|
||||||
|
bv?: string
|
||||||
|
}) {
|
||||||
|
if (isLoggedIn)
|
||||||
|
return { bookingConfirmation: await getBookingConfirmation(refId) } as const
|
||||||
|
|
||||||
|
if (!bv) return { error: "MISSING_INFO", bookingConfirmation: null } as const
|
||||||
|
|
||||||
|
logger.debug(`MyStay: bv`, bv)
|
||||||
|
const {
|
||||||
|
firstName,
|
||||||
|
email,
|
||||||
|
confirmationNumber: bvConfirmationNo,
|
||||||
|
} = JSON.parse(bv) as AdditionalInfoCookieValue
|
||||||
|
|
||||||
|
if (!firstName || !email || bvConfirmationNo !== confirmationNumber) {
|
||||||
|
return { error: "MISSING_INFO", bookingConfirmation: null } as const
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
bookingConfirmation: await findBooking(
|
||||||
|
confirmationNumber,
|
||||||
|
lastName,
|
||||||
|
firstName,
|
||||||
|
email
|
||||||
|
),
|
||||||
|
} as const
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper function to handle conditional Promise.all calls
|
||||||
|
async function noop() {
|
||||||
|
return Promise.resolve(null)
|
||||||
|
}
|
||||||
|
|||||||
-7
@@ -4,13 +4,6 @@
|
|||||||
padding: var(--Space-x3) var(--Space-x2);
|
padding: var(--Space-x3) var(--Space-x2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.subtitle {
|
|
||||||
font-family: var(--typography-Subtitle-2-fontFamily);
|
|
||||||
font-size: var(--typography-Subtitle-2-Mobile-fontSize);
|
|
||||||
font-weight: var(--typography-Subtitle-2-fontWeight);
|
|
||||||
color: var(--Base-Text-High-contrast);
|
|
||||||
}
|
|
||||||
|
|
||||||
.list {
|
.list {
|
||||||
list-style: none;
|
list-style: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,8 +16,8 @@
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
height: 32px;
|
height: 32px;
|
||||||
width: 32px;
|
width: 32px;
|
||||||
font-size: var(--typography-Body-Bold-fontSize);
|
font-size: var(--Body-Paragraph-Size);
|
||||||
font-weight: var(--typography-Body-Bold-fontWeight);
|
font-weight: var(--Body-Paragraph-Font-weight-2);
|
||||||
padding: 0;
|
padding: 0;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
import { env } from "@/env/server"
|
|
||||||
|
|
||||||
import { getIntl } from "@/i18n"
|
import { getIntl } from "@/i18n"
|
||||||
|
|
||||||
import { Section } from "../Section"
|
import { Section } from "../Section"
|
||||||
@@ -17,7 +15,7 @@ export async function CommunicationSettings() {
|
|||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<EmailSlot />
|
<EmailSlot />
|
||||||
{env.ENABLE_PROFILE_CONSENT && <PersonalizationSlot />}
|
<PersonalizationSlot />
|
||||||
</Section>
|
</Section>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import { env } from "@/env/server"
|
|
||||||
import { getProfile, getProfilingConsent } from "@/lib/trpc/memoizedRequests"
|
import { getProfile, getProfilingConsent } from "@/lib/trpc/memoizedRequests"
|
||||||
|
|
||||||
import { GetMainIconByCSIdentifier, userHasConsent } from "../utils"
|
import { GetMainIconByCSIdentifier, userHasConsent } from "../utils"
|
||||||
@@ -9,8 +8,6 @@ import { BannerButton } from "./Button"
|
|||||||
import styles from "./profilingConsentBanner.module.css"
|
import styles from "./profilingConsentBanner.module.css"
|
||||||
|
|
||||||
export async function ProfilingConsentBanner() {
|
export async function ProfilingConsentBanner() {
|
||||||
if (!env.ENABLE_PROFILE_CONSENT) return null
|
|
||||||
|
|
||||||
const user = await getProfile()
|
const user = await getProfile()
|
||||||
if (!user || userHasConsent(user?.profilingConsent)) return null
|
if (!user || userHasConsent(user?.profilingConsent)) return null
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
# Profiling Consent
|
# Profiling Consent
|
||||||
|
|
||||||
Profiling consent allows users to opt in/out of personalized experiences. The feature is controlled by the `ENABLE_PROFILE_CONSENT` environment variable.
|
Profiling consent allows users to opt in/out of personalized experiences.
|
||||||
|
|
||||||
## User Journey
|
## User Journey
|
||||||
|
|
||||||
@@ -121,11 +121,9 @@ Replace `<memberKey>` with the actual `membershipNumber` or `profileId`.
|
|||||||
Required content for the feature:
|
Required content for the feature:
|
||||||
|
|
||||||
1. **Profiling Consent (config)**
|
1. **Profiling Consent (config)**
|
||||||
|
|
||||||
- Config needs to be created and published in each language
|
- Config needs to be created and published in each language
|
||||||
|
|
||||||
2. **/consent (account page)**
|
2. **/consent (account page)**
|
||||||
|
|
||||||
- Page needs to be created and published in each language
|
- Page needs to be created and published in each language
|
||||||
|
|
||||||
3. **/overview (account page)**
|
3. **/overview (account page)**
|
||||||
|
|||||||
+1
@@ -2,6 +2,7 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--Space-x05);
|
gap: var(--Space-x05);
|
||||||
|
align-items: flex-start;
|
||||||
}
|
}
|
||||||
|
|
||||||
.link {
|
.link {
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
import Footnote from "@scandic-hotels/design-system/Footnote"
|
|
||||||
import {
|
import {
|
||||||
MaterialIcon,
|
MaterialIcon,
|
||||||
type MaterialIconSetIconProps,
|
type MaterialIconSetIconProps,
|
||||||
} from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
} from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||||
import Link from "@scandic-hotels/design-system/OldDSLink"
|
import { TextLink } from "@scandic-hotels/design-system/TextLink"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
import { getValueFromContactConfig } from "@scandic-hotels/trpc/utils/contactConfig"
|
import { getValueFromContactConfig } from "@scandic-hotels/trpc/utils/contactConfig"
|
||||||
|
|
||||||
import { serverClient } from "@/lib/trpc/server"
|
import { serverClient } from "@/lib/trpc/server"
|
||||||
|
|
||||||
// import { getValueFromContactConfig } from "@/utils/contactConfig"
|
|
||||||
import styles from "./contactRow.module.css"
|
import styles from "./contactRow.module.css"
|
||||||
|
|
||||||
import type { ContactRowProps } from "@/types/components/sidebar/joinLoyaltyContact"
|
import type { ContactRowProps } from "@/types/components/sidebar/joinLoyaltyContact"
|
||||||
@@ -46,22 +44,27 @@ export default async function ContactRow({ contact }: ContactRowProps) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.wrapper}>
|
<div className={styles.wrapper}>
|
||||||
<Typography
|
{contact.display_text ? (
|
||||||
variant="Body/Paragraph/mdBold"
|
<Typography
|
||||||
className={styles.displayText}
|
variant="Body/Paragraph/mdBold"
|
||||||
>
|
className={styles.displayText}
|
||||||
<p>{contact.display_text}</p>
|
>
|
||||||
</Typography>
|
<p>{contact.display_text}</p>
|
||||||
<Link
|
</Typography>
|
||||||
|
) : null}
|
||||||
|
<TextLink
|
||||||
|
typography="Link/sm"
|
||||||
className={styles.link}
|
className={styles.link}
|
||||||
href={openableLink}
|
href={openableLink}
|
||||||
textDecoration="underline"
|
|
||||||
size="small"
|
|
||||||
>
|
>
|
||||||
{Icon ? <Icon size={20} color="Icon/Interactive/Default" /> : null}
|
{Icon ? <Icon size={20} color="Icon/Interactive/Default" /> : null}
|
||||||
{val}
|
{val}
|
||||||
</Link>
|
</TextLink>
|
||||||
{footnote && <Footnote color="burgundy">{footnote}</Footnote>}
|
{footnote && (
|
||||||
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
|
<p>{footnote}</p>
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,18 +13,9 @@
|
|||||||
gap: var(--Space-x15);
|
gap: var(--Space-x15);
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact > div {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (min-width: 1367px) {
|
@media screen and (min-width: 1367px) {
|
||||||
.contactContainer {
|
.contactContainer {
|
||||||
align-items: start;
|
align-items: start;
|
||||||
padding-top: var(--Space-x2);
|
padding-top: var(--Space-x2);
|
||||||
}
|
}
|
||||||
|
|
||||||
.contact > div {
|
|
||||||
justify-content: start;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { scandicFriends } from "@scandic-hotels/common/constants/routes/myPages"
|
import { scandicFriends } from "@scandic-hotels/common/constants/routes/customerService"
|
||||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||||
import Link from "@scandic-hotels/design-system/OldDSLink"
|
import Link from "@scandic-hotels/design-system/OldDSLink"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|||||||
@@ -2,15 +2,6 @@ import { Lang } from "@scandic-hotels/common/constants/language"
|
|||||||
|
|
||||||
import type { LangRoute } from "@scandic-hotels/common/constants/routes/langRoute"
|
import type { LangRoute } from "@scandic-hotels/common/constants/routes/langRoute"
|
||||||
|
|
||||||
export const sasPartnershipTermsAndConditions: LangRoute = {
|
|
||||||
da: `/${Lang.da}/kundeservice/politikker/scandic-friends-betingelser/sas-eurobonus`,
|
|
||||||
de: `/${Lang.de}/kundenbetreuung/richtlinien/scandic-friends-allgemeine-geschaeftsbedingungen/sas-eurobonus`,
|
|
||||||
en: `/${Lang.en}/customer-service/policies/scandic-friends/sas-eurobonus`,
|
|
||||||
fi: `/${Lang.fi}/asiakaspalvelu/ehdot/scandic-friends/sas-eurobonus`,
|
|
||||||
no: `/${Lang.no}/kundeservice/betingelser/scandic-friends-betingelser/sas-eurobonus`,
|
|
||||||
sv: `/${Lang.sv}/kundservice/villkor/scandic-friends/sas-eurobonus`,
|
|
||||||
}
|
|
||||||
|
|
||||||
export const faq: LangRoute = {
|
export const faq: LangRoute = {
|
||||||
da: `/${Lang.da}/scandic-friends/hjalp-og-service/faq`,
|
da: `/${Lang.da}/scandic-friends/hjalp-og-service/faq`,
|
||||||
de: `/${Lang.de}/scandic-friends/hilfe-und-service/faq`,
|
de: `/${Lang.de}/scandic-friends/hilfe-und-service/faq`,
|
||||||
|
|||||||
Vendored
+6
@@ -16,6 +16,11 @@ export const env = createEnv({
|
|||||||
.transform((s) =>
|
.transform((s) =>
|
||||||
getSemver("scandic-web", s, process.env.BRANCH || "development")
|
getSemver("scandic-web", s, process.env.BRANCH || "development")
|
||||||
),
|
),
|
||||||
|
NEXT_PUBLIC_NEW_POINTCLAIMS: z
|
||||||
|
.string()
|
||||||
|
.optional()
|
||||||
|
.default("false")
|
||||||
|
.transform((s) => s === "true"),
|
||||||
},
|
},
|
||||||
emptyStringAsUndefined: true,
|
emptyStringAsUndefined: true,
|
||||||
runtimeEnv: {
|
runtimeEnv: {
|
||||||
@@ -26,5 +31,6 @@ export const env = createEnv({
|
|||||||
process.env.NEXT_PUBLIC_SENTRY_CLIENT_SAMPLERATE,
|
process.env.NEXT_PUBLIC_SENTRY_CLIENT_SAMPLERATE,
|
||||||
NEXT_PUBLIC_PUBLIC_URL: process.env.NEXT_PUBLIC_PUBLIC_URL,
|
NEXT_PUBLIC_PUBLIC_URL: process.env.NEXT_PUBLIC_PUBLIC_URL,
|
||||||
NEXT_PUBLIC_RELEASE_TAG: process.env.NEXT_PUBLIC_RELEASE_TAG,
|
NEXT_PUBLIC_RELEASE_TAG: process.env.NEXT_PUBLIC_RELEASE_TAG,
|
||||||
|
NEXT_PUBLIC_NEW_POINTCLAIMS: process.env.NEXT_PUBLIC_NEW_POINTCLAIMS,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
Vendored
-6
@@ -96,11 +96,6 @@ export const env = createEnv({
|
|||||||
.refine((s) => s === "1" || s === "0")
|
.refine((s) => s === "1" || s === "0")
|
||||||
.transform((s) => s === "1")
|
.transform((s) => s === "1")
|
||||||
.default("0"),
|
.default("0"),
|
||||||
ENABLE_PROFILE_CONSENT: z
|
|
||||||
.string()
|
|
||||||
.refine((s) => s === "true" || s === "false")
|
|
||||||
.transform((s) => s === "true")
|
|
||||||
.default("false"),
|
|
||||||
RELEASE_TAG: z
|
RELEASE_TAG: z
|
||||||
.string()
|
.string()
|
||||||
.optional()
|
.optional()
|
||||||
@@ -160,7 +155,6 @@ export const env = createEnv({
|
|||||||
DTMC_ENTRA_ID_SECRET: process.env.DTMC_ENTRA_ID_SECRET,
|
DTMC_ENTRA_ID_SECRET: process.env.DTMC_ENTRA_ID_SECRET,
|
||||||
CHATBOT_LIVE_LANGS: process.env.CHATBOT_LIVE_LANGS,
|
CHATBOT_LIVE_LANGS: process.env.CHATBOT_LIVE_LANGS,
|
||||||
SEO_INERT: process.env.SEO_INERT,
|
SEO_INERT: process.env.SEO_INERT,
|
||||||
ENABLE_PROFILE_CONSENT: process.env.ENABLE_PROFILE_CONSENT,
|
|
||||||
RELEASE_TAG: process.env.NEXT_PUBLIC_RELEASE_TAG,
|
RELEASE_TAG: process.env.NEXT_PUBLIC_RELEASE_TAG,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -231,6 +231,12 @@
|
|||||||
"value": "Særlige ønsker (valgfrit)"
|
"value": "Særlige ønsker (valgfrit)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"ancillaries.label.quantity": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Antal"
|
||||||
|
}
|
||||||
|
],
|
||||||
"ancillaries.unableToDisplayBreakfastPrices": [
|
"ancillaries.unableToDisplayBreakfastPrices": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -249,6 +255,18 @@
|
|||||||
"value": "Jeg accepterer booking- og annulleringsbetingelserne"
|
"value": "Jeg accepterer booking- og annulleringsbetingelserne"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"booking.alert.extraBeds.text": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Ved booking for mere end 2 gæster vil der blive opkrævet et ekstra gebyr pr. person. Se link for detaljer."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"booking.alert.extraguests": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Ekstra gæst(er)"
|
||||||
|
}
|
||||||
|
],
|
||||||
"booking.approx": [
|
"booking.approx": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -953,6 +971,16 @@
|
|||||||
"value": "Tilføj kode"
|
"value": "Tilføj kode"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.bookingCode.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Læs mere om brug af "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "codeVoucher"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.bookingCode.remember": [
|
"bookingWidget.bookingCode.remember": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -1061,6 +1089,16 @@
|
|||||||
"value": "details"
|
"value": "details"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.reward.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Læs mere om booking med "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "reward"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.reward.rewardNight": [
|
"bookingWidget.reward.rewardNight": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -2510,6 +2548,44 @@
|
|||||||
"value": "Datoer"
|
"value": "Datoer"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.currentCount": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Nuværende "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": " "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "count"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.decrease": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Reducer "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.increase": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Øg "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
"destination.backToCities": [
|
"destination.backToCities": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -5783,7 +5859,7 @@
|
|||||||
"myPages.myStay.ancillaries.reachedMaxItemsStepperMessage": [
|
"myPages.myStay.ancillaries.reachedMaxItemsStepperMessage": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Maksimal antal nået for denne vare."
|
"value": "Maksimalt antal nået for denne vare."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"myPages.myStay.ancillaries.reachedMaxPointsMessage": [
|
"myPages.myStay.ancillaries.reachedMaxPointsMessage": [
|
||||||
@@ -7569,6 +7645,58 @@
|
|||||||
"value": "Samlede ophold"
|
"value": "Samlede ophold"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"price.numberOfEuroBonusPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB-point"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB-point"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfEuroBonusPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"price.numberOfScandicPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Point"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Point"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfScandicPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
"price.numberOfVouchers": [
|
"price.numberOfVouchers": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
@@ -8629,6 +8757,12 @@
|
|||||||
"value": "). Se tilgængelige priser nedenfor."
|
"value": "). Se tilgængelige priser nedenfor."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"selectRate.alert.reservationPolicies": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Se prisdetaljer"
|
||||||
|
}
|
||||||
|
],
|
||||||
"selectRate.availableRooms.all": [
|
"selectRate.availableRooms.all": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
|
|||||||
@@ -231,6 +231,12 @@
|
|||||||
"value": "Sonderwünsche (optional)"
|
"value": "Sonderwünsche (optional)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"ancillaries.label.quantity": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Menge"
|
||||||
|
}
|
||||||
|
],
|
||||||
"ancillaries.unableToDisplayBreakfastPrices": [
|
"ancillaries.unableToDisplayBreakfastPrices": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -249,6 +255,18 @@
|
|||||||
"value": "Ich akzeptiere die Buchungs- und Stornierungsbedingungen"
|
"value": "Ich akzeptiere die Buchungs- und Stornierungsbedingungen"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"booking.alert.extraBeds.text": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Bei Buchungen für mehr als 2 Gäste fällt eine zusätzliche Gebühr pro Person an. Siehe Link für Details."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"booking.alert.extraguests": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Zusätzliche Gäste"
|
||||||
|
}
|
||||||
|
],
|
||||||
"booking.approx": [
|
"booking.approx": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -949,6 +967,16 @@
|
|||||||
"value": "Code hinzufügen"
|
"value": "Code hinzufügen"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.bookingCode.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Mehr erfahren über die Verwendung von "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "codeVoucher"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.bookingCode.remember": [
|
"bookingWidget.bookingCode.remember": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -1057,6 +1085,20 @@
|
|||||||
"value": "details"
|
"value": "details"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.reward.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Mehr über Buchungen mit "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "reward"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "erfahren"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.reward.rewardNight": [
|
"bookingWidget.reward.rewardNight": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -2522,6 +2564,44 @@
|
|||||||
"value": "Daten"
|
"value": "Daten"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.currentCount": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Aktuell "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": " "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "count"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.decrease": [
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "verringern"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.increase": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Erhöhen "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
"destination.backToCities": [
|
"destination.backToCities": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -7570,6 +7650,58 @@
|
|||||||
"value": "Aufenthalte insgesamt"
|
"value": "Aufenthalte insgesamt"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"price.numberOfEuroBonusPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB-Punkt"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB-Punkte"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfEuroBonusPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"price.numberOfScandicPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Punkt"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Punkte"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfScandicPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
"price.numberOfVouchers": [
|
"price.numberOfVouchers": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
@@ -8634,6 +8766,12 @@
|
|||||||
"value": "). Verfügbare Übernachtungspreise sind unten zu sehen."
|
"value": "). Verfügbare Übernachtungspreise sind unten zu sehen."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"selectRate.alert.reservationPolicies": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Tarifdetails anzeigen"
|
||||||
|
}
|
||||||
|
],
|
||||||
"selectRate.availableRooms.all": [
|
"selectRate.availableRooms.all": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
|
|||||||
@@ -231,6 +231,12 @@
|
|||||||
"value": "Special requests (optional)"
|
"value": "Special requests (optional)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"ancillaries.label.quantity": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Quantity"
|
||||||
|
}
|
||||||
|
],
|
||||||
"ancillaries.unableToDisplayBreakfastPrices": [
|
"ancillaries.unableToDisplayBreakfastPrices": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -249,6 +255,18 @@
|
|||||||
"value": "I accept the booking and cancellation terms"
|
"value": "I accept the booking and cancellation terms"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"booking.alert.extraBeds.text": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "When booking for more than 2 guests, an additional fee will apply per person. See link for details."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"booking.alert.extraguests": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Extra guest(s)"
|
||||||
|
}
|
||||||
|
],
|
||||||
"booking.approx": [
|
"booking.approx": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -337,6 +355,12 @@
|
|||||||
"value": "Change or cancel"
|
"value": "Change or cancel"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"booking.changeTitle": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Change"
|
||||||
|
}
|
||||||
|
],
|
||||||
"booking.codeVoucher": [
|
"booking.codeVoucher": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -953,6 +977,16 @@
|
|||||||
"value": "Add code"
|
"value": "Add code"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.bookingCode.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Read more about using "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "codeVoucher"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.bookingCode.remember": [
|
"bookingWidget.bookingCode.remember": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -1061,6 +1095,16 @@
|
|||||||
"value": "details"
|
"value": "details"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.reward.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Read more about booking with "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "reward"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.reward.rewardNight": [
|
"bookingWidget.reward.rewardNight": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -2198,6 +2242,16 @@
|
|||||||
"value": " points"
|
"value": " points"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"common.pointsInLine": [
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "points"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": " points"
|
||||||
|
}
|
||||||
|
],
|
||||||
"common.pointsToSpend": [
|
"common.pointsToSpend": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -2510,6 +2564,44 @@
|
|||||||
"value": "Dates"
|
"value": "Dates"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.currentCount": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Current "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": " "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "count"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.decrease": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Decrease "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.increase": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Increase "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
"destination.backToCities": [
|
"destination.backToCities": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -3060,6 +3152,12 @@
|
|||||||
"value": "When you confirm the booking the room will be guaranteed for late arrival. If you fail to arrive without cancelling in advance or if you cancel after 18:00 local time, you will be charged for one reward night."
|
"value": "When you confirm the booking the room will be guaranteed for late arrival. If you fail to arrive without cancelling in advance or if you cancel after 18:00 local time, you will be charged for one reward night."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"enterDetails.confirmBooking.rewardNightGuaranteeInfo": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "When you complete the booking the room will be guaranteed for late arrival. The hotel will hold your booking, even if you arrive after 18:00. In case of a no-show, you will be charged for one reward night."
|
||||||
|
}
|
||||||
|
],
|
||||||
"enterDetails.details.description": [
|
"enterDetails.details.description": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -3344,6 +3442,40 @@
|
|||||||
"value": "Select payment method"
|
"value": "Select payment method"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"enterDetails.paymentStep.flexBookingTermsAndConditions": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "To complete your booking, please accept the general "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Booking & Cancellation Terms"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": 8,
|
||||||
|
"value": "termsAndConditionsLink"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": ", and acknowledge that your data will be processed in accordance with Scandic's "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Privacy policy"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"type": 8,
|
||||||
|
"value": "privacyPolicyLink"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "."
|
||||||
|
}
|
||||||
|
],
|
||||||
"enterDetails.priceChangeDialog.acceptButton": [
|
"enterDetails.priceChangeDialog.acceptButton": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -5669,6 +5801,24 @@
|
|||||||
"value": "!"
|
"value": "!"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"myPages.l6progress.modal.title": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Level upgrade and membership year"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPages.l6progress.modal.youCanAlsoReach": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "You can also reach Best Friend, our highest membership level, by staying 100 nights with us within a membership year."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPages.l6progress.modal.yourLevelDuring": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Your level during the current and next period is based on the points you earn during this 12-month period."
|
||||||
|
}
|
||||||
|
],
|
||||||
"myPages.leftToLevelUp": [
|
"myPages.leftToLevelUp": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -6057,6 +6207,88 @@
|
|||||||
"value": "Your membership"
|
"value": "Your membership"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"myPoints.pointTransactions.extrasToBooking": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Extras to your booking"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.formerScandicHotel": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Former Scandic Hotel"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.noTransactions": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "No transactions available"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.pointShop": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Scandic Friends Point Shop"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.pointsActivity": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Point activity"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.pointsEarnedPriorMay2021": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Points earned prior to May 1, 2021"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.redGift": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Reward Gift"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.rewardNight": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Reward Night"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.scandicFriendsMastercard": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Scandic Friends Mastercard"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.showMoreTransactions": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Show more transactions"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.signUpBonus": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Sign up bonus"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.stayAt": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Stay at "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "hotelName"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"myPoints.pointTransactions.tuiPoints": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "TUI Points"
|
||||||
|
}
|
||||||
|
],
|
||||||
"myStay.accessDenied.bookingNotFound": [
|
"myStay.accessDenied.bookingNotFound": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -7546,6 +7778,58 @@
|
|||||||
"value": "Total stays"
|
"value": "Total stays"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"price.numberOfEuroBonusPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB Point"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB Points"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfEuroBonusPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"price.numberOfScandicPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Point"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Points"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfScandicPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
"price.numberOfVouchers": [
|
"price.numberOfVouchers": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
@@ -8610,6 +8894,12 @@
|
|||||||
"value": "). See available rates below."
|
"value": "). See available rates below."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"selectRate.alert.reservationPolicies": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "See rate details"
|
||||||
|
}
|
||||||
|
],
|
||||||
"selectRate.availableRooms.all": [
|
"selectRate.availableRooms.all": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
|
|||||||
@@ -231,6 +231,12 @@
|
|||||||
"value": "Erityistoiveet (valinnainen)"
|
"value": "Erityistoiveet (valinnainen)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"ancillaries.label.quantity": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Määrä"
|
||||||
|
}
|
||||||
|
],
|
||||||
"ancillaries.unableToDisplayBreakfastPrices": [
|
"ancillaries.unableToDisplayBreakfastPrices": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -249,6 +255,18 @@
|
|||||||
"value": "Hyväksyn varaus- ja peruutusehdot"
|
"value": "Hyväksyn varaus- ja peruutusehdot"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"booking.alert.extraBeds.text": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Kun varaat yli 2 vieraalle, lisämaksu veloitetaan per henkilö. Katso lisätietoja linkistä."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"booking.alert.extraguests": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Ylimääräinen vieras/vieraat"
|
||||||
|
}
|
||||||
|
],
|
||||||
"booking.approx": [
|
"booking.approx": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -953,6 +971,20 @@
|
|||||||
"value": "Lisää koodi"
|
"value": "Lisää koodi"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.bookingCode.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Lue lisää "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "codeVoucher"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": ":n käytöstä"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.bookingCode.remember": [
|
"bookingWidget.bookingCode.remember": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -1061,6 +1093,20 @@
|
|||||||
"value": "details"
|
"value": "details"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.reward.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Lue lisää "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "reward"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": " varaamisesta"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.reward.rewardNight": [
|
"bookingWidget.reward.rewardNight": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -2514,6 +2560,44 @@
|
|||||||
"value": "Päivämäärät"
|
"value": "Päivämäärät"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.currentCount": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Nykyinen "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": " "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "count"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.decrease": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Vähennä "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.increase": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Lisää "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
"destination.backToCities": [
|
"destination.backToCities": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -7586,6 +7670,58 @@
|
|||||||
"value": "Majoitukset yhteensä"
|
"value": "Majoitukset yhteensä"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"price.numberOfEuroBonusPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB-piste"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB-pistettä"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfEuroBonusPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"price.numberOfScandicPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Piste"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "pistettä"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfScandicPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
"price.numberOfVouchers": [
|
"price.numberOfVouchers": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
@@ -8658,6 +8794,12 @@
|
|||||||
"value": "). Katso saatavilla olevat hinnat alla."
|
"value": "). Katso saatavilla olevat hinnat alla."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"selectRate.alert.reservationPolicies": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Katso hinnan tiedot"
|
||||||
|
}
|
||||||
|
],
|
||||||
"selectRate.availableRooms.all": [
|
"selectRate.availableRooms.all": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
|
|||||||
@@ -231,6 +231,12 @@
|
|||||||
"value": "Spesielle ønsker (valgfritt)"
|
"value": "Spesielle ønsker (valgfritt)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"ancillaries.label.quantity": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Antall"
|
||||||
|
}
|
||||||
|
],
|
||||||
"ancillaries.unableToDisplayBreakfastPrices": [
|
"ancillaries.unableToDisplayBreakfastPrices": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -249,6 +255,18 @@
|
|||||||
"value": "Jeg godtar bestillings- og avbestillingsvilkårene"
|
"value": "Jeg godtar bestillings- og avbestillingsvilkårene"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"booking.alert.extraBeds.text": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Ved bestilling for mer enn 2 gjester vil det påløpe et tilleggsgebyr per person. Se lenke for detaljer."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"booking.alert.extraguests": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Ekstra gjest(er)"
|
||||||
|
}
|
||||||
|
],
|
||||||
"booking.approx": [
|
"booking.approx": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -953,6 +971,16 @@
|
|||||||
"value": "Legg til kode"
|
"value": "Legg til kode"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.bookingCode.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Les mer om bruk av "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "codeVoucher"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.bookingCode.remember": [
|
"bookingWidget.bookingCode.remember": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -1061,6 +1089,16 @@
|
|||||||
"value": "details"
|
"value": "details"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.reward.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Les mer om bestilling med "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "reward"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.reward.rewardNight": [
|
"bookingWidget.reward.rewardNight": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -2510,6 +2548,44 @@
|
|||||||
"value": "Datoer"
|
"value": "Datoer"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.currentCount": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Gjeldende "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": " "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "count"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.decrease": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Reduser "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.increase": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Øk "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
"destination.backToCities": [
|
"destination.backToCities": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -7582,6 +7658,58 @@
|
|||||||
"value": "Totalt antall opphold"
|
"value": "Totalt antall opphold"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"price.numberOfEuroBonusPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB-poeng"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB-poeng"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfEuroBonusPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"price.numberOfScandicPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Poeng"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Poeng"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfScandicPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
"price.numberOfVouchers": [
|
"price.numberOfVouchers": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
@@ -8654,6 +8782,12 @@
|
|||||||
"value": " ). Se tilgjengelige priser nedenfor."
|
"value": " ). Se tilgjengelige priser nedenfor."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"selectRate.alert.reservationPolicies": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Se prisdetaljer"
|
||||||
|
}
|
||||||
|
],
|
||||||
"selectRate.availableRooms.all": [
|
"selectRate.availableRooms.all": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
|
|||||||
@@ -231,6 +231,12 @@
|
|||||||
"value": "Särskilda önskemål (valfritt)"
|
"value": "Särskilda önskemål (valfritt)"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"ancillaries.label.quantity": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Antal"
|
||||||
|
}
|
||||||
|
],
|
||||||
"ancillaries.unableToDisplayBreakfastPrices": [
|
"ancillaries.unableToDisplayBreakfastPrices": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -249,6 +255,18 @@
|
|||||||
"value": "Jag accepterar boknings- och avbokningsvillkoren"
|
"value": "Jag accepterar boknings- och avbokningsvillkoren"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"booking.alert.extraBeds.text": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "När du bokar för fler än 2 gäster tillkommer en extra avgift per person. Se länk för detaljer."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"booking.alert.extraguests": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Extra gäst(er)"
|
||||||
|
}
|
||||||
|
],
|
||||||
"booking.approx": [
|
"booking.approx": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -953,6 +971,16 @@
|
|||||||
"value": "Lägg till kod"
|
"value": "Lägg till kod"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.bookingCode.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Läs mer om att använda "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "codeVoucher"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.bookingCode.remember": [
|
"bookingWidget.bookingCode.remember": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -1061,6 +1089,16 @@
|
|||||||
"value": "details"
|
"value": "details"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"bookingWidget.reward.readMore": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Läs mer om bokning med "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "reward"
|
||||||
|
}
|
||||||
|
],
|
||||||
"bookingWidget.reward.rewardNight": [
|
"bookingWidget.reward.rewardNight": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -2510,6 +2548,44 @@
|
|||||||
"value": "Datum"
|
"value": "Datum"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.currentCount": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Aktuell "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": " "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "count"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.decrease": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Minska "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"designSystem.stepper.ariaLabel.increase": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Öka "
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": 1,
|
||||||
|
"value": "label"
|
||||||
|
}
|
||||||
|
],
|
||||||
"destination.backToCities": [
|
"destination.backToCities": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
@@ -3845,7 +3921,7 @@
|
|||||||
"findMyBooking.findYourStay": [
|
"findMyBooking.findYourStay": [
|
||||||
{
|
{
|
||||||
"type": 0,
|
"type": 0,
|
||||||
"value": "Hitta ditt hotell"
|
"value": "Hitta din bokning"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"findMyBooking.manageBooking": [
|
"findMyBooking.manageBooking": [
|
||||||
@@ -7566,6 +7642,58 @@
|
|||||||
"value": "Totalt antal vistelser"
|
"value": "Totalt antal vistelser"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"price.numberOfEuroBonusPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB-poäng"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "EB-poäng"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfEuroBonusPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"price.numberOfScandicPoints": [
|
||||||
|
{
|
||||||
|
"offset": 0,
|
||||||
|
"options": {
|
||||||
|
"one": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Punkt"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"other": {
|
||||||
|
"value": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Poäng"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"pluralType": "cardinal",
|
||||||
|
"type": 6,
|
||||||
|
"value": "numberOfScandicPoints"
|
||||||
|
}
|
||||||
|
],
|
||||||
"price.numberOfVouchers": [
|
"price.numberOfVouchers": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
@@ -8626,6 +8754,12 @@
|
|||||||
"value": "). Se tillgängliga priser nedan."
|
"value": "). Se tillgängliga priser nedan."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"selectRate.alert.reservationPolicies": [
|
||||||
|
{
|
||||||
|
"type": 0,
|
||||||
|
"value": "Se bokningsvillkor"
|
||||||
|
}
|
||||||
|
],
|
||||||
"selectRate.availableRooms.all": [
|
"selectRate.availableRooms.all": [
|
||||||
{
|
{
|
||||||
"offset": 0,
|
"offset": 0,
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
"@internationalized/date": "^3.8.0",
|
"@internationalized/date": "^3.8.0",
|
||||||
"@netlify/blobs": "^8.1.0",
|
"@netlify/blobs": "^8.1.0",
|
||||||
"@netlify/functions": "^3.0.0",
|
"@netlify/functions": "^3.0.0",
|
||||||
"@netlify/plugin-nextjs": "^5.15.1",
|
"@netlify/plugin-nextjs": "^5.15.7",
|
||||||
"@radix-ui/react-slot": "^1.2.2",
|
"@radix-ui/react-slot": "^1.2.2",
|
||||||
"@scandic-hotels/booking-flow": "workspace:*",
|
"@scandic-hotels/booking-flow": "workspace:*",
|
||||||
"@scandic-hotels/common": "workspace:*",
|
"@scandic-hotels/common": "workspace:*",
|
||||||
@@ -38,7 +38,7 @@
|
|||||||
"@scandic-hotels/tracking": "workspace:*",
|
"@scandic-hotels/tracking": "workspace:*",
|
||||||
"@scandic-hotels/trpc": "workspace:*",
|
"@scandic-hotels/trpc": "workspace:*",
|
||||||
"@sentry/nextjs": "^10.33.0",
|
"@sentry/nextjs": "^10.33.0",
|
||||||
"@swc/plugin-formatjs": "^3.2.2",
|
"@swc/plugin-formatjs": "^8.1.0",
|
||||||
"@t3-oss/env-nextjs": "^0.13.4",
|
"@t3-oss/env-nextjs": "^0.13.4",
|
||||||
"@tanstack/react-query": "^5.75.5",
|
"@tanstack/react-query": "^5.75.5",
|
||||||
"@tanstack/react-query-devtools": "^5.75.5",
|
"@tanstack/react-query-devtools": "^5.75.5",
|
||||||
@@ -66,12 +66,12 @@
|
|||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"md5": "^2.3.0",
|
"md5": "^2.3.0",
|
||||||
"motion": "^12.10.0",
|
"motion": "^12.10.0",
|
||||||
"next": "16.0.10",
|
"next": "16.1.6",
|
||||||
"next-auth": "5.0.0-beta.29",
|
"next-auth": "5.0.0-beta.29",
|
||||||
"react": "19.2.1",
|
"react": "19.2.4",
|
||||||
"react-aria-components": "1.8.0",
|
"react-aria-components": "1.8.0",
|
||||||
"react-day-picker": "^9.6.7",
|
"react-day-picker": "^9.6.7",
|
||||||
"react-dom": "19.2.1",
|
"react-dom": "19.2.4",
|
||||||
"react-feather": "^2.0.10",
|
"react-feather": "^2.0.10",
|
||||||
"react-focus-lock": "^2.13.6",
|
"react-focus-lock": "^2.13.6",
|
||||||
"react-hook-form": "^7.56.2",
|
"react-hook-form": "^7.56.2",
|
||||||
|
|||||||
@@ -13,22 +13,20 @@ import { MyStaySkeleton } from "@/components/HotelReservation/MyStay/myStaySkele
|
|||||||
import { MyStayContext } from "@/contexts/MyStay"
|
import { MyStayContext } from "@/contexts/MyStay"
|
||||||
|
|
||||||
import type { Lang } from "@scandic-hotels/common/constants/language"
|
import type { Lang } from "@scandic-hotels/common/constants/language"
|
||||||
import type {
|
import type { BookingConfirmation } from "@scandic-hotels/trpc/types/bookingConfirmation"
|
||||||
BookingConfirmation,
|
|
||||||
BookingConfirmationSchema,
|
|
||||||
} from "@scandic-hotels/trpc/types/bookingConfirmation"
|
|
||||||
import type { RoomCategories } from "@scandic-hotels/trpc/types/hotel"
|
import type { RoomCategories } from "@scandic-hotels/trpc/types/hotel"
|
||||||
import type { CreditCard } from "@scandic-hotels/trpc/types/user"
|
import type { CreditCard } from "@scandic-hotels/trpc/types/user"
|
||||||
|
|
||||||
import type { Packages } from "@/types/components/myPages/myStay/ancillaries"
|
import type { Packages } from "@/types/components/myPages/myStay/ancillaries"
|
||||||
import type { MyStayStore } from "@/types/contexts/my-stay"
|
import type { MyStayStore } from "@/types/contexts/my-stay"
|
||||||
|
import type { getLinkedReservations } from "@/lib/trpc/memoizedRequests"
|
||||||
|
|
||||||
interface MyStayProviderProps {
|
interface MyStayProviderProps {
|
||||||
bookingConfirmation: BookingConfirmation
|
bookingConfirmation: BookingConfirmation
|
||||||
breakfastPackages: Packages | null
|
breakfastPackages: Packages | null
|
||||||
isLoggedIn?: boolean
|
isLoggedIn?: boolean
|
||||||
lang: Lang
|
lang: Lang
|
||||||
linkedReservationsPromise: Promise<BookingConfirmationSchema[]>
|
linkedReservationsPromise: ReturnType<typeof getLinkedReservations>
|
||||||
refId: string
|
refId: string
|
||||||
roomCategories: RoomCategories
|
roomCategories: RoomCategories
|
||||||
savedCreditCards: CreditCard[] | null
|
savedCreditCards: CreditCard[] | null
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 847 KiB After Width: | Height: | Size: 301 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 4.4 MiB After Width: | Height: | Size: 208 KiB |
@@ -212,6 +212,12 @@ describe("getTimeAgoText", () => {
|
|||||||
|
|
||||||
expect(result).toBe("")
|
expect(result).toBe("")
|
||||||
})
|
})
|
||||||
|
it("should return Today for todays date", () => {
|
||||||
|
const todaysDate = dt().format("YYYY-MM-DD")
|
||||||
|
const result = getTimeAgoText(todaysDate, mockIntl)
|
||||||
|
|
||||||
|
expect(result).toBe("Today")
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe("boundary transitions", () => {
|
describe("boundary transitions", () => {
|
||||||
|
|||||||
@@ -22,6 +22,13 @@ export function getTimeAgoText(checkoutDate: string, intl: IntlShape): string {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (daysDiff === 0) {
|
||||||
|
return intl.formatMessage({
|
||||||
|
id: "nextStay.today",
|
||||||
|
defaultMessage: "Today",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
if (daysDiff <= 30) {
|
if (daysDiff <= 30) {
|
||||||
// 1-30 days
|
// 1-30 days
|
||||||
return intl.formatMessage(
|
return intl.formatMessage(
|
||||||
|
|||||||
@@ -198,10 +198,15 @@ export function Room({
|
|||||||
{isFlexBooking || isChangeBooking ? (
|
{isFlexBooking || isChangeBooking ? (
|
||||||
<li className={styles.listItem}>
|
<li className={styles.listItem}>
|
||||||
<p className={styles.label}>
|
<p className={styles.label}>
|
||||||
{intl.formatMessage({
|
{isChangeBooking
|
||||||
id: "booking.changeOrCancel",
|
? intl.formatMessage({
|
||||||
defaultMessage: "Change or cancel",
|
id: "booking.changeTitle",
|
||||||
})}
|
defaultMessage: "Change",
|
||||||
|
})
|
||||||
|
: intl.formatMessage({
|
||||||
|
id: "booking.changeOrCancel",
|
||||||
|
defaultMessage: "Change or cancel",
|
||||||
|
})}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
{intl.formatMessage(
|
{intl.formatMessage(
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@
|
|||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
padding: var(--Space-x2) var(--Space-x3);
|
padding: var(--Space-x2) var(--Space-x3);
|
||||||
position: fixed;
|
position: fixed;
|
||||||
top: calc(140px + max(var(--sitewide-alert-sticky-height), 25px));
|
top: calc(140px + max(var(--sitewide-alert-sticky-height), 15px));
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: calc(100% - 200px);
|
height: calc(100% - 200px);
|
||||||
z-index: 10010;
|
z-index: 10010;
|
||||||
|
|||||||
+4
-6
@@ -1,13 +1,11 @@
|
|||||||
import Footnote from "@scandic-hotels/design-system/Footnote"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import styles from "./list.module.css"
|
import styles from "./list.module.css"
|
||||||
|
|
||||||
export default function Label({ children }: React.PropsWithChildren) {
|
export default function Label({ children }: React.PropsWithChildren) {
|
||||||
return (
|
return (
|
||||||
<li className={styles.label}>
|
<Typography variant="Title/Overline/sm">
|
||||||
<Footnote color="uiTextPlaceholder" textTransform="uppercase">
|
<li className={styles.label}>{children}</li>
|
||||||
{children}
|
</Typography>
|
||||||
</Footnote>
|
|
||||||
</li>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-1
@@ -5,5 +5,6 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.label {
|
.label {
|
||||||
padding: 0 var(--Space-x1);
|
padding: 0 var(--Space-x1) var(--Space-x05);
|
||||||
|
color: var(--Text-Tertiary);
|
||||||
}
|
}
|
||||||
|
|||||||
+16
-17
@@ -6,7 +6,6 @@ import { useIntl } from "react-intl"
|
|||||||
import { useDebounceValue } from "usehooks-ts"
|
import { useDebounceValue } from "usehooks-ts"
|
||||||
|
|
||||||
import { Divider } from "@scandic-hotels/design-system/Divider"
|
import { Divider } from "@scandic-hotels/design-system/Divider"
|
||||||
import Footnote from "@scandic-hotels/design-system/Footnote"
|
|
||||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
import { trpc } from "@scandic-hotels/trpc/client"
|
import { trpc } from "@scandic-hotels/trpc/client"
|
||||||
@@ -192,16 +191,14 @@ export default function SearchList({
|
|||||||
{typeFilteredSearchHistory && typeFilteredSearchHistory.length > 0 && (
|
{typeFilteredSearchHistory && typeFilteredSearchHistory.length > 0 && (
|
||||||
<>
|
<>
|
||||||
<Divider className={styles.noResultsDivider} />
|
<Divider className={styles.noResultsDivider} />
|
||||||
<Footnote
|
<Typography variant="Title/Overline/sm">
|
||||||
className={styles.text}
|
<p className={styles.text}>
|
||||||
color="uiTextPlaceholder"
|
{intl.formatMessage({
|
||||||
textTransform="uppercase"
|
id: "bookingWidget.searchList.latestSearches",
|
||||||
>
|
defaultMessage: "Latest searches",
|
||||||
{intl.formatMessage({
|
})}
|
||||||
id: "bookingWidget.searchList.latestSearches",
|
</p>
|
||||||
defaultMessage: "Latest searches",
|
</Typography>
|
||||||
})}
|
|
||||||
</Footnote>
|
|
||||||
<List
|
<List
|
||||||
getItemProps={getItemProps}
|
getItemProps={getItemProps}
|
||||||
highlightedIndex={highlightedIndex}
|
highlightedIndex={highlightedIndex}
|
||||||
@@ -226,12 +223,14 @@ export default function SearchList({
|
|||||||
if (displaySearchHistory) {
|
if (displaySearchHistory) {
|
||||||
return (
|
return (
|
||||||
<Dialog getMenuProps={getMenuProps}>
|
<Dialog getMenuProps={getMenuProps}>
|
||||||
<Footnote color="uiTextPlaceholder" textTransform="uppercase">
|
<Typography variant="Title/Overline/sm">
|
||||||
{intl.formatMessage({
|
<p className={styles.text}>
|
||||||
id: "bookingWidget.searchList.latestSearches",
|
{intl.formatMessage({
|
||||||
defaultMessage: "Latest searches",
|
id: "bookingWidget.searchList.latestSearches",
|
||||||
})}
|
defaultMessage: "Latest searches",
|
||||||
</Footnote>
|
})}
|
||||||
|
</p>
|
||||||
|
</Typography>
|
||||||
<List
|
<List
|
||||||
getItemProps={getItemProps}
|
getItemProps={getItemProps}
|
||||||
highlightedIndex={highlightedIndex}
|
highlightedIndex={highlightedIndex}
|
||||||
|
|||||||
+2
@@ -33,6 +33,8 @@
|
|||||||
|
|
||||||
.text {
|
.text {
|
||||||
padding: 0 var(--Space-x1);
|
padding: 0 var(--Space-x1);
|
||||||
|
color: var(--Text-Tertiary);
|
||||||
|
white-space: normal;
|
||||||
}
|
}
|
||||||
.textPlaceholderColor {
|
.textPlaceholderColor {
|
||||||
color: var(--UI-Text-Placeholder);
|
color: var(--UI-Text-Placeholder);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { cx } from "class-variance-authority"
|
|||||||
import { useSearchParams } from "next/navigation"
|
import { useSearchParams } from "next/navigation"
|
||||||
import { use, useEffect, useRef, useState } from "react"
|
import { use, useEffect, useRef, useState } from "react"
|
||||||
import { FormProvider, useForm } from "react-hook-form"
|
import { FormProvider, useForm } from "react-hook-form"
|
||||||
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { dt } from "@scandic-hotels/common/dt"
|
import { dt } from "@scandic-hotels/common/dt"
|
||||||
import { useScrollLock } from "@scandic-hotels/common/hooks/useScrollLock"
|
import { useScrollLock } from "@scandic-hotels/common/hooks/useScrollLock"
|
||||||
@@ -12,7 +13,7 @@ import useStickyPosition from "@scandic-hotels/common/hooks/useStickyPosition"
|
|||||||
import { StickyElementNameEnum } from "@scandic-hotels/common/stores/sticky-position"
|
import { StickyElementNameEnum } from "@scandic-hotels/common/stores/sticky-position"
|
||||||
import { debounce } from "@scandic-hotels/common/utils/debounce"
|
import { debounce } from "@scandic-hotels/common/utils/debounce"
|
||||||
import isValidJson from "@scandic-hotels/common/utils/isValidJson"
|
import isValidJson from "@scandic-hotels/common/utils/isValidJson"
|
||||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
import { IconButton } from "@scandic-hotels/design-system/IconButton"
|
||||||
import { trpc } from "@scandic-hotels/trpc/client"
|
import { trpc } from "@scandic-hotels/trpc/client"
|
||||||
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
|
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
|
||||||
|
|
||||||
@@ -66,6 +67,7 @@ export default function BookingWidgetClient({
|
|||||||
autoLock: false,
|
autoLock: false,
|
||||||
})
|
})
|
||||||
const shouldFetchAutoComplete = !!data.hotelId || !!data.city
|
const shouldFetchAutoComplete = !!data.hotelId || !!data.city
|
||||||
|
const intl = useIntl()
|
||||||
|
|
||||||
const { data: destinationsData, isPending } =
|
const { data: destinationsData, isPending } =
|
||||||
trpc.autocomplete.destinations.useQuery(
|
trpc.autocomplete.destinations.useQuery(
|
||||||
@@ -257,13 +259,17 @@ export default function BookingWidgetClient({
|
|||||||
/>
|
/>
|
||||||
<div className={styles.backdrop} onClick={closeMobileSearch} />
|
<div className={styles.backdrop} onClick={closeMobileSearch} />
|
||||||
<div className={formContainerClassNames}>
|
<div className={formContainerClassNames}>
|
||||||
<button
|
<IconButton
|
||||||
className={styles.close}
|
className={styles.close}
|
||||||
onClick={closeMobileSearch}
|
variant="Muted"
|
||||||
type="button"
|
emphasis
|
||||||
>
|
aria-label={intl.formatMessage({
|
||||||
<MaterialIcon icon="close" />
|
id: "common.close",
|
||||||
</button>
|
defaultMessage: "Close",
|
||||||
|
})}
|
||||||
|
onPress={closeMobileSearch}
|
||||||
|
iconName="close"
|
||||||
|
/>
|
||||||
<Form
|
<Form
|
||||||
type={type}
|
type={type}
|
||||||
onClose={closeMobileSearch}
|
onClose={closeMobileSearch}
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export default function DatePickerRangeDesktop({
|
|||||||
range_start: styles.rangeStart,
|
range_start: styles.rangeStart,
|
||||||
root: `${classNames.root} ${styles.container}`,
|
root: `${classNames.root} ${styles.container}`,
|
||||||
week: styles.week,
|
week: styles.week,
|
||||||
weekday: `${classNames.weekday} ${styles.weekDay}`,
|
weekday: styles.weekDay,
|
||||||
nav: `${classNames.nav} ${styles.nav}`,
|
nav: `${classNames.nav} ${styles.nav}`,
|
||||||
button_next: `${classNames.button_next} ${styles.button_next}`,
|
button_next: `${classNames.button_next} ${styles.button_next}`,
|
||||||
button_previous: `${classNames.button_previous} ${styles.button_previous}`,
|
button_previous: `${classNames.button_previous} ${styles.button_previous}`,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { useIntl } from "react-intl"
|
|||||||
import { Lang } from "@scandic-hotels/common/constants/language"
|
import { Lang } from "@scandic-hotels/common/constants/language"
|
||||||
import { dt } from "@scandic-hotels/common/dt"
|
import { dt } from "@scandic-hotels/common/dt"
|
||||||
import { Button } from "@scandic-hotels/design-system/Button"
|
import { Button } from "@scandic-hotels/design-system/Button"
|
||||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
import { IconButton } from "@scandic-hotels/design-system/IconButton"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import useLang from "../../../../hooks/useLang"
|
import useLang from "../../../../hooks/useLang"
|
||||||
@@ -72,9 +72,17 @@ export default function DatePickerRangeMobile({
|
|||||||
return (
|
return (
|
||||||
<div className={styles.container} ref={monthsRef}>
|
<div className={styles.container} ref={monthsRef}>
|
||||||
<header className={styles.header}>
|
<header className={styles.header}>
|
||||||
<button className={styles.close} onClick={close} type="button">
|
<IconButton
|
||||||
<MaterialIcon icon="close" />
|
className={styles.close}
|
||||||
</button>
|
variant="Muted"
|
||||||
|
emphasis
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id: "common.close",
|
||||||
|
defaultMessage: "Close",
|
||||||
|
})}
|
||||||
|
onPress={close}
|
||||||
|
iconName="close"
|
||||||
|
/>
|
||||||
</header>
|
</header>
|
||||||
<DayPicker
|
<DayPicker
|
||||||
classNames={{
|
classNames={{
|
||||||
@@ -90,7 +98,7 @@ export default function DatePickerRangeMobile({
|
|||||||
range_start: styles.rangeStart,
|
range_start: styles.rangeStart,
|
||||||
root: `${classNames.root} ${styles.root}`,
|
root: `${classNames.root} ${styles.root}`,
|
||||||
week: styles.week,
|
week: styles.week,
|
||||||
weekday: `${classNames.weekday} ${styles.weekDay}`,
|
weekday: styles.weekDay,
|
||||||
}}
|
}}
|
||||||
disabled={[
|
disabled={[
|
||||||
{ from: lastDayOfPreviousMonth, to: yesterday },
|
{ from: lastDayOfPreviousMonth, to: yesterday },
|
||||||
|
|||||||
+15
-14
@@ -20,12 +20,12 @@ div.months {
|
|||||||
td.day,
|
td.day,
|
||||||
td.rangeEnd,
|
td.rangeEnd,
|
||||||
td.rangeStart {
|
td.rangeStart {
|
||||||
font-family: var(--typography-Body-Bold-fontFamily);
|
font-family: var(--Body-Paragraph-Font-family), var(--Body-Paragraph-Font-fallback);
|
||||||
font-size: var(--typography-Body-Bold-fontSize);
|
font-size: var(--Body-Paragraph-Size);
|
||||||
font-weight: 500;
|
font-weight: var(--Body-Paragraph-Font-weight-2);
|
||||||
letter-spacing: var(--typography-Body-Bold-letterSpacing);
|
letter-spacing: var(--Body-Paragraph-Letter-spacing);
|
||||||
line-height: var(--typography-Body-Bold-lineHeight);
|
line-height: 1.5;
|
||||||
text-decoration: var(--typography-Body-Bold-textDecoration);
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
td.rangeEnd,
|
td.rangeEnd,
|
||||||
@@ -92,14 +92,15 @@ td.day[data-outside="true"] ~ td.day[data-disabled="true"] button.dayButton,
|
|||||||
}
|
}
|
||||||
|
|
||||||
.weekDay {
|
.weekDay {
|
||||||
color: var(--UI-Text-Placeholder);
|
color: var(--Text-Tertiary);
|
||||||
font-family: var(--typography-Footnote-Labels-fontFamily);
|
font-family: var(--Title-Overline-sm-Font-family), var(--Title-Overline-sm-Font-fallback);
|
||||||
font-size: var(--typography-Footnote-Labels-fontSize);
|
font-size: var(--Title-Overline-sm-Size);
|
||||||
font-weight: var(--typography-Footnote-Labels-fontWeight);
|
font-style: normal;
|
||||||
letter-spacing: var(--typography-Footnote-Labels-letterSpacing);
|
font-weight: var(--Title-Overline-sm-Font-weight);
|
||||||
line-height: var(--typography-Footnote-Labels-lineHeight);
|
line-height: 1.5;
|
||||||
text-decoration: var(--typography-Footnote-Labels-textDecoration);
|
letter-spacing: var(--Title-Overline-sm-Letter-spacing);
|
||||||
text-transform: uppercase;
|
text-transform: var(--Title-Overline-sm-Text-Transform);
|
||||||
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.footer {
|
.footer {
|
||||||
|
|||||||
+15
-15
@@ -97,12 +97,12 @@ div.months {
|
|||||||
td.day,
|
td.day,
|
||||||
td.rangeEnd,
|
td.rangeEnd,
|
||||||
td.rangeStart {
|
td.rangeStart {
|
||||||
font-family: var(--typography-Body-Bold-fontFamily);
|
font-family: var(--Body-Paragraph-Font-family), var(--Body-Paragraph-Font-fallback);
|
||||||
font-size: var(--typography-Body-Bold-fontSize);
|
font-size: var(--Body-Paragraph-Size);
|
||||||
font-weight: 500;
|
font-weight: var(--Body-Paragraph-Font-weight-2);
|
||||||
letter-spacing: var(--typography-Body-Bold-letterSpacing);
|
letter-spacing: var(--Body-Paragraph-Letter-spacing);
|
||||||
line-height: var(--typography-Body-Bold-lineHeight);
|
line-height: 1.5;
|
||||||
text-decoration: var(--typography-Body-Bold-textDecoration);
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
td.rangeEnd,
|
td.rangeEnd,
|
||||||
@@ -165,15 +165,15 @@ td.day[data-outside="true"] ~ td.day[data-disabled="true"] button.dayButton,
|
|||||||
}
|
}
|
||||||
|
|
||||||
.weekDay {
|
.weekDay {
|
||||||
color: var(--Base-Text-Medium-contrast);
|
color: var(--Text-Tertiary);
|
||||||
opacity: 1;
|
font-family: var(--Title-Overline-sm-Font-family), var(--Title-Overline-sm-Font-fallback);
|
||||||
font-family: var(--typography-Caption-Labels-fontFamily);
|
font-size: var(--Title-Overline-sm-Size);
|
||||||
font-size: var(--typography-Caption-Labels-fontSize);
|
font-style: normal;
|
||||||
font-weight: var(--typography-Caption-Labels-fontWeight);
|
font-weight: var(--Title-Overline-sm-Font-weight);
|
||||||
letter-spacing: var(--typography-Caption-Labels-letterSpacing);
|
line-height: 1.5;
|
||||||
line-height: var(--typography-Caption-Labels-lineHeight);
|
letter-spacing: var(--Title-Overline-sm-Letter-spacing);
|
||||||
text-decoration: var(--typography-Caption-Labels-textDecoration);
|
text-transform: var(--Title-Overline-sm-Text-Transform);
|
||||||
text-transform: uppercase;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 1367px) {
|
@media screen and (min-width: 1367px) {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import { useFormContext, useWatch } from "react-hook-form"
|
|||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import { Button } from "@scandic-hotels/design-system/Button"
|
import { Button } from "@scandic-hotels/design-system/Button"
|
||||||
|
import { IconButton } from "@scandic-hotels/design-system/IconButton"
|
||||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
|
import { SEARCH_TYPE_REDEMPTION } from "@scandic-hotels/trpc/constants/booking"
|
||||||
@@ -114,9 +115,17 @@ export default function GuestsRoomsPickerDialog({
|
|||||||
<>
|
<>
|
||||||
<section className={styles.contentWrapper}>
|
<section className={styles.contentWrapper}>
|
||||||
<header className={styles.header}>
|
<header className={styles.header}>
|
||||||
<button type="button" className={styles.close} onClick={onClose}>
|
<IconButton
|
||||||
<MaterialIcon icon="close" />
|
className={styles.close}
|
||||||
</button>
|
variant="Muted"
|
||||||
|
emphasis
|
||||||
|
aria-label={intl.formatMessage({
|
||||||
|
id: "common.close",
|
||||||
|
defaultMessage: "Close",
|
||||||
|
})}
|
||||||
|
onPress={onClose}
|
||||||
|
iconName="close"
|
||||||
|
/>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div className={styles.contentContainer}>
|
<div className={styles.contentContainer}>
|
||||||
|
|||||||
+8
-10
@@ -4,9 +4,8 @@ import { useIntl } from "react-intl"
|
|||||||
|
|
||||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||||
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
||||||
import Footnote from "@scandic-hotels/design-system/Footnote"
|
|
||||||
import Checkbox from "@scandic-hotels/design-system/Form/Checkbox"
|
import Checkbox from "@scandic-hotels/design-system/Form/Checkbox"
|
||||||
import Link from "@scandic-hotels/design-system/OldDSLink"
|
import { TextLink } from "@scandic-hotels/design-system/TextLink"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import { useBookingFlowConfig } from "../../../../../bookingFlowConfig/bookingFlowConfigContext"
|
import { useBookingFlowConfig } from "../../../../../bookingFlowConfig/bookingFlowConfigContext"
|
||||||
@@ -83,8 +82,8 @@ export default function JoinScandicFriendsCard({
|
|||||||
</Typography>
|
</Typography>
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
|
|
||||||
<div className={styles.terms}>
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
<Footnote color="uiTextPlaceholder">
|
<p className={styles.terms}>
|
||||||
{intl.formatMessage(
|
{intl.formatMessage(
|
||||||
{
|
{
|
||||||
id: "enterDetails.joinScandicFriendsCard.terms",
|
id: "enterDetails.joinScandicFriendsCard.terms",
|
||||||
@@ -93,19 +92,18 @@ export default function JoinScandicFriendsCard({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
termsAndConditionsLink: (str) => (
|
termsAndConditionsLink: (str) => (
|
||||||
<Link
|
<TextLink
|
||||||
textDecoration="underline"
|
typography="Link/sm"
|
||||||
size="tiny"
|
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href={routes.membershipTermsAndConditions[lang]}
|
href={routes.membershipTermsAndConditions[lang]}
|
||||||
>
|
>
|
||||||
{str}
|
{str}
|
||||||
</Link>
|
</TextLink>
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
</Footnote>
|
</p>
|
||||||
</div>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
.terms {
|
.terms {
|
||||||
grid-area: terms;
|
grid-area: terms;
|
||||||
|
color: var(--Text-Secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
|
|||||||
+8
-10
@@ -4,9 +4,8 @@ import { useIntl } from "react-intl"
|
|||||||
|
|
||||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||||
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
||||||
import Footnote from "@scandic-hotels/design-system/Footnote"
|
|
||||||
import Checkbox from "@scandic-hotels/design-system/Form/Checkbox"
|
import Checkbox from "@scandic-hotels/design-system/Form/Checkbox"
|
||||||
import Link from "@scandic-hotels/design-system/OldDSLink"
|
import { TextLink } from "@scandic-hotels/design-system/TextLink"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import { useBookingFlowConfig } from "../../../../../bookingFlowConfig/bookingFlowConfigContext"
|
import { useBookingFlowConfig } from "../../../../../bookingFlowConfig/bookingFlowConfigContext"
|
||||||
@@ -97,8 +96,8 @@ export function PartnerSASJoinScandicFriendsCard({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.terms}>
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
<Footnote color="uiTextPlaceholder">
|
<p className={styles.terms}>
|
||||||
{intl.formatMessage(
|
{intl.formatMessage(
|
||||||
{
|
{
|
||||||
id: "enterDetails.joinScandicFriendsCard.terms",
|
id: "enterDetails.joinScandicFriendsCard.terms",
|
||||||
@@ -107,19 +106,18 @@ export function PartnerSASJoinScandicFriendsCard({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
termsAndConditionsLink: (str) => (
|
termsAndConditionsLink: (str) => (
|
||||||
<Link
|
<TextLink
|
||||||
textDecoration="underline"
|
typography="Link/sm"
|
||||||
size="tiny"
|
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href={routes.membershipTermsAndConditions[lang]}
|
href={routes.membershipTermsAndConditions[lang]}
|
||||||
>
|
>
|
||||||
{str}
|
{str}
|
||||||
</Link>
|
</TextLink>
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
</Footnote>
|
</p>
|
||||||
</div>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
.terms {
|
.terms {
|
||||||
grid-area: terms;
|
grid-area: terms;
|
||||||
|
color: var(--Text-Secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
|
|||||||
+8
-10
@@ -4,10 +4,9 @@ import { useIntl } from "react-intl"
|
|||||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||||
import { useLazyPathname } from "@scandic-hotels/common/hooks/useLazyPathname"
|
import { useLazyPathname } from "@scandic-hotels/common/hooks/useLazyPathname"
|
||||||
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
||||||
import Footnote from "@scandic-hotels/design-system/Footnote"
|
|
||||||
import Checkbox from "@scandic-hotels/design-system/Form/Checkbox"
|
import Checkbox from "@scandic-hotels/design-system/Form/Checkbox"
|
||||||
import { LoginButton } from "@scandic-hotels/design-system/LoginButton"
|
import { LoginButton } from "@scandic-hotels/design-system/LoginButton"
|
||||||
import Link from "@scandic-hotels/design-system/OldDSLink"
|
import { TextLink } from "@scandic-hotels/design-system/TextLink"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
import { trackEvent } from "@scandic-hotels/tracking/base"
|
import { trackEvent } from "@scandic-hotels/tracking/base"
|
||||||
import { trackLoginClick } from "@scandic-hotels/tracking/navigation"
|
import { trackLoginClick } from "@scandic-hotels/tracking/navigation"
|
||||||
@@ -101,8 +100,8 @@ export function JoinScandicFriendsCard({ name = "join" }: Props) {
|
|||||||
})}
|
})}
|
||||||
</LoginButton>
|
</LoginButton>
|
||||||
|
|
||||||
<div className={styles.terms}>
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
<Footnote color="uiTextPlaceholder">
|
<p className={styles.terms}>
|
||||||
{intl.formatMessage(
|
{intl.formatMessage(
|
||||||
{
|
{
|
||||||
id: "enterDetails.joinScandicFriendsCard.terms",
|
id: "enterDetails.joinScandicFriendsCard.terms",
|
||||||
@@ -111,19 +110,18 @@ export function JoinScandicFriendsCard({ name = "join" }: Props) {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
termsAndConditionsLink: (str) => (
|
termsAndConditionsLink: (str) => (
|
||||||
<Link
|
<TextLink
|
||||||
textDecoration="underline"
|
typography="Link/sm"
|
||||||
size="tiny"
|
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href={routes.membershipTermsAndConditions[lang]}
|
href={routes.membershipTermsAndConditions[lang]}
|
||||||
>
|
>
|
||||||
{str}
|
{str}
|
||||||
</Link>
|
</TextLink>
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
</Footnote>
|
</p>
|
||||||
</div>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -34,6 +34,7 @@
|
|||||||
|
|
||||||
.terms {
|
.terms {
|
||||||
grid-area: terms;
|
grid-area: terms;
|
||||||
|
color: var(--Text-Secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
|
|||||||
+8
-10
@@ -4,9 +4,8 @@ import { useIntl } from "react-intl"
|
|||||||
|
|
||||||
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
import { CurrencyEnum } from "@scandic-hotels/common/constants/currency"
|
||||||
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
import { formatPrice } from "@scandic-hotels/common/utils/numberFormatting"
|
||||||
import Footnote from "@scandic-hotels/design-system/Footnote"
|
|
||||||
import Checkbox from "@scandic-hotels/design-system/Form/Checkbox"
|
import Checkbox from "@scandic-hotels/design-system/Form/Checkbox"
|
||||||
import Link from "@scandic-hotels/design-system/OldDSLink"
|
import { TextLink } from "@scandic-hotels/design-system/TextLink"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
import { trpc } from "@scandic-hotels/trpc/client"
|
import { trpc } from "@scandic-hotels/trpc/client"
|
||||||
|
|
||||||
@@ -94,8 +93,8 @@ export function PartnerSASJoinScandicFriendsCard({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={styles.terms}>
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
<Footnote color="uiTextPlaceholder">
|
<p className={styles.terms}>
|
||||||
{intl.formatMessage(
|
{intl.formatMessage(
|
||||||
{
|
{
|
||||||
id: "enterDetails.joinScandicFriendsCard.terms",
|
id: "enterDetails.joinScandicFriendsCard.terms",
|
||||||
@@ -104,19 +103,18 @@ export function PartnerSASJoinScandicFriendsCard({
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
termsAndConditionsLink: (str) => (
|
termsAndConditionsLink: (str) => (
|
||||||
<Link
|
<TextLink
|
||||||
textDecoration="underline"
|
typography="Link/sm"
|
||||||
size="tiny"
|
|
||||||
target="_blank"
|
target="_blank"
|
||||||
href={routes.membershipTermsAndConditions[lang]}
|
href={routes.membershipTermsAndConditions[lang]}
|
||||||
>
|
>
|
||||||
{str}
|
{str}
|
||||||
</Link>
|
</TextLink>
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
</Footnote>
|
</p>
|
||||||
</div>
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -31,6 +31,7 @@
|
|||||||
|
|
||||||
.terms {
|
.terms {
|
||||||
grid-area: terms;
|
grid-area: terms;
|
||||||
|
color: var(--Text-Secondary);
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 768px) {
|
@media screen and (min-width: 768px) {
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user