# Kart Tasarım Editörü Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.

**Goal:** Parametre değerlerini canlı binding ile görsel şablona bağlayan inline kart tasarım editörü.

**Architecture:** Inline editör paylaş kutusundaki "Tasarım" sekmesi olarak çalışır. Şablon JSON olarak parametre seti içinde tanımlanır (TS + DB hybrid). Feed'de SablonRenderer ile kartlar görsel olarak gösterilir. DOM-based render, cdkDrag ile sürükle-bırak.

**Tech Stack:** Angular 20, CDK DragDrop, OnPush + signal(), LESS, mevcut FikoYT altyapısı

**Spec:** `docs/superpowers/specs/2026-03-19-kart-tasarim-editoru-design.md`

---

## Dosya Haritası

| Dosya | İşlem | Sorumluluk |
|-------|-------|------------|
| `src/service/fikoyt-ui/src/lib/paylasim-sistemi/sablon-editoru/sablon.interface.ts` | Oluştur | ISablon, ISablonElement, ISablonStil, IArkaPlan, IBolge, HAZIR_ARKAPLANLAR |
| `src/service/fikoyt-ui/src/lib/paylasim-sistemi/sablon-renderer/sablon-renderer.component.ts` | Oluştur | Şablon + parametreler → görsel kart (DOM render) |
| `src/service/fikoyt-ui/src/lib/paylasim-sistemi/sablon-renderer/sablon-renderer.component.less` | Oluştur | Renderer stilleri |
| `src/service/fikoyt-ui/src/lib/paylasim-sistemi/sablon-editoru/sablon-editoru.component.ts` | Oluştur | Ana editör bileşeni (canvas + araç çubuğu + stil paneli) |
| `src/service/fikoyt-ui/src/lib/paylasim-sistemi/sablon-editoru/sablon-editoru.component.html` | Oluştur | Editör template |
| `src/service/fikoyt-ui/src/lib/paylasim-sistemi/sablon-editoru/sablon-editoru.component.less` | Oluştur | Editör stilleri |
| `src/service/fikoyt-ui/interfaces/IParametreSeti.interface.ts` | Düzenle | `sablon?: ISablon` alanını IParametreSeti'ne ekle |
| `src/service/fikoyt-ui/src/lib/paylasim-sistemi/fikoyt-kart-kok/icerikler/icerikler.component.html` | Düzenle | UYGULAMAICERIK render'ına SablonRenderer ekle |
| `src/service/fikoyt-ui/src/lib/paylasim-sistemi/fikoyt-kart-kok/icerikler/icerikler.component.ts` | Düzenle | SablonRenderer import |
| `src/service/fikoyt-ui/src/lib/paylasim-sistemi/paylas-kutusu/IKart/ikart-kutusu.component.html` | Düzenle | "Tasarım" butonu ekle |
| `src/service/fikoyt-ui/src/lib/paylasim-sistemi/paylas-kutusu/IKart/paylas-icerik-editor/paylas-icerik-editor.component.html` | Düzenle | SablonEditoru entegrasyonu |
| `src/service/fikoyt-ui/src/lib/paylasim-sistemi/paylas-kutusu/IKart/paylas-icerik-editor/paylas-icerik-editor.component.ts` | Düzenle | Tasarım sekmesi logic |
| `src/app/uygulamalar/hmi-pano/pano/pages/personel/pages/projeler/hmi-projeler.service.ts` | Düzenle | Parametre setine örnek şablon ekle |

---

### Task 1: Interface Tanımları

**Files:**
- Create: `src/service/fikoyt-ui/src/lib/paylasim-sistemi/sablon-editoru/sablon.interface.ts`
- Modify: `src/service/fikoyt-ui/interfaces/IParametreSeti.interface.ts`

- [ ] **Step 1: ISablon interface dosyası oluştur**

```typescript
// sablon.interface.ts
import { FikoYTId } from '@/src/service/fikoyt-ui/helpers/FikoYTId';

export type SablonMod = 'blok' | 'serbest' | 'hibrit';
export type ArkaplanTur = 'renk' | 'gradient' | 'resim' | 'hazir';
export type ElementTur = 'parametre-metin' | 'sabit-metin' | 'sekil' | 'resim' | 'parametre-render';
export type SekilTuru = 'dikdortgen' | 'daire' | 'cizgi' | 'ucgen';

export interface ISablonStil {
  fontSize?: number;
  fontWeight?: number;
  fontFamily?: string;
  color?: string;
  textAlign?: 'left' | 'center' | 'right';
  fill?: string;
  stroke?: string;
  strokeWidth?: number;
  borderRadius?: number;
  opacity?: number;
  rotate?: number;
  shadow?: string;
}

export interface IArkaPlan {
  tur: ArkaplanTur;
  deger: string;
}

export interface IBolge {
  id: string;
  yukseklik: number | 'auto';
}

export interface ISablonElement {
  _id: string;
  tur: ElementTur;
  x: number;
  y: number;
  genislik?: number;
  yukseklik?: number;
  bolge?: string;
  parametreIsmi?: string;
  deger?: string;
  sekilTuru?: SekilTuru;
  resimUrl?: string;
  stil: ISablonStil;
}

export interface ISablon {
  genislik: number;
  yukseklik: number;
  mod: SablonMod;
  arkaPlan: IArkaPlan;
  bolgeler?: IBolge[];
  elementler: ISablonElement[];
}

export function yeniSablonElement(tur: ElementTur, overrides?: Partial<ISablonElement>): ISablonElement {
  return {
    _id: new FikoYTId().toString(),
    tur,
    x: 50, y: 50,
    stil: { fontSize: 14, color: '#ffffff' },
    ...overrides,
  };
}

export function bosISablon(mod: SablonMod = 'hibrit'): ISablon {
  return {
    genislik: 520,
    yukseklik: 300,
    mod,
    arkaPlan: { tur: 'gradient', deger: 'linear-gradient(135deg, #1e3a5f, #0a1628)' },
    bolgeler: mod !== 'serbest' ? [
      { id: 'ust', yukseklik: 60 },
      { id: 'orta', yukseklik: 'auto' as any },
      { id: 'alt', yukseklik: 40 },
    ] : undefined,
    elementler: [],
  };
}

export const HAZIR_ARKAPLANLAR: { id: string; baslik: string; deger: string }[] = [
  { id: 'karanlik-mavi', baslik: 'Kurumsal', deger: 'linear-gradient(135deg, #1e3a5f, #0a1628)' },
  { id: 'neon-yesil', baslik: 'Teknoloji', deger: 'linear-gradient(135deg, #0d2818, #1a4a2e)' },
  { id: 'sicak-turuncu', baslik: 'Enerji', deger: 'linear-gradient(135deg, #7c2d12, #dc6c35)' },
  { id: 'mor-pembe', baslik: 'Yaratıcı', deger: 'linear-gradient(135deg, #4a1942, #c850c0)' },
  { id: 'okyanus', baslik: 'Okyanus', deger: 'linear-gradient(135deg, #0c2340, #1565c0)' },
  { id: 'gece', baslik: 'Gece', deger: 'linear-gradient(135deg, #0d0d0d, #1a1a2e)' },
  { id: 'gunbatimi', baslik: 'Günbatımı', deger: 'linear-gradient(135deg, #ff6b35, #f7c948)' },
  { id: 'orman', baslik: 'Orman', deger: 'linear-gradient(135deg, #1b4332, #52b788)' },
  { id: 'sade-beyaz', baslik: 'Minimal', deger: '#ffffff' },
  { id: 'koyu-gri', baslik: 'Karanlık', deger: '#1a1a1a' },
  { id: 'krem', baslik: 'Krem', deger: '#faf3e0' },
  { id: 'buz', baslik: 'Buz', deger: 'linear-gradient(135deg, #e0f7fa, #80deea)' },
];
```

- [ ] **Step 2: IParametreSeti interface'ine sablon alanı ekle**

`src/service/fikoyt-ui/interfaces/IParametreSeti.interface.ts` dosyasında `IParametreSeti` interface'ine:
```typescript
import { ISablon } from '../src/lib/paylasim-sistemi/sablon-editoru/sablon.interface';

// IParametreSeti'ye ekle:
sablon?: ISablon;
```

Aynı şekilde `IParametreSetiGrubu`'na da:
```typescript
sablon?: ISablon;
```

- [ ] **Step 3: Build kontrol**

Run: `npx ng build`
Expected: Başarılı build, yeni interface'ler import edilebilir

---

### Task 2: Şablon Renderer Bileşeni

**Files:**
- Create: `src/service/fikoyt-ui/src/lib/paylasim-sistemi/sablon-renderer/sablon-renderer.component.ts`
- Create: `src/service/fikoyt-ui/src/lib/paylasim-sistemi/sablon-renderer/sablon-renderer.component.less`

- [ ] **Step 1: SablonRenderer bileşenini oluştur**

Bu bileşen `ISablon` + parametre değerleri alır ve DOM-based görsel kart üretir. Her element türü için ayrı render bloğu.

```typescript
@Component({
  selector: 'sablon-renderer',
  standalone: true,
  imports: [CommonModule],
  changeDetection: ChangeDetectionStrategy.OnPush,
  templateUrl: inline,
  styleUrl: './sablon-renderer.component.less',
})
export class SablonRendererComponent {
  @Input() sablon: ISablon;
  @Input() parametreDegerleri: { [ismi: string]: any } = {};

  parametreDeger(ismi: string): string { ... }
  arkaplanStil(): { [key: string]: string } { ... }
  elementStil(el: ISablonElement): { [key: string]: string } { ... }
  bolgeElementleri(bolgeId: string): ISablonElement[] { ... }
}
```

Template yapısı:
- `mod === 'serbest'`: Tüm elementler absolute positioned
- `mod === 'blok'`: Bölgeler flex column, elementler bölge içinde relative
- `mod === 'hibrit'`: Bölgeler flex column, elementler bölge içinde absolute

Her element türü için `@switch (el.tur)`:
- `parametre-metin`: `{{ parametreDeger(el.parametreIsmi) }}`
- `sabit-metin`: `{{ el.deger }}`
- `sekil`: CSS şekil (border-radius, background)
- `resim`: `<img>` veya emoji
- `parametre-render`: Parametre türüne göre mini widget (progress bar, tarih badge, dot vb.)

- [ ] **Step 2: LESS stilleri**

Renderer stilleri: `.sr-container`, `.sr-bolge`, `.sr-element`, `.sr-metin`, `.sr-sekil`, `.sr-resim`, `.sr-param-render` sınıfları. Dark/light tema uyumlu.

- [ ] **Step 3: Build kontrol**

Run: `npx ng build`
Expected: Başarılı

---

### Task 3: Feed Entegrasyonu (icerikler.component)

**Files:**
- Modify: `src/service/fikoyt-ui/src/lib/paylasim-sistemi/fikoyt-kart-kok/icerikler/icerikler.component.html:414-430`
- Modify: `src/service/fikoyt-ui/src/lib/paylasim-sistemi/fikoyt-kart-kok/icerikler/icerikler.component.ts`

- [ ] **Step 1: icerikler.component.ts'e SablonRenderer import et**

```typescript
import { SablonRendererComponent } from '../../sablon-renderer/sablon-renderer.component';
// imports array'ine ekle
```

- [ ] **Step 2: UYGULAMAICERIK render bloğunu güncelle**

`icerikler.component.html` satır 414-430 civarı, mevcut `fikoyt-parametreler` gösteriminin üstüne şablon kontrolü ekle:

```html
<div *ngIf="aktifIcerik?.ek?.turu == 'UYGULAMAICERIK'" class="icerik-uygulama-icerik">
    @if (aktifIcerik?.ek?.sablonOverride || aktifIcerik?.ek?.sablon) {
        <sablon-renderer
            [sablon]="aktifIcerik.ek.sablonOverride || aktifIcerik.ek.sablon"
            [parametreDegerleri]="parametreDegerleriHesapla(aktifIcerik)">
        </sablon-renderer>
    }
    @if (aktifIcerik?.ek?.parametreler?.length > 0) {
        <fikoyt-parametreler ...mevcut bindings...>
        </fikoyt-parametreler>
    } @else if (!aktifIcerik?.ek?.sablonOverride && !aktifIcerik?.ek?.sablon) {
        <div style="padding:12px;color:#8c8c8c;font-size:12px;text-align:center;">
            Parametre bilgisi yok
        </div>
    }
</div>
```

- [ ] **Step 3: parametreDegerleriHesapla helper metodu ekle**

```typescript
parametreDegerleriHesapla(icerik: any): { [key: string]: any } {
  const sonuc: any = {};
  for (const p of (icerik?.ek?.parametreler ?? [])) {
    if (p.ismi) sonuc[p.ismi] = p.degeri;
  }
  return sonuc;
}
```

- [ ] **Step 4: Build kontrol ve test**

Run: `npx ng build`
Expected: Başarılı. Mevcut kartlar değişmez (sablon yoksa eski parametreler gösterilir). Sablon varsa üstte görsel kart, altında parametreler.

---

### Task 4: Şablon Editörü Bileşeni

**Files:**
- Create: `src/service/fikoyt-ui/src/lib/paylasim-sistemi/sablon-editoru/sablon-editoru.component.ts`
- Create: `src/service/fikoyt-ui/src/lib/paylasim-sistemi/sablon-editoru/sablon-editoru.component.html`
- Create: `src/service/fikoyt-ui/src/lib/paylasim-sistemi/sablon-editoru/sablon-editoru.component.less`

- [ ] **Step 1: Ana editör bileşenini oluştur**

```typescript
@Component({
  selector: 'sablon-editoru',
  standalone: true,
  imports: [CommonModule, DragDropModule, FormsModule, NzXxx..., SablonRendererComponent],
  changeDetection: ChangeDetectionStrategy.OnPush,
  templateUrl: './sablon-editoru.component.html',
  styleUrl: './sablon-editoru.component.less',
})
export class SablonEditoruComponent {
  @Input() sablon: ISablon;
  @Input() parametreler: IParametre[] = [];
  @Output() sablonDegisti = new EventEmitter<ISablon>();

  seciliElement = signal<ISablonElement | null>(null);
  aktifMod = signal<SablonMod>('hibrit');
  arkaplanSeciciAcik = signal(false);

  // Element CRUD
  elementEkle(tur: ElementTur) { ... }
  elementSil(id: string) { ... }
  elementSec(el: ISablonElement) { ... }
  stilGuncelle(key: string, value: any) { ... }
  parametreBagla(parametreIsmi: string) { ... }

  // Drag-drop
  onDragEnded(event: CdkDragEnd, el: ISablonElement) { ... }

  // Mod değiştir
  modDegistir(mod: SablonMod) { ... }

  // Arka plan
  arkaplanDegistir(ap: IArkaPlan) { ... }
}
```

- [ ] **Step 2: Editör HTML template**

Yapı:
1. **Üst bar**: Mod seçici (Blok|Serbest|Hibrit segmented) + Arka plan butonu
2. **Canvas**: Canlı önizleme alanı — SablonRenderer + cdkDrag overlay
3. **Araç çubuğu**: Element ekleme butonları (T Metin, ▭ Şekil, 🖼 Resim, ⚡ Parametre)
4. **Stil paneli**: Seçili element için font/renk/boyut/parametre bağlantısı (element seçiliyken görünür)
5. **Arka plan seçici**: Hazır şablonlar grid + gradient picker + resim yükleme (toggle panel)

- [ ] **Step 3: Editör LESS stilleri**

Kompakt inline tasarım:
- Canvas alanı max 100% genişlik, aspect-ratio ile orantılı yükseklik
- Araç çubuğu: yatay icon butonları
- Stil paneli: accordion tarzı, seçili elementin altında
- Arka plan seçici: grid kartlar
- cdkDrag handle'ları + seçim kenarlığı

- [ ] **Step 4: Build kontrol**

Run: `npx ng build`
Expected: Başarılı

---

### Task 5: Paylaş Kutusu Entegrasyonu

**Files:**
- Modify: `src/service/fikoyt-ui/src/lib/paylasim-sistemi/paylas-kutusu/IKart/ikart-kutusu.component.html:277-291`
- Modify: `src/service/fikoyt-ui/src/lib/paylasim-sistemi/paylas-kutusu/IKart/paylas-icerik-editor/paylas-icerik-editor.component.html`
- Modify: `src/service/fikoyt-ui/src/lib/paylasim-sistemi/paylas-kutusu/IKart/paylas-icerik-editor/paylas-icerik-editor.component.ts`

- [ ] **Step 1: ikart-kutusu alt bara "Tasarım" butonu ekle**

`ikart-kutusu.component.html` satır 277-291 civarında, "Parametreler" ve "Şablondan" butonlarının arasına:

```html
<button nz-button nzType="link" nzSize="small" class="anim-buton"
        (click)="tasarimModuAcKapa()">
    <i class="paint brush icon"></i> Tasarım
</button>
```

`ikart-kutusu.component.ts`'e:
```typescript
tasarimModu = false;
tasarimModuAcKapa() {
  this.tasarimModu = !this.tasarimModu;
  this.CD.detectChanges();
}
```

- [ ] **Step 2: paylas-icerik-editor'a SablonEditoru entegre et**

`paylas-icerik-editor.component.html`'de UYGULAMAICERIK bloğu içinde, parametreler gösteriminin altına:

```html
@if (tasarimModu && icerik?.ek?.turu === 'UYGULAMAICERIK') {
    <sablon-editoru
        [sablon]="aktifSablon()"
        [parametreler]="icerik.ek.parametreler ?? []"
        (sablonDegisti)="sablonGuncelle($event)">
    </sablon-editoru>
}
```

`paylas-icerik-editor.component.ts`'e:
```typescript
@Input() tasarimModu = false;

aktifSablon(): ISablon {
  return this.icerik?.ek?.sablonOverride
    || this.icerik?.ek?.sablon
    || bosISablon();
}

sablonGuncelle(sablon: ISablon) {
  if (!this.icerik?.ek) return;
  this.icerik.ek.sablonOverride = sablon;
  this.icerikChange.emit(this.icerik);
}
```

- [ ] **Step 3: tasarimModu binding'ini ikart → paylas-icerik-editor'a ilet**

`ikart-kutusu.component.html`'deki `fikoyt-paylas-icerik-editor` etiketine `[tasarimModu]="tasarimModu"` ekle.

- [ ] **Step 4: Paylaşım sırasında sablon verisini karta ekle**

`ikart-kutusu.component.ts` `paylasTiklandi()` içinde, kart verisi hazırlanırken `icerik.ek.sablonOverride` zaten JSON serialize edilecek (mevcut `JSON.stringify` akışında).

Doğrulama: `sablonOverride` alanının kart ile birlikte sunucuya gönderilip gönderilmediğini test et.

- [ ] **Step 5: Build kontrol**

Run: `npx ng build`
Expected: Başarılı. "Tasarım" butonu görünür, tıklayınca editör açılır.

---

### Task 6: HMI Projeler Örnek Şablon

**Files:**
- Modify: `src/app/uygulamalar/hmi-pano/pano/pages/personel/pages/projeler/hmi-projeler.service.ts`

- [ ] **Step 1: "Proje Ekle" parametre setine örnek şablon ekle**

```typescript
import { ISablon } from '@/src/service/fikoyt-ui/src/lib/paylasim-sistemi/sablon-editoru/sablon.interface';

// 'hmi.proje.ekle' altGrup'una sablon alanı ekle:
sablon: {
  genislik: 520,
  yukseklik: 280,
  mod: 'hibrit',
  arkaPlan: { tur: 'gradient', deger: 'linear-gradient(135deg, #1e3a5f, #0a1628)' },
  bolgeler: [
    { id: 'ust', yukseklik: 70 },
    { id: 'orta', yukseklik: 'auto' },
    { id: 'alt', yukseklik: 50 },
  ],
  elementler: [
    { _id: '1', tur: 'sabit-metin', deger: '📁', x: 15, y: 20, bolge: 'ust', stil: { fontSize: 28 } },
    { _id: '2', tur: 'parametre-metin', parametreIsmi: 'projeAdi', x: 50, y: 35, bolge: 'ust', stil: { fontSize: 20, fontWeight: 700, color: '#ffffff', textAlign: 'center' } },
    { _id: '3', tur: 'parametre-metin', parametreIsmi: 'aciklama', x: 50, y: 50, bolge: 'orta', stil: { fontSize: 13, color: '#8899aa', textAlign: 'center' } },
    { _id: '4', tur: 'parametre-render', parametreIsmi: 'baslangic', x: 20, y: 50, bolge: 'alt', stil: { fontSize: 11, color: '#ffc800' } },
    { _id: '5', tur: 'sabit-metin', deger: '→', x: 50, y: 50, bolge: 'alt', stil: { fontSize: 11, color: '#666' } },
    { _id: '6', tur: 'parametre-render', parametreIsmi: 'bitis', x: 80, y: 50, bolge: 'alt', stil: { fontSize: 11, color: '#ffc800' } },
  ],
} as ISablon,
```

- [ ] **Step 2: Build ve test**

Run: `npx ng build`
Expected: HMI projeler sayfasında paylaşılan kartlar artık görsel şablon ile gösterilir. Mevcut kartlar (sablon olmayan) eski parametre gösterimini kullanmaya devam eder.

---

### Task 7: Son Dokunuşlar ve Polish

**Files:**
- Çeşitli dosyalar

- [ ] **Step 1: Arka plan resim yükleme desteği**

Editörde arka plan seçiciye dosya yükleme butonu ekle. Resim Base64'e çevrilip `arkaPlan.deger`'e yazılır.

- [ ] **Step 2: Element kopyalama/yapıştırma**

Seçili elemente sağ tık veya toolbar'dan "Kopyala" → yeni element aynı stille oluşturulur.

- [ ] **Step 3: Undo/Redo (basit)**

Son 20 state'i array'de tut. Ctrl+Z / Ctrl+Y ile geri/ileri.

- [ ] **Step 4: Responsive renderer**

SablonRenderer'a `olcek` input'u ekle. Feed'de kart genişliğine göre otomatik scale.

- [ ] **Step 5: Build ve entegrasyon testi**

Run: `npx ng build`
Expected: Tam çalışan editör + renderer + feed entegrasyonu.
