# Acente Yönetim Sistemi — Proje Dokümantasyonu

Çok kiracılı (multi-tenant) bir **seyahat acentesi yönetim platformu**. Acentelerin tur/ürün satışı, müşteri (CRM), tedarikçi, satış, finans ve kendilerine ait web sitesi yönetimi yapabildiği; platform sahibinin (admin) acenteleri onayladığı bir SaaS uygulamasıdır.

## Teknoloji Yığını

| Katman | Teknoloji |
|--------|-----------|
| Framework | Laravel 12 (PHP ^8.2) |
| Yetkilendirme | spatie/laravel-permission ^6.21 (rol & izin) |
| Görsel işleme | intervention/image ^3.11 |
| Veritabanı | SQLite (varsayılan, `database/database.sqlite`) |
| Frontend | Blade şablonları + Vite, CSS/JS (`resources/css/app.css`, `resources/js/app.js`) |
| Geliştirme araçları | Pint (kod stili), Pail (log), Sail, PHPUnit ^11, Faker, Mockery |
| Dil | Varsayılan locale `tr`, fallback `en` |

> Geliştirme sunucusu: `composer dev` → `php artisan serve` + `queue:listen` + `pail` + `npm run dev` aynı anda çalışır.

## Üç Ana Alan (Domain)

Uygulama üç ayrı arayüz/rol etrafında kurgulanmıştır. Her birinin kendi route dosyası, controller namespace'i, view klasörü ve auth middleware'i vardır.

### 1. Admin (Platform Yönetimi) — `routes/admin.php`
- **Prefix:** `/admin`, **Middleware:** `admin.auth`
- Acente onaylama/reddetme/aktive etme, belge yönetimi
- Kullanıcı yönetimi (CRUD, durum değiştirme, şifre sıfırlama, Excel/PDF dışa aktarma — `can:users.*` izinleriyle)
- Rol yönetimi (CRUD)
- Controller: `app/Http/Controllers/Admin/`, View: `resources/views/admin/`

### 2. Agency (Acente Paneli) — `routes/agency.php` *(606 satır, en geniş alan)*
- **Prefix:** `/agency`, **Middleware:** `agency.auth`
- Bir kullanıcı birden çok acenteye bağlı olabilir → **acente seçimi** (`agency.select`) ve `active_agency_id` oturum bazlı tutulur
- **Davet sistemi:** acente personeli davet/kabul/red (`AgencyStaffInvitation`)
- Modüller: Dashboard, Tanımlar (Definitions), Turlar, Günübirlik Turlar (DayTour), Ürünler/Ürün Tipleri, Müşteriler (CRM), Tedarikçiler, Satışlar, Finans, Personel Yönetimi, Kaynaklar (Resources), Disk (dosya yönetimi), Web Sitesi/Sayfa editörü, Ayarlar, Profil, Bildirimler
- Controller: `app/Http/Controllers/Agency/`, View: `resources/views/agency/`

### 3. Page / Customer (Genel/Müşteriye Açık Site) — `routes/web.php`
- Ana sayfa, acente listesi, acente kayıt formu
- Her acentenin yayınlanmış public sayfası: `/agency/{agency:slug}` — tur listeleme, arama, tur detay
- Müşteri (son kullanıcı) kayıt/giriş/şifre sıfırlama ve dashboard
- İletişim formu
- Controller: `app/Http/Controllers/Page/`, View: `resources/views/page/`

## Mimari Notlar

- **Auth:** Tek `User` modeli; `SoftDeletes`, `HasRoles` (spatie), `Notifiable` trait'leri. Üç ayrı giriş akışı (admin / agency / customer) özel middleware ile ayrılır (`app/Http/Middleware/AdminAuth.php`, `AgencyAuth.php`).
- **Multi-tenant:** Acente bağlamı oturumdaki `active_agency_id` ile belirlenir; veriler acenteye göre izole edilir.
- **Yetkilendirme:** spatie rol/izin + Laravel Policy'leri (`app/Policies/` — Tour, Disk, Definition, AgencyPage, Gallery için).
- **Bootstrap:** `bootstrap/app.php` — middleware alias'ları burada; `web.php` içinden `admin.php` ve `agency.php` require ediliyor.
- **Servisler & Trait'ler:** `app/Services/Agency/AgencyPageService.php`, `app/Traits/HasProduct.php`, View Composer (`AgencyHeaderComposer`).

## Veri Modeli (Öne Çıkan Alanlar)

Modeller alana göre alt klasörlere ayrılmış (`app/Models/`, ~65 model):

- **Agency/** — Agency, AgencyContract, AgencyPage(+Revision, +Section), AgencyTheme, AgencyStaff(+Invitation), AgencySalesTarget, Currency, Marketplace, AgencyProductType
- **Customer/** (CRM) — Customer, Company, CustomerActivity, CustomerDocument(+Type), CustomerFieldDefinition/Value (özel alanlar)
- **Tour/** — Tour, TourCategory, TourDate, TourItinerary, TourPricing, TourImage, TourTransferPoint
- **DayTour/** — DayTour, DayTourDate, DayTourItinerary, DayTourImage, DayTourTransferPoint
- **Product/** — Product, ProductCategory, ProductType, ProductMarketplacePrice/Translation; DayTripTour ailesi
- **Sale/** — Sale, SaleItem, SaleActivity, SaleDocument, SaleFieldDefinition/Value
- **Supplier/** — Supplier, SupplierActivity, SupplierPriceList/Item, SupplierResponsible
- **Definition/** — Definition(+Type, +Attribute, +Value), City, Country, District, Language (esnek tanım/sözlük sistemi)
- **Disk/** — DiskFolder, DiskItem (acente dosya yönetimi)
- Diğer — Resource(+Schedule), CustomerPassenger, User

## Veritabanı

- **66 migration** (`database/migrations/`) — temel tablolar 2025-09 tarihli, son güncellemeler 2026-06 (CRM alanları, vergi alanları, firma ilişkisi vb.)
- **25 seeder** (`database/seeders/`) — `DatabaseSeeder` üzerinden; ülke/şehir/ilçe, dil, para birimi, rol-izin, örnek acente/müşteri/tur/satış/tedarikçi verileri
- Sayfa içerikleri **revizyon (revision)** ile versiyonlanır — yayınlanan revizyon `published_revision_id` ile işaretlenir

## Veri Tablosu Bileşeni — `<x-data-table>`

Liste/index sayfaları için merkezi, yeniden kullanılabilir tablo bileşeni. Arama, filtre, sıralama, kolon yönetimi, toplu işlem, istatistik kartları ve sayfalamayı tek bileşende toplar.

- **Bileşen:** `resources/views/components/data-table.blade.php` (stil + JS `@once @push` ile bir kez basılır)
- **Sayfalama partial'ı:** `resources/views/components/partials/dt-pager.blade.php` (üst + alt çubuk için ortak)
- **Bağımlılık:** Font Awesome (ikonlar `fas fa-*`), `meta[name="csrf-token"]` (toplu işlemler için sayfa tarafında)
- **Kalıcılık:** Tablo ayarları `localStorage` anahtarı `dt-settings:{module}` altında saklanır → her tabloya **benzersiz `module`** verilmeli.

### Prop'lar

| Prop | Tip / Varsayılan | Açıklama |
|------|------------------|----------|
| `module` | string, `'table'` | localStorage anahtarı + form id. **Her sayfada benzersiz olmalı.** |
| `title` / `subtitle` / `icon` | string\|null | Başlık bloğu (icon: `fa-*`). Hiçbiri yoksa başlık çubuğu gizlenir. |
| `columns` | array, `[]` | Kolon tanımları (aşağıya bak). |
| `filters` | array, `[]` | Filtre alanları (aşağıya bak). |
| `search` | string, `''` | Arama input'unun mevcut değeri (genelde `request('search','')`). |
| `searchPlaceholder` | string, `'Ara...'` | Arama placeholder'ı. |
| `sort` / `dir` | string\|null / `'desc'` | Aktif sıralama kolonu ve yönü. |
| `createUrl` / `createLabel` | string\|null / `'Yeni Ekle'` | Sağ üstteki "Yeni Ekle" butonu (URL yoksa gizli). |
| `bulk` | bool, `false` | Toplu seçim kolonu + yüzen toplu işlem çubuğunu açar. |
| `stats` | array, `[]` | Üstte istatistik kartları. |
| `paginator` | LengthAwarePaginator\|null | Verildiğinde tablo **üst + alt** otomatik sayfalama çubuğu basar. |
| `perPageOptions` | array, `[10,25,50,100]` | "Sayfa başına" seçenekleri. |

### Kolon tanımı (`columns` öğesi)

```php
['key' => 'name', 'label' => 'Ad', 'sortable' => true, 'sort' => 'name',
 'visible' => true, 'fixed' => false, 'align' => 'start|center|end', 'width' => '150px']
```

- `key` — `<td data-col="...">` ile eşleşir; kolon gizleme/gösterme bu anahtarla çalışır.
- `sortable` + `sort` — sıralanabilir başlık; `sort` verilmezse `key` kullanılır.
- `visible: false` — varsayılan gizli kolon (ayarlardan açılabilir).
- `fixed: true` — ayarlardan gizlenemez; kolon listesinde görünmez (örn. `_actions`).
- **Özel anahtarlar:** `_select` (toplu seçim kolonu) ve `_actions` (işlem butonları) gizlenemez/gösterilmez, JS bunları atlar.

### Filtre tanımı (`filters` öğesi)

```php
['key' => 'status', 'label' => 'Durum', 'value' => request('status'),
 'options' => ['active' => 'Aktif', ...], 'placeholder' => 'Tümü']      // select
['key' => 'date_from', 'label' => 'Tarih', 'type' => 'date', 'value' => request('date_from')] // tarih
```

Filtreler `dt-autosubmit` ile değişince formu otomatik gönderir. Dolu filtre sayısı toggle'da rozet olarak gösterilir.

### İstatistik kartı (`stats` öğesi)

```php
['label' => 'Toplam', 'value' => 128, 'icon' => 'fa-users', 'color' => '#6366f1']
```

### Slot'lar

- **Varsayılan slot** → `<tbody>` içeriği. Satırları `@forelse` ile sen basarsın.
- `headerActions` → "Yeni Ekle" butonunun soluna ek butonlar.
- `bulkActions` → toplu işlem çubuğundaki butonlar (`bulk` true iken).
- `footer` → **yalnızca `paginator` verilmediğinde** kullanılır (geriye dönük uyum). Paginator varsa otomatik pager basılır, bu slot yok sayılır.

### Satır (`<tr>`) yazım kuralları

- Her `<td>`'ye **`data-col="{key}"`** verilmeli (kolon gizleme için zorunlu).
- Toplu seçim kolonu: `<td data-col="_select"><label class="dt-check"><input type="checkbox" class="dt-rowcheck" value="{{ $row->id }}"><span></span></label></td>`
- Boş durum: `@empty` içinde `<td colspan="100"><div class="dt-empty">...</div></td>`.

### Hücre yardımcı CSS sınıfları

- `dt-cell-main` (kalın ana metin), `dt-cell-sub` (gri alt metin)
- `dt-thumb` (46×36 görsel), `dt-thumb-ph` (görsel placeholder)
- Rozetler: `dt-badge` + `dt-badge-green|amber|red|slate|indigo`
- İşlem butonları: `dt-rowact` (sarmalayıcı), `dt-iconbtn` + opsiyonel `danger|warn|success`

### Tablo ayarları (kullanıcı başına, localStorage)

Sağ üstteki ⚙ panelinden, `module` bazında kalıcı: **görünür kolonlar**, **satır yoğunluğu** (compact/normal/comfortable), **zebra satırlar**, **sabit başlık** (sticky), **hücre kenarlıkları**, ve "varsayılana sıfırla".

### Sıralama mekaniği

Başlığa tıklama gizli `sort`/`dir` input'larını günceller ve formu (`GET`) gönderir. Controller `request('sort')` / `request('dir')` okur. Güvenlik için controller'da **izinli kolon beyaz listesi** uygula (örn. `SalesController`'daki `$allowedSorts`).

### Sayfalama (pager)

`:paginator` verildiğinde tablonun üst ve altına otomatik çubuk basılır:

- Toplam kayıt + gösterilen aralık (`1–12 / 248 kayıt`)
- "Sayfa başına" seçici → `per_page` query param'ı set eder, `page`'i sıfırlar, diğer parametreleri korur
- İlk / önceki / numaralı (aktif ±2 + `…`) / sonraki / son butonları

**Controller bağlama deseni** — `per_page`'i güvenli değerlere clamp'le ve `withQueryString()` ile koru:

```php
$perPage = (int) $request->input('per_page', 12);
$perPage = in_array($perPage, [12, 24, 48, 96], true) ? $perPage : 12;
$rows = $query->paginate($perPage)->withQueryString();
```

> Sayfa numarası linklerinin filtre/arama/`per_page`'i koruması için sorgu **mutlaka `->withQueryString()`** ile bitmelidir.

### JS API

- `root.dtSelected()` → seçili satır id'lerini dizi olarak döndürür (`.dt-root` elemanından).
- Sayfada toplu işlem için tipik kullanım: `document.querySelectorAll('.dt-root .dt-rowcheck:checked')`.

### Tam kullanım örneği

```blade
<x-data-table
    module="products-kultur"
    title="Kültür Turları" icon="fa-landmark"
    :columns="$columns" :filters="$filters" :bulk="true"
    :search="request('search','')" :sort="$sort" :dir="$dir"
    :create-url="route('agency.products.kultur-turlari.create')" create-label="Yeni Kültür Turu"
    :paginator="$tours" :per-page-options="[12, 24, 48, 96]">

    <x-slot:bulkActions>
        <button type="button" class="dt-mini-btn" onclick="bulkAction('delete')"><i class="fas fa-trash"></i> Sil</button>
    </x-slot:bulkActions>

    @forelse($tours as $tour)
        <tr>
            <td data-col="_select"><label class="dt-check"><input type="checkbox" class="dt-rowcheck" value="{{ $tour->id }}"><span></span></label></td>
            <td data-col="name"><div class="dt-cell-main">{{ $tour->name }}</div></td>
            {{-- ... --}}
            <td data-col="_actions" class="text-end">
                <div class="dt-rowact">
                    <a href="#" class="dt-iconbtn"><i class="fas fa-pen"></i></a>
                    <button class="dt-iconbtn danger"><i class="fas fa-trash"></i></button>
                </div>
            </td>
        </tr>
    @empty
        <tr><td colspan="100"><div class="dt-empty"><i class="fas fa-landmark"></i><h4>Kayıt yok</h4></div></td></tr>
    @endforelse
</x-data-table>
```

> Mevcut kullanımlar: `agency/products/kultur-turlari/index`, `agency/products/gunubirlik-turlar/index`, `agency/sales/partials/shared_list`.

## Dizin Yapısı (Özet)

```
app/
├── Enums/              # ActivityType
├── Http/
│   ├── Controllers/    # Admin/, Agency/, Page/  (~57 controller)
│   ├── Middleware/     # AdminAuth, AgencyAuth
│   ├── Requests/       # Form Request doğrulama (Admin/, Agency/)
│   └── View/Composers/ # AgencyHeaderComposer
├── Models/             # Alana göre alt klasörler (~65 model)
├── Policies/           # Yetkilendirme politikaları
├── Services/           # AgencyPageService
└── Traits/             # HasProduct
routes/                 # web.php, admin.php, agency.php, console.php
resources/views/        # admin/, agency/, page/, components/, vendor/
database/               # migrations/ (66), seeders/ (25), database.sqlite
config/                 # app, auth, permission, database, filesystems, ...
```

## Hızlı Başlangıç

```bash
composer install
cp .env.example .env
php artisan key:generate
php artisan migrate --seed      # SQLite veritabanını oluşturur ve doldurur
composer dev                    # sunucu + queue + log + vite (tek komut)
```

Giriş noktaları: `/admin/login` · `/agency/login` · `/customer/login` · `/` (genel site)
