Compare commits
27 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 089bbe7c4f | |||
| c62999879f | |||
| 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,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
@@ -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);
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
import { notFound } from "next/navigation"
|
import { notFound } from "next/navigation"
|
||||||
import { Suspense } from "react"
|
import { Suspense } from "react"
|
||||||
|
|
||||||
|
import { LinkChips } from "@scandic-hotels/design-system/LinkChips"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
|
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
|
||||||
|
|
||||||
import { getCampaignOverviewPage } from "@/lib/trpc/memoizedRequests"
|
import { getCampaignOverviewPage } from "@/lib/trpc/memoizedRequests"
|
||||||
|
|
||||||
import { TopCampaign } from "@/components/ContentType/CampaignOverviewPage/TopCampaign"
|
import { TopCampaign } from "@/components/ContentType/CampaignOverviewPage/TopCampaign"
|
||||||
import LinkChips from "@/components/TempDesignSystem/LinkChips"
|
|
||||||
|
|
||||||
import Blocks from "./Blocks"
|
import Blocks from "./Blocks"
|
||||||
import CampaignOverviewPageSkeleton from "./CampaignOverviewPageSkeleton"
|
import CampaignOverviewPageSkeleton from "./CampaignOverviewPageSkeleton"
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import {
|
|||||||
} from "@scandic-hotels/design-system/Breadcrumbs"
|
} from "@scandic-hotels/design-system/Breadcrumbs"
|
||||||
import ButtonLink from "@scandic-hotels/design-system/ButtonLink"
|
import ButtonLink from "@scandic-hotels/design-system/ButtonLink"
|
||||||
import Image from "@scandic-hotels/design-system/Image"
|
import Image from "@scandic-hotels/design-system/Image"
|
||||||
|
import { LinkChips } from "@scandic-hotels/design-system/LinkChips"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
|
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
|
||||||
|
|
||||||
@@ -17,7 +18,6 @@ import { Breadcrumbs } from "@/components/Breadcrumbs"
|
|||||||
import HeaderDynamicContent from "@/components/Headers/DynamicContent"
|
import HeaderDynamicContent from "@/components/Headers/DynamicContent"
|
||||||
import { HeroVideo } from "@/components/HeroVideo"
|
import { HeroVideo } from "@/components/HeroVideo"
|
||||||
import MeetingPackageWidget from "@/components/MeetingPackageWidget"
|
import MeetingPackageWidget from "@/components/MeetingPackageWidget"
|
||||||
import LinkChips from "@/components/TempDesignSystem/LinkChips"
|
|
||||||
|
|
||||||
import styles from "./collectionPage.module.css"
|
import styles from "./collectionPage.module.css"
|
||||||
|
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { Suspense } from "react"
|
|||||||
|
|
||||||
import { BreadcrumbsSkeleton } from "@scandic-hotels/design-system/Breadcrumbs"
|
import { BreadcrumbsSkeleton } from "@scandic-hotels/design-system/Breadcrumbs"
|
||||||
import ButtonLink from "@scandic-hotels/design-system/ButtonLink"
|
import ButtonLink from "@scandic-hotels/design-system/ButtonLink"
|
||||||
|
import { LinkChips } from "@scandic-hotels/design-system/LinkChips"
|
||||||
import { Typography } from "@scandic-hotels/design-system/Typography"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
|
import { TrackingSDK } from "@scandic-hotels/tracking/TrackingSDK"
|
||||||
|
|
||||||
@@ -16,7 +17,6 @@ import { HeroVideo } from "@/components/HeroVideo"
|
|||||||
import Sidebar from "@/components/Sidebar"
|
import Sidebar from "@/components/Sidebar"
|
||||||
import SidebarSkeleton from "@/components/Sidebar/SidebarSkeleton"
|
import SidebarSkeleton from "@/components/Sidebar/SidebarSkeleton"
|
||||||
import StickyMeetingPackageWidget from "@/components/StickyMeetingPackageWidget"
|
import StickyMeetingPackageWidget from "@/components/StickyMeetingPackageWidget"
|
||||||
import LinkChips from "@/components/TempDesignSystem/LinkChips"
|
|
||||||
|
|
||||||
import styles from "./contentPage.module.css"
|
import styles from "./contentPage.module.css"
|
||||||
|
|
||||||
|
|||||||
+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) {
|
||||||
|
|||||||
@@ -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
@@ -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"
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
"use client"
|
|
||||||
|
|
||||||
import { ChipLink } from "@scandic-hotels/design-system/ChipLink"
|
|
||||||
import { Chips } from "@scandic-hotels/design-system/Chips"
|
|
||||||
import { MaterialIcon } from "@scandic-hotels/design-system/Icons/MaterialIcon"
|
|
||||||
|
|
||||||
interface LinkChipsProps {
|
|
||||||
chips: {
|
|
||||||
url: string
|
|
||||||
title: string
|
|
||||||
}[]
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function LinkChips({ chips }: LinkChipsProps) {
|
|
||||||
if (!chips.length) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Chips>
|
|
||||||
{chips.map(({ title, url }) => (
|
|
||||||
<ChipLink key={`${title}-${url}`} href={url}>
|
|
||||||
{title}
|
|
||||||
<MaterialIcon icon="chevron_right" size={20} color="CurrentColor" />
|
|
||||||
</ChipLink>
|
|
||||||
))}
|
|
||||||
</Chips>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -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,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ import { getPaymentHeadingConfig } from "./utils"
|
|||||||
|
|
||||||
import styles from "./payment.module.css"
|
import styles from "./payment.module.css"
|
||||||
|
|
||||||
import type { CreateBookingInput } from "@scandic-hotels/trpc/routers/booking/mutation/create/schema"
|
import type { CreateBookingInput } from "@scandic-hotels/trpc/routers/booking/input"
|
||||||
import type { CreditCard } from "@scandic-hotels/trpc/types/user"
|
import type { CreditCard } from "@scandic-hotels/trpc/types/user"
|
||||||
|
|
||||||
import type { PriceChangeData } from "../PriceChangeData"
|
import type { PriceChangeData } from "../PriceChangeData"
|
||||||
@@ -128,45 +128,46 @@ export default function PaymentClient({
|
|||||||
|
|
||||||
const initiateBooking = trpc.booking.create.useMutation({
|
const initiateBooking = trpc.booking.create.useMutation({
|
||||||
onSuccess: (result) => {
|
onSuccess: (result) => {
|
||||||
if (result) {
|
if (!result) {
|
||||||
if ("error" in result) {
|
|
||||||
const queryParams = new URLSearchParams(searchParams.toString())
|
|
||||||
queryParams.set("errorCode", result.cause)
|
|
||||||
window.history.replaceState(
|
|
||||||
{},
|
|
||||||
"",
|
|
||||||
`${pathname}?${queryParams.toString()}`
|
|
||||||
)
|
|
||||||
handlePaymentError(result.cause)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
const { booking } = result
|
|
||||||
const mainRoom = booking.rooms[0]
|
|
||||||
|
|
||||||
if (booking.reservationStatus == BookingStatusEnum.BookingCompleted) {
|
|
||||||
clearBookingWidgetState()
|
|
||||||
// Cookie is used by Booking Confirmation page to validate that the user came from payment callback
|
|
||||||
// eslint-disable-next-line react-hooks/immutability
|
|
||||||
document.cookie = `bcsig=${result.sig}; Path=/; Max-Age=60; Secure; SameSite=Strict`
|
|
||||||
const confirmationUrl = `${bookingConfirmation(lang)}?RefId=${encodeURIComponent(mainRoom.refId)}`
|
|
||||||
router.push(confirmationUrl)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
setRefId(mainRoom.refId)
|
|
||||||
|
|
||||||
const hasPriceChange = booking.rooms.some((r) => r.priceChangedMetadata)
|
|
||||||
if (hasPriceChange) {
|
|
||||||
const priceChangeData = booking.rooms
|
|
||||||
.map((room) => room.priceChangedMetadata || null)
|
|
||||||
.filter(isNotNull)
|
|
||||||
setPriceChangeData(priceChangeData)
|
|
||||||
} else {
|
|
||||||
setIsPollingForBookingStatus(true)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
handlePaymentError("No confirmation number")
|
handlePaymentError("No confirmation number")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("error" in result) {
|
||||||
|
const queryParams = new URLSearchParams(searchParams.toString())
|
||||||
|
queryParams.set("errorCode", result.cause)
|
||||||
|
window.history.replaceState(
|
||||||
|
{},
|
||||||
|
"",
|
||||||
|
`${pathname}?${queryParams.toString()}`
|
||||||
|
)
|
||||||
|
handlePaymentError(result.cause)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { booking } = result
|
||||||
|
const mainRoom = booking.rooms[0]
|
||||||
|
|
||||||
|
if (booking.reservationStatus == BookingStatusEnum.BookingCompleted) {
|
||||||
|
clearBookingWidgetState()
|
||||||
|
// Cookie is used by Booking Confirmation page to validate that the user came from payment callback
|
||||||
|
// eslint-disable-next-line react-hooks/immutability
|
||||||
|
document.cookie = `bcsig=${result.sig}; Path=/; Max-Age=60; Secure; SameSite=Strict`
|
||||||
|
const confirmationUrl = `${bookingConfirmation(lang)}?RefId=${encodeURIComponent(mainRoom.refId)}`
|
||||||
|
router.push(confirmationUrl)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setRefId(mainRoom.refId)
|
||||||
|
|
||||||
|
const hasPriceChange = booking.rooms.some((r) => r.priceChangedMetadata)
|
||||||
|
if (hasPriceChange) {
|
||||||
|
const priceChangeData = booking.rooms
|
||||||
|
.map((room) => room.priceChangedMetadata || null)
|
||||||
|
.filter(isNotNull)
|
||||||
|
setPriceChangeData(priceChangeData)
|
||||||
|
} else {
|
||||||
|
setIsPollingForBookingStatus(true)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
@@ -419,6 +420,7 @@ export default function PaymentClient({
|
|||||||
}
|
}
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
|
||||||
initiateBooking.mutate(payload)
|
initiateBooking.mutate(payload)
|
||||||
},
|
},
|
||||||
[
|
[
|
||||||
|
|||||||
+1
-1
@@ -35,7 +35,7 @@ export default function TermsAndConditions({
|
|||||||
<span>
|
<span>
|
||||||
{intl.formatMessage({
|
{intl.formatMessage({
|
||||||
id: "booking.acceptBookingTerms",
|
id: "booking.acceptBookingTerms",
|
||||||
defaultMessage: "I accept the booking and cancellation terms.",
|
defaultMessage: "I accept the booking and cancellation terms",
|
||||||
})}
|
})}
|
||||||
</span>
|
</span>
|
||||||
</Typography>
|
</Typography>
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import { useIntl } from "react-intl"
|
|||||||
|
|
||||||
import { selectRate } from "@scandic-hotels/common/constants/routes/hotelReservation"
|
import { selectRate } from "@scandic-hotels/common/constants/routes/hotelReservation"
|
||||||
import { Button } from "@scandic-hotels/design-system/Button"
|
import { Button } from "@scandic-hotels/design-system/Button"
|
||||||
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 { getHotelRoom } from "@scandic-hotels/trpc/routers/booking/helpers"
|
import { getHotelRoom } from "@scandic-hotels/trpc/routers/booking/helpers"
|
||||||
@@ -47,20 +46,14 @@ export default function SelectedRoom() {
|
|||||||
<div className={styles.wrapper} data-available={room.isAvailable}>
|
<div className={styles.wrapper} data-available={room.isAvailable}>
|
||||||
<div className={styles.main}>
|
<div className={styles.main}>
|
||||||
<div className={styles.headerContainer}>
|
<div className={styles.headerContainer}>
|
||||||
<Footnote
|
<Typography variant="Title/Overline/sm">
|
||||||
className={styles.title}
|
<h2 className={styles.title}>
|
||||||
asChild
|
|
||||||
textTransform="uppercase"
|
|
||||||
type="label"
|
|
||||||
color="uiTextHighContrast"
|
|
||||||
>
|
|
||||||
<h2>
|
|
||||||
{intl.formatMessage({
|
{intl.formatMessage({
|
||||||
id: "common.room",
|
id: "common.room",
|
||||||
defaultMessage: "Room",
|
defaultMessage: "Room",
|
||||||
})}
|
})}
|
||||||
</h2>
|
</h2>
|
||||||
</Footnote>
|
</Typography>
|
||||||
<Typography
|
<Typography
|
||||||
variant="Title/Subtitle/md"
|
variant="Title/Subtitle/md"
|
||||||
className={styles.description}
|
className={styles.description}
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.facilities {
|
.facilities {
|
||||||
font-family: var(--typography-Body-Bold-fontFamily);
|
font-family: var(--Body-Paragraph-Font-family), var(--Body-Paragraph-Font-fallback);
|
||||||
padding-bottom: var(--Space-x3);
|
padding-bottom: var(--Space-x3);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-2
@@ -177,12 +177,12 @@ export function SelectHotelMapContent({
|
|||||||
>
|
>
|
||||||
<MaterialIcon icon="close" size={20} color="CurrentColor" />
|
<MaterialIcon icon="close" size={20} color="CurrentColor" />
|
||||||
<Typography variant="Body/Supporting text (caption)/smBold">
|
<Typography variant="Body/Supporting text (caption)/smBold">
|
||||||
<p>
|
<span>
|
||||||
{intl.formatMessage({
|
{intl.formatMessage({
|
||||||
id: "selectHotel.closeMap",
|
id: "selectHotel.closeMap",
|
||||||
defaultMessage: "Close the map",
|
defaultMessage: "Close the map",
|
||||||
})}
|
})}
|
||||||
</p>
|
</span>
|
||||||
</Typography>
|
</Typography>
|
||||||
</Link>
|
</Link>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
+1
-1
@@ -29,7 +29,7 @@
|
|||||||
.link {
|
.link {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: var(--Space-x05);
|
gap: var(--Space-x05);
|
||||||
align-items: baseline;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bookingCodeFilter {
|
.bookingCodeFilter {
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
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 { 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"
|
||||||
@@ -37,9 +36,11 @@ export default function SignupPromoDesktop({
|
|||||||
data-testid="signup-promo-desktop"
|
data-testid="signup-promo-desktop"
|
||||||
>
|
>
|
||||||
{badgeContent && <span className={styles.badge}>{badgeContent}</span>}
|
{badgeContent && <span className={styles.badge}>{badgeContent}</span>}
|
||||||
<Footnote color="burgundy">
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
<Message price={price} isEnterDetailsPage={isEnterDetailsPage} />
|
<p>
|
||||||
</Footnote>
|
<Message price={price} isEnterDetailsPage={isEnterDetailsPage} />
|
||||||
|
</p>
|
||||||
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { useIntl } from "react-intl"
|
import { useIntl } from "react-intl"
|
||||||
|
|
||||||
import Footnote from "@scandic-hotels/design-system/Footnote"
|
import { Typography } from "@scandic-hotels/design-system/Typography"
|
||||||
|
|
||||||
import { useBookingFlowConfig } from "../../bookingFlowConfig/bookingFlowConfigContext"
|
import { useBookingFlowConfig } from "../../bookingFlowConfig/bookingFlowConfigContext"
|
||||||
|
|
||||||
@@ -14,9 +14,11 @@ export default function SignupPromoMobile() {
|
|||||||
data-footer-spacing-signup
|
data-footer-spacing-signup
|
||||||
className={styles.memberDiscountBannerMobile}
|
className={styles.memberDiscountBannerMobile}
|
||||||
>
|
>
|
||||||
<Footnote color="burgundy">
|
<Typography variant="Body/Supporting text (caption)/smRegular">
|
||||||
<Message />
|
<p>
|
||||||
</Footnote>
|
<Message />
|
||||||
|
</p>
|
||||||
|
</Typography>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user