ข้ามไปยังเนื้อหาหลัก

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-checkrequest เหมือนใบสั่งขายแสดงเฉพาะแถว 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/apiEnvironment/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 ของสาขา

ActionMethod และ endpointPayload
LoginPOST login แล้ว GET branches?userCode=...email, password; ใช้ token ที่คืนมาอ่านสาขา
ประเภทส่งPATCH orders/:id/shipping-type หรือ invoicesshippingType เป็น string
พิกัดPATCH orders/:id/lat-lng หรือ invoicesLatitude/Longitude เป็น string
สถานะหน้าร้านPATCH invoices/:id/statusinvoiceStatus
สถานะสายส่งPATCH deliveries/:id/statusdeliveryStatus
สิทธิพนักงาน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, isAdmin Export คง 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 ใช้ type bo_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 ที่ทำในงานนี้