first commit

This commit is contained in:
2026-06-16 13:02:08 +03:00
commit 76af0683fd
51 changed files with 12923 additions and 0 deletions
+9
View File
@@ -0,0 +1,9 @@
.next
node_modules
.env
.env.*.local
.git
.vscode
docs
README.md
AGENTS.md
+41
View File
@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
+42
View File
@@ -0,0 +1,42 @@
# AGENTS.md
## Stack
- Framework: Next.js 16, App Router, TypeScript strict
- Styling: Tailwind CSS v4
- UI: shadcn/ui (new-york style, OKLCH)
- Animation: Framer Motion
- Icons: Lucide React
- i18n: next-intl
- ORM: Prisma + PostgreSQL
- Auth: NextAuth.js v5
- Media: Cloudinary
- Deploy: Coolify (Docker, standalone output)
## Sabit Tercihler
- Mock data: USE_MOCK=true (demo aşaması)
- proxy.ts kullan — middleware.ts deprecated (Next.js 15.3+)
- İletişim formu sadece /iletisim sayfasında — ana sayfada olmaz
- Footer'da "Created by ayris.tech" linki zorunlu
- Dockerfile'da dummy DATABASE_URL (prisma generate için)
## Altyapı
- Gitea: https://git.ayris.tech (kullanıcı: ayrisdev)
- Coolify: https://client2.ayris.tech
- Cloudflare zone: ayris.tech
- Server IP: 188.245.175.169
## docs/ Klasörü
- docs/prd.md → ana içerik kaynağı
- docs/*.html → varsa mevcut site içeriği
- docs/*.md → ek belgeler
## Aktif Skill'ler
- nextjs-seo → sitemap, metadata, robots.txt
- next-best-practices → kod kalitesi
- nextjs-app-router-patterns → Server Actions, Suspense
- demo-site → komple site üretimi
- design-demo → görsel kalite
- coolify-deploy → deploy pipeline
## Proje Özel Notlar
<!-- Buraya proje bazlı notlar ekle -->
+1
View File
@@ -0,0 +1 @@
@AGENTS.md
+34
View File
@@ -0,0 +1,34 @@
FROM node:22-alpine AS base
FROM base AS deps
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY package.json package-lock.json* ./
RUN npm ci --legacy-peer-deps
FROM base AS builder
WORKDIR /app
COPY --from=deps /app/node_modules ./node_modules
COPY . .
ENV NEXT_TELEMETRY_DISABLED=1
# Prisma generate için dummy URL — build sırasında gerçek DB gerekmez
ARG DATABASE_URL=postgresql://dummy:dummy@localhost:5432/dummy
ENV DATABASE_URL=$DATABASE_URL
RUN npx prisma generate
RUN npm run build
FROM base AS runner
WORKDIR /app
ENV NODE_ENV=production
ENV NEXT_TELEMETRY_DISABLED=1
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
RUN mkdir .next && chown nextjs:nodejs .next
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjs
EXPOSE 3000
ENV PORT=3000
ENV HOSTNAME="0.0.0.0"
CMD ["node", "server.js"]
+36
View File
@@ -0,0 +1,36 @@
This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app).
## Getting Started
First, run the development server:
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome!
## Deploy on Vercel
The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.
Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details.
+97
View File
@@ -0,0 +1,97 @@
'use client'
import { signOut } from 'next-auth/react'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { LayoutDashboard, Users, Settings, LogOut, Menu, X } from 'lucide-react'
import { useState } from 'react'
export default function AdminLayout({ children }: { children: React.ReactNode }) {
const pathname = usePathname()
const [sidebarOpen, setSidebarOpen] = useState(false)
const navigation = [
{ name: 'Dashboard', href: '/admin', icon: LayoutDashboard },
{ name: 'Kullanıcılar', href: '/admin/users', icon: Users },
{ name: 'Ayarlar', href: '/admin/settings', icon: Settings },
]
return (
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 flex">
{/* Mobile sidebar backdrop */}
{sidebarOpen && (
<div
className="fixed inset-0 z-40 bg-gray-900/80 lg:hidden"
onClick={() => setSidebarOpen(false)}
/>
)}
{/* Sidebar */}
<div className={`
fixed inset-y-0 left-0 z-50 w-64 bg-white dark:bg-gray-950 border-r border-gray-200 dark:border-gray-800
transform transition-transform duration-200 ease-in-out lg:translate-x-0 lg:static lg:inset-0
${sidebarOpen ? 'translate-x-0' : '-translate-x-full'}
`}>
<div className="h-full flex flex-col">
<div className="h-16 flex items-center px-6 border-b border-gray-200 dark:border-gray-800">
<h1 className="text-lg font-bold text-gray-900 dark:text-white">Admin Paneli</h1>
<button
className="ml-auto lg:hidden text-gray-500"
onClick={() => setSidebarOpen(false)}
>
<X className="h-5 w-5" />
</button>
</div>
<nav className="flex-1 px-4 py-6 space-y-1 overflow-y-auto">
{navigation.map((item) => {
const isActive = pathname.endsWith(item.href)
return (
<Link
key={item.name}
href={item.href}
className={`
flex items-center px-3 py-2.5 text-sm font-medium rounded-md transition-colors
${isActive
? 'bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-white'
: 'text-gray-600 dark:text-gray-400 hover:bg-gray-50 dark:hover:bg-gray-800 hover:text-gray-900 dark:hover:text-white'}
`}
>
<item.icon className={`mr-3 flex-shrink-0 h-5 w-5 ${isActive ? 'text-gray-900 dark:text-white' : 'text-gray-400'}`} />
{item.name}
</Link>
)
})}
</nav>
<div className="p-4 border-t border-gray-200 dark:border-gray-800">
<button
onClick={() => signOut({ callbackUrl: '/' })}
className="flex w-full items-center px-3 py-2.5 text-sm font-medium text-red-600 dark:text-red-400 rounded-md hover:bg-red-50 dark:hover:bg-red-950/30 transition-colors"
>
<LogOut className="mr-3 h-5 w-5" />
Çıkış Yap
</button>
</div>
</div>
</div>
{/* Main content */}
<div className="flex-1 flex flex-col min-w-0 overflow-hidden">
<header className="h-16 flex items-center lg:hidden bg-white dark:bg-gray-950 border-b border-gray-200 dark:border-gray-800 px-4">
<button
onClick={() => setSidebarOpen(true)}
className="text-gray-500 hover:text-gray-900 dark:hover:text-white focus:outline-none"
>
<Menu className="h-6 w-6" />
</button>
<span className="ml-4 text-lg font-bold text-gray-900 dark:text-white">Admin Paneli</span>
</header>
<main className="flex-1 overflow-y-auto p-4 sm:p-6 lg:p-8">
{children}
</main>
</div>
</div>
)
}
+47
View File
@@ -0,0 +1,47 @@
import { auth } from '@/lib/auth'
export default async function AdminDashboardPage() {
const session = await auth()
return (
<div className="space-y-6">
<div>
<h2 className="text-2xl font-bold tracking-tight text-gray-900 dark:text-white">Dashboard</h2>
<p className="text-gray-500 dark:text-gray-400 mt-2">
Hoş geldiniz, {session?.user?.name || session?.user?.email}. İşte projenizin genel görünümü.
</p>
</div>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
{/* Placeholder Stat Cards */}
{[
{ name: 'Toplam Kullanıcı', stat: '1,245' },
{ name: 'Aktif Oturumlar', stat: '42' },
{ name: 'Yeni Kayıtlar', stat: '8' },
{ name: 'Sistem Durumu', stat: 'Online' },
].map((item) => (
<div
key={item.name}
className="overflow-hidden rounded-lg bg-white dark:bg-gray-950 border border-gray-200 dark:border-gray-800 px-4 py-5 shadow-sm sm:p-6"
>
<dt className="truncate text-sm font-medium text-gray-500 dark:text-gray-400">{item.name}</dt>
<dd className="mt-1 text-3xl font-semibold tracking-tight text-gray-900 dark:text-white">
{item.stat}
</dd>
</div>
))}
</div>
<div className="rounded-lg bg-white dark:bg-gray-950 border border-gray-200 dark:border-gray-800 shadow-sm">
<div className="p-6">
<h3 className="text-lg font-medium text-gray-900 dark:text-white">Son Aktiviteler</h3>
<div className="mt-4 border-t border-gray-100 dark:border-gray-800">
<div className="py-4 text-sm text-gray-500">
Henüz aktivite kaydı bulunmuyor.
</div>
</div>
</div>
</div>
</div>
)
}
+24
View File
@@ -0,0 +1,24 @@
import { setRequestLocale } from 'next-intl/server';
import { aktiviteler } from '@/lib/data';
import { TourCard } from '@/components/ui/TourCard';
export default async function ActivitiesPage({ params }: { params: Promise<{ locale: string }> }) {
const { locale } = await params;
setRequestLocale(locale);
return (
<div className="py-20 bg-sandwhite-100 flex-1">
<div className="container mx-auto px-4">
<h1 className="text-4xl md:text-5xl font-bold text-deepblue-900 mb-4 text-center">Aktiviteler</h1>
<p className="text-center text-gray-600 max-w-2xl mx-auto mb-12">
Tatilinize heyecan katacak, birbirinden eğlenceli aktiviteler.
</p>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
{aktiviteler.map(activity => (
<TourCard key={activity.id} item={activity} basePath="/aktiviteler" />
))}
</div>
</div>
</div>
);
}
+33
View File
@@ -0,0 +1,33 @@
import { setRequestLocale } from 'next-intl/server';
export default async function AboutPage({ params }: { params: Promise<{ locale: string }> }) {
const { locale } = await params;
setRequestLocale(locale);
return (
<div className="py-20 bg-sandwhite-100 flex-1">
<div className="container mx-auto px-4 max-w-4xl">
<h1 className="text-4xl md:text-5xl font-bold text-deepblue-900 mb-8 text-center">Hakkımızda</h1>
<div className="bg-white p-8 md:p-12 rounded-3xl shadow-sm border border-gray-100 text-gray-700 leading-relaxed space-y-6">
<p className="text-lg">
<strong>ÖLÜDENİZ TOUR TRAVELS</strong> - Ölüdeniz Tur Rezervasyon
</p>
<p>
Mavi Yolculuk - Günübirlik Turlar - Aktiviteler - Özel Turlar - Araç Kiralama - Villa Kiralama
</p>
<p>
Uzman kadroya sahip olan şirketimiz Deniz Turu, Kara Turu ve Aktiviteler ile Siz değerli misafirlerimize Hizmet Vermektedir.
Firmamız, profesyonel uzman kadrosu ile, konforlu, modern ve üstün kalitede hizmet sunmaktadır.
Firmamızın, değişmeyen prensipleri daima kaliteli hizmet, zamanında ulaşım ve uygun fiyat olmuştur.
</p>
<p>
Kaliteye verdiği önem ve zoru başarma azmi sayesinde kısa sürede Turizm sektöründe tanınmış ve bu ilkelerden asla vazgeçmemiştir.
</p>
<p>
Turizm Sektöründe başarı ve sürekliliğin teminatı hizmette dürüstlük ve kalitedir prensibiyle çalışan Ölüdeniz Tour Travel, gösterdiğiniz yakın ilgi ve desteğinizden ötürü teşekkür ederek sizlere bugün ve gelecekte hizmet vermeye devam edecektir.
</p>
</div>
</div>
</div>
);
}
+83
View File
@@ -0,0 +1,83 @@
import { setRequestLocale } from 'next-intl/server';
import { siteInfo } from '@/lib/data';
import { MapPin, Phone, Mail } from 'lucide-react';
export default async function ContactPage({ params }: { params: Promise<{ locale: string }> }) {
const { locale } = await params;
setRequestLocale(locale);
return (
<div className="py-20 bg-sandwhite-100 flex-1">
<div className="container mx-auto px-4 max-w-5xl">
<h1 className="text-4xl md:text-5xl font-bold text-deepblue-900 mb-4 text-center">İletişim</h1>
<p className="text-center text-gray-600 max-w-2xl mx-auto mb-12">
Rezervasyon ve sorularınız için bize ulaşın.
</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-12 bg-white p-8 rounded-3xl shadow-sm border border-gray-100">
<div>
<h2 className="text-2xl font-bold text-deepblue-900 mb-6">İletişim Bilgileri</h2>
<div className="space-y-6">
<div className="flex items-start gap-4">
<div className="w-12 h-12 bg-turquoise-500/10 rounded-full flex items-center justify-center shrink-0">
<MapPin className="w-6 h-6 text-turquoise-500" />
</div>
<div>
<h3 className="font-semibold text-lg text-deepblue-900">Adres</h3>
<p className="text-gray-600 mt-1">{siteInfo.address}</p>
</div>
</div>
<div className="flex items-start gap-4">
<div className="w-12 h-12 bg-turquoise-500/10 rounded-full flex items-center justify-center shrink-0">
<Phone className="w-6 h-6 text-turquoise-500" />
</div>
<div>
<h3 className="font-semibold text-lg text-deepblue-900">Telefon</h3>
<a href={siteInfo.phoneLink} className="text-gray-600 mt-1 hover:text-turquoise-500 block">{siteInfo.phone}</a>
</div>
</div>
<div className="flex items-start gap-4">
<div className="w-12 h-12 bg-turquoise-500/10 rounded-full flex items-center justify-center shrink-0">
<Mail className="w-6 h-6 text-turquoise-500" />
</div>
<div>
<h3 className="font-semibold text-lg text-deepblue-900">E-Posta</h3>
<a href={`mailto:${siteInfo.email}`} className="text-gray-600 mt-1 hover:text-turquoise-500 block">{siteInfo.email}</a>
</div>
</div>
</div>
<div className="mt-8">
<a href={siteInfo.whatsappLink} target="_blank" rel="noreferrer" className="inline-flex items-center justify-center w-full py-4 bg-[#25D366] hover:bg-[#20bd5a] text-white rounded-xl font-bold text-lg transition-colors">
WhatsApp'tan Bize Yazın
</a>
</div>
</div>
<div>
<h2 className="text-2xl font-bold text-deepblue-900 mb-6">Bize Mesaj Gönderin</h2>
<form className="space-y-4">
<div>
<label htmlFor="name" className="block text-sm font-medium text-gray-700 mb-1">Adınız Soyadınız</label>
<input type="text" id="name" className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-turquoise-500 focus:ring-2 focus:ring-turquoise-500/20 outline-none transition-all" placeholder="Adınız Soyadınız" />
</div>
<div>
<label htmlFor="email" className="block text-sm font-medium text-gray-700 mb-1">E-Posta Adresiniz</label>
<input type="email" id="email" className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-turquoise-500 focus:ring-2 focus:ring-turquoise-500/20 outline-none transition-all" placeholder="ornek@email.com" />
</div>
<div>
<label htmlFor="message" className="block text-sm font-medium text-gray-700 mb-1">Mesajınız</label>
<textarea id="message" rows={4} className="w-full px-4 py-3 rounded-xl border border-gray-200 focus:border-turquoise-500 focus:ring-2 focus:ring-turquoise-500/20 outline-none transition-all resize-none" placeholder="Mesajınızı buraya yazın..."></textarea>
</div>
<button type="button" className="w-full py-4 bg-deepblue-900 hover:bg-deepblue-900/90 text-white rounded-xl font-bold text-lg transition-colors">
Gönder
</button>
</form>
</div>
</div>
</div>
</div>
);
}
+62
View File
@@ -0,0 +1,62 @@
import type { Metadata } from "next";
import { Outfit, Work_Sans } from "next/font/google";
import { NextIntlClientProvider } from 'next-intl';
import { getMessages, setRequestLocale } from 'next-intl/server';
import { notFound } from 'next/navigation';
import { routing } from '@/i18n/routing';
import { Navbar } from '@/components/layout/Navbar';
import { Footer } from '@/components/layout/Footer';
import "../globals.css";
const outfit = Outfit({
variable: "--font-heading",
subsets: ["latin"],
});
const workSans = Work_Sans({
variable: "--font-sans",
subsets: ["latin"],
});
export const metadata: Metadata = {
title: "Ölüdeniz Tour Travels",
description: "Mavi Yolculuk, Günübirlik Turlar ve Aktiviteler",
};
export function generateStaticParams() {
return routing.locales.map((locale) => ({locale}));
}
export default async function RootLayout({
children,
params
}: Readonly<{
children: React.ReactNode;
params: Promise<{ locale: string }>;
}>) {
const { locale } = await params;
if (!routing.locales.includes(locale as any)) {
notFound();
}
setRequestLocale(locale);
const messages = await getMessages();
return (
<html
lang={locale}
className={`${outfit.variable} ${workSans.variable} h-full antialiased`}
>
<body className="min-h-full flex flex-col" suppressHydrationWarning>
<NextIntlClientProvider messages={messages}>
<Navbar />
<main className="flex-1 flex flex-col">
{children}
</main>
<Footer />
</NextIntlClientProvider>
</body>
</html>
);
}
+94
View File
@@ -0,0 +1,94 @@
'use client'
import { useState } from 'react'
import { signIn } from 'next-auth/react'
import { useRouter } from 'next/navigation'
export default function LoginPage() {
const router = useRouter()
const [email, setEmail] = useState('')
const [password, setPassword] = useState('')
const [error, setError] = useState('')
const [loading, setLoading] = useState(false)
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setLoading(true)
setError('')
const result = await signIn('credentials', {
redirect: false,
email,
password,
})
if (result?.error) {
setError('Geçersiz e-posta veya şifre')
setLoading(false)
} else {
router.push('/admin')
router.refresh()
}
}
return (
<div className="min-h-screen flex items-center justify-center bg-gray-50 dark:bg-gray-900 px-4">
<div className="w-full max-w-md bg-white dark:bg-gray-800 rounded-xl shadow-lg border border-gray-100 dark:border-gray-800 overflow-hidden">
<div className="p-8">
<div className="text-center mb-8">
<h1 className="text-2xl font-bold text-gray-900 dark:text-white">Admin Girişi</h1>
<p className="text-sm text-gray-500 mt-2">Yönetim paneline erişmek için giriş yapın</p>
</div>
{error && (
<div className="bg-red-50 text-red-600 p-3 rounded-md text-sm mb-6 border border-red-100">
{error}
</div>
)}
<form onSubmit={handleSubmit} className="space-y-5">
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1" htmlFor="email">
E-posta
</label>
<input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
className="w-full px-4 py-2 border border-gray-300 dark:border-gray-700 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-900 text-gray-900 dark:text-white transition-colors"
placeholder="admin@ayris.tech"
/>
</div>
<div>
<label className="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1" htmlFor="password">
Şifre
</label>
<input
id="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
className="w-full px-4 py-2 border border-gray-300 dark:border-gray-700 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500 bg-white dark:bg-gray-900 text-gray-900 dark:text-white transition-colors"
placeholder="••••••••"
/>
</div>
<button
type="submit"
disabled={loading}
className="w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-2.5 px-4 rounded-md transition-colors disabled:opacity-70 disabled:cursor-not-allowed"
>
{loading ? 'Giriş yapılıyor...' : 'Giriş Yap'}
</button>
</form>
<div className="mt-6 text-center text-xs text-gray-400">
Demo credentials: admin@ayris.tech / admin
</div>
</div>
</div>
</div>
)
}
+86
View File
@@ -0,0 +1,86 @@
import { setRequestLocale } from 'next-intl/server';
import { HeroSlider } from '@/components/home/HeroSlider';
import { turlar, aktiviteler } from '@/lib/data';
import { TourCard } from '@/components/ui/TourCard';
import { Link } from '@/i18n/routing';
import { ArrowRight, Sparkles } from 'lucide-react';
export default async function HomePage({ params }: { params: Promise<{ locale: string }> }) {
const { locale } = await params;
setRequestLocale(locale);
return (
<div className="flex flex-col">
<HeroSlider />
{/* Featured Tours Section */}
<section className="py-24 bg-sandwhite-100 relative overflow-hidden">
{/* Background decorative elements */}
<div className="absolute top-0 left-0 w-full h-full overflow-hidden pointer-events-none">
<div className="absolute -top-[20%] -right-[10%] w-[50%] h-[50%] rounded-full bg-turquoise-500/5 blur-[120px]" />
<div className="absolute top-[60%] -left-[10%] w-[40%] h-[40%] rounded-full bg-vibrantorange-500/5 blur-[100px]" />
</div>
<div className="container mx-auto px-4 relative z-10">
<div className="text-center mb-16 flex flex-col items-center">
<div className="inline-flex items-center justify-center p-3 bg-white rounded-2xl shadow-sm mb-4">
<Sparkles className="w-6 h-6 text-turquoise-500" />
</div>
<h2 className="text-4xl md:text-5xl font-extrabold text-deepblue-900 mb-6 tracking-tight">Öne Çıkan Turlar</h2>
<p className="text-lg text-gray-600 max-w-2xl mx-auto">
Unutulmaz anılar biriktireceğiniz, özenle seçilmiş tur programlarımızı keşfedin ve hayalinizdeki tatili yaşayın.
</p>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-8">
{turlar.slice(0, 4).map((tour, index) => (
<TourCard key={tour.id} item={tour} basePath="/turlar" index={index} />
))}
</div>
<div className="text-center mt-16">
<Link href="/turlar" className="inline-flex items-center gap-2 px-8 py-4 bg-white border border-gray-200 text-deepblue-900 hover:text-turquoise-500 hover:border-turquoise-500 rounded-2xl font-bold transition-all shadow-sm hover:shadow-md group">
<span>Tüm Turları Görüntüle</span>
<ArrowRight className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
</Link>
</div>
</div>
</section>
{/* Featured Activities Section */}
<section className="py-24 bg-white relative">
<div className="container mx-auto px-4">
<div className="text-center mb-16">
<h2 className="text-4xl md:text-5xl font-extrabold text-deepblue-900 mb-6 tracking-tight">Popüler Aktiviteler</h2>
<p className="text-lg text-gray-600 max-w-2xl mx-auto">
Tatilinize heyecan katacak, birbirinden eğlenceli ve adrenalin dolu aktiviteler.
</p>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-8">
{aktiviteler.slice(0, 4).map((activity, index) => (
<TourCard key={activity.id} item={activity} basePath="/aktiviteler" index={index} />
))}
</div>
<div className="text-center mt-16">
<Link href="/aktiviteler" className="inline-flex items-center gap-2 px-8 py-4 bg-deepblue-900 text-white hover:bg-turquoise-500 rounded-2xl font-bold transition-colors shadow-md hover:shadow-xl group">
<span>Tüm Aktiviteleri Keşfet</span>
<ArrowRight className="w-5 h-5 group-hover:translate-x-1 transition-transform" />
</Link>
</div>
</div>
</section>
{/* Intro / About Summary */}
<section className="py-28 bg-deepblue-900 relative overflow-hidden">
<div className="absolute inset-0 bg-[url('https://images.unsplash.com/photo-1542837265-728b971a812d')] bg-cover bg-center opacity-10 mix-blend-overlay" />
<div className="container mx-auto px-4 max-w-4xl relative z-10 text-center">
<h2 className="text-4xl md:text-6xl font-extrabold text-white mb-8 tracking-tight">Sınırları Aşan Bir Deneyim</h2>
<p className="text-xl md:text-2xl text-gray-300 leading-relaxed mb-12 font-light">
Uzman kadroya sahip olan şirketimiz; deniz turları, kara turları ve birbirinden heyecanlı aktiviteler ile siz değerli misafirlerimize yıllardır unutulmaz bir tatil vaat ediyor.
</p>
<Link href="/hakkimizda" className="inline-flex items-center gap-2 px-10 py-5 bg-gradient-to-r from-vibrantorange-500 to-orange-400 hover:from-orange-400 hover:to-vibrantorange-500 text-white rounded-2xl font-bold text-lg transition-all shadow-[0_0_40px_rgba(249,115,22,0.4)] hover:shadow-[0_0_60px_rgba(249,115,22,0.6)] hover:-translate-y-1">
Bizimle Tanışın
</Link>
</div>
</section>
</div>
);
}
+24
View File
@@ -0,0 +1,24 @@
import { setRequestLocale } from 'next-intl/server';
import { tekneTurlari } from '@/lib/data';
import { TourCard } from '@/components/ui/TourCard';
export default async function BoatToursPage({ params }: { params: Promise<{ locale: string }> }) {
const { locale } = await params;
setRequestLocale(locale);
return (
<div className="py-20 bg-sandwhite-100 flex-1">
<div className="container mx-auto px-4">
<h1 className="text-4xl md:text-5xl font-bold text-deepblue-900 mb-4 text-center">Tekne Turları</h1>
<p className="text-center text-gray-600 max-w-2xl mx-auto mb-12">
Masmavi suların keyfini çıkaracağınız, eşsiz koyları keşfedeceğiniz tekne turları.
</p>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
{tekneTurlari.map(tour => (
<TourCard key={tour.id} item={tour} basePath="/tekne-turlari" />
))}
</div>
</div>
</div>
);
}
+24
View File
@@ -0,0 +1,24 @@
import { setRequestLocale } from 'next-intl/server';
import { turlar } from '@/lib/data';
import { TourCard } from '@/components/ui/TourCard';
export default async function ToursPage({ params }: { params: Promise<{ locale: string }> }) {
const { locale } = await params;
setRequestLocale(locale);
return (
<div className="py-20 bg-sandwhite-100 flex-1">
<div className="container mx-auto px-4">
<h1 className="text-4xl md:text-5xl font-bold text-deepblue-900 mb-4 text-center">Turlar</h1>
<p className="text-center text-gray-600 max-w-2xl mx-auto mb-12">
Sizin için özenle hazırladığımız tur seçeneklerimizi inceleyin ve unutulmaz bir tatil deneyimi yaşayın.
</p>
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-6">
{turlar.map(tour => (
<TourCard key={tour.id} item={tour} basePath="/turlar" />
))}
</div>
</div>
</div>
);
}
+3
View File
@@ -0,0 +1,3 @@
import { handlers } from "@/lib/auth"
export const { GET, POST } = handlers
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+134
View File
@@ -0,0 +1,134 @@
@import "tailwindcss";
@import "tw-animate-css";
@import "shadcn/tailwind.css";
@custom-variant dark (&:is(.dark *));
@theme inline {
--color-background: var(--background);
--color-foreground: var(--foreground);
--font-sans: var(--font-sans);
--font-mono: var(--font-mono);
--font-heading: var(--font-heading);
--color-sidebar-ring: var(--sidebar-ring);
--color-sidebar-border: var(--sidebar-border);
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
--color-sidebar-accent: var(--sidebar-accent);
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
--color-sidebar-primary: var(--sidebar-primary);
--color-sidebar-foreground: var(--sidebar-foreground);
--color-sidebar: var(--sidebar);
--color-chart-5: var(--chart-5);
--color-chart-4: var(--chart-4);
--color-chart-3: var(--chart-3);
--color-chart-2: var(--chart-2);
--color-chart-1: var(--chart-1);
--color-ring: var(--ring);
--color-input: var(--input);
--color-border: var(--border);
--color-destructive: var(--destructive);
--color-accent-foreground: var(--accent-foreground);
--color-accent: var(--accent);
--color-muted-foreground: var(--muted-foreground);
--color-muted: var(--muted);
--color-secondary-foreground: var(--secondary-foreground);
--color-secondary: var(--secondary);
--color-primary-foreground: var(--primary-foreground);
--color-primary: var(--primary);
--color-popover-foreground: var(--popover-foreground);
--color-popover: var(--popover);
--color-card-foreground: var(--card-foreground);
--color-card: var(--card);
--color-turquoise-500: oklch(0.72 0.14 210);
--color-deepblue-900: oklch(0.25 0.08 260);
--color-sandwhite-100: oklch(0.96 0.01 90);
--color-vibrantorange-500: oklch(0.65 0.2 40);
--radius-sm: calc(var(--radius) * 0.6);
--radius-md: calc(var(--radius) * 0.8);
--radius-lg: var(--radius);
--radius-xl: calc(var(--radius) * 1.4);
--radius-2xl: calc(var(--radius) * 1.8);
--radius-3xl: calc(var(--radius) * 2.2);
--radius-4xl: calc(var(--radius) * 2.6);
}
:root {
--background: oklch(1 0 0);
--foreground: oklch(0.145 0 0);
--card: oklch(1 0 0);
--card-foreground: oklch(0.145 0 0);
--popover: oklch(1 0 0);
--popover-foreground: oklch(0.145 0 0);
--primary: oklch(0.205 0 0);
--primary-foreground: oklch(0.985 0 0);
--secondary: oklch(0.97 0 0);
--secondary-foreground: oklch(0.205 0 0);
--muted: oklch(0.97 0 0);
--muted-foreground: oklch(0.556 0 0);
--accent: oklch(0.97 0 0);
--accent-foreground: oklch(0.205 0 0);
--destructive: oklch(0.577 0.245 27.325);
--border: oklch(0.922 0 0);
--input: oklch(0.922 0 0);
--ring: oklch(0.708 0 0);
--chart-1: oklch(0.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
--chart-4: oklch(0.371 0 0);
--chart-5: oklch(0.269 0 0);
--radius: 0.625rem;
--sidebar: oklch(0.985 0 0);
--sidebar-foreground: oklch(0.145 0 0);
--sidebar-primary: oklch(0.205 0 0);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.97 0 0);
--sidebar-accent-foreground: oklch(0.205 0 0);
--sidebar-border: oklch(0.922 0 0);
--sidebar-ring: oklch(0.708 0 0);
}
.dark {
--background: oklch(0.145 0 0);
--foreground: oklch(0.985 0 0);
--card: oklch(0.205 0 0);
--card-foreground: oklch(0.985 0 0);
--popover: oklch(0.205 0 0);
--popover-foreground: oklch(0.985 0 0);
--primary: oklch(0.922 0 0);
--primary-foreground: oklch(0.205 0 0);
--secondary: oklch(0.269 0 0);
--secondary-foreground: oklch(0.985 0 0);
--muted: oklch(0.269 0 0);
--muted-foreground: oklch(0.708 0 0);
--accent: oklch(0.269 0 0);
--accent-foreground: oklch(0.985 0 0);
--destructive: oklch(0.704 0.191 22.216);
--border: oklch(1 0 0 / 10%);
--input: oklch(1 0 0 / 15%);
--ring: oklch(0.556 0 0);
--chart-1: oklch(0.87 0 0);
--chart-2: oklch(0.556 0 0);
--chart-3: oklch(0.439 0 0);
--chart-4: oklch(0.371 0 0);
--chart-5: oklch(0.269 0 0);
--sidebar: oklch(0.205 0 0);
--sidebar-foreground: oklch(0.985 0 0);
--sidebar-primary: oklch(0.488 0.243 264.376);
--sidebar-primary-foreground: oklch(0.985 0 0);
--sidebar-accent: oklch(0.269 0 0);
--sidebar-accent-foreground: oklch(0.985 0 0);
--sidebar-border: oklch(1 0 0 / 10%);
--sidebar-ring: oklch(0.556 0 0);
}
@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground font-sans;
}
h1, h2, h3, h4, h5, h6 {
@apply font-heading tracking-tight;
}
}
+25
View File
@@ -0,0 +1,25 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "base-nova",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "",
"css": "app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"iconLibrary": "lucide",
"rtl": false,
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"menuColor": "default",
"menuAccent": "subtle",
"registries": {}
}
+96
View File
@@ -0,0 +1,96 @@
'use client';
import { useState, useEffect } from 'react';
import Image from 'next/image';
import { heroSlides } from '@/lib/data';
import { ChevronLeft, ChevronRight } from 'lucide-react';
import { Link } from '@/i18n/routing';
export function HeroSlider() {
const [current, setCurrent] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setCurrent((prev) => (prev === heroSlides.length - 1 ? 0 : prev + 1));
}, 5000);
return () => clearInterval(timer);
}, []);
const nextSlide = () => setCurrent(current === heroSlides.length - 1 ? 0 : current + 1);
const prevSlide = () => setCurrent(current === 0 ? heroSlides.length - 1 : current - 1);
return (
<div className="relative h-[80vh] min-h-[600px] w-full overflow-hidden group bg-deepblue-900">
{/* Decorative Aurora Gradients */}
<div className="absolute inset-0 overflow-hidden pointer-events-none z-10 opacity-60 mix-blend-screen">
<div className="absolute -top-[30%] -left-[10%] w-[60%] h-[60%] rounded-full bg-vibrantorange-500/30 blur-[120px] animate-pulse" style={{ animationDuration: '8s' }} />
<div className="absolute top-[20%] -right-[20%] w-[70%] h-[70%] rounded-full bg-turquoise-500/30 blur-[150px] animate-pulse" style={{ animationDuration: '12s' }} />
<div className="absolute -bottom-[40%] left-[20%] w-[80%] h-[80%] rounded-full bg-deepblue-900/40 blur-[100px] animate-pulse" style={{ animationDuration: '10s' }} />
</div>
{heroSlides.map((slide, index) => (
<div
key={index}
className={`absolute inset-0 transition-all duration-1000 ease-in-out ${
index === current ? 'opacity-100 z-10 scale-100' : 'opacity-0 z-0 scale-105'
}`}
>
<Image
src={slide}
alt={`Ölüdeniz Slider ${index + 1}`}
fill
className="object-cover mix-blend-overlay opacity-60"
priority={index === 0}
/>
{/* Overlay gradient for text readability and aurora blend */}
<div className="absolute inset-0 bg-gradient-to-t from-deepblue-900 via-deepblue-900/60 to-transparent" />
</div>
))}
{/* Content */}
<div className="absolute inset-0 z-20 flex flex-col items-center justify-center text-center px-4 pt-16">
<h1 className="text-5xl md:text-7xl lg:text-8xl font-extrabold text-white tracking-tight mb-6 animate-in fade-in slide-in-from-bottom-12 duration-1000 fill-mode-both drop-shadow-2xl">
ÖLÜDENİZ <span className="text-transparent bg-clip-text bg-gradient-to-r from-turquoise-500 to-vibrantorange-500">TOUR</span> TRAVELS
</h1>
<p className="text-xl md:text-3xl text-sandwhite-100 mb-12 max-w-4xl mx-auto font-medium animate-in fade-in slide-in-from-bottom-8 duration-1000 delay-300 fill-mode-both drop-shadow-lg">
Mavi Yolculuk, Günübirlik Turlar ve Unutulmaz Aktiviteler
</p>
<div className="flex flex-col sm:flex-row gap-6 animate-in fade-in slide-in-from-bottom-8 duration-1000 delay-700 fill-mode-both">
<Link href="/turlar" className="px-10 py-5 bg-gradient-to-r from-vibrantorange-500 to-orange-400 hover:from-orange-400 hover:to-vibrantorange-500 text-white rounded-2xl font-bold text-lg transition-all shadow-[0_0_30px_rgba(249,115,22,0.4)] hover:shadow-[0_0_50px_rgba(249,115,22,0.6)] hover:-translate-y-1">
Turları Keşfet
</Link>
<Link href="/aktiviteler" className="px-10 py-5 bg-white/10 hover:bg-white/20 backdrop-blur-md border border-white/20 text-white rounded-2xl font-bold text-lg transition-all shadow-lg hover:shadow-xl hover:-translate-y-1">
Aktiviteler
</Link>
</div>
</div>
{/* Controls */}
<button
onClick={prevSlide}
className="absolute left-4 top-1/2 -translate-y-1/2 z-30 w-12 h-12 bg-black/20 hover:bg-black/50 text-white rounded-full flex items-center justify-center opacity-0 group-hover:opacity-100 transition-all backdrop-blur-sm"
>
<ChevronLeft className="w-6 h-6" />
</button>
<button
onClick={nextSlide}
className="absolute right-4 top-1/2 -translate-y-1/2 z-30 w-12 h-12 bg-black/20 hover:bg-black/50 text-white rounded-full flex items-center justify-center opacity-0 group-hover:opacity-100 transition-all backdrop-blur-sm"
>
<ChevronRight className="w-6 h-6" />
</button>
{/* Indicators */}
<div className="absolute bottom-8 left-1/2 -translate-x-1/2 z-30 flex gap-2">
{heroSlides.map((_, index) => (
<button
key={index}
onClick={() => setCurrent(index)}
className={`w-3 h-3 rounded-full transition-all ${
index === current ? 'bg-white scale-125' : 'bg-white/50'
}`}
/>
))}
</div>
</div>
);
}
+54
View File
@@ -0,0 +1,54 @@
import { Link } from '@/i18n/routing';
import { siteInfo } from '@/lib/data';
import { Phone, Mail, MapPin } from 'lucide-react';
export function Footer() {
return (
<footer className="bg-deepblue-900 text-sandwhite-100 py-12">
<div className="container mx-auto px-4 grid grid-cols-1 md:grid-cols-3 gap-8">
<div>
<h3 className="font-bold text-xl mb-4 text-white">{siteInfo.name}</h3>
<p className="text-sm text-gray-300 max-w-sm">
{siteInfo.slogan}
</p>
</div>
<div>
<h4 className="font-semibold text-lg mb-4 text-white">Hızlı Linkler</h4>
<ul className="space-y-2 text-sm text-gray-300">
<li><Link href="/hakkimizda" className="hover:text-turquoise-500 transition-colors">Hakkımızda</Link></li>
<li><Link href="/turlar" className="hover:text-turquoise-500 transition-colors">Turlar</Link></li>
<li><Link href="/aktiviteler" className="hover:text-turquoise-500 transition-colors">Aktiviteler</Link></li>
<li><Link href="/tekne-turlari" className="hover:text-turquoise-500 transition-colors">Tekne Turları</Link></li>
<li><Link href="/iletisim" className="hover:text-turquoise-500 transition-colors">İletişim</Link></li>
</ul>
</div>
<div>
<h4 className="font-semibold text-lg mb-4 text-white">İletişim</h4>
<ul className="space-y-3 text-sm text-gray-300">
<li className="flex items-start gap-2">
<MapPin className="w-5 h-5 text-turquoise-500 shrink-0" />
<span>{siteInfo.address}</span>
</li>
<li className="flex items-center gap-2">
<Phone className="w-5 h-5 text-turquoise-500 shrink-0" />
<a href={siteInfo.phoneLink} className="hover:text-white transition-colors">{siteInfo.phone}</a>
</li>
<li className="flex items-center gap-2">
<Mail className="w-5 h-5 text-turquoise-500 shrink-0" />
<a href={`mailto:${siteInfo.email}`} className="hover:text-white transition-colors">{siteInfo.email}</a>
</li>
</ul>
</div>
</div>
<div className="container mx-auto px-4 mt-12 pt-8 border-t border-white/10 flex flex-col md:flex-row items-center justify-between text-sm text-gray-400">
<p>&copy; {new Date().getFullYear()} {siteInfo.name}. Tüm hakları saklıdır.</p>
<p className="mt-2 md:mt-0">
<a href="https://ayris.tech" target="_blank" rel="noreferrer" className="hover:text-white transition-colors">Created by ayris.tech</a>
</p>
</div>
</footer>
);
}
+62
View File
@@ -0,0 +1,62 @@
'use client';
import { Link } from '@/i18n/routing';
import { siteInfo } from '@/lib/data';
import { Phone, MapPin, Menu } from 'lucide-react';
import { useState } from 'react';
export function Navbar() {
const [isOpen, setIsOpen] = useState(false);
return (
<div className="fixed top-4 left-4 right-4 z-50">
<header className="container mx-auto bg-white/80 backdrop-blur-xl border border-white/20 shadow-lg rounded-2xl transition-all duration-300">
<div className="px-6 h-20 flex items-center justify-between">
<Link href="/" className="font-extrabold text-2xl text-deepblue-900 tracking-tighter flex items-center gap-2 hover:opacity-80 transition-opacity">
{siteInfo.name}
</Link>
{/* Desktop Nav */}
<nav className="hidden md:flex items-center gap-6">
<Link href="/" className="text-sm font-medium hover:text-turquoise-500 transition-colors">Ana Sayfa</Link>
<Link href="/hakkimizda" className="text-sm font-medium hover:text-turquoise-500 transition-colors">Hakkımızda</Link>
<Link href="/turlar" className="text-sm font-medium hover:text-turquoise-500 transition-colors">Turlar</Link>
<Link href="/aktiviteler" className="text-sm font-medium hover:text-turquoise-500 transition-colors">Aktiviteler</Link>
<Link href="/tekne-turlari" className="text-sm font-medium hover:text-turquoise-500 transition-colors">Tekne Turları</Link>
<Link href="/iletisim" className="text-sm font-medium hover:text-turquoise-500 transition-colors">İletişim</Link>
</nav>
<div className="hidden md:flex items-center gap-4">
<a href={siteInfo.phoneLink} className="flex items-center gap-2 text-sm font-semibold text-deepblue-900">
<Phone className="w-4 h-4 text-turquoise-500" />
{siteInfo.phone}
</a>
<a href={siteInfo.whatsappLink} target="_blank" rel="noreferrer" className="px-4 py-2 bg-vibrantorange-500 hover:bg-orange-600 text-white rounded-md text-sm font-medium transition-colors">
Rezervasyon
</a>
</div>
{/* Mobile Toggle */}
<button className="md:hidden p-2" onClick={() => setIsOpen(!isOpen)}>
<Menu className="w-6 h-6" />
</button>
</div>
{/* Mobile Nav */}
{isOpen && (
<div className="md:hidden border-t p-4 flex flex-col gap-4 bg-sandwhite-100">
<Link href="/" onClick={() => setIsOpen(false)} className="text-sm font-medium">Ana Sayfa</Link>
<Link href="/hakkimizda" onClick={() => setIsOpen(false)} className="text-sm font-medium">Hakkımızda</Link>
<Link href="/turlar" onClick={() => setIsOpen(false)} className="text-sm font-medium">Turlar</Link>
<Link href="/aktiviteler" onClick={() => setIsOpen(false)} className="text-sm font-medium">Aktiviteler</Link>
<Link href="/tekne-turlari" onClick={() => setIsOpen(false)} className="text-sm font-medium">Tekne Turları</Link>
<Link href="/iletisim" onClick={() => setIsOpen(false)} className="text-sm font-medium">İletişim</Link>
<a href={siteInfo.whatsappLink} className="mt-2 block text-center px-4 py-2 bg-vibrantorange-500 text-white rounded-md text-sm font-medium">
Rezervasyon
</a>
</div>
)}
</header>
</div>
);
}
+54
View File
@@ -0,0 +1,54 @@
'use client';
import Image from 'next/image';
import { Link } from '@/i18n/routing';
import { motion } from 'framer-motion';
import { ArrowRight } from 'lucide-react';
interface TourCardProps {
item: {
id: number;
slug: string;
title: string;
image: string;
price: string;
};
basePath: string; // e.g. "/turlar", "/aktiviteler"
index?: number;
}
export function TourCard({ item, basePath, index = 0 }: TourCardProps) {
return (
<motion.div
initial={{ opacity: 0, y: 20 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true, margin: "-50px" }}
transition={{ duration: 0.5, delay: index * 0.1 }}
className="bg-white/80 backdrop-blur-md rounded-3xl overflow-hidden shadow-[0_8px_30px_rgb(0,0,0,0.04)] hover:shadow-[0_20px_40px_rgb(0,0,0,0.08)] transition-all duration-500 border border-white/50 flex flex-col group"
>
<div className="relative aspect-[4/3] w-full overflow-hidden">
<Image
src={item.image}
alt={item.title}
fill
className="object-cover group-hover:scale-110 transition-transform duration-700 ease-out"
sizes="(max-width: 768px) 100vw, (max-width: 1200px) 50vw, 33vw"
/>
<div className="absolute inset-0 bg-gradient-to-t from-black/60 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
<div className="absolute top-4 right-4 bg-white/90 backdrop-blur-md px-4 py-1.5 rounded-full text-sm font-extrabold text-vibrantorange-500 shadow-lg transform group-hover:scale-105 transition-transform duration-300">
{item.price}
</div>
</div>
<div className="p-6 flex flex-col flex-1 relative bg-gradient-to-b from-white/50 to-white">
<h3 className="font-bold text-xl mb-3 text-deepblue-900 line-clamp-2 flex-1 group-hover:text-turquoise-500 transition-colors">{item.title}</h3>
<Link
href={`${basePath}/${item.slug}`}
className="mt-4 flex items-center justify-center gap-2 w-full py-3 bg-gradient-to-r from-turquoise-500 to-deepblue-900 hover:from-deepblue-900 hover:to-turquoise-500 text-white font-semibold rounded-2xl transition-all duration-500 shadow-md hover:shadow-xl transform hover:-translate-y-1"
>
<span>Hemen İncele</span>
<ArrowRight className="w-4 h-4 group-hover:translate-x-1 transition-transform" />
</Link>
</div>
</motion.div>
);
}
+58
View File
@@ -0,0 +1,58 @@
import { Button as ButtonPrimitive } from "@base-ui/react/button"
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
{
variants: {
variant: {
default: "bg-primary text-primary-foreground hover:bg-primary/80",
outline:
"border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
secondary:
"bg-secondary text-secondary-foreground hover:bg-[color-mix(in_oklch,var(--secondary),var(--foreground)_5%)] aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
ghost:
"hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50",
destructive:
"bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
link: "text-primary underline-offset-4 hover:underline",
},
size: {
default:
"h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
icon: "size-8",
"icon-xs":
"size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
"icon-sm":
"size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
"icon-lg": "size-9",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
function Button({
className,
variant = "default",
size = "default",
...props
}: ButtonPrimitive.Props & VariantProps<typeof buttonVariants>) {
return (
<ButtonPrimitive
data-slot="button"
className={cn(buttonVariants({ variant, size, className }))}
{...props}
/>
)
}
export { Button, buttonVariants }
+103
View File
@@ -0,0 +1,103 @@
import * as React from "react"
import { cn } from "@/lib/utils"
function Card({
className,
size = "default",
...props
}: React.ComponentProps<"div"> & { size?: "default" | "sm" }) {
return (
<div
data-slot="card"
data-size={size}
className={cn(
"group/card flex flex-col gap-(--card-spacing) overflow-hidden rounded-xl bg-card py-(--card-spacing) text-sm text-card-foreground ring-1 ring-foreground/10 [--card-spacing:--spacing(4)] has-data-[slot=card-footer]:pb-0 has-[>img:first-child]:pt-0 data-[size=sm]:[--card-spacing:--spacing(3)] data-[size=sm]:has-data-[slot=card-footer]:pb-0 *:[img:first-child]:rounded-t-xl *:[img:last-child]:rounded-b-xl",
className
)}
{...props}
/>
)
}
function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-header"
className={cn(
"group/card-header @container/card-header grid auto-rows-min items-start gap-1 rounded-t-xl px-(--card-spacing) has-data-[slot=card-action]:grid-cols-[1fr_auto] has-data-[slot=card-description]:grid-rows-[auto_auto] [.border-b]:pb-(--card-spacing)",
className
)}
{...props}
/>
)
}
function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-title"
className={cn(
"font-heading text-base leading-snug font-medium group-data-[size=sm]/card:text-sm",
className
)}
{...props}
/>
)
}
function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-description"
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
)
}
function CardAction({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-action"
className={cn(
"col-start-2 row-span-2 row-start-1 self-start justify-self-end",
className
)}
{...props}
/>
)
}
function CardContent({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-content"
className={cn("px-(--card-spacing)", className)}
{...props}
/>
)
}
function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
return (
<div
data-slot="card-footer"
className={cn(
"flex items-center rounded-b-xl border-t bg-muted/50 p-(--card-spacing)",
className
)}
{...props}
/>
)
}
export {
Card,
CardHeader,
CardFooter,
CardTitle,
CardAction,
CardDescription,
CardContent,
}
+20
View File
@@ -0,0 +1,20 @@
import * as React from "react"
import { Input as InputPrimitive } from "@base-ui/react/input"
import { cn } from "@/lib/utils"
function Input({ className, type, ...props }: React.ComponentProps<"input">) {
return (
<InputPrimitive
type={type}
data-slot="input"
className={cn(
"h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
className
)}
{...props}
/>
)
}
export { Input }
@@ -0,0 +1,7 @@
İçerik analizi alınamadı.
---
## Görsel Analiz (Gemini Vision)
Generate a modern, responsive UI for an adventure-focused travel agency and tour operator website, featuring a top navigation and card-based content display. The design should utilize a fresh color palette of turquoise, deep blue, and sand white, with vibrant orange for prominent call-to-action elements, emphasizing high-quality destination visuals and a streamlined user experience for tour bookings.
+169
View File
@@ -0,0 +1,169 @@
# Site Verisi — http://www.oludeniztourtravels.com/
Tarama Tarihi: 2026-06-10
---
[![ÖLÜDENİZ TOUR TRAVELS](http://www.oludeniztourtravels.com/image/catalog/logo.png)](http://www.oludeniztourtravels.com/index.php?route=common/home)
Çağrı Merkezi [+90 535 317 19 12](tel:+90%20535%20317%2019%2012)
[](https://www.tripadvisor.com.tr/Attraction_Review-g312737-d4881157-Reviews-Pegas_Paragliding-Oludeniz_Mugla_Province_Turkish_Aegean_Coast.html) [](https://www.instagram.com/pegasturfethiye/) [](https://www.facebook.com/pages/PEGASTUR/444290662644119/)
[Türkçe](http://www.oludeniztourtravels.com/)
* [English](javascript:;)
* [Türkçe](javascript:;)
[ ![WhatsApp Destek Hatt覺](http://www.oludeniztourtravels.com/whatsapp.png) ](https://api.whatsapp.com/send?phone=905353171912%0A "WhatsApp Destek Hatt覺")
ÖLÜDENİZ TOUR TRAVELS
* [](http://www.oludeniztourtravels.com/index.php?route=common/home)
* [**Hakkımızda**](http://www.oludeniztourtravels.com/hakkimizda)
* [**Turlar**](http://www.oludeniztourtravels.com/turlar)
* [Mehtap Turu](http://www.oludeniztourtravels.com/turlar/mehtap-turu)
* [Dalyan Turu](http://www.oludeniztourtravels.com/turlar/dalyan-turu)
* [Pamukkale Turu](http://www.oludeniztourtravels.com/turlar/pamukkale-turu)
* [Özel Turlar](http://www.oludeniztourtravels.com/turlar/ozel-turlar)
* [**Aktiviteler**](http://www.oludeniztourtravels.com/aktiviteler)
* [Saklıkent Tlos Safari Turu](http://www.oludeniztourtravels.com/aktiviteler/saklikent-tlos-safari-turu)
* [Yamaç Paraşütü](http://www.oludeniztourtravels.com/aktiviteler/yamac-parasutu)
* [Dalış Turu](http://www.oludeniztourtravels.com/aktiviteler/dalis-turu)
* [Atv Safari](http://www.oludeniztourtravels.com/aktiviteler/atv-safari)
* [Rafting Turu](http://www.oludeniztourtravels.com/aktiviteler/rafting-turu)
* [Aquapark](http://www.oludeniztourtravels.com/aktiviteler/aquapark)
* [At Turu](http://www.oludeniztourtravels.com/aktiviteler/at-turu)
* [Türk Hamamı](http://www.oludeniztourtravels.com/aktiviteler/turk-hamami)
* [**Tekne Turları**](http://www.oludeniztourtravels.com/tekne-turlari)
* [Ölüdeniz Kelebekler Vadisi Tekne Turu](http://www.oludeniztourtravels.com/tekne-turlari/oludeniz-kelebekler-vadisi-tekne-turu)
* [12 Adalar Tekne Turu](http://www.oludeniztourtravels.com/tekne-turlari/12-adalar-tekne-turu)
* [**Araç Kiralama**](http://www.oludeniztourtravels.com/arac-kiralama)
* [**Villa Kiralama**](http://www.oludeniztourtravels.com/villa-kiralama)
* [**İletişim**](http://www.oludeniztourtravels.com/index.php?route=information/contact)
* [**![](http://www.oludeniztourtravels.com/image/catalog/whatsapp-logo-png-transparent.png)Rezervasyon**](https://api.whatsapp.com/send?phone=905353171912)
![Slider](http://www.oludeniztourtravels.com/image/catalog/slayt/1.jpg)
![Slider](http://www.oludeniztourtravels.com/image/catalog/slayt/2.jpg)
![Slider](http://www.oludeniztourtravels.com/image/catalog/slayt/3.jpg)
![Slider](http://www.oludeniztourtravels.com/image/catalog/slayt/4.jpg)
![Slider](http://www.oludeniztourtravels.com/image/catalog/slayt/5.jpg)
![Slider](http://www.oludeniztourtravels.com/image/catalog/slayt/12.jpg)
![Slider](http://www.oludeniztourtravels.com/image/catalog/slayt/7.jpg)
![Slider](http://www.oludeniztourtravels.com/image/catalog/slayt/8.jpg)
![Slider](http://www.oludeniztourtravels.com/image/catalog/slayt/9.jpg)
![Slider](http://www.oludeniztourtravels.com/image/catalog/slayt/10.JPG)
![Slider](http://www.oludeniztourtravels.com/image/catalog/slayt/16.jpg)
![Slider](http://www.oludeniztourtravels.com/image/catalog/slayt/15.jpg)
![Slider](http://www.oludeniztourtravels.com/image/catalog/slayt/fgfggff.jpeg)
![Slider](http://www.oludeniztourtravels.com/image/catalog/slayt/gfhgb.jpeg)
# ÖLÜDENİZ TOUR TRAVELS
# Ölüdeniz Tur Rezervasyon
## **Mavi Yolculuk - Günübirlik Turlar - Aktiviteler - Özel Turlar - Araç Kiralama - Villa Kiralama**
## Uzman kadroya sahip olan şirketimiz Deniz Turu, Kara Turu ve Aktiviteler ile Siz değerli misafirlerimize Hizmet Vermektedir.
Firmamız, profesyonel uzman kadrosu ile, konforlu, modern ve üstün kalitede hizmet sunmaktadır.
Firmamızın , değişmeyen prensipleri daima kaliteli hizmet, zamanında ulaşım ve uygun fiyat olmuştur.
Kaliteye verdiği önem ve zoru başarma azmi sayesinde kısa sürede Turizm sektöründe tanınmış ve bu ilkelerden asla vazgeçmemiştir
Turizm Sektöründe başarı ve sürekliliğin teminatı hizmette dürüstlük ve kalitedir prensibiyle çalışan Ölüdeniz Tour Travel , gösterdiğiniz yakın ilgi ve desteğinizden ötürü teşekkür ederek sizlere bugün ve gelecekte hizmet vermeye devam edecektir.
[](http://www.oludeniztourtravels.com/#myCarousel19124476) [](http://www.oludeniztourtravels.com/#myCarousel19124476)
TURLAR
[ ![Kara İnci Günlük Tur Teknesi](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/karainci/sdsdf-400x259.jpeg) ![Kara İnci Günlük Tur Teknesi](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=137)
[Kara İnci Günlük Tur Teknesi](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=137)
0,00TL
[ ![Pegas Günlük Tur Teknesi](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/pegas/gfhgb-400x259.jpeg) ![Pegas Günlük Tur Teknesi](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=133)
[Pegas Günlük Tur Teknesi](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=133)
0,00TL
[ ![Medusa Günlük Tur Teknesi](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/medusa/fdgfghjhjhgfd-400x259.jpg) ![Medusa Günlük Tur Teknesi](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=132)
[Medusa Günlük Tur Teknesi](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=132)
0,00TL
[ ![Sürat Teknesi](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/suratteknesi/vbnbvbcvcc-400x259.jpg) ![Sürat Teknesi](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=136)
[Sürat Teknesi](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=136)
0,00TL
[ ![Mehtap Turu](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/mehtapturu/hjhkhg-400x259.jpg) ![Mehtap Turu](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=122)
[Mehtap Turu](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=122)
0,00TL
[ ![Pamukkale Turu](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/pamukkaleturu/dfgfh-400x259.jpg) ![Pamukkale Turu](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=121)
[Pamukkale Turu](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=121)
0,00TL
[ ![Saklıkent Tlos Safari Turu](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/saklikenttlossafarituru/jhhgfd-400x259.JPG) ![Saklıkent Tlos Safari Turu](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=124)
[Saklıkent Tlos Safari Turu](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=124)
0,00TL
[ ![Atv Safari Turu](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/atvsafarituru/tykjk%C3%A7kjuy-400x259.jpg) ![Atv Safari Turu](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=127)
[Atv Safari Turu](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=127)
0,00TL
[ ![Yamaç Paraşütü](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/yamacparasutu/3456544-400x259.jpg) ![Yamaç Paraşütü](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=125)
[Yamaç Paraşütü](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=125)
0,00TL
[ ![Dalış Turu](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/dalisturu/snorkelingneeleylva-400x259.jpg) ![Dalış Turu](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=126)
[Dalış Turu](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=126)
0,00TL
[ ![Dalyan Turu](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/dalyanturu/fgnmhgf-400x259.jpg) ![Dalyan Turu](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=123)
[Dalyan Turu](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=123)
0,00TL
[ ![Aquapark](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/aquapark/fgnbbv-400x259.jpg) ![Aquapark](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=129)
[Aquapark](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=129)
0,00TL
[ ![Rafting Tur](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/raftingturu/erhhkkj-400x259.jpg) ![Rafting Tur](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=128)
[Rafting Tur](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=128)
0,00TL
[ ![Türk Hamamı](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/turkhamami/dfghhghf-400x259.jpg) ![Türk Hamamı](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=131)
[Türk Hamamı](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=131)
0,00TL
[ ![At Turu](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/atturu/fghkjhjghg-400x259.jpg) ![At Turu](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=130)
[At Turu](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=130)
0,00TL
[ ![Villa Kiralama](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/villakiralama/jkjhgfds-400x259.jpg) ![Villa Kiralama](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=134)
[Villa Kiralama](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=134)
0,00TL
[ ![Araç Kiralama](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/arackiralama/dffgmnnm-400x259.jpg) ![Araç Kiralama](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=135)
[Araç Kiralama](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=135)
0,00TL
[](http://www.oludeniztourtravels.com/#myCarousel92225766) [](http://www.oludeniztourtravels.com/#myCarousel92225766)
ARAÇ KİRALAMA - VİLLA KİRALAMA - ÖZEL TURLAR
[ ![Araç Kiralama](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/arackiralama/dffgmnnm-400x259.jpg) ![Araç Kiralama](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=135)
[Araç Kiralama](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=135)
0,00TL
[ ![Villa Kiralama](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/villakiralama/jkjhgfds-400x259.jpg) ![Villa Kiralama](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=134)
[Villa Kiralama](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=134)
0,00TL
[ ![Medusa Günlük Tur Teknesi](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/medusa/fdgfghjhjhgfd-400x259.jpg) ![Medusa Günlük Tur Teknesi](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=132)
[Medusa Günlük Tur Teknesi](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=132)
0,00TL
[ ![Pegas Günlük Tur Teknesi](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/pegas/gfhgb-400x259.jpeg) ![Pegas Günlük Tur Teknesi](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=133)
[Pegas Günlük Tur Teknesi](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=133)
0,00TL
[ ![Sürat Teknesi](http://www.oludeniztourtravels.com/image/cache/catalog/turlar/suratteknesi/vbnbvbcvcc-400x259.jpg) ![Sürat Teknesi](http://www.oludeniztourtravels.com/image/catalog/blank.gif) ](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=136)
[Sürat Teknesi](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=136)
0,00TL
![Phone](http://www.oludeniztourtravels.com/image/catalog/architecture/icon-phone.png)
0535 317 19 12
Bize Ulaşın...
![Phone](http://www.oludeniztourtravels.com/image/catalog/architecture/icon-phone2.png)
08:00 - 24:00
Çalışma Saatleri
![Mail](http://www.oludeniztourtravels.com/image/catalog/architecture/icon-mail.png) [Send e-mail](http://www.oludeniztourtravels.com/index.php?route=information/contact)
![Logo](http://www.oludeniztourtravels.com/image/catalog/architecture/logo.png)
[](https://www.tripadvisor.com.tr/Attraction_Review-g312737-d4881157-Reviews-Pegas_Paragliding-Oludeniz_Mugla_Province_Turkish_Aegean_Coast.html) [](https://www.instagram.com/pegasturfethiye/) [](https://www.facebook.com/pages/PEGASTUR/444290662644119/)
#### HİZMETLERİMİZ
* [Turlar](http://www.oludeniztourtravels.com/turlar)
* [Aktiviteler](http://www.oludeniztourtravels.com/aktiviteler)
* [Tekne Turları](http://www.oludeniztourtravels.com/tekne-turlari)
* [Özel Turlar](http://www.oludeniztourtravels.com/ozel-turlar)
* [Araç Kiralama](http://www.oludeniztourtravels.com/arac-kiralama)
* [Villa Kiralama](http://www.oludeniztourtravels.com/villa-kiralama)
* [İletişim](http://www.oludeniztourtravels.com/index.php?route=information/contact)
#### İLETİŞİM
Ölüdeniz Cad. No: 57/A Ölüdeniz
48300 Fethiye MUĞLA
+90 535 317 19 12
+90 535 317 19 12
+90 535 317 19 12
info@oludeniztourtravels.com
[![](http://www.oludeniztourtravels.com/tripadvisor.png)](https://www.tripadvisor.com.tr/Attraction_Review-g312737-d4881157-Reviews-Pegas_Paragliding-Oludeniz_Mugla_Province_Turkish_Aegean_Coast.html)
[![](http://www.oludeniztourtravels.com/tursab.png)](https://www.tursab.org.tr/)
[TrFirma Web Tasarım © 2021](http://www.trfirmawebtasarim.com)
+18
View File
@@ -0,0 +1,18 @@
import { defineConfig, globalIgnores } from "eslint/config";
import nextVitals from "eslint-config-next/core-web-vitals";
import nextTs from "eslint-config-next/typescript";
const eslintConfig = defineConfig([
...nextVitals,
...nextTs,
// Override default ignores of eslint-config-next.
globalIgnores([
// Default ignores of eslint-config-next:
".next/**",
"out/**",
"build/**",
"next-env.d.ts",
]),
]);
export default eslintConfig;
+15
View File
@@ -0,0 +1,15 @@
import {getRequestConfig} from 'next-intl/server';
import {routing} from './routing';
export default getRequestConfig(async ({requestLocale}) => {
let locale = await requestLocale;
if (!locale || !routing.locales.includes(locale as any)) {
locale = routing.defaultLocale;
}
return {
locale,
messages: (await import(`../messages/${locale}.json`)).default
};
});
+10
View File
@@ -0,0 +1,10 @@
import {defineRouting} from 'next-intl/routing';
import {createNavigation} from 'next-intl/navigation';
export const routing = defineRouting({
locales: ['en', 'tr'],
defaultLocale: 'tr'
});
export const {Link, redirect, usePathname, useRouter, getPathname} =
createNavigation(routing);
+47
View File
@@ -0,0 +1,47 @@
import NextAuth from "next-auth"
import CredentialsProvider from "next-auth/providers/credentials"
export const { handlers, auth, signIn, signOut } = NextAuth({
providers: [
CredentialsProvider({
name: "Credentials",
credentials: {
email: { label: "Email", type: "email" },
password: { label: "Password", type: "password" }
},
async authorize(credentials) {
// Boilerplate mock logic
// TODO: In production, lookup user in Prisma and verify password using bcrypt
// const user = await db.user.findUnique({ where: { email: credentials.email } })
if (credentials?.email === "admin@ayris.tech" && credentials?.password === "admin") {
return {
id: "1",
name: "Admin User",
email: "admin@ayris.tech",
role: "ADMIN"
}
}
return null
}
})
],
callbacks: {
async jwt({ token, user }) {
if (user) {
token.role = (user as any).role
}
return token
},
async session({ session, token }) {
if (session.user && token.role) {
(session.user as any).role = token.role
}
return session
}
},
pages: {
signIn: '/login'
}
})
+20
View File
@@ -0,0 +1,20 @@
import { v2 as cloudinary } from 'cloudinary'
cloudinary.config({
cloud_name: process.env.CLOUDINARY_CLOUD_NAME!,
api_key: process.env.CLOUDINARY_API_KEY!,
api_secret: process.env.CLOUDINARY_API_SECRET!,
})
export async function uploadImage(file: string, folder: string) {
const result = await cloudinary.uploader.upload(file, {
folder, transformation: [{ quality: 'auto', fetch_format: 'auto' }],
})
return { url: result.secure_url, publicId: result.public_id }
}
export async function deleteImage(publicId: string) {
await cloudinary.uploader.destroy(publicId)
}
export { cloudinary }
+55
View File
@@ -0,0 +1,55 @@
export const siteInfo = {
name: "ÖLÜDENİZ TOUR TRAVELS",
nameAlt: "Ölüdeniz Tur Rezervasyon",
slogan: "Mavi Yolculuk - Günübirlik Turlar - Aktiviteler - Özel Turlar - Araç Kiralama - Villa Kiralama",
phone: "+90 535 317 19 12",
phoneLink: "tel:+905353171912",
whatsappLink: "https://api.whatsapp.com/send?phone=905353171912",
email: "info@oludeniztourtravels.com",
address: "Ölüdeniz Cad. No: 57/A Ölüdeniz 48300 Fethiye MUĞLA",
socials: {
tripadvisor: "https://www.tripadvisor.com.tr/Attraction_Review-g312737-d4881157-Reviews-Pegas_Paragliding-Oludeniz_Mugla_Province_Turkish_Aegean_Coast.html",
instagram: "https://www.instagram.com/pegasturfethiye/",
facebook: "https://www.facebook.com/pages/PEGASTUR/444290662644119/"
}
};
export const heroSlides = [
"http://www.oludeniztourtravels.com/image/catalog/slayt/1.jpg",
"http://www.oludeniztourtravels.com/image/catalog/slayt/2.jpg",
"http://www.oludeniztourtravels.com/image/catalog/slayt/3.jpg",
"http://www.oludeniztourtravels.com/image/catalog/slayt/4.jpg",
"http://www.oludeniztourtravels.com/image/catalog/slayt/5.jpg"
];
export const turlar = [
{ id: 122, slug: "mehtap-turu", title: "Mehtap Turu", image: "http://www.oludeniztourtravels.com/image/cache/catalog/turlar/mehtapturu/hjhkhg-400x259.jpg", price: "0,00TL" },
{ id: 123, slug: "dalyan-turu", title: "Dalyan Turu", image: "http://www.oludeniztourtravels.com/image/cache/catalog/turlar/dalyanturu/fgnmhgf-400x259.jpg", price: "0,00TL" },
{ id: 121, slug: "pamukkale-turu", title: "Pamukkale Turu", image: "http://www.oludeniztourtravels.com/image/cache/catalog/turlar/pamukkaleturu/dfgfh-400x259.jpg", price: "0,00TL" },
{ id: 999, slug: "ozel-turlar", title: "Özel Turlar", image: "http://www.oludeniztourtravels.com/image/cache/catalog/turlar/villakiralama/jkjhgfds-400x259.jpg", price: "0,00TL" }
];
export const aktiviteler = [
{ id: 124, slug: "saklikent-tlos-safari-turu", title: "Saklıkent Tlos Safari Turu", image: "http://www.oludeniztourtravels.com/image/cache/catalog/turlar/saklikenttlossafarituru/jhhgfd-400x259.JPG", price: "0,00TL" },
{ id: 125, slug: "yamac-parasutu", title: "Yamaç Paraşütü", image: "http://www.oludeniztourtravels.com/image/cache/catalog/turlar/yamacparasutu/3456544-400x259.jpg", price: "0,00TL" },
{ id: 126, slug: "dalis-turu", title: "Dalış Turu", image: "http://www.oludeniztourtravels.com/image/cache/catalog/turlar/dalisturu/snorkelingneeleylva-400x259.jpg", price: "0,00TL" },
{ id: 127, slug: "atv-safari", title: "Atv Safari Turu", image: "http://www.oludeniztourtravels.com/image/cache/catalog/turlar/atvsafarituru/tykjk%C3%A7kjuy-400x259.jpg", price: "0,00TL" },
{ id: 128, slug: "rafting-turu", title: "Rafting Tur", image: "http://www.oludeniztourtravels.com/image/cache/catalog/turlar/raftingturu/erhhkkj-400x259.jpg", price: "0,00TL" },
{ id: 129, slug: "aquapark", title: "Aquapark", image: "http://www.oludeniztourtravels.com/image/cache/catalog/turlar/aquapark/fgnbbv-400x259.jpg", price: "0,00TL" },
{ id: 130, slug: "at-turu", title: "At Turu", image: "http://www.oludeniztourtravels.com/image/cache/catalog/turlar/atturu/fghkjhjghg-400x259.jpg", price: "0,00TL" },
{ id: 131, slug: "turk-hamami", title: "Türk Hamamı", image: "http://www.oludeniztourtravels.com/image/cache/catalog/turlar/turkhamami/dfghhghf-400x259.jpg", price: "0,00TL" }
];
export const tekneTurlari = [
{ id: 137, slug: "kara-inci-gunluk-tur-teknesi", title: "Kara İnci Günlük Tur Teknesi", image: "http://www.oludeniztourtravels.com/image/cache/catalog/turlar/karainci/sdsdf-400x259.jpeg", price: "0,00TL" },
{ id: 133, slug: "pegas-gunluk-tur-teknesi", title: "Pegas Günlük Tur Teknesi", image: "http://www.oludeniztourtravels.com/image/cache/catalog/turlar/pegas/gfhgb-400x259.jpeg", price: "0,00TL" },
{ id: 132, slug: "medusa-gunluk-tur-teknesi", title: "Medusa Günlük Tur Teknesi", image: "http://www.oludeniztourtravels.com/image/cache/catalog/turlar/medusa/fdgfghjhjhgfd-400x259.jpg", price: "0,00TL" },
{ id: 136, slug: "surat-teknesi", title: "Sürat Teknesi", image: "http://www.oludeniztourtravels.com/image/cache/catalog/turlar/suratteknesi/vbnbvbcvcc-400x259.jpg", price: "0,00TL" },
{ id: 801, slug: "oludeniz-kelebekler-vadisi-tekne-turu", title: "Ölüdeniz Kelebekler Vadisi Tekne Turu", image: "http://www.oludeniztourtravels.com/image/cache/catalog/turlar/medusa/fdgfghjhjhgfd-400x259.jpg", price: "0,00TL" },
{ id: 802, slug: "12-adalar-tekne-turu", title: "12 Adalar Tekne Turu", image: "http://www.oludeniztourtravels.com/image/cache/catalog/turlar/pegas/gfhgb-400x259.jpeg", price: "0,00TL" }
];
export const digerHizmetler = [
{ id: 135, slug: "arac-kiralama", title: "Araç Kiralama", image: "http://www.oludeniztourtravels.com/image/cache/catalog/turlar/arackiralama/dffgmnnm-400x259.jpg", price: "0,00TL" },
{ id: 134, slug: "villa-kiralama", title: "Villa Kiralama", image: "http://www.oludeniztourtravels.com/image/cache/catalog/turlar/villakiralama/jkjhgfds-400x259.jpg", price: "0,00TL" }
];
+9
View File
@@ -0,0 +1,9 @@
import { PrismaClient } from '@prisma/client'
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined
}
export const db = globalForPrisma.prisma ?? new PrismaClient()
if (process.env.NODE_ENV !== 'production') globalForPrisma.prisma = db
+6
View File
@@ -0,0 +1,6 @@
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs))
}
+14
View File
@@ -0,0 +1,14 @@
{
"nav": {
"home": "Home",
"about": "About Us",
"contact": "Contact"
},
"hero": {
"title": "Welcome",
"cta": "Learn More"
},
"footer": {
"rights": "All rights reserved."
}
}
+14
View File
@@ -0,0 +1,14 @@
{
"nav": {
"home": "Ana Sayfa",
"about": "Hakkımızda",
"contact": "İletişim"
},
"hero": {
"title": "Hoş Geldiniz",
"cta": "Daha Fazla Bilgi"
},
"footer": {
"rights": "Tüm hakları saklıdır."
}
}
+18
View File
@@ -0,0 +1,18 @@
import type { NextConfig } from 'next'
import createNextIntlPlugin from 'next-intl/plugin'
const withNextIntl = createNextIntlPlugin('./i18n/request.ts')
const nextConfig: NextConfig = {
output: 'standalone',
images: {
remotePatterns: [
{ protocol: 'https', hostname: 'res.cloudinary.com' },
{ protocol: 'https', hostname: 'images.unsplash.com' },
{ protocol: 'http', hostname: 'www.oludeniztourtravels.com' },
{ protocol: 'https', hostname: 'www.oludeniztourtravels.com' },
],
},
}
export default withNextIntl(nextConfig)
+11008
View File
File diff suppressed because it is too large Load Diff
+40
View File
@@ -0,0 +1,40 @@
{
"name": "fethiye-holiday",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint"
},
"dependencies": {
"@base-ui/react": "^1.5.0",
"@prisma/client": "^6.4.1",
"class-variance-authority": "^0.7.1",
"cloudinary": "^2.10.0",
"clsx": "^2.1.1",
"developer-icons": "^7.0.1",
"framer-motion": "^12.40.0",
"lucide-react": "^1.18.0",
"next": "16.2.9",
"next-auth": "^5.0.0-beta.31",
"next-intl": "^4.13.0",
"react": "19.2.4",
"react-dom": "19.2.4",
"shadcn": "^4.11.0",
"tailwind-merge": "^3.6.0",
"tw-animate-css": "^1.4.0"
},
"devDependencies": {
"@tailwindcss/postcss": "^4",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"eslint": "^9",
"eslint-config-next": "16.2.9",
"prisma": "^6.4.1",
"tailwindcss": "^4",
"typescript": "^5"
}
}
+7
View File
@@ -0,0 +1,7 @@
const config = {
plugins: {
"@tailwindcss/postcss": {},
},
};
export default config;
+61
View File
@@ -0,0 +1,61 @@
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
enum Role {
ADMIN
USER
}
model User {
id String @id @default(cuid())
name String?
email String @unique
password String?
role Role @default(USER)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
deletedAt DateTime?
accounts Account[]
sessions Session[]
}
model Account {
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String? @db.Text
access_token String? @db.Text
expires_at Int?
token_type String?
scope String?
id_token String? @db.Text
session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
}
model Session {
id String @id @default(cuid())
sessionToken String @unique
userId String
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
model VerificationToken {
identifier String
token String @unique
expires DateTime
@@unique([identifier, token])
}
+20
View File
@@ -0,0 +1,20 @@
import { NextRequest, NextResponse } from 'next/server'
import createMiddleware from 'next-intl/middleware'
import { auth } from '@/lib/auth'
import { routing } from '@/i18n/routing'
const intlMiddleware = createMiddleware(routing)
export async function proxy(request: NextRequest) {
if (request.nextUrl.pathname.includes('/admin')) {
const session = await auth()
if (!session || (session.user as any)?.role !== 'ADMIN') {
return NextResponse.redirect(new URL('/login', request.url))
}
}
return intlMiddleware(request)
}
export const config = {
matcher: ['/((?!api|_next|_vercel|.*\\..*).*)']
}
+1
View File
@@ -0,0 +1 @@
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 391 B

+1
View File
@@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 394 80"><path fill="#000" d="M262 0h68.5v12.7h-27.2v66.6h-13.6V12.7H262V0ZM149 0v12.7H94v20.4h44.3v12.6H94v21h55v12.6H80.5V0h68.7zm34.3 0h-17.8l63.8 79.4h17.9l-32-39.7 32-39.6h-17.9l-23 28.6-23-28.6zm18.3 56.7-9-11-27.1 33.7h17.8l18.3-22.7z"/><path fill="#000" d="M81 79.3 17 0H0v79.3h13.6V17l50.2 62.3H81Zm252.6-.4c-1 0-1.8-.4-2.5-1s-1.1-1.6-1.1-2.6.3-1.8 1-2.5 1.6-1 2.6-1 1.8.3 2.5 1a3.4 3.4 0 0 1 .6 4.3 3.7 3.7 0 0 1-3 1.8zm23.2-33.5h6v23.3c0 2.1-.4 4-1.3 5.5a9.1 9.1 0 0 1-3.8 3.5c-1.6.8-3.5 1.3-5.7 1.3-2 0-3.7-.4-5.3-1s-2.8-1.8-3.7-3.2c-.9-1.3-1.4-3-1.4-5h6c.1.8.3 1.6.7 2.2s1 1.2 1.6 1.5c.7.4 1.5.5 2.4.5 1 0 1.8-.2 2.4-.6a4 4 0 0 0 1.6-1.8c.3-.8.5-1.8.5-3V45.5zm30.9 9.1a4.4 4.4 0 0 0-2-3.3 7.5 7.5 0 0 0-4.3-1.1c-1.3 0-2.4.2-3.3.5-.9.4-1.6 1-2 1.6a3.5 3.5 0 0 0-.3 4c.3.5.7.9 1.3 1.2l1.8 1 2 .5 3.2.8c1.3.3 2.5.7 3.7 1.2a13 13 0 0 1 3.2 1.8 8.1 8.1 0 0 1 3 6.5c0 2-.5 3.7-1.5 5.1a10 10 0 0 1-4.4 3.5c-1.8.8-4.1 1.2-6.8 1.2-2.6 0-4.9-.4-6.8-1.2-2-.8-3.4-2-4.5-3.5a10 10 0 0 1-1.7-5.6h6a5 5 0 0 0 3.5 4.6c1 .4 2.2.6 3.4.6 1.3 0 2.5-.2 3.5-.6 1-.4 1.8-1 2.4-1.7a4 4 0 0 0 .8-2.4c0-.9-.2-1.6-.7-2.2a11 11 0 0 0-2.1-1.4l-3.2-1-3.8-1c-2.8-.7-5-1.7-6.6-3.2a7.2 7.2 0 0 1-2.4-5.7 8 8 0 0 1 1.7-5 10 10 0 0 1 4.3-3.5c2-.8 4-1.2 6.4-1.2 2.3 0 4.4.4 6.2 1.2 1.8.8 3.2 2 4.3 3.4 1 1.4 1.5 3 1.5 5h-5.8z"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

+1
View File
@@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1155 1000"><path d="m577.3 0 577.4 1000H0z" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 128 B

+1
View File
@@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill-rule="evenodd" clip-rule="evenodd" d="M1.5 2.5h13v10a1 1 0 0 1-1 1h-11a1 1 0 0 1-1-1zM0 1h16v11.5a2.5 2.5 0 0 1-2.5 2.5h-11A2.5 2.5 0 0 1 0 12.5zm3.75 4.5a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5M7 4.75a.75.75 0 1 1-1.5 0 .75.75 0 0 1 1.5 0m1.75.75a.75.75 0 1 0 0-1.5.75.75 0 0 0 0 1.5" fill="#666"/></svg>

After

Width:  |  Height:  |  Size: 385 B

+34
View File
@@ -0,0 +1,34 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
"noEmit": true,
"esModuleInterop": true,
"module": "esnext",
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
"name": "next"
}
],
"paths": {
"@/*": ["./*"]
}
},
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts",
"**/*.mts"
],
"exclude": ["node_modules"]
}