# Task 5-b — full-stack-developer (AI Features)

## Task
Built the 4 AI features for AFC: chat, smart-journal (VLM OCR), financial insight, and tax review. Created 4 backend API routes using `z-ai-web-dev-sdk` (server-side only) + 4 client view components (neuphormic UI). Shared server-side finance-context helper.

## Files Created
- `src/lib/ai-context.ts` — server-side helper: `computeAiFinanceContext()` (KPIs, monthly trend, ratios, top expenses, fiscal context), `financeContextToCompactJson()`, `logAiCall()`, `withTimeout()`. Uses saldo formula: `saldoAwal + (posisi Debit? debet−kredit : kredit−debet)` for current-year journals.
- `src/app/api/ai/chat/route.ts` — POST: AI Chat Accounting. LLM multi-turn with system prompt + embedded current-year finance context. Returns `{ reply }`. Best-effort AiLog.
- `src/app/api/ai/smart-journal/route.ts` — POST: VLM `createVision` on invoice data URL. Strips markdown fences + extracts JSON. Returns `{ draft, raw }` or `{ draft: null, raw, error }` on parse failure.
- `src/app/api/ai/insight/route.ts` — POST: LLM financial analysis. Computes KPIs/ratios/monthly server-side, returns `{ analysis, context }`.
- `src/app/api/ai/tax-review/route.ts` — POST: LLM tax review. Computes fiscal context (entertainment 6.1.2, promosi 6.1.1, koreksiPositif/Negatif, estimasi PPh 22%), returns `{ analysis, context }`.
- `src/components/afc/views/ai-chat.tsx` — chat UI: scrollable message area (`max-h-[60vh] neu-scroll`), user right (neu-sm bg-primary/10), AI left (neu-inset-sm) rendered via `react-markdown`, suggested-prompt chips, textarea+send, loading spinner, auto-scroll.
- `src/components/afc/views/ai-smart-journal.tsx` — dropzone (PNG/JPEG/WEBP/PDF, 10MB), preview thumbnail, "Analisis dengan AI" button, loading state, result panel with draft info + journal lines table + balance check + "Gunakan Draft Ini" (stores to localStorage `afc-ai-draft`, navigates to journal-create) + collapsible raw output.
- `src/components/afc/views/ai-insight.tsx` — "Generate Insight" button, 6 KPI cards + 5 ratio cards grid, AI analysis rendered via `react-markdown` in `rounded-2xl neu p-6`.
- `src/components/afc/views/ai-tax-review.tsx` — "Jalankan Tax Review" button, 5 fiscal context cards (laba komersial, entertainment, promosi, estimasi koreksi, estimasi PPh tambahan) + 2 koreksi counter cards, AI analysis markdown.

## Verification (dev.log)
```
POST /api/ai/insight 200 in 19.8s (compile: 286ms, render: 19.5s)
POST /api/ai/tax-review 200 in 25.3s (compile: 219ms, render: 25.1s)
POST /api/ai/chat 200 in 2.0s (compile: 429ms, render: 1579ms)
POST /api/ai/smart-journal 400 in 381ms (compile: 368ms, render: 13ms)   ← validation: no image
POST /api/ai/smart-journal 200 in 4.0s (compile: 11ms, render: 4.0s)     ← valid VLM call on sample invoice
GET / 200 in 269ms (compile: 65ms, render: 204ms)                         ← all 4 AI views statically imported by ViewRegistry compile clean
```

## AiLog table (verified)
4 rows created — FinancialInsight, TaxReview, Chat, SmartJournal — each with input preview + output text.

## TypeScript
`bunx tsc --noEmit` shows ZERO errors in any new AI file (pre-existing errors only in examples/, skills/, src/app/layout.tsx, src/lib/format.ts).

## ESLint
`node node_modules/eslint/bin/eslint.js` on all 5 AI source paths → 0 errors, 0 warnings.

## Stack / Conventions Followed
- Neuphormism utility classes: `neu`, `neu-sm`, `neu-inset`, `neu-inset-sm`, `neu-flat`, `neu-hover`, `neu-pressable`, `neu-scroll`, `tnum`. No indigo/blue. Primary-tinted via `bg-primary/10 border-primary/20`.
- Indonesian labels throughout.
- Mobile responsive (mobile-first, `grid-cols-2 sm:grid-cols-3 xl:grid-cols-6` patterns, sticky-friendly cards).
- shadcn/ui components used: Button, Textarea, Badge, Collapsible.
- `react-markdown` v10 for AI replies.
- `toast` from `sonner` for feedback.
- `useNav().setView('journal-create')` for cross-view navigation.
- `export const dynamic='force-dynamic'` and `export const maxDuration = 60` on every AI route.
- z-ai-web-dev-sdk imported only in route.ts (server-side).
- All SDK calls wrapped in `withTimeout` (50–55s) + try/catch; failures return 502/504 with Indonesian error message.
- AiLog writes are best-effort (don't fail request on log error).
