Files
menulio/docs/PROGRESS.md
T

13 KiB
Raw Blame History

Progress Log

Faz tanımları için 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.

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

  • Shared: AI çıkarma (AiExtractedCategory, AiExtractedItem), yanıt (AiImportResponse) ve uygulama (AiImportApplyPayload) veri sözleşmeleri.
  • API: apps/api/src/lib/ai-scanner.ts — Gemini 1.5 Flash Vision, OpenAI GPT-4o Vision ve akıllı mutfak sezgisel fallback motoru.
  • 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.
  • API: GET /ai-imports/:id — kaydedilmiş import durumunu döner.
  • 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.
  • Mobile: expo-image-picker entegrasyonu (kamera & galeri izinleri ve fotoğraf seçimi).
  • 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).
  • 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
  • Mobile: apps/mobile/src/app/menu/index.tsx içerisine " AI ile Menü Fotoğrafı Tara" tetikleyicisi.
  • 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)

  • 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).
  • 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)
  • Web: apps/web/src/components/TemplateGalleryClient.tsx & /templates — İnteraktif şablon galerisi ve canlı telefon simülatörü.
  • Web: apps/web/src/app/demo/page.tsx — Tam ekran interaktif demo menü.
  • Mobil & API: Yerel geliştirme ortamına uyumlu URL ve QR mimarisi (http://localhost:3000/menu/[slug]).
  • Mobil: ThemeSelectorSheet.tsx — Menü editöründen tek tıkla 5 şablondan birini seçme ve anında aktifleştirme.
  • Mobil: Ürün Görseli Ekleme & Düzenleme (expo-image-picker ile AddItemModal ve ItemDetailSheet içinde galeri seçimi ve canlı önizleme).
  • Mobil: MenuItemCard.tsx — Menü ürün kartlarında görsel küçük resim (thumbnail) desteği.
  • Mobil: AccountScreen.tsx — Restoran Logosu yükleme/değiştirme ve detaylı restoran bilgileri güncelleme.
  • Mobil: Alt barda kalıcı "👁️ Önizle" butonu ile tek tıkla canlı menüyü tarayıcıda açma.
  • 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.techmenul.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.