From cbc59222c245636e8c22a7013b8b140179276dcb Mon Sep 17 00:00:00 2001 From: AyrisAI Date: Sun, 12 Jul 2026 20:18:55 +0300 Subject: [PATCH] first commit --- .dockerignore | 9 + .gitignore | 41 + AGENTS.md | 42 + CLAUDE.md | 1 + Dockerfile | 34 + README.md | 36 + app/[locale]/[category]/[slug]/page.tsx | 272 + app/[locale]/admin/categories/page.tsx | 43 + app/[locale]/admin/layout.tsx | 123 + .../admin/listings/[id]/ListingForm.tsx | 444 + app/[locale]/admin/listings/[id]/page.tsx | 56 + app/[locale]/admin/listings/page.tsx | 115 + app/[locale]/admin/messages/page.tsx | 88 + app/[locale]/admin/neighborhoods/page.tsx | 43 + app/[locale]/admin/page.tsx | 126 + app/[locale]/admin/submissions/page.tsx | 118 + app/[locale]/apartlar/page.tsx | 161 + app/[locale]/hakkinda/page.tsx | 126 + app/[locale]/iletisim/ContactForm.tsx | 124 + app/[locale]/iletisim/page.tsx | 51 + app/[locale]/isletme-ekle/BusinessForm.tsx | 217 + app/[locale]/isletme-ekle/page.tsx | 79 + app/[locale]/isletmeler/page.tsx | 161 + app/[locale]/layout.tsx | 64 + app/[locale]/login/page.tsx | 94 + app/[locale]/mahalle/[slug]/page.tsx | 74 + app/[locale]/page.tsx | 248 + app/[locale]/restoranlar/page.tsx | 163 + app/actions.ts | 232 + app/api/auth/[...nextauth]/route.ts | 3 + app/favicon.ico | Bin 0 -> 25931 bytes app/globals.css | 153 + components.json | 25 + components/Footer.tsx | 103 + components/ListingCard.tsx | 146 + components/Navbar.tsx | 192 + components/ui/button.tsx | 58 + components/ui/card.tsx | 103 + components/ui/input.tsx | 20 + docs/marmaris-local-brand.html | 398 + docs/prd.md | 225 + eslint.config.mjs | 18 + i18n/request.ts | 15 + i18n/routing.ts | 10 + lib/auth.ts | 47 + lib/cloudinary.ts | 20 + lib/db.ts | 9 + lib/mockDb.ts | 675 + lib/openinary.ts | 36 + lib/utils.ts | 6 + messages/en.json | 83 + messages/ru.json | 83 + messages/tr.json | 83 + next.config.ts | 16 + package-lock.json | 10695 ++++++++++++++++ package.json | 40 + postcss.config.mjs | 7 + prisma/schema.prisma | 158 + proxy.ts | 20 + public/file.svg | 1 + public/globe.svg | 1 + public/next.svg | 1 + public/vercel.svg | 1 + public/window.svg | 1 + tsconfig.json | 34 + 65 files changed, 16871 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 app/[locale]/[category]/[slug]/page.tsx create mode 100644 app/[locale]/admin/categories/page.tsx create mode 100644 app/[locale]/admin/layout.tsx create mode 100644 app/[locale]/admin/listings/[id]/ListingForm.tsx create mode 100644 app/[locale]/admin/listings/[id]/page.tsx create mode 100644 app/[locale]/admin/listings/page.tsx create mode 100644 app/[locale]/admin/messages/page.tsx create mode 100644 app/[locale]/admin/neighborhoods/page.tsx create mode 100644 app/[locale]/admin/page.tsx create mode 100644 app/[locale]/admin/submissions/page.tsx create mode 100644 app/[locale]/apartlar/page.tsx create mode 100644 app/[locale]/hakkinda/page.tsx create mode 100644 app/[locale]/iletisim/ContactForm.tsx create mode 100644 app/[locale]/iletisim/page.tsx create mode 100644 app/[locale]/isletme-ekle/BusinessForm.tsx create mode 100644 app/[locale]/isletme-ekle/page.tsx create mode 100644 app/[locale]/isletmeler/page.tsx create mode 100644 app/[locale]/layout.tsx create mode 100644 app/[locale]/login/page.tsx create mode 100644 app/[locale]/mahalle/[slug]/page.tsx create mode 100644 app/[locale]/page.tsx create mode 100644 app/[locale]/restoranlar/page.tsx create mode 100644 app/actions.ts create mode 100644 app/api/auth/[...nextauth]/route.ts create mode 100644 app/favicon.ico create mode 100644 app/globals.css create mode 100644 components.json create mode 100644 components/Footer.tsx create mode 100644 components/ListingCard.tsx create mode 100644 components/Navbar.tsx create mode 100644 components/ui/button.tsx create mode 100644 components/ui/card.tsx create mode 100644 components/ui/input.tsx create mode 100644 docs/marmaris-local-brand.html create mode 100644 docs/prd.md create mode 100644 eslint.config.mjs create mode 100644 i18n/request.ts create mode 100644 i18n/routing.ts create mode 100644 lib/auth.ts create mode 100644 lib/cloudinary.ts create mode 100644 lib/db.ts create mode 100644 lib/mockDb.ts create mode 100644 lib/openinary.ts create mode 100644 lib/utils.ts create mode 100644 messages/en.json create mode 100644 messages/ru.json create mode 100644 messages/tr.json create mode 100644 next.config.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 postcss.config.mjs create mode 100644 prisma/schema.prisma create mode 100644 proxy.ts create mode 100644 public/file.svg create mode 100644 public/globe.svg create mode 100644 public/next.svg create mode 100644 public/vercel.svg create mode 100644 public/window.svg create mode 100644 tsconfig.json 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]/[category]/[slug]/page.tsx b/app/[locale]/[category]/[slug]/page.tsx new file mode 100644 index 0000000..f9661b7 --- /dev/null +++ b/app/[locale]/[category]/[slug]/page.tsx @@ -0,0 +1,272 @@ +import { getTranslations, setRequestLocale } from 'next-intl/server' +import { mockDb } from '@/lib/mockDb' +import Navbar from '@/components/Navbar' +import Footer from '@/components/Footer' +import ListingCard from '@/components/ListingCard' +import { notFound } from 'next/navigation' +import Image from 'next/image' +import { Phone, Globe, MapPin, Clock, Star, MessageSquare } from 'lucide-react' + +interface DetailPageProps { + params: Promise<{ locale: string; category: string; slug: string }> +} + +export default async function ListingDetailPage({ params }: DetailPageProps) { + const { locale, category, slug } = await params + setRequestLocale(locale) + + const t = await getTranslations('detail') + + const listing = await mockDb.getListingBySlug(slug) + if (!listing) { + notFound() + } + + // Fetch similar listings in same category (limit to 3, excluding current) + const allCategoryListings = await mockDb.getListings({ + categoryId: listing.categoryId + }) + const relatedListings = allCategoryListings + .filter(l => l.id !== listing.id) + .slice(0, 3) + + // Localized values + const name = + locale === 'ru' + ? listing.nameRu + : locale === 'en' + ? listing.nameEn + : listing.nameTr + + const description = + locale === 'ru' + ? listing.descriptionRu + : locale === 'en' + ? listing.descriptionEn + : listing.descriptionTr + + const priceSymbols = '₺'.repeat(listing.priceRange) + + // Format WhatsApp Link + const getWhatsAppLink = (number: string) => { + // Clean spaces, parenthesis, plus sign + const cleanNum = number.replace(/\D/g, '') + return `https://wa.me/${cleanNum}` + } + + return ( +
+ + +
+ + {/* Breadcrumb / Category Link */} +
+ marmaris local + / + + {locale === 'ru' ? listing.category?.nameRu : locale === 'en' ? listing.category?.nameEn : listing.category?.nameTr} + + / + {name} +
+ + {/* Hero Details Block */} +
+ +
+ + {/* Left: Gallery Panel */} +
+
+ 0 ? listing.images[0].url : 'https://images.unsplash.com/photo-1555396273-367ea4eb4db5?w=800&auto=format&fit=crop&q=80'} + alt={name} + fill + priority + className="object-cover" + /> +
+ + {/* Thumbnails if multiple images exist */} + {listing.images && listing.images.length > 1 && ( +
+ {listing.images.slice(1, 5).map((img, idx) => ( +
+ {`${name} +
+ ))} +
+ )} +
+ + {/* Right: Info Panel */} +
+ + {/* Badge & Title */} +
+
+ + {locale === 'ru' ? listing.category?.nameRu : locale === 'en' ? listing.category?.nameEn : listing.category?.nameTr} + + + {listing.isLocalApproved && ( + + ★ {t('approved')} + + )} +
+ +

+ {name} +

+ + {/* Stars and Price level */} +
+ {listing.rating && ( +
+ + {t('rating')}: {listing.rating.toFixed(1)} +
+ )} +
+ {t('price')}: {priceSymbols} +
+
+
+ + {/* Description */} +
+ {description} +
+ + {/* Contact Actions */} +
+

{t('contact')}

+ +
+ {listing.phone && ( + + + {t('call')} + + )} + + {listing.whatsapp && ( + + + {t('whatsapp')} + + )} +
+ + {/* External links */} +
+ {listing.website && ( + + + {t('website')} + + )} + + {listing.instagram && ( + + + {t('instagram')} + + )} +
+
+ +
+ +
+ + {/* Details footer (Hours & Location map) */} +
+ + {/* Address */} +
+
+ + {t('address')} +
+

+ {listing.address} +

+
+ + {/* Hours */} +
+
+ + {t('hours')} +
+
+ {listing.openingHours ? ( +

{(listing.openingHours as any).all || t('noHours')}

+ ) : ( +

{t('noHours')}

+ )} +
+
+ + {/* Map Frame */} + {listing.latitude && listing.longitude && ( +
+
+ + {t('location')} +
+
+