← back to geinersito__paris-luxe-journey

Function bodies 202 total

All specs Real LLM only Function bodies
Article function · typescript · L1-L7 (7 LOC)
src/data/blog/articles/perfect-3-day-paris-itinerary/pt.tsx
export default function Article() {
  return (
    <article>
      <p>[Article content in pt - To be completed]</p>
    </article>
  )
}
Article function · typescript · L1-L112 (112 LOC)
src/data/blog/articles/vtc-vs-taxi-vs-uber-paris/es.tsx
export default function Article() {
  return (
    <article>
      <p>
        ¿Planificando tu traslado al aeropuerto de París y confundido sobre si reservar un VTC, tomar un taxi o usar Uber?
        En esta guía completa, compararemos las tres opciones principales de transporte en París para ayudarte a tomar la mejor decisión.
      </p>

      <h2>¿Qué es VTC?</h2>
      <p>
        VTC significa "Voiture de Transport avec Chauffeur" (Vehículo de Transporte con Chofer). Es un servicio
        de chofer privado con licencia en Francia, similar a los servicios ejecutivos de automóviles.
      </p>

      <h3>Características del VTC:</h3>
      <ul>
        <li>Servicio pre-reservado con precios fijos garantizados</li>
        <li>Choferes profesionales con licencia</li>
        <li>Vehículos premium (Mercedes, BMW)</li>
        <li>Servicio de recepción en aeropuertos con cartel</li>
        <li>Monitoreo de vuelo incluido sin cargo extra</li>
        <li>Sin precios dinámicos ni sor
Article function · typescript · L1-L112 (112 LOC)
src/data/blog/articles/vtc-vs-taxi-vs-uber-paris/fr.tsx
export default function Article() {
  return (
    <article>
      <p>
        Vous planifiez votre transfert aéroport à Paris et vous hésitez entre réserver un VTC, prendre un taxi ou utiliser Uber ?
        Dans ce guide complet, nous comparerons les trois principales options de transport à Paris pour vous aider à prendre la meilleure décision.
      </p>

      <h2>Qu'est-ce qu'un VTC ?</h2>
      <p>
        VTC signifie "Voiture de Transport avec Chauffeur". C'est un service de chauffeur privé avec licence en France,
        similaire aux services de voitures exécutives.
      </p>

      <h3>Caractéristiques du VTC :</h3>
      <ul>
        <li>Service pré-réservé avec prix fixes garantis</li>
        <li>Chauffeurs professionnels avec licence</li>
        <li>Véhicules premium (Mercedes, BMW)</li>
        <li>Service d'accueil à l'aéroport avec pancarte</li>
        <li>Suivi de vol inclus sans frais supplémentaires</li>
        <li>Pas de tarification dynamique ni de surprises<
Article function · typescript · L1-L112 (112 LOC)
src/data/blog/articles/vtc-vs-taxi-vs-uber-paris/pt.tsx
export default function Article() {
  return (
    <article>
      <p>
        Planejando sua transferência do aeroporto em Paris e confuso sobre reservar um VTC, pegar um táxi ou usar Uber?
        Neste guia completo, compararemos as três principais opções de transporte em Paris para ajudá-lo a tomar a melhor decisão.
      </p>

      <h2>O que é VTC?</h2>
      <p>
        VTC significa "Voiture de Transport avec Chauffeur" (Veículo de Transporte com Motorista). É um serviço
        de motorista privado licenciado na França, similar aos serviços executivos de carros.
      </p>

      <h3>Características do VTC:</h3>
      <ul>
        <li>Serviço pré-reservado com preços fixos garantidos</li>
        <li>Motoristas profissionais licenciados</li>
        <li>Veículos premium (Mercedes, BMW)</li>
        <li>Serviço de recepção no aeroporto com placa</li>
        <li>Monitoramento de voo incluído sem custo extra</li>
        <li>Sem preços dinâmicos ou surpresas</li>
      </ul>

  
getExcursionFaqItems function · typescript · L158-L163 (6 LOC)
src/data/excursions/faq-content.ts
export function getExcursionFaqItems(
  language: Language,
  destination: string,
): ExcursionFaqItem[] {
  return faqBuilders[language](destination);
}
useExitIntent function · typescript · L18-L148 (131 LOC)
src/hooks/useExitIntent.ts
export default function useExitIntent({
  sensitivity = 10,
  delayMobile = 30000,
  scrollThreshold = 0.5,
  cookieExpiry = 24 * 60 * 60 * 1000, // 24 hours
  onTrigger
}: ExitIntentConfig) {
  const location = useLocation()
  const [hasTriggered, setHasTriggered] = useState(false)
  const [hasScrolled, setHasScrolled] = useState(false)
  const [hasInteracted, setHasInteracted] = useState(false)
  const timerRef = useRef<NodeJS.Timeout>()

  // Routes where popup should NOT show
  const excludedRoutes = ['/booking', '/payment', '/confirmation']
  const isExcluded = excludedRoutes.some(route => 
    location.pathname.startsWith(route)
  )

  // Check localStorage for previous popup display
  const checkStorage = (): boolean => {
    try {
      const stored = localStorage.getItem('exitIntentShown')
      if (!stored) return false

      const data: StoredData = JSON.parse(stored)
      const isExpired = Date.now() - data.timestamp > cookieExpiry

      if (isExpired) {
        localSto
useLocationDetails function · typescript · L13-L35 (23 LOC)
src/hooks/useLocationDetails.ts
export function useLocationDetails({ pickupId, dropoffId }: UseLocationDetailsProps) {
  const [locationDetails, setLocationDetails] = useState<LocationDetails | null>(null);

  useEffect(() => {
    if (!pickupId || !dropoffId) return;

    // Formatear los IDs para que sean más legibles
    const formatLocationId = (id: string) => {
      // Si es un UUID, mostrar solo los primeros 8 caracteres
      if (id.includes('-')) {
        return `Ubicación ${id.split('-')[0]}`;
      }
      return `Ubicación ${id.slice(0, 8)}`;
    };

    setLocationDetails({
      pickup: formatLocationId(pickupId),
      dropoff: formatLocationId(dropoffId)
    });
  }, [pickupId, dropoffId]);

  return { locationDetails };
}
Want fix-PRs on findings? Install Repobility's GitHub App · github.com/apps/repobility-bot
useIsMobile function · typescript · L5-L19 (15 LOC)
src/hooks/use-mobile.tsx
export function useIsMobile() {
  const [isMobile, setIsMobile] = React.useState<boolean | undefined>(undefined)

  React.useEffect(() => {
    const mql = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT - 1}px)`)
    const onChange = () => {
      setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
    }
    mql.addEventListener("change", onChange)
    setIsMobile(window.innerWidth < MOBILE_BREAKPOINT)
    return () => mql.removeEventListener("change", onChange)
  }, [])

  return !!isMobile
}
useScrollReveal function · typescript · L20-L61 (42 LOC)
src/hooks/useScrollReveal.ts
export function useScrollReveal<T extends HTMLElement = HTMLDivElement>(
  options: UseScrollRevealOptions = {}
) {
  const {
    threshold = 0.1,
    rootMargin = '0px',
    triggerOnce = true,
  } = options;

  const ref = useRef<T>(null);
  const [isVisible, setIsVisible] = useState(false);

  useEffect(() => {
    const element = ref.current;
    if (!element) return;

    const observer = new IntersectionObserver(
      ([entry]) => {
        if (entry.isIntersecting) {
          setIsVisible(true);
          if (triggerOnce) {
            observer.unobserve(element);
          }
        } else if (!triggerOnce) {
          setIsVisible(false);
        }
      },
      {
        threshold,
        rootMargin,
      }
    );

    observer.observe(element);

    return () => {
      observer.disconnect();
    };
  }, [threshold, rootMargin, triggerOnce]);

  return { ref, isVisible };
}
useStaggeredReveal function · typescript · L75-L89 (15 LOC)
src/hooks/useScrollReveal.ts
export function useStaggeredReveal(
  count: number,
  options: UseScrollRevealOptions & { delay?: number } = {}
) {
  const { delay = 100, ...revealOptions } = options;
  const items = Array.from({ length: count }, () => useScrollReveal(revealOptions));

  return items.map((item, index) => ({
    ...item,
    style: {
      animationDelay: `${index * delay}ms`,
      animationFillMode: 'both' as const,
    },
  }));
}
useServiceLevels function · typescript · L14-L59 (46 LOC)
src/hooks/useServiceLevels.ts
export function useServiceLevels() {
  const [serviceLevels, setServiceLevels] = useState<ServiceLevel[]>([]);
  const [isLoading, setIsLoading] = useState(true);
  const { toast } = useToast();
  const { t } = useLanguage();

  useEffect(() => {
    const fetchServiceLevels = async () => {
      try {
        setIsLoading(true);
        // TODO: Create service_levels table in Supabase
        // For now, use vehicles table as fallback
        const { data, error } = await supabase
          .from('vehicles')
          .select('*')
          .order('base_price');

        if (error) throw error;

        // Map vehicles to service levels format
        const mappedData = (data || []).map(vehicle => ({
          id: vehicle.id,
          name: vehicle.name,
          description: { en: vehicle.description || '' },
          features: vehicle.features || {},
          multiplier: 1.0 // Default multiplier
        }));

        setServiceLevels(mappedData);
      } catch (error) {
       
toast function · typescript · L13-L19 (7 LOC)
src/hooks/use-toast.ts
export function toast(props: ToastProps) {
  sonnerToast(props.title, {
    description: props.description,
    duration: props.duration || 5000,
    action: props.action,
  })
}
useToast function · typescript · L21-L27 (7 LOC)
src/hooks/use-toast.ts
export function useToast() {
  return {
    toast,
    dismiss: sonnerToast.dismiss,
    toasts: [] as any[]
  }
}
calculateReadingTime function · typescript · L8-L13 (6 LOC)
src/lib/blog-utils.ts
export function calculateReadingTime(content: string): number {
  const wordsPerMinute = 200;
  const wordCount = content.trim().split(/\s+/).length;
  const readingTime = Math.ceil(wordCount / wordsPerMinute);
  return readingTime;
}
generateTableOfContents function · typescript · L18-L41 (24 LOC)
src/lib/blog-utils.ts
export function generateTableOfContents(
  content: string,
): TableOfContentsItem[] {
  const headingRegex = /^(#{2,3})\s+(.+)$/gm;
  const headings: TableOfContentsItem[] = [];
  let match;

  while ((match = headingRegex.exec(content)) !== null) {
    const level = match[1].length; // Number of # characters
    const title = match[2].trim();
    const id = title
      .toLowerCase()
      .replace(/[^a-z0-9]+/g, "-")
      .replace(/(^-|-$)/g, "");

    headings.push({
      id,
      title,
      level,
    });
  }

  return headings;
}
All rows above produced by Repobility · https://repobility.com
formatDate function · typescript · L46-L54 (9 LOC)
src/lib/blog-utils.ts
export function formatDate(dateString: string, locale: string = "en"): string {
  const options: Intl.DateTimeFormatOptions = {
    year: "numeric",
    month: "long",
    day: "numeric",
  };

  return formatParisDateWithLocale(dateString, locale, options);
}
getRelativeTime function · typescript · L59-L87 (29 LOC)
src/lib/blog-utils.ts
export function getRelativeTime(
  dateString: string,
  locale: string = "en",
): string {
  const date = new Date(dateString);
  const now = new Date();
  const diffInSeconds = Math.floor((now.getTime() - date.getTime()) / 1000);

  const intervals = {
    year: 31536000,
    month: 2592000,
    week: 604800,
    day: 86400,
    hour: 3600,
    minute: 60,
  };

  for (const [unit, seconds] of Object.entries(intervals)) {
    const interval = Math.floor(diffInSeconds / seconds);
    if (interval >= 1) {
      return new Intl.RelativeTimeFormat(locale, { numeric: "auto" }).format(
        -interval,
        unit as Intl.RelativeTimeFormatUnit,
      );
    }
  }

  return "just now";
}
extractFirstParagraph function · typescript · L100-L110 (11 LOC)
src/lib/blog-utils.ts
export function extractFirstParagraph(content: string): string {
  // Remove headings
  const withoutHeadings = content.replace(/^#{1,6}\s+.+$/gm, "");

  // Get first non-empty paragraph
  const paragraphs = withoutHeadings
    .split("\n\n")
    .filter((p) => p.trim().length > 0);

  return paragraphs[0]?.trim() || "";
}
generateSlug function · typescript · L115-L120 (6 LOC)
src/lib/blog-utils.ts
export function generateSlug(title: string): string {
  return title
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/(^-|-$)/g, "");
}
getReadTimeText function · typescript · L125-L137 (13 LOC)
src/lib/blog-utils.ts
export function getReadTimeText(
  minutes: number,
  locale: string = "en",
): string {
  const texts = {
    en: `${minutes} min read`,
    fr: `${minutes} min de lecture`,
    es: `${minutes} min de lectura`,
    pt: `${minutes} min de leitura`,
  };

  return texts[locale as keyof typeof texts] || texts.en;
}
parseMarkdownToHTML function · typescript · L143-L165 (23 LOC)
src/lib/blog-utils.ts
export function parseMarkdownToHTML(markdown: string): string {
  let html = markdown;

  // Headers
  html = html.replace(/^### (.*$)/gim, "<h3>$1</h3>");
  html = html.replace(/^## (.*$)/gim, "<h2>$1</h2>");
  html = html.replace(/^# (.*$)/gim, "<h1>$1</h1>");

  // Bold
  html = html.replace(/\*\*(.*?)\*\*/gim, "<strong>$1</strong>");

  // Italic
  html = html.replace(/\*(.*?)\*/gim, "<em>$1</em>");

  // Links
  html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/gim, '<a href="$2">$1</a>');

  // Line breaks
  html = html.replace(/\n\n/g, "</p><p>");
  html = "<p>" + html + "</p>";

  return html;
}
toDate function · typescript · L11-L16 (6 LOC)
src/lib/datetime/paris.ts
function toDate(input: string | number | Date): Date {
  if (input instanceof Date) {
    return input;
  }
  return new Date(input);
}
formatParisDateTime function · typescript · L22-L33 (12 LOC)
src/lib/datetime/paris.ts
export function formatParisDateTime(
  input: string | number | Date,
  options?: Intl.DateTimeFormatOptions,
): string {
  const date = toDate(input);
  const mergedOptions: Intl.DateTimeFormatOptions = {
    ...options,
    timeZone: PARIS_TZ,
  };

  return date.toLocaleString(undefined, mergedOptions);
}
Repobility · severity-and-effort ranking · https://repobility.com
formatParisDate function · typescript · L39-L50 (12 LOC)
src/lib/datetime/paris.ts
export function formatParisDate(
  input: string | number | Date,
  options?: Intl.DateTimeFormatOptions,
): string {
  const date = toDate(input);
  const mergedOptions: Intl.DateTimeFormatOptions = {
    ...options,
    timeZone: PARIS_TZ,
  };

  return date.toLocaleDateString(undefined, mergedOptions);
}
formatParisDateWithLocale function · typescript · L56-L68 (13 LOC)
src/lib/datetime/paris.ts
export function formatParisDateWithLocale(
  input: string | number | Date,
  locale: string,
  options?: Intl.DateTimeFormatOptions,
): string {
  const date = toDate(input);
  const mergedOptions: Intl.DateTimeFormatOptions = {
    ...options,
    timeZone: PARIS_TZ,
  };

  return date.toLocaleDateString(locale, mergedOptions);
}
buildEventWhatsAppUrl function · typescript · L63-L90 (28 LOC)
src/lib/eventsPrefill.ts
export function buildEventWhatsAppUrl(
  event: Event,
  language: Language,
  formatDate: (dateString: string) => string,
): string {
  const t = getTemplates(language);
  const lines: string[] = [];

  lines.push(`${t.greeting}: ${event.title[language]}`);

  if (event.startAt) {
    lines.push(`${t.dateLabel}: ${formatDate(event.startAt)}`);
  }
  if (event.venueName?.[language]) {
    lines.push(
      `${t.venueLabel}: ${event.venueName[language]}${event.district ? ` (${event.district})` : ""}`,
    );
  }
  if (event.eventUrl) {
    lines.push(`${t.linkLabel}: ${event.eventUrl}`);
  }

  lines.push("");
  lines.push(t.closing);

  const text = encodeURIComponent(lines.join("\n"));
  return `https://wa.me/${WHATSAPP_NUMBER}?text=${text}`;
}
buildEventEmailUrl function · typescript · L92-L119 (28 LOC)
src/lib/eventsPrefill.ts
export function buildEventEmailUrl(
  event: Event,
  language: Language,
  formatDate: (dateString: string) => string,
): string {
  const t = getTemplates(language);

  const subject = encodeURIComponent(
    `${t.genericSubject}: ${event.title[language]}`,
  );

  const bodyLines: string[] = [];
  bodyLines.push(`${t.greeting}: ${event.title[language]}`);
  if (event.startAt) {
    bodyLines.push(`${t.dateLabel}: ${formatDate(event.startAt)}`);
  }
  if (event.venueName?.[language]) {
    bodyLines.push(`${t.venueLabel}: ${event.venueName[language]}`);
  }
  if (event.eventUrl) {
    bodyLines.push(`${t.linkLabel}: ${event.eventUrl}`);
  }
  bodyLines.push("");
  bodyLines.push(t.closing);

  const body = encodeURIComponent(bodyLines.join("\n"));
  return `mailto:${EMAIL_ADDRESS}?subject=${subject}&body=${body}`;
}
buildGenericEmailUrl function · typescript · L127-L132 (6 LOC)
src/lib/eventsPrefill.ts
export function buildGenericEmailUrl(language: string): string {
  const t = getTemplates(language);
  const subject = encodeURIComponent(t.genericSubject);
  const body = encodeURIComponent(t.genericBody);
  return `mailto:${EMAIL_ADDRESS}?subject=${subject}&body=${body}`;
}
generateArticleJsonLd function · typescript · L64-L104 (41 LOC)
src/lib/seo/json-ld.ts
export function generateArticleJsonLd(
  post: BlogPostMeta,
  lang: Language,
  url: string,
): JsonLdArticle {
  const category = getCategoryBySlug(post.category);

  // Handle keywords - BlogPostMeta.seo.keywords is always string[]
  const keywords: string = post.seo.keywords
    ? post.seo.keywords.join(", ")
    : "";

  return {
    "@context": "https://schema.org",
    "@type": "Article",
    headline: post.title[lang],
    description: post.description[lang],
    image: post.image.url,
    datePublished: post.publishedAt,
    dateModified: post.updatedAt,
    author: {
      "@type": "Person",
      name: post.author.name,
    },
    publisher: {
      "@type": "Organization",
      name: "Paris Luxe Journey",
      logo: {
        "@type": "ImageObject",
        url: "https://parisluxejourney.com/logo.png",
      },
    },
    mainEntityOfPage: {
      "@type": "WebPage",
      "@id": url,
    },
    articleSection: category?.name[lang],
    keywords,
    inLanguage: lang,
  }
generateBreadcrumbJsonLd function · typescript · L109-L122 (14 LOC)
src/lib/seo/json-ld.ts
export function generateBreadcrumbJsonLd(
  items: Array<{ name: string; url?: string }>,
): JsonLdBreadcrumb {
  return {
    "@context": "https://schema.org",
    "@type": "BreadcrumbList",
    itemListElement: items.map((item, index) => ({
      "@type": "ListItem",
      position: index + 1,
      name: item.name,
      ...(item.url && { item: item.url }),
    })),
  };
}
generateWebsiteJsonLd function · typescript · L127-L144 (18 LOC)
src/lib/seo/json-ld.ts
export function generateWebsiteJsonLd(): JsonLdWebsite {
  return {
    "@context": "https://schema.org",
    "@type": "WebSite",
    name: "Paris Luxe Journey",
    url: "https://parisluxejourney.com",
    description: "Premium airport transfer and travel services in Paris",
    potentialAction: {
      "@type": "SearchAction",
      target: {
        "@type": "EntryPoint",
        urlTemplate:
          "https://parisluxejourney.com/blog?search={search_term_string}",
      },
      "query-input": "required name=search_term_string",
    },
  };
}
Provenance: Repobility (https://repobility.com) — every score reproducible from /scan/
generateOrganizationJsonLd function · typescript · L182-L198 (17 LOC)
src/lib/seo/json-ld.ts
export function generateOrganizationJsonLd(params: {
  name: string;
  url: string;
  logoUrl?: string;
  sameAs?: string[];
  description?: string;
}): JsonLdOrganization {
  return {
    "@context": "https://schema.org",
    "@type": "Organization",
    name: params.name,
    url: params.url,
    ...(params.logoUrl && { logo: params.logoUrl }),
    ...(params.sameAs && { sameAs: params.sameAs }),
    ...(params.description && { description: params.description }),
  };
}
generateLocalBusinessJsonLd function · typescript · L203-L242 (40 LOC)
src/lib/seo/json-ld.ts
export function generateLocalBusinessJsonLd(params: {
  name: string;
  url: string;
  telephone?: string;
  address?: {
    streetAddress?: string;
    addressLocality?: string;
    addressRegion?: string;
    postalCode?: string;
    addressCountry?: string;
  };
  geo?: {
    latitude?: string;
    longitude?: string;
  };
  priceRange?: string;
  description?: string;
}): JsonLdLocalBusiness {
  return {
    "@context": "https://schema.org",
    "@type": "LocalBusiness",
    name: params.name,
    url: params.url,
    ...(params.telephone && { telephone: params.telephone }),
    ...(params.address && {
      address: {
        "@type": "PostalAddress",
        ...params.address,
      },
    }),
    ...(params.geo && {
      geo: {
        "@type": "GeoCoordinates",
        ...params.geo,
      },
    }),
    ...(params.priceRange && { priceRange: params.priceRange }),
    ...(params.description && { description: params.description }),
  };
}
getSiteOrigin function · typescript · L6-L26 (21 LOC)
src/lib/seo/site.ts
export function getSiteOrigin(): string {
  // Runtime: use actual domain (supports eliteparistransfer.com, parisluxejourney.com, etc.)
  if (typeof window !== "undefined" && window.location?.origin) {
    return window.location.origin;
  }

  // Build/SSR fallback
  const envUrl = (
    import.meta.env?.VITE_PUBLIC_SITE_URL as string | undefined
  )?.trim();

  if (envUrl) {
    try {
      return new URL(envUrl).origin;
    } catch {
      return envUrl.replace(/\/+$/, "");
    }
  }

  return "https://eliteparistransfer.com";
}
AgenciesPage function · typescript · L7-L69 (63 LOC)
src/pages/Agencies.tsx
export default function AgenciesPage() {
  const { t } = useLanguage();

  const bullets = [
    t.agencies.bullets.volume,
    t.agencies.bullets.invoicing,
    t.agencies.bullets.support,
    t.agencies.bullets.availability,
  ];

  return (
    <>
      <Helmet>
        <title>{t.agencies.metaTitle}</title>
        <meta name="description" content={t.agencies.intro} />
      </Helmet>

      <section className="py-16 md:py-20 bg-background">
        <div className="container mx-auto px-4 max-w-4xl">
          <div className="rounded-2xl border border-border bg-card p-6 md:p-10 shadow-sm">
            <h1 className="text-3xl md:text-4xl font-display font-bold text-secondary mb-4">
              {t.agencies.h1}
            </h1>
            <p className="text-base md:text-lg text-muted-foreground mb-6 leading-relaxed">
              {t.agencies.intro}
            </p>

            <ul className="space-y-3 mb-6">
              {bullets.map((bullet) => (
                <li key={bullet}
BlogCategory function · typescript · L16-L168 (153 LOC)
src/pages/blog/BlogCategory.tsx
export default function BlogCategory() {
  const { category } = useParams<{ category: string }>();
  const navigate = useNavigate();
  const { i18n, t } = useTranslation();
  const currentLang = i18n.language as "en" | "es" | "fr" | "pt";
  const [searchQuery, setSearchQuery] = useState("");

  // Validate category
  if (!category || !isValidCategory(category)) {
    navigate("/blog/404", { replace: true });
    return null;
  }

  const categoryMeta = getCategoryBySlug(category)!;
  const allPosts = getPostsByCategory(category);

  // Filter posts by search query
  const filteredPosts = allPosts.filter((post) => {
    if (searchQuery === "") return true;
    const query = searchQuery.toLowerCase();
    return (
      post.title[currentLang].toLowerCase().includes(query) ||
      post.description[currentLang].toLowerCase().includes(query) ||
      post.tags.some((tag) => tag.toLowerCase().includes(query))
    );
  });

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  
loadPost function · typescript · L40-L91 (52 LOC)
src/pages/blog/BlogPost.tsx
    async function loadPost() {
      if (!category || !slug) {
        setError(true);
        setLoading(false);
        return;
      }

      // Validate category
      if (!isValidCategory(category)) {
        navigate("/blog/404", { replace: true });
        return;
      }

      // Get post metadata
      const postMeta = getPostBySlug(slug);
      if (!postMeta) {
        navigate("/blog/404", { replace: true });
        return;
      }

      // Verify category matches
      if (postMeta.category !== category) {
        navigate(`/blog/${postMeta.category}/${slug}`, { replace: true });
        return;
      }

      setPost(postMeta);

      // Load article content
      try {
        const exists = await articleExists(slug, currentLang);
        if (!exists) {
          // Fallback to English if translation doesn't exist
          const existsEn = await articleExists(slug, "en");
          if (!existsEn) {
            setError(true);
            setLoading(false);
          
BlogNotFound function · typescript · L9-L107 (99 LOC)
src/pages/blog/NotFound.tsx
export default function BlogNotFound() {
  const { t } = useTranslation()

  // Get 3 random popular posts to suggest
  const suggestedPosts = blogPosts
    .sort(() => Math.random() - 0.5)
    .slice(0, 3)

  return (
    <>
      <Helmet>
        <title>{t('blog.notFound.title') || '404 - Page Not Found | Blog'}</title>
        <meta name="robots" content="noindex, nofollow" />
      </Helmet>

      <div className="min-h-screen bg-background">
        {/* 404 Hero */}
        <section className="relative bg-gradient-to-br from-primary/10 via-background to-background py-20 md:py-32">
          <div className="container mx-auto px-4">
            <div className="max-w-3xl mx-auto text-center">
              {/* 404 Number */}
              <div className="text-9xl md:text-[12rem] font-bold text-primary/20 leading-none mb-4">
                404
              </div>

              {/* Title */}
              <h1 className="text-4xl md:text-5xl font-bold text-foreground mb-6">
         
BookingPage function · typescript · L22-L84 (63 LOC)
src/pages/booking/BookingPage.tsx
export default function BookingPage() {
  const navigate = useNavigate();
  const { t } = useLanguage();
  const { toast } = useToast();
  const [currentStep, setCurrentStep] = React.useState<'details' | 'payment' | 'confirmation'>('details');
  const [bookingDetails, setBookingDetails] = React.useState<BookingDetails>({
    tourId: 'default-tour',
    tourName: 'Default Tour'
    // Eliminamos basePrice del estado inicial
  });
  const [selectedTour, setSelectedTour] = React.useState<any>(null);
  const [isProcessing, setIsProcessing] = React.useState(false);
  const [error, setError] = React.useState('');

  const handleBookingSubmit = async (details: BookingDetails) => {
    try {
      setBookingDetails(details);
      setCurrentStep('payment');
    } catch (err) {
      setError(err instanceof Error ? err.message : 'Error al procesar la reserva');
    }
  };

  const handlePaymentSuccess = async (paymentDetails: PaymentDetails) => {
    try {
      setIsProcessing(true);
      // 
Want fix-PRs on findings? Install Repobility's GitHub App · github.com/apps/repobility-bot
formatParisDateTime function · typescript · L53-L64 (12 LOC)
src/pages/booking/Confirmation.tsx
function formatParisDateTime(value: string | null | undefined): string | null {
  if (!value) {
    return null;
  }

  const date = new Date(value);
  if (Number.isNaN(date.getTime())) {
    return null;
  }

  return PARIS_DATE_TIME_FORMATTER.format(date);
}
CompaniesPage function · typescript · L7-L69 (63 LOC)
src/pages/Companies.tsx
export default function CompaniesPage() {
  const { t } = useLanguage();

  const bullets = [
    t.companies.bullets.billing,
    t.companies.bullets.routes,
    t.companies.bullets.chauffeurs,
    t.companies.bullets.support,
  ];

  return (
    <>
      <Helmet>
        <title>{t.companies.metaTitle}</title>
        <meta name="description" content={t.companies.intro} />
      </Helmet>

      <section className="py-16 md:py-20 bg-background">
        <div className="container mx-auto px-4 max-w-4xl">
          <div className="rounded-2xl border border-border bg-card p-6 md:p-10 shadow-sm">
            <h1 className="text-3xl md:text-4xl font-display font-bold text-secondary mb-4">
              {t.companies.h1}
            </h1>
            <p className="text-base md:text-lg text-muted-foreground mb-6 leading-relaxed">
              {t.companies.intro}
            </p>

            <ul className="space-y-3 mb-6">
              {bullets.map((bullet) => (
                <li key={bu
DesignPreview function · typescript · L5-L188 (184 LOC)
src/pages/DesignPreview.tsx
export default function DesignPreview() {
  const [passengersBefore, setPassengersBefore] = useState(2);
  const [passengersAfter, setPassengersAfter] = useState(2);

  return (
    <div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 p-8">
      <div className="max-w-7xl mx-auto">
        <h1 className="text-4xl font-display text-primary mb-2 text-center">
          🎨 Design Preview - Paris Elite Services
        </h1>
        <p className="text-center text-slate-600 mb-12">
          Comparación lado a lado: Antes vs. Después
        </p>

        {/* 1. HERO OVERLAY */}
        <section className="mb-16">
          <h2 className="text-2xl font-display text-primary mb-6">1. Hero Overlay</h2>
          <div className="grid md:grid-cols-2 gap-8">
            {/* ANTES */}
            <div>
              <p className="text-sm font-semibold text-red-600 mb-3">❌ ANTES (Plano)</p>
              <div className="relative h-64 rounded-xl overflow-hidden">
                
DestinationPage function · typescript · L9-L77 (69 LOC)
src/pages/destination/[id].tsx
export default function DestinationPage() {
  const { id } = useParams();
  const [activeSection, setActiveSection] = React.useState('description');
  
  const navigationItems = [
    { id: 'description', label: 'Description' },
    { id: 'tours', label: 'Tours' },
    { id: 'map', label: 'Map' },
    { id: 'events', label: 'Events' },
    { id: 'faq', label: 'FAQ' }
  ];

  const destinationData = {
    title: 'Destination Title',
    image: '/images/destination.jpg',
    distance: '30km from Paris',
    duration: '1 day',
    currentPath: `/destination/${id}`
  };

  const tours = [
    {
      id: '1',
      title: 'Tour Name',
      description: 'Tour Description',
      duration: '4 hours',
      price: 99,
      includes: ['Item 1', 'Item 2']
    }
  ];

  const renderTours = () => (
    <div className="grid gap-6">
      {tours.map(tour => (
        <TourCard
          key={tour.id}
          title={tour.title}
          description={tour.description}
          duration={tour.du
ChampagnePage function · typescript · L11-L133 (123 LOC)
src/pages/excursions/champagne.tsx
export default function ChampagnePage() {
  const { t, language } = useLanguage();
  const [activeSection, setActiveSection] = React.useState("description");
  const [selectedTour, setSelectedTour] = React.useState<string | null>(null);

  const champagneData = t.champagne;
  const excursionData = excursions.find((e) => e.id === "champagne");

  const navigationItems = [
    { id: "description", label: t.champagne.navigation.description },
    { id: "tours", label: t.champagne.navigation.tours },
    { id: "map", label: t.champagne.navigation.map },
    { id: "events", label: t.champagne.navigation.events },
    { id: "faq", label: t.champagne.navigation.faq },
  ];

  const handleSectionChange = (sectionId: string) => {
    setActiveSection(sectionId);
  };

  const faqItems = getExcursionFaqItems(language, t.champagne.title);

  const content = {
    description: (
      <div>
        <h3 className="text-2xl font-bold mb-4">{champagneData.title}</h3>
        <p className="mb-4">{cham
GivernyHonfleurPage function · typescript · L11-L129 (119 LOC)
src/pages/excursions/giverny-honfleur.tsx
export default function GivernyHonfleurPage() {
  const { t, language } = useLanguage();
  const [activeSection, setActiveSection] = React.useState("description");
  const [selectedTour, setSelectedTour] = React.useState<string | null>(null);

  const givernyData = t.giverny;
  const excursionData = excursions.find((e) => e.id === "giverny-honfleur");

  const navigationItems = [
    { id: "description", label: t.giverny.navigation.description },
    { id: "tours", label: t.giverny.navigation.tours },
    { id: "map", label: t.giverny.navigation.map },
    { id: "events", label: t.giverny.navigation.events },
    { id: "faq", label: t.giverny.navigation.faq },
  ];

  const handleSectionChange = (sectionId: string) => {
    setActiveSection(sectionId);
  };

  const faqItems = getExcursionFaqItems(language, t.giverny.title);

  const content = {
    description: (
      <div>
        <h3 className="text-2xl font-bold mb-4">{givernyData.title}</h3>
        <p className="mb-4">{givernyDa
ExcursionsPage function · typescript · L26-L94 (69 LOC)
src/pages/excursions/index.tsx
export default function ExcursionsPage() {
  const { t } = useTranslation();
  const [searchQuery, setSearchQuery] = useState("");
  const [selectedDuration, setSelectedDuration] = useState("");
  const [selectedType, setSelectedType] = useState("");

  return (
    <div className="min-h-screen bg-background">
      <main className="container mx-auto px-4 py-8">
        <div className="space-y-8">
          {/* Filtros */}
          <div className="flex flex-col md:flex-row gap-4">
            <Input
              type="text"
              placeholder={t('excursions.search.placeholder')}
              value={searchQuery}
              onChange={(e) => setSearchQuery(e.target.value)}
              className="md:w-1/3"
            />
            
            <Select
              value={selectedDuration}
              onValueChange={setSelectedDuration}
            >
              <SelectTrigger className="md:w-1/4">
                <SelectValue placeholder={t('excursions.search.duration
LoireValleyPage function · typescript · L11-L125 (115 LOC)
src/pages/excursions/loire-valley.tsx
export default function LoireValleyPage() {
  const { t, language } = useLanguage();
  const [activeSection, setActiveSection] = React.useState("description");
  const [selectedTour, setSelectedTour] = React.useState<string | null>(null);

  const loireData = t.loire;
  const excursionData = excursions.find((e) => e.id === "loire-valley");

  const navigationItems = [
    { id: "description", label: t.loire.navigation.description },
    { id: "tours", label: t.loire.navigation.tours },
    { id: "map", label: t.loire.navigation.map },
    { id: "events", label: t.loire.navigation.events },
    { id: "faq", label: t.loire.navigation.faq },
  ];

  const handleSectionChange = (sectionId: string) => {
    setActiveSection(sectionId);
  };

  const faqItems = getExcursionFaqItems(language, t.loire.title);

  const content = {
    description: (
      <div>
        <h3 className="text-2xl font-bold mb-4">{loireData.title}</h3>
        <p className="mb-4">{loireData.description}</p>
        
All rows above produced by Repobility · https://repobility.com
VersaillesPage function · typescript · L11-L154 (144 LOC)
src/pages/excursions/versailles.tsx
export default function VersaillesPage() {
  const { t, language } = useLanguage();
  const [activeSection, setActiveSection] = React.useState("description");
  const [selectedTour, setSelectedTour] = React.useState<string | null>(null);

  // Get versailles data from translations
  const versaillesData = t.versailles;

  // Navigation items
  const navigationItems = [
    { id: "description", label: t.versailles.navigation.description },
    { id: "tours", label: t.versailles.navigation.tours },
    { id: "map", label: t.versailles.navigation.map },
    { id: "events", label: t.versailles.navigation.events },
    { id: "faq", label: t.versailles.navigation.faq },
  ];

  // Handle section change
  const handleSectionChange = (sectionId: string) => {
    setActiveSection(sectionId);
  };

  const faqItems = getExcursionFaqItems(language, t.versailles.title);

  // Enhanced tours data with more details
  const tours = [
    {
      id: "versailles-tour-1",
      name: "Guided Tour of Ve
FAQPage function · typescript · L5-L64 (60 LOC)
src/pages/FAQPage.tsx
export default function FAQPage() {
  const { t } = useLanguage();

  return (
    <div className="min-h-screen bg-gradient-to-b from-white via-champagne/30 to-white">
      {/* Hero Section */}
      <section className="relative py-20 bg-gradient-to-br from-primary/10 via-champagne/20 to-white">
        <div className="container mx-auto px-4 sm:px-6 lg:px-8 max-w-4xl">
          <div className="text-center">
            <h1 className="text-4xl md:text-5xl lg:text-6xl font-display font-bold text-secondary mb-6">
              {t.faq?.title || "Frequently Asked Questions"}
            </h1>
            <p className="text-lg md:text-xl text-gray-600 max-w-2xl mx-auto leading-relaxed">
              {t.faq?.subtitle || "Find answers to common questions about our premium transfer services"}
            </p>
          </div>
        </div>
      </section>

      {/* FAQ Component */}
      <section className="py-12">
        <FAQ />
      </section>

      {/* CTA Section */}
      <sectio
Home function · typescript · L19-L171 (153 LOC)
src/pages/Home.tsx
export default function Home() {
  const { t, language } = useLanguage();

  // Use runtime domain (supports eliteparistransfer.com, parisluxejourney.com, etc.)
  const siteOrigin = getSiteOrigin();

  // Generate JSON-LD structured data
  const organizationJsonLd = generateOrganizationJsonLd({
    name: "Paris Luxe Journey",
    url: siteOrigin,
    logoUrl: `${siteOrigin}/logo.png`,
    description: t.seo.home.description,
    sameAs: [
      "https://www.facebook.com/parisluxejourney",
      "https://www.instagram.com/parisluxejourney",
    ],
  });

  const localBusinessJsonLd = generateLocalBusinessJsonLd({
    name: "Paris Luxe Journey",
    url: siteOrigin,
    telephone: "+33668251102",
    description: t.seo.home.description,
    priceRange: "€€€",
    address: {
      addressLocality: "Paris",
      addressRegion: "Île-de-France",
      addressCountry: "FR",
    },
  });

  const canonicalUrl = `${siteOrigin}/${language}`;

  const handleCtaClickCapture = (event: React.MouseE
‹ prevpage 3 / 5next ›