first commit
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
.next
|
||||
node_modules
|
||||
.env
|
||||
.env.*.local
|
||||
.git
|
||||
.vscode
|
||||
docs
|
||||
README.md
|
||||
AGENTS.md
|
||||
+41
@@ -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
|
||||
@@ -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 -->
|
||||
+34
@@ -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"]
|
||||
@@ -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.
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import { handlers } from "@/lib/auth"
|
||||
|
||||
export const { GET, POST } = handlers
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 25 KiB |
+134
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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": {}
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>© {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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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>
|
||||
);
|
||||
}
|
||||
@@ -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 }
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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.
|
||||
@@ -0,0 +1,169 @@
|
||||
# Site Verisi — http://www.oludeniztourtravels.com/
|
||||
|
||||
Tarama Tarihi: 2026-06-10
|
||||
|
||||
---
|
||||
|
||||
[](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:;)
|
||||
|
||||
|
||||
[  ](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)
|
||||
* [**Rezervasyon**](https://api.whatsapp.com/send?phone=905353171912)
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
# Ö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
|
||||
[   ](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
|
||||
[   ](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
|
||||
[   ](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
|
||||
[   ](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
|
||||
[   ](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
|
||||
[   ](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
|
||||
[   ](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
|
||||
[   ](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
|
||||
[   ](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
|
||||
[   ](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
|
||||
[   ](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
|
||||
[   ](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
|
||||
[   ](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
|
||||
[   ](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
|
||||
[   ](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
|
||||
[   ](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
|
||||
[   ](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
|
||||
[   ](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/index.php?route=product/product&product_id=134)
|
||||
[Villa Kiralama](http://www.oludeniztourtravels.com/index.php?route=product/product&product_id=134)
|
||||
0,00TL
|
||||
[   ](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
|
||||
[   ](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
|
||||
[   ](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
|
||||

|
||||
0535 317 19 12
|
||||
Bize Ulaşın...
|
||||

|
||||
08:00 - 24:00
|
||||
Çalışma Saatleri
|
||||
 [Send e-mail](http://www.oludeniztourtravels.com/index.php?route=information/contact)
|
||||

|
||||
|
||||
|
||||
[](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
|
||||
[](https://www.tripadvisor.com.tr/Attraction_Review-g312737-d4881157-Reviews-Pegas_Paragliding-Oludeniz_Mugla_Province_Turkish_Aegean_Coast.html)
|
||||
[](https://www.tursab.org.tr/)
|
||||
[TrFirma Web Tasarım © 2021](http://www.trfirmawebtasarim.com)
|
||||
@@ -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;
|
||||
@@ -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
|
||||
};
|
||||
});
|
||||
@@ -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
@@ -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'
|
||||
}
|
||||
})
|
||||
@@ -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
@@ -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" }
|
||||
];
|
||||
@@ -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
|
||||
@@ -0,0 +1,6 @@
|
||||
import { clsx, type ClassValue } from "clsx"
|
||||
import { twMerge } from "tailwind-merge"
|
||||
|
||||
export function cn(...inputs: ClassValue[]) {
|
||||
return twMerge(clsx(inputs))
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"nav": {
|
||||
"home": "Home",
|
||||
"about": "About Us",
|
||||
"contact": "Contact"
|
||||
},
|
||||
"hero": {
|
||||
"title": "Welcome",
|
||||
"cta": "Learn More"
|
||||
},
|
||||
"footer": {
|
||||
"rights": "All rights reserved."
|
||||
}
|
||||
}
|
||||
@@ -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."
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
Generated
+11008
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
const config = {
|
||||
plugins: {
|
||||
"@tailwindcss/postcss": {},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
@@ -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])
|
||||
}
|
||||
@@ -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|.*\\..*).*)']
|
||||
}
|
||||
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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 |
@@ -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"]
|
||||
}
|
||||
Reference in New Issue
Block a user