# Tasarım Dokümanı: Safnaturel Özel Tasarım Takı Uygulaması

## Genel Bakış

Safnaturel, kullanıcıların doğal taşlarla bileklik, kolye ve tesbih tasarlayabildiği bir web uygulamasıdır. Sistem iki ana bileşenden oluşur:

1. **Frontend (React SPA)**: Kullanıcı arayüzü — ana sayfa, tasarım editörü ve sipariş akışı. React + TypeScript + Vite ile geliştirilir.
2. **Backend (PHP + MySQL)**: REST API ve admin paneli. Standart shared hosting (cPanel) ortamında çalışacak şekilde saf PHP ile geliştirilir, framework kullanılmaz.

Frontend, localStorage yerine PHP REST API üzerinden MySQL veritabanıyla iletişim kurar. Admin paneli sunucu taraflı PHP sayfaları olarak render edilir (SSR), ayrı bir SPA değildir.

## Mimari

### Genel Mimari Diyagramı

```mermaid
graph TB
    subgraph "Tarayıcı"
        subgraph "React SPA - Frontend"
            LP[Ana Sayfa]
            DE[Tasarım Editörü]
            subgraph "Paneller"
                SP[Sol - Taş Kataloğu]
                CP[Orta - Takı Modeli]
                RP[Sağ - Ayarlar & Sepet]
            end
        end
        subgraph "Zustand Store"
            DS[Design Store]
            CS[Cart Store]
        end
    end

    subgraph "Shared Hosting Sunucu"
        subgraph "PHP Backend"
            API[REST API - /api/]
            AP[Admin Paneli - /admin/]
            AUTH[Auth Middleware]
            UPLOAD[Dosya Yükleme]
        end
        subgraph "MySQL"
            DB[(Veritabanı)]
        end
        STATIC[/uploads/ - Görseller]
    end

    LP -->|Tür Seçimi| DE
    DE --> SP & CP & RP
    SP -->|Sürükle| CP
    CP -->|Güncelle| DS & CS

    DE <-->|fetch| API
    AP <-->|PHP Session| AUTH
    API <-->|PDO| DB
    AP <-->|PDO| DB
    UPLOAD --> STATIC
```

### Dizin Yapısı

```
safnaturel/
├── frontend/                  # React SPA (Vite ile build edilir)
│   ├── src/
│   │   ├── components/
│   │   ├── store/
│   │   ├── types/
│   │   ├── utils/
│   │   └── api/               # API istemci fonksiyonları
│   └── dist/                  # Build çıktısı → sunucuya kopyalanır
├── backend/
│   ├── api/                   # REST API endpoint'leri
│   │   ├── stones.php
│   │   ├── templates.php
│   │   ├── orders.php
│   │   └── auth.php
│   ├── admin/                 # Admin paneli (PHP SSR)
│   │   ├── index.php          # Dashboard
│   │   ├── login.php
│   │   ├── stones.php
│   │   ├── templates.php
│   │   ├── orders.php
│   │   └── includes/
│   │       ├── header.php
│   │       ├── footer.php
│   │       └── sidebar.php
│   ├── config/
│   │   ├── database.php       # DB bağlantı ayarları
│   │   └── config.php         # Genel ayarlar
│   ├── includes/
│   │   ├── db.php             # PDO bağlantı sınıfı
│   │   ├── auth.php           # Oturum yönetimi
│   │   └── helpers.php        # Yardımcı fonksiyonlar
│   ├── uploads/               # Yüklenen görseller
│   │   ├── stones/
│   │   └── templates/
│   ├── sql/
│   │   └── setup.sql          # Veritabanı kurulum betiği
│   └── .htaccess              # URL yönlendirme
└── public/                    # Web root (hosting'e deploy edilir)
    ├── index.html             # React SPA entry point
    ├── assets/                # Vite build çıktısı
    ├── api/                   # → backend/api/ symlink veya kopyası
    ├── admin/                 # → backend/admin/
    └── uploads/               # → backend/uploads/
```

### Teknoloji Seçimleri

| Teknoloji | Amaç | Gerekçe |
|-----------|-------|---------|
| React 18 + TypeScript | Frontend UI | Bileşen tabanlı mimari, tip güvenliği |
| Vite | Build tool | Hızlı geliştirme deneyimi |
| Zustand | Durum yönetimi | Hafif, basit API |
| @dnd-kit/core | Sürükle-bırak | Erişilebilir, performanslı |
| Tailwind CSS | Stil | Hızlı prototipleme, duyarlı tasarım |
| PHP 7.4+ | Backend API & Admin | Shared hosting uyumlu, ek kurulum gerektirmez |
| MySQL 5.7+ | Veritabanı | Shared hosting'lerde standart, phpMyAdmin ile yönetim |
| PDO | DB erişimi | Prepared statement desteği, SQL injection koruması |
| Bootstrap 5 | Admin paneli stili | CDN'den yüklenir, hızlı admin UI |
| Vitest + fast-check | Frontend test | Birim ve özellik tabanlı testler |

### Sayfa Akışı

```mermaid
stateDiagram-v2
    [*] --> AnaSayfa
    AnaSayfa --> TasarimEditoru : Kategori Seçimi

    state TasarimEditoru {
        [*] --> ModelSecimi
        ModelSecimi --> TasYerlestirme : Şablon Seçildi
        TasYerlestirme --> SiparisOzeti : Tüm Yuvalar Dolu
        TasYerlestirme --> AyarDegisikligi : Ayar Değiştirildi
        AyarDegisikligi --> ModelSecimi : Tür Değişti
        AyarDegisikligi --> TasYerlestirme : Ölçü/Boyut Değişti
    end

    SiparisOzeti --> SiparisOnay : Onaylandı (API'ye gönderilir)
    SiparisOnay --> [*]
```

```mermaid
stateDiagram-v2
    [*] --> AdminGiris
    AdminGiris --> Dashboard : Başarılı Giriş
    Dashboard --> TasYonetimi : Taşlar menüsü
    Dashboard --> SablonYonetimi : Şablonlar menüsü
    Dashboard --> SiparisYonetimi : Siparişler menüsü
    TasYonetimi --> Dashboard
    SablonYonetimi --> Dashboard
    SiparisYonetimi --> Dashboard
    Dashboard --> [*] : Çıkış
```


## Bileşenler ve Arayüzler

### Frontend Bileşen Hiyerarşisi

```mermaid
graph TD
    App[App]
    App --> Router[React Router]
    Router --> LandingPage[LandingPage]
    Router --> DesignEditor[DesignEditor]

    LandingPage --> Logo[Logo]
    LandingPage --> CategoryCard[CategoryCard x3]

    DesignEditor --> StonePanel[StonePanel - Sol]
    DesignEditor --> ModelView[ModelView - Orta]
    DesignEditor --> SettingsPanel[SettingsPanel - Sağ]

    StonePanel --> SearchBar[SearchBar]
    StonePanel --> StoneGrid[StoneGrid]
    StoneGrid --> StoneCard[StoneCard]

    ModelView --> JewelryCanvas[JewelryCanvas]
    ModelView --> ZoomControls[ZoomControls]
    JewelryCanvas --> BeadSlot[BeadSlot x N]

    SettingsPanel --> TypeSelector[TypeSelector]
    SettingsPanel --> SizeSelector[SizeSelector]
    SettingsPanel --> StoneSizeSelector[StoneSizeSelector]
    SettingsPanel --> Cart[Cart]
    SettingsPanel --> OrderButton[OrderButton]

    Cart --> CartItem[CartItem]
```

### Frontend Temel Arayüzler (TypeScript)

```typescript
interface Stone {
  id: number;
  name: string;
  image: string; // URL: /uploads/stones/xxx.jpg
  active: boolean;
}

type JewelryType = 'bileklik' | 'kolye' | 'tesbih';

interface JewelrySize {
  label: 'S' | 'M' | 'L';
  circumferenceCm: number;
}

type StoneSizeMm = 6 | 8;

interface ModelTemplate {
  id: number;
  name: string;
  jewelryType: JewelryType;
  image: string;
}

interface SlotPosition {
  index: number;
  angle: number;
  x: number;
  y: number;
}

interface BeadSlot {
  index: number;
  stone: Stone | null;
  position: SlotPosition;
}

interface DesignState {
  jewelryType: JewelryType;
  size: JewelrySize;
  stoneSizeMm: StoneSizeMm;
  selectedTemplate: ModelTemplate | null;
  slots: BeadSlot[];
  slotCount: number;
}

interface CartItem {
  slotIndex: number;
  stone: Stone;
}

// Sipariş oluşturma isteği (API'ye gönderilir)
interface CreateOrderRequest {
  customerName: string;
  customerPhone: string;
  customerNote?: string;
  jewelryType: JewelryType;
  templateId: number;
  size: string;
  stoneSizeMm: number;
  items: { slotIndex: number; stoneId: number }[];
}

// API yanıt formatı
interface ApiResponse<T> {
  success: boolean;
  data?: T;
  error?: string;
}
```

### API İstemci Fonksiyonları (Frontend)

```typescript
// src/api/client.ts
const API_BASE = '/api';

async function fetchStones(): Promise<Stone[]>;
async function fetchTemplates(jewelryType?: JewelryType): Promise<ModelTemplate[]>;
async function createOrder(order: CreateOrderRequest): Promise<{ orderId: number }>;
```

### REST API Endpoint'leri (PHP Backend)

| Method | Endpoint | Açıklama | Yetki |
|--------|----------|----------|-------|
| GET | `/api/stones.php` | Aktif taşları listele | Herkese açık |
| GET | `/api/templates.php?type=bileklik` | Şablonları türe göre listele | Herkese açık |
| POST | `/api/orders.php` | Yeni sipariş oluştur | Herkese açık |
| POST | `/api/auth.php?action=login` | Admin giriş | Herkese açık |
| POST | `/api/auth.php?action=logout` | Admin çıkış | Admin |
| GET | `/api/stones.php?admin=1` | Tüm taşları listele (admin) | Admin |
| POST | `/api/stones.php` | Taş ekle | Admin |
| PUT | `/api/stones.php?id=X` | Taş güncelle | Admin |
| DELETE | `/api/stones.php?id=X` | Taş sil | Admin |
| GET | `/api/templates.php?admin=1` | Tüm şablonları listele (admin) | Admin |
| POST | `/api/templates.php` | Şablon ekle | Admin |
| PUT | `/api/templates.php?id=X` | Şablon güncelle | Admin |
| DELETE | `/api/templates.php?id=X` | Şablon sil | Admin |
| GET | `/api/orders.php?admin=1` | Siparişleri listele (admin) | Admin |
| GET | `/api/orders.php?id=X&admin=1` | Sipariş detayı (admin) | Admin |
| PUT | `/api/orders.php?id=X` | Sipariş durumu güncelle | Admin |

### Admin Paneli Bileşenleri (PHP SSR)

Admin paneli sunucu taraflı PHP sayfaları olarak render edilir. Bootstrap 5 CDN ile stillendirilir.

| Dosya | Açıklama |
|-------|----------|
| `admin/login.php` | Giriş formu |
| `admin/index.php` | Dashboard — özet kartları ve son siparişler |
| `admin/stones.php` | Taş CRUD — tablo, ekleme/düzenleme modal, silme |
| `admin/templates.php` | Şablon CRUD — tablo, ekleme/düzenleme modal, silme |
| `admin/orders.php` | Sipariş listesi ve detay — filtreleme, durum güncelleme |
| `admin/includes/header.php` | Ortak üst kısım, navbar |
| `admin/includes/sidebar.php` | Sol menü |
| `admin/includes/footer.php` | Ortak alt kısım, JS |

### PHP Backend Temel Fonksiyonlar

```php
// includes/db.php
class Database {
    public static function getConnection(): PDO;
}

// includes/auth.php
function requireAdmin(): void;  // Oturum kontrolü, yoksa 401
function isLoggedIn(): bool;
function loginAdmin(string $username, string $password): bool;
function logoutAdmin(): void;

// includes/helpers.php
function jsonResponse(array $data, int $statusCode = 200): void;
function validateImageUpload(array $file): string|false;  // Dosya yolu veya false
function sanitizeInput(string $input): string;
```


## Veri Modelleri

### MySQL Veritabanı Şeması

```mermaid
erDiagram
    admin_users {
        int id PK
        varchar username UK
        varchar password_hash
        datetime created_at
    }

    stones {
        int id PK
        varchar name
        varchar image_path
        boolean active
        datetime created_at
        datetime updated_at
    }

    templates {
        int id PK
        varchar name
        enum jewelry_type
        varchar image_path
        boolean active
        datetime created_at
        datetime updated_at
    }

    orders {
        int id PK
        varchar customer_name
        varchar customer_phone
        text customer_note
        enum jewelry_type
        int template_id FK
        varchar size_label
        int stone_size_mm
        enum status
        datetime created_at
        datetime updated_at
    }

    order_items {
        int id PK
        int order_id FK
        int slot_index
        int stone_id FK
        datetime created_at
    }

    orders ||--o{ order_items : "içerir"
    templates ||--o{ orders : "kullanılır"
    stones ||--o{ order_items : "yerleştirilir"
```

### SQL Kurulum Betiği (setup.sql)

```sql
CREATE TABLE admin_users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL UNIQUE,
    password_hash VARCHAR(255) NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

CREATE TABLE stones (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    image_path VARCHAR(255) NOT NULL,
    active BOOLEAN DEFAULT TRUE,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

CREATE TABLE templates (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    jewelry_type ENUM('bileklik', 'kolye', 'tesbih') NOT NULL,
    image_path VARCHAR(255) NOT NULL,
    active BOOLEAN DEFAULT TRUE,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    INDEX idx_jewelry_type (jewelry_type)
);

CREATE TABLE orders (
    id INT AUTO_INCREMENT PRIMARY KEY,
    customer_name VARCHAR(100) NOT NULL,
    customer_phone VARCHAR(20) NOT NULL,
    customer_note TEXT,
    jewelry_type ENUM('bileklik', 'kolye', 'tesbih') NOT NULL,
    template_id INT NOT NULL,
    size_label VARCHAR(10) NOT NULL,
    stone_size_mm INT NOT NULL,
    status ENUM('beklemede', 'onaylandi', 'hazirlaniyor', 'kargoda', 'teslim_edildi', 'iptal') DEFAULT 'beklemede',
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
    FOREIGN KEY (template_id) REFERENCES templates(id),
    INDEX idx_status (status),
    INDEX idx_created_at (created_at)
);

CREATE TABLE order_items (
    id INT AUTO_INCREMENT PRIMARY KEY,
    order_id INT NOT NULL,
    slot_index INT NOT NULL,
    stone_id INT NOT NULL,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (order_id) REFERENCES orders(id) ON DELETE CASCADE,
    FOREIGN KEY (stone_id) REFERENCES stones(id),
    INDEX idx_order_id (order_id)
);

-- Varsayılan admin kullanıcısı (şifre: admin123, bcrypt hash)
INSERT INTO admin_users (username, password_hash) VALUES
('admin', '$2y$10$placeholder_hash_to_be_generated');
```

### Frontend Zustand Store Yapısı

```typescript
// Design Store — mevcut yapı korunur
interface DesignStore {
  jewelryType: JewelryType;
  size: JewelrySize;
  stoneSizeMm: StoneSizeMm;
  selectedTemplate: ModelTemplate | null;
  slots: BeadSlot[];
  slotCount: number;
  zoom: number;
  setJewelryType: (type: JewelryType) => void;
  setSize: (size: JewelrySize) => void;
  setStoneSizeMm: (size: StoneSizeMm) => void;
  setTemplate: (template: ModelTemplate) => void;
  placeStone: (slotIndex: number, stone: Stone) => void;
  removeStone: (slotIndex: number) => void;
  resetSlots: () => void;
  zoomIn: () => void;
  zoomOut: () => void;
}

// Stone Store — API'den veri çeker (localStorage yerine)
interface StoneStore {
  stones: Stone[];
  loading: boolean;
  error: string | null;
  fetchStones: () => Promise<void>;
}

// Template Store — API'den veri çeker
interface TemplateStore {
  templates: ModelTemplate[];
  loading: boolean;
  error: string | null;
  fetchTemplates: (jewelryType?: JewelryType) => Promise<void>;
}
```

### Yuva Sayısı Hesaplama Formülü

```
yuvaSayısı = Math.floor(çevreCm * 10 / taşBoyutuMm)
```

Örnek hesaplamalar:
- S (16cm) + 8mm = 20 yuva
- M (18cm) + 8mm = 22 yuva
- L (20cm) + 8mm = 25 yuva
- S (16cm) + 6mm = 26 yuva
- M (18cm) + 6mm = 30 yuva
- L (20cm) + 6mm = 33 yuva

### PHP Veritabanı Bağlantı Yapılandırması

```php
// config/database.php
return [
    'host' => 'localhost',
    'dbname' => 'safnaturel_db',
    'username' => 'db_user',
    'password' => 'db_password',
    'charset' => 'utf8mb4',
];

// includes/db.php
class Database {
    private static ?PDO $connection = null;

    public static function getConnection(): PDO {
        if (self::$connection === null) {
            $config = require __DIR__ . '/../config/database.php';
            self::$connection = new PDO(
                "mysql:host={$config['host']};dbname={$config['dbname']};charset={$config['charset']}",
                $config['username'],
                $config['password'],
                [
                    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
                    PDO::ATTR_EMULATE_PREPARES => false,
                ]
            );
        }
        return self::$connection;
    }
}
```
