4.2 · vtrc-rc-backoffice Deep Dive
เล่มนี้เจาะลึก vtrc-rc-backoffice/ — frontend ฝั่ง HR/Admin ของสภากาชาดไทย เป็น React 16.13.1 + CRA react-scripts@3.4.3 (ไม่ eject) + Apollo Client v2 (react-apollo@3.1.5) + AntD v4.15.4 + MUI v5.11 (ประกาศใน package.json แต่ import จริงเพียง ~15 ไฟล์) + Recoil 0.7.5
เป้าหมายคือให้ผู้อ่านอ่านจบแล้วเข้าใจโครงสร้าง รู้จุดที่ต้องระวัง และสามารถแก้ไขหรือเพิ่มฟีเจอร์ได้โดยไม่ทำลาย pattern ที่มีอยู่ ทุก claim มี citation file:line ให้ผู้อ่านเปิดตรวจได้
vtrc-rc-backoffice คืออะไร
vtrc-rc-backoffice คือเว็บแอปที่ฝ่าย HR/Admin สภากาชาดใช้งานจริง — จัดการข้อมูลพนักงาน อนุมัติใบลา อนุมัติค่ารักษาพยาบาล บริหารกองทุนสงเคราะห์ บริหารการสรรหาและระบบ PMS จัดการผู้ใช้และสิทธิ์ และอื่น ๆ ครอบคลุมงาน backoffice ขนาดใหญ่ที่มี module ย่อยกว่า 15 หมวด (Leave, Hospital, Welfare, WorkForce, Recruitment, PMS, Meeting, Attendance, Fund, Payment, Document, Policy, …)
ตัวแอปรันที่ https://vtrcbackoffice.redcross.or.th/ (production) และ https://uat-vtrcbackoffice.redcross.or.th/ (UAT) — โดยทั้งสอง environment ใช้ bundle ตัวเดียวกันที่เลือก GraphQL endpoint ตอน runtime ตาม origin ที่เปิดผ่านฟังก์ชัน checkLinkURL() (vtrc-rc-backoffice/src/config/apollo.js:14-36) ไม่ได้ขับ GraphQL endpoint ด้วยตัวแปร REACT_APP_* ใด ๆ
คู่สมรสของ vtrc-rc-backoffice คือ vtrc-web (ฝั่งพนักงานทั่วไป) ซึ่งอยู่ใน Volume 4.1 — ทั้งสองแอปใช้ Apollo Client v2 เหมือนกัน แต่ต่างกันที่เวอร์ชัน AntD (v4 vs v3), ระบบ build (CRA eject ไม่ได้ vs Webpack eject แล้ว), state management (มี Recoil 57 โฟลเดอร์ vs ไม่มี library เฉพาะ), และขนาด (vtrc-rc-backoffice ใหญ่กว่ามาก — 871 page files vs ~280)
สไตล์โค้ด — 4 ข้อที่ต้องจำ
- pattern ผสม class + function component — module เก่า (Leave, Hospital, ManageUser, Document, Policy) ใช้
class extends React.Component+React.createRef+<Form ref={formRef}>; module ใหม่ (WorkForce, PMS, Recruitment) ใช้ function component +Form.useForm+ Recoil atom ผ่านuseRecoilState fetchPolicy: 'no-cache'เป็น default เหมือนvtrc-web— ตั้งที่apolloClient.defaultOptionsในvtrc-rc-backoffice/src/config/apollo.js:176-185ทำให้แทบทุก query ยิง server เสมอ cache ของ Apollo แทบไม่ถูกใช้- เมนู dynamic — ดึงจาก
fetchgroupmenu+fetchsidemenu(REST ผ่านaxiosWorkforce) ในSiderLayout.componentDidMount(vtrc-rc-backoffice/src/components/SiderLayout/index.js) ไม่ใช่ static array ในroutes.js - มี HTTP client 17 ตัว (15 axios + 2 Apollo) แยกตาม microservice — ระวังเลือกผิดตัว เช่น recruitment ต้องใช้
axios-recruitment.js, PMS ต้องใช้axios-pms.js
ตัวเลขที่ควรจำ
| ตัวเลข | ค่า | ที่มา |
|---|---|---|
Page files (รวม _bk) | 871 ไฟล์ | src/pages/ |
Page files (ไม่นับ _bk) | ~773 ไฟล์ | src/pages/ (grep ใหม่) |
| Component files | 104 ไฟล์ | src/components/ |
| Recoil atom folders | 57 โฟลเดอร์ | src/recoil/states/ |
| Util files | 33 ไฟล์ | src/utils/ |
| GraphQL queries (main) | ~121 ตัว | src/graphql/queries.js (4,108 บรรทัด) |
| GraphQL mutations (main) | 94 ตัว | src/graphql/mutations.js (2,208 บรรทัด) |
| Routes | ~276 paths | src/config/routes.js (2,550 บรรทัด) |
| SiderLayout (shell) | 1,168 บรรทัด | src/components/SiderLayout/index.js |
custom-antd.css (compiled) | 21,189 บรรทัด | src/config/custom-antd.css |
main.css | 4,644 บรรทัด | src/assets/css/main.css |
| Legacy/backup files | 76 ไฟล์ + 4 โฟลเดอร์ _bk | ทั่วระบบ |
| HTTP clients | 17 ตัว (15 axios + 2 Apollo) | src/config/axios-*.js + apollo*.js |
| React | 16.13.1 | vtrc-rc-backoffice/package.json:51 |
| Apollo Client | v2 (react-apollo@3.1.5) | vtrc-rc-backoffice/package.json:52 |
| AntD | 4.15.4 | vtrc-rc-backoffice/package.json:21 |
| MUI | 5.11.1 (import ~15 ไฟล์จริง) | vtrc-rc-backoffice/package.json:17 |
| Recoil | 0.7.5 | vtrc-rc-backoffice/package.json:65 |
| React Router | v5.2.0 | vtrc-rc-backoffice/package.json:60 |
| CRA | react-scripts@3.4.3 (ไม่ eject) | vtrc-rc-backoffice/package.json:61 |
| Node (ใน Docker build stage) | 16 | vtrc-rc-backoffice/Dockerfile:1 |
| nginx (ใน Docker serve stage) | 1.24.0 | vtrc-rc-backoffice/Dockerfile:13 |
| Bundle ที่ commit ใน repo | ~130 MB | vtrc-rc-backoffice/build/ |
หมายเหตุ — ตัวเลขหนึ่งบางค่า verify ใหม่เมื่อแก้ (เช่น count atom() หรือ `gql``) ดูหัด "V1 corrections" ด้านล่าง
สิ่งที่ Volume นี้ไม่ cover
- รายละเอียด GraphQL schema ฝั่ง vtrc-api — cover ใน Volume 3 บท schema
- HRMI MSSQL schema และ data dictionary — cover ใน Volume data-model
- business rule ของแต่ละฟีเจอร์ (เช่น เงื่อนไขการอนุมัติใบลา การคำนวณเงินกองทุน) — cover ใน Volume business
vtrc-web— Volume 4.1 แยกต่างหาก- ระบบ SSO ฝั่ง backend —
PathLoginที่ route/pathLogin/{token}/{refreshToken}เป็นเพียงจุดรับ token จากระบบอื่น ตัว issuer ของ token อยู่นอก repo
โครงสร้างบท (mirror V1 + แยก security/performance)
| บท | ชื่อ | คำถามที่ตอบ |
|---|---|---|
| 4.2.1 | Repository map + Bootstrap/CRA build + Apollo Client + Routing/dynamic menu | แอปเริ่มที่ไฟล์ใด ส่ง request อย่างไร เลือก endpoint อย่างไร route ทำงานอย่างไร |
| 4.2.2 | GraphQL catalog + Component patterns + Recoil + MUI architecture | มี query/mutation อะไรบ้าง component แบ่งอย่างไร state อยู่ที่ไหน |
| 4.2.3 | State + Styling + Locale (พ.ศ.) + Security + Scorecard | state อยู่ที่ไหน CSS กี่ระบบ จัดการพ.ศ. อย่างไร จุดเสี่ยงด้านความปลอดภัย |
โครงสร้างนี้ตั้งใจกระชับกว่า V1 (10 บท → 3 บท) เพื่อให้อ่านจบในเวลาจำกัด ผู้อ่านที่ต้องการ detail เช่น page deep dive แบบ step-by-step ให้เปิดไฟล์จริงใน vtrc-rc-backoffice/src/pages/ ควบคู่กับ citation ในแต่ละบท
วิธีอ่านเล่มนี้
ถ้ามีเวลาน้อย — อ่าน 4.2.1 (map + Apollo + routes) + ส่วน Scorecard ใน 4.2.3 ก็พอเข้าใจภาพรวม
ถ้าจะแก้ฟีเจอร์ — อ่าน 4.2.2 (catalog) ก่อนเพื่อหา query/mutation ที่เกี่ยวข้อง แล้วเปิดไฟล์ page จริงใน src/pages/ ทำตาม pattern ของ page ใกล้เคียง (class หรือ function)
ถ้าจะทำ security review — อ่าน 4.2.3 ก่อน จากนั้น cross-check กับ volume security ของแพลตฟอร์ม
ขอแนะนำให้เปิดไฟล์จริงใน repo ควบคู่กับการอ่าน — คู่มือนี้เป็นแผนที่ ไม่ใช่ตัวแทนของโค้ด
การเปลี่ยนแปลงจาก V1
เนื้อหาพอร์ตจาก docs-v1-archive/volume-07-vtrc-rc-backoffice/ (10 บท + index รวม ~3,899 บรรทัด) — บีบเป็น 3 บทที่ลึกขึ้น โดยรวม security, performance, scorecard เข้ากับ state/styling/locale ในบทสุดท้าย
ทุก citation ที่มี file:line ใน V1 ผ่านการ verify ใหม่กับโค้ดปัจจุบัน (อ้างอิง snapshot 2026-07-13) รวมถึง package.json versions, Dockerfile, src/config/apollo.js, src/utils/auth.js, src/containers/App/index.js, src/pages/Login/index.js, src/pages/PathLogin/index.js
V1 corrections — สิ่งที่ V1 เขียนคลาดเคลื่อน และ V2 แก้ไขแล้ว
| # | V1 เขียน | ความจริงตามโค้ด | ที่มา |
|---|---|---|---|
| 1 | "Page files 871" ในตาราง | 773 active + ~98 _bk ≈ 871 รวม — V1 ระบุรวม backup โดยไม่แยก | grep *.js ใน vtrc-rc-backoffice/src/pages/ |
| 2 | "120 queries" ใน queries.js | ~121 queries (นับ `gql`` template) | vtrc-rc-backoffice/src/graphql/queries.js |
| 3 | V1 บท 7.7 Login "ดึง RSA public key จาก backend แล้ว encrypt password ด้วย JSEncrypt" | ไม่มี RSA — Login จริงเรียก Mutations.loginBackoffice ตรง ๆ ด้วย password plain text (ส่งผ่าน HTTPS) ไม่มี axios.get('/api/rsa-key') ไม่มี JSEncrypt import ใด ๆ ในไฟล์ Login | vtrc-rc-backoffice/src/pages/Login/index.js:1-66 + grep ทั้ง repo ไม่พบ rsa/JSEncrypt |
| 4 | V1 "token เก็บทั้งใน localStorage และ sessionStorage" | จริงแล้ว token อยู่ อย่างใดอย่างหนึ่ง ขึ้นกับ flag isRemember — setToken() เลือก storage จาก localStorage.getItem('isRemember') | vtrc-rc-backoffice/src/utils/auth.js:1-17 |
| 5 | V1 "PathLogin redirect ไป /home" | จริง redirect ไป / ผ่าน window.location.href = '/' ไม่ใช่ history.push('/home') | vtrc-rc-backoffice/src/pages/PathLogin/index.js:20 |
| 6 | V1 ไม่ได้ทำา charged ว่า class name ใน PathLogin/index.js คือ TheamCss (copy-paste จากหน้า TheamCss) | class name ในไฟล์คือ TheamCss ทั้งที่ไฟล์อยู่ใน pages/PathLogin/ — typo ที่ไม่กระทบ routing เพราะ default export เท่านั้นที่ถูก import | vtrc-rc-backoffice/src/pages/PathLogin/index.js:11 |
| 7 | V1 บท 7.3 "ไม่มี fallback return ใน checkLinkURL()" | ถูกต้อง — origin ที่ไม่ match ได้ undefined ทำให้ createUploadLink({ uri: undefined }) พังได้ ข้อควรระวังจริง | vtrc-rc-backoffice/src/config/apollo.js:14-36 |
ผู้อ่านที่อ้างอิง V1 ควรเช็คกับตารางนี้ก่อน โดยเฉพาะเรื่อง Login (V1 เขียนผิดเรื่อง RSA) และ token storage (V1 เขียนสับสนเรื่อง localStorage/sessionStorage)
Typos ที่ติดอยู่ในชื่อไฟล์/โฟลเดอร์จริง — ห้ามแก้
เพราะเป็นชื่อที่ใช้ใน import path ทั้งระบบ + route entry + recoil atom name — การแก้ชื่อทำให้ไฟล์พังทั้งระบบ ตัวอย่างที่พบบ่อย:
| Typo | ควรเป็น | ที่พบ |
|---|---|---|
PerfessionalLicense | ProfessionalLicense | vtrc-rc-backoffice/src/pages/WorkForce/PerfessionalLicense/, recoil/states/PerfessionalLicense/, config/routes.js |
PerfessionalDepartment | ProfessionalDepartment | vtrc-rc-backoffice/src/pages/WorkForce/PerfessionalDepartment/ |
TheamCss | ThemeCss | vtrc-rc-backoffice/src/pages/TheamCss/, route /TheamCss ใน containers/App/index.js:8 |
AboutUS | AboutUs | vtrc-rc-backoffice/src/pages/AboutUS/, components/AboutUS/ |
announment | announcement | vtrc-rc-backoffice/src/pages/Recruitment/Announcement/announment/ |
HospitalHr | HospitalHR | vtrc-rc-backoffice/src/pages/HospitalHr/ (Hr ตัวเล็ก) |
couse_pdf | course_pdf | vtrc-rc-backoffice/src/assets/couse_pdf/ (PDF template ของ course) |
ModalshowAllImage | ModalShowAllImage | vtrc-rc-backoffice/src/components/ModalshowAllImage/ |
ImageFileNewtab | ImageFileNewTab | vtrc-rc-backoffice/src/components/ImageFileNewtab/ |
FormContactUS | FormContactUs | vtrc-rc-backoffice/src/components/FormContactUS/ |
กฎเหล็กเมื่อเจอ typo
- อย่าแก้ชื่อโฟลเดอร์/ไฟล์/query — เพราะจะทำให้ไฟล์อื่นที่ import พัง
- ถ้าสร้างไฟล์ใหม่ในโฟลเดอร์ที่มี typo ให้ใช้ชื่อโฟลเดอร์เดิม
- ถ้าทนไม่ได้ ให้ plan refactor เป็น batch (เปลี่ยนทุก import + route entry + recoil atom พร้อมกัน) และทดสอบ build ก่อน merge
ไฟล์ legacy/backup — ห้ามแตะ ห้ามใช้เป็นต้นแบบ
vtrc-rc-backoffice มีไฟล์ legacy/backup มากกว่า 76 ไฟล์ รวมถึง โฟลเดอร์ page _bk ทั้งโฟลเดอร์ 4 ชุด
กฎเหล็ก
- อย่าแก้ไข — ไฟล์ที่ลงท้ายด้วย
_bk,_bak,_prd,_copy,_tempFilter,_bk-*,_bk-*หรือไฟล์ที่อยู่ในโฟลเดอร์_bkทั้งโฟลเดอร์ - อย่าใช้เป็นต้นแบบ — ถ้า copy โค้ดจากไฟล์เหล่านี้ มีโอกาสได้ query ที่ deprecated แล้ว
- อย่าลบทิ้งเอง — บางไฟล์เก็บไว้สำหรับ rollback ดู git history ก่อน และถามทีมก่อนลบ
โฟลเดอร์ page _bk ทั้งโฟลเดอร์
| โฟลเดอร์ | จำนวนไฟล์ | ที่มา |
|---|---|---|
vtrc-rc-backoffice/src/pages/Leave_bk/ | 12 ไฟล์ | snapshot ระบบลาเก่า |
vtrc-rc-backoffice/src/pages/LeaveHR_bk/ | 13 ไฟล์ | snapshot ฝั่ง HR |
vtrc-rc-backoffice/src/pages/LeaveStudies_bk/ | 15 ไฟล์ (รวม Form/Tab0..10/) | snapshot การลาศึกษาเก่า |
vtrc-rc-backoffice/src/pages/OtherWelfare/Form_bk/ | 3 ไฟล์ | snapshot ฟอร์มสวัสดิการอื่น |
ตัวอย่างที่ต้องระวังมาก — src/config/routes*
vtrc-rc-backoffice/src/config/
├── routes.js ← ตัวจริง (2,550 บรรทัด)
├── routes_bak.js ← snapshot
├── routes_bk-prd.js ← snapshot prod
├── routes_bk_uat_100423.js ← snapshot UAT ลงวันที่ 10/04/2023
├── routes_bk_up_prd_100423.js ← snapshot update-prod ลงวันที่ 10/04/2023
├── routes_payments_axios.js ← ทดลอง payment ด้วย axios
└── routes_gql_payments.js ← ทดลอง payment ด้วย GraphQLวิธีตรวจสอบว่าไฟล์ที่จะแก้เป็นของจริง — ชื่อไฟล์ต้องไม่มี _bk, _bak, _prd, _copy, _tempFilter, _bk-*, index-axios, index-axio และไม่ได้อยู่ในโฟลเดอร์ที่ลงท้ายด้วย _bk
เคล็ดลับตอนทำงานจริง
ตอนค้นหา "ฟีเจอร์นี้อยู่ที่ไฟล์ไหน"
- เริ่มที่
vtrc-rc-backoffice/src/config/routes.js— grep หาชื่อเมนูหรือ path - ดู
componentfield ใน route entry — เป็นชื่อ component ที่ import จากpages/ - ถ้าไม่เจอใน
routes.jsอาจเป็นเพราะเมนู dynamic — ค้นหาใน backendfetchsidemenuresponse ที่ตอบกลับจากaxiosWorkforce - จากนั้นเปิดไฟล์ใน
vtrc-rc-backoffice/src/pages/{Feature}/index.js
ตอน grep ไม่เจอ symbol ที่คาดไว้
- ถ้า grep
<Query,<Mutation,useQuery,useMutationใน module เก่า — ไม่เจอเพราะใช้withApollo+this.props.client.query/mutate - ถ้า grep ใน module ใหม่ (WorkForce, PMS) — อาจเจอ
useRecoilStateแทนthis.state
ตอนเจอ route entry ที่ import โฟลเดอร์ _bk
บาง route entry อาจ import จากโฟลเดอร์ _bk โดยไม่ได้ตั้งใจ (เช่น LeaveStudies_bk/Form/Tab0/) — ให้ตรวจก่อนว่า route นั้นเปิดใช้งานจริงหรือเป็น fallback ที่ไม่มีใครเข้า