ปัญหาที่เจอ
ทำไมกลุ่มบริษัทถึงต้องมี Digital OS ของตัวเอง?
8 แผนกใช้ระบบคนละชุด — ขอซื้อใน Excel, เบิกเงินใน LINE, จองที่พักใน Google Form, ตั๋วเครื่องบินใน Email Approval cycle 3-5 วันต่อ 1 เอกสาร และไม่มีใครเห็นภาพรวม ผู้บริหารไม่รู้ด้วยซ้ำว่างานค้างอยู่ที่ใคร
8 แผนกใช้ระบบแยก ไม่มีใครเห็นภาพรวม
ขอซื้อใน Excel, เบิกเงินใน LINE, จองที่พักใน Google Form, ตั๋วเครื่องบินใน Email — ไม่มี Source of Truth เดียว ตามไม่ได้ว่าเอกสารอยู่ที่ขั้นไหน
Approval Cycle 3-5 วันต่อเอกสาร
เอกสารต้อง print → เซ็น → สแกน → ส่งต่อ ผู้บริหารไม่รู้ว่างานค้างที่ใคร เกินวันยังไม่ได้แก้ — กระทบแผนงานทั้งบริษัท
ไม่มี Audit Trail — ตามย้อนไม่ได้
ใคร approve ใคร reject เพราะอะไร ใช้งบเกินวงเงินหรือไม่ — ไม่มีบันทึก พอ Auditor มาตรวจหาเอกสารกันยกใหญ่
ทำเอกสาร PDF ด้วยมือทุกครั้ง
เปิด Word เติมตัวเลข → export PDF → print → เซ็นปากกา → สแกนกลับ — เปลืองเวลา ผิดพลาดเยอะ ไม่มี standard layout
15+ User Roles สิทธิ์ต่างกัน
Hardcode role check ทั่ว codebase — แค่เพิ่ม role ใหม่ก็ต้องไล่แก้ทุกหน้า maintain ไม่ไหวเมื่อบริษัทขยาย
วิธีแก้ปัญหา
1 Portal เดียว — Digital OS pattern ที่รองรับทุกแผนกพร้อมกัน
1 Portal เดียวรวมทุก Request Type
PR/PO, Expense, Booking, HR Request — เข้าใน Portal เดียว 1 source of truth ผู้ใช้ login ครั้งเดียว เห็นทุก task ของตัวเอง
Approval Engine แบบ Configurable
State machine กลาง 1 ตัว รองรับ chain แบบ M3→E1→E2→E3→CEO ตามวงเงิน — แต่ละ doc type / department config เองได้ ไม่ต้องเขียน code ใหม่
Real-time Status ผ่าน Socket.io
ผู้ขอเห็นทันทีว่างานค้างที่ใคร ผู้บริหารเห็น dashboard "เกินวันค้าง" แบบ real-time — ลด follow-up ใน LINE ไปได้กว่า 80%
Document PDF Component ที่ Reuse ได้
Voucher + Signature Block + Attachment Index = component กลาง ใช้ได้ทุก doc type ไม่ต้อง duplicate layout
RBAC 18 Roles แยก Approval Level
M3/E1/E2/E3, Finance Lv1/Head, Acc Lv1/Head, Procurement Manager + Reviewer ฯลฯ — ตรงกับ org chart จริง config ผ่าน Permissions Matrix
i18n Bilingual TH/EN ตั้งแต่วันแรก
ทุก string ผ่าน useLocale composable + type-safe LocaleKey รองรับขยายตลาดต่างประเทศโดยไม่ต้อง refactor
Architecture Overview
Vue 3 + Express + Prisma — Modern stack, shared types, single source of truth
สิ่งที่สร้าง — 8 Modules
Phase 1 MVP — Core Platform + 7 Functional Modules
PR / Procurement
Purchase Request + PO with approval chain (M3→E1→E2→E3→CEO ตามวงเงิน) + supplier comparison
Expense Suite
Petty Cash, Reimbursement, Advance + Clear, Credit Card OCR, Travel — 5 sub-flows + WHT 1/2/3/5%
Booking
ที่พัก (HR confirm จาก voucher pool ของบริษัท) + ตั๋วเครื่องบิน (HR แนบ e-ticket)
HR
Employee Directory, Vehicle Registry, Course Library, Form Templates, Announcements
Think Tank
กิจกรรม engagement + leaderboard + scoring โดย C-Level พร้อมรายงานผล
Legal
Knowledge Base + AI Chat (Phase 1: Legal Admin only) — รวบรวมสัญญา + ข้อกฎหมายภายใน
C-Level Dashboard
Pending approvals, Budget vs Actual, Approval Bottleneck, Department Ranking, Spending Trend
Admin
Permissions Matrix (18 roles × all routes), Workflow config, System settings — เปลี่ยนสิทธิ์โดยไม่ต้อง deploy
Hack กับปัญหา
6 challenges ที่ไม่มีอยู่ใน playbook — ต้องคิดทาง workaround เอง
RBAC 18 Roles แตกจาก 11
PRD ระบุ 15 roles แต่ codebase รวบยอดเป็น 11 → approval threshold ทำงานไม่ตรงกับ org chart จริง คนที่ควรเห็นเอกสารเห็นไม่ครบ คนที่ไม่ควรเห็นกลับเข้าได้
Document PDF ที่ Scale ได้ 8 Doc Types
8 doc types × signature block × attachment index × ต้องการ real PDF merge (text-selectable) — ถ้า duplicate layout ต่อ doc คือฝันร้าย maintainability
components/document/* (SignatureBlock, AttachmentIndex, AttachmentPage) + ใช้ pdf-lib merge real PDF bytes ติดท้าย + html2canvas สำหรับ image-only attachments — 1 abstraction รองรับทุก doci18n Project-wide ใน 1 Sprint
~50 views, 1000+ strings, ต้องบังคับ TH/EN parity แบบ strict (ขาด key ฝั่งใดฝั่งหนึ่ง = build แตก) — ทำมือคนเดียวไม่ทันแน่
useLocale (zero-dep, type-safe via LocaleKey union) + delegate batch ไป subagents 10 ตัว + auto type-check ทุก batch ก่อน commit → TH/EN parity บังคับด้วย TypeScript ไม่ใช่ disciplineLogin Dark Mode ที่ขัดกับ App
App รองรับ dark mode ทั้งระบบ แต่หน้า Login เป็น brand experience ที่ต้องคงสีขาว + brand panel สีเข้มตลอด — token cascade เผลอ flip สีตอนเปิด dark mode
.login-light-scope lock token values ใน CSS scope; brand panel hardcoded #231F20 + !important rule กัน cascade override; SplashScreen ใช้ pattern เดียวกัน reuse ได้Approval Bottleneck Real-time
C-Level อยากเห็นว่างานค้างอยู่ที่ใคร, chain ขั้นไหน, ค้างมานานเท่าไหร่ — แต่ refresh เอามันก็ไม่ทันใจ
Design Tokens 100% — กัน Drift ก่อนเกิด
Tailwind v4 + PrimeVue Aura theme + custom tokens — 3 layers ที่ override กันได้ ถ้าไม่มีระเบียบ hex hardcode จะแอบหลุดเข้ามาเรื่อยๆ
design-tokens.css เป็น single source of truth; dark mode flip ผ่าน :root.dark-mode overrides — ไม่ต้องเขียน Tailwind dark: variantStakeholders & Build Period
Internal product — co-designed กับทีมจริงทุก department
Portal Realfact ไม่ใช่ product ที่ออกแบบจาก ivory tower — ทุก flow ผ่าน workshop กับ HR, Finance, Accounting, Procurement, Legal, C-Level จริง ทุก approval threshold มาจาก policy ของ Real Factory จริง
Real Factory Group
ลูกค้า + Production team — 8 departments stakeholder
Build Period
6 weeks — Phase 1 MVP (Core + 7 modules)
Team Composition
TechLead, PM×2, BA, UX/UI — cross-functional small team
AI-Augmented Workflow
BMAD framework + Claude subagents ช่วยทำ i18n + create-story
Internal user คือ user ที่หนีไม่ได้ — บังคับใช้ก็ต้องใช้ แต่ถ้าออกแบบไม่ตรง pain point จริง พวกเขาจะหา workaround กลับไป Excel เหมือนเดิม co-design ทำให้ทุก approval rule, ทุก field, ทุก label มาจาก domain expert จริง ไม่ใช่ assumption
Key Takeaways
4 บทเรียน Design Thinking จาก Portal Realfact
ตอนแรกใช้ 11 roles ตามที่ codebase มีอยู่ พอ test กับ Finance Head จริง เขาบอก "ผมไม่ใช่ Lv1 ผมเป็น Head ทำไมต้องเห็นเอกสารเดียวกับ Lv1?" — ถึงรู้ว่า role ต้องสะท้อน hierarchy จริง ไม่ใช่ลดเพื่อความง่ายของ dev สุดท้าย split เป็น 18 roles แล้ว approval ตรงกับ policy ของบริษัทเป๊ะ
ถ้ามี constant 1 ตัวอยู่ 3 ที่ มันจะ drift แน่นอน — ไม่ใช่ "ถ้า" แต่เป็น "เมื่อไหร่" Portal Realfact บังคับให้ทุก type, ทุก validation, ทุก label ดึงมาจากแหล่งเดียว → frontend กับ backend mismatch กันไม่ได้ tokens กับ component ขัดกันไม่ได้
SignatureBlock + AttachmentIndex + AttachmentPage + DocumentLayout — แค่ 4 components รับ prop interface เดียว generate ได้ 8 doc types (PR, PO, Petty Cash, Reimbursement, Advance, Clear, Travel, Booking) ถ้า duplicate ต่อ doc คือ 8× maintenance burden — Composition ชนะ duplication เสมอ
Translate ทีละไฟล์ทีละ string คือฝันร้าย — ใช้ Claude subagents 10 ตัว parallel แต่ละตัวรับ batch ของ views + auto type-check guard ก่อน commit ถ้า key ขาด TS แตกทันที — ไม่ต้องพึ่ง human discipline สิ่งสำคัญคือออกแบบ guard ที่ machine ตรวจได้ ไม่ใช่ checklist ใน Notion