Function bodies 202 total
check_file function · python · L5-L33 (29 LOC)check_encoding.py
def check_file(filepath):
"""Report all non-ASCII characters."""
path = Path(filepath)
data = path.read_bytes()
non_ascii = []
for i, byte in enumerate(data):
if byte > 127:
non_ascii.append((i, byte))
if not non_ascii:
print(f"[OK] {filepath}: pure ASCII")
return
print(f"[INFO] {filepath}: found {len(non_ascii)} non-ASCII bytes")
# Group by byte value
byte_counts = {}
for pos, byte in non_ascii:
byte_counts[byte] = byte_counts.get(byte, 0) + 1
print(f" Byte distribution:")
for byte, count in sorted(byte_counts.items()):
try:
# Try to decode as UTF-8 continuation
char_hex = f"0x{byte:02X}"
print(f" {char_hex}: {count}x")
except:
print(f" 0x{byte:02X}: {count}x")normalize_char function · python · L40-L54 (15 LOC)clean_unicode.py
def normalize_char(ch):
if ch in IGNORE_CONTROLS:
return ch, False
cp = ord(ch)
if (0xFE00 <= cp <= 0xFE0F) or (0xE0100 <= cp <= 0xE01EF):
# Variation selectors are default-ignorable and can trigger GitHub warnings.
return '', True
if cp in REPLACEMENTS:
return REPLACEMENTS[cp], True
cat = unicodedata.category(ch)
if cat == 'Zs':
return ' ' if ch != ' ' else ch, ch != ' '
if cat in SUSPICIOUS_CATEGORIES:
return '', True
return ch, Falseclean_file function · python · L57-L99 (43 LOC)clean_unicode.py
def clean_file(filepath):
"""Remove suspicious Unicode chars from file."""
path = Path(filepath)
if not path.exists():
print(f"[ERROR] File not found: {filepath}")
return False
# Read file as bytes to preserve line endings
try:
data = path.read_bytes()
content = data.decode('utf-8')
except Exception as e:
print(f"[ERROR] Error reading {filepath}: {e}")
return False
# Normalize content
found = {}
normalized = []
for ch in content:
replacement, changed = normalize_char(ch)
if changed:
cp = ord(ch)
cat = unicodedata.category(ch)
name = unicodedata.name(ch, 'UNKNOWN')
key = (cp, cat, name)
found[key] = found.get(key, 0) + 1
normalized.append(replacement)
normalized_text = ''.join(normalized)
if not found:
print(f"[OK] {filepath}: clean (no suspicious chars)")
return False
# WriparseDotEnvFile function · javascript · L50-L84 (35 LOC)scripts/generate-seo.mjs
function parseDotEnvFile(filePath) {
const parsed = {};
if (!existsSync(filePath)) {
return parsed;
}
const content = readFileSync(filePath, "utf8");
const lines = content.split(/\r?\n/);
for (const line of lines) {
const trimmed = line.trim();
if (!trimmed || trimmed.startsWith("#")) {
continue;
}
const equalsIndex = trimmed.indexOf("=");
if (equalsIndex <= 0) {
continue;
}
const key = trimmed.slice(0, equalsIndex).trim();
let value = trimmed.slice(equalsIndex + 1).trim();
if (
(value.startsWith('"') && value.endsWith('"')) ||
(value.startsWith("'") && value.endsWith("'"))
) {
value = value.slice(1, -1);
}
parsed[key] = value;
}
return parsed;
}loadBuildEnvFromFiles function · javascript · L86-L93 (8 LOC)scripts/generate-seo.mjs
function loadBuildEnvFromFiles() {
const merged = {};
for (const envFile of ENV_FILES_IN_PRECEDENCE) {
const envPath = resolve(process.cwd(), envFile);
Object.assign(merged, parseDotEnvFile(envPath));
}
return merged;
}normalizeBaseUrl function · javascript · L95-L105 (11 LOC)scripts/generate-seo.mjs
function normalizeBaseUrl(rawValue) {
try {
const parsed = new URL(rawValue);
return parsed.origin;
} catch {
console.error(
`ERROR: ${REQUIRED_SITE_URL_ENV} must be a valid absolute URL, got "${rawValue}".`,
);
process.exit(1);
}
}resolveBaseUrl function · javascript · L107-L131 (25 LOC)scripts/generate-seo.mjs
function resolveBaseUrl() {
const fileEnv = loadBuildEnvFromFiles();
const rawSiteUrl =
process.env[REQUIRED_SITE_URL_ENV] ?? fileEnv[REQUIRED_SITE_URL_ENV] ?? "";
const trimmed = rawSiteUrl.trim();
const fallback = "http://localhost:8082";
if (!trimmed) {
console.warn(
`⚠️ WARNING: ${REQUIRED_SITE_URL_ENV} not set. Using fallback: ${fallback}`,
);
console.warn(
` For production builds, set ${REQUIRED_SITE_URL_ENV} in CI or .env.production (e.g. https://eliteparistransfer.com).`,
);
return normalizeBaseUrl(fallback);
}
// Basic scheme validation to avoid malformed canonicals like "eliteparistransfer.com"
if (!/^https?:\/\//i.test(trimmed)) {
console.warn(
`⚠️ WARNING: ${REQUIRED_SITE_URL_ENV} is invalid (missing http/https): "${trimmed}". Using fallback: ${fallback}`,
);
return normalizeBaseUrl(fallback);
}Want fix-PRs on findings? Install Repobility's GitHub App · github.com/apps/repobility-bot
xmlEscape function · javascript · L136-L143 (8 LOC)scripts/generate-seo.mjs
function xmlEscape(value) {
return value
.replace(/&/g, "&")
.replace(/</g, "<")
.replace(/>/g, ">")
.replace(/"/g, """)
.replace(/'/g, "'");
}safeIsoDate function · javascript · L145-L154 (10 LOC)scripts/generate-seo.mjs
function safeIsoDate(value) {
if (!value) {
return null;
}
const parsed = new Date(value);
if (Number.isNaN(parsed.getTime())) {
return null;
}
return parsed.toISOString().slice(0, 10);
}isIndexableBlogSlug function · javascript · L156-L161 (6 LOC)scripts/generate-seo.mjs
function isIndexableBlogSlug(slug) {
if (!slug) {
return false;
}
return !BLOG_SLUG_EXCLUSIONS.some((pattern) => pattern.test(slug));
}extractBlogCategories function · javascript · L196-L211 (16 LOC)scripts/generate-seo.mjs
function extractBlogCategories() {
const categoriesPath = join(
process.cwd(),
"src",
"data",
"blog",
"categories.ts",
);
if (!existsSync(categoriesPath)) {
return [];
}
const content = readFileSync(categoriesPath, "utf8");
const slugRegex = /slug:\s*["']([^"']+)["']/g;
return [...content.matchAll(slugRegex)].map((match) => match[1]);
}dedupeUrlEntries function · javascript · L213-L222 (10 LOC)scripts/generate-seo.mjs
function dedupeUrlEntries(entries) {
const seen = new Set();
return entries.filter((entry) => {
if (seen.has(entry.loc)) {
return false;
}
seen.add(entry.loc);
return true;
});
}buildSitemapEntries function · javascript · L224-L239 (16 LOC)scripts/generate-seo.mjs
function buildSitemapEntries(baseUrl) {
const staticEntries = STATIC_ROUTES.map((path) => ({
loc: `${baseUrl}${path}`,
}));
const categoryEntries = extractBlogCategories().map((categorySlug) => ({
loc: `${baseUrl}/blog/${encodeURIComponent(categorySlug)}`,
}));
const postEntries = extractBlogPosts().map((post) => ({
loc: `${baseUrl}/blog/${encodeURIComponent(post.category)}/${encodeURIComponent(post.slug)}`,
lastmod: post.lastmod,
}));
return dedupeUrlEntries([...staticEntries, ...categoryEntries, ...postEntries]);
}buildSitemapXml function · javascript · L241-L256 (16 LOC)scripts/generate-seo.mjs
function buildSitemapXml(entries) {
const urlBlocks = entries.map((entry) => {
const lines = [" <url>", ` <loc>${xmlEscape(entry.loc)}</loc>`];
if (entry.lastmod) {
lines.push(` <lastmod>${entry.lastmod}</lastmod>`);
}
lines.push(" </url>");
return lines.join("\n");
});
return `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
${urlBlocks.join("\n")}
</urlset>
`;
}buildRobotsTxt function · javascript · L258-L270 (13 LOC)scripts/generate-seo.mjs
function buildRobotsTxt(baseUrl) {
if (process.env[ROBOTS_NOINDEX_ENV] === "1") {
return `User-agent: *
Disallow: /
`;
}
return `User-agent: *
Allow: /
Sitemap: ${baseUrl}/sitemap.xml
`;
}Provenance: Repobility (https://repobility.com) — every score reproducible from /scan/
main function · javascript · L272-L292 (21 LOC)scripts/generate-seo.mjs
function main() {
const distDir = join(process.cwd(), "dist");
if (!existsSync(distDir)) {
console.error("ERROR: dist/ not found. Run `vite build` first.");
process.exit(1);
}
const baseUrl = resolveBaseUrl();
const sitemapEntries = buildSitemapEntries(baseUrl);
const sitemapXml = buildSitemapXml(sitemapEntries);
const robotsTxt = buildRobotsTxt(baseUrl);
writeFileSync(join(distDir, "sitemap.xml"), sitemapXml, "utf8");
writeFileSync(join(distDir, "robots.txt"), robotsTxt, "utf8");
console.log(
`sitemap.xml: ${sitemapEntries.length} URLs written to dist/sitemap.xml`,
);
console.log("robots.txt: written to dist/robots.txt");
console.log(`Base URL: ${baseUrl}`);
}App function · typescript · L343-L357 (15 LOC)src/App.tsx
export default function App() {
return (
<HelmetProvider>
<QueryClientProvider client={queryClient}>
<LanguageProvider>
<AuthProvider>
<BookingProvider>
<RouterProvider router={router} />
</BookingProvider>
</AuthProvider>
</LanguageProvider>
</QueryClientProvider>
</HelmetProvider>
);
}AuthorBox function · typescript · L8-L37 (30 LOC)src/components/blog/AuthorBox.tsx
export default function AuthorBox({ author }: AuthorBoxProps) {
const { i18n } = useTranslation()
const currentLang = i18n.language as 'en' | 'es' | 'fr' | 'pt'
return (
<div className="border-t border-b border-border py-6 my-8">
<div className="flex items-start gap-4">
{/* Avatar */}
<img
src={author.avatar}
alt={author.name}
className="w-16 h-16 rounded-full object-cover flex-shrink-0"
/>
{/* Author Info */}
<div className="flex-1">
<h3 className="text-lg font-semibold text-foreground mb-1">
{author.name}
</h3>
<p className="text-sm text-muted-foreground mb-2">
{author.role[currentLang]}
</p>
<p className="text-sm text-foreground/80">
{author.bio[currentLang]}
</p>
</div>
</div>
</div>
)
}BlogCard function · typescript · L15-L109 (95 LOC)src/components/blog/BlogCard.tsx
export default function BlogCard({ post, featured = false }: BlogCardProps) {
const { i18n } = useTranslation()
const currentLang = i18n.language as 'en' | 'fr' | 'es' | 'pt'
const category = getCategoryBySlug(post.category)
return (
<Card className={`group overflow-hidden glass-card-premium hover:shadow-luxury-hover transition-all duration-500 border-2 border-primary/20 hover:border-primary/40 hover:-translate-y-2 ${
featured ? 'md:col-span-2 md:row-span-2' : ''
}`}>
<Link to={`/blog/${post.category}/${post.slug}`} className="block">
{/* Cover Image */}
<div className="relative overflow-hidden aspect-video">
<img
src={post.image.url}
alt={post.image.alt[currentLang]}
className="w-full h-full object-cover group-hover:scale-110 transition-transform duration-700"
/>
{/* Gradient overlay on hover */}
<div className="absolute inset-0 bg-gradient-to-br from-primary/5 viaBlogContent function · typescript · L8-L68 (61 LOC)src/components/blog/BlogContent.tsx
export default function BlogContent({ content, children }: BlogContentProps) {
const contentRef = useRef<HTMLDivElement>(null)
useEffect(() => {
if (!contentRef.current) return
// Add IDs to headings for table of contents navigation
const headings = contentRef.current.querySelectorAll('h2, h3')
headings.forEach((heading) => {
const id = heading.textContent
?.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/(^-|-$)/g, '')
if (id) {
heading.id = id
}
})
}, [content, children])
// Convert markdown to HTML (basic implementation)
const renderMarkdown = (markdown: string) => {
let html = markdown
// Headers
html = html.replace(/^### (.*$)/gim, '<h3 class="text-xl font-bold mt-8 mb-4 text-foreground">$1</h3>')
html = html.replace(/^## (.*$)/gim, '<h2 class="text-2xl font-bold mt-10 mb-6 text-foreground">$1</h2>')
html = html.replace(/^# (.*$)/gim, '<h1 class="text-3xl font-bold mt-12 mBlogHeader function · typescript · L14-L137 (124 LOC)src/components/blog/BlogHeader.tsx
export default function BlogHeader({ post }: BlogHeaderProps) {
const { i18n } = useTranslation();
const currentLang = i18n.language as "en" | "fr" | "es" | "pt";
const category = getCategoryBySlug(post.category);
const handleShare = async () => {
const shareData = {
title: post.title[currentLang],
text: post.description[currentLang],
url: window.location.href,
};
try {
if (navigator.share) {
await navigator.share(shareData);
} else {
// Fallback: copy to clipboard
await navigator.clipboard.writeText(window.location.href);
toast({
title: "Link copied!",
description: "The article link has been copied to your clipboard.",
});
}
} catch (error) {
console.error("Error sharing:", error);
}
};
return (
<div className="relative">
{/* Cover Image */}
<div className="relative w-full aspect-[16/9] max-h-[420px] md:max-h-[500px] overflow-hidden BlogSidebar function · typescript · L23-L146 (124 LOC)src/components/blog/BlogSidebar.tsx
export default function BlogSidebar() {
const { t } = useTranslation();
const navigate = useNavigate();
const [selectedRoute, setSelectedRoute] = useState<string | null>(null);
const popularRoutes: PopularRoute[] = [
{ from: 'CDG Airport', to: 'Paris Center', price: 85, href: '/airports/cdg' },
{ from: 'Orly Airport', to: 'Paris Center', price: 70, href: '/airports/orly' },
{ from: 'CDG Airport', to: 'Disneyland', price: 105, href: '/booking' },
{ from: 'Paris Center', to: 'Versailles', price: 85, href: '/booking' },
];
const handleQuickQuote = () => {
navigate('/booking');
};
const handleWhatsApp = () => {
const message = encodeURIComponent('Hi! I need a quote for a transfer in Paris.');
window.open(`https://wa.me/33668251102?text=${message}`, '_blank');
};
return (
<aside className="space-y-6">
{/* Quick Quote Card */}
<Card className="border-2 border-primary/20 shadow-lg">
<CardHeader className="bg-gradient-Breadcrumb function · typescript · L13-L53 (41 LOC)src/components/blog/Breadcrumb.tsx
export default function Breadcrumb({ items }: BreadcrumbProps) {
return (
<nav aria-label="Breadcrumb" className="py-4">
<ol className="flex items-center flex-wrap gap-2 text-sm">
{/* Home Icon */}
<li>
<Link
to="/"
className="flex items-center text-muted-foreground hover:text-foreground transition-colors"
>
<Home className="w-4 h-4" />
<span className="sr-only">Home</span>
</Link>
</li>
{/* Breadcrumb Items */}
{items.map((item, index) => {
const isLast = index === items.length - 1
return (
<li key={index} className="flex items-center gap-2">
<ChevronRight className="w-4 h-4 text-muted-foreground" />
{isLast || !item.href ? (
<span className="font-medium text-foreground">
{item.label}
</span>
) : (
<Link
Generated by Repobility's multi-pass static-analysis pipeline (https://repobility.com)
CategoryFilter function · typescript · L13-L66 (54 LOC)src/components/blog/CategoryFilter.tsx
export default function CategoryFilter({
selectedCategory,
onCategoryChange,
}: CategoryFilterProps) {
const { i18n } = useTranslation()
const currentLang = i18n.language as 'en' | 'fr' | 'es' | 'pt'
const allCategoriesText = {
en: 'All Articles',
fr: 'Tous les Articles',
es: 'Todos los Artículos',
pt: 'Todos os Artigos',
}
return (
<div className="flex flex-wrap gap-3 justify-center">
{/* All Categories Button - Premium Style */}
<Button
variant={selectedCategory === 'all' ? 'default' : 'outline'}
onClick={() => onCategoryChange('all')}
className={cn(
'transition-all duration-300 font-semibold',
selectedCategory === 'all'
? 'silk-button shadow-lg'
: 'button-outline-gold hover:scale-105'
)}
>
{allCategoriesText[currentLang]}
</Button>
{/* Category Buttons - Premium Style */}
{blogCategories.map((category) => {
const IconComFinalCTA function · typescript · L7-L76 (70 LOC)src/components/blog/FinalCTA.tsx
export default function FinalCTA() {
const { t } = useTranslation();
const [isModalOpen, setIsModalOpen] = useState(false);
const benefits = [
t("blog.professionalService") || "Professional chauffeur service",
t("blog.freeCancellation") || "Free cancellation up to 24h",
t("blog.flightMonitoring") || "Flight monitoring included",
t("blog.premiumVehicles") || "Premium vehicles (Mercedes, BMW)",
];
return (
<>
<div className="bg-gradient-to-br from-primary/10 via-primary/5 to-transparent border border-primary/20 rounded-2xl p-8 md:p-12 my-12">
<div className="grid md:grid-cols-2 gap-8 items-center">
{/* Text Content */}
<div>
<h2 className="text-3xl md:text-4xl font-bold text-foreground mb-4">
{t("blog.readyToBook") || "Ready to Book Your Transfer?"}
</h2>
<p className="text-muted-foreground mb-6">
Experience hassle-free airport transfers with our professional
InlineBookingCTA function · typescript · L7-L49 (43 LOC)src/components/blog/InlineBookingCTA.tsx
export default function InlineBookingCTA() {
const { t } = useTranslation();
const [isModalOpen, setIsModalOpen] = useState(false);
return (
<>
<div className="bg-secondary/10 border border-primary/20 rounded-lg p-6 my-8">
<div className="flex items-center gap-4">
<div className="flex-shrink-0">
<div className="w-12 h-12 bg-primary/10 rounded-full flex items-center justify-center">
<Car className="w-6 h-6 text-primary" />
</div>
</div>
<div className="flex-1">
<h3 className="text-lg font-semibold text-foreground mb-1">
{t("blog.needTransfer") || "Need a transfer?"}
</h3>
<p className="text-sm text-muted-foreground">
{t("blog.calculatePrice") ||
"Get an instant quote for your airport transfer"}
</p>
</div>
<Button
onClick={() => setIsModalOpen(true)}
className="NewsletterCTA function · typescript · L8-L137 (130 LOC)src/components/blog/NewsletterCTA.tsx
export default function NewsletterCTA() {
const { t, i18n } = useTranslation()
const { toast } = useToast()
const [email, setEmail] = useState('')
const [isLoading, setIsLoading] = useState(false)
const handleSubmit = async (e: FormEvent) => {
e.preventDefault()
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!email || !emailRegex.test(email)) {
toast({
title: t('exitPopup.invalidEmail'),
variant: 'destructive',
})
return
}
setIsLoading(true)
try {
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY
const isConfigured =
supabaseUrl &&
supabaseAnonKey &&
!supabaseAnonKey.includes('temporary') &&
!supabaseAnonKey.includes('your_')
if (!isConfigured) {
console.warn('⚠️ Supabase not configured. Using demo mode.')
toast({
title: t('exitPopup.success'),
descripRelatedPosts function · typescript · L12-L45 (34 LOC)src/components/blog/RelatedPosts.tsx
export default function RelatedPosts({ currentPostId, category, maxPosts = 3 }: RelatedPostsProps) {
const { t } = useTranslation()
// Get posts from same category, excluding current post
const categoryPosts = getPostsByCategory(category)
.filter((post) => post.id !== currentPostId)
.slice(0, maxPosts)
// If not enough posts in category, fill with other posts
const relatedPosts = categoryPosts.length < maxPosts
? [
...categoryPosts,
...blogPosts
.filter((post) => post.id !== currentPostId && post.category !== category)
.slice(0, maxPosts - categoryPosts.length),
]
: categoryPosts
if (relatedPosts.length === 0) return null
return (
<section className="py-16 mt-16 border-t">
<h2 className="text-3xl font-bold text-foreground mb-8">
{t('blog.relatedArticles') || 'Related Articles'}
</h2>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{relatedPosts.map((ShareButtons function · typescript · L12-L105 (94 LOC)src/components/blog/ShareButtons.tsx
export default function ShareButtons({ title, url }: ShareButtonsProps) {
const { t } = useTranslation()
const { toast } = useToast()
const [isOpen, setIsOpen] = useState(false)
const shareUrl = url || window.location.href
const encodedUrl = encodeURIComponent(shareUrl)
const encodedTitle = encodeURIComponent(title)
const handleCopyLink = async () => {
try {
await navigator.clipboard.writeText(shareUrl)
toast({
title: t('blog.linkCopied') || 'Link copied!',
description: t('blog.copySuccess') || 'The link has been copied to your clipboard.',
duration: 3000,
})
} catch (error) {
toast({
title: t('blog.copyError') || 'Error',
description: 'Failed to copy link. Please try again.',
variant: 'destructive',
})
}
}
const shareLinks = [
{
name: 'WhatsApp',
icon: MessageCircle,
url: `https://wa.me/?text=${encodedTitle}%20${encodedUrl}`,
color: 'hover:bg-greenTableOfContents function · typescript · L10-L118 (109 LOC)src/components/blog/TableOfContents.tsx
export default function TableOfContents({ items: propItems }: TableOfContentsProps) {
const [activeId, setActiveId] = useState<string>('')
const [items, setItems] = useState<TableOfContentsItem[]>(propItems || [])
// Auto-generate TOC from DOM if items not provided
useEffect(() => {
if (propItems && propItems.length > 0) {
setItems(propItems)
return
}
// Generate TOC from headings in the document
const headings = document.querySelectorAll('article h2, article h3, main h2, main h3')
const generatedItems: TableOfContentsItem[] = []
headings.forEach((heading) => {
const id = heading.id || heading.textContent
?.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/(^-|-$)/g, '') || ''
if (!heading.id && id) {
heading.id = id
}
if (id) {
generatedItems.push({
id,
title: heading.textContent || '',
level: parseInt(heading.tagName.substring(1)),
BookingConfirmation function · typescript · L11-L21 (11 LOC)src/components/BookingConfirmation.tsx
export function BookingConfirmation({ bookingId, bookingData, tourData }: BookingConfirmationProps) {
const { t } = useLanguage();
return (
<div className="space-y-6">
<h2 className="text-2xl font-bold">{t.booking.success.title}</h2>
<p>{t.booking.success.description}</p>
{/* Add more booking confirmation details */}
</div>
);
}Methodology: Repobility · https://repobility.com/research/state-of-ai-code-2026/
CouponInput function · typescript · L9-L103 (95 LOC)src/components/booking/CouponInput.tsx
export default function CouponInput() {
const { t } = useTranslation()
const { coupon, validateCoupon, removeCoupon } = useBooking()
const [code, setCode] = useState('')
const [isValidating, setIsValidating] = useState(false)
const handleApply = async () => {
if (!code.trim()) return
setIsValidating(true)
await validateCoupon(code.trim())
setIsValidating(false)
setCode('') // Clear input after validation
}
const handleKeyPress = (e: React.KeyboardEvent) => {
if (e.key === 'Enter') {
e.preventDefault()
handleApply()
}
}
// If coupon is already applied, show it
if (coupon?.valid) {
return (
<div className="space-y-2">
<label className="text-sm font-medium text-foreground flex items-center gap-2">
<Tag className="w-4 h-4" />
{t('booking.coupon.label')}
</label>
<div className="flex items-center gap-2 p-3 bg-green-50 dark:bg-green-900/20 border border-green-200 dark:border-DestinationContent function · typescript · L15-L25 (11 LOC)src/components/destination/DestinationContent.tsx
export function DestinationContent({ activeSection, content }: DestinationContentProps) {
return (
<div className="py-8">
{activeSection === 'description' && content.description}
{activeSection === 'tours' && content.tours}
{activeSection === 'map' && content.map}
{activeSection === 'events' && content.events}
{activeSection === 'faq' && content.faq}
</div>
);
}DestinationHeader function · typescript · L12-L31 (20 LOC)src/components/destination/DestinationHeader.tsx
export function DestinationHeader({ title, image, distance, duration, currentPath }: DestinationHeaderProps) {
return (
<header className="relative h-[60vh] min-h-[400px] w-full">
<div className="absolute inset-0">
<img src={image} alt={title} className="w-full h-full object-cover" />
<div className="absolute inset-0 bg-black/50" />
</div>
<div className="absolute inset-0 flex items-center justify-center text-white">
<div className="text-center">
<h1 className="text-4xl md:text-5xl font-display mb-4">{title}</h1>
<div className="flex items-center justify-center gap-4 text-sm">
<span>{distance}</span>
<span>•</span>
<span>{duration}</span>
</div>
</div>
</div>
</header>
);
}DestinationNavigation function · typescript · L10-L32 (23 LOC)src/components/destination/DestinationNavigation.tsx
export function DestinationNavigation({ items, activeSection, onSectionChange }: DestinationNavigationProps) {
return (
<nav className="sticky top-0 z-10 bg-white dark:bg-gray-800 border-b border-border">
<div className="container mx-auto px-4">
<div className="flex overflow-x-auto">
{items.map((item) => (
<button
key={item.id}
onClick={() => onSectionChange(item.id)}
className={`px-4 py-3 text-sm whitespace-nowrap border-b-2 transition-colors ${
activeSection === item.id
? 'border-primary text-primary'
: 'border-transparent text-muted-foreground hover:text-primary'
}`}
>
{item.label}
</button>
))}
</div>
</div>
</nav>
);
}DestinationSidebar function · typescript · L9-L18 (10 LOC)src/components/destination/DestinationSidebar.tsx
export function DestinationSidebar({ children, tours }: DestinationSidebarProps) {
return (
<aside className="space-y-8">
{children}
<div className="sticky top-24">
{tours}
</div>
</aside>
);
}ExcursionCard function · typescript · L16-L52 (37 LOC)src/components/excursions/ExcursionCard.tsx
export function ExcursionCard({
title,
description,
image,
duration,
price,
link
}: ExcursionCardProps) {
const { t } = useLanguage();
return (
<Card className="overflow-hidden">
<div className="relative h-48 overflow-hidden">
<img
src={image}
alt={title}
className="w-full h-full object-cover"
/>
</div>
<CardContent className="p-4">
<h3 className="text-xl font-semibold mb-2">{title}</h3>
<p className="text-muted-foreground text-sm mb-4">{description}</p>
<div className="flex justify-between items-center">
<span className="text-sm">{duration}</span>
<span className="font-semibold">{`€${price}`}</span>
</div>
</CardContent>
<CardFooter className="p-4 pt-0">
<Link to={link} className="w-full">
<Button className="w-full">
{t.excursions.viewDetails}
</Button>
</Link>
</CardFooter>
</CardExcursionFilters function · typescript · L22-L93 (72 LOC)src/components/excursions/ExcursionFilters.tsx
export function ExcursionFilters({
searchQuery,
setSearchQuery,
selectedDuration,
setSelectedDuration,
selectedType,
setSelectedType
}: ExcursionFiltersProps) {
const { t } = useLanguage();
const handleClearFilters = () => {
setSearchQuery("");
setSelectedDuration(null);
setSelectedType(null);
};
return (
<div className="flex flex-col md:flex-row gap-4 p-4 bg-white rounded-lg shadow-sm">
<div className="flex-1">
<Input
type="text"
placeholder={t.excursions.searchPlaceholder}
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="w-full"
/>
</div>
<div className="w-full md:w-48">
<Select
value={selectedDuration ?? ""}
onValueChange={(value) => setSelectedDuration(value as DurationType || null)}
>
<SelectTrigger>
<SelectValue placeholder={t.excursions.filters.allDurations} />
HashScroll function · typescript · L4-L32 (29 LOC)src/components/HashScroll.tsx
export function HashScroll() {
const location = useLocation();
useEffect(() => {
if (!location.hash) return;
const id = location.hash.slice(1);
// Use requestAnimationFrame for SPA-safe scroll after DOM paint
requestAnimationFrame(() => {
const element = document.getElementById(id);
if (!element) return;
// Get navbar height for proper offset
const navbar = document.querySelector("nav");
const headerOffset = navbar?.getBoundingClientRect().height ?? 80;
const elementPosition = element.getBoundingClientRect().top;
const offsetPosition =
elementPosition + window.scrollY - headerOffset - 8;
window.scrollTo({
top: offsetPosition,
behavior: "smooth",
});
});
}, [location.hash, location.pathname]);
return null;
}Want fix-PRs on findings? Install Repobility's GitHub App · github.com/apps/repobility-bot
LanguageChangeNotification function · typescript · L5-L24 (20 LOC)src/components/LanguageChangeNotification.tsx
export function LanguageChangeNotification() {
const { language, t } = useLanguage();
const prevLanguageRef = React.useRef(language);
React.useEffect(() => {
if (prevLanguageRef.current !== language) {
const message = t.toast?.languageChanged || 'Language changed successfully';
// Usar setTimeout para asegurar que el toast se muestre después de que el DOM se actualice
setTimeout(() => {
toast({
title: message,
duration: 2000,
});
}, 0);
prevLanguageRef.current = language;
}
}, [language, t.toast?.languageChanged]);
return null;
}LanguageSelector function · typescript · L20-L63 (44 LOC)src/components/LanguageSelector.tsx
export function LanguageSelector() {
const { i18n } = useTranslation();
const { setLanguage } = useLanguage();
const handleLanguageChange = (value: Language) => {
setLanguage(value);
};
const currentLanguage = useMemo(
() => languages.find((lang) => lang.code === i18n.language),
[i18n.language],
);
if (!currentLanguage) return null;
return (
<Select value={i18n.language} onValueChange={handleLanguageChange}>
<SelectTrigger className="w-[110px] h-10 px-3">
<SelectValue>
<span className="flex items-center gap-2">
<span className="text-base">{currentLanguage.flag}</span>
<span className="font-medium uppercase text-xs tracking-wide">
{currentLanguage.code}
</span>
</span>
</SelectValue>
</SelectTrigger>
<SelectContent>
{languages.map((language) => (
<SelectItem
key={language.code}
value={language.code}
Layout function · typescript · L10-L24 (15 LOC)src/components/Layout.tsx
export function Layout() {
return (
<div className="min-h-screen flex flex-col bg-background text-foreground antialiased transition-colors duration-300">
<HashScroll />
<Navbar />
<main className="flex-1 pt-16 pb-20 lg:pb-0">
<Outlet />
</main>
<Footer />
<FloatingWhatsApp />
<CookieConsent />
<ExitIntentPopup />
</div>
);
}MobileStickyCTA function · typescript · L10-L33 (24 LOC)src/components/MobileStickyCTA.tsx
export default function MobileStickyCTA({
label,
onClick,
isVisible,
}: MobileStickyCTAProps) {
if (!isVisible) return null;
return (
<div
className="fixed bottom-0 left-0 right-0 z-40 lg:hidden border-t border-primary/20 bg-white/95 backdrop-blur-sm"
style={{ paddingBottom: "env(safe-area-inset-bottom, 0px)" }}
>
<div className="px-4 py-3">
<button
onClick={onClick}
className="silk-button w-full flex items-center justify-center gap-2 min-h-[48px] text-sm"
>
{label}
<ArrowRight className="w-4 h-4" />
</button>
</div>
</div>
);
}PaymentForm function · typescript · L13-L32 (20 LOC)src/components/PaymentForm.tsx
export function PaymentForm({ bookingData, onSubmit, isProcessing, error }: FormProps) {
const { t } = useLanguage();
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
onSubmit(bookingData);
};
return (
<form onSubmit={handleSubmit} className="space-y-6">
{/* Payment form content */}
{error && (
<div className="text-red-500">{error}</div>
)}
<Button type="submit" disabled={isProcessing}>
{isProcessing ? t.common.processing : t.booking.payment.title}
</Button>
</form>
);
}RouteHighlights function · typescript · L13-L170 (158 LOC)src/components/RouteHighlights.tsx
export default function RouteHighlights() {
const { t } = useLanguage();
const popularRoutes = [
{
icon: Plane,
title: t.routes?.cdg || "CDG Airport",
description: t.routes?.cdgDesc || "Paris ⇄ Charles de Gaulle",
priceFrom: "70",
bgGradient: "from-primary/5 to-primary/10",
},
{
icon: Plane,
title: t.routes?.orly || "Orly Airport",
description: t.routes?.orlyDesc || "Paris ⇄ Orly",
priceFrom: "60",
bgGradient: "from-primary/5 to-primary/10",
},
{
icon: Castle,
title: t.routes?.disney || "Disneyland Paris",
description: t.routes?.disneyDesc || "Magical day trip",
priceFrom: "95",
bgGradient: "from-primary/5 to-primary/10",
},
{
icon: Crown,
title: t.routes?.versailles || "Versailles",
description: t.routes?.versaillesDesc || "Royal palace tour",
priceFrom: "75",
bgGradient: "from-primary/5 to-primary/10",
},
];
const handleRoutJsonLd function · typescript · L10-L18 (9 LOC)src/components/seo/JsonLd.tsx
export default function JsonLd({ data }: JsonLdProps) {
return (
<Helmet>
<script type="application/ld+json">
{JSON.stringify(data)}
</script>
</Helmet>
)
}ServiceCard function · typescript · L13-L48 (36 LOC)src/components/ServiceCard.tsx
export default function ServiceCard({
title,
description,
icon: Icon,
price,
className
}: ServiceCardProps) {
return (
<div className={cn(
"group relative overflow-hidden rounded-lg bg-white/80 dark:bg-gray-800/80 backdrop-blur-lg shadow-lg hover:shadow-xl transition-all duration-300 p-8 border border-primary/10",
className
)}>
<div className="absolute inset-0 bg-gradient-to-b from-primary/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-300" />
<div className="relative z-10 flex flex-col items-center">
<div className="w-14 h-14 bg-primary/10 rounded-full flex items-center justify-center mb-6 group-hover:scale-110 transition-transform duration-300">
<Icon className="h-7 w-7 text-primary" />
</div>
<h3 className="text-xl font-semibold text-primary dark:text-primary-foreground mb-4 text-center group-hover:text-primary/90 transition-colors">
{title}
Provenance: Repobility (https://repobility.com) — every score reproducible from /scan/
TestEmail function · typescript · L21-L121 (101 LOC)src/components/TestEmail.tsx
export function TestEmail() {
const [response, setResponse] = useState<EmailResponse | null>(null);
const [loading, setLoading] = useState(false);
const { toast } = useToast();
const handleTestEmail = async () => {
setLoading(true);
try {
const { data, error } = await supabase.functions.invoke('send-booking-emails', {
body: {
customerName: "Geiner Boris",
customerEmail: "[email protected]",
bookingId: "TEST-" + new Date().getTime(),
pickupLocation: "Charles de Gaulle Airport (CDG)",
dropoffLocation: "Eiffel Tower",
pickupDateTime: new Date().toISOString(),
passengers: 2,
vehicleType: "Luxury Sedan",
totalPrice: 150,
flightNumber: "AF123"
}
});
if (error) {
console.error('Error details:', error);
throw error;
}
setResponse(data);
toast({
title: "Email enviado",
description: "El emailTourCard function · typescript · L14-L38 (25 LOC)src/components/TourCard.tsx
export function TourCard({ title, description, duration, price, includes, onBook }: TourCardProps) {
return (
<div className="bg-white dark:bg-gray-800 rounded-lg shadow-lg p-6">
<h3 className="text-xl font-display text-primary dark:text-primary-foreground mb-2">{title}</h3>
<p className="text-muted-foreground mb-4">{description}</p>
<div className="flex items-center justify-between mb-4">
<span className="text-sm text-muted-foreground">{duration}</span>
<span className="text-lg font-semibold">€{price}</span>
</div>
<div className="space-y-2 mb-4">
{includes.map((item, index) => (
<div key={index} className="flex items-center text-sm text-muted-foreground">
<span className="mr-2">•</span>
{item}
</div>
))}
</div>
{onBook && (
<Button onClick={onBook} className="w-full">
Book Now
</Button>
)}
</div>
);
}TrustBar function · typescript · L10-L65 (56 LOC)src/components/TrustBar.tsx
export default function TrustBar() {
const { t } = useLanguage();
const trustItems = [
{
icon: ShieldCheck,
label: t.trustBar?.securePayment || "Secure Payment",
description: t.trustBar?.securePaymentDesc || "SSL encrypted",
},
{
icon: Award,
label: t.trustBar?.licensed || "Licensed & Insured",
description: t.trustBar?.licensedDesc || "Official VTC license",
},
{
icon: Clock,
label: t.trustBar?.available || "24/7 Available",
description: t.trustBar?.availableDesc || "Always at your service",
},
{
icon: Shield,
label: t.trustBar?.insurance || "Full Insurance",
description: t.trustBar?.insuranceDesc || "Complete coverage",
},
];
return (
<div className="w-full bg-accent/50 border-t border-b border-border py-6 px-4">
<div className="container max-w-4xl mx-auto">
<div className="grid grid-cols-2 md:grid-cols-4 gap-4 md:gap-6">
{trustItems.map((item, page 1 / 5next ›