Files
marmarislocal/docs/prd-2.md
T

9.1 KiB
Raw Blame History

Faz 2 Planı — Marmaris Local

Önceki: prd.md (MVP — tamamlandı) Bu doküman: MVP sonrası geliştirme sırası

Sıralama mantığı: önce trafik getiren şey (içerik/SEO), sonra geleni tutan şey (etkileşim), sonra parayı getiren şey (gelir modeli — ama listeleme sayısı bir eşiğe ulaşmadan anlamsız), en altta her an eklenebilecek düşük efor altyapı işleri.


Faz 2.1 — İçerik / SEO Motoru (öncelik: yüksek, ilk başlanacak)

Directory sitesi tek başına trafik çekmez — kürasyonlu içerik olmadan sadece Google'ın zaten indekslediği işletmelerin bir kopyası olursun. Bu faz olmadan diğerlerinin anlamı sınırlı.

Blog

model BlogPost {
  id            String   @id @default(cuid())
  slug          String   @unique
  titleTr       String
  titleEn       String
  titleRu       String
  contentTr     String   // MDX/rich text
  contentEn     String
  contentRu     String
  coverImage    String?  // Openinary
  tags          String[]
  relatedListingIds String[] // Listing'lere internal link için

  publishedAt   DateTime?
  createdAt     DateTime @default(now())
  updatedAt     DateTime @updatedAt
  deletedAt     DateTime?
}
  • Route: /blog, /blog/[slug]
  • Admin: /admin/blog — rich text editör (Tiptap öneri, shadcn ile uyumlu)
  • Her yazıda ilgili Listing'lere otomatik link kartı (internal linking = SEO)
  • schema.org Article structured data

İlk içerik önerisi (mock/gerçek karışık başlanabilir): "Marmaris'te Nerede Yenir 2026", "Gün Batımı İçin 5 Mekan", "İçmeler mi Yat Limanı mı — Nerede Kalınır"

Kürasyon Listeleri (Koleksiyonlar)

model Collection {
  id           String   @id @default(cuid())
  slug         String   @unique
  titleTr      String
  titleEn      String
  titleRu      String
  descriptionTr String
  descriptionEn String
  descriptionRu String
  coverImage   String?

  listings     Listing[] @relation("CollectionListings")

  createdAt    DateTime @default(now())
  updatedAt    DateTime @updatedAt
}
  • Route: /seckiler, /secki/[slug]
  • Admin: /admin/collections — listeleme seç (multi-select), sürükle-bırak sıralama
  • Örnek koleksiyonlar: "Aile Dostu 5 Restoran", "Bütçe Dostu Mekanlar", "Deniz Kenarında Kahvaltı"
  • Bu, "Yerel Onaylı" mührünün doğal uzantısı — tekil rozetten küratörlü gruplamaya geçiş

RSS

  • /feed.xml — blog yazıları + yeni eklenen "Yerel Onaylı" listelemeler
  • Öncelik gerekçesi: kendi başına trafik kanalı değil ama Google News / ileride newsletter otomasyonu (n8n) için altyapı — blog ile aynı sprintte, 1 saatlik ek iş

Faz 2.2 — Etkileşim (öncelik: orta, 2.1 ile paralel gidebilir)

Auth gerektirmeyen, dönüşümü ucuza artıran işler.

Favorilere Ekle

  • Backend yok — client-side localStorage, savedListingIds: string[]
  • Route: /kaydedilenler — localStorage'daki ID'leri okuyup listeler
  • Paylaşılabilir link: /kaydedilenler?ids=abc,def,ghi — arkadaşla link paylaşımı, auth şart değil

WhatsApp Paylaş

  • Her listeleme detay sayfasında wa.me/?text=... deep link buton
  • Şema değişikliği yok, ~30 dakikalık iş

Instagram Feed (her listeleme sayfasında)

Sadece anasayfa değil — Instagram hesabı olan her listeleme (kategori fark etmez) kendi detay sayfasında son gönderilerini gösterir + profile giden link.

Veri kaynağı: RapidAPI üzerinden bir Instagram scraper endpoint'i (ör. "Instagram Scraper API2" / "Instagram Data" — RapidAPI marketplace'te birden fazla sağlayıcı var, fiyat/limit karşılaştırıp seçilecek). Instagram'ın resmi Graph API'si business-verified hesap + Facebook app review gerektirdiği için, işletme sahiplerinden bu izni almak pratik değil — RapidAPI scraper daha az sürtünmeli.

Canlı istekte RapidAPI çağırma — YAPMA. Her sayfa görüntülemesinde çağırırsan hem yavaş hem RapidAPI faturası trafiğe bağlı, öngörülemez olur. Bunun yerine 24 saatte bir çalışan bir arka plan job'ı ile önbelleğe al:

model InstagramFeedCache {
  id         String   @id @default(cuid())
  listingId  String   @unique
  handle     String
  posts      Json     // [{ imageUrl, caption, permalink, postedAt }]
  fetchedAt  DateTime @default(now())

  listing    Listing  @relation(fields: [listingId], references: [id])
}
  • Listing.instagram alanı zaten PRD'de vardı (handle/URL) — bunu kaynak olarak kullan
  • Senkron mekanizması: n8n'de günlük (24h) tetiklenen bir workflow — instagram dolu olan tüm Listing'leri gez, her biri için RapidAPI'yi çağır, sonucu POST /api/cron/instagram-sync (secret header ile korunan internal endpoint) üzerinden InstagramFeedCache'e yaz. n8n zaten elinde (auto2.ayris.tech), bu tam onun işi.
  • Frontend sadece DB'den okur, hiçbir zaman canlı RapidAPI çağrısı yapmaz — sayfa yükü hızlı kalır
  • fetchedAt 24 saatten eskiyse UI'da sessizce eski veriyi göstermeye devam et (job bir sonraki döngüde tazeleyecek), kullanıcıya hata gösterme

Menüyü Gör Butonu

Restoran/bar/club tarzı kategorilerde, işletmenin kendi menü sayfası/PDF'i varsa, listeleme detay sayfasında "Menüyü Gör" butonu → yeni sekmede işletmenin kendi sitesine/menü linkine gönderir.

// Listing modeline eklenecek alan
menuUrl   String?
  • Kategoriye göre kod içinde zorlama yok — buton sadece menuUrl doluysa görünür, admin doğal olarak bunu sadece ilgili kategorilerde dolduracak
  • Admin formunda (/admin/listings/[id]) yeni alan: "Menü Linki (opsiyonel)"

"Yeni Eklenenler" Widget

  • Şema değişikliği yok — Listing.findMany({ orderBy: createdAt desc, take: 6 })
  • Anasayfa + kategori sayfalarında taze içerik sinyali

Faz 2.3 — Gelir Modeli: Öne Çıkan Listeleme (öncelik: koşullu — belirli bir listeleme sayısına ulaşınca anlamlı)

Ön koşul: En az ~30-40 aktif "Yerel Onaylı" listeleme olmadan bu faza girmeye gerek yok — az listelemeyle "öne çıkan" kavramının hiçbir ayırt edici gücü olmaz.

// Listing modeline eklenecek alanlar
isFeatured    Boolean   @default(false)
featuredUntil DateTime? // otomatik süre dolumu için
  • Kategori/anasayfa sıralamasında isFeatured önce gelir
  • Admin toggle + tarih seçici (/admin/listings/[id])
  • Gelir mekanizması: Ayris Tech'in mevcut 40+ müşteri ilişkisiyle doğal satış kanalı — ayrı bir ödeme altyapısı MVP'de gerekmez, manuel faturalama yeterli

Faz 2.4 — Altyapı (öncelik: düşük efor, istenilen an araya sokulabilir)

PWA

  • manifest.json + service worker (next-pwa veya manuel)
  • "Ana ekrana ekle" — turist telefonda kullanıyor, pratik fark yaratır

Analytics

  • Kendi VPS'teki self-hosted panel (Plausible-tarzı, zaten var) — yeni SaaS ödemesi gerekmiyor
  • Takip edilecek event'ler: favori ekleme, WhatsApp paylaşım tıklaması, QR kaynak trafiği, kategori bazlı arama

Önerilen Sıra

  1. Blog + Koleksiyonlar (2.1) — SEO motoru olmadan geri kalanının önemi düşük
  2. Menü butonu + Instagram feed + Favoriler + WhatsApp paylaş + Yeni Eklenenler (2.2) — düşük-orta efor, hızlı kazanım, 2.1 ile aynı sprint'te bile yapılabilir. Instagram feed'in n8n cron tarafı ayrı bir iş parçası olarak planlanmalı (senkron job önce kurulmalı, sonra frontend)
  3. PWA + Analytics (2.4) — ne zaman uygun olursa, bağımsız işler
  4. Öne Çıkan Listeleme (2.3) — listeleme sayısı eşiğe ulaşınca devreye al

Env Değişkenleri (Faz 2 eklentileri)

RAPIDAPI_KEY=""
RAPIDAPI_INSTAGRAM_HOST=""          # seçilen sağlayıcıya göre (ör. instagram-scraper-api2.p.rapidapi.com)
CRON_SECRET=""                      # /api/cron/instagram-sync endpoint'ini korumak için

Kararlar (önceki açık sorulardan)

  • Blog içeriği: DeepSeek ile üretilip otomasyonla (n8n) yayınlanacak — akış detayı ayrıca kurulacak
  • Koleksiyon kapak görselleri: stok veya yerinde çekim fark etmiyor, ikisi de kullanılabilir
  • Öne çıkan listeleme fiyatlandırması: aylık sabit ücret (süre bazlı değil)
  • RapidAPI sağlayıcısı: free-tier bir model kullanılacak

Free-tier RapidAPI konusunda dikkat edilecek nokta: RapidAPI'deki Instagram scraper'ların çoğunda (ör. "Instagram Scraper API2", "Instagram Looter2") free/basic plan genelde aylık ~50-100 istek civarında sınırlı. 24 saatte bir senkron ile bile, listeleme sayısı arttıkça (30-40 listeleme × günlük = ~900-1200 istek/ay) free tier hızla yetersiz kalır. Pratik yaklaşım:

  • Az listelemeyle (MVP/Faz 2 başlangıcı) free tier rahatça yeter
  • Listeleme sayısı büyüdükçe ya senkron sıklığını düşür (24h yerine 48-72h), ya sadece isLocalApproved olanları senkronla, ya da o noktada ücretli tiere geç
  • İlk kurulumda 2-3 free-tier sağlayıcıyı denemek (aynı endpoint şeklini farklı sağlayıcıdan alacağın için InstagramFeedCache şeması sağlayıcıdan bağımsız kalacak şekilde tasarlandı) makul bir başlangıç

Açık Sorular

  • Free-tier adaylarından hangisi seçilecek — birkaçını test edip (aynı listeleme üzerinde) veri kalitesi/limit karşılaştırması yapılabilir