Merged in feat/SW-1555-jobylon-integration (pull request #1484)
Feat/SW-1555 jobylon integration * feat(SW-1555): Added jobylon feed query * feat(SW-1555): Added jobylon feed component Approved-by: Fredrik Thorsson Approved-by: Matilda Landström
This commit is contained in:
@@ -0,0 +1,57 @@
|
|||||||
|
import { OpenInNewSmallIcon } from "@/components/Icons"
|
||||||
|
import Button from "@/components/TempDesignSystem/Button"
|
||||||
|
import Caption from "@/components/TempDesignSystem/Text/Caption"
|
||||||
|
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||||
|
import { getIntl } from "@/i18n"
|
||||||
|
import { getLang } from "@/i18n/serverContext"
|
||||||
|
|
||||||
|
import styles from "./jobylonCard.module.css"
|
||||||
|
|
||||||
|
import type { JobylonItem } from "@/types/trpc/routers/jobylon"
|
||||||
|
|
||||||
|
interface JobylonCardProps {
|
||||||
|
job: JobylonItem
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function JobylonCard({ job }: JobylonCardProps) {
|
||||||
|
const intl = await getIntl()
|
||||||
|
const lang = getLang()
|
||||||
|
const deadlineText = job.toDate
|
||||||
|
? intl.formatMessage(
|
||||||
|
{ id: "Deadline: {date}" },
|
||||||
|
{ date: job.toDate.locale(lang).format("Do MMMM") }
|
||||||
|
)
|
||||||
|
: intl.formatMessage({ id: "Open for application" })
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.jobylonCard}>
|
||||||
|
<Subtitle asChild>
|
||||||
|
<h3>{job.title}</h3>
|
||||||
|
</Subtitle>
|
||||||
|
|
||||||
|
<div className={styles.contentWrapper}>
|
||||||
|
<div className={styles.content}>
|
||||||
|
<Caption>{job.categories.map((cat) => cat.text).join(", ")}</Caption>
|
||||||
|
<Caption>
|
||||||
|
{job.locations
|
||||||
|
.map((loc) => `${loc.city}, ${loc.country}`)
|
||||||
|
.join(" | ")}
|
||||||
|
</Caption>
|
||||||
|
<Caption color="uiTextPlaceholder">{deadlineText}</Caption>
|
||||||
|
</div>
|
||||||
|
<Button
|
||||||
|
theme="base"
|
||||||
|
size="small"
|
||||||
|
intent="tertiary"
|
||||||
|
variant="icon"
|
||||||
|
asChild
|
||||||
|
>
|
||||||
|
<a href={job.url} target="_blank" rel="noopener noreferrer">
|
||||||
|
{intl.formatMessage({ id: "View & apply" })}
|
||||||
|
<OpenInNewSmallIcon />
|
||||||
|
</a>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
.jobylonCard {
|
||||||
|
display: grid;
|
||||||
|
width: 100%;
|
||||||
|
padding: var(--Spacing-x2);
|
||||||
|
background-color: var(--Base-Surface-Primary-light-Normal);
|
||||||
|
border: 1px solid var(--Base-Border-Subtle);
|
||||||
|
border-radius: var(--Corner-radius-Medium);
|
||||||
|
}
|
||||||
|
|
||||||
|
.contentWrapper {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--Spacing-x1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (min-width: 768px) {
|
||||||
|
.contentWrapper {
|
||||||
|
grid-template-columns: 1fr auto;
|
||||||
|
gap: var(--Spacing-x2);
|
||||||
|
align-items: end;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,60 @@
|
|||||||
|
import { getJobylonFeed } from "@/lib/trpc/memoizedRequests"
|
||||||
|
|
||||||
|
import SectionContainer from "@/components/Section/Container"
|
||||||
|
import SectionHeader from "@/components/Section/Header"
|
||||||
|
import SectionLink from "@/components/Section/Link"
|
||||||
|
import Subtitle from "@/components/TempDesignSystem/Text/Subtitle"
|
||||||
|
import { getIntl } from "@/i18n"
|
||||||
|
|
||||||
|
import JobylonCard from "./JobylonCard"
|
||||||
|
|
||||||
|
import styles from "./jobylonFeed.module.css"
|
||||||
|
|
||||||
|
interface JobylonFeedProps {
|
||||||
|
title?: string
|
||||||
|
subtitle?: string
|
||||||
|
link?: { href: string; text: string }
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function JobylonFeed({
|
||||||
|
title,
|
||||||
|
subtitle,
|
||||||
|
link,
|
||||||
|
}: JobylonFeedProps) {
|
||||||
|
const intl = await getIntl()
|
||||||
|
const allJobs = await getJobylonFeed()
|
||||||
|
|
||||||
|
if (!allJobs) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SectionContainer>
|
||||||
|
<SectionHeader
|
||||||
|
link={link}
|
||||||
|
preamble={subtitle}
|
||||||
|
title={title}
|
||||||
|
headingAs="h3"
|
||||||
|
headingLevel="h2"
|
||||||
|
/>
|
||||||
|
<div className={styles.content}>
|
||||||
|
<Subtitle type="two">
|
||||||
|
{intl.formatMessage(
|
||||||
|
{
|
||||||
|
id: "{count, plural, one {{count} Result} other {{count} Results}}",
|
||||||
|
},
|
||||||
|
{ count: allJobs.length }
|
||||||
|
)}
|
||||||
|
</Subtitle>
|
||||||
|
<ul className={styles.list}>
|
||||||
|
{allJobs.map((job) => (
|
||||||
|
<li key={job.id}>
|
||||||
|
<JobylonCard job={job} />
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<SectionLink link={link} variant="mobile" />
|
||||||
|
</SectionContainer>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
.list {
|
||||||
|
list-style: none;
|
||||||
|
display: grid;
|
||||||
|
gap: var(--Spacing-x2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
display: grid;
|
||||||
|
gap: var(--Spacing-x2);
|
||||||
|
}
|
||||||
@@ -19,6 +19,8 @@ import SoonestStays from "@/components/Blocks/DynamicContent/Stays/Soonest"
|
|||||||
import UpcomingStays from "@/components/Blocks/DynamicContent/Stays/Upcoming"
|
import UpcomingStays from "@/components/Blocks/DynamicContent/Stays/Upcoming"
|
||||||
import LoadingSpinner from "@/components/LoadingSpinner"
|
import LoadingSpinner from "@/components/LoadingSpinner"
|
||||||
|
|
||||||
|
import JobylonFeed from "./JobylonFeed"
|
||||||
|
|
||||||
import type { DynamicContentProps } from "@/types/components/blocks/dynamicContent"
|
import type { DynamicContentProps } from "@/types/components/blocks/dynamicContent"
|
||||||
import { DynamicContentEnum } from "@/types/enums/dynamicContent"
|
import { DynamicContentEnum } from "@/types/enums/dynamicContent"
|
||||||
|
|
||||||
@@ -45,6 +47,8 @@ function DynamicContentBlocks(props: DynamicContentProps) {
|
|||||||
return (
|
return (
|
||||||
<HowItWorks dynamic_content={dynamic_content} firstItem={firstItem} />
|
<HowItWorks dynamic_content={dynamic_content} firstItem={firstItem} />
|
||||||
)
|
)
|
||||||
|
case DynamicContentEnum.Blocks.components.jobylon_feed:
|
||||||
|
return <JobylonFeed {...dynamic_content} />
|
||||||
case DynamicContentEnum.Blocks.components.loyalty_levels:
|
case DynamicContentEnum.Blocks.components.loyalty_levels:
|
||||||
return (
|
return (
|
||||||
<LoyaltyLevels
|
<LoyaltyLevels
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"+46 8 517 517 00": "+46 8 517 517 00",
|
"+46 8 517 517 00": "+46 8 517 517 00",
|
||||||
"Are you sure you want to remove this product?": "Er du sikker på, at du vil fjerne dette produkt?",
|
|
||||||
"/night per adult": "/nat per voksen",
|
"/night per adult": "/nat per voksen",
|
||||||
"<b>Included</b> (based on availability)": "<b>Inkluderet</b> (baseret på tilgængelighed)",
|
"<b>Included</b> (based on availability)": "<b>Inkluderet</b> (baseret på tilgængelighed)",
|
||||||
"<b>Total price</b> (incl VAT)": "<b>Samlet pris</b> (inkl. moms)",
|
"<b>Total price</b> (incl VAT)": "<b>Samlet pris</b> (inkl. moms)",
|
||||||
@@ -56,6 +55,7 @@
|
|||||||
"Are you sure you want to cancel your stay at {hotel} from {checkInDate} to {checkOutDate}? This can't be reversed.": "Er du sikker på, at du vil annullere dit ophold hos {hotel} fra {checkInDate} til {checkOutDate}? Dette kan ikke gendannes.",
|
"Are you sure you want to cancel your stay at {hotel} from {checkInDate} to {checkOutDate}? This can't be reversed.": "Er du sikker på, at du vil annullere dit ophold hos {hotel} fra {checkInDate} til {checkOutDate}? Dette kan ikke gendannes.",
|
||||||
"Are you sure you want to continue with the cancellation?": "Er du sikker på, at du vil fortsætte med annullereringen?",
|
"Are you sure you want to continue with the cancellation?": "Er du sikker på, at du vil fortsætte med annullereringen?",
|
||||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Er du sikker på, at du vil fjerne kortet, der slutter me {lastFourDigits} fra din medlemsprofil?",
|
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Er du sikker på, at du vil fjerne kortet, der slutter me {lastFourDigits} fra din medlemsprofil?",
|
||||||
|
"Are you sure you want to remove this product?": "Er du sikker på, at du vil fjerne dette produkt?",
|
||||||
"Arrival date": "Ankomstdato",
|
"Arrival date": "Ankomstdato",
|
||||||
"As our Close Friend": "Som vores nære ven",
|
"As our Close Friend": "Som vores nære ven",
|
||||||
"As our {level}": "Som vores {level}",
|
"As our {level}": "Som vores {level}",
|
||||||
@@ -182,6 +182,7 @@
|
|||||||
"Date of Birth": "Fødselsdato",
|
"Date of Birth": "Fødselsdato",
|
||||||
"Date of birth not matching": "Date of birth not matching",
|
"Date of birth not matching": "Date of birth not matching",
|
||||||
"Day": "Dag",
|
"Day": "Dag",
|
||||||
|
"Deadline: {date}": "Deadline: {date}",
|
||||||
"Delivered at:": "Leveret til:",
|
"Delivered at:": "Leveret til:",
|
||||||
"Delivery between {deliveryTime}. Payment will be made on check-in": "Levering mellem {deliveryTime}. Betaling vil ske ved check-in.",
|
"Delivery between {deliveryTime}. Payment will be made on check-in": "Levering mellem {deliveryTime}. Betaling vil ske ved check-in.",
|
||||||
"Description": "Beskrivelse",
|
"Description": "Beskrivelse",
|
||||||
@@ -459,6 +460,7 @@
|
|||||||
"On your journey": "På din rejse",
|
"On your journey": "På din rejse",
|
||||||
"Oops! Something went wrong while showing your surprise. Please refresh the page or try again later. If the issue persists, <link>contact the support.</link>": "Ups! Noget gik galt under visningen af din overraskelse. Opdater siden, eller prøv igen senere. Hvis problemet fortsætter, skal du <link>kontakte supporten.</link>",
|
"Oops! Something went wrong while showing your surprise. Please refresh the page or try again later. If the issue persists, <link>contact the support.</link>": "Ups! Noget gik galt under visningen af din overraskelse. Opdater siden, eller prøv igen senere. Hvis problemet fortsætter, skal du <link>kontakte supporten.</link>",
|
||||||
"Open": "Åben",
|
"Open": "Åben",
|
||||||
|
"Open for application": "Åben for ansøgning",
|
||||||
"Open image gallery": "Åbn billedgalleri",
|
"Open image gallery": "Åbn billedgalleri",
|
||||||
"Open language menu": "Åbn sprogmenuen",
|
"Open language menu": "Åbn sprogmenuen",
|
||||||
"Open menu": "Åbn menuen",
|
"Open menu": "Åbn menuen",
|
||||||
@@ -681,6 +683,7 @@
|
|||||||
"VAT {vat}%": "Moms {vat}%",
|
"VAT {vat}%": "Moms {vat}%",
|
||||||
"Valid through {expirationDate}": "Gyldig til og med {expirationDate}",
|
"Valid through {expirationDate}": "Gyldig til og med {expirationDate}",
|
||||||
"Verification code": "Verification code",
|
"Verification code": "Verification code",
|
||||||
|
"View & apply": "Se og anvend",
|
||||||
"View all": "Vis alle",
|
"View all": "Vis alle",
|
||||||
"View all hotels in {country}": "Se alle hoteller i {country}",
|
"View all hotels in {country}": "Se alle hoteller i {country}",
|
||||||
"View and buy add-ons": "View and buy add-ons",
|
"View and buy add-ons": "View and buy add-ons",
|
||||||
@@ -798,6 +801,7 @@
|
|||||||
"{checkOutDate} from {checkOutTime}": "{checkOutDate} fra {checkOutTime}",
|
"{checkOutDate} from {checkOutTime}": "{checkOutDate} fra {checkOutTime}",
|
||||||
"{count, plural, one {{count} Hotel} other {{count} Hotels}}": "{count, plural, one {# hotel} other {# hoteller}}",
|
"{count, plural, one {{count} Hotel} other {{count} Hotels}}": "{count, plural, one {# hotel} other {# hoteller}}",
|
||||||
"{count, plural, one {{count} Location} other {{count} Locations}}": "{count, plural, one {# sted} other {# steder}}",
|
"{count, plural, one {{count} Location} other {{count} Locations}}": "{count, plural, one {# sted} other {# steder}}",
|
||||||
|
"{count, plural, one {{count} Result} other {{count} Results}}": "{count, plural, one {{count} Result} other {{count} Results}}",
|
||||||
"{count} destinations": "{count} destinationer",
|
"{count} destinations": "{count} destinationer",
|
||||||
"{count} hotels": "{count} hotels",
|
"{count} hotels": "{count} hotels",
|
||||||
"{count} lowercase letter": "{count} lille bogstav",
|
"{count} lowercase letter": "{count} lille bogstav",
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"+46 8 517 517 00": "+46 8 517 517 00",
|
"+46 8 517 517 00": "+46 8 517 517 00",
|
||||||
"Are you sure you want to remove this product?": "Möchten Sie dieses Produkt wirklich entfernen?",
|
|
||||||
"/night per adult": "/Nacht pro Erwachsenem",
|
"/night per adult": "/Nacht pro Erwachsenem",
|
||||||
"<b>Included</b> (based on availability)": "<b>Inbegriffen</b> (je nach Verfügbarkeit)",
|
"<b>Included</b> (based on availability)": "<b>Inbegriffen</b> (je nach Verfügbarkeit)",
|
||||||
"<b>Total price</b> (incl VAT)": "<b>Gesamtpreis</b> (inkl. MwSt.)",
|
"<b>Total price</b> (incl VAT)": "<b>Gesamtpreis</b> (inkl. MwSt.)",
|
||||||
@@ -56,6 +55,7 @@
|
|||||||
"Are you sure you want to cancel your stay at {hotel} from {checkInDate} to {checkOutDate}? This can't be reversed.": "Sind Sie sicher, dass Sie Ihren Aufenthalt bei {hotel} vom {checkInDate} bis {checkOutDate} stornieren möchten? Dies kann nicht rückgängig gemacht werden.",
|
"Are you sure you want to cancel your stay at {hotel} from {checkInDate} to {checkOutDate}? This can't be reversed.": "Sind Sie sicher, dass Sie Ihren Aufenthalt bei {hotel} vom {checkInDate} bis {checkOutDate} stornieren möchten? Dies kann nicht rückgängig gemacht werden.",
|
||||||
"Are you sure you want to continue with the cancellation?": "Sind Sie sicher, dass Sie mit der Stornierung fortfahren möchten?",
|
"Are you sure you want to continue with the cancellation?": "Sind Sie sicher, dass Sie mit der Stornierung fortfahren möchten?",
|
||||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Möchten Sie die Karte mit der Endung {lastFourDigits} wirklich aus Ihrem Mitgliedsprofil entfernen?",
|
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Möchten Sie die Karte mit der Endung {lastFourDigits} wirklich aus Ihrem Mitgliedsprofil entfernen?",
|
||||||
|
"Are you sure you want to remove this product?": "Möchten Sie dieses Produkt wirklich entfernen?",
|
||||||
"Arrival date": "Ankunftsdatum",
|
"Arrival date": "Ankunftsdatum",
|
||||||
"As our Close Friend": "Als unser enger Freund",
|
"As our Close Friend": "Als unser enger Freund",
|
||||||
"As our {level}": "Als unser {level}",
|
"As our {level}": "Als unser {level}",
|
||||||
@@ -183,6 +183,7 @@
|
|||||||
"Date of Birth": "Geburtsdatum",
|
"Date of Birth": "Geburtsdatum",
|
||||||
"Date of birth not matching": "Date of birth not matching",
|
"Date of birth not matching": "Date of birth not matching",
|
||||||
"Day": "Tag",
|
"Day": "Tag",
|
||||||
|
"Deadline: {date}": "Deadline: {date}",
|
||||||
"Delivered at:": "Geliefert bei:",
|
"Delivered at:": "Geliefert bei:",
|
||||||
"Delivery between {deliveryTime}. Payment will be made on check-in": "Lieferung zwischen {deliveryTime}. Die Zahlung erfolgt beim Check-in.",
|
"Delivery between {deliveryTime}. Payment will be made on check-in": "Lieferung zwischen {deliveryTime}. Die Zahlung erfolgt beim Check-in.",
|
||||||
"Description": "Beschreibung",
|
"Description": "Beschreibung",
|
||||||
@@ -460,6 +461,7 @@
|
|||||||
"On your journey": "Auf deiner Reise",
|
"On your journey": "Auf deiner Reise",
|
||||||
"Oops! Something went wrong while showing your surprise. Please refresh the page or try again later. If the issue persists, <link>contact the support.</link>": "Ups! Beim Anzeigen Ihrer Überraschung ist ein Fehler aufgetreten. Bitte aktualisieren Sie die Seite oder versuchen Sie es später erneut. Wenn das Problem weiterhin besteht, <link>kontaktieren Sie den Support.</link>",
|
"Oops! Something went wrong while showing your surprise. Please refresh the page or try again later. If the issue persists, <link>contact the support.</link>": "Ups! Beim Anzeigen Ihrer Überraschung ist ein Fehler aufgetreten. Bitte aktualisieren Sie die Seite oder versuchen Sie es später erneut. Wenn das Problem weiterhin besteht, <link>kontaktieren Sie den Support.</link>",
|
||||||
"Open": "Offen",
|
"Open": "Offen",
|
||||||
|
"Open for application": "Offen für Bewerbungen",
|
||||||
"Open image gallery": "Bildergalerie öffnen",
|
"Open image gallery": "Bildergalerie öffnen",
|
||||||
"Open language menu": "Sprachmenü öffnen",
|
"Open language menu": "Sprachmenü öffnen",
|
||||||
"Open menu": "Menü öffnen",
|
"Open menu": "Menü öffnen",
|
||||||
@@ -679,6 +681,7 @@
|
|||||||
"VAT {vat}%": "MwSt. {vat}%",
|
"VAT {vat}%": "MwSt. {vat}%",
|
||||||
"Valid through {expirationDate}": "Gültig bis {expirationDate}",
|
"Valid through {expirationDate}": "Gültig bis {expirationDate}",
|
||||||
"Verification code": "Verification code",
|
"Verification code": "Verification code",
|
||||||
|
"View & apply": "Ansehen & bewerben",
|
||||||
"View all": "Alle anzeigen",
|
"View all": "Alle anzeigen",
|
||||||
"View all hotels in {country}": "Alle Hotels in {country} anzeigen",
|
"View all hotels in {country}": "Alle Hotels in {country} anzeigen",
|
||||||
"View and buy add-ons": "View and buy add-ons",
|
"View and buy add-ons": "View and buy add-ons",
|
||||||
@@ -796,6 +799,7 @@
|
|||||||
"{checkOutDate} from {checkOutTime}": "{checkOutDate} aus {checkOutTime}",
|
"{checkOutDate} from {checkOutTime}": "{checkOutDate} aus {checkOutTime}",
|
||||||
"{count, plural, one {{count} Hotel} other {{count} Hotels}}": "{count, plural, one {{count} Hotel} other {{count} Hotels}}}",
|
"{count, plural, one {{count} Hotel} other {{count} Hotels}}": "{count, plural, one {{count} Hotel} other {{count} Hotels}}}",
|
||||||
"{count, plural, one {{count} Location} other {{count} Locations}}": "{count, plural, one {# Standort} other {# Standorte}}",
|
"{count, plural, one {{count} Location} other {{count} Locations}}": "{count, plural, one {# Standort} other {# Standorte}}",
|
||||||
|
"{count, plural, one {{count} Result} other {{count} Results}}": "{count, plural, one {{count} Ergebnis} other {{count} Ergebnisse}}",
|
||||||
"{count} destinations": "{count} Ziele",
|
"{count} destinations": "{count} Ziele",
|
||||||
"{count} hotels": "{count} hotels",
|
"{count} hotels": "{count} hotels",
|
||||||
"{count} lowercase letter": "{count} Kleinbuchstabe",
|
"{count} lowercase letter": "{count} Kleinbuchstabe",
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"Are you sure you want to remove this product?": "Are you sure you want to remove this product?",
|
|
||||||
"+46 8 517 517 00": "+46 8 517 517 00",
|
"+46 8 517 517 00": "+46 8 517 517 00",
|
||||||
"/night per adult": "/night per adult",
|
"/night per adult": "/night per adult",
|
||||||
"<b>Included</b> (based on availability)": "<b>Included</b> (based on availability)",
|
"<b>Included</b> (based on availability)": "<b>Included</b> (based on availability)",
|
||||||
@@ -55,6 +54,7 @@
|
|||||||
"Are you sure you want to cancel your stay at {hotel} from {checkInDate} to {checkOutDate}? This can't be reversed.": "Are you sure you want to cancel your stay at {hotel} from {checkInDate} to {checkOutDate}? This can't be reversed.",
|
"Are you sure you want to cancel your stay at {hotel} from {checkInDate} to {checkOutDate}? This can't be reversed.": "Are you sure you want to cancel your stay at {hotel} from {checkInDate} to {checkOutDate}? This can't be reversed.",
|
||||||
"Are you sure you want to continue with the cancellation?": "Are you sure you want to continue with the cancellation?",
|
"Are you sure you want to continue with the cancellation?": "Are you sure you want to continue with the cancellation?",
|
||||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?",
|
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?",
|
||||||
|
"Are you sure you want to remove this product?": "Are you sure you want to remove this product?",
|
||||||
"Arrival date": "Arrival date",
|
"Arrival date": "Arrival date",
|
||||||
"As our Close Friend": "As our Close Friend",
|
"As our Close Friend": "As our Close Friend",
|
||||||
"As our {level}": "As our {level}",
|
"As our {level}": "As our {level}",
|
||||||
@@ -181,6 +181,7 @@
|
|||||||
"Date of Birth": "Date of Birth",
|
"Date of Birth": "Date of Birth",
|
||||||
"Date of birth not matching": "Date of birth not matching",
|
"Date of birth not matching": "Date of birth not matching",
|
||||||
"Day": "Day",
|
"Day": "Day",
|
||||||
|
"Deadline: {date}": "Deadline: {date}",
|
||||||
"Delivered at:": "Delivered at:",
|
"Delivered at:": "Delivered at:",
|
||||||
"Delivery between {deliveryTime}. Payment will be made on check-in": "Delivery between {deliveryTime}. Payment will be made on check-in.",
|
"Delivery between {deliveryTime}. Payment will be made on check-in": "Delivery between {deliveryTime}. Payment will be made on check-in.",
|
||||||
"Description": "Description",
|
"Description": "Description",
|
||||||
@@ -458,6 +459,7 @@
|
|||||||
"On your journey": "On your journey",
|
"On your journey": "On your journey",
|
||||||
"Oops! Something went wrong while showing your surprise. Please refresh the page or try again later. If the issue persists, <link>contact the support.</link>": "Oops! Something went wrong while showing your surprise. Please refresh the page or try again later. If the issue persists, <link>contact the support.</link>",
|
"Oops! Something went wrong while showing your surprise. Please refresh the page or try again later. If the issue persists, <link>contact the support.</link>": "Oops! Something went wrong while showing your surprise. Please refresh the page or try again later. If the issue persists, <link>contact the support.</link>",
|
||||||
"Open": "Open",
|
"Open": "Open",
|
||||||
|
"Open for application": "Open for application",
|
||||||
"Open image gallery": "Open image gallery",
|
"Open image gallery": "Open image gallery",
|
||||||
"Open language menu": "Open language menu",
|
"Open language menu": "Open language menu",
|
||||||
"Open menu": "Open menu",
|
"Open menu": "Open menu",
|
||||||
@@ -677,6 +679,7 @@
|
|||||||
"VAT {vat}%": "VAT {vat}%",
|
"VAT {vat}%": "VAT {vat}%",
|
||||||
"Valid through {expirationDate}": "Valid through {expirationDate}",
|
"Valid through {expirationDate}": "Valid through {expirationDate}",
|
||||||
"Verification code": "Verification code",
|
"Verification code": "Verification code",
|
||||||
|
"View & apply": "View & apply",
|
||||||
"View all": "View all",
|
"View all": "View all",
|
||||||
"View all hotels in {country}": "View all hotels in {country}",
|
"View all hotels in {country}": "View all hotels in {country}",
|
||||||
"View and buy add-ons": "View and buy add-ons",
|
"View and buy add-ons": "View and buy add-ons",
|
||||||
@@ -791,6 +794,7 @@
|
|||||||
"{checkOutDate} from {checkOutTime}": "{checkOutDate} from {checkOutTime}",
|
"{checkOutDate} from {checkOutTime}": "{checkOutDate} from {checkOutTime}",
|
||||||
"{count, plural, one {{count} Hotel} other {{count} Hotels}}": "{count, plural, one {{count} Hotel} other {{count} Hotels}}",
|
"{count, plural, one {{count} Hotel} other {{count} Hotels}}": "{count, plural, one {{count} Hotel} other {{count} Hotels}}",
|
||||||
"{count, plural, one {{count} Location} other {{count} Locations}}": "{count, plural, one {{count} Location} other {{count} Locations}}",
|
"{count, plural, one {{count} Location} other {{count} Locations}}": "{count, plural, one {{count} Location} other {{count} Locations}}",
|
||||||
|
"{count, plural, one {{count} Result} other {{count} Results}}": "{count, plural, one {{count} Result} other {{count} Results}}",
|
||||||
"{count} destinations": "{count} destinations",
|
"{count} destinations": "{count} destinations",
|
||||||
"{count} hotels": "{count} hotels",
|
"{count} hotels": "{count} hotels",
|
||||||
"{count} lowercase letter": "{count} lowercase letter",
|
"{count} lowercase letter": "{count} lowercase letter",
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"+46 8 517 517 00": "+46 8 517 517 00",
|
"+46 8 517 517 00": "+46 8 517 517 00",
|
||||||
"Are you sure you want to remove this product?": "Haluatko varmasti poistaa tämän tuotteen?",
|
|
||||||
"/night per adult": "/yötä aikuista kohti",
|
"/night per adult": "/yötä aikuista kohti",
|
||||||
"<b>Included</b> (based on availability)": "<b>Sisältyy</b> (saatavuuden mukaan)",
|
"<b>Included</b> (based on availability)": "<b>Sisältyy</b> (saatavuuden mukaan)",
|
||||||
"<b>Total price</b> (incl VAT)": "<b>Kokonaishinta</b> (sis. ALV)",
|
"<b>Total price</b> (incl VAT)": "<b>Kokonaishinta</b> (sis. ALV)",
|
||||||
@@ -55,6 +54,7 @@
|
|||||||
"Are you sure you want to cancel your stay at {hotel} from {checkInDate} to {checkOutDate}? This can't be reversed.": "Oletko varmasti haluamassa peruuttaa majoituksesi hoteleissa {hotel} alkaen {checkInDate} asti {checkOutDate}? Tätä ei voi kumota.",
|
"Are you sure you want to cancel your stay at {hotel} from {checkInDate} to {checkOutDate}? This can't be reversed.": "Oletko varmasti haluamassa peruuttaa majoituksesi hoteleissa {hotel} alkaen {checkInDate} asti {checkOutDate}? Tätä ei voi kumota.",
|
||||||
"Are you sure you want to continue with the cancellation?": "Oletko varmasti haluamassa jatkaa peruuttamista?",
|
"Are you sure you want to continue with the cancellation?": "Oletko varmasti haluamassa jatkaa peruuttamista?",
|
||||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Haluatko varmasti poistaa kortin, joka päättyy numeroon {lastFourDigits} jäsenprofiilistasi?",
|
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Haluatko varmasti poistaa kortin, joka päättyy numeroon {lastFourDigits} jäsenprofiilistasi?",
|
||||||
|
"Are you sure you want to remove this product?": "Haluatko varmasti poistaa tämän tuotteen?",
|
||||||
"Arrival date": "Saapumispäivä",
|
"Arrival date": "Saapumispäivä",
|
||||||
"As our Close Friend": "Läheisenä ystävänämme",
|
"As our Close Friend": "Läheisenä ystävänämme",
|
||||||
"As our {level}": "{level}-etu",
|
"As our {level}": "{level}-etu",
|
||||||
@@ -182,6 +182,7 @@
|
|||||||
"Date of Birth": "Syntymäaika",
|
"Date of Birth": "Syntymäaika",
|
||||||
"Date of birth not matching": "Date of birth not matching",
|
"Date of birth not matching": "Date of birth not matching",
|
||||||
"Day": "Päivä",
|
"Day": "Päivä",
|
||||||
|
"Deadline: {date}": "Määräaika: {date}",
|
||||||
"Delivered at:": "Toimitettu:",
|
"Delivered at:": "Toimitettu:",
|
||||||
"Delivery between {deliveryTime}. Payment will be made on check-in": "Toimitus välillä {deliveryTime}. Maksu suoritetaan sisäänkirjautumisen yhteydessä.",
|
"Delivery between {deliveryTime}. Payment will be made on check-in": "Toimitus välillä {deliveryTime}. Maksu suoritetaan sisäänkirjautumisen yhteydessä.",
|
||||||
"Description": "Kuvaus",
|
"Description": "Kuvaus",
|
||||||
@@ -459,6 +460,7 @@
|
|||||||
"On your journey": "Matkallasi",
|
"On your journey": "Matkallasi",
|
||||||
"Oops! Something went wrong while showing your surprise. Please refresh the page or try again later. If the issue persists, <link>contact the support.</link>": "Hups! Jotain meni pieleen yllätyksesi näyttämisessä. Päivitä sivu tai yritä myöhemmin uudelleen. Jos ongelma jatkuu, <link>ota yhteyttä tukeen.</link>",
|
"Oops! Something went wrong while showing your surprise. Please refresh the page or try again later. If the issue persists, <link>contact the support.</link>": "Hups! Jotain meni pieleen yllätyksesi näyttämisessä. Päivitä sivu tai yritä myöhemmin uudelleen. Jos ongelma jatkuu, <link>ota yhteyttä tukeen.</link>",
|
||||||
"Open": "Avata",
|
"Open": "Avata",
|
||||||
|
"Open for application": "Avoinna hakemuksille",
|
||||||
"Open image gallery": "Avaa kuvagalleria",
|
"Open image gallery": "Avaa kuvagalleria",
|
||||||
"Open language menu": "Avaa kielivalikko",
|
"Open language menu": "Avaa kielivalikko",
|
||||||
"Open menu": "Avaa valikko",
|
"Open menu": "Avaa valikko",
|
||||||
@@ -679,6 +681,7 @@
|
|||||||
"VAT {vat}%": "ALV {vat}%",
|
"VAT {vat}%": "ALV {vat}%",
|
||||||
"Valid through {expirationDate}": "Voimassa {expirationDate} asti",
|
"Valid through {expirationDate}": "Voimassa {expirationDate} asti",
|
||||||
"Verification code": "Verification code",
|
"Verification code": "Verification code",
|
||||||
|
"View & apply": "Näytä ja käytä",
|
||||||
"View all": "Näytä kaikki",
|
"View all": "Näytä kaikki",
|
||||||
"View all hotels in {country}": "Näytä kaikki hotellit maassa {country}",
|
"View all hotels in {country}": "Näytä kaikki hotellit maassa {country}",
|
||||||
"View and buy add-ons": "View and buy add-ons",
|
"View and buy add-ons": "View and buy add-ons",
|
||||||
@@ -796,6 +799,7 @@
|
|||||||
"{checkOutDate} from {checkOutTime}": "{checkOutDate} alkaen {checkOutTime}",
|
"{checkOutDate} from {checkOutTime}": "{checkOutDate} alkaen {checkOutTime}",
|
||||||
"{count, plural, one {{count} Hotel} other {{count} Hotels}}": "{count, plural, one {# hotelli} other {# hotellit}}",
|
"{count, plural, one {{count} Hotel} other {{count} Hotels}}": "{count, plural, one {# hotelli} other {# hotellit}}",
|
||||||
"{count, plural, one {{count} Location} other {{count} Locations}}": "{count, plural, one {# sijainti} other {# sijainnit}}",
|
"{count, plural, one {{count} Location} other {{count} Locations}}": "{count, plural, one {# sijainti} other {# sijainnit}}",
|
||||||
|
"{count, plural, one {{count} Result} other {{count} Results}}": "{count, plural, one {{count} tulos} other {{count} tulosta}}",
|
||||||
"{count} destinations": "{count} kohdetta",
|
"{count} destinations": "{count} kohdetta",
|
||||||
"{count} hotels": "{count} hotels",
|
"{count} hotels": "{count} hotels",
|
||||||
"{count} lowercase letter": "{count} pien kirjain",
|
"{count} lowercase letter": "{count} pien kirjain",
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"+46 8 517 517 00": "+46 8 517 517 00",
|
"+46 8 517 517 00": "+46 8 517 517 00",
|
||||||
"Are you sure you want to remove this product?": "Er du sikker på at du vil fjerne dette produktet?",
|
|
||||||
"/night per adult": "/natt per voksen",
|
"/night per adult": "/natt per voksen",
|
||||||
"<b>Included</b> (based on availability)": "<b>Inkludert</b> (basert på tilgjengelighet)",
|
"<b>Included</b> (based on availability)": "<b>Inkludert</b> (basert på tilgjengelighet)",
|
||||||
"<b>Total price</b> (incl VAT)": "<b>Totalpris</b> (inkl. mva)",
|
"<b>Total price</b> (incl VAT)": "<b>Totalpris</b> (inkl. mva)",
|
||||||
@@ -55,6 +54,7 @@
|
|||||||
"Are you sure you want to cancel your stay at {hotel} from {checkInDate} to {checkOutDate}? This can't be reversed.": "Er du sikker på, at du vil annullere dit ophold hos {hotel} fra {checkInDate} til {checkOutDate}? Dette kan ikke gendannes.",
|
"Are you sure you want to cancel your stay at {hotel} from {checkInDate} to {checkOutDate}? This can't be reversed.": "Er du sikker på, at du vil annullere dit ophold hos {hotel} fra {checkInDate} til {checkOutDate}? Dette kan ikke gendannes.",
|
||||||
"Are you sure you want to continue with the cancellation?": "Er du sikker på, at du vil fortsætte med annullereringen?",
|
"Are you sure you want to continue with the cancellation?": "Er du sikker på, at du vil fortsætte med annullereringen?",
|
||||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Er du sikker på at du vil fjerne kortet som slutter på {lastFourDigits} fra medlemsprofilen din?",
|
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Er du sikker på at du vil fjerne kortet som slutter på {lastFourDigits} fra medlemsprofilen din?",
|
||||||
|
"Are you sure you want to remove this product?": "Er du sikker på at du vil fjerne dette produktet?",
|
||||||
"Arrival date": "Ankomstdato",
|
"Arrival date": "Ankomstdato",
|
||||||
"As our Close Friend": "Som vår nære venn",
|
"As our Close Friend": "Som vår nære venn",
|
||||||
"As our {level}": "Som vår {level}",
|
"As our {level}": "Som vår {level}",
|
||||||
@@ -181,6 +181,7 @@
|
|||||||
"Date of Birth": "Fødselsdato",
|
"Date of Birth": "Fødselsdato",
|
||||||
"Date of birth not matching": "Date of birth not matching",
|
"Date of birth not matching": "Date of birth not matching",
|
||||||
"Day": "Dag",
|
"Day": "Dag",
|
||||||
|
"Deadline: {date}": "Frist: {date}",
|
||||||
"Delivered at:": "Delivered at:",
|
"Delivered at:": "Delivered at:",
|
||||||
"Delivery between {deliveryTime}. Payment will be made on check-in": "Levering mellom {deliveryTime}. Betaling vil skje ved innsjekking.",
|
"Delivery between {deliveryTime}. Payment will be made on check-in": "Levering mellom {deliveryTime}. Betaling vil skje ved innsjekking.",
|
||||||
"Description": "Beskrivelse",
|
"Description": "Beskrivelse",
|
||||||
@@ -458,6 +459,7 @@
|
|||||||
"On your journey": "På reisen din",
|
"On your journey": "På reisen din",
|
||||||
"Oops! Something went wrong while showing your surprise. Please refresh the page or try again later. If the issue persists, <link>contact the support.</link>": "Beklager! Noe gikk galt under visningen av overraskelsen din. Oppdater siden eller prøv igjen senere. Hvis problemet vedvarer, <link>kontakt brukerstøtten.</link>",
|
"Oops! Something went wrong while showing your surprise. Please refresh the page or try again later. If the issue persists, <link>contact the support.</link>": "Beklager! Noe gikk galt under visningen av overraskelsen din. Oppdater siden eller prøv igjen senere. Hvis problemet vedvarer, <link>kontakt brukerstøtten.</link>",
|
||||||
"Open": "Åpen",
|
"Open": "Åpen",
|
||||||
|
"Open for application": "Åpen for søknad",
|
||||||
"Open image gallery": "Åpne bildegalleri",
|
"Open image gallery": "Åpne bildegalleri",
|
||||||
"Open language menu": "Åpne språkmenyen",
|
"Open language menu": "Åpne språkmenyen",
|
||||||
"Open menu": "Åpne menyen",
|
"Open menu": "Åpne menyen",
|
||||||
@@ -677,6 +679,7 @@
|
|||||||
"VAT {vat}%": "mva {vat}%",
|
"VAT {vat}%": "mva {vat}%",
|
||||||
"Valid through {expirationDate}": "Gyldig til og med {expirationDate}",
|
"Valid through {expirationDate}": "Gyldig til og med {expirationDate}",
|
||||||
"Verification code": "Verification code",
|
"Verification code": "Verification code",
|
||||||
|
"View & apply": "Se og bruk",
|
||||||
"View all": "Vis alle",
|
"View all": "Vis alle",
|
||||||
"View all hotels in {country}": "Se alle hotellene i {country}",
|
"View all hotels in {country}": "Se alle hotellene i {country}",
|
||||||
"View and buy add-ons": "View and buy add-ons",
|
"View and buy add-ons": "View and buy add-ons",
|
||||||
@@ -794,6 +797,7 @@
|
|||||||
"{checkOutDate} from {checkOutTime}": "{checkOutDate} fra {checkOutTime}",
|
"{checkOutDate} from {checkOutTime}": "{checkOutDate} fra {checkOutTime}",
|
||||||
"{count, plural, one {{count} Hotel} other {{count} Hotels}}": "{count, plural, one {# hotell} other {# hoteller}}",
|
"{count, plural, one {{count} Hotel} other {{count} Hotels}}": "{count, plural, one {# hotell} other {# hoteller}}",
|
||||||
"{count, plural, one {{count} Location} other {{count} Locations}}": "{count, plural, one {# sted} other {# steder}}",
|
"{count, plural, one {{count} Location} other {{count} Locations}}": "{count, plural, one {# sted} other {# steder}}",
|
||||||
|
"{count, plural, one {{count} Result} other {{count} Results}}": "{count, plural, one {{count} Result} other {{count} Results}}",
|
||||||
"{count} destinations": "{count} destinasjoner",
|
"{count} destinations": "{count} destinasjoner",
|
||||||
"{count} hotels": "{count} hotels",
|
"{count} hotels": "{count} hotels",
|
||||||
"{count} lowercase letter": "{count} liten bokstav",
|
"{count} lowercase letter": "{count} liten bokstav",
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"+46 8 517 517 00": "+46 8 517 517 00",
|
"+46 8 517 517 00": "+46 8 517 517 00",
|
||||||
"Are you sure you want to remove this product?": "Är du säker på att du vill ta bort den här produkten?",
|
|
||||||
"/night per adult": "/natt per vuxen",
|
"/night per adult": "/natt per vuxen",
|
||||||
"<b>Included</b> (based on availability)": "<b>Ingår</b> (baserat på tillgänglighet)",
|
"<b>Included</b> (based on availability)": "<b>Ingår</b> (baserat på tillgänglighet)",
|
||||||
"<b>Total price</b> (incl VAT)": "<b>Totalpris</b> (inkl moms)",
|
"<b>Total price</b> (incl VAT)": "<b>Totalpris</b> (inkl moms)",
|
||||||
@@ -55,6 +54,7 @@
|
|||||||
"Are you sure you want to cancel your stay at {hotel} from {checkInDate} to {checkOutDate}? This can't be reversed.": "Är du säker på att du vill avboka din vistelse hos {hotel} från {checkInDate} till {checkOutDate}? Detta kan inte ångras.",
|
"Are you sure you want to cancel your stay at {hotel} from {checkInDate} to {checkOutDate}? This can't be reversed.": "Är du säker på att du vill avboka din vistelse hos {hotel} från {checkInDate} till {checkOutDate}? Detta kan inte ångras.",
|
||||||
"Are you sure you want to continue with the cancellation?": "Är du säker på att du vill fortsätta med avbokningen?",
|
"Are you sure you want to continue with the cancellation?": "Är du säker på att du vill fortsätta med avbokningen?",
|
||||||
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Är du säker på att du vill ta bort kortet som slutar med {lastFourDigits} från din medlemsprofil?",
|
"Are you sure you want to remove the card ending with {lastFourDigits} from your member profile?": "Är du säker på att du vill ta bort kortet som slutar med {lastFourDigits} från din medlemsprofil?",
|
||||||
|
"Are you sure you want to remove this product?": "Är du säker på att du vill ta bort den här produkten?",
|
||||||
"Arrival date": "Ankomstdatum",
|
"Arrival date": "Ankomstdatum",
|
||||||
"As our Close Friend": "Som vår nära vän",
|
"As our Close Friend": "Som vår nära vän",
|
||||||
"As our {level}": "Som vår {level}",
|
"As our {level}": "Som vår {level}",
|
||||||
@@ -181,6 +181,7 @@
|
|||||||
"Date of Birth": "Födelsedatum",
|
"Date of Birth": "Födelsedatum",
|
||||||
"Date of birth not matching": "Date of birth not matching",
|
"Date of birth not matching": "Date of birth not matching",
|
||||||
"Day": "Dag",
|
"Day": "Dag",
|
||||||
|
"Deadline: {date}": "Deadline: {date}",
|
||||||
"Delivered at:": "Levereras vid:",
|
"Delivered at:": "Levereras vid:",
|
||||||
"Delivery between {deliveryTime}. Payment will be made on check-in": "Leverans mellan {deliveryTime}. Betalning kommer att göras vid incheckning.",
|
"Delivery between {deliveryTime}. Payment will be made on check-in": "Leverans mellan {deliveryTime}. Betalning kommer att göras vid incheckning.",
|
||||||
"Description": "Beskrivning",
|
"Description": "Beskrivning",
|
||||||
@@ -458,6 +459,7 @@
|
|||||||
"On your journey": "På din resa",
|
"On your journey": "På din resa",
|
||||||
"Oops! Something went wrong while showing your surprise. Please refresh the page or try again later. If the issue persists, <link>contact the support.</link>": "Hoppsan! Något gick fel när din överraskning visades. Uppdatera sidan eller försök igen senare. Om problemet kvarstår, <link>kontakta supporten.</link>",
|
"Oops! Something went wrong while showing your surprise. Please refresh the page or try again later. If the issue persists, <link>contact the support.</link>": "Hoppsan! Något gick fel när din överraskning visades. Uppdatera sidan eller försök igen senare. Om problemet kvarstår, <link>kontakta supporten.</link>",
|
||||||
"Open": "Öppna",
|
"Open": "Öppna",
|
||||||
|
"Open for application": "Öppen för ansökan",
|
||||||
"Open image gallery": "Öppna bildgalleri",
|
"Open image gallery": "Öppna bildgalleri",
|
||||||
"Open language menu": "Öppna språkmenyn",
|
"Open language menu": "Öppna språkmenyn",
|
||||||
"Open menu": "Öppna menyn",
|
"Open menu": "Öppna menyn",
|
||||||
@@ -677,6 +679,7 @@
|
|||||||
"VAT {vat}%": "Moms {vat}%",
|
"VAT {vat}%": "Moms {vat}%",
|
||||||
"Valid through {expirationDate}": "Gäller till och med {expirationDate}",
|
"Valid through {expirationDate}": "Gäller till och med {expirationDate}",
|
||||||
"Verification code": "Verification code",
|
"Verification code": "Verification code",
|
||||||
|
"View & apply": "Visa & ansök",
|
||||||
"View all": "Visa alla",
|
"View all": "Visa alla",
|
||||||
"View all hotels in {country}": "Visa alla hotell i {country}",
|
"View all hotels in {country}": "Visa alla hotell i {country}",
|
||||||
"View and buy add-ons": "View and buy add-ons",
|
"View and buy add-ons": "View and buy add-ons",
|
||||||
@@ -796,6 +799,7 @@
|
|||||||
"{checkOutDate} from {checkOutTime}": "{checkOutDate} från {checkOutTime}",
|
"{checkOutDate} from {checkOutTime}": "{checkOutDate} från {checkOutTime}",
|
||||||
"{count, plural, one {{count} Hotel} other {{count} Hotels}}": "{count, plural, one {# hotell} other {# hotell}}",
|
"{count, plural, one {{count} Hotel} other {{count} Hotels}}": "{count, plural, one {# hotell} other {# hotell}}",
|
||||||
"{count, plural, one {{count} Location} other {{count} Locations}}": "{count, plural, one {# plats} other {# platser}}",
|
"{count, plural, one {{count} Location} other {{count} Locations}}": "{count, plural, one {# plats} other {# platser}}",
|
||||||
|
"{count, plural, one {{count} Result} other {{count} Results}}": "{count, plural, one {{count} Result} other {{count} Results}}",
|
||||||
"{count} destinations": "{count} destinationer",
|
"{count} destinations": "{count} destinationer",
|
||||||
"{count} hotels": "{count} hotels",
|
"{count} hotels": "{count} hotels",
|
||||||
"{count} lowercase letter": "{count} liten bokstav",
|
"{count} lowercase letter": "{count} liten bokstav",
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import d from "dayjs"
|
|||||||
import advancedFormat from "dayjs/plugin/advancedFormat"
|
import advancedFormat from "dayjs/plugin/advancedFormat"
|
||||||
import duration from "dayjs/plugin/duration"
|
import duration from "dayjs/plugin/duration"
|
||||||
import isSameOrAfter from "dayjs/plugin/isSameOrAfter"
|
import isSameOrAfter from "dayjs/plugin/isSameOrAfter"
|
||||||
|
import isSameOrBefore from "dayjs/plugin/isSameOrBefore"
|
||||||
import isToday from "dayjs/plugin/isToday"
|
import isToday from "dayjs/plugin/isToday"
|
||||||
import relativeTime from "dayjs/plugin/relativeTime"
|
import relativeTime from "dayjs/plugin/relativeTime"
|
||||||
import timezone from "dayjs/plugin/timezone"
|
import timezone from "dayjs/plugin/timezone"
|
||||||
@@ -65,6 +66,7 @@ d.extend(relativeTime)
|
|||||||
d.extend(timezone)
|
d.extend(timezone)
|
||||||
d.extend(utc)
|
d.extend(utc)
|
||||||
d.extend(isSameOrAfter)
|
d.extend(isSameOrAfter)
|
||||||
|
d.extend(isSameOrBefore)
|
||||||
d.extend(duration)
|
d.extend(duration)
|
||||||
|
|
||||||
export const dt = d
|
export const dt = d
|
||||||
|
|||||||
@@ -259,3 +259,7 @@ export const isBookingWidgetHidden = cache(
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
export const getJobylonFeed = cache(async function getMemoizedJobylonFeed() {
|
||||||
|
return serverClient().partner.jobylon.feed.get()
|
||||||
|
})
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import { router } from "@/server/trpc"
|
import { router } from "@/server/trpc"
|
||||||
|
|
||||||
|
import { jobylonQueryRouter } from "./jobylon/query"
|
||||||
import { sasRouter } from "./sas"
|
import { sasRouter } from "./sas"
|
||||||
|
|
||||||
export const partnerRouter = router({ sas: sasRouter })
|
export const partnerRouter = router({
|
||||||
|
sas: sasRouter,
|
||||||
|
jobylon: jobylonQueryRouter,
|
||||||
|
})
|
||||||
|
|||||||
121
apps/scandic-web/server/routers/partners/jobylon/output.ts
Normal file
121
apps/scandic-web/server/routers/partners/jobylon/output.ts
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
import { z } from "zod"
|
||||||
|
|
||||||
|
import { dt } from "@/lib/dt"
|
||||||
|
|
||||||
|
const categoriesSchema = z.array(
|
||||||
|
z
|
||||||
|
.object({ category: z.object({ id: z.number(), text: z.string() }) })
|
||||||
|
.transform(({ category }) => {
|
||||||
|
return {
|
||||||
|
id: category.id,
|
||||||
|
text: category.text,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
const departmentsSchema = z
|
||||||
|
.array(
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
department: z.object({
|
||||||
|
id: z.number(),
|
||||||
|
name: z.string(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.transform(({ department }) => {
|
||||||
|
if (!department.id || !department.name) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
id: department.id,
|
||||||
|
name: department.name,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.transform((departments) =>
|
||||||
|
departments.filter(
|
||||||
|
(department): department is NonNullable<typeof department> => !!department
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
const locationsSchema = z
|
||||||
|
.array(
|
||||||
|
z
|
||||||
|
.object({
|
||||||
|
location: z.object({
|
||||||
|
city: z.string().nullish(),
|
||||||
|
country: z.string().nullish(),
|
||||||
|
place_id: z.string().nullish(),
|
||||||
|
country_short: z.string().nullish(),
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
.transform(({ location }) => {
|
||||||
|
if (!location.city || !location.country) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
city: location.city,
|
||||||
|
country: location.country,
|
||||||
|
countryShort: location.country_short ?? null,
|
||||||
|
placeId: location.place_id ?? null,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
)
|
||||||
|
.transform((locations) =>
|
||||||
|
locations.filter(
|
||||||
|
(location): location is NonNullable<typeof location> => !!location
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
const urlsSchema = z
|
||||||
|
.object({
|
||||||
|
apply: z.string(),
|
||||||
|
ad: z.string(),
|
||||||
|
})
|
||||||
|
.transform(({ ad }) => ad)
|
||||||
|
|
||||||
|
export const jobylonItemSchema = z
|
||||||
|
.object({
|
||||||
|
id: z.number(),
|
||||||
|
title: z.string(),
|
||||||
|
from_date: z.string().nullish(),
|
||||||
|
to_date: z.string().nullish(),
|
||||||
|
categories: categoriesSchema,
|
||||||
|
departments: departmentsSchema,
|
||||||
|
locations: locationsSchema,
|
||||||
|
urls: urlsSchema,
|
||||||
|
})
|
||||||
|
.transform(
|
||||||
|
({
|
||||||
|
id,
|
||||||
|
from_date,
|
||||||
|
to_date,
|
||||||
|
title,
|
||||||
|
categories,
|
||||||
|
departments,
|
||||||
|
locations,
|
||||||
|
urls,
|
||||||
|
}) => {
|
||||||
|
const now = dt.utc()
|
||||||
|
const fromDate = from_date ? dt(from_date) : null
|
||||||
|
const toDate = to_date ? dt(to_date) : null
|
||||||
|
|
||||||
|
return {
|
||||||
|
id,
|
||||||
|
title,
|
||||||
|
isActive:
|
||||||
|
fromDate &&
|
||||||
|
now.isSameOrAfter(fromDate) &&
|
||||||
|
(!toDate || now.isSameOrBefore(toDate)),
|
||||||
|
categories,
|
||||||
|
departments,
|
||||||
|
toDate,
|
||||||
|
locations,
|
||||||
|
url: urls,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
export const jobylonFeedSchema = z
|
||||||
|
.array(jobylonItemSchema)
|
||||||
|
.transform((jobs) => jobs.filter((job) => job.isActive))
|
||||||
94
apps/scandic-web/server/routers/partners/jobylon/query.ts
Normal file
94
apps/scandic-web/server/routers/partners/jobylon/query.ts
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
import { publicProcedure, router } from "@/server/trpc"
|
||||||
|
|
||||||
|
import { jobylonFeedSchema } from "./output"
|
||||||
|
import {
|
||||||
|
getJobylonFeedCounter,
|
||||||
|
getJobylonFeedFailCounter,
|
||||||
|
getJobylonFeedSuccessCounter,
|
||||||
|
} from "./telemetry"
|
||||||
|
|
||||||
|
export const TWENTYFOUR_HOURS = 60 * 60 * 24
|
||||||
|
|
||||||
|
// The URL for the Jobylon feed including the hash for the specific feed.
|
||||||
|
// The URL and hash are generated by Jobylon. Documentation: https://developer.jobylon.com/feed-api
|
||||||
|
const feedUrl =
|
||||||
|
"https://feed.jobylon.com/feeds/cc04ba19-f0bd-4412-8b9b-d1d1fcbf0800"
|
||||||
|
|
||||||
|
export const jobylonQueryRouter = router({
|
||||||
|
feed: router({
|
||||||
|
get: publicProcedure.query(async function () {
|
||||||
|
const url = new URL(feedUrl)
|
||||||
|
url.search = new URLSearchParams({
|
||||||
|
format: "json",
|
||||||
|
}).toString()
|
||||||
|
const urlString = url.toString()
|
||||||
|
|
||||||
|
getJobylonFeedCounter.add(1, { url: urlString })
|
||||||
|
console.info(
|
||||||
|
"jobylon.feed start",
|
||||||
|
JSON.stringify({ query: { url: urlString } })
|
||||||
|
)
|
||||||
|
|
||||||
|
const response = await fetch(url, {
|
||||||
|
cache: "force-cache",
|
||||||
|
next: {
|
||||||
|
revalidate: TWENTYFOUR_HOURS,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const text = await response.text()
|
||||||
|
const error = {
|
||||||
|
status: response.status,
|
||||||
|
statusText: response.statusText,
|
||||||
|
text,
|
||||||
|
}
|
||||||
|
getJobylonFeedFailCounter.add(1, {
|
||||||
|
url: urlString,
|
||||||
|
error_type: "http_error",
|
||||||
|
error: JSON.stringify(error),
|
||||||
|
})
|
||||||
|
console.error(
|
||||||
|
"jobylon.feed error",
|
||||||
|
JSON.stringify({
|
||||||
|
query: { url: urlString },
|
||||||
|
error,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
const responseJson = await response.json()
|
||||||
|
const validatedResponse = jobylonFeedSchema.safeParse(responseJson)
|
||||||
|
|
||||||
|
if (!validatedResponse.success) {
|
||||||
|
getJobylonFeedFailCounter.add(1, {
|
||||||
|
urlString,
|
||||||
|
error_type: "validation_error",
|
||||||
|
error: JSON.stringify(validatedResponse.error),
|
||||||
|
})
|
||||||
|
|
||||||
|
console.error(
|
||||||
|
"jobylon.feed error",
|
||||||
|
JSON.stringify({
|
||||||
|
query: { url: urlString },
|
||||||
|
error: validatedResponse.error,
|
||||||
|
})
|
||||||
|
)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
getJobylonFeedSuccessCounter.add(1, {
|
||||||
|
url: urlString,
|
||||||
|
})
|
||||||
|
console.info(
|
||||||
|
"jobylon.feed success",
|
||||||
|
JSON.stringify({
|
||||||
|
query: { url: urlString },
|
||||||
|
})
|
||||||
|
)
|
||||||
|
|
||||||
|
return validatedResponse.data
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
})
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { metrics } from "@opentelemetry/api"
|
||||||
|
|
||||||
|
const meter = metrics.getMeter("trpc.booking")
|
||||||
|
export const getJobylonFeedCounter = meter.createCounter("trpc.jobylon-feed")
|
||||||
|
export const getJobylonFeedSuccessCounter = meter.createCounter(
|
||||||
|
"trpc.jobylon-feed-success"
|
||||||
|
)
|
||||||
|
export const getJobylonFeedFailCounter = meter.createCounter(
|
||||||
|
"trpc.jobylon-feed-fail"
|
||||||
|
)
|
||||||
@@ -5,6 +5,7 @@ export namespace DynamicContentEnum {
|
|||||||
earn_and_burn = "earn_and_burn",
|
earn_and_burn = "earn_and_burn",
|
||||||
expiring_points = "expiring_points",
|
expiring_points = "expiring_points",
|
||||||
how_it_works = "how_it_works",
|
how_it_works = "how_it_works",
|
||||||
|
jobylon_feed = "jobylon_feed",
|
||||||
loyalty_levels = "loyalty_levels",
|
loyalty_levels = "loyalty_levels",
|
||||||
membership_overview = "membership_overview",
|
membership_overview = "membership_overview",
|
||||||
my_points = "my_points",
|
my_points = "my_points",
|
||||||
@@ -12,12 +13,12 @@ export namespace DynamicContentEnum {
|
|||||||
overview_table = "overview_table",
|
overview_table = "overview_table",
|
||||||
points_overview = "points_overview",
|
points_overview = "points_overview",
|
||||||
previous_stays = "previous_stays",
|
previous_stays = "previous_stays",
|
||||||
|
sas_linked_account = "sas_linked_account",
|
||||||
|
sas_tier_comparison = "sas_tier_comparison",
|
||||||
sign_up_form = "sign_up_form",
|
sign_up_form = "sign_up_form",
|
||||||
sign_up_verification = "sign_up_verification",
|
sign_up_verification = "sign_up_verification",
|
||||||
soonest_stays = "soonest_stays",
|
soonest_stays = "soonest_stays",
|
||||||
upcoming_stays = "upcoming_stays",
|
upcoming_stays = "upcoming_stays",
|
||||||
sas_linked_account = "sas_linked_account",
|
|
||||||
sas_tier_comparison = "sas_tier_comparison",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Type needed to satisfy zod enum type */
|
/** Type needed to satisfy zod enum type */
|
||||||
@@ -26,6 +27,7 @@ export namespace DynamicContentEnum {
|
|||||||
components.earn_and_burn,
|
components.earn_and_burn,
|
||||||
components.expiring_points,
|
components.expiring_points,
|
||||||
components.how_it_works,
|
components.how_it_works,
|
||||||
|
components.jobylon_feed,
|
||||||
components.loyalty_levels,
|
components.loyalty_levels,
|
||||||
components.membership_overview,
|
components.membership_overview,
|
||||||
components.my_points,
|
components.my_points,
|
||||||
@@ -33,12 +35,12 @@ export namespace DynamicContentEnum {
|
|||||||
components.overview_table,
|
components.overview_table,
|
||||||
components.points_overview,
|
components.points_overview,
|
||||||
components.previous_stays,
|
components.previous_stays,
|
||||||
|
components.sas_linked_account,
|
||||||
|
components.sas_tier_comparison,
|
||||||
components.sign_up_form,
|
components.sign_up_form,
|
||||||
components.sign_up_verification,
|
components.sign_up_verification,
|
||||||
components.soonest_stays,
|
components.soonest_stays,
|
||||||
components.upcoming_stays,
|
components.upcoming_stays,
|
||||||
components.sas_linked_account,
|
|
||||||
components.sas_tier_comparison,
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
5
apps/scandic-web/types/trpc/routers/jobylon/index.ts
Normal file
5
apps/scandic-web/types/trpc/routers/jobylon/index.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import type { z } from "zod"
|
||||||
|
|
||||||
|
import type { jobylonItemSchema } from "@/server/routers/partners/jobylon/output"
|
||||||
|
|
||||||
|
export interface JobylonItem extends z.output<typeof jobylonItemSchema> {}
|
||||||
Reference in New Issue
Block a user