EOP Dashboard
diff --git a/src/app/(protected)/orders/page.tsx b/src/app/(protected)/orders/page.tsx
index c70126a..641a666 100644
--- a/src/app/(protected)/orders/page.tsx
+++ b/src/app/(protected)/orders/page.tsx
@@ -1,22 +1,8 @@
// File: src/app/(protected)/orders/page.tsx
"use client";
-/**
- * EOP Orders — มุมมองเต็ม (Full) + ตัวกรองแบบ Select + Modal ปรับแต่งคอลัมน์
- * - เฮดเดอร์ตารางแบบ 2 ชั้น (กลุ่มหัวข้อ ➜ หัวคอลัมน์ย่อย) ภาษาไทยทั้งหมด
- * - เลือกคอลัมน์ที่ต้องการแสดงได้จาก "ปรับแต่งตาราง" (จำค่าบน localStorage)
- * - แก้ Modal ให้เนื้อหาเลื่อน (scroll) ได้ และปุ่ม Apply ชิดด้านล่างเสมอ
- * - เพิ่มฟิลด์ "ผู้เปิดออเดอร์" (createdBy*)
- * - ตารางไม่ fix size โดยให้คอนเทนต์กำหนดความกว้างเอง และให้ทุกเซลล์แสดงบรรทัดเดียว (nowrap)
- *
- * หมายเหตุ: ใช้ mock แค่ไม่กี่เรคคอร์ดเพื่อโชว์ UX — ต่อ API จริงได้โดยแทนที่ MOCK_ORDERS
- */
-
import { useEffect, useMemo, useState, type ReactNode } from "react";
-/* ===========================
- Types
-=========================== */
type Channel = "shopee" | "lazada" | "tiktok" | "d2c" | "chat";
type PaymentStatus = "paid" | "pending" | "failed" | "cod" | "refunded";
type FulfillmentStatus =
@@ -28,7 +14,6 @@ type FulfillmentStatus =
| "cancelled";
type Order = {
- // เอกลักษณ์ & ช่องทาง
id: string;
orderNo: string;
orderRef: string;
@@ -36,27 +21,19 @@ type Order = {
channelOrderId: string;
marketplaceShopId: string;
marketplaceShopName: string;
-
- // ผู้เปิดออเดอร์
createdById: string;
createdByName: string;
createdByEmail?: string | null;
-
- // วัน–เวลา (ISO)
dateCreated: string;
datePaid?: string | null;
datePacked?: string | null;
dateShipped?: string | null;
dateDelivered?: string | null;
dateCancelled?: string | null;
-
- // ลูกค้า
customerId: string;
customerName: string;
customerEmail?: string | null;
customerPhone?: string | null;
-
- // ที่อยู่จัดส่ง
shippingName: string;
shippingPhone?: string | null;
shippingAddressLine1: string;
@@ -66,8 +43,6 @@ type Order = {
shippingProvince: string;
shippingPostcode: string;
shippingCountry: string;
-
- // ออกใบเสร็จ/ภาษี
billingName: string;
billingTaxId?: string | null;
billingAddressLine1: string;
@@ -76,17 +51,13 @@ type Order = {
billingProvince: string;
billingPostcode: string;
billingCountry: string;
-
- // การชำระเงิน
paymentMethod: "card" | "bank_qr" | "cod" | "wallet" | "transfer";
paymentStatus: PaymentStatus;
paymentProvider?: string | null;
paymentTxId?: string | null;
paymentFeeTHB?: number | null;
-
- // ราคา/ต้นทุนย่อย
currency: "THB";
- exchangeRate: number; // 1 สำหรับ THB
+ exchangeRate: number;
subtotalTHB: number;
discountTHB: number;
shippingFeeTHB: number;
@@ -94,30 +65,22 @@ type Order = {
packagingFeeTHB: number;
taxTHB: number;
totalTHB: number;
-
- // สินค้า/โลจิสติกส์
itemsCount: number;
totalWeightGrams: number;
packageCount: number;
dimensionsCmL?: number | null;
dimensionsCmW?: number | null;
dimensionsCmH?: number | null;
-
- // สถานะจัดส่ง
fulfillStatus: FulfillmentStatus;
shipBy?: string | null;
carrier?: string | null;
serviceLevel?: string | null;
trackingNo?: string | null;
-
- // โกดัง/ปฏิบัติการ
warehouseCode: "BKK-DC" | "CNX-HUB" | "HKT-MINI";
pickListId?: string | null;
waveId?: string | null;
binFrom?: string | null;
binTo?: string | null;
-
- // ธง/โน้ต/ความเสี่ยง
tags?: string[];
rmaFlag?: boolean;
rmaReason?: string | null;
@@ -135,15 +98,12 @@ type Order = {
type ColumnKey = keyof Order;
type Column = {
key: ColumnKey;
- labelTH: string; // ป้ายหัวคอลัมน์ (ไทย)
- groupTH: string; // กลุ่มหัวข้อชั้นบน (ไทย)
+ labelTH: string;
+ groupTH: string;
align?: "left" | "right";
format?: (v: Order[ColumnKey], row: Order) => ReactNode;
};
-/* ===========================
- Mock (ไม่กี่เรคคอร์ด)
-=========================== */
const MOCK_ORDERS: Order[] = [
{
id: "1",
@@ -153,23 +113,19 @@ const MOCK_ORDERS: Order[] = [
channelOrderId: "SP-9988776655",
marketplaceShopId: "sp_shop_10293",
marketplaceShopName: "Amrez Beauty Official",
-
createdById: "U-001",
createdByName: "อ้อม ส.",
createdByEmail: "aom@amrez.co.th",
-
dateCreated: "2025-10-08T08:15:00+07:00",
datePaid: "2025-10-08T08:16:10+07:00",
datePacked: null,
dateShipped: null,
dateDelivered: null,
dateCancelled: null,
-
customerId: "CUST-000142",
customerName: "Ploy Ch.",
customerEmail: "ploy@example.com",
customerPhone: "0812345678",
-
shippingName: "Ploy Ch.",
shippingPhone: "0812345678",
shippingAddressLine1: "91/518 หมู่ 1",
@@ -179,7 +135,6 @@ const MOCK_ORDERS: Order[] = [
shippingProvince: "นนทบุรี",
shippingPostcode: "11000",
shippingCountry: "TH",
-
billingName: "Ploy Ch.",
billingTaxId: null,
billingAddressLine1: "91/518 หมู่ 1",
@@ -188,13 +143,11 @@ const MOCK_ORDERS: Order[] = [
billingProvince: "นนทบุรี",
billingPostcode: "11000",
billingCountry: "TH",
-
paymentMethod: "bank_qr",
paymentStatus: "paid",
paymentProvider: "2C2P",
paymentTxId: "2C2P_2a8e7b0b",
paymentFeeTHB: 9.5,
-
currency: "THB",
exchangeRate: 1,
subtotalTHB: 1550,
@@ -204,26 +157,22 @@ const MOCK_ORDERS: Order[] = [
packagingFeeTHB: 0,
taxTHB: 0,
totalTHB: 1490,
-
itemsCount: 2,
totalWeightGrams: 420,
packageCount: 1,
dimensionsCmL: 22,
dimensionsCmW: 16,
dimensionsCmH: 10,
-
fulfillStatus: "processing",
shipBy: "2025-10-09T18:00:00+07:00",
carrier: null,
serviceLevel: "Standard",
trackingNo: null,
-
warehouseCode: "BKK-DC",
pickListId: null,
waveId: null,
binFrom: "A-01-03",
binTo: null,
-
tags: ["priority", "giftwrap"],
rmaFlag: false,
rmaReason: null,
@@ -245,23 +194,19 @@ const MOCK_ORDERS: Order[] = [
channelOrderId: "LZ-5566778899",
marketplaceShopId: "lz_shop_8811",
marketplaceShopName: "Kathy Labz Store",
-
createdById: "U-002",
createdByName: "บี ที.",
createdByEmail: "bee@amrez.co.th",
-
dateCreated: "2025-10-08T08:22:00+07:00",
datePaid: null,
datePacked: null,
dateShipped: null,
dateDelivered: null,
dateCancelled: null,
-
customerId: "CUST-000320",
customerName: "Thanawat K.",
customerEmail: "thanawat@example.com",
customerPhone: "0820001111",
-
shippingName: "Thanawat K.",
shippingPhone: "0820001111",
shippingAddressLine1: "128/7 ซอยสวนหลวง",
@@ -271,7 +216,6 @@ const MOCK_ORDERS: Order[] = [
shippingProvince: "กรุงเทพมหานคร",
shippingPostcode: "10200",
shippingCountry: "TH",
-
billingName: "Thanawat K.",
billingTaxId: "0105561234567",
billingAddressLine1: "128/7 ซอยสวนหลวง",
@@ -280,13 +224,11 @@ const MOCK_ORDERS: Order[] = [
billingProvince: "กรุงเทพมหานคร",
billingPostcode: "10200",
billingCountry: "TH",
-
paymentMethod: "cod",
paymentStatus: "cod",
paymentProvider: null,
paymentTxId: null,
paymentFeeTHB: 0,
-
currency: "THB",
exchangeRate: 1,
subtotalTHB: 590,
@@ -296,26 +238,22 @@ const MOCK_ORDERS: Order[] = [
packagingFeeTHB: 0,
taxTHB: 0,
totalTHB: 640,
-
itemsCount: 1,
totalWeightGrams: 220,
packageCount: 1,
dimensionsCmL: 18,
dimensionsCmW: 12,
dimensionsCmH: 8,
-
fulfillStatus: "unfulfilled",
- shipBy: "2025-10-09T18:00:00+07:00",
+ shipBy: "2025-10-09T18:00+07:00",
carrier: "Flash",
serviceLevel: "COD-Standard",
trackingNo: null,
-
warehouseCode: "BKK-DC",
pickListId: null,
waveId: "WAVE-101",
binFrom: "C-02-11",
binTo: null,
-
tags: ["fragile"],
rmaFlag: false,
rmaReason: null,
@@ -337,23 +275,19 @@ const MOCK_ORDERS: Order[] = [
channelOrderId: "TT-4433221100",
marketplaceShopId: "tt_shop_5501",
marketplaceShopName: "Amrez TikTok",
-
createdById: "U-003",
createdByName: "กร พ.",
createdByEmail: "korn@amrez.co.th",
-
dateCreated: "2025-10-08T08:40:00+07:00",
datePaid: "2025-10-08T08:41:05+07:00",
datePacked: "2025-10-08T09:05:30+07:00",
dateShipped: "2025-10-08T12:10:00+07:00",
dateDelivered: null,
dateCancelled: null,
-
customerId: "CUST-000511",
customerName: "Mint W.",
customerEmail: "mint@example.com",
customerPhone: "0869997777",
-
shippingName: "Mint W.",
shippingPhone: "0869997777",
shippingAddressLine1: "55/9 ถนนสายน้ำ",
@@ -363,7 +297,6 @@ const MOCK_ORDERS: Order[] = [
shippingProvince: "เชียงใหม่",
shippingPostcode: "50300",
shippingCountry: "TH",
-
billingName: "Mint W.",
billingTaxId: null,
billingAddressLine1: "55/9 ถนนสายน้ำ",
@@ -372,13 +305,11 @@ const MOCK_ORDERS: Order[] = [
billingProvince: "เชียงใหม่",
billingPostcode: "50300",
billingCountry: "TH",
-
paymentMethod: "wallet",
paymentStatus: "paid",
paymentProvider: "TikTokPay",
paymentTxId: "TTPAY_c0ffeecafe",
paymentFeeTHB: 14.9,
-
currency: "THB",
exchangeRate: 1,
subtotalTHB: 2250,
@@ -388,26 +319,22 @@ const MOCK_ORDERS: Order[] = [
packagingFeeTHB: 10,
taxTHB: 0,
totalTHB: 2200,
-
itemsCount: 4,
totalWeightGrams: 650,
packageCount: 1,
dimensionsCmL: 24,
dimensionsCmW: 18,
dimensionsCmH: 10,
-
fulfillStatus: "shipped",
shipBy: "2025-10-10T12:00:00+07:00",
carrier: "J&T",
serviceLevel: "Express",
trackingNo: "JTTH123456789",
-
warehouseCode: "CNX-HUB",
pickListId: "PICK-5509",
waveId: "WAVE-102",
binFrom: "Z-03-02",
binTo: null,
-
tags: [],
rmaFlag: false,
rmaReason: null,
@@ -423,9 +350,6 @@ const MOCK_ORDERS: Order[] = [
},
];
-/* ===========================
- Helpers
-=========================== */
const LS_KEY = "orders.visibleCols.v2";
const THB = (n: number) => `฿${n.toLocaleString()}`;
@@ -452,6 +376,7 @@ function mapPaymentColor(p: PaymentStatus) {
return pill("neutral");
}
}
+
function mapFulfillmentColor(f: FulfillmentStatus) {
switch (f) {
case "delivered":
@@ -466,7 +391,6 @@ function mapFulfillmentColor(f: FulfillmentStatus) {
}
}
-// typed getter (เลี่ยง as any)
function getProp
(obj: T, key: K): T[K] {
return obj[key];
}
@@ -506,218 +430,88 @@ function defaultCell(v: unknown): ReactNode {
return String(v);
}
-/* ===========================
- คอนฟิกคอลัมน์ (ไทย + กลุ่ม)
-=========================== */
const ALL_COLS: Column[] = [
- // เอกลักษณ์ & ช่องทาง
{ key: "id", labelTH: "ไอดี", groupTH: "เอกลักษณ์ & ช่องทาง" },
{ key: "orderNo", labelTH: "เลขออเดอร์", groupTH: "เอกลักษณ์ & ช่องทาง" },
{ key: "orderRef", labelTH: "อ้างอิงภายใน", groupTH: "เอกลักษณ์ & ช่องทาง" },
{ key: "channel", labelTH: "ช่องทาง", groupTH: "เอกลักษณ์ & ช่องทาง" },
- {
- key: "channelOrderId",
- labelTH: "เลขออเดอร์ช่องทาง",
- groupTH: "เอกลักษณ์ & ช่องทาง",
- },
- {
- key: "marketplaceShopId",
- labelTH: "รหัสร้าน",
- groupTH: "เอกลักษณ์ & ช่องทาง",
- },
- {
- key: "marketplaceShopName",
- labelTH: "ชื่อร้าน",
- groupTH: "เอกลักษณ์ & ช่องทาง",
- },
-
- // ผู้เปิดออเดอร์
+ { key: "channelOrderId", labelTH: "เลขออเดอร์ช่องทาง", groupTH: "เอกลักษณ์ & ช่องทาง" },
+ { key: "marketplaceShopId", labelTH: "รหัสร้าน", groupTH: "เอกลักษณ์ & ช่องทาง" },
+ { key: "marketplaceShopName", labelTH: "ชื่อร้าน", groupTH: "เอกลักษณ์ & ช่องทาง" },
{ key: "createdByName", labelTH: "ผู้เปิดออเดอร์", groupTH: "ผู้เปิดออเดอร์" },
{ key: "createdByEmail", labelTH: "อีเมลผู้เปิด", groupTH: "ผู้เปิดออเดอร์" },
{ key: "createdById", labelTH: "รหัสผู้เปิด", groupTH: "ผู้เปิดออเดอร์" },
-
- // วัน–เวลา
{ key: "dateCreated", labelTH: "สร้างเมื่อ", groupTH: "วัน–เวลา", format: (v) => fmtDate(v as string | null | undefined) },
{ key: "datePaid", labelTH: "ชำระเงิน", groupTH: "วัน–เวลา", format: (v) => fmtDate(v as string | null | undefined) },
{ key: "datePacked", labelTH: "แพ็คของ", groupTH: "วัน–เวลา", format: (v) => fmtDate(v as string | null | undefined) },
{ key: "dateShipped", labelTH: "ส่งของ", groupTH: "วัน–เวลา", format: (v) => fmtDate(v as string | null | undefined) },
{ key: "dateDelivered", labelTH: "ถึงปลายทาง", groupTH: "วัน–เวลา", format: (v) => fmtDate(v as string | null | undefined) },
{ key: "dateCancelled", labelTH: "ยกเลิก", groupTH: "วัน–เวลา", format: (v) => fmtDate(v as string | null | undefined) },
-
- // ลูกค้า
{ key: "customerId", labelTH: "รหัสลูกค้า", groupTH: "ลูกค้า" },
{ key: "customerName", labelTH: "ชื่อลูกค้า", groupTH: "ลูกค้า" },
{ key: "customerEmail", labelTH: "อีเมลลูกค้า", groupTH: "ลูกค้า" },
{ key: "customerPhone", labelTH: "เบอร์ลูกค้า", groupTH: "ลูกค้า" },
-
- // ที่อยู่จัดส่ง
{ key: "shippingName", labelTH: "ชื่อผู้รับ", groupTH: "ที่อยู่จัดส่ง" },
{ key: "shippingPhone", labelTH: "เบอร์ผู้รับ", groupTH: "ที่อยู่จัดส่ง" },
- {
- key: "shippingAddressLine1",
- labelTH: "ที่อยู่ (บรรทัด 1)",
- groupTH: "ที่อยู่จัดส่ง",
- },
- {
- key: "shippingAddressLine2",
- labelTH: "ที่อยู่ (บรรทัด 2)",
- groupTH: "ที่อยู่จัดส่ง",
- },
+ { key: "shippingAddressLine1", labelTH: "ที่อยู่ (บรรทัด 1)", groupTH: "ที่อยู่จัดส่ง" },
+ { key: "shippingAddressLine2", labelTH: "ที่อยู่ (บรรทัด 2)", groupTH: "ที่อยู่จัดส่ง" },
{ key: "shippingSubdistrict", labelTH: "แขวง/ตำบล", groupTH: "ที่อยู่จัดส่ง" },
{ key: "shippingDistrict", labelTH: "เขต/อำเภอ", groupTH: "ที่อยู่จัดส่ง" },
{ key: "shippingProvince", labelTH: "จังหวัด", groupTH: "ที่อยู่จัดส่ง" },
{ key: "shippingPostcode", labelTH: "รหัสไปรษณีย์", groupTH: "ที่อยู่จัดส่ง" },
{ key: "shippingCountry", labelTH: "ประเทศ", groupTH: "ที่อยู่จัดส่ง" },
-
- // ออกใบเสร็จ/ภาษี
{ key: "billingName", labelTH: "ชื่อสำหรับบิล", groupTH: "ออกใบเสร็จ/ภาษี" },
{ key: "billingTaxId", labelTH: "เลขภาษี", groupTH: "ออกใบเสร็จ/ภาษี" },
- {
- key: "billingAddressLine1",
- labelTH: "ที่อยู่บิล (บรรทัด 1)",
- groupTH: "ออกใบเสร็จ/ภาษี",
- },
+ { key: "billingAddressLine1", labelTH: "ที่อยู่บิล (บรรทัด 1)", groupTH: "ออกใบเสร็จ/ภาษี" },
{ key: "billingSubdistrict", labelTH: "แขวง/ตำบล (บิล)", groupTH: "ออกใบเสร็จ/ภาษี" },
{ key: "billingDistrict", labelTH: "เขต/อำเภอ (บิล)", groupTH: "ออกใบเสร็จ/ภาษี" },
{ key: "billingProvince", labelTH: "จังหวัด (บิล)", groupTH: "ออกใบเสร็จ/ภาษี" },
- {
- key: "billingPostcode",
- labelTH: "รหัสไปรษณีย์ (บิล)",
- groupTH: "ออกใบเสร็จ/ภาษี",
- },
+ { key: "billingPostcode", labelTH: "รหัสไปรษณีย์ (บิล)", groupTH: "ออกใบเสร็จ/ภาษี" },
{ key: "billingCountry", labelTH: "ประเทศ (บิล)", groupTH: "ออกใบเสร็จ/ภาษี" },
-
- // การชำระเงิน
{ key: "paymentMethod", labelTH: "วิธีชำระ", groupTH: "การชำระเงิน" },
- {
- key: "paymentStatus",
- labelTH: "สถานะชำระ",
- groupTH: "การชำระเงิน",
- format: (v) => (
- {v as string}
- ),
- },
+ { key: "paymentStatus", labelTH: "สถานะชำระ", groupTH: "การชำระเงิน", format: (v) => ({v as string}) },
{ key: "paymentProvider", labelTH: "ผู้ให้บริการชำระ", groupTH: "การชำระเงิน" },
{ key: "paymentTxId", labelTH: "รหัสรายการชำระ", groupTH: "การชำระเงิน" },
- {
- key: "paymentFeeTHB",
- labelTH: "ค่าธรรมเนียม",
- groupTH: "การชำระเงิน",
- align: "right",
- format: (v) => (typeof v === "number" ? THB(v) : "-"),
- },
-
- // ราคา/ต้นทุนย่อย
+ { key: "paymentFeeTHB", labelTH: "ค่าธรรมเนียม", groupTH: "การชำระเงิน", align: "right", format: (v) => (typeof v === "number" ? THB(v) : "-") },
{ key: "currency", labelTH: "สกุล", groupTH: "ราคา/ต้นทุนย่อย" },
{ key: "exchangeRate", labelTH: "เรท", groupTH: "ราคา/ต้นทุนย่อย", align: "right" },
- {
- key: "subtotalTHB",
- labelTH: "ยอดก่อนลด",
- groupTH: "ราคา/ต้นทุนย่อย",
- align: "right",
- format: (v) => THB(Number(v)),
- },
- {
- key: "discountTHB",
- labelTH: "ส่วนลด",
- groupTH: "ราคา/ต้นทุนย่อย",
- align: "right",
- format: (v) => THB(Number(v)),
- },
- {
- key: "shippingFeeTHB",
- labelTH: "ค่าส่ง",
- groupTH: "ราคา/ต้นทุนย่อย",
- align: "right",
- format: (v) => THB(Number(v)),
- },
- {
- key: "codFeeTHB",
- labelTH: "ค่า COD",
- groupTH: "ราคา/ต้นทุนย่อย",
- align: "right",
- format: (v) => THB(Number(v)),
- },
- {
- key: "packagingFeeTHB",
- labelTH: "ค่าบรรจุภัณฑ์",
- groupTH: "ราคา/ต้นทุนย่อย",
- align: "right",
- format: (v) => THB(Number(v)),
- },
+ { key: "subtotalTHB", labelTH: "ยอดก่อนลด", groupTH: "ราคา/ต้นทุนย่อย", align: "right", format: (v) => THB(Number(v)) },
+ { key: "discountTHB", labelTH: "ส่วนลด", groupTH: "ราคา/ต้นทุนย่อย", align: "right", format: (v) => THB(Number(v)) },
+ { key: "shippingFeeTHB", labelTH: "ค่าส่ง", groupTH: "ราคา/ต้นทุนย่อย", align: "right", format: (v) => THB(Number(v)) },
+ { key: "codFeeTHB", labelTH: "ค่า COD", groupTH: "ราคา/ต้นทุนย่อย", align: "right", format: (v) => THB(Number(v)) },
+ { key: "packagingFeeTHB", labelTH: "ค่าบรรจุภัณฑ์", groupTH: "ราคา/ต้นทุนย่อย", align: "right", format: (v) => THB(Number(v)) },
{ key: "taxTHB", labelTH: "ภาษี", groupTH: "ราคา/ต้นทุนย่อย", align: "right", format: (v) => THB(Number(v)) },
{ key: "totalTHB", labelTH: "ยอดรวม", groupTH: "ราคา/ต้นทุนย่อย", align: "right", format: (v) => THB(Number(v)) },
-
- // สินค้า/โลจิสติกส์
{ key: "itemsCount", labelTH: "จำนวนชิ้น", groupTH: "สินค้า/โลจิสติกส์", align: "right" },
{ key: "totalWeightGrams", labelTH: "น้ำหนัก(g)", groupTH: "สินค้า/โลจิสติกส์", align: "right" },
{ key: "packageCount", labelTH: "จำนวนพัสดุ", groupTH: "สินค้า/โลจิสติกส์", align: "right" },
{ key: "dimensionsCmL", labelTH: "ยาว(cm)", groupTH: "สินค้า/โลจิสติกส์", align: "right" },
{ key: "dimensionsCmW", labelTH: "กว้าง(cm)", groupTH: "สินค้า/โลจิสติกส์", align: "right" },
{ key: "dimensionsCmH", labelTH: "สูง(cm)", groupTH: "สินค้า/โลจิสติกส์", align: "right" },
-
- // สถานะจัดส่ง
- {
- key: "fulfillStatus",
- labelTH: "สถานะจัดส่ง",
- groupTH: "สถานะจัดส่ง",
- format: (v) => (
- {v as string}
- ),
- },
+ { key: "fulfillStatus", labelTH: "สถานะจัดส่ง", groupTH: "สถานะจัดส่ง", format: (v) => ({v as string}) },
{ key: "shipBy", labelTH: "กำหนดส่งภายใน", groupTH: "สถานะจัดส่ง", format: (v) => fmtDate(v as string | null | undefined) },
{ key: "carrier", labelTH: "ขนส่ง", groupTH: "สถานะจัดส่ง" },
{ key: "serviceLevel", labelTH: "บริการ", groupTH: "สถานะจัดส่ง" },
{ key: "trackingNo", labelTH: "เลขติดตาม", groupTH: "สถานะจัดส่ง" },
-
- // โกดัง/ปฏิบัติการ
{ key: "warehouseCode", labelTH: "คลังสินค้า", groupTH: "โกดัง/ปฏิบัติการ" },
{ key: "pickListId", labelTH: "ใบหยิบ", groupTH: "โกดัง/ปฏิบัติการ" },
{ key: "waveId", labelTH: "รหัสเวฟ", groupTH: "โกดัง/ปฏิบัติการ" },
{ key: "binFrom", labelTH: "หยิบจากช่อง", groupTH: "โกดัง/ปฏิบัติการ" },
{ key: "binTo", labelTH: "ย้ายไปช่อง", groupTH: "โกดัง/ปฏิบัติการ" },
-
- // ธง/โน้ต/ความเสี่ยง
- {
- key: "tags",
- labelTH: "แท็ก",
- groupTH: "ธง/โน้ต/ความเสี่ยง",
- format: (v) => (Array.isArray(v) && v.length ? v.join("|") : "-"),
- },
- {
- key: "rmaFlag",
- labelTH: "มีเคลม?",
- groupTH: "ธง/โน้ต/ความเสี่ยง",
- format: (v) => (typeof v === "boolean" ? (v ? "YES" : "NO") : "-"),
- },
+ { key: "tags", labelTH: "แท็ก", groupTH: "ธง/โน้ต/ความเสี่ยง", format: (v) => (Array.isArray(v) && v.length ? v.join("|") : "-") },
+ { key: "rmaFlag", labelTH: "มีเคลม?", groupTH: "ธง/โน้ต/ความเสี่ยง", format: (v) => (typeof v === "boolean" ? (v ? "YES" : "NO") : "-") },
{ key: "rmaReason", labelTH: "สาเหตุเคลม", groupTH: "ธง/โน้ต/ความเสี่ยง" },
{ key: "noteSeller", labelTH: "โน้ตผู้ขาย", groupTH: "ธง/โน้ต/ความเสี่ยง" },
{ key: "noteBuyer", labelTH: "โน้ตผู้ซื้อ", groupTH: "ธง/โน้ต/ความเสี่ยง" },
- {
- key: "slaShipOnTime",
- labelTH: "ส่งทันเวลา?",
- groupTH: "ธง/โน้ต/ความเสี่ยง",
- format: (v) => (typeof v === "boolean" ? (v ? "OK" : "N/A") : "N/A"),
- },
- {
- key: "giftWrap",
- labelTH: "ห่อของขวัญ",
- groupTH: "ธง/โน้ต/ความเสี่ยง",
- format: (v) => (typeof v === "boolean" ? (v ? "YES" : "NO") : "NO"),
- },
- {
- key: "fragile",
- labelTH: "แตกหักง่าย",
- groupTH: "ธง/โน้ต/ความเสี่ยง",
- format: (v) => (typeof v === "boolean" ? (v ? "YES" : "NO") : "NO"),
- },
+ { key: "slaShipOnTime", labelTH: "ส่งทันเวลา?", groupTH: "ธง/โน้ต/ความเสี่ยง", format: (v) => (typeof v === "boolean" ? (v ? "OK" : "N/A") : "N/A") },
+ { key: "giftWrap", labelTH: "ห่อของขวัญ", groupTH: "ธง/โน้ต/ความเสี่ยง", format: (v) => (typeof v === "boolean" ? (v ? "YES" : "NO") : "NO") },
+ { key: "fragile", labelTH: "แตกหักง่าย", groupTH: "ธง/โน้ต/ความเสี่ยง", format: (v) => (typeof v === "boolean" ? (v ? "YES" : "NO") : "NO") },
{ key: "priorityLevel", labelTH: "ลำดับความสำคัญ", groupTH: "ธง/โน้ต/ความเสี่ยง", align: "right" },
{ key: "riskScore", labelTH: "คะแนนเสี่ยง", groupTH: "ธง/โน้ต/ความเสี่ยง", align: "right" },
{ key: "holdReason", labelTH: "เหตุผลพักออเดอร์", groupTH: "ธง/โน้ต/ความเสี่ยง" },
{ key: "fraudCheckStatus", labelTH: "สถานะ Fraud", groupTH: "ธง/โน้ต/ความเสี่ยง" },
];
-// ค่าตั้งต้นคอลัมน์ที่แสดง
const DEFAULT_VISIBLE: ColumnKey[] = [
"orderNo",
"dateCreated",
@@ -734,106 +528,30 @@ const DEFAULT_VISIBLE: ColumnKey[] = [
"warehouseCode",
];
-/* ===========================
- จัดกลุ่มสำหรับ Modal (ไทย)
-=========================== */
const GROUPS: { titleTH: string; keys: ColumnKey[] }[] = [
- {
- titleTH: "เอกลักษณ์ & ช่องทาง",
- keys: ["id", "orderNo", "orderRef", "channel", "channelOrderId", "marketplaceShopId", "marketplaceShopName"],
- },
- {
- titleTH: "ผู้เปิดออเดอร์",
- keys: ["createdByName", "createdByEmail", "createdById"],
- },
- {
- titleTH: "วัน–เวลา",
- keys: ["dateCreated", "datePaid", "datePacked", "dateShipped", "dateDelivered", "dateCancelled"],
- },
- {
- titleTH: "ลูกค้า",
- keys: ["customerId", "customerName", "customerEmail", "customerPhone"],
- },
- {
- titleTH: "ที่อยู่จัดส่ง",
- keys: [
- "shippingName",
- "shippingPhone",
- "shippingAddressLine1",
- "shippingAddressLine2",
- "shippingSubdistrict",
- "shippingDistrict",
- "shippingProvince",
- "shippingPostcode",
- "shippingCountry",
- ],
- },
- {
- titleTH: "ออกใบเสร็จ/ภาษี",
- keys: [
- "billingName",
- "billingTaxId",
- "billingAddressLine1",
- "billingSubdistrict",
- "billingDistrict",
- "billingProvince",
- "billingPostcode",
- "billingCountry",
- ],
- },
- {
- titleTH: "การชำระเงิน",
- keys: ["paymentMethod", "paymentStatus", "paymentProvider", "paymentTxId", "paymentFeeTHB"],
- },
- {
- titleTH: "ราคา/ต้นทุนย่อย",
- keys: ["currency", "exchangeRate", "subtotalTHB", "discountTHB", "shippingFeeTHB", "codFeeTHB", "packagingFeeTHB", "taxTHB", "totalTHB"],
- },
- {
- titleTH: "สินค้า/โลจิสติกส์",
- keys: ["itemsCount", "totalWeightGrams", "packageCount", "dimensionsCmL", "dimensionsCmW", "dimensionsCmH"],
- },
- {
- titleTH: "สถานะจัดส่ง",
- keys: ["fulfillStatus", "shipBy", "carrier", "serviceLevel", "trackingNo"],
- },
- {
- titleTH: "โกดัง/ปฏิบัติการ",
- keys: ["warehouseCode", "pickListId", "waveId", "binFrom", "binTo"],
- },
- {
- titleTH: "ธง/โน้ต/ความเสี่ยง",
- keys: [
- "tags",
- "rmaFlag",
- "rmaReason",
- "noteSeller",
- "noteBuyer",
- "slaShipOnTime",
- "giftWrap",
- "fragile",
- "priorityLevel",
- "riskScore",
- "holdReason",
- "fraudCheckStatus",
- ],
- },
+ { titleTH: "เอกลักษณ์ & ช่องทาง", keys: ["id", "orderNo", "orderRef", "channel", "channelOrderId", "marketplaceShopId", "marketplaceShopName"] },
+ { titleTH: "ผู้เปิดออเดอร์", keys: ["createdByName", "createdByEmail", "createdById"] },
+ { titleTH: "วัน–เวลา", keys: ["dateCreated", "datePaid", "datePacked", "dateShipped", "dateDelivered", "dateCancelled"] },
+ { titleTH: "ลูกค้า", keys: ["customerId", "customerName", "customerEmail", "customerPhone"] },
+ { titleTH: "ที่อยู่จัดส่ง", keys: ["shippingName", "shippingPhone", "shippingAddressLine1", "shippingAddressLine2", "shippingSubdistrict", "shippingDistrict", "shippingProvince", "shippingPostcode", "shippingCountry"] },
+ { titleTH: "ออกใบเสร็จ/ภาษี", keys: ["billingName", "billingTaxId", "billingAddressLine1", "billingSubdistrict", "billingDistrict", "billingProvince", "billingPostcode", "billingCountry"] },
+ { titleTH: "การชำระเงิน", keys: ["paymentMethod", "paymentStatus", "paymentProvider", "paymentTxId", "paymentFeeTHB"] },
+ { titleTH: "ราคา/ต้นทุนย่อย", keys: ["currency", "exchangeRate", "subtotalTHB", "discountTHB", "shippingFeeTHB", "codFeeTHB", "packagingFeeTHB", "taxTHB", "totalTHB"] },
+ { titleTH: "สินค้า/โลจิสติกส์", keys: ["itemsCount", "totalWeightGrams", "packageCount", "dimensionsCmL", "dimensionsCmW", "dimensionsCmH"] },
+ { titleTH: "สถานะจัดส่ง", keys: ["fulfillStatus", "shipBy", "carrier", "serviceLevel", "trackingNo"] },
+ { titleTH: "โกดัง/ปฏิบัติการ", keys: ["warehouseCode", "pickListId", "waveId", "binFrom", "binTo"] },
+ { titleTH: "ธง/โน้ต/ความเสี่ยง", keys: ["tags", "rmaFlag", "rmaReason", "noteSeller", "noteBuyer", "slaShipOnTime", "giftWrap", "fragile", "priorityLevel", "riskScore", "holdReason", "fraudCheckStatus"] },
];
-/* ===========================
- Page
-=========================== */
export default function OrdersPage() {
const [q, setQ] = useState("");
const [channel, setChannel] = useState<"all" | Channel>("all");
const [payment, setPayment] = useState<"all" | PaymentStatus>("all");
const [fulfillment, setFulfillment] = useState<"all" | FulfillmentStatus>("all");
-
const [visibleKeys, setVisibleKeys] = useState(DEFAULT_VISIBLE);
const [openCustomize, setOpenCustomize] = useState(false);
const [tempKeys, setTempKeys] = useState(DEFAULT_VISIBLE);
- // โหลด/จำคอลัมน์ที่เลือก
useEffect(() => {
try {
const raw = localStorage.getItem(LS_KEY);
@@ -853,7 +571,6 @@ export default function OrdersPage() {
} catch {}
};
- // กรองรายการ
const rows = useMemo(() => {
let arr = [...MOCK_ORDERS];
if (channel !== "all") arr = arr.filter((o) => o.channel === channel);
@@ -878,18 +595,15 @@ export default function OrdersPage() {
.some((x) => x.toLowerCase().includes(s)),
);
}
- // ใหม่สุดก่อน
arr.sort((a, b) => +new Date(b.dateCreated) - +new Date(a.dateCreated));
return arr;
}, [q, channel, payment, fulfillment]);
- // สร้างคอลัมน์ที่แสดงจริง
const visibleCols: Column[] = useMemo(() => {
const map = new Map(ALL_COLS.map((c) => [c.key, c]));
return visibleKeys.map((k) => map.get(k)!).filter(Boolean);
}, [visibleKeys]);
- // สร้างหัวตาราง 2 ชั้น: ชั้นบน = กลุ่ม, ชั้นล่าง = หัวคอลัมน์ย่อย
const topHeaderSegments = useMemo(() => {
type Seg = { groupTH: string; colSpan: number };
const segs: Seg[] = [];
@@ -905,8 +619,8 @@ export default function OrdersPage() {
}, [visibleCols]);
return (
-
- {/* ส่วนหัว */}
+
+ {/* Header */}
ออเดอร์
@@ -933,8 +647,8 @@ export default function OrdersPage() {
- {/* ตัวกรอง */}
-
+ {/* Filters — ใช้ p-5 ให้เท่ากันทุกการ์ด */}
+
-
- {/* ช่องทาง (Select) */}
-
- {/* การชำระเงิน */}
-
- {/* สถานะจัดส่ง */}
- {/* Modal: ปรับแต่งตาราง (ทำให้ scroll ได้ + footer ติดล่าง) */}
{openCustomize && (
e.stopPropagation()}
>
- {/* Header (ติดบน) */}
-
+
ปรับแต่งตาราง
- {/* Body (เลื่อนลงได้) */}
- {/* Footer (ติดล่าง) */}
-
-
เคล็ดลับ: ระบบจะจำคอลัมน์ที่คุณเลือกไว้ในเบราว์เซอร์นี้
+
+
+ เคล็ดลับ: ระบบจะจำคอลัมน์ที่คุณเลือกไว้ในเบราว์เซอร์นี้
+