# Progress Log

Faz tanımları için [ROADMAP.md](./ROADMAP.md). Bu dosya ne yapıldığını, ne yapılmadığını ve bilinen sınırlamaları takip eder — faz atlarken buraya bakıp neyin gerçek neyin stub olduğunu hatırlamak için.

## App Store Connect + RevenueCat kurulumu (2026-08-20)

**App Store Connect** (API ile, gerçek üretim hesabına karşı):
- Store metinleri yazıldı: alt başlık, açıklama, anahtar kelimeler, tanıtım metni, destek/pazarlama/gizlilik URL'leri, telif hakkı
- Kategori: Food & Drink
- En son build (v10) versiyona bağlandı
- Subscription group "Menulio Pro" + iki ürün oluşturuldu: `menu_pro_monthly`, `menu_pro_yearly` (Türkçe isim/açıklama ile)
- Fiyatlandırma kullanıcı tarafından App Store Connect UI'dan girildi: aylık 250 TL, yıllık 2500 TL
- **Kullanıcının tamamlaması gereken:** Yaş derecelendirmesi anketi, en az 1 ekran görüntüsü — API'nin güvenilir şekilde dolduramadığı/dolduramayacağı alanlar

**RevenueCat** (proje zaten `proj0d3bc1c1` olarak mevcuttu, API ile tamamlandı):
- Gerçek App Store ürünleri (`menu_pro_monthly`, `menu_pro_yearly`) "Menul.io (App Store)" app'ine eklendi
- "Menulio Pro" entitlement'ına bağlandı
- "default" offering'in Monthly/Yearly paketlerine iliştirildi

**Backend:** `POST /subscription/webhook` gerçek mantıkla yazıldı — Authorization header'ı (shared secret) doğruluyor, RevenueCat event tipini `subscriptions.status`'a çeviriyor (`INITIAL_PURCHASE`/`RENEWAL`/`PRODUCT_CHANGE`/`UNCANCELLATION` → active, `BILLING_ISSUE` → grace_period, `EXPIRATION` → suspended; `CANCELLATION` tek başına suspend etmiyor, sadece auto-renew kapanıyor). Gerçek Supabase'e karşı uçtan uca test edildi.

**Tamamlandı (devamı, 2026-08-20):**
- `REVENUECAT_WEBHOOK_SECRET` env değişkeni API servisine eklendi, RevenueCat Dashboard'da webhook (URL + Authorization header) kullanıcı tarafından yapılandırıldı.
- Mobile: `react-native-purchases` SDK'sı kuruldu (`apps/mobile/src/lib/purchases.ts`). `Purchases.configure({ appUserID: restaurantId })` restaurantId'nin bilindiği her noktada (login sonrası, cold-start rehydration, onboarding restoran oluşturma) çağrılıyor — webhook'un `app_user_id`'yi doğru eşleştirmesi için bu şart.
- `apps/mobile/src/app/account/subscription.tsx` — paywall ekranı: mevcut entitlement durumu, offering paketleri (gerçek mağaza fiyatlarıyla), satın alma, restore. Hesap ekranından linklendi.

**Tamamlandı (devamı, 2026-08-20):**
- App Store Connect: her iki üründe de 7 günlük ücretsiz deneme (introductory offer, `ONE_WEEK`/`FREE_TRIAL`) Türkiye territory'sinde tanımlandı.
- Mobile: `onPublish` artık önce Pro entitlement kontrolü yapıyor — aktif değilse yayınlama isteğini API'ye hiç göndermeden paywall'a yönlendiriyor (bu, PRD §15'teki "abonelik sona erince public menü kapanır, yeniden ödeme ile aktifleşir" davranışını doğal olarak da kapsıyor, çünkü her publish denemesi tekrar kontrol ediyor). Paywall ekranı, introductory offer'ı olan paketlerde "7 gün ücretsiz dene" rozetini gösteriyor.

**Tamamlandı (devamı, 2026-08-20 — gerçek cihaz/TestFlight testi sırasında bulunan bug'lar):**
- `eas.json`'ın `production` build profili kendi sabit `env` bloğunu kullanıyor — local `.env` cloud/local EAS build'lerine hiç gitmiyor. `EXPO_PUBLIC_REVENUECAT_API_KEY` bu listede yoktu, "There is no singleton instance" hatasına sebep oluyordu. Eklendi.
- `apps/web/src/middleware.ts`'deki trailing-slash düzeltmesi gibi, entitlement kontrolü de gerçek bir bug barındırıyordu: RevenueCat'teki entitlement identifier'ı `U+2024` (normal nokta değil, "ONE DOT LEADER") içeriyor — kodumuzdaki sabit string `"Menul.io Pro"` (normal nokta) hiçbir zaman eşleşmiyordu. Gerçek bir sandbox trial satın alması RevenueCat'te "active" görünürken uygulama hâlâ "Ücretsiz plan" gösteriyordu. RevenueCat API'si entitlement identifier'ını sonradan değiştirmeye izin vermiyor (405) — kod tarafında düzeltildi: artık spesifik bir isimle değil, "herhangi bir aktif entitlement var mı" mantığıyla kontrol ediyor (bu uygulamada tek entitlement türü olduğu için güvenli).
- Gerçek satın alma/trial akışı gerçek cihazda (TestFlight, `eas build --local`) doğrulandı — `menu_pro_monthly` için gerçek bir sandbox trial satın alması RevenueCat dashboard'unda göründü.

## Faz 0 — Altyapı ✅ (2026-08-19)

- pnpm monorepo: `apps/api` (Fastify), `apps/web` (Next.js), `apps/mobile` (Expo Router), `packages/shared`
- `apps/api`: health check + PRD §23'teki endpointlerin stub'ları
- `apps/web`: host-header tabanlı wildcard subdomain middleware, `/menu/[slug]` route
- `apps/mobile`: Expo Router iskeleti, onboarding stub ekranları
- `supabase/migrations/20260819000000_initial_schema.sql`: PRD §22'deki 17 tablo, RLS açık, owner-scoped + published-menu public read policy'leri
- `supabase/seed.sql`: 5 başlangıç teması
- Doğrulama: `tsc --noEmit` tüm app'lerde temiz, API `/health` gerçekten çalıştırılıp test edildi

## Faz 1 — Manuel akış ✅ (2026-08-19)

Hedef: Restoran sahibi mobil uygulamada elle veri girip gerçek bir QR menüyü yayınlayabilsin. **Karşılandı ve gerçek Supabase Cloud projesine karşı canlı test edildi.**

- [x] API: auth middleware (Supabase JWT doğrulama, `requireAuth`/`requireRestaurantMember`) + restaurant CRUD
- [x] API: menu/category/item CRUD + publish/unpublish (`menus`, `menu-categories`, `menu-items` route'ları)
- [x] API: QR üretimi (PNG/SVG, `/q/:id` stabil redirect URL ile — PRD §12)
- [x] API: `/me/restaurant` — mobile cold-start rehydration için
- [x] Supabase: Faz 1 RLS policy migration'ı (`20260819010000_public_menu_access.sql`)
- [x] Web: gerçek public menü render (Supabase'ten canlı veri, 5 zengin tema desteği, canlı arama, sticky kategori barı, ürün detay modalı)
- [x] Web: `/q/[id]` QR redirect route'u
- [x] Mobile: auth ekranları (email login/register) + Supabase client (AsyncStorage session)
- [x] Mobile: restoran onboarding formu → API'ye bağlı
- [x] Mobile: menü editörü (kategori/ürün CRUD, yayınla butonu) — `apps/mobile/src/app/menu/index.tsx`
- [x] Mobile: QR ekranı (üret, göster, native share) — `apps/mobile/src/app/menu/qr.tsx`

## Faz 2 — AI Menu Scanner ✅ (2026-08-19)

Hedef: Menü fotoğrafını çekip/yükleyip AI ile saniyeler içinde dijitalleştirmek, onaylayıp canlı menüye aktarmak.

- [x] Shared: AI çıkarma (`AiExtractedCategory`, `AiExtractedItem`), yanıt (`AiImportResponse`) ve uygulama (`AiImportApplyPayload`) veri sözleşmeleri.
- [x] API: `apps/api/src/lib/ai-scanner.ts` — Gemini 1.5 Flash Vision, OpenAI GPT-4o Vision ve akıllı mutfak sezgisel fallback motoru.
- [x] API: `POST /menus/:id/import-ai` — menü görselini analiz eder, `ai_imports` ve `ai_import_items` denetim loglarını oluşturur, confidence skorları hesaplar.
- [x] API: `GET /ai-imports/:id` — kaydedilmiş import durumunu döner.
- [x] API: `POST /ai-imports/:id/apply` — AI Review ekranından onaylanan/düzenlenen kategori ve ürünleri menüye (`menu_categories`, `menu_items`) topluca yazar.
- [x] Mobile: `expo-image-picker` entegrasyonu (kamera & galeri izinleri ve fotoğraf seçimi).
- [x] Mobile: `apps/mobile/src/app/(onboarding)/scan.tsx` — Menü Fotoğrafı Çek / Galeriden Seç ve aşamalı AI analiz animasyonu ekranı (Adım 2 & 3).
- [x] Mobile: `apps/mobile/src/app/(onboarding)/ai-review.tsx` — AI Review & Onay ekranı (Adım 4):
  - Kategori bazlı ürün listesi
  - Düşük güven skorlu öğeler için sarı uyarı rozeti (`⚠️ Düşük Güven (Fiyatı Kontrol Edin)`)
  - Anında ürün adı, fiyat ve açıklama düzenleme
  - Kategori/ürün silme ve ekleme
  - "✓ Menüyü Onayla ve Kaydet" butonu ile menüye aktarma
- [x] Mobile: `apps/mobile/src/app/menu/index.tsx` içerisine "✨ AI ile Menü Fotoğrafı Tara" tetikleyicisi.
- [x] Mobile: `apps/mobile/src/app/account/index.tsx` — Hesabım & Restoran Ayarları ekranı:
  - Kullanıcı profili ve aktif restoran durumu
  - Restoran adı, telefon ve adres güncelleme (`PATCH /restaurants/:id`)
  - Canlı menü linki kopyalama ve tarayıcıda açma
  - 5 Temadan birini seçip menüye anında uygulama (`GET` / `PUT /restaurants/:id/theme`)
  - Abonelik plan durumu
  - Çıkış yapma (Sign out) ve oturum temizleme

## Faz 3 — Web Frontend & 5 Menü Şablonu Engine ✅ (2026-08-20)

- [x] Web: `apps/web/src/app/page.tsx` — Lüks Menulio SaaS Landing Page (Hero, 5 Şablon vitrini, Nasıl Çalışır, İstatistikler, Canlı Demo linkleri).
- [x] Web: `apps/web/src/lib/theme.ts` — 5 Ayrı Tasarım Şablonu Tanımları:
  1. 🌟 **Elegant Gold** (Fine dining & lüks restoranlar - Altın & Krem, serif tipografi)
  2. 💎 **Modern Sapphire** (Kafe & Fast-Casual - Mavi safir & beyaz, 2'li grid layout)
  3. 🌙 **Luxury Dark** (Gece kulübü & Kokteyl bar - Obsidian & Kehribar lüks mod)
  4. ❄️ **Nordic Minimal** (Butik kahveci & fırın - Sade monokrom liste mizanpajı)
  5. 🍷 **Classic Bistro** (Meyhane & Trattoria - Toskana bordo & rustik doku)
- [x] Web: `apps/web/src/components/TemplateGalleryClient.tsx` & `/templates` — İnteraktif şablon galerisi ve canlı telefon simülatörü.
- [x] Web: `apps/web/src/app/demo/page.tsx` — Tam ekran interaktif demo menü.
- [x] Mobil & API: Yerel geliştirme ortamına uyumlu URL ve QR mimarisi (`http://localhost:3000/menu/[slug]`).
- [x] Mobil: `ThemeSelectorSheet.tsx` — Menü editöründen tek tıkla 5 şablondan birini seçme ve anında aktifleştirme.
- [x] Mobil: Ürün Görseli Ekleme & Düzenleme (`expo-image-picker` ile `AddItemModal` ve `ItemDetailSheet` içinde galeri seçimi ve canlı önizleme).
- [x] Mobil: `MenuItemCard.tsx` — Menü ürün kartlarında görsel küçük resim (thumbnail) desteği.
- [x] Mobil: `AccountScreen.tsx` — Restoran Logosu yükleme/değiştirme ve detaylı restoran bilgileri güncelleme.
- [x] Mobil: Alt barda kalıcı "👁️ Önizle" butonu ile tek tıkla canlı menüyü tarayıcıda açma.
- [x] Web: `apps/web/src/components/PublicMenuClient.tsx` — Anında arama, masa numarası parametresi (`?table=X`), garson çağır / hesap iste floating barı, ürün detay modalı ve canlı tema değiştirici çekmecesi.

**Kapsam dışı bırakılanlar (bilinçli, sonraki fazlara ertelendi):**
- Apple/Google login (V1.5+) — sadece email auth
- Abonelik/RevenueCat (Faz 5) — bu fazda erişim kısıtı yok

## Production altyapı + canlı hata ayıklama (2026-08-20)

Bu tarihte proje production'a alınmış halde bulundu: `menul.io` (Next.js public web), `api.menul.io` (Fastify API), Coolify (host: Traefik değil **Caddy**), Cloudflare (DNS/proxy), gerçek Supabase Cloud projesi (`fwahdopsyvryvjaqande`), git.ayris.tech (self-hosted Gitea, `main`'e push → Coolify otomatik deploy). Custom domain özelliği (`ad.ayris.tech` → `menul.io`) canlıda kurulup test edildi ve çalışır hale getirildi. Bu süreçte bulunup düzeltilen gerçek prod bug'ları:

- **API `SUPABASE_SERVICE_ROLE_KEY` placeholder'dı** — `api.menul.io` her auth isteğini 401'liyordu (mobilde sürekli login döngüsüne sokuyordu). Kullanıcı gerçek key'i env'e girdi.
- **Fastify default `bodyLimit` 1 MiB** — AI menü fotoğrafı yüklerken "Request body is too large" hatası (model/key ile ilgisiz). `apps/api/src/index.ts`'te 20 MiB'e çıkarıldı.
- **`domains.ts` DNS-CNAME doğrulaması proxied domainlerde yapısal olarak asla çalışmaz** — Cloudflare proxy (turuncu bulut) gerçek CNAME hedefini public DNS'ten gizliyor (`ENODATA`). `/verify` her çağrıldığında zaten doğrulanmış bir domain'i "pending"e geri düşürüyordu. Artık zaten verified olan bir domain bir daha downgrade edilmiyor. **Kalıcı çözüm hâlâ eksik:** gerçek doğrulama Cloudflare Custom Hostnames API'sinin `ssl.status`'una bakmalı — bunun için Cloudflare API token + zone ID gerekiyor (henüz alınmadı).
- **`apps/web/src/middleware.ts` trailing slash bug'ı** — root path istekleri `/menu/<slug>/` (sonda slash'lı) üretiyordu, `[slug]` route'u eşleştiremiyordu → her custom domain/subdomain kök isteği "Menü Bulunamadı" veriyordu. Düzeltildi.
- **`PUT /restaurants/:id/theme` crash'i** — tanımsız `themeData` değişkeni kullanılıyordu (main'de önceden var olan `tsc` hatası, muhtemelen build type-check adımından geçmeden deploy edilmişti). Eksik `themes` sorgusu eklendi.
- **Coolify custom domain routing:** her custom domain, ilgili Next.js servisine Coolify panelinden elle eklenmesi + redeploy gerektiriyor (otomasyon yok). Wildcard subdomain (`*.menul.io`) hiç kurulmadı çünkü **gerekmiyor** — restoran menüleri `menul.io/menu/{slug}` path'i üzerinden çalışıyor, alt-domain mimarisi kullanılmıyor.

**Sonraki adım (otomasyon):** Cloudflare API token + zone ID alınırsa, `POST /restaurants/:id/domains` restoran custom domain eklediğinde otomatik Custom Hostname oluşturup gereken DNS talimatlarını döndürecek, `/verify` de Cloudflare'in kendi SSL durumuna bakacak şekilde güncellenebilir — böylece bugünkü manuel Cloudflare dashboard adımları (Fallback Origin, TXT kayıtları, Coolify domain ekleme) her yeni restoran için tekrarlanmaz.
