This commit is contained in:
Bognar 2026-03-13 20:23:19 +01:00
parent d4b7ed1f27
commit fb59e93643
51 changed files with 372 additions and 119 deletions

View File

@ -70,9 +70,9 @@ export default function PricingPage() {
</Link> </Link>
<div className="space-y-4"> <div className="space-y-4">
<h1 className="text-5xl md:text-8xl font-black text-white tracking-tighter leading-none"> <h1 className="text-5xl md:text-8xl font-black text-white tracking-tighter leading-none uppercase">
{t.pricing.title} <br /> {t.pricing.title} <br />
<span className="text-secondary/40 italic">{t.pricing.titleAccent}</span> <span className="text-slate-900 italic">{t.pricing.titleAccent}</span>
</h1> </h1>
<p className="text-xl text-white/70 max-w-2xl leading-relaxed font-medium"> <p className="text-xl text-white/70 max-w-2xl leading-relaxed font-medium">
{t.pricing.description} {t.pricing.description}

View File

@ -41,7 +41,7 @@ export default function ConditionsPage() {
<div className="space-y-4"> <div className="space-y-4">
<h1 className="text-5xl md:text-8xl font-black text-white tracking-tighter leading-none uppercase"> <h1 className="text-5xl md:text-8xl font-black text-white tracking-tighter leading-none uppercase">
{t.conditionsPage.title} <br /> {t.conditionsPage.title} <br />
<span className="text-secondary/40 italic">{t.conditionsPage.titleAccent}</span> <span className="text-slate-900 italic">{t.conditionsPage.titleAccent}</span>
</h1> </h1>
</div> </div>
</div> </div>

View File

@ -1,5 +1,6 @@
"use client" "use client"
import { useState, useEffect } from "react"
import Navbar from "@/components/navbar" import Navbar from "@/components/navbar"
import Footer from "@/components/footer" import Footer from "@/components/footer"
import PageReveal from "@/components/page-reveal" import PageReveal from "@/components/page-reveal"
@ -9,36 +10,53 @@ import Link from "next/link"
import Image from "next/image" import Image from "next/image"
import { useLanguage } from "@/lib/language-context" import { useLanguage } from "@/lib/language-context"
import { motion } from "framer-motion" import { motion } from "framer-motion"
import { GalleryDialog } from "@/components/gallery-dialog"
export default function FleetPage() { export default function FleetPage() {
const { t } = useLanguage() const { t } = useLanguage()
const [galleryData, setGalleryData] = useState<Record<string, string[]>>({})
const [isGalleryOpen, setIsGalleryOpen] = useState(false)
const [selectedGallery, setSelectedGallery] = useState<string[]>([])
const [selectedVehicleName, setSelectedVehicleName] = useState("")
useEffect(() => {
fetch('/gallery.json')
.then(res => res.json())
.then(data => setGalleryData(data))
.catch(err => console.error('Error fetching gallery:', err))
}, [])
const openGallery = (categoryId: string, vehicleName: string) => {
const images = galleryData[categoryId] || []
if (images.length > 0) {
setSelectedGallery(images)
setSelectedVehicleName(vehicleName)
setIsGalleryOpen(true)
}
}
const vehicles = [ const vehicles = [
{ {
id: "vclass", id: "vclass",
categoryId: "vip",
data: t.fleetPage.vehicles.vclass, data: t.fleetPage.vehicles.vclass,
image: "/images/fleet/v-class.jpg", image: "/images/fleet/Flotta_VIP_kezdő.png",
large: true, large: true,
icon: Users icon: Users
}, },
{
id: "eclass",
data: t.fleetPage.vehicles.eclass,
image: "/images/fleet/e-class.jpg",
large: false,
icon: Briefcase
},
{ {
id: "superb", id: "superb",
categoryId: "personal",
data: t.fleetPage.vehicles.superb, data: t.fleetPage.vehicles.superb,
image: "/images/fleet/superb.jpg", image: "/images/fleet/3_skoda_repter.png",
large: false, large: true,
icon: Wind icon: Wind
}, },
{ {
id: "transit", id: "transit",
categoryId: "minibus",
data: t.fleetPage.vehicles.transit, data: t.fleetPage.vehicles.transit,
image: "/images/fleet/transit.jpg", image: "/images/fleet/Tourneo_hatter_hegy.png",
large: true, large: true,
icon: Users icon: Users
} }
@ -159,24 +177,27 @@ export default function FleetPage() {
</p> </p>
</div> </div>
<div className="flex flex-wrap gap-x-8 gap-y-4 pt-4"> {vehicle.data.features && vehicle.data.features.length > 0 && (
{vehicle.data.features.map((feature: string, fIdx: number) => ( <div className="flex flex-wrap gap-x-8 gap-y-4 pt-4">
<div key={fIdx} className="flex items-center gap-3 text-white/80"> {vehicle.data.features.map((feature: string, fIdx: number) => (
<div className="w-1.5 h-1.5 rounded-full bg-primary" /> <div key={fIdx} className="flex items-center gap-3 text-white/80">
<span className="text-[10px] md:text-xs font-black uppercase tracking-widest">{feature}</span> <div className="w-1.5 h-1.5 rounded-full bg-primary" />
</div> <span className="text-[10px] md:text-xs font-black uppercase tracking-widest">{feature}</span>
))} </div>
</div> ))}
</div>
)}
<div className="pt-8 border-t border-white/10 flex items-center justify-between"> <div className="pt-8 border-t border-white/10 flex items-center justify-between">
<Link <div className="flex flex-wrap gap-6 items-center">
href="https://app.skyflytravel.hu/public/offers/new" <button
target="_blank" onClick={() => openGallery(vehicle.categoryId, vehicle.data.name)}
className="inline-flex items-center gap-4 text-primary font-black uppercase tracking-[0.2em] text-[10px] hover:gap-6 transition-all" className="inline-flex items-center gap-4 text-primary font-black uppercase tracking-[0.2em] text-[10px] hover:gap-6 transition-all cursor-pointer group/btn"
> >
{t.nav.cta} {t.common.moreImages}
<ChevronRight className="w-4 h-4" /> <ChevronRight className="w-4 h-4 group-hover/btn:translate-x-1 transition-transform" />
</Link> </button>
</div>
<div className="hidden md:flex gap-4"> <div className="hidden md:flex gap-4">
<div className="w-12 h-12 rounded-2xl bg-white/10 backdrop-blur-md flex items-center justify-center text-white border border-white/10 group-hover:bg-primary/20 group-hover:border-primary/30 transition-all"> <div className="w-12 h-12 rounded-2xl bg-white/10 backdrop-blur-md flex items-center justify-center text-white border border-white/10 group-hover:bg-primary/20 group-hover:border-primary/30 transition-all">
@ -224,7 +245,15 @@ export default function FleetPage() {
</div> </div>
</div> </div>
</section> </section>
<Footer />
<GalleryDialog
isOpen={isGalleryOpen}
onClose={() => setIsGalleryOpen(false)}
images={selectedGallery}
vehicleName={selectedVehicleName}
/>
<Footer />
</PageReveal> </PageReveal>
) )
} }

View File

@ -67,7 +67,7 @@ export default function ContactPage() {
<div className="space-y-4"> <div className="space-y-4">
<h1 className="text-5xl md:text-8xl font-black text-white tracking-tighter leading-none uppercase"> <h1 className="text-5xl md:text-8xl font-black text-white tracking-tighter leading-none uppercase">
{t.contactPage.title} <br /> {t.contactPage.title} <br />
<span className="text-secondary/40 italic">{t.contactPage.titleAccent}</span> <span className="text-slate-900 italic">{t.contactPage.titleAccent}</span>
</h1> </h1>
<p className="text-xl text-white/70 max-w-2xl leading-relaxed font-medium"> <p className="text-xl text-white/70 max-w-2xl leading-relaxed font-medium">
{t.contactPage.description} {t.contactPage.description}
@ -96,7 +96,7 @@ export default function ContactPage() {
<a <a
key={`${value}-${valueIdx}`} key={`${value}-${valueIdx}`}
href={href} href={href}
className="text-2xl font-black text-slate-900 hover:text-primary transition-colors" className="text-2xl font-black text-slate-900 hover:text-primary transition-colors block"
> >
{value} {value}
</a> </a>

View File

@ -71,7 +71,7 @@ export default function Home() {
<div className="absolute inset-0 bg-indigo-500/10 blur-3xl rounded-full scale-110 group-hover:scale-125 transition-transform" /> <div className="absolute inset-0 bg-indigo-500/10 blur-3xl rounded-full scale-110 group-hover:scale-125 transition-transform" />
<Card className="relative w-full aspect-square md:w-[450px] md:h-[450px] rounded-[3rem] overflow-hidden border-none shadow-2xl"> <Card className="relative w-full aspect-square md:w-[450px] md:h-[450px] rounded-[3rem] overflow-hidden border-none shadow-2xl">
<Image <Image
src="/images/mercedes.jpg" src="/images/Mercedes Vito_.JPG"
alt="Egyéb személyszállítás" alt="Egyéb személyszállítás"
fill fill
sizes="(min-width: 768px) 450px, 90vw" sizes="(min-width: 768px) 450px, 90vw"

View File

@ -22,13 +22,7 @@ export default function ServicesPage() {
const { t } = useLanguage() const { t } = useLanguage()
const packageEntries = [ const packageEntries = [
{
unique: t.servicesPage.packages.classic.unique,
name: t.servicesPage.packages.classic.title,
desc: t.servicesPage.packages.classic.desc,
toAirport: t.servicesPage.packages.classic.toAirport,
toCity: t.servicesPage.packages.classic.toCity
},
{ {
unique: t.servicesPage.packages.express.unique, unique: t.servicesPage.packages.express.unique,
name: t.servicesPage.packages.express.title, name: t.servicesPage.packages.express.title,

View File

@ -0,0 +1,174 @@
'use client';
import React, { useState, useEffect } from 'react';
import Image from 'next/image';
import { motion, AnimatePresence } from 'framer-motion';
import { X, ChevronLeft, ChevronRight, Maximize2, ImageIcon } from 'lucide-react';
import { cn } from '@/lib/utils';
interface GalleryDialogProps {
isOpen: boolean;
onClose: () => void;
images: string[];
vehicleName: string;
}
export function GalleryDialog({ isOpen, onClose, images, vehicleName }: GalleryDialogProps) {
const [currentIndex, setCurrentIndex] = useState(0);
const [direction, setDirection] = useState(0);
// Lock scroll when open
useEffect(() => {
if (isOpen) {
document.body.style.overflow = 'hidden';
} else {
document.body.style.overflow = 'unset';
}
return () => {
document.body.style.overflow = 'unset';
};
}, [isOpen]);
if (!isOpen) return null;
const nextImage = () => {
setDirection(1);
setCurrentIndex((prev) => (prev + 1) % images.length);
};
const prevImage = () => {
setDirection(-1);
setCurrentIndex((prev) => (prev - 1 + images.length) % images.length);
};
const variants = {
enter: (direction: number) => ({
x: direction > 0 ? 1000 : -1000,
opacity: 0,
scale: 0.9,
}),
center: {
zIndex: 1,
x: 0,
opacity: 1,
scale: 1,
},
exit: (direction: number) => ({
zIndex: 0,
x: direction < 0 ? 1000 : -1000,
opacity: 0,
scale: 0.9,
}),
};
return (
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
className="fixed inset-0 z-[100] flex items-center justify-center bg-black/95 backdrop-blur-xl"
>
{/* Close Button */}
<button
onClick={onClose}
className="absolute top-8 right-8 z-[110] p-4 text-white/50 hover:text-white hover:bg-white/10 rounded-full transition-all"
>
<X className="w-8 h-8" />
</button>
{/* Header Info */}
<div className="absolute top-8 left-8 z-[110] flex flex-col gap-2">
<h3 className="text-2xl font-black uppercase tracking-[0.3em] text-white">
{vehicleName}
</h3>
<div className="flex items-center gap-4 text-primary text-[10px] font-black tracking-widest uppercase">
<ImageIcon className="w-4 h-4" />
<span>{currentIndex + 1} / {images.length} Kép</span>
</div>
</div>
{/* Main Stage */}
<div className="relative w-full h-full max-w-7xl px-4 md:px-12 flex items-center justify-center overflow-hidden">
<AnimatePresence initial={false} custom={direction}>
{images.length > 0 && (
<motion.div
key={currentIndex}
custom={direction}
variants={variants}
initial="enter"
animate="center"
exit="exit"
transition={{
x: { type: "spring", stiffness: 300, damping: 30 },
opacity: { duration: 0.3 },
scale: { duration: 0.4 }
}}
className="absolute inset-0 flex items-center justify-center p-4 md:p-12"
>
<div className="relative w-full h-full shadow-2xl shadow-primary/10 rounded-[2rem] overflow-hidden border border-white/10">
<Image
src={images[currentIndex]}
alt={`${vehicleName} view ${currentIndex + 1}`}
fill
className="object-cover"
priority
quality={100}
/>
{/* Vignette Overlay */}
<div className="absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent pointer-events-none" />
</div>
</motion.div>
)}
</AnimatePresence>
{/* Navigation Handles */}
<button
onClick={prevImage}
className="absolute left-8 z-[110] w-16 h-16 rounded-full border border-white/10 bg-black/20 backdrop-blur-md flex items-center justify-center text-white hover:bg-primary hover:border-primary transition-all group active:scale-95"
>
<ChevronLeft className="w-8 h-8 group-hover:-translate-x-1 transition-transform" />
</button>
<button
onClick={nextImage}
className="absolute right-8 z-[110] w-16 h-16 rounded-full border border-white/10 bg-black/20 backdrop-blur-md flex items-center justify-center text-white hover:bg-primary hover:border-primary transition-all group active:scale-95"
>
<ChevronRight className="w-8 h-8 group-hover:translate-x-1 transition-transform" />
</button>
</div>
{/* Thumbnails Sidebar/Bottom Bar */}
<div className="absolute bottom-8 left-0 right-0 z-[110] flex justify-center gap-4 px-8 overflow-x-auto pb-4 scrollbar-hide">
{images.map((img, idx) => (
<button
key={idx}
onClick={() => {
setDirection(idx > currentIndex ? 1 : -1);
setCurrentIndex(idx);
}}
className={cn(
"relative w-32 h-20 rounded-xl overflow-hidden border-2 transition-all flex-shrink-0 active:scale-95",
currentIndex === idx
? "border-primary scale-110 shadow-lg shadow-primary/20"
: "border-white/10 opacity-40 hover:opacity-100"
)}
>
<Image
src={img}
alt="Thumbnail"
fill
className="object-cover"
/>
</button>
))}
</div>
{/* Subtle noise/texture overlay for premium feel */}
<div className="fixed inset-0 pointer-events-none opacity-[0.03] bg-[url('/images/noise.png')] mix-blend-overlay" />
</motion.div>
)}
</AnimatePresence>
);
}

BIN
images/3 skoda reptér.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

BIN
images/E-osztály4.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

BIN
images/Enyaq reptér.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 128 KiB

BIN
images/Ford transit_2.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

BIN
images/Mercedes Vito_.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

BIN
images/Scenic_1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 KiB

BIN
images/Skoda Octavia.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

BIN
images/Skoda SuperB.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 KiB

BIN
images/Skoda_kék1.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 KiB

BIN
images/Skoda_kék2.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 352 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

BIN
images/mercedes E-class.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

BIN
images/skoda_enyaq 2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

BIN
images/vito.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

BIN
images/Új Trafic1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

BIN
images/új Trafic_2.JPG Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 KiB

View File

@ -4,7 +4,8 @@
brand: "SkyFly Travel", brand: "SkyFly Travel",
email: "info@skyflytravel.hu", email: "info@skyflytravel.hu",
phone: "+36 30 554 3838", phone: "+36 30 554 3838",
tiktokHandle: "@skyflytravel.transfer" tiktokHandle: "@skyflytravel.transfer",
moreImages: "További képek"
}, },
meta: { meta: {
title: "SkyFly Travel - Gyors, megbízható, kényelmes reptéri transzferek!", title: "SkyFly Travel - Gyors, megbízható, kényelmes reptéri transzferek!",
@ -280,13 +281,7 @@
toCityDesc: "Összeültetésnél a viszonyítási alap: Tervezett leszállás + 20-30 perc csomagfelvétel." toCityDesc: "Összeültetésnél a viszonyítási alap: Tervezett leszállás + 20-30 perc csomagfelvétel."
}, },
packages: { packages: {
classic: {
title: "Gyűjtő CLASSIC",
unique: "Egyedi jellemző \"Gyűjtő CLASSIC\":",
desc: "Igazodva más utasokhoz, szabad kapacitás függvényében. Igénybe vehető, ha van más utasunk is, és teljesül a feltétel: min. 30 perc max. 1,5 óra alkalmazkodás.",
toAirport: "Az ideális utas felvételhez képest max. 1-1,5 órával korábbi indulás.",
toCity: "Tervezett leszállás, csomagfelvétel után max. 1-1,5 órával későbbi indulás."
},
express: { express: {
title: "Gyűjtő EXPRESS", title: "Gyűjtő EXPRESS",
unique: "Egyedi jellemző \"Gyűjtő EXPRESS\":", unique: "Egyedi jellemző \"Gyűjtő EXPRESS\":",
@ -304,14 +299,14 @@
family: { family: {
title: "CSALÁDI", title: "CSALÁDI",
unique: "Egyedi jellemző \"CSALÁDI\":", unique: "Egyedi jellemző \"CSALÁDI\":",
desc: "Időben és térben nem kell más utashoz igazodni. Teljesítés: egyterű személygépjárművel, feltétele: max. 3 normál bőrönd, és 3 kézipoggyász.", desc: "Időben és térben nem kell más utashoz igazodni. Teljesítés: egyterű személygépjárművel, feltétele: min. 1 gyerek (0-14 éves), max. 3 normál bőrönd, és 3 kézipoggyász.",
toAirport: "Az utas felvételi időpont az utas számára legideálisabb időpontban.", toAirport: "Az utas felvételi időpont az utas számára legideálisabb időpontban.",
toCity: "Nincs várakozás." toCity: "Nincs várakozás."
}, },
bigFamily: { bigFamily: {
title: "NAGY CSALÁDI", title: "NAGY CSALÁDI",
unique: "Egyedi jellemző \"Nagy\" CSALÁDI:", unique: "Egyedi jellemző \"Nagy\" CSALÁDI:",
desc: "Időben és térben nem kell más utashoz igazodni. Teljesítés: kisbusszal, feltétele: max. 6 normál bőrönd, és 6 kézipoggyász.", desc: "Időben és térben nem kell más utashoz igazodni. Teljesítés: kisbusszal, feltétele: min. 1 gyerek (0-14 éves), max. 6 normál bőrönd, és 6 kézipoggyász.",
toAirport: "Az utas felvételi időpont az utas számára legideálisabb időpontban.", toAirport: "Az utas felvételi időpont az utas számára legideálisabb időpontban.",
toCity: "Nincs várakozás." toCity: "Nincs várakozás."
} }
@ -323,7 +318,7 @@
"Autóink tisztaságára, műszaki állapotára nagy hangsúlyt fektetünk.", "Autóink tisztaságára, műszaki állapotára nagy hangsúlyt fektetünk.",
"A flottához tartozó gépkocsikat tervezett rendszerességgel cseréljük, hogy folyamatosan új autókkal álljunk utasaink rendelkezésére.", "A flottához tartozó gépkocsikat tervezett rendszerességgel cseréljük, hogy folyamatosan új autókkal álljunk utasaink rendelkezésére.",
"A SkyFly Travel rendelkezik minden hatályos jogszabály által előírt személyszállítói engedéllyel.", "A SkyFly Travel rendelkezik minden hatályos jogszabály által előírt személyszállítói engedéllyel.",
"Sofőreink szakképzettek, GKI engedéllyel, PÁV-II tanúsítvánnyal és több éves tapasztalattal rendelkeznek.",
"NON-STOP ügyelet megrendelt fuvarokra (+36 30 5543838)", "NON-STOP ügyelet megrendelt fuvarokra (+36 30 5543838)",
"Háztól Házig szállítjuk (Ön által megadott címről/címre)" "Háztól Házig szállítjuk (Ön által megadott címről/címre)"
] ]
@ -361,17 +356,17 @@
{ {
title: "2. A szolgáltatás megrendelése, információ-, ajánlatkérés", title: "2. A szolgáltatás megrendelése, információ-, ajánlatkérés",
content: [ content: [
"Foglalási szándékát honlapunkon az online foglalási felület kitöltésével tudja leadni. Ajánlatkérését kérjük szíveskedjen email-ben vagy az „ÜZENET” fül alatti űrlap kitöltésével megküldeni részünkre. Foglalása kizárólag a visszaigazolásunk megküldésével válik teljessé, e-nélkül foglalási szándéknak minősül. A foglalásokat szabad kapacitás függvényében tudjuk visszaigazolni a szándék beérkezésétól számítva max. 3 munkanapon belül, írásban, az Ön által megadott email címre megküldve.", "Foglalási szándékát / Árajánlat kérését honlapunkon az Online foglalás / Ajánlat menüpont alatti felület kitöltésével tudja leadni. Egyéb személyszállítás, vagy szolgáltatással kapcsolatos kérdések felmerülése esetén küldjön email-t vagy töltse ki Üzenetküldési űrlapunkat, melyet a Kapcsolat menüpont alatt talál. Foglalása kizárólag a visszaigazolásunk megküldésével válik teljessé, e-nélkül foglalási szándéknak minősül. A foglalásokat szabad kapacitás függvényében tudjuk visszaigazolni a szándék beérkezésétól számítva legfeljebb 2 munkanapon belül, írásban, az Ön által megadott email címre megküldve.",
"Amennyiben foglalási szándékát az út előtti munkanap 12:00 után jelzi, szabad kapacitás függvényében feláras szolgáltatás keretében tudjuk vállalni, melynek díja plusz 2.500 Ft. Ez esetben, kérem szíveskedjen mindképp munkatársunkkal is egyeztetni ügyeleti telefonszámunkon: +36305543838", "Amennyiben foglalási szándékát az út előtti munkanap 12:00 után jelzi, szabad kapacitás függvényében feláras szolgáltatás keretében tudjuk vállalni, melynek díja plusz 2.500 Ft. Ez esetben, kérem szíveskedjen mindképp munkatársunkkal is egyeztetni ügyeleti telefonszámunkon: +36305543838",
"Amennyiben szolgáltatással kapcsolatos információkra van szüksége, kérjük hívja hotline vonalunkat: +36 30 5543838, vagy tegye fel kérdéseit emailben: info@skyflytravel.hu.", "Amennyiben szolgáltatással kapcsolatos információkra van szüksége, kérjük hívja hotline vonalunkat: +36 30 5543838, vagy tegye fel kérdéseit emailben: info@skyflytravel.hu. Munkaidőn kívül hotline vonalunkat szíveskedjen abban az esetben hívni csak, ha SOS kérdése, problémája merül fel az általunk nyújtott szolgáltatással kapcsolatban.",
"Az árlistában nem szereplő útvonalak, extra csomagmennyiség (a viteldíj magában foglalja: 1 db kézipoggyász és 1 normál méretű bőrönd max. 20 Kg / fő szállítását) vagy egyedi személyszállítás esetén kérje gyorsan és egyszerűen itt: ÜZENET KÜLDÉS", "Az árlistában nem szereplő útvonalak, extra csomagmennyiség (a viteldíj magában foglalja: 1 db kézipoggyász és 1 normál méretű bőrönd max. 20 Kg / fő szállítását) vagy egyedi személyszállítás esetén lépjen velünk kapcsolatban email-ben: info@skyflytravel.hu vagy töltse ki Üzenetküldési űrlapunkat a Kapcsolat menüpont alatt.",
"Helytelen, nem valós adatok megadása esetén (pl. hibás dátum, időpont, cím, telefonszám) nem tudjuk garantálni a szolgáltatás teljesítését." "Helytelen, nem valós adatok megadása esetén (pl. hibás dátum, időpont, cím, telefonszám) nem tudjuk garantálni a szolgáltatás teljesítését."
] ]
}, },
{ {
title: "3. Fizetés módja", title: "3. Fizetés módja",
content: [ content: [
"Sofőrünknél készpénzben, bankszámlánkra történő előreutalással, vagy egyedi megállapodás alapján utólagos átutalással." "Sofőrünknél készpénzben vagy bankkártyával (korlátozott mennyiségben, előre egyeztetés alapján), bankszámlánkra történő előreutalással, vagy egyedi megállapodás alapján utólagos átutalással."
] ]
}, },
{ {
@ -385,9 +380,9 @@
title: "5. Várakozás, gépkésés", title: "5. Várakozás, gépkésés",
content: [ content: [
"A reptéren való várakozási idő eltérő, attól függően, hogy a SkyFly Travel által nyújtott szolgáltatási csomagok közül melyiket kívánja igénybe venni.", "A reptéren való várakozási idő eltérő, attól függően, hogy a SkyFly Travel által nyújtott szolgáltatási csomagok közül melyiket kívánja igénybe venni.",
"Vienna International Airport (Schwechat): miután felvette a csomagokat és menetkész, kérem szíveskedjen felfáradni az emeletre (érkezési csarnok, McDonalds szemben lift vagy mozgó lépcső), majd forduljon jobbra és az utolsó kijáraton szíveskedj kifáradni, sofőrünk ott fogja várni a visszaigazolt utasfelvételi időpontban névvel ellátott táblával. Amennyiben táblást várás kérnek az érkezési csarnokban a parkolás díj 2.500 Ft, mely az utast terheli.", "Vienna International Airport (Schwechat): miután felvette a csomagokat és menetkész, kérem szíveskedjen felfáradni az emeletre (érkezési csarnok, Burger King-gel szemben lift vagy mozgó lépcső használatával), majd forduljon jobbra és az utolsó kijáraton szíveskedj kifáradni. Mikor már a találkozási pontra tart, szíveskedjen sofőrünkkel felvenni a kapcsolatot (elérhetőségét az út előtti munkanapon email-ben küldjük), hogy egyeztessenek a részletekről. Amennyiben táblást várás kérnek az érkezési csarnokban a parkolás díj 2.500 Ft, mely az utast terheli.",
"Budapest Liszt Ferenc Repülőtér: utasaink két lehetőség közül választhatnak- az utazás előtti utolsó munkanap elküldjük sofőrünk telefonszámát, melyen őt kell értesíteni, ha felvették csomagjaikat. Másik lehetőség, hogy névvel ellátott táblával várjuk az érkezési csarnokban, ez esetben a parkolási díj (3.000 Ft) az utast terheli.", "Budapest Liszt Ferenc Repülőtér: utasaink két lehetőség közül választhatnak- az utazás előtti utolsó munkanap elküldjük sofőrünk telefonszámát, melyen őt kell értesíteni, ha felvették csomagjaikat. Másik lehetőség, hogy névvel ellátott táblával várjuk az érkezési csarnokban, ez esetben a parkolási díj (3.000 Ft) az utast terheli.",
"Bratislava Repülőtér: sofőrünk az érkezési csarnokban névvel ellátott táblával várja az utasokat." "Bratislava Repülőtér: miután felvették a csomagokat, kérjük szíveskedjen sofőrünket hívni az út előtti munkanapon email-ben kapott elérhetőségen."
] ]
}, },
{ {
@ -432,34 +427,27 @@
}, },
categories: { categories: {
vip: "VIP Prémium", vip: "VIP Prémium",
business: "Üzleti kategória", personal: "Személygépjárművek",
family: "Családi és csoportos", minibus: "Kisbuszok"
minibus: "Kisbusz"
}, },
vehicles: { vehicles: {
vclass: { vclass: {
name: "Mercedes-Benz V-Class", name: "Mercedes E - class / Mercedes V-class / Mercedes Vito",
category: "VIP Prémium", category: "VIP Prémium",
description: "A luxus és tágas tér találkozása. Ideális üzleti delegációk vagy prémium kényelmet kereső családok számára.", description: "A luxus és tágas tér találkozása. Ideális üzleti delegációk vagy prémium kényelmet kereső családok számára.",
features: ["6-7 Utas", "4-6 Bőrönd", "Klíma", "Bőr belső", "Wifi"] features: []
},
eclass: {
name: "Mercedes-Benz E-Class",
category: "Üzleti kategória",
description: "Elegancia és megbízhatóság. Professzionális megjelenés üzleti utazásokhoz és repülőtéri transzferekhez.",
features: ["3 Utas", "2 Bőrönd", "Dokumentum hűtő", "Bőr belső"]
}, },
superb: { superb: {
name: "Skoda Superb Combi", name: "Skoda Octavia / Skoda SuperB / Skoda Enyq / Renault Grand Scenic",
category: "Üzleti kategória", category: "Személygépjárművek",
description: "Kategóriájának legnagyobb lábtere és csomagtere. A tökéletes választás hosszú távú utazásokhoz.", description: "Elegancia, megbízhatóság, kényelem legyen szó akár üzleti útról vagy családi transzferről.",
features: ["4 Utas", "3 Bőrönd", "Hatalmas csomagtér", "Extra lábtér"] features: []
}, },
transit: { transit: {
name: "Ford Transit / Renault Trafic", name: "Ford Transit / Ford Tourneo Custom / Renault Trafic / Opel Vivaro",
category: "Kisbusz", category: "Kisbuszok",
description: "Nagyobb csoportok számára a legpraktikusabb megoldás. Gazdaságos és kényelmes utazás 8 főig.", description: "Nagyobb csoportok számára a legpraktikusabb megoldás. Gazdaságos és kényelmes utazás 1-8 főig. Nagyobb létszám esetén több kisbusz rendelhető egy időben.",
features: ["8 Utas", "8 Bőrönd", "Extra csomagtér", "Dupla klíma"] features: []
} }
} }
}, },
@ -534,7 +522,7 @@
title: "Információk", title: "Információk",
infoLine: "Info vonal (iroda munkanapokon 08:00-16:00ig):", infoLine: "Info vonal (iroda munkanapokon 08:00-16:00ig):",
infoLineValues: ["+36 96 283676", "+36 30 5543838"], infoLineValues: ["+36 96 283676", "+36 30 5543838"],
dutyLine: "0-24 ügyelet megrendelt fuvarokra:", dutyLine: "0-24 ügyelet: Megrendelt fuvarokra, SOS kérdések, határidőn túli megrendelések esetére:",
dutyLineValue: "+36 30 5543838", dutyLineValue: "+36 30 5543838",
phone: "Hotline (0-24):", phone: "Hotline (0-24):",
phoneValue: "+36 30 554 3838", phoneValue: "+36 30 554 3838",
@ -664,7 +652,8 @@
brand: "SkyFly Travel", brand: "SkyFly Travel",
email: "info@skyflytravel.hu", email: "info@skyflytravel.hu",
phone: "+36 30 554 3838", phone: "+36 30 554 3838",
tiktokHandle: "@skyflytravel.transfer" tiktokHandle: "@skyflytravel.transfer",
moreImages: "More images"
}, },
meta: { meta: {
title: "SkyFly Travel - Fast, reliable, comfortable airport transfers!", title: "SkyFly Travel - Fast, reliable, comfortable airport transfers!",
@ -940,13 +929,7 @@
toCityDesc: "Factors when assigning passengers to groups: Scheduled time of arrival + 20-30 min for baggage claim." toCityDesc: "Factors when assigning passengers to groups: Scheduled time of arrival + 20-30 min for baggage claim."
}, },
packages: { packages: {
classic: {
title: "Economy CLASSIC",
unique: "Unique feature \"Economy CLASSIC\":",
desc: "Free seats are sold, if available, adapting to other passengers' schedule. min. 30 min max. 1.5 hours adjustment.",
toAirport: "Passengers are picked up maximum 1-1.5 hrs earlier than the ideal pick-up time.",
toCity: "Passengers leave the airport maximum 1-1.5 hrs later than scheduled time of arrival and baggage claim."
},
express: { express: {
title: "Economy EXPRESS", title: "Economy EXPRESS",
unique: "Unique feature \"Economy EXPRESS\":", unique: "Unique feature \"Economy EXPRESS\":",
@ -964,14 +947,14 @@
family: { family: {
title: "FAMILY", title: "FAMILY",
unique: "Unique feature \"FAMILY\":", unique: "Unique feature \"FAMILY\":",
desc: "No need to adapt to other passengers. Performed by a passenger car/SUV, max. 3 standard suitcases and 3 hand luggage.", desc: "No need to adapt to other passengers. Performed by a passenger car/SUV, min. 1 child (0-14 years), max. 3 standard suitcases and 3 hand luggage.",
toAirport: "Pick-up time is ideal for the passenger.", toAirport: "Pick-up time is ideal for the passenger.",
toCity: "No waiting time." toCity: "No waiting time."
}, },
bigFamily: { bigFamily: {
title: "BIG FAMILY", title: "BIG FAMILY",
unique: "Unique feature \"Big\" FAMILY:", unique: "Unique feature \"Big\" FAMILY:",
desc: "No need to adapt to other passengers. Performed by a minivan, max. 6 standard suitcases and 6 hand luggage.", desc: "No need to adapt to other passengers. Performed by a minivan, min. 1 child (0-14 years), max. 6 standard suitcases and 6 hand luggage.",
toAirport: "Pick-up time is ideal for the passenger.", toAirport: "Pick-up time is ideal for the passenger.",
toCity: "No waiting time." toCity: "No waiting time."
} }
@ -983,7 +966,7 @@
"We focus on the cleanness and the technical condition of our cars.", "We focus on the cleanness and the technical condition of our cars.",
"We continuously renew our fleets to be at your service with new cars.", "We continuously renew our fleets to be at your service with new cars.",
"SkyFly Travel has all passenger licenses required by current law.", "SkyFly Travel has all passenger licenses required by current law.",
"Our drivers are trained professionals with GKI license, PÁV-II certification and years of experience.",
"NON-STOP hotline for booked transfers (+36 30 5543838)", "NON-STOP hotline for booked transfers (+36 30 5543838)",
"Door-to-door transfer (from given address to given address)" "Door-to-door transfer (from given address to given address)"
] ]
@ -1021,15 +1004,17 @@
{ {
title: "2. Placing an order, inquiries, requesting a quote", title: "2. Placing an order, inquiries, requesting a quote",
content: [ content: [
"You can place an order by filling in the online order form, by sending an email to info@skyflytravel.hu or by sending a text message to +36 30 5543838. Deadline for placing an order: 12:00 noon the last working day before departure. Orders placed after the deadline are dealt with on an individual basis.", "You can submit your booking request / quote request by filling out the form under the Online Booking / Quote menu on our website. For other passenger transport or service-related questions, please send an email or fill out our Message Form, which can be found under the Contact menu. Your booking is only finalized upon receipt of our written confirmation; without it, it is considered a booking request. Bookings are confirmed pending free capacity within a maximum of 2 business days from receipt, sent in writing to your provided email address.",
"For information about our service, please call our hotline or send us an email with your questions. For destinations not listed in our price list and for extra luggage (the fare includes: 1 piece of hand luggage and 1 large suitcase, approx. 20 kg per person) or for unique passenger transport solutions, please ask for a quote via email.", "If you submit your booking request after 12:00 on the business day preceding your travel, we can only provide the service under a surcharge (HUF 2,500), subject to availability. In this case, please make sure to coordinate with our staff via our duty phone line: +36305543838.",
"In case the data provided are incorrect (e.g. incorrect date, time, address or phone number), we cannot guarantee the performance of the service." "If you need information regarding our service, please call our hotline at +36 30 5543838 or send your questions by email to info@skyflytravel.hu. Outside of business hours, please only call our hotline if you have an SOS question or problem related to the service we provide.",
"For routes not listed in the price list, extra luggage capacity (the fare includes 1 hand luggage and 1 standard suitcase max. 20 kg per person), or unique passenger transport solutions, please contact us via email at info@skyflytravel.hu or fill out our Message Form under the Contact menu.",
"In case the data provided are incorrect (e.g., incorrect date, time, address, or phone number), we cannot guarantee the performance of the service."
] ]
}, },
{ {
title: "3. Payment methods", title: "3. Payment methods",
content: [ content: [
"Payment in cash to the driver, prepayment by wire transfer, or post payment by wire transfer according to specific agreement." "Payment in cash or by bank card (in limited availability, by prior arrangement) to the driver, prepayment by wire transfer, or post-payment by wire transfer according to specific agreement."
] ]
}, },
{ {
@ -1042,10 +1027,10 @@
{ {
title: "5. Waiting time, delayed flights", title: "5. Waiting time, delayed flights",
content: [ content: [
"Time spent at the airport varies depending which SkyFly Travel package you intend to use.", "Waiting time at the airport varies depending on which SkyFly Travel service package you intend to use.",
"Vienna International Airport (Schwechat): meeting point at the airport: from the arrival hall please go upstairs with the moving steps (you can find opposite of McDonalds), after that turn right and go out at the last exit of Terminal 3. If you want that we wait for you with a sign in the arriving hall, the additional charge is 2.500 HUF.", "Vienna International Airport (Schwechat): after collecting your luggage and being ready to depart, please go upstairs (using the elevator or escalator opposite Burger King in the arrivals hall), then turn right and exit through the last door. When you are heading to the meeting point, please contact our driver (contact details will be sent via email on the business day preceding your travel) to coordinate the details. If you request a pickup with a name sign in the arrivals hall, a parking fee of HUF 2,500 applies, which is borne by the passenger.",
"Budapest Liszt Ferenc Airport: there are two options - we either send passengers the drivers phone number the last working day before the flight, so that they can get in contact once their baggage is claimed. Passengers can also choose to be met at arrivals, by our driver holding a name plate. In the latter case passengers have to pay the additional parking fee (HUF 3.000 HUF).", "Budapest Liszt Ferenc Airport: our passengers can choose from two options - we send our driver's phone number on the last business day before travel, whom you should notify once you have collected your luggage. Alternatively, we can wait for you in the arrivals hall with a name sign, in which case a parking fee (HUF 3,000) is borne by the passenger.",
"Bratislava Airport: passengers are met by our driver at arrivals, holding a name plate." "Bratislava Airport: after collecting your luggage, please call our driver at the contact number provided in the email received on the business day preceding your travel."
] ]
}, },
{ {
@ -1089,34 +1074,27 @@
}, },
categories: { categories: {
vip: "VIP Premium", vip: "VIP Premium",
business: "Business Class", personal: "Passenger Cars",
family: "Family & Group", minibus: "Minibuses"
minibus: "Minibus"
}, },
vehicles: { vehicles: {
vclass: { vclass: {
name: "Mercedes-Benz V-Class", name: "Mercedes E - class / Mercedes V-class / Mercedes Vito",
category: "VIP Premium", category: "VIP Premium",
description: "Where luxury meets spaciousness. Ideal for business delegations or families seeking premium comfort.", description: "Where luxury meets spaciousness. Ideal for business delegations or families seeking premium comfort.",
features: ["6-7 Passengers", "4-6 Suitcases", "Climate control", "Leather interior", "Wifi"] features: []
},
eclass: {
name: "Mercedes-Benz E-Class",
category: "Business Class",
description: "Elegance and reliability. Professional appearance for business trips and airport transfers.",
features: ["3 Passengers", "2 Suitcases", "Document cooler", "Leather interior"]
}, },
superb: { superb: {
name: "Skoda Superb Combi", name: "Skoda Octavia - Skoda SuperB - Skoda Enyq - Renault Grand Scenic",
category: "Business Class", category: "Passenger Cars",
description: "Largest legroom and trunk in its category. The perfect choice for long-distance travel.", description: "Elegance, reliability, comfort whether it's for business trips or family transfers.",
features: ["4 Passengers", "3 Suitcases", "Huge trunk", "Extra legroom"] features: []
}, },
transit: { transit: {
name: "Ford Transit / Renault Trafic", name: "Ford Transit / Ford Tourneo Custom / Renault Trafic / Opel Vivaro",
category: "Minibus", category: "Minibuses",
description: "The most practical solution for larger groups. Economical and comfortable travel for up to 8 people.", description: "The most practical solution for larger groups. Economical and comfortable travel for 1-8 people. Multiple minibuses can be ordered at once for larger groups.",
features: ["8 Passengers", "8 Suitcases", "Extra luggage space", "Dual climate control"] features: []
} }
} }
}, },
@ -1186,7 +1164,7 @@
title: "Information", title: "Information",
infoLine: "Info line (office hours Mon-Fri 08:00-16:00):", infoLine: "Info line (office hours Mon-Fri 08:00-16:00):",
infoLineValues: ["+36 96 283676", "+36 30 5543838"], infoLineValues: ["+36 96 283676", "+36 30 5543838"],
dutyLine: "24/7 duty for booked transfers:", dutyLine: "24/7 Duty: For booked transfers, SOS questions, or orders after the deadline:",
dutyLineValue: "+36 30 5543838", dutyLineValue: "+36 30 5543838",
phone: "Hotline (0-24):", phone: "Hotline (0-24):",
phoneValue: "+36 30 554 3838", phoneValue: "+36 30 554 3838",

View File

@ -3,9 +3,10 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "npm run generate:gallery && next dev",
"generate:gallery": "node scripts/generate-gallery.mjs",
"optimize:images": "node scripts/optimize-images.mjs", "optimize:images": "node scripts/optimize-images.mjs",
"build": "npm run optimize:images && next build", "build": "npm run generate:gallery && npm run optimize:images && next build",
"start": "next start", "start": "next start",
"lint": "eslint" "lint": "eslint"
}, },

22
public/gallery.json Normal file
View File

@ -0,0 +1,22 @@
{
"vip": [
"/images/gallery/vip/Flotta_VIP_kezdő.png",
"/images/gallery/vip/Mercedes Vito_belső.jpeg",
"/images/gallery/vip/mercedes E-class.JPG",
"/images/gallery/vip/vito.jpg"
],
"personal": [
"/images/gallery/personal/3 skoda reptér.png",
"/images/gallery/personal/Enyaq reptér.png",
"/images/gallery/personal/Skoda SuperB-inside.jpeg",
"/images/gallery/personal/Skoda SuperB.jpeg",
"/images/gallery/personal/skoda_enyaq 2.jpg"
],
"minibus": [
"/images/gallery/minibus/Ford Tourneo belső.JPG",
"/images/gallery/minibus/Ford Tourneo.JPG",
"/images/gallery/minibus/Ford Transit.jpg",
"/images/gallery/minibus/Tourneo háttér hegy.png",
"/images/gallery/minibus/új Trafic_2.JPG"
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 KiB

View File

@ -0,0 +1,55 @@
import fs from "node:fs/promises";
import path from "node:path";
const ROOT = process.cwd();
const GALLERY_ROOT = path.join(ROOT, "public", "images", "gallery");
const OUTPUT_FILE = path.join(ROOT, "public", "gallery.json");
async function exists(p) {
try {
await fs.access(p);
return true;
} catch {
return false;
}
}
async function main() {
try {
if (!(await exists(GALLERY_ROOT))) {
console.log("Gallery root not found, creating empty gallery.json");
await fs.writeFile(OUTPUT_FILE, JSON.stringify({ vip: [], personal: [], minibus: [] }, null, 2));
return;
}
const categories = ["vip", "personal", "minibus"];
const result = {};
for (const category of categories) {
const categoryPath = path.join(GALLERY_ROOT, category);
if (await exists(categoryPath)) {
const files = await fs.readdir(categoryPath);
const images = files
.filter((file) => {
const ext = path.extname(file).toLowerCase();
return [".jpg", ".jpeg", ".png", ".webp", ".gif", ".svg"].includes(ext);
})
.sort()
.map((img) => `/images/gallery/${category}/${img}`);
result[category] = images;
} else {
result[category] = [];
}
}
await fs.writeFile(OUTPUT_FILE, JSON.stringify(result, null, 2));
console.log(`Gallery metadata generated at ${OUTPUT_FILE}`);
console.log(`Summary: ${Object.keys(result).map(c => `${c}: ${result[c].length}`).join(", ")}`);
} catch (err) {
console.error("Error generating gallery metadata:", err);
process.exit(1);
}
}
main();