# Dashboard Widget Sistemi 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:** HMI Pano personel ana sayfasına tam dinamik, DB+TS hibrit, WebSocket destekli, olay takipli, responsive, özelleştirilebilir dashboard widget sistemi eklemek.

**Architecture:** Mevcut ISablon + SablonRenderer altyapısı yeniden kullanılır. Widget'lar JSON-driven, TS'te hazır setler + DB'de kullanıcı özelleştirmeleri. CSS Grid responsive layout. Socket.IO real-time updates. 42 zengin element türü.

**Tech Stack:** Angular 20, Ng-Zorro, CDK DragDrop, Socket.IO, MongoDB hareketler koleksiyonu, SVG (grafikler), ISablon/SablonRenderer

**Spec:** `docs/superpowers/specs/2026-03-20-dashboard-widget-sistemi-design.md` (Rev.3)

---

## Dosya Haritası

### Yeni Dosyalar
```
src/app/uygulamalar/hmi-pano/dashboard/
├── interfaces/widget.interface.ts          — IWidget, IWidgetItem, IWidgetVeriKaynagi
├── hazir-widgetler.ts                      — VARSAYILAN_WIDGETLER + ISablon tanımları
├── dashboard.service.ts                    — CRUD + merge + veri çekme + soket + cache
├── dashboard.component.ts                 — CSS Grid + CDK DragDrop + düzenleme modu
├── dashboard.component.html
├── dashboard.component.less
├── widget-renderer/
│   ├── widget-renderer.component.ts        — Tek widget render (OnPush)
│   ├── widget-renderer.component.html
│   └── widget-renderer.component.less
├── widget-editor/
│   ├── widget-editor.component.ts          — NzDrawer düzenleme
│   ├── widget-editor.component.html
│   └── widget-editor.component.less
└── widget-elementler/
    ├── sayac.component.ts                  — Büyük sayı + trend
    ├── ilerleme-halka.component.ts         — SVG circular progress
    ├── gauge.component.ts                  — SVG yarım daire gösterge
    ├── sparkline.component.ts              — SVG mini çizgi grafik
    ├── bar-grafik.component.ts             — SVG mini bar chart
    ├── pasta-grafik.component.ts           — SVG donut/pie
    ├── analog-saat.component.ts            — SVG analog saat
    ├── dijital-saat.component.ts           — Dijital saat
    ├── countdown.component.ts              — Geri sayım
    ├── durum-dot.component.ts              — Durum göstergesi
    ├── durum-kart.component.ts             — Mini durum kartı
    ├── led-gosterge.component.ts           — LED ışık
    ├── trafik-isigi.component.ts           — 3'lü trafik ışığı
    ├── sinyal-cubugu.component.ts          — Sinyal seviyeleri
    ├── seviye-cubugu.component.ts          — Dikey seviye
    ├── liste-tekrar.component.ts           — Veri listesi
    ├── siralama-listesi.component.ts       — Sıralı liste + bar
    ├── mini-tablo.component.ts             — Kompakt tablo
    ├── ticker.component.ts                 — Kayan metin
    ├── badge.component.ts                  — Bildirim badge
    ├── avatar.component.ts                 — Yuvarlak avatar
    ├── markdown.component.ts               — Markdown render
    └── buton.component.ts                  — Tıklanabilir buton
```

### Değişecek Dosyalar
```
src/app/uygulamalar/hmi-pano/hmi-pano.routes.ts          — Route güncelleme
src/app/uygulamalar/hmi-pano/pano/pages/personel/pages/ana-sayfa/ana-sayfa.component.ts  — Dashboard entegrasyonu
```

---

## FAZ 1: Çekirdek Altyapı

### Task 1: Interface Tanımları

**Files:**
- Create: `src/app/uygulamalar/hmi-pano/dashboard/interfaces/widget.interface.ts`

- [ ] **Step 1: IWidget, IWidgetItem, IWidgetVeriKaynagi interface'lerini yaz**

Spec Section 2'deki tüm interface'leri birebir uygula. `pozisyon` alanında `genislik`, `yukseklik`, `x?`, `y?`, `sira?` ayrımı. `silindi?: boolean` dahil.

- [ ] **Step 2: Build doğrula**

Run: `npx ng build`
Expected: Hatasız

- [ ] **Step 3: Commit**

---

### Task 2: Dashboard Service — Çekirdek

**Files:**
- Create: `src/app/uygulamalar/hmi-pano/dashboard/dashboard.service.ts`

- [ ] **Step 1: Servis iskeletini yaz**

`@Injectable({ providedIn: 'root' })`. Signals: `dbWidgetler`, `tumWidgetler` (computed). Metodlar: `yukle(cihazId)`, `kaydet(cihazId, widgetler)`, `birlestir()`, `temizle()`. FikoYTIstekKok ile DB CRUD. `kodWidgetler` başlangıçta boş dizi (hazır widgetler Task 6'da).

- [ ] **Step 2: `widgetVerisiCek(widget)` implementasyonu**

4 mod desteği: aggregate (FikoYTIstekKok), sabit (sabitVeri dön), soket (skip — Task 10), hesaplama (piFunc çalıştır). try-catch sarmalama. `vt` alanı: pipeline yoksa otomatik $match ekle, varsa ignore.

- [ ] **Step 3: `widgetGuncelle(id, degisiklikler)` — immutable güncelleme**

`dbWidgetler.update()` ile signal güncelle. Deep merge. `tumWidgetler` computed otomatik recompute.

- [ ] **Step 4: `widgetEkle`, `widgetSil`, `layoutSifirla` implementasyonu**

- [ ] **Step 5: Build doğrula + commit**

---

### Task 3: Dashboard Component — CSS Grid

**Files:**
- Create: `src/app/uygulamalar/hmi-pano/dashboard/dashboard.component.ts`
- Create: `src/app/uygulamalar/hmi-pano/dashboard/dashboard.component.html`
- Create: `src/app/uygulamalar/hmi-pano/dashboard/dashboard.component.less`

- [ ] **Step 1: Component iskeletini yaz**

Standalone, OnPush, `FikoYTKokBilesenComponent` extend. `DashboardService` inject. `ngOnInit`: `localStorage.getItem('fikoyt_cihaz_kimlik')` → `dashboardService.yukle()`. `ngOnDestroy`: `dashboardService.temizle()`.

- [ ] **Step 2: HTML — CSS Grid + widget-renderer loop**

```html
<div class="dashboard-grid" [style.--kolonlar]="dashboardService.tumWidgetler().length ? kolonSayisi : 0">
  @for (widget of dashboardService.tumWidgetler(); track widget.id) {
    <widget-renderer [widget]="widget" [duzenleModu]="duzenleModu" />
  }
</div>
```

Fallback: `tumWidgetler()` boşsa mevcut uygulama kartlarını göster.

- [ ] **Step 3: LESS — Responsive grid**

4 breakpoint: 4→3→2→1 sütun. `grid-template-columns: repeat(var(--kolonlar, 4), 1fr)`. Gap: 12px. Dark mode desteği.

- [ ] **Step 4: Build doğrula + commit**

---

### Task 4: Widget Renderer — Basit Render

**Files:**
- Create: `src/app/uygulamalar/hmi-pano/dashboard/widget-renderer/widget-renderer.component.ts`
- Create: `src/app/uygulamalar/hmi-pano/dashboard/widget-renderer/widget-renderer.component.html`
- Create: `src/app/uygulamalar/hmi-pano/dashboard/widget-renderer/widget-renderer.component.less`

- [ ] **Step 1: Component iskeletini yaz**

`input()` signal: widget (IWidget), duzenleModu (boolean). Signals: `veri`, `yukleniyor`, `hata`. OnPush.

- [ ] **Step 2: HTML template**

Spec Section 10'daki template. `grid-column: span genislik`, `grid-row: span yukseklik`, `order: sira`. Başlık çubuğu + sablon render + item listesi + fallback. `[parametreDegerleri]` (not: `parametreler` değil!).

- [ ] **Step 3: `ngOnInit` — veri çekme**

`dashboardService.widgetVerisiCek(widget)` çağır. Sonuç → `veri` signal. `olay('yenilendi')` tetikle. Polling: `yenilemeAraligi > 0` ise `setInterval`.

- [ ] **Step 4: Olay tetikleme metodu**

```typescript
olayTetikle(olayAdi: string) {
  const kod = this.widget().olaylar?.[olayAdi];
  if (!kod) return;
  try {
    const fn = new Function('veri', 'widget', 'router', 'bildirim', 'guncelle', kod);
    fn(this.veri(), this.widget(), this.router, this.bildirimFn, this.guncelleFn);
  } catch(e) { console.warn('Widget olay hatası:', this.widget().id, e); }
}
```

- [ ] **Step 5: LESS stili — kart görünümü**

Widget kartı: border-radius, box-shadow, dark mode. Başlık çubuğu: flex, simge + metin + butonlar. Hover efekti.

- [ ] **Step 6: Build doğrula + commit**

---

### Task 5: Route Entegrasyonu

**Files:**
- Modify: `src/app/uygulamalar/hmi-pano/hmi-pano.routes.ts`
- Modify: `src/app/uygulamalar/hmi-pano/pano/pages/personel/pages/ana-sayfa/ana-sayfa.component.ts`

- [ ] **Step 1: Ana sayfa component'i dashboard'a dönüştür**

Mevcut `kartlar` dizisini koru (fallback). `DashboardComponent`'i import et. Template'de: `tumWidgetler().length > 0` ise dashboard, değilse mevcut grid.

VEYA daha temiz: ana-sayfa route'unu doğrudan `DashboardComponent`'e yönlendir.

- [ ] **Step 2: Route güncelle**

`hmi-pano.routes.ts`'de personel altındaki `''` path → `DashboardComponent` loadComponent.

- [ ] **Step 3: Build doğrula + commit**

---

## FAZ 2: Hazır Widget'lar

### Task 6: Varsayılan Widget Tanımları

**Files:**
- Create: `src/app/uygulamalar/hmi-pano/dashboard/hazir-widgetler.ts`

- [ ] **Step 1: 7 hazır widget tanımla**

Spec Section 7: sys-saat, sys-uretim-sayac, sys-is-emirleri, sys-tezgah, sys-bakim, sys-uygulamalar, sys-duyuru. Her biri tam ISablon ile (mod, arkaPlan, bolgeler, elementler). Gerçek aggregate pipeline'lar.

- [ ] **Step 2: Dashboard service'e bağla**

`kodWidgetler = VARSAYILAN_WIDGETLER` import.

- [ ] **Step 3: Build doğrula + commit**

---

## FAZ 3: Zengin Element Türleri (paralel 10+ ajan)

### Task 7: Sayısal Elementler

**Files:**
- Create: `dashboard/widget-elementler/sayac.component.ts`
- Create: `dashboard/widget-elementler/ilerleme-halka.component.ts`
- Create: `dashboard/widget-elementler/gauge.component.ts`
- Create: `dashboard/widget-elementler/seviye-cubugu.component.ts`

- [ ] **Step 1: Sayaç — büyük sayı + trend oku + delta**

SVG veya DOM. Animasyonlu sayı geçişi. Trend: ↑ yeşil, ↓ kırmızı, = gri.

- [ ] **Step 2: İlerleme Halka — SVG circular progress**

`stroke-dashoffset` ile doluluk. Merkez değer. Renk eşiği.

- [ ] **Step 3: Gauge — SVG yarım daire**

180° ark. İbre (rotate). Renk zonları (yeşil/sarı/kırmızı). Min/max etiket.

- [ ] **Step 4: Seviye Çubuğu — dikey bar**

CSS gradient ile doluluk animasyonu. Seviye etiketleri.

- [ ] **Step 5: Build doğrula + commit**

---

### Task 8: Grafik Elementleri

**Files:**
- Create: `dashboard/widget-elementler/sparkline.component.ts`
- Create: `dashboard/widget-elementler/bar-grafik.component.ts`
- Create: `dashboard/widget-elementler/pasta-grafik.component.ts`

- [ ] **Step 1: Sparkline — SVG polyline + gradient fill**

Veri dizisi → SVG path. Responsive (viewBox). Gradient dolgu. Hover'da tooltip.

- [ ] **Step 2: Bar Grafik — SVG dikey çubuklar**

Veri dizisi → rect'ler. Etiketler. Renk mapping.

- [ ] **Step 3: Pasta Grafik — SVG donut**

Segment'ler `stroke-dasharray` ile. Legend. Hover'da vurgu.

- [ ] **Step 4: Build doğrula + commit**

---

### Task 9: Saat & Zaman Elementleri

**Files:**
- Create: `dashboard/widget-elementler/analog-saat.component.ts`
- Create: `dashboard/widget-elementler/dijital-saat.component.ts`
- Create: `dashboard/widget-elementler/countdown.component.ts`

- [ ] **Step 1: Analog Saat — SVG**

Kadran (daire + çizgiler). Akrep/yelkovan/saniye (rotate transform). `requestAnimationFrame` veya `setInterval(1s)`. NgZone dışında.

- [ ] **Step 2: Dijital Saat — DOM**

HH:MM:SS + tarih. Monospace font. Yanıp sönen `:`.

- [ ] **Step 3: Countdown — geri sayım**

Hedef tarihten fark. Gün:Saat:Dakika:Saniye. Bitiş animasyonu.

- [ ] **Step 4: Build doğrula + commit**

---

### Task 10: Durum & Gösterge Elementleri

**Files:**
- Create: `dashboard/widget-elementler/durum-dot.component.ts`
- Create: `dashboard/widget-elementler/durum-kart.component.ts`
- Create: `dashboard/widget-elementler/led-gosterge.component.ts`
- Create: `dashboard/widget-elementler/trafik-isigi.component.ts`
- Create: `dashboard/widget-elementler/sinyal-cubugu.component.ts`

- [ ] **Step 1: Durum Dot — CSS**

12px daire + label. Renk binding. Nabız animasyonu (pulse).

- [ ] **Step 2: Durum Kart — mini kart**

Simge + başlık + açıklama + sol renk şeridi.

- [ ] **Step 3: LED Gösterge — CSS**

Açık/kapalı/yanıp sönen. box-shadow glow efekti.

- [ ] **Step 4: Trafik Işığı — SVG 3 daire**

Aktif olan yanar, diğerleri söner.

- [ ] **Step 5: Sinyal Çubuğu — CSS bars**

5 dikey bar, seviyeye göre doluluk.

- [ ] **Step 6: Build doğrula + commit**

---

### Task 11: Liste & Medya Elementleri

**Files:**
- Create: `dashboard/widget-elementler/liste-tekrar.component.ts`
- Create: `dashboard/widget-elementler/siralama-listesi.component.ts`
- Create: `dashboard/widget-elementler/mini-tablo.component.ts`
- Create: `dashboard/widget-elementler/ticker.component.ts`
- Create: `dashboard/widget-elementler/badge.component.ts`
- Create: `dashboard/widget-elementler/avatar.component.ts`
- Create: `dashboard/widget-elementler/markdown.component.ts`
- Create: `dashboard/widget-elementler/buton.component.ts`

- [ ] **Step 1: Liste Tekrar — ngFor benzeri**

Veri dizisi + item sablon template. Her item SablonRenderer ile.

- [ ] **Step 2: Sıralama Listesi**

# sıra + isim + bar göstergesi + değer.

- [ ] **Step 3: Mini Tablo**

Sütun başlıkları + satırlar. Zebra stili. Compact.

- [ ] **Step 4: Ticker — CSS animation**

`@keyframes ticker { from { transform: translateX(100%); } to { transform: translateX(-100%); } }`

- [ ] **Step 5: Badge, Avatar, Markdown, Buton**

Basit DOM bileşenleri. Badge: nabız animasyonu. Avatar: UniversalAvatarComponent reuse. Markdown: innerHTML + sanitize. Buton: olay tetikleme.

- [ ] **Step 6: Build doğrula + commit**

---

### Task 12: Widget Renderer'a Element Entegrasyonu

**Files:**
- Modify: `dashboard/widget-renderer/widget-renderer.component.ts`
- Modify: `dashboard/widget-renderer/widget-renderer.component.html`

- [ ] **Step 1: Tüm element component'lerini import et**

- [ ] **Step 2: ISablon element switch — yeni türler**

SablonRenderer'ın `elementler` loop'unda `@switch (el.tur)` ile yeni element türlerini route et. Veya SablonRenderer'ı extend edip yeni element render desteği ekle.

- [ ] **Step 3: Build doğrula + commit**

---

## FAZ 4: Widget Editörü

### Task 13: Widget Editor Component

**Files:**
- Create: `dashboard/widget-editor/widget-editor.component.ts`
- Create: `dashboard/widget-editor/widget-editor.component.html`
- Create: `dashboard/widget-editor/widget-editor.component.less`

- [ ] **Step 1: NzDrawer shell**

Sağdan açılan drawer. NzTabset ile sekmeler: Genel, Veri, Tasarım, Olaylar, Pozisyon.

- [ ] **Step 2: Genel sekmesi**

Başlık, simge, açıklama input'ları.

- [ ] **Step 3: Veri sekmesi**

Mod seçimi (nz-select), VT input, pipeline JSON textarea, yenileme aralığı, soket event.

- [ ] **Step 4: Tasarım sekmesi**

SablonEditoruComponent reuse (lazy load). Veya basit stil form (gradyan, renk, borderRadius).

- [ ] **Step 5: Olaylar sekmesi**

Olay adı + JS kod textarea. Mevcut olayları listele, yeni ekle/sil.

- [ ] **Step 6: Pozisyon sekmesi**

Grid genişlik/yükseklik number input.

- [ ] **Step 7: Build doğrula + commit**

---

### Task 14: CDK DragDrop Entegrasyonu

**Files:**
- Modify: `dashboard/dashboard.component.ts`
- Modify: `dashboard/dashboard.component.html`

- [ ] **Step 1: Düzenleme modu toggle**

Sağ üst köşede ⚙️ butonu. `duzenleModu = signal(false)`.

- [ ] **Step 2: CDK DragDrop — grid reorder**

`cdkDropList` + `cdkDrag`. Drop handler: `sira` güncelle → `dashboardService.widgetGuncelle()`.

- [ ] **Step 3: Widget ekle butonu**

Düzenleme modunda "+" widget ekle butonu. Tip seçici popover.

- [ ] **Step 4: Widget sil butonu**

Widget başlığındaki ✕ butonu. `dashboardService.widgetSil()`.

- [ ] **Step 5: Kaydet butonu**

Düzenleme modunda "Kaydet" → `dashboardService.kaydet(cihazId, widgetler)`.

- [ ] **Step 6: Build doğrula + commit**

---

## FAZ 5: WebSocket & Olay Sistemi

### Task 15: Socket.IO Entegrasyonu

**Files:**
- Modify: `dashboard/dashboard.service.ts`

- [ ] **Step 1: `soketDinle(widget, callback)` implementasyonu**

`FikoYT.io.fromEvent(soketOlay)`. `soketFiltre` ile payload filtrele. `takeUntil(destroy$)`. `soketAbonelikler` Map'e kaydet.

- [ ] **Step 2: Widget renderer'da soket bağlantısı**

`ngOnInit`: `veriKaynagi.soketOlay` varsa `soketDinle()` çağır. Callback: `veri` signal güncelle + `olayTetikle('yenilendi')`.

- [ ] **Step 3: Fallback polling + soket kombinasyonu**

`yenilemeAraligi > 0`: interval ile aggregate çek. Soket event gelince de güncelle. Son güncelleme zamanı takibi ile gereksiz polling engelle.

- [ ] **Step 4: Build doğrula + commit**

---

### Task 16: Son Kontrol & Build

- [ ] **Step 1: `npx ng build` — hatasız build**
- [ ] **Step 2: 20 ajan kontrol turu**
- [ ] **Step 3: Bulunan sorunları düzelt**
- [ ] **Step 4: Final build doğrulama**
