Merged in feature/SW-2320-languagebased-hide-for-next-release (pull request #1937)

Language based alternative to HIDE_FOR_NEXT_RELEASE

Approved-by: Anton Gunnarsson
This commit is contained in:
Joakim Jäderberg
2025-05-05 10:53:28 +00:00
parent 3bcf6cff4a
commit 5784822a1e
41 changed files with 232 additions and 133 deletions

View File

@@ -54,7 +54,6 @@ GOOGLE_DYNAMIC_MAP_ID=""
NEXT_PUBLIC_HIDE_FOR_NEXT_RELEASE="false"
ENABLE_BOOKING_FLOW="false"
ENABLE_BOOKING_WIDGET="false"
ENABLE_BOOKING_WIDGET_HOTELRESERVATION_PATH="false"
ENABLE_SURPRISES="true"

View File

@@ -42,11 +42,11 @@ GOOGLE_STATIC_MAP_SIGNATURE_SECRET="test"
GOOGLE_STATIC_MAP_ID="test"
GOOGLE_DYNAMIC_MAP_ID="test"
NEXT_PUBLIC_HIDE_FOR_NEXT_RELEASE="true"
NEXT_PUBLIC_HIDE_FOR_NEXT_RELEASE_LANG="en,sv,da,de,fi,no"
SALESFORCE_PREFERENCE_BASE_URL="test"
USE_NEW_REWARD_MODEL="true"
TZ=UTC
ENABLE_BOOKING_FLOW="false"
ENABLE_BOOKING_WIDGET="false"
ENABLE_BOOKING_WIDGET_HOTELRESERVATION_PATH="false"
ENABLE_SURPRISES="true"

View File

@@ -4,8 +4,9 @@ import CurrentLoadingSpinner from "@/components/Current/LoadingSpinner"
import LoadingSpinner from "@/components/LoadingSpinner"
export default function Loading() {
if (env.HIDE_FOR_NEXT_RELEASE) {
if (env.NEW_SITE_LIVE_STATUS === "NOT_LIVE") {
return <CurrentLoadingSpinner />
}
return <LoadingSpinner />
}

View File

@@ -13,8 +13,8 @@ export { generateMetadata } from "@/utils/generateMetadata"
export default function DestinationCityPagePage({
searchParams,
}: PageArgs<{}, { view?: "map" }>) {
if (env.HIDE_FOR_NEXT_RELEASE) {
notFound()
if (env.NEW_SITE_LIVE_STATUS === "NOT_LIVE") {
return notFound()
}
return (

View File

@@ -6,16 +6,16 @@ import { env } from "@/env/server"
import DestinationCountryPage from "@/components/ContentType/DestinationPage/DestinationCountryPage"
import DestinationCountryPageSkeleton from "@/components/ContentType/DestinationPage/DestinationCountryPage/DestinationCountryPageSkeleton"
import type { PageArgs } from "@/types/params"
import type { LangParams, PageArgs } from "@/types/params"
export { generateMetadata } from "@/utils/generateMetadata"
export default function DestinationCountryPagePage({}: PageArgs<
{},
LangParams,
{ view?: "map" }
>) {
if (env.HIDE_FOR_NEXT_RELEASE) {
notFound()
if (env.NEW_SITE_LIVE_STATUS === "NOT_LIVE") {
return notFound()
}
return (

View File

@@ -4,10 +4,12 @@ import { env } from "@/env/server"
import DestinationOverviewPage from "@/components/ContentType/DestinationPage/DestinationOverviewPage"
import type { LangParams, PageArgs } from "@/types/params"
export { generateMetadata } from "@/utils/generateMetadata"
export default function DestinationOverviewPagePage() {
if (env.HIDE_FOR_NEXT_RELEASE) {
export default function DestinationOverviewPagePage({}: PageArgs<LangParams>) {
if (env.NEW_SITE_LIVE_STATUS === "NOT_LIVE") {
return notFound()
}

View File

@@ -14,13 +14,14 @@ export { generateMetadata } from "@/utils/generateMetadata"
export default async function HotelPagePage({
searchParams,
}: PageArgs<{}, { subpage?: string; view?: "map" }>) {
if (env.HIDE_FOR_NEXT_RELEASE) {
if (env.NEW_SITE_LIVE_STATUS === "NOT_LIVE") {
return notFound()
}
const hotelPageData = await getHotelPage()
if (!hotelPageData) {
notFound()
return notFound()
}
if (searchParams.subpage) {

View File

@@ -12,8 +12,9 @@ export { generateMetadata } from "@/utils/generateMetadata"
export default function StartPagePage({
searchParams,
}: PageArgs<{}, BookingWidgetSearchData>) {
if (env.HIDE_FOR_NEXT_RELEASE) {
notFound()
if (env.NEW_SITE_LIVE_STATUS === "NOT_LIVE") {
return notFound()
}
return <StartPage searchParams={searchParams} />
}

View File

@@ -11,7 +11,7 @@ import {
import type { LangParams, PageArgs } from "@/types/params"
export default function HotelReservationPage({ params }: PageArgs<LangParams>) {
if (!env.ENABLE_BOOKING_WIDGET_HOTELRESERVATION_PATH) {
if (env.NEW_SITE_LIVE_STATUS === "NOT_LIVE") {
return null
}

View File

@@ -1,21 +1,22 @@
import { notFound } from "next/navigation"
import { env } from "@/env/server"
import type { Metadata } from "next"
import type { LangParams, PageArgs } from "@/types/params"
export const metadata: Metadata = {
robots: {
index: false,
follow: false,
},
export async function generateMetadata({ params }: PageArgs<LangParams>) {
return {
robots: {
index: env.isLangLive(params.lang),
follow: env.isLangLive(params.lang),
},
}
}
export default function HotelReservationLayout({
children,
}: React.PropsWithChildren) {
if (!env.ENABLE_BOOKING_FLOW) {
return notFound()
if (env.NEW_SITE_LIVE_STATUS === "NOT_LIVE") {
return null
}
return <>{children}</>
}

View File

@@ -175,7 +175,7 @@ export default async function MyStay({
hotel.galleryImages[0]?.imageSizes.large
const baseUrl = env.PUBLIC_URL || "https://www.scandichotels.com"
const promoUrl = env.HIDE_FOR_NEXT_RELEASE
const promoUrl = !env.isLangLive(lang)
? new URL(getCurrentWebUrl({ path: "/", lang }))
: new URL(`${baseUrl}/${lang}/`)

View File

@@ -1,7 +1,7 @@
import { env } from "@/env/server"
import { getDestinationCityPage } from "@/lib/trpc/memoizedRequests"
import BookingWidget from "@/components/BookingWidget"
import { BookingWidget } from "@/components/BookingWidget"
import type { BookingWidgetSearchData } from "@/types/components/bookingWidget"
import type { PageArgs } from "@/types/params"
@@ -9,9 +9,10 @@ import type { PageArgs } from "@/types/params"
export default async function BookingWidgetDestinationCityPage({
searchParams,
}: PageArgs<{}, BookingWidgetSearchData>) {
if (!env.ENABLE_BOOKING_WIDGET) {
if (env.NEW_SITE_LIVE_STATUS === "NOT_LIVE") {
return null
}
const { bookingCode } = searchParams
const pageData = await getDestinationCityPage()

View File

@@ -1,7 +1,7 @@
import { env } from "@/env/server"
import { getHotel, getHotelPage } from "@/lib/trpc/memoizedRequests"
import BookingWidget from "@/components/BookingWidget"
import { BookingWidget } from "@/components/BookingWidget"
import { getLang } from "@/i18n/serverContext"
import type { BookingWidgetSearchData } from "@/types/components/bookingWidget"
@@ -10,9 +10,10 @@ import type { PageArgs } from "@/types/params"
export default async function BookingWidgetHotelPage({
searchParams,
}: PageArgs<{}, BookingWidgetSearchData & { subpage?: string }>) {
if (!env.ENABLE_BOOKING_WIDGET) {
if (env.NEW_SITE_LIVE_STATUS === "NOT_LIVE") {
return null
}
const { bookingCode, subpage } = searchParams
const hotelPageData = await getHotelPage()

View File

@@ -1,11 +0,0 @@
import { env } from "@/env/server"
import { BookingWidgetSkeleton } from "@/components/BookingWidget/Client"
export default function LoadingBookingWidget() {
if (!env.ENABLE_BOOKING_WIDGET_HOTELRESERVATION_PATH) {
return null
}
return <BookingWidgetSkeleton />
}

View File

@@ -1,6 +1,6 @@
import { env } from "@/env/server"
import BookingWidget from "@/components/BookingWidget"
import { BookingWidget } from "@/components/BookingWidget"
import type { BookingWidgetSearchData } from "@/types/components/bookingWidget"
import type { LangParams, PageArgs } from "@/types/params"
@@ -8,7 +8,7 @@ import type { LangParams, PageArgs } from "@/types/params"
export default async function BookingWidgetPage({
searchParams,
}: PageArgs<LangParams, BookingWidgetSearchData>) {
if (!env.ENABLE_BOOKING_WIDGET_HOTELRESERVATION_PATH) {
if (env.NEW_SITE_LIVE_STATUS === "NOT_LIVE") {
return null
}

View File

@@ -1,11 +0,0 @@
import { env } from "@/env/server"
import { BookingWidgetSkeleton } from "@/components/BookingWidget/Client"
export default function LoadingBookingWidget() {
if (!env.ENABLE_BOOKING_WIDGET) {
return null
}
return <BookingWidgetSkeleton />
}

View File

@@ -1,14 +1,15 @@
import { env } from "@/env/server"
import BookingWidget from "@/components/BookingWidget"
import { BookingWidget } from "@/components/BookingWidget"
import type { BookingWidgetSearchData } from "@/types/components/bookingWidget"
import type { PageArgs } from "@/types/params"
import type { LangParams, PageArgs } from "@/types/params"
export default async function BookingWidgetPage({
searchParams,
}: PageArgs<{}, BookingWidgetSearchData>) {
if (!env.ENABLE_BOOKING_WIDGET) {
params,
}: PageArgs<LangParams, BookingWidgetSearchData>) {
if (!env.isLangLive(params.lang)) {
return null
}

View File

@@ -1,21 +1,22 @@
import { notFound } from "next/navigation"
import { env } from "@/env/server"
import type { Metadata } from "next"
import type { LangParams, PageArgs } from "@/types/params"
export const metadata: Metadata = {
robots: {
index: false,
follow: false,
},
export async function generateMetadata({ params }: PageArgs<LangParams>) {
return {
robots: {
index: env.isLangLive(params.lang),
follow: env.isLangLive(params.lang),
},
}
}
export default function HotelReservationLayout({
children,
}: React.PropsWithChildren) {
if (!env.ENABLE_BOOKING_FLOW) {
return notFound()
if (env.NEW_SITE_LIVE_STATUS === "NOT_LIVE") {
return null
}
return <>{children}</>
}

View File

@@ -172,7 +172,7 @@ export default async function MyStay({
hotel.galleryImages[0]?.imageSizes.large
const baseUrl = env.PUBLIC_URL || "https://www.scandichotels.com"
const promoUrl = env.HIDE_FOR_NEXT_RELEASE
const promoUrl = !env.isLangLive(params.lang)
? new URL(getCurrentWebUrl({ path: "/", lang }))
: new URL(`${baseUrl}/${lang}/`)

View File

@@ -12,8 +12,8 @@ export async function GET(
_request: NextRequest,
context: { params: { sitemapId: string } }
) {
if (env.HIDE_FOR_NEXT_RELEASE) {
notFound()
if (env.NEW_SITE_LIVE_STATUS !== "ALL_LANGUAGES_LIVE") {
return notFound()
}
const sitemapId = context.params.sitemapId
@@ -21,12 +21,12 @@ export async function GET(
console.log("[SITEMAP] Fetching sitemap by ID", sitemapId)
if (!sitemapId) {
notFound()
return notFound()
}
const sitemapData = await getSitemapDataById(Number(sitemapId))
if (!sitemapData.length) {
notFound()
return notFound()
}
const entries = sitemapData.map((entry) => {

View File

@@ -7,8 +7,8 @@ import { getLastUpdated, getSitemapIds } from "@/utils/sitemap"
export const dynamic = "force-dynamic"
export async function GET() {
if (env.HIDE_FOR_NEXT_RELEASE) {
notFound()
if (env.NEW_SITE_LIVE_STATUS !== "ALL_LANGUAGES_LIVE") {
return notFound()
}
console.log(`[SITEMAP] Fetching sitemap`)

View File

@@ -15,7 +15,7 @@ export default async function EmptyUpcomingStaysBlock() {
const lang = getLang()
const baseUrl = env.PUBLIC_URL || "https://www.scandichotels.com"
const href = env.HIDE_FOR_NEXT_RELEASE
const href = !env.isLangLive(lang)
? getCurrentWebUrl({ path: "/", lang, baseUrl })
: `/${lang}`

View File

@@ -15,7 +15,7 @@ export default async function EmptyUpcomingStaysBlock() {
const lang = getLang()
const baseUrl = env.PUBLIC_URL || "https://www.scandichotels.com"
const href = env.HIDE_FOR_NEXT_RELEASE
const href = !env.isLangLive(lang)
? getCurrentWebUrl({ path: "/", lang, baseUrl })
: `/${lang}`

View File

@@ -41,7 +41,7 @@ function DynamicContentBlocks(props: DynamicContentProps) {
case DynamicContentEnum.Blocks.components.earn_and_burn:
return <EarnAndBurn {...dynamic_content} />
case DynamicContentEnum.Blocks.components.expiring_points:
return env.HIDE_FOR_NEXT_RELEASE ? null : (
return env.NEW_SITE_LIVE_STATUS === "NOT_LIVE" ? null : (
<ExpiringPoints {...dynamic_content} />
)
case DynamicContentEnum.Blocks.components.how_it_works:

View File

@@ -1,13 +1,23 @@
import { Suspense } from "react"
import {
getPageSettingsBookingCode,
isBookingWidgetHidden,
} from "@/lib/trpc/memoizedRequests"
import BookingWidgetClient from "./Client"
import BookingWidgetClient, { BookingWidgetSkeleton } from "./Client"
import type { BookingWidgetProps } from "@/types/components/bookingWidget"
export default async function BookingWidget({
export async function BookingWidget(props: BookingWidgetProps) {
return (
<Suspense fallback={<BookingWidgetSkeleton />}>
<InternalBookingWidget {...props} />
</Suspense>
)
}
async function InternalBookingWidget({
type,
bookingWidgetSearchParams,
}: BookingWidgetProps) {

View File

@@ -2,6 +2,7 @@ import { env } from "@/env/server"
import { getFooter } from "@/lib/trpc/memoizedRequests"
import CurrentFooter from "@/components/Current/Footer"
import { getLang } from "@/i18n/serverContext"
import FooterDetails from "./Details"
import FooterNavigation from "./Navigation"
@@ -13,7 +14,8 @@ export function preload() {
}
export default function Footer() {
if (env.HIDE_FOR_NEXT_RELEASE) {
const lang = getLang()
if (!env.isLangLive(lang)) {
return <CurrentFooter />
}

View File

@@ -16,6 +16,7 @@ import useDropdownStore from "@/stores/main-menu"
import { IconName } from "@/components/Icons/iconName"
import LanguageSwitcher from "@/components/LanguageSwitcher"
import { useHandleKeyUp } from "@/hooks/useHandleKeyUp"
import { useIsLangLive } from "@/hooks/useIsLangLive"
import useLang from "@/hooks/useLang"
import { getCurrentWebUrl } from "@/utils/url"
@@ -32,6 +33,7 @@ export default function MobileMenu({
topLink,
isLoggedIn,
}: React.PropsWithChildren<MobileMenuProps>) {
const isLangLive = useIsLangLive()
const lang = useLang()
const intl = useIntl()
const {
@@ -76,7 +78,7 @@ export default function MobileMenu({
})
const baseUrl = env.NEXT_PUBLIC_PUBLIC_URL || "https://www.scandichotels.com"
const findMyBookingUrl = env.NEXT_PUBLIC_HIDE_FOR_NEXT_RELEASE
const findMyBookingUrl = !isLangLive
? getCurrentWebUrl({
path: findMyBookingCurrentWebPath[lang],
lang,

View File

@@ -34,7 +34,7 @@ export default async function TopMenu() {
const lang = getLang()
const baseUrl = env.PUBLIC_URL || "https://www.scandichotels.com"
const findMyBookingUrl = env.HIDE_FOR_NEXT_RELEASE
const findMyBookingUrl = !env.isLangLive(lang)
? getCurrentWebUrl({
path: findMyBookingCurrentWebPath[lang],
lang,

View File

@@ -4,6 +4,7 @@ import { env } from "@/env/server"
import { getHeader, getName } from "@/lib/trpc/memoizedRequests"
import CurrentHeader from "@/components/Current/Header"
import { getLang } from "@/i18n/serverContext"
import HeaderFallback from "../Current/Header/HeaderFallback"
import MainMenu from "./MainMenu"
@@ -14,7 +15,8 @@ import styles from "./header.module.css"
export default function Header() {
void getName()
if (env.HIDE_FOR_NEXT_RELEASE) {
const lang = getLang()
if (!env.isLangLive(lang)) {
return (
<Suspense fallback={<HeaderFallback />}>
<CurrentHeader />

View File

@@ -28,9 +28,6 @@ export default function LanguageSwitcherContent({
const urlKeys = Object.keys(urls) as Lang[]
const pathname = usePathname()
const relValue = env.NEXT_PUBLIC_HIDE_FOR_NEXT_RELEASE
? "nofollow"
: undefined
return (
<div className={styles.languageWrapper}>
@@ -43,13 +40,14 @@ export default function LanguageSwitcherContent({
{urlKeys.map((key) => {
const url = urls[key]?.url
const isActive = currentLanguage === key
if (url) {
return (
<li key={key}>
<Link
className={`${styles.link} ${isActive ? styles.active : ""}`}
href={replaceUrlPart(pathname, url)}
rel={relValue}
rel={env.isLangLive(key) ? undefined : "nofollow"}
onClick={onLanguageSwitch}
keepSearchParams
>

View File

@@ -1,18 +1,24 @@
import { createEnv } from "@t3-oss/env-nextjs"
import { z } from "zod"
export const env = createEnv({
import { getLiveStatus } from "./getLiveStatus"
import { isLangLive } from "./isLangLive"
import type { Lang } from "@/constants/languages"
const _env = createEnv({
client: {
NEXT_PUBLIC_NODE_ENV: z.enum(["development", "test", "production"]),
NEXT_PUBLIC_PORT: z.string().default("3000"),
NEXT_PUBLIC_SENTRY_ENVIRONMENT: z.string().default("development"),
NEXT_PUBLIC_SENTRY_CLIENT_SAMPLERATE: z.coerce.number().default(0.001),
NEXT_PUBLIC_HIDE_FOR_NEXT_RELEASE: z
NEXT_PUBLIC_NEW_SITE_LIVE_FOR_LANGS: z
.string()
// only allow "true" or "false"
.refine((s) => s === "true" || s === "false")
// transform to boolean
.transform((s) => s === "true"),
.regex(/^([a-z]{2},)*([a-z]{2}){0,1}$/)
.transform((val) => {
return val.split(",")
})
.default(""),
NEXT_PUBLIC_PUBLIC_URL: z.string().optional(),
},
emptyStringAsUndefined: true,
@@ -22,8 +28,15 @@ export const env = createEnv({
NEXT_PUBLIC_SENTRY_ENVIRONMENT: process.env.NEXT_PUBLIC_SENTRY_ENVIRONMENT,
NEXT_PUBLIC_SENTRY_CLIENT_SAMPLERATE:
process.env.NEXT_PUBLIC_SENTRY_CLIENT_SAMPLERATE,
NEXT_PUBLIC_HIDE_FOR_NEXT_RELEASE:
process.env.NEXT_PUBLIC_HIDE_FOR_NEXT_RELEASE,
NEXT_PUBLIC_NEW_SITE_LIVE_FOR_LANGS:
process.env.NEXT_PUBLIC_NEW_SITE_LIVE_FOR_LANGS,
NEXT_PUBLIC_PUBLIC_URL: process.env.NEXT_PUBLIC_PUBLIC_URL,
},
})
export const env = {
..._env,
NEW_SITE_LIVE_STATUS: getLiveStatus(_env),
isLangLive: (lang: Lang) =>
isLangLive(lang, _env.NEXT_PUBLIC_NEW_SITE_LIVE_FOR_LANGS),
}

54
apps/scandic-web/env/getLiveStatus.ts vendored Normal file
View File

@@ -0,0 +1,54 @@
// Cannot use from "@/constants/languages" due to jiti
import { Lang } from "../constants/languages"
export function getLiveStatus(
internalEnv:
| {
NEW_SITE_LIVE_FOR_LANGS: string[]
}
| {
NEXT_PUBLIC_NEW_SITE_LIVE_FOR_LANGS: string[]
}
): "ALL_LANGUAGES_LIVE" | "SOME_LANGUAGES_LIVE" | "NOT_LIVE" {
const configuredLangs = getConfiguredLangs(internalEnv)
if (!configuredLangs) {
return "NOT_LIVE"
}
const allLangs = Object.keys(Lang)
const liveLangs = allLangs.filter((lang) => configuredLangs.includes(lang))
if (liveLangs.length === 0) {
return "NOT_LIVE"
}
if (
liveLangs.length === allLangs.length &&
allLangs.every((lang) => liveLangs.includes(lang))
) {
return "ALL_LANGUAGES_LIVE"
}
return "SOME_LANGUAGES_LIVE"
}
function getConfiguredLangs<T extends {}>(env: T): string[] | undefined {
if (
!(
"NEW_SITE_LIVE_FOR_LANGS" in env ||
"NEXT_PUBLIC_NEW_SITE_LIVE_FOR_LANGS" in env
)
) {
return undefined
}
const configuredLangs =
"NEW_SITE_LIVE_FOR_LANGS" in env
? env.NEW_SITE_LIVE_FOR_LANGS
: env.NEXT_PUBLIC_NEW_SITE_LIVE_FOR_LANGS
if (!Array.isArray(configuredLangs)) {
throw new Error("Misconfigured environment variable, expected array")
}
return configuredLangs
}

18
apps/scandic-web/env/isLangLive.test.ts vendored Normal file
View File

@@ -0,0 +1,18 @@
import { describe, expect, it } from "@jest/globals"
import { Lang } from "@/constants/languages"
import { isLangLive } from "./isLangLive"
describe("hideForNextRelease", () => {
it("should return true if en is part of live languages", () => {
expect(isLangLive(Lang.en, ["en", "sv"])).toBe(true)
expect(isLangLive(Lang.en, ["en"])).toBe(true)
})
it("should return false if en is not part of live languages", () => {
expect(isLangLive(Lang.en, [])).toBe(false)
expect(isLangLive(Lang.en, ["sv"])).toBe(false)
expect(isLangLive(Lang.en, ["sv,fi"])).toBe(false)
})
})

5
apps/scandic-web/env/isLangLive.ts vendored Normal file
View File

@@ -0,0 +1,5 @@
import type { Lang } from "@/constants/languages"
export function isLangLive(lang: Lang, liveLangs: string[]): boolean {
return liveLangs.includes(lang)
}

View File

@@ -1,9 +1,14 @@
import { createEnv } from "@t3-oss/env-nextjs"
import { z } from "zod"
import { getLiveStatus } from "./getLiveStatus"
import { isLangLive } from "./isLangLive"
import type { Lang } from "../constants/languages"
const TWENTYFOUR_HOURS = 24 * 60 * 60
export const env = createEnv({
const _env = createEnv({
/**
* Due to t3-env only checking typeof window === "undefined"
* and Netlify running Deno, window is never "undefined"
@@ -107,19 +112,6 @@ export const env = createEnv({
GOOGLE_STATIC_MAP_SIGNATURE_SECRET: z.string(),
GOOGLE_DYNAMIC_MAP_ID: z.string(),
GOOGLE_STATIC_MAP_ID: z.string(),
HIDE_FOR_NEXT_RELEASE: z
.string()
// only allow "true" or "false"
.refine((s) => s === "true" || s === "false")
// transform to boolean
.transform((s) => s === "true"),
ENABLE_BOOKING_FLOW: z
.string()
// only allow "true" or "false"
.refine((s) => s === "true" || s === "false")
// transform to boolean
.transform((s) => s === "true")
.default("true"),
ENABLE_BOOKING_WIDGET: z
.string()
// only allow "true" or "false"
@@ -127,13 +119,6 @@ export const env = createEnv({
// transform to boolean
.transform((s) => s === "true")
.default("false"),
ENABLE_BOOKING_WIDGET_HOTELRESERVATION_PATH: z
.string()
// only allow "true" or "false"
.refine((s) => s === "true" || s === "false")
// transform to boolean
.transform((s) => s === "true")
.default("true"),
ENABLE_SURPRISES: z
.string()
// only allow "true" or "false"
@@ -200,6 +185,17 @@ export const env = createEnv({
.transform((s) => s === "true")
.default("true"),
WARMUP_TOKEN: z.string().optional(),
/**
* Include the languages that should be hidden for the next release
* Should be in the format of "en,da,de,fi,no,sv" or empty
*/
NEW_SITE_LIVE_FOR_LANGS: z
.string()
.regex(/^([a-z]{2},)*([a-z]{2}){0,1}$/)
.transform((val) => {
return val.split(",")
})
.default(""),
},
emptyStringAsUndefined: true,
runtimeEnv: {
@@ -268,12 +264,8 @@ export const env = createEnv({
process.env.GOOGLE_STATIC_MAP_SIGNATURE_SECRET,
GOOGLE_STATIC_MAP_ID: process.env.GOOGLE_STATIC_MAP_ID,
GOOGLE_DYNAMIC_MAP_ID: process.env.GOOGLE_DYNAMIC_MAP_ID,
HIDE_FOR_NEXT_RELEASE: process.env.NEXT_PUBLIC_HIDE_FOR_NEXT_RELEASE,
USE_NEW_REWARD_MODEL: process.env.USE_NEW_REWARD_MODEL,
ENABLE_BOOKING_FLOW: process.env.ENABLE_BOOKING_FLOW,
ENABLE_BOOKING_WIDGET: process.env.ENABLE_BOOKING_WIDGET,
ENABLE_BOOKING_WIDGET_HOTELRESERVATION_PATH:
process.env.ENABLE_BOOKING_WIDGET_HOTELRESERVATION_PATH,
ENABLE_SURPRISES: process.env.ENABLE_SURPRISES,
SHOW_SITE_WIDE_ALERT: process.env.SHOW_SITE_WIDE_ALERT,
SENTRY_ENVIRONMENT: process.env.NEXT_PUBLIC_SENTRY_ENVIRONMENT,
@@ -295,9 +287,16 @@ export const env = createEnv({
GIT_SHA: process.env.GIT_SHA,
ENABLE_WARMUP_HOTEL: process.env.ENABLE_WARMUP_HOTEL,
WARMUP_TOKEN: process.env.WARMUP_TOKEN,
NEW_SITE_LIVE_FOR_LANGS: process.env.NEXT_PUBLIC_NEW_SITE_LIVE_FOR_LANGS,
},
})
export const env = {
..._env,
NEW_SITE_LIVE_STATUS: getLiveStatus(_env),
isLangLive: (lang: Lang) => isLangLive(lang, _env.NEW_SITE_LIVE_FOR_LANGS),
} as const
function replaceTopLevelDomain(url: string, domain: string) {
return url.replaceAll("{topleveldomain}", domain)
}

View File

@@ -0,0 +1,10 @@
"use client"
import { env } from "@/env/client"
import useLang from "./useLang"
export function useIsLangLive(): boolean {
const lang = useLang()
return env.isLangLive(lang)
}

View File

@@ -2,7 +2,6 @@
import { useCallback, useEffect, useState } from "react"
import { env } from "@/env/client"
import useStickyPositionStore, {
type StickyElement,
type StickyElementNameEnum,
@@ -10,6 +9,8 @@ import useStickyPositionStore, {
import { debounce } from "@/utils/debounce"
import { useIsLangLive } from "./useIsLangLive"
interface UseStickyPositionProps {
ref?: React.RefObject<HTMLElement>
name?: StickyElementNameEnum
@@ -46,6 +47,7 @@ export default function useStickyPosition({
getAllElements,
} = useStickyPositionStore()
const isLangLive = useIsLangLive()
/* Used for Current mobile header since that doesn't use this hook.
*
* Instead, calculate if the mobile header is shown and add the height of
@@ -123,7 +125,7 @@ export default function useStickyPosition({
updateHeights()
// Only do this special handling if we have the current header
if (env.NEXT_PUBLIC_HIDE_FOR_NEXT_RELEASE) {
if (!isLangLive) {
if (document.body.clientWidth > 950) {
setBaseTopOffset(0)
} else {
@@ -142,7 +144,7 @@ export default function useStickyPosition({
resizeObserver.unobserve(document.body)
}
}
}, [updateHeights])
}, [updateHeights, isLangLive])
return {
currentHeight: ref?.current?.offsetHeight || null,

View File

@@ -1,7 +1,5 @@
import { type NextMiddleware, NextResponse } from "next/server"
import { notFound } from "@/server/errors/next"
import { getUidAndContentTypeByPath } from "@/services/cms/getUidAndContentTypeByPath"
import { findLang } from "@/utils/languages"
import { removeTrailingSlash } from "@/utils/url"
@@ -29,7 +27,7 @@ export const middleware: NextMiddleware = async (request) => {
if (!contentType || !uid) {
// Routes to subpages we need to check if the parent of the incomingPathName exists.
// Then we considered the incomingPathName to be a subpage. These subpages do not live in the CMS.
// Then we considered the incomingPathName to be a subpage. These subpages do NOT_LIVE in the CMS.
const incomingPathNameParts = incomingPathName.split("/")
// If the incomingPathName has 2 or more parts, it could possibly be a subpage.

View File

@@ -230,7 +230,7 @@ export const metadataQueryRouter = router({
if (metadata) {
if (alternates) {
// Hiding alternates until all languages are released in production
if (!env.HIDE_FOR_NEXT_RELEASE) {
if (env.NEW_SITE_LIVE_STATUS === "ALL_LANGUAGES_LIVE") {
metadata.alternates = alternates
}
}

View File

@@ -334,7 +334,7 @@ export async function updateStaysBookingUrl(
const baseUrl = env.PUBLIC_URL || "https://www.scandichotels.com"
// Construct Booking URL.
const bookingUrl = env.HIDE_FOR_NEXT_RELEASE
const bookingUrl = !env.isLangLive(lang)
? new URL(
getCurrentWebUrl({
path: myBookingPath[lang],

View File

@@ -11,9 +11,8 @@ export async function getServiceToken() {
const tracer = trace.getTracer("getServiceToken")
return await tracer.startActiveSpan("getServiceToken", async () => {
const scopes = env.ENABLE_BOOKING_FLOW
? ["profile", "hotel", "booking", "package", "availability"]
: ["profile"]
const scopes = ["profile", "hotel", "booking", "package", "availability"]
const cacheKey = getServiceTokenCacheKey(scopes)
const cacheClient = await getCacheClient()
const token = await getOrSetServiceTokenFromCache(cacheKey, scopes, tracer)