# ADR-026: Document Chat UI Pattern

**Status:** Accepted
**Date:** 2026-05-19
**Decision Makers:** Development Team, UX Lead, System Architect
**Related Documents:**
- [ADR-025: AI Tool Layer Architecture](./ADR-025-ai-tool-layer-architecture.md)
- [ADR-024: Intent Classification Strategy](./ADR-024-intent-classification-strategy.md)
- [ADR-023A: Unified AI Architecture — Model Revision](./ADR-023A-unified-ai-architecture.md)
- [CONTEXT.md](../../CONTEXT.md)

> **หมายเหตุ:** ADR นี้กำหนดรูปแบบ UI สำหรับ Document Chat — ช่องสนทนากับ AI ใน context ของเอกสารที่กำลังดูอยู่ เป็นการเชื่อมต่อระหว่างผู้ใช้กับ AI Runtime Layer (ADR-024/025)

---

## Context and Problem Statement

เมื่อ Intent Classifier (ADR-024) และ AI Tool Layer (ADR-025) พร้อมใช้งาน ผู้ใช้ต้องการช่องทางโต้ตอบกับ AI ใน context ของเอกสารที่กำลังดูอยู่

ความท้าทาย:
1. **Context switching** — ถ้าเปิดหน้าใหม่ ผู้ใช้จะเสีย context ของเอกสารที่กำลังดู
2. **Screen real estate** — หน้าจอต้องแสดงทั้งเอกสารและ chat พร้อมกัน
3. **Multi-device** — ต้องรองรับ desktop (จอใหญ่) และ tablet (ไซต์ก่อสร้าง)
4. **Workflow continuity** — ผู้ใช้อาจสลับไปมาระหว่างดูเอกสารและถาม AI หลายครั้ง

---

## Decision Drivers

- **Preserve document context** — ผู้ใช้ต้องเห็นเอกสารและ chat พร้อมกัน
- **Minimize interruption** — ไม่ควร block หน้าจอ หรือพาไปหน้าใหม่
- **Responsive design** — รองรับ 1920×1080 (office) ถึง 768×1024 (tablet ไซต์)
- **Collapsible** — ผู้ใช้ควบคุมได้ว่าจะเปิด/ปิด chat หรือไม่
- **Consistent placement** — อยู่ตำแหน่งเดียวกันทุกหน้า เพื่อ muscle memory

---

## Considered Options

### Option A: Modal (Overlay Dialog)

Chat แสดงเป็น modal กลางจอ พร้อม backdrop มืด

**Pros:**
- Implement ง่าย ใช้ shadcn Dialog ได้เลย
- โฟกัสที่ chat สุดๆ ไม่มีสิ่งรบกวน

**Cons:**
- ❌ บดบังเอกสารทั้งหมด — เสีย context
- ❌ ปิด modal = สนทนาหาย — ไม่เหมาะกับการสลับไปมา
- ❌ ไม่เห็นข้อมูลเอกสารระหว่างพิมพ์คำถาม

### Option B: Separate Page (/documents/[id]/chat)

Chat เป็นหน้าแยกต่างหาก มี URL ของตัวเอง

**Pros:**
- URL shareable — ส่งลิงก์สนทนาให้คนอื่นได้
- หน้าจอเต็มที่สำหรับ chat history ยาวๆ

**Cons:**
- ❌ Context switching รุนแรง — ต้องกด Back เพื่อดูเอกสาร
- ❌ ต้องโหลดเอกสารซ้ำในหน้า chat (หรือเก็บ state ซับซ้อน)
- ❌ ไม่สามารถดูเอกสารและ chat พร้อมกัน

### Option C: Side-panel (Right side, collapsible) ✅ (เลือก)

Chat แสดงเป็น panel ทางขวา กด toggle เปิด/ปิดได้

**Pros:**
- ✅ เอกสารยังเห็นอยู่ — context ไม่หาย
- ✅ สลับไปมาง่าย — toggle เปิด/ปิดไม่กระทบเอกสาร
- ✅ รองรับ responsive — desktop (fixed 400px), tablet (30% width), mobile (bottom sheet)
- ✅ ตำแหน่ง consistent — อยู่ขวาทุกหน้า

**Cons:**
- เอกสารหลักเหลือพื้นที่น้อยลงเมื่อเปิด chat
- Implement resizable ซับซ้อนกว่า modal (แต่ v1 ใช้ fixed width)

---

## Decision

**เลือก Option C: Right-side collapsible side-panel**

---

## Layout Specification

### Desktop (≥ 1024px)

```
┌─────────────────────────────────────────────────────────────┐
│  Header                                                     │
├─────────────────────────────────────┬───────────────────────┤
│                                     │                       │
│  Document Content                   │  [Toggle] AI Chat     │
│  (remaining width)                │  ─────────────────    │
│                                     │  User: สรุป RFA นี้   │
│  • Drawing A-101                    │  ─────────────────    │
│  • Revision 3                       │  AI: ตอบ...           │
│  • Status: Approved                 │  ─────────────────    │
│                                     │  [Suggested Actions]  │
│                                     │  • View latest RFA    │
│                                     │  • Create new RFA     │
│                                     │                       │
└─────────────────────────────────────┴───────────────────────┘
```

- **Panel width:** 400px (fixed)
- **Toggle button:** มุมขวาบนของเอกสาร (แยกจาก panel)
- **Animation:** slide in/out 200ms ease-out
- **Z-index:** 40 (สูงกว่าเอกสาร แต่ต่ำกว่า modal/dialog อื่น)

### Tablet (768px – 1023px)

```
┌────────────────────────────────────────┐
│  Header                                │
├────────────────────┬───────────────────┤
│                    │                   │
│  Document          │  AI Chat (30%)    │
│  (70%)             │                   │
│                    │                   │
└────────────────────┴───────────────────┘
```

- **Panel width:** 30% of viewport
- **Min-width:** 320px (ถ้าน้อยกว่า ให้เป็น overlay แทน)

### Mobile (< 768px)

```
┌─────────────────────────────┐
│  Header                     │
├─────────────────────────────┤
│                             │
│  Document Content           │
│  (เต็มจอ)                   │
│                             │
│                             │
├─────────────────────────────┤
│  [💬] Toggle Chat           │  ← floating button
└─────────────────────────────┘

เมื่อกด Toggle:
┌─────────────────────────────┐
│  Header                     │
├─────────────────────────────┤
│  AI Chat (Bottom Sheet)     │
│  ─────────────────────      │
│  สูง 60% ของจอ              │
│                             │
│  [Drag handle]              │
└─────────────────────────────┘
```

- **Pattern:** Bottom sheet (shadcn Sheet with side="bottom")
- **Height:** 60% of viewport (expandable to 90%)
- **Backdrop:** มี overlay มืดบางๆ บนเอกสาร

---

## Component Structure

```
frontend/components/ai/
├── ai-chat-panel.tsx           ← หลัก (รวมทุก breakpoint)
├── ai-chat-toggle.tsx          ← ปุ่มเปิด/ปิด (floating บน mobile)
├── ai-chat-messages.tsx        ← message list + bubble
├── ai-chat-input.tsx           ← input + send button
├── ai-suggested-actions.tsx    ← action chips
└── hooks/
    └── use-ai-chat.ts          ← TanStack Query + state
```

---

## State Management

### Local State (per page)

```typescript
// useAiChat hook
interface AiChatState {
  isOpen: boolean;           // panel เปิดอยู่หรือไม่
  messages: ChatMessage[];   // สนทนาใน session นี้
  isLoading: boolean;      // AI กำลังตอบ
  suggestedActions: SuggestedAction[]; // actions ล่าสุดจาก AI
}
```

### Persistence

- **Session storage:** เก็บ `messages` ต่อ session (refresh = หาย)
- **No server persistence:** v1 ไม่เก็บ chat history บน server (ลด scope)
- **Context preservation:** `documentPublicId` + `documentType` ส่งทุก request เพื่อให้ AI รู้ context

---

## Integration with AI Runtime Layer

### Request Flow

```
User พิมพ์ใน Chat Input
    │
    ▼
AiChatInput → useAiChat.sendMessage()
    │
    ▼
POST /api/ai/chat
{
  "query": "สรุปเอกสารนี้",
  "context": {
    "type": "drawing",
    "publicId": "0195..."
  }
}
    │
    ▼
AI Gateway (ADR-023A)
    │
    ├─→ Intent Classifier (ADR-024)
    │
    ├─→ AI Tool Layer (ADR-025) ถ้าเป็น GET_* intent
    │
    └─→ RAG Pipeline ถ้าเป็น RAG_QUERY
    │
    ▼
Ollama (gemma4:e2b)
    │
    ▼
Response → Stream/Chunk → UI
```

### Context Injection

ทุก request อัตโนมัติแนบ `context` จากหน้าปัจจุบัน:

| Page | context.type | context.publicId |
|------|--------------|------------------|
| /drawings/[id] | "drawing" | drawing.publicId |
| /rfas/[id] | "rfa" | rfa.publicId |
| /transmittals/[id] | "transmittal" | transmittal.publicId |
| /correspondences/[id] | "correspondence" | correspondence.publicId |

---

## UX Patterns

### Initial State

- **Default:** Panel ปิด (ผู้ใช้ต้องกดเปิดเอง)
- **First visit:** แสดง subtle hint (pulse animation ที่ toggle button) ครั้งเดียว
- **Returning user:** จำ state จาก session storage (ถ้าเคยเปิดไว้ → เปิดต่อ)

### Message Types

| Type | ลักษณะ | ตัวอย่าง |
|------|--------|----------|
| **User** | ขวา, primary color | "สรุป RFA นี้ให้หน่อย" |
| **AI Text** | ซ้าย, สีธรรมดา | ข้อความตอบ |
| **AI Tool Result** | ซ้าย, card style | รายการ RFA 3 รายการ |
| **AI Suggestion** | ซ้ายล่าง, chip buttons | "ควรสร้าง RFA ใหม่?" |
| **System** | กลาง, จางๆ | "กำลังเชื่อมต่อ AI..." |

### Suggested Actions

AI ตอบพร้อม suggested actions (ถ้ามี):

```
┌─────────────────────────────────┐
│ AI: สรุป RFA-0042                 │
│ • ส่ง 2024-05-10                  │
│ • สถานะ: รอตอบกลับ                │
│ • มี 3 drawings                   │
├─────────────────────────────────┤
│ [ดู RFA ฉบับเต็ม] [สร้าง RFA ตัวถัดไป] │
└─────────────────────────────────┘
```

- Action กดได้ทันที ไม่ต้องพิมพ์ใหม่
- ถ้ากด → ส่ง query ใหม่อัตโนมัติ (เช่น "ดู RFA ฉบับเต็ม")

---

## Error Handling

### Network Error

- แสดง "ไม่สามารถเชื่อมต่อ AI ได้ กรุณาลองใหม่" + ปุ่ม Retry
- ไม่ clear messages ที่มีอยู่

### AI Timeout (> 10s)

- แสดง "AI ตอบช้าเกินไป กรุณาลองอีกครั้ง"
- Log ใน ai_audit_logs สำหรับ debug

### Permission Error (จาก Tool Layer)

- แสดง "คุณไม่มีสิทธิ์เข้าถึงข้อมูลนี้" (จาก message ที่ Tool Layer คืน)
- ไม่ expose technical details

---

## Accessibility

- **Keyboard:** Toggle ด้วย `Ctrl/Cmd + .` (custom shortcut)
- **Focus trap:** เมื่อ panel เปิด focus อยู่ใน panel จนกว่าจะปิด
- **Screen reader:** อ่าน "AI Chat panel opened" / "AI message received"
- **Reduced motion:** ปิด animation เมื่อ user ตั้งค่า reduced motion

---

## Audit & Observability

ทุก interaction บันทึกใน `ai_audit_logs`:

```json
{
  "action": "chat_message",
  "contextType": "drawing",
  "contextPublicId": "0195...",
  "query": "สรุปเอกสารนี้",
  "responseType": "text",
  "hasSuggestedActions": true,
  "latencyMs": 2500,
  "projectPublicId": "...",
  "userPublicId": "..."
}
```

---

## Consequences

### Positive
- Context ของเอกสารไม่หายระหว่างถาม AI
- สลับไปมาระหว่างเอกสารและ chat ได้ราบรื่น
- Responsive รองรับทั้ง office และไซต์ก่อสร้าง
- ตำแหน่ง consistent — ผู้ใช้รู้ว่าหา chat ที่ไหน

### Negative
- เอกสารหลักเหลือพื้นที่น้อยลงบนจอเล็ก (แต่ collapsible ช่วยได้)
- Mobile bottom sheet อาจบดบังเนื้อหาส่วนล่างของเอกสาร
- Chat history ไม่ persist (refresh หาย) — v2 อาจเพิ่ม server persistence

### Risks
- User เปิด chat ทิ้งไว้แล้วลืม → สิ้นเปลืองพื้นที่จอ → mitigate ด้วย auto-collapse เมื่อ navigate ไปหน้าอื่น
- Mobile bottom sheet gesture ชนกับ scrolling → mitigate ด้วย drag handle ชัดเจน

---

## Out of Scope (Phase 4)

- **Multi-document chat** — chat ที่ context หลายเอกสารพร้อมกัน
- **Persistent chat history** — เก็บสนทนาย้อนหลังบน server
- **Real-time collaboration** — หลายคน chat ในห้องเดียวกัน
- **Voice input** — พิมพ์อย่างเดียว (v1)

---

## Migration Notes (ADR-009)

- ไม่มี schema change — ADR-026 เป็น frontend-only decision
- ใช้ตาราง `ai_audit_logs` ที่มีอยู่แล้วสำหรับ logging
- เพิ่ม component ใน `frontend/components/ai/`

