/** * Landing page picker for classifications (metadata.landing_page_id). * Shown only when Custom Landing Pages module is enabled. Public URL logic is in Yatra Pro. */ import React, { useEffect, useMemo, useState } from "react"; import { __, sprintf } from "../../lib/i18n"; import { Card, CardContent, CardHeader, CardTitle } from "../ui/card"; import { Select } from "../ui/select"; import { Loader2 } from "lucide-react"; type WpPageSearchItem = { id: number; title?: { rendered?: string }; }; function restRoot(): string { const raw = window.yatraAdmin?.restUrl || "/wp-json/"; return raw.endsWith("/") ? raw : `${raw}/`; } function parsePageTitle(payload: unknown): string { if (!payload || typeof payload !== "object") { return ""; } const p = payload as WpPageSearchItem; if (typeof p.title?.rendered === "string") { return p.title.rendered.replace(/<[^>]+>/g, "").trim(); } return ""; } export function parseLandingPageIdFromMetadata( metadata?: { landing_page_id?: unknown } | null, ): number | null { const raw = metadata?.landing_page_id; if (raw == null || raw === "") { return null; } const n = typeof raw === "number" ? raw : parseInt(String(raw).trim(), 10); return Number.isFinite(n) && n > 0 ? n : null; } export async function fetchPublishedPagePermalink( pageId: number, ): Promise { if (!pageId) { return null; } const nonce = window.yatraAdmin?.nonce || ""; const url = `${restRoot()}wp/v2/pages/${pageId}?context=embed`; const res = await fetch(url, { credentials: "same-origin", headers: { "X-WP-Nonce": nonce, Accept: "application/json", }, }); if (!res.ok) { return null; } const data = await res.json(); if (typeof data?.link !== "string" || data.link === "") { return null; } return data.link; } async function fetchPublishedPagesFirstPage(): Promise<{ items: WpPageSearchItem[]; total: number | null; }> { const nonce = window.yatraAdmin?.nonce || ""; const params = new URLSearchParams({ per_page: "100", orderby: "title", order: "asc", status: "publish", context: "embed", }); const res = await fetch(`${restRoot()}wp/v2/pages?${params}`, { credentials: "same-origin", headers: { "X-WP-Nonce": nonce, Accept: "application/json", }, }); if (!res.ok) { return { items: [], total: null }; } const data = await res.json(); const items = Array.isArray(data) ? data : []; const totalRaw = res.headers.get("X-WP-Total"); const total = totalRaw !== null && totalRaw !== "" ? parseInt(totalRaw, 10) : null; return { items, total: Number.isFinite(total) ? total : null }; } async function fetchSinglePublishedPage( pageId: number, ): Promise { const nonce = window.yatraAdmin?.nonce || ""; const res = await fetch(`${restRoot()}wp/v2/pages/${pageId}?context=embed`, { credentials: "same-origin", headers: { "X-WP-Nonce": nonce, Accept: "application/json", }, }); if (!res.ok) { return null; } const data = await res.json(); if (!data || typeof data.id !== "number") { return null; } return data as WpPageSearchItem; } function sortPagesByTitle(list: WpPageSearchItem[]): WpPageSearchItem[] { return [...list].sort((a, b) => { const ta = parsePageTitle(a).toLowerCase(); const tb = parsePageTitle(b).toLowerCase(); return ta.localeCompare(tb); }); } function mergePageUnique( list: WpPageSearchItem[], extra: WpPageSearchItem, ): WpPageSearchItem[] { const id = Number(extra.id); if (!Number.isFinite(id) || id <= 0) { return list; } if (list.some((p) => Number(p.id) === id)) { return list; } return sortPagesByTitle([...list, extra]); } interface ClassificationLandingPageFieldProps { value: number | null; onChange: (id: number | null) => void; /** Unique HTML id prefix for the select (per form). */ selectId?: string; } export function ClassificationLandingPageField({ value, onChange, selectId = "yatra-classification-landing-page", }: ClassificationLandingPageFieldProps) { const moduleOn = Boolean( typeof window !== "undefined" && window.yatraAdmin?.customLandingPagesModuleEnabled, ); const [pages, setPages] = useState([]); const [loading, setLoading] = useState(true); const [loadError, setLoadError] = useState(false); const [totalPublished, setTotalPublished] = useState(null); useEffect(() => { if (!moduleOn) { return; } let cancelled = false; void (async () => { setLoading(true); setLoadError(false); try { const { items, total } = await fetchPublishedPagesFirstPage(); if (cancelled) { return; } setPages(sortPagesByTitle(items)); setTotalPublished(total); } catch { if (!cancelled) { setLoadError(true); setPages([]); } } finally { if (!cancelled) { setLoading(false); } } })(); return () => { cancelled = true; }; }, [moduleOn]); useEffect(() => { if (!moduleOn || !value) { return; } if (pages.some((p) => Number(p.id) === value)) { return; } let cancelled = false; void (async () => { const one = await fetchSinglePublishedPage(value); if (cancelled || !one) { return; } setPages((prev) => mergePageUnique(prev, one)); })(); return () => { cancelled = true; }; }, [moduleOn, value, pages]); const selectValue = useMemo( () => (value && value > 0 ? String(value) : ""), [value], ); if (!moduleOn) { return null; } return ( {__("Custom landing page", "yatra")}

{__( "Public catalog links and redirects use this WordPress page URL instead of the default Yatra taxonomy path.", "yatra", )}

{loading ? (
{__("Loading pages…", "yatra")}
) : loadError ? (

{__( "Could not load pages. Check that you are logged in and try again.", "yatra", )}

) : ( )} {totalPublished !== null && totalPublished > 100 && !loading && (

{sprintf( // translators: %d: total number of published pages on the site. __( "Showing the first 100 published pages (of %d). Change the selection in WordPress → Pages if yours is not listed.", "yatra", ), totalPublished, )}

)}
); }