Work 05 · Deep dive

เว็บแคตตาล็อกสินค้าสำหรับลูกค้า B2B

B2B product catalog website

เว็บสาธารณะ · เปิดใช้จริง
Public website · live

เว็บที่ให้ร้านค้าและผู้รับเหมาเปิดดูสินค้า ราคา และสต็อกได้เอง ข้อมูลทั้งหมดดึงมาจาก ERP ในออฟฟิศอัตโนมัติ ไม่มีใครต้องมานั่งกรอกซ้ำ

A site where shops and contractors browse products, prices and stock themselves. Everything is pulled from the office ERP automatically; nobody re-types anything.

How it works

ระบบทำงานยังไง

How it works

01
ERP ในออฟฟิศERP in the officeสินค้า ราคา การเคลื่อนไหวสต็อกProducts, prices, stock movements
02
สคริปต์ทุก 5 นาทีScript every 5 minutesคำนวณสต็อก แล้วส่งขึ้นคลาวด์พร้อมรหัสยืนยันCalculates stock and pushes it to the cloud with a secret key
03
คลาวด์Cloudเก็บข้อมูลไว้ให้เว็บอ่าน ฟรีไม่ต้องผูกบัตรStores the data for the site; free tier, no card needed
04
เว็บลูกค้าCustomer siteค้นหา ดูหมวด ใส่ตะกร้า ส่งฟอร์มสอบถามSearch, browse categories, cart, inquiry form
05
อีเมลแจ้งเตือนEmail alertรายการที่ลูกค้าสอบถามเด้งเข้าอีเมลทันทีCustomer inquiries land in email instantly
06
หลังบ้านAdminเจ้าของแก้ราคา รายละเอียด และตั้งค่าเว็บเองThe owner edits prices, details and site settings

ขั้นที่มีคนเป็นผู้ตัดสินใจหรือใช้งานSteps where a person decides or uses it

หน้ารวมสินค้าบนเดสก์ท็อป แบ่งตามหมวดจริงจาก ERP (โลโก้ร้านถูกเบลอ)
หน้ารวมสินค้าบนเดสก์ท็อป แบ่งตามหมวดจริงจาก ERP (โลโก้ร้านถูกเบลอ)
Product listing on desktop, organized by the ERP's real categories (shop logo blurred)
บนมือถือ: ราคา สต็อกคงเหลือ และปุ่มใส่ตะกร้า
บนมือถือ: ราคา สต็อกคงเหลือ และปุ่มใส่ตะกร้า
On mobile: price, remaining stock and add-to-cart
My calls

ส่วนที่ผมเป็นคนตัดสินใจ

Decisions I made

AI เขียนโค้ดให้ แต่โจทย์ เกณฑ์ และสิ่งที่ยอมไม่ได้ ผมเป็นคนกำหนด

The AI writes the code; the problem, the criteria and the non-negotiables are mine.

ใช้ของฟรีให้มากที่สุดFree wherever possible

ธุรกิจเล็กไม่ควรมีค่าใช้จ่ายรายเดือนเพิ่ม ผมเลยเลือกบริการคลาวด์ที่ใช้ฟรีได้และไม่ต้องผูกบัตร

A small business shouldn't gain a new monthly bill, so I chose cloud services with a usable free tier and no card required.

สต็อกหมดต้องหมดจริงOut of stock must mean out of stock

สินค้าที่ไม่เคยรับเข้าระบบเลยจะไม่ถูกขึ้นว่าหมด เพราะบางตัวเป็นของยกมาจากก่อนใช้ระบบ ถ้าขึ้นว่าหมดจะทำให้ลูกค้าเข้าใจผิด

Items never received into the system are not shown as sold out, because some are legacy stock from before the system; showing them as out would mislead customers.

สินค้าชุดคิดจากชิ้นส่วนBundles are computed from their parts

ถ้าชิ้นส่วนไหนของชุดหมด ทั้งชุดจะขึ้นว่าหมด และจำนวนที่เหลือคิดจากชิ้นส่วนที่มีน้อยที่สุด

If any part of a bundle is out, the whole bundle shows as out, and the remaining count comes from the scarcest part.

วัดผลได้ตั้งแต่วันแรกMeasurable from day one

ติด Google Analytics 4 ส่งแผนผังเว็บให้ Google และยืนยันเว็บกับ Search Console

Google Analytics 4, a sitemap submitted to Google, and the site verified in Search Console.

Code

โค้ดจริงบางส่วน

Real code excerpts

โค้ดทั้งหมดเขียนโดย Claude Code ตามที่ผมสั่ง ผมอ่านแล้วอธิบายได้ว่าแต่ละท่อนทำอะไร และทำไมต้องเขียนแบบนี้ (ข้อมูลลับอย่างรหัสผ่านและที่อยู่เครื่องถูกตัดออก)

All code was written by Claude Code under my direction. I can explain what each part does and why it's written that way. (Secrets such as keys and machine paths are removed; code comments are in Thai.)

รับข้อมูลจากออฟฟิศแบบปลอดภัย

Accepting data from the office securely

ฝั่งคลาวด์รับข้อมูลเฉพาะจากเครื่องในออฟฟิศที่ส่งรหัสลับมาถูกเท่านั้น และรับแค่ข้อมูลสามชนิดที่กำหนดไว้ ถ้าใครยิงข้อมูลอื่นเข้ามาจะถูกปฏิเสธ (รหัสจริงเก็บเป็นความลับบนคลาวด์ ไม่อยู่ในโค้ด)

The cloud side only accepts data from the office machine when the secret key matches, and only the three data types defined. Anything else is rejected. (The real key is stored as a cloud secret, not in the code.)

if (url.pathname === "/push" && req.method === "POST") {
  if ((req.headers.get("Authorization") || "") !== "Bearer " + env.PUSH_TOKEN)
    return j({ error: "unauthorized" }, 401);
  let b;
  try { b = await req.json(); } catch { return j({ error: "bad json" }, 400); }
  if (!b || !["stock", "settings", "catalog"].includes(b.key))
    return j({ error: "bad key" }, 400);
  await env.KV.put(b.key, JSON.stringify(b.data));
  return j({ ok: true, key: b.key });
}
worker.jsworker.js · เขียนโดย Claude Codewritten by Claude Code

สินค้าชุด: ชิ้นไหนหมด ชุดหมด

Bundles: one part out, bundle out

เช็คทุกชิ้นในชุดกับรายการสินค้าที่หมด แล้วคำนวณว่ายังประกอบได้อีกกี่ชุด จากชิ้นที่เหลือน้อยที่สุด

Check every part of a bundle against the out-of-stock list, then compute how many bundles can still be made from the scarcest part.

const setOut = p.set.some(c => OUT.has(c.code));
const setsLeft = Math.min(...p.set.map(c => Math.floor(BAL[c.code] / c.qty)));
app.js (ย่อ)app.js (condensed) · เขียนโดย Claude Codewritten by Claude Code
Problems solved

ปัญหาที่เจอ และวิธีแก้

Problems and fixes

ปัญหาProblem

แก้หน้าเว็บแล้วมือถือยังเห็นของเก่า

Phones kept showing the old page after updates

แก้ยังไงFix

สั่งไม่ให้เบราว์เซอร์เก็บหน้าแรกไว้ในแคช

Told browsers not to cache the main page

ปัญหาProblem

ของยกมาขึ้นว่าหมดทั้งที่มีของ

Legacy stock showed as sold out

แก้ยังไงFix

นับว่าหมดเฉพาะสินค้าที่เคยรับเข้าระบบแล้วเท่านั้น

Only count as sold out items that have been received into the system

ปัญหาProblem

หลังบ้านต้องเข้าจากข้างนอกได้ แต่ห้ามเปิดเครื่องสู่สาธารณะ

Admin had to be reachable remotely without exposing the office machine

แก้ยังไงFix

ใช้อุโมงค์เชื่อมต่อของคลาวด์แทนการเปิดพอร์ต

Used the cloud provider's tunnel instead of opening ports

← แบ่งกลุ่มลูกค้าตามพฤติกรรมการซื้อBehavior-based customer segmentationเครื่องมือทำรายงานผลการเทรดTrade review reporting tool →