v7 - Service/Page Tasarim & Dark Mode Fix

t-{id} Tema Yapisi

Multi-Tenant Tema Sistemi - Layout, Settings, SEO, Dark Mode, Leonardo AI

1. Felsefe

t-{id} = Tenant temasi. Sadece ozel dosyalar burada.

simple = Fallback (mevcut, zaten var). t-{id}'de yoksa buradan alir.

Ornek: t-3/homepage.blade.php var -> onu kullan, t-3/blog/show.blade.php yok -> simple'dan al

2. Klasor Yapisi

resources/views/themes/
|
+-- simple/                         ← FALLBACK (mevcut, dokunma)
|   +-- layouts/
|   +-- homepage.blade.php
|
+-- t-1001/                         ← Muzibu
+-- t-2/                            ← Ixtif
+-- t-3/                            ← Panjur
    +-- layouts/
    |   +-- app.blade.php           (Ana layout)
    |   +-- header.blade.php        (Header)
    |   +-- footer.blade.php        (Footer)
    +-- homepage.blade.php          (STANDALONE)

ONEMLI: Modul view'lari AYRI konumda! "Modul View Path'leri" bolumune bak.

3. Layout Sistemi (Her Temada Olmali!)

Basit Anlatim

Her temanin kendi header ve footer'i olmali. Fallback degil, temaya ozel. Bu sayede her site kendi gorunumune sahip olur.

KURAL: Fallback Header/Footer YOK!

Her tema kendi layouts/header.blade.php ve layouts/footer.blade.php dosyasina sahip olmali. Baska temadan cekme!

Klasor Yapisi

resources/views/themes/t-{id}/
+-- layouts/
|   +-- app.blade.php      ← Ana layout (head, body wrapper)
|   +-- header.blade.php   ← Top bar + Navigation
|   +-- footer.blade.php   ← Footer + WhatsApp button
+-- homepage.blade.php             ← Standalone veya @extends

Container Padding Standardi

{{-- TUM sayfalarda ayni padding kullan --}}
<div class="container mx-auto px-4 sm:px-6 md:px-8 lg:px-12 xl:px-16 2xl:px-20">
    ...
</div>

// Bu pattern: header, footer, subheader, content tum bolumler icin gecerli!

Kullanim (Service/Page view'larinda)

{{-- Service/Page view'lari bu layout'u kullanir --}}
@extends('themes.t-3.layouts.app')

@section('module_content')
    <!-- Sayfa icerigi -->
@endsection

4. Service/Page Tasarim Pattern

YENI v7

Basit Anlatim

Service/Page sayfalari minimal subheader + icerik tasarimi kullanir. Sol taraf icerik, sag taraf gorsel ve iletisim. Kartlar kare gorsel + gradient overlay + beyaz yazi.

Minimal Subheader (Hero Degil!)

{{-- MINIMAL SUBHEADER - Sadece breadcrumb + title --}}
<section class="bg-gray-50 dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700">
    <div class="container mx-auto px-4 sm:px-6 md:px-8 lg:px-12 xl:px-16 2xl:px-20 py-4">
        <!-- Breadcrumb -->
        <nav class="text-sm text-gray-500 dark:text-gray-400 mb-2">
            @foreach($breadcrumbsArray as $index => $crumb)
                ...
            @endforeach
        </nav>
        <!-- Title -->
        <h1 class="text-2xl md:text-3xl font-bold font-heading text-gray-900 dark:text-white">{{ $title }}</h1>
    </div>
</section>

Iki Kolonlu Layout (show.blade.php)

{{-- 5 kolon grid: 3 icerik + 2 sidebar --}}
<div class="grid grid-cols-1 lg:grid-cols-5 gap-8 lg:gap-12">

    <!-- SOL: Ana Icerik (3 kolon) -->
    <article class="lg:col-span-3">
        <div class="prose prose-base max-w-none dark:prose-invert font-body">
            @parsewidgets($body ?? '')
        </div>
    </article>

    <!-- SAG: Gorsel + Iletisim (2 kolon) -->
    <aside class="lg:col-span-2">
        <div class="sticky top-24 space-y-6">
            <!-- Kare Gorsel -->
            <figure class="rounded-2xl overflow-hidden shadow-2xl">
                <a href="{{ $image->getUrl() }}" class="glightbox">
                    <img class="w-full aspect-square object-cover">
                </a>
            </figure>
            <!-- Iletisim Karti -->
            ...
        </div>
    </aside>

</div>

Iletisim Karti (ixtif.com Stili)

{{-- 2 kolonlu minimal tasarim --}}
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-lg border border-gray-200 dark:border-gray-700 p-4">
    <div class="grid grid-cols-2 gap-2">
        @if($sitePhone)
            <a href="tel:..." class="flex items-center gap-3 bg-gray-100 dark:bg-gray-700 hover:bg-gray-200 dark:hover:bg-gray-600 rounded-lg p-3 transition-all">
                <i class="fa-solid fa-phone text-primary-600"></i>
                <div>
                    <div class="text-xs text-gray-500">Telefon</div>
                    <div class="text-sm font-semibold text-gray-900 dark:text-white">{{ $sitePhone }}</div>
                </div>
            </a>
        @endif
        @if($whatsappUrl)
            <a href="{{ $whatsappUrl }}" class="flex items-center gap-3 ...">
                <i class="fa-brands fa-whatsapp text-green-600"></i>
                <div>
                    <div class="text-xs">WhatsApp</div>
                    <div class="text-sm font-semibold">Hemen Yazin</div>
                </div>
            </a>
        @endif
    </div>
</div>

Kare Kart Pattern (index + related services)

{{-- Kare gorsel + gradient + beyaz yazi --}}
<div class="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4 md:gap-6">
    @foreach ($items as $item)
        <a href="..." class="group relative aspect-square rounded-xl overflow-hidden shadow-lg hover:shadow-xl transition-all duration-300">

            <!-- Gorsel -->
            <img class="w-full h-full object-cover group-hover:scale-105 transition-transform duration-500">

            <!-- Gradient Overlay -->
            <div class="absolute inset-0 bg-gradient-to-t from-black/70 via-black/20 to-transparent"></div>

            <!-- Baslik (Beyaz) -->
            <div class="absolute bottom-0 left-0 right-0 p-4">
                <h2 class="text-base md:text-lg font-semibold text-white font-heading drop-shadow-lg">{{ $title }}</h2>
            </div>

        </a>
    @endforeach
</div>

// DIKKAT: Kart hover'da ziplamasin!
// hover:-translate-y-2 KULLANMA!
// Sadece: hover:shadow-xl ve group-hover:scale-105 (gorsel icin)

Font Class'lari

font-heading

Basliklar icin (h1, h2, h3, kart basliklari)

font-body

Icerik metni icin (paragraf, prose)

5. Settings Kullanim Kurallari

YASAK: Fallback Degerler

// YANLIS - YAPMA!
$siteName = setting('site_name') ?: 'Varsayilan Ad';
$sitePhone = setting('site_phone') ?: '0212 123 45 67';

DOGRU: Kosullu Gosterim

// Fallback yok, bos ise gizle
@if($sitePhone)
<a href="tel:{{ $sitePhone }}">{{ $sitePhone }}</a>
@endif

6. SEO Meta Tag

<!-- head icine ekle -->
<x-seo-meta />

// Dosya: resources/views/components/seo-meta.blade.php

7. Dark/Light Mode

GUNCELLENDI v7

Bilinen Sorun & Cozum

Sayfa yenilendiginde dark mode kayboluyordu. Alpine.js'in baslangic degeri sistem tercihini kontrol etmiyordu.

1. FOUC Onleme (head icinde)

<script>
    if (localStorage.getItem('darkMode') === 'true' ||
        (!localStorage.getItem('darkMode') &&
         window.matchMedia('(prefers-color-scheme: dark)').matches)) {
        document.documentElement.classList.add('dark');
    }
</script>

2. Alpine.js Body (DOGRU - v7)

<body x-data="{
          darkMode: localStorage.getItem('darkMode') === 'true' ||
                   (!localStorage.getItem('darkMode') &&
                    window.matchMedia('(prefers-color-scheme: dark)').matches),
          mobileMenu: false
      }"
      x-init="
          document.documentElement.classList.toggle('dark', darkMode);
          $watch('darkMode', val => {
              localStorage.setItem('darkMode', val);
              document.documentElement.classList.toggle('dark', val);
          });
      "
      :class="{ 'dark': darkMode }"
      class="bg-white dark:bg-slate-900 ...">

Kritik: x-init icinde document.documentElement.classList.toggle('dark', darkMode) olmali!

YANLIS Pattern (Eski)

// BU YANLIS! Sistem tercihini kontrol etmiyor
x-data="{ darkMode: localStorage.getItem('darkMode') === 'true' }"
x-init="$watch('darkMode', val => { ... })"

// Sorun: localStorage bos ise darkMode = false olur
// Sistem dark mode'da olsa bile light gosterir

3. Tailwind Config

tailwind.config = {
    darkMode: 'class',  // class bazli!
}

8. GLightbox (Gorsel Lightbox)

YENI v7

CDN Ekleme (app.blade.php head)

{{-- GLightbox CSS --}}
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/glightbox@3.2.0/dist/css/glightbox.min.css">

JS Init (body sonu)

<script src="https://cdn.jsdelivr.net/npm/glightbox@3.2.0/dist/js/glightbox.min.js"></script>
<script>
    const lightbox = GLightbox({
        selector: '.glightbox',
        touchNavigation: true,
        loop: true,
        closeButton: true
    });
</script>

Kullanim

{{-- Tek gorsel --}}
<a href="{{ $image->getUrl() }}" class="glightbox">
    <img src="{{ $image->getUrl('medium') }}">
</a>

{{-- Galeri (ayni data-gallery ile grupla) --}}
@foreach($galleryImages as $image)
    <a href="{{ $image->getUrl() }}"
       class="glightbox"
       data-gallery="service-gallery">
        <img src="{{ $image->getUrl('thumb') }}">
    </a>
@endforeach

9. Leonardo AI Gorsel Uretimi

Helper Fonksiyon

// Queue'ya gonder
generate_ai_cover($service, $service->title, 'service');

// Dosya: Modules/MediaManagement/helpers.php

Senkron Uretim

$leonardo = app(\App\Services\Media\LeonardoAIService::class);
$imageData = $leonardo->generateFromPrompt($prompt, [
    'width' => 1472, 'height' => 832, 'style' => 'stock_photo'
]);

if ($imageData) {
    $model->addMediaFromUrl($imageData['url'])
        ->toMediaCollection('hero');
}

10. Modul View Path'leri

Modules/Page/resources/views/themes/
+-- simple/homepage.blade.php      ← Fallback
+-- t-3/homepage.blade.php         ← Panjur ozel

Modules/Service/resources/views/themes/
+-- simple/index.blade.php         ← Fallback (tum tenantlar)
+-- simple/show.blade.php

Modules/Blog/resources/views/themes/
+-- simple/blog/index.blade.php
+-- simple/blog/show.blade.php

Service fallback: Modules/Service/resources/views/themes/simple/ klasorundeki dosyalar TUM tenantlar icin gecerli. t-3 icin ozel view yok, simple kullanilir.

Yeni Tema Checklist