diff --git a/src/app/(protected)/dashboard/page.tsx b/src/app/(protected)/dashboard/page.tsx index e12978b..a088e9c 100644 --- a/src/app/(protected)/dashboard/page.tsx +++ b/src/app/(protected)/dashboard/page.tsx @@ -141,7 +141,6 @@ export default function DashboardPage() { return (
- {/* Header / Filters */}

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) */}
- - {/* การชำระเงิน */}
- - {/* สถานะจัดส่ง */}