1. ภาพรวม
NongNorn คือเว็บอีคอมเมิร์ซขายเตียงสำหรับหมา/แมว ออกแบบให้โตระดับโลกได้ตั้งแต่วันแรก
| ด้าน | เทคโนโลยี |
| Framework | Next.js 16 (App Router) · React 19 · TypeScript |
| Styling | Tailwind CSS v4 (semantic token, dark mode แบบ class) |
| Database | Supabase — PostgreSQL + PostGIS + Storage + Auth |
| i18n | next-intl (เส้นทาง /th · /en) |
| Payments | Stripe (บัตร/คริปโต) + โอนเงิน + อัปสลิป |
| AI | Google Gemini (free tier) — ผู้ช่วยแชท |
| Deploy | Cloudflare (OpenNext Workers) + Cloudflare Stream (วิดีโอ) |
โค้ดอยู่บน GitHub: github.com/artwa74/nongnorn
2. เริ่มใช้งาน
ยังไม่ต้องมี Supabase ก็รันได้ — เว็บจะเข้าสู่ โหมดเดโม่ (ข้อมูลตัวอย่าง + avatar น้องตัวอย่าง)
npm install
npm run dev # เปิด http://localhost:3000 → เด้งไป /th
ใส่คีย์จริงเมื่อพร้อม: ก๊อป .env.example เป็น .env.local แล้วเติมค่า Supabase / Stripe / Cloudflare / Gemini
คำสั่งที่ใช้บ่อย
| คำสั่ง | ทำอะไร |
npm run dev | รันเซิร์ฟเวอร์พัฒนา |
npm run build | สร้าง production build |
npm run db:push | ส่ง schema เข้า Supabase |
npm run cf:deploy | deploy ขึ้น Cloudflare |
3. โครงสร้างโปรเจกต์
src/
├─ app/[locale]/ หน้าเว็บ (layout + home + products/[slug])
├─ app/api/assistant/ ผู้ช่วย AI (route handler)
├─ components/
│ ├─ layout/ Navbar, PromoBar, ThemeToggle, LocaleSwitcher
│ ├─ product/ ProductCard, ProductDetail, ProductView
│ ├─ branch/ BranchShowcase
│ ├─ promo/ PromotionsSection
│ ├─ news/ NewsTicker
│ ├─ assistant/ AssistantDock (แชท mascot)
│ └─ ui/ Reveal (motion), Stars
├─ contexts/ PetAvatarContext, ThemeContext
├─ i18n/ routing, request, navigation
└─ lib/ money, media, mascots, demo-data, cms-demo,
supabase/, cloudflare-stream
messages/th.json · messages/en.json ข้อความสองภาษา
public/PicAvatar/ รูป mascot (malee, mabel)
supabase/migrations/ 0001 schema · 0002 media+slips · 0003 cms
4. Design system
โทนอุ่นเฟรนด์ลี กำหนดเป็น token ใน src/app/globals.css สลับ light/dark อัตโนมัติ
สี (light)
| Token | ใช้กับ | Hex |
| paper | พื้นหลัง | #faf7f2 |
| surface | การ์ด | #ffffff |
| ink | ตัวอักษรหลัก | #2b2420 |
| muted | ตัวอักษรรอง | #7a6e63 |
| line | เส้นขอบ | #e7ded3 |
| accent | สีแบรนด์ | #a4632e (dark #c8854f) |
| accent-soft | พื้นเน้นอ่อน | #f1e7dc |
ฟอนต์
Latin · Geistไทย · IBM Plex Sans Thai ตั้ง stack แบบ per-glyph — ละตินใช้ Geist, ไทยตกไป IBM Plex Sans Thai อัตโนมัติ
ธีมสี (6 โทน)
เลือกได้จากปุ่มจานสีบน Header หรือหน้า /admin: อบอุ่น (ค่าเริ่มต้น) · ขาว–ฟ้า · เทาเท่ · น้ำผึ้ง · ดินเผา · ดอกไม้ — ทำงานคู่กับ dark/light, จำค่าไว้ใน localStorage (nn.palette) และใช้ได้ทั้งเว็บผ่าน html[data-theme] override ตัวแปรสี
หน้าหลังบ้าน (Studio)
/admin — แดชบอร์ดมี sidebar: ภาพรวม · สินค้า · ออเดอร์ · โปรโมชั่น · ข่าวสาร · รีวิว · มาสคอต · ธีม/หน้าตา. สินค้า/โปรโมชั่น/ข่าว เพิ่ม/แก้/ลบได้จริง (CRUD) เก็บใน localStorage และ หน้าร้านดึงข้อมูลชุดเดียวกัน (ผ่าน CmsContext) — แก้ในหลังบ้านเห็นผลหน้าร้านทันที. ธีมสีตั้งได้เฉพาะในหลังบ้าน (หน้า ธีม/หน้าตา) ส่วนหน้าร้านมีแค่ปุ่ม dark/light. เชื่อม Supabase จริงแล้ว (โปรเจกต์ nongnorn) — สินค้า/โปรโมชั่น/ข่าวอ่านจาก DB. /blog — ฟีดบล็อกสไตล์โซเชียล
วิธีใช้หลังบ้าน (ที่เพิ่มใหม่)
- สต็อก / Sold out: ใส่จำนวนในช่อง "สต็อก" ตอนแก้สินค้า — ถ้า 0 หน้าร้านจะขึ้น "สินค้าหมด" (การ์ดเป็นขาวดำ ปุ่มกดไม่ได้), ถ้าเหลือน้อยจะขึ้น "เหลือ N ชิ้น"
- แปลไทย↔อังกฤษ (AI): ในฟอร์มแก้สินค้า/โปรโมชั่น/ข่าว มีปุ่ม
TH → EN / EN → TH — พิมพ์ภาษาเดียวแล้วกดให้เติมอีกภาษาอัตโนมัติ (Google Translate)
- แกลเลอรีตามสี: หน้าสินค้า กดเลือกสี รูปจะเปลี่ยนตามสีนั้น (ถ้าสีนั้นมีรูปของตัวเอง)
- ออเดอร์จริง (ใหม่): ลูกค้ากด "ยืนยันสั่งซื้อ" ที่หน้าชำระเงิน → คำสั่งซื้อถูกบันทึกลง Supabase จริง แล้วโผล่ที่หน้า
/admin/orders ทันที (มีป้าย "ของจริง"). เก็บชื่อ/เบอร์/ที่อยู่/รายการสินค้า/ยอดรวม/วิธีจ่าย. ยังไม่มีออเดอร์ = โชว์ตัวอย่างเดโม่
คอมโพเนนต์หลัก
ปุ่ม pill · chip หมวดหมู่ · badge ส่วนลด · ดาวรีวิว · การ์ดสินค้า · avatar น้อง · มุมโค้ง 8/12/40px · เส้น 0.5px · motion scroll-reveal (เคารพ prefers-reduced-motion)
5. สองภาษา (i18n)
ข้อความ UI อยู่ใน messages/th.json และ messages/en.json ส่วนชื่อสินค้า/สาขาเก็บเป็น jsonb ({"th":...,"en":...}) ใน DB — เพิ่มภาษาใหม่ไม่ต้องแก้ตาราง
เพิ่มภาษา (เช่น จีน)
// src/i18n/routing.ts
locales: ['th','en','zh'] // เพิ่ม 'zh'
// แล้วสร้าง messages/zh.json + เติมคีย์ zh ใน jsonb
6. แก้ไขเนื้อหา
ตอนยังไม่ต่อ DB เนื้อหามาจากไฟล์เดโม่ (src/lib/demo-data.ts, cms-demo.ts) เมื่อต่อ Supabase แล้วให้ดึงจากตารางแทน
| เนื้อหา | ไฟล์เดโม่ | ตารางจริง |
| สินค้า / SKU / สต็อก | demo-data.ts | products · variants · inventory_levels |
| สาขา + โปรโมท | demo-data.ts | branches |
| ข่าวสาร (ticker) | cms-demo.ts | news |
| โปรโมชั่น | cms-demo.ts | promotions |
| Mascot ผู้ช่วย | mascots.ts | mascots |
7. Mascot & ผู้ช่วย AI
เปลี่ยนรูป/ชื่อ Mascot
วางรูปจริงในโฟลเดอร์ public/PicAvatar/ (ใช้ชื่อเดิม หรือแก้ path ใน src/lib/mascots.ts)
malee.svg — มาลี ตัวแลบลิ้น ขี้เล่น
mabel.svg — มาเบล ใจเย็น ดูแลดี
รองรับ .png/.jpg/.webp ด้วย · รูปสี่เหลี่ยมจัตุรัส (1:1) สวยสุด · บุคลิก+คำทักทายแก้ได้ในไฟล์เดียวกัน หรือในตาราง mascots
เปิดผู้ช่วย AI จริง (Google Gemini — ฟรี)
# 1) ขอ API key ฟรีที่ https://aistudio.google.com/apikey
# 2) ใส่ใน .env.local
GEMINI_API_KEY=xxxxxxxx
GEMINI_MODEL=gemini-2.0-flash
ถ้าไม่ใส่คีย์ ผู้ช่วยจะตอบแบบเดโม่ (คำทักทาย + วิธีเปิด AI) — ใส่คีย์แล้วคุยกับลูกค้าได้จริงทันที โค้ดอยู่ที่ src/app/api/assistant/route.ts
AI ฟรีตัวอื่น (สลับได้)
Cloudflare Workers AI (รัน edge เดียวกับ deploy) · Groq (เร็วมาก) · OpenRouter (มีโมเดลฟรีหลายตัว)
8. Supabase (หลังบ้าน)
npx supabase login
npx supabase link --project-ref <project-ref-ของคุณ>
npm run db:push # ลง schema 0001–0003
# seed ข้อมูลเริ่มต้น: รัน supabase/seed.sql ใน SQL editor
ตารางหลัก
| ตาราง | หน้าที่ |
| profiles | ผู้ใช้ (ผูกกับ auth.users) ภาษา/สกุลเงิน/สิทธิ์ staff |
| pet_profiles | รูปน้อง (Pet Avatar) ตามผู้ใช้ข้ามอุปกรณ์ |
| products · variants · variant_prices | สินค้า + SKU (เช่น BED-DOG-BLK-M) + ราคาแต่ละสกุล |
| branches · inventory_levels | สาขา (PostGIS) + สต็อกต่อสาขา |
| orders · order_items · payments | คำสั่งซื้อ + การชำระเงิน |
| payment_slips | สลิปโอน (เก็บคมชัด ตรวจโดย staff) |
| mascots · promotions · news | CMS — staff แก้ avatar/โปรโมชั่น/ข่าวได้ |
ตรรกะเด่น
allocate_order() — เมื่อมีออเดอร์ ระบบหาสาขาที่ มีของ และ ใกล้ที่สุด ด้วย PostGIS แล้วจองสต็อกแบบ atomic กัน oversell · ทุกตารางที่มีข้อมูลผู้ใช้เปิด RLS
เงินเก็บเป็นจำนวนเต็มหน่วยย่อย (สตางค์/cent) เสมอ ห้ามใช้ float — กันปัญหาปัดเศษ (ดู src/lib/money.ts)
9. Deploy (Cloudflare)
npx wrangler login
npm run cf:preview # ทดสอบบน runtime ของ Workers ในเครื่อง
npm run cf:deploy # deploy ขึ้นบัญชี Cloudflare
วิดีโอสินค้าส่งขึ้น Cloudflare Stream ซึ่ง transcode เป็น adaptive HLS ให้อัตโนมัติ (เซฟแบนด์วิดท์จริง) — ดู src/lib/cloudflare-stream.ts
รูป/วิดีโอสินค้า = บีบเล็กเซฟแบนด์วิดท์ · สลิปโอน = เก็บเกือบ original ให้อ่านออก (สองเส้นทางแยกกันใน src/lib/media.ts ห้ามรวม)
10. ซื้อขาย & หลังบ้าน
ร้านขายจริงครบวงจร — เงินเก็บเป็น สตางค์ (minor units) ทั้งระบบเสมอ
หน้าร้าน (ลูกค้า)
แคตตาล็อกจริง (variant/สี/ไซซ์/ผ้า) · รูปต่อสี + วิดีโอ · รีวิว · wishlist · ตะกร้า + checkout (โอนเงิน + แนบสลิป) · คูปอง + บัตรของขวัญ + แต้มสะสม ใช้รวมกันได้ · ค่าส่งตามโซน · ติดตามออเดอร์ (ไม่ต้องล็อกอิน) + ยกเลิก/ขอคืนสินค้าเอง · SEO JSON-LD · สินค้าแนะนำ/ดูล่าสุด · ตัวช่วยเลือกไซซ์ตามน้ำหนัก · ศูนย์ช่วยเหลือ/FAQ · แชทสด + ผู้ช่วย AI
หลังบ้าน /admin
สินค้า/คลัง/รับ-กระจาย · ออเดอร์ · แพ็คของ (พิมพ์ใบแพ็ค) · คืนสินค้า (RMA) · ยอดขาย · ลูกค้า · แชท · คูปอง · บัตรของขวัญ · โปรโมชั่น/ข่าว/บล็อก · รีวิว · หน้าแรก/สาขา/มาสคอต/อีเมล · ฟีเจอร์ (สวิตช์กลาง) · ตั้งค่าร้าน · ธีม
โหมด lock หลังบ้าน: ค่าเริ่มต้นเปิดให้เข้าได้ (bootstrap) — เปิด lock ใน ตั้งค่า แล้วจะเหลือเฉพาะบัญชีทีมงาน (profiles.is_staff) ดู src/lib/admin-auth.ts
11. ขายส่ง (B2B)
พอร์ทัลแยกที่ /wholesale-admin (เมนู/หน้าเข้าของตัวเอง ใช้บัญชีทีมงานเดิม) — เปิด/ปิดทั้งระบบด้วยฟีเจอร์ wholesale
- สมัคร + อนุมัติ — ลูกค้าส่งสมัครที่
/wholesale → อนุมัติ/ปฏิเสธในหลังบ้าน → ผูกบัญชีด้วยอีเมล
- ราคาขั้นบันได — กลุ่มราคา (tier %) + price rules (ตามจำนวน/variant/สินค้า/กลุ่ม) + พรีวิวราคาสด
- MOQ + ยกลัง — ตั้งต่อสินค้า บังคับทั้งฝั่งลูกค้าและ server
- ฝั่ง buyer —
/wholesale/shop: catalog ราคาส่ง + quick order ด้วย SKU + วางออเดอร์ (จ่ายก่อน/เครดิต) + ประวัติ
- ใบเสนอราคา (RFQ) — ลูกค้าขอ → แอดมินตั้งราคา → ตอบรับ/แปลงเป็นออเดอร์
- วางบิล/เครดิต — ออกใบแจ้งหนี้ (auto เมื่อสั่งแบบเครดิต) + มาร์คชำระ + เกินกำหนดอัตโนมัติ
ราคา คิดใหม่ฝั่ง server ตอนวางออเดอร์เสมอ (กันแก้ราคา) · ดู src/lib/wholesale/
12. แต้ม · บัตรของขวัญ · คืนสินค้า
แต้มสะสม & แนะนำเพื่อน
รับแต้มอัตโนมัติตอนสั่งซื้อ (ตั้งอัตราใน ตั้งค่าร้าน) · ใช้แต้มลดเงินตอนชำระ (1 แต้ม = ฿1) · ลิงก์แนะนำเพื่อน /account?ref=CODE → ผู้แนะนำได้แต้มเมื่อเพื่อนสั่งครั้งแรก
บัตรของขวัญ
ออกบัตรในหลังบ้าน (โค้ด + ยอด) · ลูกค้ากรอกตอน checkout → ตัดยอดฝั่ง server กัน double-spend
คืนสินค้า (RMA)
ลูกค้าขอคืนจากหน้าติดตามออเดอร์ → หลังบ้านไล่สถานะ ขอคืน → อนุมัติ → รับของ → คืนเงิน
ค่าส่งตามโซน
ตั้งโซน + ค่าส่งใน ตั้งค่าร้าน → ลูกค้าเลือกโซนตอน checkout (ไม่ตั้ง = ค่าส่งเหมาเดิม)
13. Feature flags & PWA
ทุกฟีเจอร์เปิด/ปิดได้จาก /admin/features (เก็บใน app_settings.site_features) — โค้ดอ่านด้วย useFeature("key") ([[FeatureContext]]) seed จาก server กัน flash · registry: src/lib/features.ts
วิธีเพิ่มฟีเจอร์ใหม่: เพิ่ม entry ใน FEATURES → gate จุดที่ render ด้วย useFeature(key)
PWA
ติดตั้งบนมือถือได้ (manifest + public/sw.js + ปุ่มติดตั้ง) · หลายสกุลเงิน (THB/USD/SGD/EUR ในเมนูตั้งค่า) · A/B testing primitive (src/lib/ab.ts) · a11y: focus-visible + skip link + reduced-motion (ใน globals.css)
ยังต้องใส่คีย์ภายนอกถึงจะครบ: PromptPay/บัตร (เกตเวย์) · LINE OA · RESEND_API_KEY (อีเมล) · OPENAI_API_KEY (AI รูปมาสคอต) · GA4/Meta/TikTok pixel · SENTRY_DSN
14. ปัญหาที่พบบ่อย
เว็บขึ้นข้อมูลตัวอย่าง ไม่ใช่ของจริง?
ยังไม่ได้ใส่ env ของ Supabase — เป็นโหมดเดโม่ ใส่ NEXT_PUBLIC_SUPABASE_URL + ANON_KEY แล้วต่อ DB
ผู้ช่วยตอบว่า "โหมดเดโม่"?
ยังไม่ได้ใส่ GEMINI_API_KEY — ใส่แล้วรีสตาร์ทเซิร์ฟเวอร์
รูปไม่ขึ้น?
โดเมนรูปภายนอกต้องเพิ่มใน next.config.ts ที่ images.remotePatterns
เปลี่ยนฟอนต์ไทย?
แก้ที่ src/app/[locale]/layout.tsx (เปลี่ยน import จาก next/font/google) — ตัวอย่าง: Anuphan, Kanit, Prompt, Bai Jamjuree