# Phase 0 - Freeze Scope (WepAppOpusApiDesktop V1)

Status: ready for implementation  
Date: 2026-04-17  
Target: `WepAppOpusApiDesktop` (Electron + React, local-only)

## 1) Goal

ส่งมอบ Desktop App ที่หน้าตาและประสบการณ์ใกล้เว็บเดิม แต่รันบนเครื่องผู้ใช้ทั้งหมด:
- ไม่ต้อง deploy server กลาง
- ไม่ต้องติดตั้ง runtime เพิ่มเอง (ใช้ installer)
- เก็บข้อมูลใน SQLite local
- เก็บ token บนเครื่องผู้ใช้เท่านั้น (ไม่เก็บ plaintext)

## 2) In Scope (V1 Must-have)

1. Electron + React desktop app พร้อม installer Windows (`nsis`, `portable`)
2. Local DB (`SQLite`) + migration อัตโนมัติเมื่อเปิดแอปครั้งแรก
3. หน้าใช้งานหลัก 6 หน้า:
   - `Dashboard`
   - `Daily Trends`
   - `Campaign View`
   - `Demographics`
   - `Import Data`
   - `Settings`
4. Global filters (date range, campaign, objective, gender, age range, status)
5. Import CSV (`day_only`, `gender_age`) + preview + import logs
6. เรียก Meta API จากเครื่องผู้ใช้โดยตรง (ผ่าน service ในแอป)
7. ระบบเก็บ token ปลอดภัย:
   - token จริงเก็บใน OS keychain
   - DB เก็บเฉพาะ profile metadata/reference
8. Export Dashboard เป็น PDF

## 3) Out of Scope (Cut from V1)

1. Multi-user / RBAC เต็มรูปแบบในเครื่องเดียว
2. Team sharing / cloud sync / profile sharing ข้ามเครื่อง
3. Auto-update app
4. Budget action automation (เพิ่มงบ/รีเซตงบอัตโนมัติ)
5. Advanced analytics ชุดใหญ่ (Pareto, Waterfall, Heatmap, Funnel, Boxplot, Radar)
6. Real-time push/websocket streaming
7. Mobile companion app

## 4) Data Contract (Per Page)

หมายเหตุร่วม:
- วันที่ใช้รูปแบบ `YYYY-MM-DD`
- จำนวนเงินใช้หน่วย THB
- ทุก metric ที่หารด้วยศูนย์แล้วไม่ได้ค่า ให้คืน `0` (ห้าม `NaN`, `Infinity`, `null`)

### 4.1 Dashboard

Data sources:
- `getStats(filters, importType="day_only")`
- `getDaily(filters, importType="day_only")`

KPI cards (18):
1. `spend`
2. `impressions`
3. `reach`
4. `clicks`
5. `linkClicks`
6. `purchases`
7. `sales`
8. `roas`
9. `costPerPurchase`
10. `chats`
11. `costPerChat`
12. `ctr`
13. `cpc`
14. `cpm`
15. `campaigns`
16. `leads`
17. `engagement`
18. `results`

Charts:
- Dual-axis line chart `Spend + ROAS รายวัน`
  - Left axis: `daily[].spend`
  - Right axis: `daily[].roas`

Tables:
- ไม่มี table ในหน้า Dashboard V1

### 4.2 Daily Trends

Data source:
- `getDaily(filters, importType="day_only")`

KPI:
- ไม่มี KPI card เฉพาะหน้าใน V1 (ใช้ KPI ที่ Dashboard)

Charts (6):
1. Bar: `Spend รายวัน` (`date`, `spend`)
2. Line: `Impressions รายวัน` (`date`, `impressions`)
3. Dual-axis line: `Purchases + Sales` (`date`, `purchases`, `sales`)
4. Line: `ROAS รายวัน` (`date`, `roas`)
5. Bar: `Messaging / Chat` (`date`, `chats`)
6. Line: `CPM รายวัน` (`date`, `cpm`)

Tables:
- ไม่มี table ในหน้า Daily Trends V1

### 4.3 Campaign View

Data source:
- `getCampaigns(filters, importType="day_only")`

KPI:
- ไม่มี KPI card เฉพาะหน้าใน V1

Charts:
- Horizontal bar: `Top 15 Campaign by Spend`
  - `campaign`, `spend`

Tables:
- Table `ทุกแคมเปญ` columns:
  - `campaign`
  - `spend`
  - `impressions`
  - `reach`
  - `clicks`
  - `purchases`
  - `sales`
  - `roas`
  - `chats`
  - `cpm`

### 4.4 Demographics

Data source:
- `getDemographics(filters, importType="gender_age")`

KPI:
- ไม่มี KPI card เฉพาะหน้าใน V1

Charts:
1. Doughnut: `สัดส่วน Spend ตามเพศ` (`gender[].name`, `gender[].spend`)
2. Bar: `Spend ตามช่วงอายุ` (`age[].name`, `age[].spend`)

Tables:
- Table `Cross เพศ × อายุ` columns:
  - `gender`
  - `age_range`
  - `spend`
  - `impressions`

### 4.5 Import Data

Data sources:
- `previewCsv(filePath, importType)`
- `importCsv(filePath, importType)`
- `getImportLogs()`

KPI:
- ไม่มี KPI card เฉพาะหน้าใน V1

Charts:
- ไม่มี chart

Tables:
1. `Preview 5 แถวแรก` (dynamic columns จาก header CSV)
2. `ประวัติการ Import` columns:
   - `importedAt`
   - `filename`
   - `importType`
   - `rowsImported`
   - `rowsSkipped`
   - `dateFrom`
   - `dateTo`

### 4.6 Settings

Data sources:
- `getMeta()`
- `saveTokenProfile()` (desktop service)
- `testConnection()` (desktop service)

KPI:
- ไม่มี KPI card

Charts:
- ไม่มี chart

Tables:
- ไม่มี table

Displayed fields (minimum):
1. `dbPath`
2. active token profile name
3. last connection test status/time

## 5) Definition of Done (Per Page)

## 5.1 Dashboard - DoD

1. แสดง KPI ครบ 18 ค่า และค่าเป็นรูปแบบถูกต้อง (เงิน/เปอร์เซ็นต์/จำนวน)
2. กราฟ Spend + ROAS โหลดได้เมื่อเปิดหน้าแรก
3. เปลี่ยน filter แล้ว KPI/กราฟอัปเดตตรงกันภายใต้เงื่อนไขเดียวกัน
4. ไม่มี `NaN` หรือค่าแตกเมื่อข้อมูลว่าง
5. เวลาโหลดหน้าด้วยข้อมูล local ปกติไม่เกิน 2 วินาที

## 5.2 Daily Trends - DoD

1. แสดงกราฟครบ 6 กราฟ
2. ทุกกราฟใช้แกนเวลาเดียวกันตาม filter
3. เปลี่ยน filter แล้วทุกกราฟ refresh สำเร็จในรอบเดียว
4. ไม่มีจุดข้อมูลซ้ำ/ขาดจาก grouping รายวัน

## 5.3 Campaign View - DoD

1. กราฟ Top 15 ต้องตรงกับลำดับ `spend DESC`
2. ตารางทุกแคมเปญเรียงค่าได้ถูกต้องและ format ค่าถูกประเภท
3. ค่าในกราฟและตาราง reconcile กันได้ภายใต้ filter เดียวกัน

## 5.4 Demographics - DoD

1. กราฟเพศ/อายุแสดงจาก `gender_age` เท่านั้น
2. ตาราง Cross เพศ × อายุตรงกับผลรวมในกราฟ
3. หากไม่มีข้อมูล `gender_age` ต้องแสดงสถานะว่างอย่างสุภาพ (ไม่ crash)

## 5.5 Import Data - DoD

1. เลือกไฟล์ CSV ได้จาก dialog และ drag-drop
2. Preview 5 แถวแรกแสดงได้ก่อน import
3. Import แล้วบันทึก log ทุกครั้ง (สำเร็จ/ข้ามซ้ำ)
4. Import ไฟล์ซ้ำต้องไม่เกิดข้อมูลซ้ำใน `ad_entries`
5. หลัง import แล้วหน้าอื่นอ่านข้อมูลใหม่ได้ทันทีหลัง refresh filter

## 5.6 Settings - DoD

1. แสดง `dbPath` จริงที่แอปใช้งาน
2. บันทึก token profile สำเร็จโดยไม่เก็บ plaintext token ใน SQLite
3. ทดสอบการเชื่อมต่อ API ได้ และแสดงผล pass/fail พร้อมเวลา
4. log/error message ต้องไม่เผย token จริง

## 6) Global DoD (ทั้งแอป)

1. ติดตั้งบนเครื่อง Windows ใหม่ได้ด้วยไฟล์ installer เดียว
2. เปิดแอปได้โดยไม่ต้องติดตั้ง Node/Python เพิ่ม
3. ไม่มีการบังคับเปิด public server port
4. ปิด-เปิดแอปใหม่แล้วยังคงข้อมูล/การตั้งค่าเดิม
5. Build output ต้องมีทั้ง `nsis` และ `portable`

## 7) Scope Freeze Decision

เอกสารนี้เป็น baseline ของ V1:
1. ถ้าฟีเจอร์ไม่อยู่ใน In Scope ให้ย้ายเข้า backlog ทันที
2. การเพิ่ม scope ใหม่ต้องมีผลกระทบเวลา/ความเสี่ยงก่อนอนุมัติ
3. เกณฑ์รับงานอ้างอิง Definition of Done ในเอกสารนี้เท่านั้น
