commit 7ecdb30afc1cebd142d6512c59d9567e948c63e2 Author: ayrisdev Date: Tue Jun 16 13:04:11 2026 +0300 first commit diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..430f1b2 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,9 @@ +.next +node_modules +.env +.env.*.local +.git +.vscode +docs +README.md +AGENTS.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ef6a52 --- /dev/null +++ b/.gitignore @@ -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 diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..f7f0cbb --- /dev/null +++ b/AGENTS.md @@ -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 + diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..43c994c --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..20dd2ca --- /dev/null +++ b/Dockerfile @@ -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"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..e215bc4 --- /dev/null +++ b/README.md @@ -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. diff --git a/app/[locale]/HomePageClient.tsx b/app/[locale]/HomePageClient.tsx new file mode 100644 index 0000000..1366d61 --- /dev/null +++ b/app/[locale]/HomePageClient.tsx @@ -0,0 +1,210 @@ +'use client' + +import { motion } from 'framer-motion' +import Link from 'next/link' +import Image from 'next/image' +import { ArrowRight, Star } from 'lucide-react' +import { ShimmerButton } from '@/components/magicui/shimmer-button' +import { cn } from '@/components/common/Navbar' + +const EASE = [0.22, 1, 0.36, 1] as const + +const fadeInUp = { + hidden: { opacity: 0, y: 40, filter: 'blur(8px)' }, + show: { opacity: 1, y: 0, filter: 'blur(0px)', transition: { duration: 0.8, ease: EASE } } +} + +export function HomePageClient({ + dict, + tours, + categories, + reviews +}: { + dict: any, + tours: any[], + categories: any[], + reviews: any[] +}) { + return ( +
+ {/* Hero Section */} +
+ + Fethiye Hero +
+ + +
+ + + {dict.hTitle} + + + {dict.hSubtitle} + + + + + + {dict.hCta} + + + + + +
+
+ + {/* Popular Tours Section */} +
+
+ +

+ {dict.sPopular} +

+ + {dict.sViewAll} + + +
+ +
+ {tours.slice(0, 4).map((tour, i) => ( + + + {tour.title} +
+ +
+

{tour.title}

+
+ {dict.tFrom} + ${tour.price} +
+
+ + ))} +
+
+
+ + {/* Editorial Categories */} +
+
+ + {dict.sCategories} + +
+ {categories.slice(0, 3).map((cat, i) => ( + + + {cat.name} +
+
+

{cat.name}

+
+ +
+
+ + + ))} +
+
+
+ + {/* Reviews Section */} +
+
+

{dict.sReviews}

+
+ {reviews.map((review, i) => ( + +
+ {[...Array(review.rating)].map((_, j) => )} +
+

"{review.comment}"

+
+
{review.authorName}
+
+
+ ))} +
+
+
+
+ ) +} diff --git a/app/[locale]/admin/layout.tsx b/app/[locale]/admin/layout.tsx new file mode 100644 index 0000000..d16615e --- /dev/null +++ b/app/[locale]/admin/layout.tsx @@ -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 ( +
+ {/* Mobile sidebar backdrop */} + {sidebarOpen && ( +
setSidebarOpen(false)} + /> + )} + + {/* Sidebar */} +
+
+
+

Admin Paneli

+ +
+ + + +
+ +
+
+
+ + {/* Main content */} +
+
+ + Admin Paneli +
+ +
+ {children} +
+
+
+ ) +} diff --git a/app/[locale]/admin/page.tsx b/app/[locale]/admin/page.tsx new file mode 100644 index 0000000..4b16051 --- /dev/null +++ b/app/[locale]/admin/page.tsx @@ -0,0 +1,47 @@ +import { auth } from '@/lib/auth' + +export default async function AdminDashboardPage() { + const session = await auth() + + return ( +
+
+

Dashboard

+

+ Hoş geldiniz, {session?.user?.name || session?.user?.email}. İşte projenizin genel görünümü. +

+
+ +
+ {/* 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) => ( +
+
{item.name}
+
+ {item.stat} +
+
+ ))} +
+ +
+
+

Son Aktiviteler

+
+
+ Henüz aktivite kaydı bulunmuyor. +
+
+
+
+
+ ) +} diff --git a/app/[locale]/contact/page.tsx b/app/[locale]/contact/page.tsx new file mode 100644 index 0000000..c6149a8 --- /dev/null +++ b/app/[locale]/contact/page.tsx @@ -0,0 +1,77 @@ +import { setRequestLocale } from 'next-intl/server' +import { Mail, MapPin, Phone } from 'lucide-react' + +export default async function ContactPage({ params }: { params: Promise<{ locale: string }> }) { + const { locale } = await params + setRequestLocale(locale) + + return ( +
+
+

Contact Us

+ +
+ {/* Contact Info */} +
+

Get In Touch

+

+ Pioneer travel is here to help you with all your trips and excursions. Fill out the form or contact us directly using the information below. +

+ +
+
+
+ +
+
+

Office Address

+

Çarşı Caddesi Tonoz İş Merkezi No:3/1
Ölüdeniz - Fethiye / Türkiye

+
+
+
+
+ +
+
+

Phone Number

+

+90 530 378 48 82

+
+
+
+
+ +
+
+

Email Address

+

info@fethiyeholiday.com

+
+
+
+
+ + {/* Contact Form */} +
+

Send a Message

+
+
+ + +
+
+ + +
+
+ + +
+ +
+
+
+
+
+ ) +} diff --git a/app/[locale]/layout.tsx b/app/[locale]/layout.tsx new file mode 100644 index 0000000..b576ac6 --- /dev/null +++ b/app/[locale]/layout.tsx @@ -0,0 +1,65 @@ +import type { Metadata } from "next"; +import { Geist, Geist_Mono, Playfair_Display } 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/common/Navbar'; +import { Footer } from '@/components/common/Footer'; +import "../globals.css"; + +const geistSans = Geist({ + variable: "--font-geist-sans", + subsets: ["latin"], +}); + +const geistMono = Geist_Mono({ + variable: "--font-geist-mono", + subsets: ["latin"], +}); + +const playfair = Playfair_Display({ + variable: "--font-playfair", + subsets: ["latin"], +}); + +export const metadata: Metadata = { + title: "Fethiye Holiday | Pioneer Travel", + description: "Discover the beauty of the Mediterranean with Pioneer Travel.", +}; + +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 ( + + + + + {children} +