Back Office ใหม่ด้วย Vite React
apps/back-office เป็นเว็บใหม่สำหรับแทน Nuxt 2 / Vue 2 / Vuetify 2 ใน apps/back-office-old โดยเก็บโฟลเดอร์เดิมไว้ครบ ใช้ Vite, React, TypeScript และ CSS ใหม่ตามแนวภาพ Vuexy semi-dark ไม่ได้นำ source หรือ asset เชิงพาณิชย์ของ template มารวมในโปรเจค
เริ่มตรวจงาน
รันจาก root repository ใช้ Node 22.12 ขึ้นไปและ Yarn Classic:
yarn install --frozen-lockfile
yarn backoffice:dev
เปิด http://127.0.0.1:8090 แล้วเข้าสู่ระบบด้วยบัญชีใช้งาน ข้อมูลทั้งหมดมาจาก API ตาม environment ที่ตั้งค่าไว้ ไม่มีโหมดข้อมูลจำลอง และ session ของโหมดจำลองเดิมจะถูกล้างเมื่อโหลดหน้า
ค่าเริ่มต้นสำหรับบัญชีจริงเปิดให้บันทึก แก้ไข ลบ และเปลี่ยนสถานะตามฟังก์ชันเดิมและสิทธิ API ข้อกำหนดห้ามเปลี่ยนข้อมูลจริงใช้กับพฤติกรรมของผู้ทดสอบ คำสั่ง backoffice:safe ยังเป็นทางเลือกสำหรับเปิดโหมดอ่านอย่างเดียวชั่วคราว โดยบล็อก business POST/PATCH/DELETE และอนุญาต Login/GET
ค่าปกติคือ VITE_BACK_OFFICE_READ_ONLY=0 หรือไม่ตั้งค่า หากต้องการอ่านอย่างเดียว จึงตั้งค่าเป็น 1 ใน .env.local แล้ว restart yarn backoffice:dev หรือ build ใหม่ ค่า Vite ถูกฝังตอน build การเปลี่ยน env หลังสร้าง dist ไม่เปลี่ยน bundle ที่สร้างไปแล้ว
เมนูและความเข้ากันได้กับระบบเดิม
คลิกแถวหรือไอคอนรูปตาเพื่อเปิด Modal รายละเอียด ตัวกรองและการแบ่งหน้าใช้ API ส่วนการกดหัวคอลัมน์เป็นการเรียงเฉพาะหน้าที่โหลดแล้ว บนมือถือเลื่อนตารางแนวนอนได้และ Modal เป็นเต็มหน้าจอ
| Route | รายการและตัวกรอง | รายละเอียดและ action |
|---|---|---|
/, /dashboard | จำนวนเอกสารแยกสาขา/สถานะ และใบสั่งขายล่าสุด | ใช้ยอดรวม API จริง หากไม่มี total จะแสดงขีด ไม่สร้างยอดขายหรือแนวโน้มสมมติ |
/orders | ใบสั่งขาย: สาขา สถานะ ประเภทจัดส่ง ช่วงวัน ค้นหา | ลูกค้า สินค้า ยอดเงิน แก้ประเภทจัดส่ง/พิกัด; ปิดเปลี่ยนสถานะตามเดิม |
/orders-check | request เหมือนใบสั่งขาย | แสดงเฉพาะแถว refKey !== null ในหน้าปัจจุบัน ไม่ใช่ flow WAITING_PAYMENT ของ Mobile |
/invoices | ใบแจ้งหนี้: สาขา สถานะยกเลิก ประเภท Incoming ช่วงวัน ค้นหา | ขอยกเลิก ข้อผิดพลาด B1 สินค้า แก้ประเภท/พิกัด; ปิดเปลี่ยนสถานะ |
/cn | ลดหนี้: สาขา สถานะ ช่วงวัน ค้นหา | คืนสินค้า ยอดลดหนี้ ผลคลัง Q1/Q2/Q3 และภาพหลักฐาน อ่านอย่างเดียว |
/transmission-line | ใบจัดสายส่ง: สาขา สถานะ ทำขนส่งแล้ว/ยัง ช่วงวัน ค้นหา | ใบแจ้งหนี้และผล B1 รวมสินค้า เปลี่ยนสถานะใบจัดสายส่ง |
/store-line | ใบแจ้งหนี้ที่ shippingType=2: สาขา สถานะ ช่วงวัน ค้นหา | เปลี่ยน NEW / CONFIRMED / CANCELED ไม่มีแท็บประเภท/พิกัด |
/transportation | ขนส่ง: สาขา สถานะ ช่วงวัน ค้นหา | สายส่ง รถ คนขับ ใบจัดสินค้า ยอดชำระ และภาพตามลูกค้า/พิกัด; ปิดเปลี่ยนสถานะ |
/users | พนักงาน: ค้นหา | แก้สิทธิ 8 ช่อง เก็บ FCM token เดิม ส่งออก Excel ทุกหน้า |
/vehicles | รถ Master: บริษัท BFP/NFF/PCC, capacity, ค้นหา | ประเภท แชสซี เครื่องยนต์ สี ปี น้ำหนักบรรทุก; ช่องที่ปิดคง payload reset ตามเดิม |
/pick-up-point | ที่อยู่ของสาขาจาก Master | เพิ่ม แก้ ลบจุดรับส่ง ค้นหาที่อยู่ไทย เลือก Google Maps หรือกรอกพิกัด |
/signin, /signout | เข้าสู่ระบบ/ออกจากระบบ | ลบเฉพาะ session ของเว็บใหม่นี้ |
เมนูตรวจสอบใบสั่งขายที่เดิมเข้าผ่าน URL ถูกเพิ่มให้เห็นใน sidebar ไม่เพิ่มการสร้าง order การชำระเงิน หรือ business transition ใหม่ที่ระบบเก่าไม่เปิดใช้
ใช้ packages กลางอย่างไร
| Package | สิ่งที่ใช้ |
|---|---|
@bsr/api | Environment/host กลาง, Master key, web Maps key, POST guard เดิม |
@bsr/types | ชนิดข้อมูล user, employee และ permission |
@bsr/utils | ฐานข้อมูลตำบล/อำเภอ/จังหวัด/รหัสไปรษณีย์ โหลดเมื่อเปิดฟอร์ม |
rn-ui เป็นของ React Native จึงไม่ได้ใช้เป็น DOM เว็บ ส่วน theme, session ของ browser และ adapter API เดิมอยู่ในเว็บใหม่ งานนี้ไม่แก้ native UI ไม่เปลี่ยน business rule ของ Mobile ไม่แก้ env กลาง และไม่แก้ไฟล์แอปเก่า
Contract ของ API
ชั้น request ใช้ JSON, POS Bearer token หรือ Master x-api-key, timeout 40 วินาที, ยกเลิก request เมื่อเปลี่ยนหน้า และส่งข้อความ error จาก backend ถึงผู้ใช้ ตรวจว่ารายการเป็น array อ่านยอดรวมจาก content-range หากไม่มี total จะแสดงว่าไม่ทราบจำนวน หากได้ 401 จะล้าง session ของเว็บนี้แล้วกลับ Login
List ทั่วไปส่ง page, pageLimit, sort=DESC, sortBy=CreatedAt, search เอกสาร POS เพิ่มสาขา/ช่วงวัน Orders ใช้ orderStatus; Invoices ใช้ invoiceStatus, reqCancel='', toB1; Deliveries ใช้ deliveryStatus, toTransportation; Transports/CN ใช้ status รถส่ง companyCode (เริ่ม BFPDB) และ capacity โดยไม่ปนสาขา/วันของ POS จุดรับส่งอ่าน array addresses ของสาขา
| Action | Method และ endpoint | Payload |
|---|---|---|
| Login | POST login แล้ว GET branches?userCode=... | email, password; ใช้ token ที่คืนมาอ่านสาขา |
| ประเภทส่ง | PATCH orders/:id/shipping-type หรือ invoices | shippingType เป็น string |
| พิกัด | PATCH orders/:id/lat-lng หรือ invoices | Latitude/Longitude เป็น string |
| สถานะหน้าร้าน | PATCH invoices/:id/status | invoiceStatus |
| สถานะสายส่ง | PATCH deliveries/:id/status | deliveryStatus |
| สิทธิพนักงาน | PATCH users/:id | สิทธิ 8 ช่อง ตัวเลข 0/1 และ fcmToken เดิม |
| รถ | Master PATCH vehicles/:id | ช่องที่เปิดแก้และค่า reset ของช่องปิดตามเดิม |
| เพิ่ม/แก้จุดรับส่ง | Master POST branches/:id/addresses, PATCH branches/:id/addresses/:addressId | เฉพาะ field ที่อยู่เดิม |
| ลบจุดรับส่ง | Master DELETE branches/:id/addresses/:addressId | ไม่มี body |
ไม่เปิด mutation สถานะ order/invoice ทั่วไป สถานะขนส่ง หรือสถานะ CN เนื่องจาก UI เก่าปิดไว้
กติกาที่ต้องรักษา
- Order เปรียบเทียบ
Math.floor(amount)กับMath.floor(invoiceAmount)ใน CONFIRMED/WAITING_PAYMENT เพื่อแสดงว่ามี invoice ถูกยกเลิก Badge Invoice เรียงความสำคัญ: ยกเลิก → ไม่มี SAP ref/ต้อง Retry → ส่ง Incoming แล้ว → ขอยกเลิก/ยังไม่ส่ง - ยอดรวมส่วนหัวใบจัดสายส่งนับทุก invoice แต่ตารางสินค้ารวมตัด invoice CANCELED ออก รวมตาม
productCodeแล้วเรียงจำนวนมากไปน้อย - ขนส่งยังไม่ระบุ
paymentTypeแสดงamount; ระบุแล้วแสดงcash + transferการขอรูปส่ง customerCode และพิกัดทั้งสองเพื่อแยกจุดส่งของลูกค้าคนเดียวกัน - CN จำนวน/น้ำหนักคืนรวมเฉพาะ
isReturn=1แต่ยอดลดหนี้รวมpriceCNทุกแถว Q1/Q2/Q3 อ่านอย่างเดียว - สิทธิพนักงานคือ
fillWeight,editPrice,delDelivery,newCustomer,approveCustomer,resetBooking,editDeliveryDate,isAdminExport คง 15 คอลัมน์เดิม ไม่ส่ง token ลงไฟล์ และดึงทุกหน้าแทนเพดาน 999 ค่าใน Excel เป็นข้อความจริง ไม่ใช่สูตร - รถส่ง
vehicleTypeId,chassisNumber,engineNumber,vehicleColor,yearOfManufและ capacity สองช่องเป็นตัวเลข พร้อมcapacityOfFuel=0,status=1ส่วน typeOfFuel, วันซื้อ/ตรวจ/ภาษี, ประเภท/ชื่อ/วันหมดประกัน, ใบอนุญาต และ remark เป็น null ตาม legacy การบันทึกยัง reset ช่องที่ถูกปิดเหมือนเดิม หากต้องการรักษาค่าเดิมแทน ต้องแยกเปลี่ยน contract เป็นอีกงาน - ที่อยู่ส่งเฉพาะ
addressType,checkAddressName,addressName,street,block,city,country,zipCode,visOrder,U_BFP_Amphur,U_BFP_Tambon,U_ISS_RouteCode,U_ISS_RouteName,U_BFP_Latitude,U_BFP_Longitudeใช้ typebo_ShipToตรวจพิกัดเป็นตัวเลขภายในช่วงโลก
Theme, Modal และ session
Sidebar สีเข้มใช้ร่วมกับเนื้อหา Light/Dark บันทึก theme ใน key bsr.back-office.theme ครั้งแรกใช้ค่าจาก OS จอเล็กใช้ drawer จอใหญ่ย่อ sidebar ได้ Ctrl/Cmd K เปิดค้นหาเมนู Modal ใช้ native dialog ที่กัก focus และมีชื่อหัวข้อ รองรับ Modal ยืนยัน/รูปซ้อนกัน ถ้ามีข้อมูลแก้แต่ยังไม่บันทึกจะให้ยืนยันทิ้งการแก้ก่อนปิด ระหว่างบันทึกปิดไม่ได้ หลังสำเร็จอ่านตารางและรายละเอียดใหม่
Session ใช้ sessionStorage ชื่อ bsr.back-office.<environment>.session แยกจากระบบเก่า Logout ลบเฉพาะ key นี้ ไม่ clear storage ของแอปอื่น ไม่เพิ่ม client permission gate ที่ legacy ไม่มี API ยังคงเป็นผู้ตัดสินสิทธิจริง
PRD และ UAT บนโดเมนเดียว
ใช้ yarn backoffice:build:all เพื่อสร้าง apps/back-office/dist-prd สำหรับ / และ apps/back-office/dist-uat สำหรับ /uat/ แต่ละ build เลือก environment และ Master key โดยไม่แก้ config ของ Mobile/Terminal และเรียก POS/Master API จริงโดยตรงจาก browser ส่วน dev server ยังใช้ Vite proxy
วาง PRD ที่ web root และ UAT ในโฟลเดอร์ uat/ พร้อมอัปโหลด .htaccess ที่สร้างให้ในแต่ละ build กฎที่ root จะให้ UAT จัดการเส้นทางของตัวเองและ fallback เป็น /uat/index.html ไม่ต้องตั้ง API proxy บน static host แต่ API ต้องอนุญาต CORS ของโดเมนเว็บ โดยทั้งสอง path ใช้ origin เดียวกัน ผลตรวจปัจจุบัน UAT ยังต้องแก้ CORS ฝั่ง API ดูคู่มือและผลตรวจที่ apps/back-office/deploy/README.md คำสั่ง build แบบ gateway เดิมด้านล่างยังใช้ได้
Build และ deploy
yarn backoffice:test
yarn backoffice:typecheck
yarn backoffice:build
yarn workspace @bsr/back-office preview
Preview อยู่ 127.0.0.1:8091 ทั้ง dev/preview ใช้ proxy /pos-api และ /mst-api ไป host ตาม package กลาง เมื่อลง static server ต้องมี gateway ของ server เองและ SPA fallback ตัวอย่างอยู่ที่ apps/back-office/deploy/nginx.uat.example.conf สำหรับปรับร่วมกับ HTTPS ingress ของทีม ยังไม่ได้ติดตั้ง config หรือ deploy server ใดในงาน migration
นำเฉพาะ apps/back-office/dist ไปให้บริการ ตั้ง fallback เป็น index.html สำหรับ route ของเว็บ ไม่ cache API และ cache hashed assets ได้ ต้องให้ host API ตรงกับ env ที่ compile จาก package กลาง Google Maps ข้อมูลจริงต้องอนุญาต referrer ของ domain ที่ใช้งาน ค่า browser เห็นได้จึงไม่ใช่ที่เก็บ server secret
Root workspace ระบุสามแอปปัจจุบันกับ packages ชัดเจน เพื่อไม่ติดตั้ง dependency Nuxt ของโฟลเดอร์เก่าที่ ignore อยู่ ไม่ได้ลบโฟลเดอร์เก่า ExcelJS กับฐานข้อมูลที่อยู่เป็น lazy chunks ขนาดใหญ่ จึงมีคำเตือน chunk size แต่ไม่ได้โหลดในหน้า Dashboard คำเตือน Vite เรื่อง native config loader ในเวอร์ชันอนาคตไม่ขัดขวาง build ปัจจุบันที่ bundle TypeScript กลาง
การทดสอบและสิ่งที่ต้องตรวจเมื่อเปิดใช้งานจริง
ชุดทดสอบอัตโนมัติครอบคลุมการบล็อกคำสั่งในโหมดอ่านอย่างเดียว, header login/Master, error จาก API, การล้าง session จำลองเดิม, payload, query filters, Export เกิน 999, กติกายอดเงินและรวมสินค้า โดยใช้ mock network ประวัติการตรวจ migration และผลตรวจการลบโหมดจำลองอยู่ที่ apps/back-office/QA.md
งานนี้ไม่ได้บันทึก/เปลี่ยนสถานะข้อมูล UAT จริง ไม่ได้ใช้บัญชีจริง และไม่ได้ตรวจ authorization ของ Google Maps จริง ก่อนเปิดใช้หน้างานให้ตรวจ Login แบบอ่านอย่างเดียวด้วยบัญชีที่จะใช้งาน ตรวจ gateway/สิทธิข้อมูลและ domain ของ Maps แล้วจึงทำ business acceptance ของการบันทึกตามขั้นตอนทีม แยกจากการตรวจ implementation และ simulation ที่ทำในงานนี้