From 721653f6924653b58c87491b25a1a5b2ff5f50e5 Mon Sep 17 00:00:00 2001 From: phaichayon Date: Fri, 19 Jun 2026 11:11:17 +0700 Subject: [PATCH] thai-uxui --- docs/business/crm-terminology.md | 31 ++ ...x1-thai-crm-terminology-standardization.md | 58 +++ plans/task-ux.1.md | 471 ++++++++++++++++++ src/app/api/crm/dashboard/export/route.ts | 14 +- src/app/dashboard/crm/approvals/[id]/page.tsx | 4 +- src/app/dashboard/crm/approvals/page.tsx | 4 +- src/app/dashboard/crm/customers/page.tsx | 4 +- src/app/dashboard/crm/enquiries/[id]/page.tsx | 4 +- src/app/dashboard/crm/enquiries/page.tsx | 4 +- src/app/dashboard/crm/leads/[id]/page.tsx | 4 +- src/app/dashboard/crm/leads/page.tsx | 4 +- src/app/dashboard/crm/page.tsx | 2 +- .../dashboard/crm/quotations/[id]/page.tsx | 4 +- src/app/dashboard/crm/quotations/page.tsx | 4 +- src/config/nav-config.ts | 14 +- .../crm/components/project-parties-editor.tsx | 22 +- .../components/contact-form-sheet.tsx | 42 +- .../components/customer-cell-action.tsx | 12 +- .../customers/components/customer-columns.tsx | 12 +- .../components/customer-contacts-tab.tsx | 24 +- .../customers/components/customer-detail.tsx | 50 +- .../components/customer-form-sheet.tsx | 68 ++- .../components/dashboard-approval-metrics.tsx | 26 +- .../components/dashboard-filters.tsx | 32 +- .../components/dashboard-followups.tsx | 24 +- .../dashboard/components/dashboard-funnel.tsx | 8 +- .../components/dashboard-hot-projects.tsx | 16 +- .../components/dashboard-revenue-cards.tsx | 24 +- .../components/dashboard-sales-ranking.tsx | 20 +- .../components/dashboard-summary-cards.tsx | 46 +- src/features/crm/dashboard/server/service.ts | 13 +- .../components/enquiry-assignment-dialog.tsx | 28 +- .../components/enquiry-cell-action.tsx | 12 +- .../enquiries/components/enquiry-columns.tsx | 4 +- .../enquiries/components/enquiry-detail.tsx | 122 ++--- .../components/enquiry-followups-tab.tsx | 22 +- .../components/enquiry-form-sheet.tsx | 84 ++-- .../components/quotation-cell-action.tsx | 12 +- .../components/quotation-columns.tsx | 32 +- .../components/quotation-detail.tsx | 8 +- .../components/quotation-document-preview.tsx | 106 ++-- .../components/quotation-form-sheet.tsx | 2 +- src/features/crm/shared/project-party.ts | 14 +- src/features/crm/shared/terminology.ts | 51 ++ .../approval/components/approval-columns.tsx | 30 +- .../components/approval-request-panel.tsx | 72 +-- .../components/approval-status-badge.tsx | 9 +- 47 files changed, 1135 insertions(+), 538 deletions(-) create mode 100644 docs/business/crm-terminology.md create mode 100644 docs/implementation/task-ux1-thai-crm-terminology-standardization.md create mode 100644 plans/task-ux.1.md create mode 100644 src/features/crm/shared/terminology.ts diff --git a/docs/business/crm-terminology.md b/docs/business/crm-terminology.md new file mode 100644 index 0000000..e26c54f --- /dev/null +++ b/docs/business/crm-terminology.md @@ -0,0 +1,31 @@ +# CRM Terminology Registry + +| Code | English | Thai | Notes | +| ---- | ------- | ---- | ----- | +| crm | CRM | CRM | Keep as `CRM` | +| dashboard | Dashboard | Dashboard | Keep as `Dashboard` | +| lead | Lead | ลีด | ข้อมูลลูกค้าเป้าหมายก่อนส่งต่อให้ฝ่ายขาย | +| enquiry | Enquiry | โอกาสขาย | โอกาสทางการขายที่มอบหมายให้ฝ่ายขายดำเนินการ | +| quotation | Quotation | ใบเสนอราคา | ใช้กับทุกหน้าที่เกี่ยวกับเอกสารเสนอราคา | +| approval | Approval | อนุมัติเอกสาร | ใช้กับ workflow และคิวอนุมัติ | +| customer | Customer | ลูกค้า | ใช้กับทะเบียนลูกค้าและบทบาทลูกค้า | +| contact | Contact | ผู้ติดต่อ | ใช้กับบุคคลติดต่อของลูกค้า | +| billing_customer | Billing Customer | ลูกค้าผู้รับใบเสนอราคา | บริษัทที่ใช้สำหรับออกใบเสนอราคาและวางบิล | +| project_parties | Project Parties | ผู้เกี่ยวข้องในโครงการ | ใช้กับ customer roles ใน enquiry และ quotation | +| project_party.customer | Customer | ลูกค้า | บทบาทในโครงการ | +| project_party.billing_customer | Billing Customer | ลูกค้าผู้รับใบเสนอราคา | บทบาทในโครงการ | +| project_party.end_customer | End Customer | เจ้าของโครงการ | บทบาทในโครงการ | +| project_party.contractor | Contractor | ผู้รับเหมา | บทบาทในโครงการ | +| project_party.consultant | Consultant | ที่ปรึกษา | บทบาทในโครงการ | +| lead_count | Lead Count | จำนวนลีด | KPI dashboard | +| enquiry_count | Enquiry Count | จำนวนโอกาสขาย | KPI dashboard | +| won_count | Won Count | จำนวนงานที่ชนะ | KPI dashboard | +| lost_count | Lost Count | จำนวนงานที่แพ้ | KPI dashboard | +| revenue | Revenue | มูลค่าใบเสนอราคา | KPI / report label | +| conversion_rate | Conversion Rate | อัตราการเปลี่ยนสถานะ | KPI / report label | + +## Freeze Policy + +- เอกสารนี้เป็น source of truth ของคำธุรกิจ CRM ฝั่ง UI +- การเปลี่ยนคำหลังจากนี้ต้องผ่าน ADR หรือ business review ก่อน +- ห้ามใช้เอกสารนี้เพื่อเปลี่ยนชื่อ schema, API route, TypeScript type, หรือ service folder diff --git a/docs/implementation/task-ux1-thai-crm-terminology-standardization.md b/docs/implementation/task-ux1-thai-crm-terminology-standardization.md new file mode 100644 index 0000000..694762b --- /dev/null +++ b/docs/implementation/task-ux1-thai-crm-terminology-standardization.md @@ -0,0 +1,58 @@ +# Task UX.1: Thai CRM Terminology Standardization + +## Files Added + +- `docs/business/crm-terminology.md` +- `docs/implementation/task-ux1-thai-crm-terminology-standardization.md` +- `src/features/crm/shared/terminology.ts` + +## Files Modified + +- `src/config/nav-config.ts` +- `src/features/crm/shared/project-party.ts` +- `src/app/dashboard/crm/page.tsx` +- `src/app/dashboard/crm/customers/page.tsx` +- `src/app/dashboard/crm/leads/page.tsx` +- `src/app/dashboard/crm/leads/[id]/page.tsx` +- `src/app/dashboard/crm/enquiries/page.tsx` +- `src/app/dashboard/crm/enquiries/[id]/page.tsx` +- `src/app/dashboard/crm/quotations/page.tsx` +- `src/app/dashboard/crm/approvals/page.tsx` +- `src/app/dashboard/crm/approvals/[id]/page.tsx` +- `src/features/crm/dashboard/components/*` +- `src/app/api/crm/dashboard/export/route.ts` +- `src/features/crm/components/project-parties-editor.tsx` +- `src/features/crm/customers/components/*` +- `src/features/crm/enquiries/components/*` +- `src/features/crm/quotations/components/quotation-columns.tsx` +- `src/features/crm/quotations/components/quotation-document-preview.tsx` +- `src/features/foundation/approval/components/*` + +## Terminology Registry + +- Added `docs/business/crm-terminology.md` as the frozen source of truth for CRM UI business terms. + +## Menu Changes + +- Standardized CRM side menu labels to Thai business terminology while keeping `CRM` and `Dashboard` in English. + +## Dashboard Changes + +- Updated KPI cards, funnel labels, follow-up labels, approval analytics labels, revenue tables, and export labels to the frozen Thai terms. + +## Form Changes + +- Updated business-facing customer, contact, lead, and enquiry form labels and CTA copy without renaming any data fields. + +## PDF Changes + +- Updated quotation preview labels to Thai business terminology without changing template mapping keys. + +## Report Changes + +- Updated CRM dashboard export headers and section labels to Thai business terminology while keeping internal query/filter codes unchanged. + +## Remaining Risks + +- Some technical or admin-facing screens still intentionally remain English because they are out of scope for this task. +- Master option values seeded in the database may still be English in storage, so this task relies on UI copy and display-layer terminology rather than persistence changes. diff --git a/plans/task-ux.1.md b/plans/task-ux.1.md new file mode 100644 index 0000000..996b77b --- /dev/null +++ b/plans/task-ux.1.md @@ -0,0 +1,471 @@ +# Task UX.1: Thai CRM Terminology Standardization + +## Goal + +สร้างมาตรฐานคำศัพท์ภาษาไทยสำหรับ ALLA OS CRM + +เพื่อให้: + +* Menu +* Form +* Detail Page +* Dashboard +* Report +* PDF +* Approval Workflow + +ใช้ภาษาธุรกิจชุดเดียวกัน + +หลัง Task นี้ + +Terminology ถือว่า Frozen + +การเปลี่ยนคำในอนาคตต้องผ่าน ADR หรือ Business Review + +--- + +# Background + +CRM ได้ผ่าน + +```txt +Task D +Task E +Task F +Task G +Task H +Task I +Task J +``` + +แล้ว + +ปัจจุบันมีการใช้คำผสมกัน: + +```txt +Lead +Enquiry +Opportunity +Quotation +Customer +Project Party +Billing Customer +``` + +ซึ่งอาจสร้างความสับสนให้ผู้ใช้งาน + +โดยเฉพาะ: + +```txt +Marketing +Sales +Manager +Admin +``` + +--- + +# Scope UX.1.1 Terminology Registry + +Create: + +```txt +docs/business/crm-terminology.md +``` + +Document format: + +| Code | English | Thai | Notes | +| ---- | ------- | ---- | ----- | + +This file becomes the source of truth. + +--- + +# Scope UX.1.2 Frozen CRM Business Terms + +## CRM + +Keep: + +```txt +CRM +``` + +Do not translate. + +--- + +## Dashboard + +Keep: + +```txt +Dashboard +``` + +--- + +## Lead + +Display: + +```txt +ลีด +``` + +Definition: + +```txt +ข้อมูลลูกค้าเป้าหมายก่อนส่งต่อให้ฝ่ายขาย +``` + +--- + +## Enquiry + +Display: + +```txt +โอกาสขาย +``` + +Definition: + +```txt +โอกาสทางการขายที่ได้รับมอบหมายให้ฝ่ายขายดำเนินการ +``` + +--- + +## Quotation + +Display: + +```txt +ใบเสนอราคา +``` + +--- + +## Approval + +Display: + +```txt +อนุมัติเอกสาร +``` + +--- + +## Customer + +Display: + +```txt +ลูกค้า +``` + +--- + +## Contact + +Display: + +```txt +ผู้ติดต่อ +``` + +--- + +## Billing Customer + +Display: + +```txt +ลูกค้าผู้รับใบเสนอราคา +``` + +Definition: + +```txt +บริษัทที่ใช้สำหรับออกใบเสนอราคาและวางบิล +``` + +--- + +## Project Parties + +Display: + +```txt +ผู้เกี่ยวข้องในโครงการ +``` + +--- + +# Scope UX.1.3 Project Party Roles + +## customer + +Display: + +```txt +ลูกค้า +``` + +--- + +## billing_customer + +Display: + +```txt +ลูกค้าผู้รับใบเสนอราคา +``` + +--- + +## end_customer + +Display: + +```txt +เจ้าของโครงการ +``` + +--- + +## contractor + +Display: + +```txt +ผู้รับเหมา +``` + +--- + +## consultant + +Display: + +```txt +ที่ปรึกษา +``` + +--- + +# Scope UX.1.4 Dashboard KPI Terms + +## Lead Count + +Display: + +```txt +จำนวนลีด +``` + +--- + +## Enquiry Count + +Display: + +```txt +จำนวนโอกาสขาย +``` + +--- + +## Won Count + +Display: + +```txt +จำนวนงานที่ชนะ +``` + +--- + +## Lost Count + +Display: + +```txt +จำนวนงานที่แพ้ +``` + +--- + +## Revenue + +Display: + +```txt +มูลค่าใบเสนอราคา +``` + +--- + +## Conversion Rate + +Display: + +```txt +อัตราการเปลี่ยนสถานะ +``` + +--- + +# Scope UX.1.5 Status Labels + +## Lead + +```txt +ใหม่ +ติดต่อแล้ว +ผ่านการคัดกรอง +ไม่ผ่านการคัดกรอง +ส่งต่อฝ่ายขายแล้ว +``` + +--- + +## Enquiry + +```txt +โอกาสขาย +ชนะ +แพ้ +``` + +Mapped from: + +```txt +lead +enquiry +closed_won +closed_lost +``` + +--- + +# Scope UX.1.6 Menu Labels + +CRM Menu: + +```txt +Dashboard +ลีด +โอกาสขาย +ใบเสนอราคา +อนุมัติเอกสาร +ตั้งค่า CRM +``` + +--- + +# Scope UX.1.7 Form Labels + +Update labels only. + +Do NOT rename: + +```txt +database fields +api fields +typescript types +server service names +table names +``` + +Business label only. + +--- + +# Scope UX.1.8 PDF Terminology + +Update document preview and PDF rendering labels. + +Examples: + +```txt +Billing Customer +→ ลูกค้าผู้รับใบเสนอราคา + +Project Parties +→ ผู้เกี่ยวข้องในโครงการ + +Quotation +→ ใบเสนอราคา +``` + +Do not modify template mapping keys. + +--- + +# Scope UX.1.9 Reports + +Report headers must use: + +```txt +Thai business terms +``` + +while keeping export column codes unchanged internally. + +--- + +# Explicit Non-Scope + +Do NOT: + +```txt +Rename database schema +Rename API routes +Rename TS types +Rename service folders +Create i18n system +Translate admin technical screens +``` + +--- + +# Output + +1. Files Added +2. Files Modified +3. Terminology Registry +4. Menu Changes +5. Dashboard Changes +6. Form Changes +7. PDF Changes +8. Report Changes +9. Remaining Risks + +--- + +# Definition of Done + +✔ CRM terminology registry exists + +✔ Menu terminology standardized + +✔ Lead terminology standardized + +✔ Enquiry terminology standardized + +✔ Project Party terminology standardized + +✔ Dashboard terminology standardized + +✔ PDF terminology standardized + +✔ Report terminology standardized + +✔ No database changes + +✔ No API changes + +✔ No schema rename diff --git a/src/app/api/crm/dashboard/export/route.ts b/src/app/api/crm/dashboard/export/route.ts index 51321a2..2c4d110 100644 --- a/src/app/api/crm/dashboard/export/route.ts +++ b/src/app/api/crm/dashboard/export/route.ts @@ -56,12 +56,12 @@ export async function GET(request: NextRequest) { if (section === 'revenue-analytics') { rows = [ - ['Dimension', 'Customer Code', 'Customer Name', 'Revenue', 'Quotation Count', 'Won Revenue'], + ['มิติรายงาน', 'รหัสลูกค้า', 'ชื่อลูกค้า', 'มูลค่าใบเสนอราคา', 'จำนวนใบเสนอราคา', 'มูลค่างานที่ชนะ'], ...[ - ['End Customer', ...data.revenueAnalytics.topEndCustomers], - ['Billing Customer', ...data.revenueAnalytics.topBillingCustomers], - ['Contractor', ...data.revenueAnalytics.topContractors], - ['Consultant', ...data.revenueAnalytics.topConsultants] + ['เจ้าของโครงการ', ...data.revenueAnalytics.topEndCustomers], + ['ลูกค้าผู้รับใบเสนอราคา', ...data.revenueAnalytics.topBillingCustomers], + ['ผู้รับเหมา', ...data.revenueAnalytics.topContractors], + ['ที่ปรึกษา', ...data.revenueAnalytics.topConsultants] ].flatMap(([dimension, ...entries]) => (entries as typeof data.revenueAnalytics.topEndCustomers).map((row) => [ String(dimension), @@ -75,7 +75,7 @@ export async function GET(request: NextRequest) { ]; } else if (section === 'pipeline-funnel') { rows = [ - ['Stage', 'Count', 'Value', 'Conversion Rate'], + ['ขั้นตอน', 'จำนวน', 'มูลค่า', 'อัตราการเปลี่ยนสถานะ'], ...data.funnel.map((row) => [ row.label, String(row.count), @@ -85,7 +85,7 @@ export async function GET(request: NextRequest) { ]; } else { rows = [ - ['Sales Person', 'Lead Count', 'Enquiry Count', 'Quotation Count', 'Approved Quotations', 'Won Revenue', 'Conversion Rate'], + ['ฝ่ายขาย', 'จำนวนลีด', 'จำนวนโอกาสขาย', 'จำนวนใบเสนอราคา', 'จำนวนใบเสนอราคาที่อนุมัติแล้ว', 'มูลค่างานที่ชนะ', 'อัตราการเปลี่ยนสถานะ'], ...data.salesRanking.map((row) => [ row.salesPersonName, String(row.leadCount), diff --git a/src/app/dashboard/crm/approvals/[id]/page.tsx b/src/app/dashboard/crm/approvals/[id]/page.tsx index 326f2c9..a911bb3 100644 --- a/src/app/dashboard/crm/approvals/[id]/page.tsx +++ b/src/app/dashboard/crm/approvals/[id]/page.tsx @@ -48,8 +48,8 @@ export default async function ApprovalDetailRoute({ params }: PageProps) { return ( diff --git a/src/app/dashboard/crm/approvals/page.tsx b/src/app/dashboard/crm/approvals/page.tsx index 976451d..8913ea5 100644 --- a/src/app/dashboard/crm/approvals/page.tsx +++ b/src/app/dashboard/crm/approvals/page.tsx @@ -26,8 +26,8 @@ export default async function ApprovalsRoute(props: PageProps) { return ( diff --git a/src/app/dashboard/crm/customers/page.tsx b/src/app/dashboard/crm/customers/page.tsx index 1ebf5c5..f4a5920 100644 --- a/src/app/dashboard/crm/customers/page.tsx +++ b/src/app/dashboard/crm/customers/page.tsx @@ -48,8 +48,8 @@ export default async function CustomersRoute(props: PageProps) { return ( diff --git a/src/app/dashboard/crm/enquiries/[id]/page.tsx b/src/app/dashboard/crm/enquiries/[id]/page.tsx index 4cca6b3..28148ec 100644 --- a/src/app/dashboard/crm/enquiries/[id]/page.tsx +++ b/src/app/dashboard/crm/enquiries/[id]/page.tsx @@ -78,8 +78,8 @@ export default async function EnquiryDetailRoute({ params }: PageProps) { return ( diff --git a/src/app/dashboard/crm/enquiries/page.tsx b/src/app/dashboard/crm/enquiries/page.tsx index 962e2f6..26c50b0 100644 --- a/src/app/dashboard/crm/enquiries/page.tsx +++ b/src/app/dashboard/crm/enquiries/page.tsx @@ -61,8 +61,8 @@ export default async function EnquiriesRoute(props: PageProps) { return ( diff --git a/src/app/dashboard/crm/leads/[id]/page.tsx b/src/app/dashboard/crm/leads/[id]/page.tsx index d8a1c09..ce3d6d5 100644 --- a/src/app/dashboard/crm/leads/[id]/page.tsx +++ b/src/app/dashboard/crm/leads/[id]/page.tsx @@ -66,8 +66,8 @@ export default async function LeadDetailRoute({ params }: PageProps) { return ( diff --git a/src/app/dashboard/crm/leads/page.tsx b/src/app/dashboard/crm/leads/page.tsx index 623835f..9f01135 100644 --- a/src/app/dashboard/crm/leads/page.tsx +++ b/src/app/dashboard/crm/leads/page.tsx @@ -53,8 +53,8 @@ export default async function LeadsRoute(props: PageProps) { return ( diff --git a/src/app/dashboard/crm/page.tsx b/src/app/dashboard/crm/page.tsx index 8faa947..1e3c4ee 100644 --- a/src/app/dashboard/crm/page.tsx +++ b/src/app/dashboard/crm/page.tsx @@ -49,7 +49,7 @@ export default async function CrmDashboardRoute(props: PageProps) { return ( diff --git a/src/app/dashboard/crm/quotations/[id]/page.tsx b/src/app/dashboard/crm/quotations/[id]/page.tsx index 260c95d..27f4877 100644 --- a/src/app/dashboard/crm/quotations/[id]/page.tsx +++ b/src/app/dashboard/crm/quotations/[id]/page.tsx @@ -135,8 +135,8 @@ export default async function QuotationDetailRoute({ params }: PageProps) { return ( diff --git a/src/app/dashboard/crm/quotations/page.tsx b/src/app/dashboard/crm/quotations/page.tsx index 92bb54a..daf5e6f 100644 --- a/src/app/dashboard/crm/quotations/page.tsx +++ b/src/app/dashboard/crm/quotations/page.tsx @@ -48,8 +48,8 @@ export default async function QuotationsRoute(props: PageProps) { return ( diff --git a/src/config/nav-config.ts b/src/config/nav-config.ts index bc1fab9..7d99995 100644 --- a/src/config/nav-config.ts +++ b/src/config/nav-config.ts @@ -84,7 +84,7 @@ export const navGroups: NavGroup[] = [ label: "CRM", items: [ { - title: "Dashboard CRM", + title: "Dashboard", url: "/dashboard/crm", icon: "dashboard", isActive: false, @@ -92,7 +92,7 @@ export const navGroups: NavGroup[] = [ access: { requireOrg: true, permission: "crm.dashboard.read" }, }, { - title: "Customers", + title: "ลูกค้า", url: "/dashboard/crm/customers", icon: "teams", isActive: false, @@ -100,7 +100,7 @@ export const navGroups: NavGroup[] = [ access: { requireOrg: true, permission: "crm.customer.read" }, }, { - title: "Leads", + title: "ลีด", url: "/dashboard/crm/leads", icon: "forms", isActive: false, @@ -108,7 +108,7 @@ export const navGroups: NavGroup[] = [ access: { requireOrg: true, permission: "crm.lead.read" }, }, { - title: "Enquiries", + title: "โอกาสขาย", url: "/dashboard/crm/enquiries", icon: "forms", isActive: false, @@ -116,7 +116,7 @@ export const navGroups: NavGroup[] = [ access: { requireOrg: true, permission: "crm.enquiry.read" }, }, { - title: "Quotations", + title: "ใบเสนอราคา", url: "/dashboard/crm/quotations", icon: "post", isActive: false, @@ -124,7 +124,7 @@ export const navGroups: NavGroup[] = [ access: { requireOrg: true, role: "admin" }, }, { - title: "Approvals", + title: "อนุมัติเอกสาร", url: "/dashboard/crm/approvals", icon: "checks", isActive: false, @@ -132,7 +132,7 @@ export const navGroups: NavGroup[] = [ access: { requireOrg: true, permission: "crm.approval.read" }, }, { - title: "CRM Settings", + title: "ตั้งค่า CRM", url: "/dashboard/crm/settings/master-options", icon: "settings", isActive: false, diff --git a/src/features/crm/components/project-parties-editor.tsx b/src/features/crm/components/project-parties-editor.tsx index 1dd3261..4113e4f 100644 --- a/src/features/crm/components/project-parties-editor.tsx +++ b/src/features/crm/components/project-parties-editor.tsx @@ -75,19 +75,19 @@ export function ProjectPartiesEditor({
-
Project Parties
+
ผู้เกี่ยวข้องในโครงการ

- Add related companies with their role and optional remark. + เพิ่มรายชื่อบริษัทที่เกี่ยวข้อง พร้อมบทบาทและหมายเหตุเพิ่มเติม

{!items.length ? (
- No project parties yet. Billing customer will still stay separate from this list. + ยังไม่มีผู้เกี่ยวข้องในโครงการ โดยลูกค้าผู้รับใบเสนอราคายังคงแยกจากรายการนี้
) : null} @@ -95,14 +95,14 @@ export function ProjectPartiesEditor({ {items.map((item, index) => (
-
Party {index + 1}
+
ผู้เกี่ยวข้อง {index + 1}
@@ -113,10 +113,10 @@ export function ProjectPartiesEditor({ } > - + - Select customer + เลือกลูกค้า {customers.map((customer) => ( {customer.name} ({customer.code}) @@ -127,10 +127,10 @@ export function ProjectPartiesEditor({ updateItem(item.key, 'remark', event.target.value)} - placeholder='Remark' + placeholder='หมายเหตุ' />
diff --git a/src/features/crm/customers/components/contact-form-sheet.tsx b/src/features/crm/customers/components/contact-form-sheet.tsx index 9174bee..10329ee 100644 --- a/src/features/crm/customers/components/contact-form-sheet.tsx +++ b/src/features/crm/customers/components/contact-form-sheet.tsx @@ -51,21 +51,21 @@ export function ContactFormSheet({ const createMutation = useMutation({ ...createCustomerContactMutation, onSuccess: () => { - toast.success('Contact created successfully'); + toast.success('สร้างผู้ติดต่อสำเร็จ'); onOpenChange(false); }, onError: (error) => - toast.error(error instanceof Error ? error.message : 'Failed to create contact') + toast.error(error instanceof Error ? error.message : 'ไม่สามารถสร้างผู้ติดต่อได้') }); const updateMutation = useMutation({ ...updateCustomerContactMutation, onSuccess: () => { - toast.success('Contact updated successfully'); + toast.success('อัปเดตผู้ติดต่อสำเร็จ'); onOpenChange(false); }, onError: (error) => - toast.error(error instanceof Error ? error.message : 'Failed to update contact') + toast.error(error instanceof Error ? error.message : 'ไม่สามารถอัปเดตผู้ติดต่อได้') }); const form = useAppForm({ @@ -106,43 +106,41 @@ export function ContactFormSheet({ - {isEdit ? 'Edit Contact' : 'New Contact'} - - Capture the operational contact person for this customer account. - + {isEdit ? 'แก้ไขผู้ติดต่อ' : 'เพิ่มผู้ติดต่อ'} + บันทึกข้อมูลผู้ติดต่อของลูกค้ารายนี้
- - - - - + + + + +
@@ -151,11 +149,11 @@ export function ContactFormSheet({ diff --git a/src/features/crm/customers/components/customer-cell-action.tsx b/src/features/crm/customers/components/customer-cell-action.tsx index 7de8ab3..cdf607e 100644 --- a/src/features/crm/customers/components/customer-cell-action.tsx +++ b/src/features/crm/customers/components/customer-cell-action.tsx @@ -36,11 +36,11 @@ export function CustomerCellAction({ const deleteMutation = useMutation({ ...deleteCustomerMutation, onSuccess: () => { - toast.success('Customer deleted successfully'); + toast.success('ลบลูกค้าสำเร็จ'); setDeleteOpen(false); }, onError: (error) => - toast.error(error instanceof Error ? error.message : 'Failed to delete customer') + toast.error(error instanceof Error ? error.message : 'ไม่สามารถลบลูกค้าได้') }); return ( @@ -65,15 +65,15 @@ export function CustomerCellAction({ - Actions + การดำเนินการ router.push(`/dashboard/crm/customers/${data.id}`)}> - View + ดูรายละเอียด setEditOpen(true)} disabled={!canUpdate}> - Edit + แก้ไข setDeleteOpen(true)} disabled={!canDelete}> - Delete + ลบ diff --git a/src/features/crm/customers/components/customer-columns.tsx b/src/features/crm/customers/components/customer-columns.tsx index 7636d03..3438f03 100644 --- a/src/features/crm/customers/components/customer-columns.tsx +++ b/src/features/crm/customers/components/customer-columns.tsx @@ -29,7 +29,7 @@ export function getCustomerColumns({ id: 'name', accessorKey: 'name', header: ({ column }: { column: Column }) => ( - + ), cell: ({ row }) => (
@@ -46,8 +46,8 @@ export function getCustomerColumns({
), meta: { - label: 'Customer', - placeholder: 'Search customers...', + label: 'ลูกค้า', + placeholder: 'ค้นหาลูกค้า...', variant: 'text' as const, icon: Icons.search }, @@ -85,7 +85,7 @@ export function getCustomerColumns({ ), meta: { - label: 'Type', + label: 'ประเภท', variant: 'multiSelect' as const, options: referenceData.customerTypes.map((item) => ({ value: item.id, @@ -108,7 +108,7 @@ export function getCustomerColumns({ ), meta: { - label: 'Branch', + label: 'สาขา', variant: 'multiSelect' as const, options: referenceData.branches.map((item) => ({ value: item.id, @@ -137,7 +137,7 @@ export function getCustomerColumns({ id: 'contactCount', accessorKey: 'contactCount', header: ({ column }: { column: Column }) => ( - + ), cell: ({ row }) => {row.original.contactCount} }, diff --git a/src/features/crm/customers/components/customer-contacts-tab.tsx b/src/features/crm/customers/components/customer-contacts-tab.tsx index 4fe71f9..d2bfa80 100644 --- a/src/features/crm/customers/components/customer-contacts-tab.tsx +++ b/src/features/crm/customers/components/customer-contacts-tab.tsx @@ -32,20 +32,20 @@ export function CustomerContactsTab({ const deleteMutation = useMutation({ ...deleteCustomerContactMutation, onSuccess: () => { - toast.success('Contact deleted successfully'); + toast.success('ลบผู้ติดต่อสำเร็จ'); setDeleteOpen(false); setSelectedId(null); }, onError: (error) => - toast.error(error instanceof Error ? error.message : 'Failed to delete contact') + toast.error(error instanceof Error ? error.message : 'ไม่สามารถลบผู้ติดต่อได้') }); return (
- Contacts - Manage the people attached to this customer account. + ผู้ติดต่อ + จัดการรายชื่อบุคคลที่ติดต่อกับลูกค้ารายนี้
{canCreate ? ( ) : null}
{!data.items.length ? (
- No contacts have been added yet. + ยังไม่มีผู้ติดต่อ
) : ( data.items.map((contact) => ( @@ -73,16 +73,16 @@ export function CustomerContactsTab({
{contact.name} - {contact.isPrimary ? Primary : null} - {!contact.isActive ? Inactive : null} + {contact.isPrimary ? ผู้ติดต่อหลัก : null} + {!contact.isActive ? ปิดใช้งาน : null}
{[contact.position, contact.department].filter(Boolean).join(' • ') || - 'No role details'} + 'ยังไม่มีข้อมูลตำแหน่ง'}
{[contact.phone, contact.mobile, contact.email].filter(Boolean).join(' • ') || - 'No contact methods'} + 'ยังไม่มีช่องทางติดต่อ'}
{contact.notes ?

{contact.notes}

: null}
@@ -96,7 +96,7 @@ export function CustomerContactsTab({ setOpen(true); }} > - Edit + แก้ไข
diff --git a/src/features/crm/customers/components/customer-detail.tsx b/src/features/crm/customers/components/customer-detail.tsx index 6da9eac..93e1ff8 100644 --- a/src/features/crm/customers/components/customer-detail.tsx +++ b/src/features/crm/customers/components/customer-detail.tsx @@ -100,14 +100,14 @@ export function CustomerDetail({

{customer.name}

- {customer.abbr || customer.email || customer.phone || 'Customer detail record'} + {customer.abbr || customer.email || customer.phone || 'รายละเอียดข้อมูลลูกค้า'}

{canUpdate ? ( ) : null}
@@ -119,18 +119,16 @@ export function CustomerDetail({ - Overview - {canReadContacts ? Contacts : null} - Activity - Related Documents + ภาพรวม + {canReadContacts ? ผู้ติดต่อ : null} + กิจกรรม + เอกสารที่เกี่ยวข้อง - Overview - - Core account data, location, and CRM classification. - + ข้อมูลลูกค้า + ข้อมูลหลักของลูกค้า ที่อยู่ และการจัดประเภทใน CRM @@ -142,21 +140,21 @@ export function CustomerDetail({ value={customer.branchId ? branchMap.get(customer.branchId) : null} /> - +
- +
@@ -190,15 +188,13 @@ export function CustomerDetail({ - Activity - - Audit log entries captured from customer mutations. - + กิจกรรม + ประวัติการเปลี่ยนแปลงที่เกิดจากการแก้ไขข้อมูลลูกค้า {!data.activity.length ? (
- No activity recorded yet. + ยังไม่มีกิจกรรม
) : ( data.activity.map((item, index) => ( @@ -229,15 +225,13 @@ export function CustomerDetail({ - Related Documents - - Production enquiry and quotation links for this customer. - + เอกสารที่เกี่ยวข้อง + ลิงก์ไปยังโอกาสขายและใบเสนอราคาที่เกี่ยวข้องกับลูกค้ารายนี้ {!relatedEnquiries.length && !relatedQuotations.length ? (
- No enquiries or quotations have been linked to this customer yet. + ยังไม่มีโอกาสขายหรือใบเสนอราคาที่เชื่อมกับลูกค้ารายนี้
) : (
@@ -288,8 +282,8 @@ export function CustomerDetail({
- Record Snapshot - Operational metadata for this customer row. + ข้อมูลระบบ + ข้อมูลประกอบการติดตามและตรวจสอบของรายการลูกค้านี้ diff --git a/src/features/crm/customers/components/customer-form-sheet.tsx b/src/features/crm/customers/components/customer-form-sheet.tsx index e97af6b..16ed9d3 100644 --- a/src/features/crm/customers/components/customer-form-sheet.tsx +++ b/src/features/crm/customers/components/customer-form-sheet.tsx @@ -73,21 +73,21 @@ export function CustomerFormSheet({ const createMutation = useMutation({ ...createCustomerMutation, onSuccess: () => { - toast.success('Customer created successfully'); + toast.success('สร้างลูกค้าสำเร็จ'); onOpenChange(false); }, onError: (error) => - toast.error(error instanceof Error ? error.message : 'Failed to create customer') + toast.error(error instanceof Error ? error.message : 'ไม่สามารถสร้างลูกค้าได้') }); const updateMutation = useMutation({ ...updateCustomerMutation, onSuccess: () => { - toast.success('Customer updated successfully'); + toast.success('อัปเดตลูกค้าสำเร็จ'); onOpenChange(false); }, onError: (error) => - toast.error(error instanceof Error ? error.message : 'Failed to update customer') + toast.error(error instanceof Error ? error.message : 'ไม่สามารถอัปเดตลูกค้าได้') }); const form = useAppForm({ @@ -146,10 +146,8 @@ export function CustomerFormSheet({ - {isEdit ? 'Edit Customer' : 'New Customer'} - - Maintain the core account profile, ownership branch, and CRM classification here. - + {isEdit ? 'แก้ไขลูกค้า' : 'เพิ่มลูกค้า'} + จัดการข้อมูลหลักของลูกค้า สาขาที่ดูแล และการจัดประเภทใน CRM
@@ -157,25 +155,25 @@ export function CustomerFormSheet({ - - - - + + + + - - + + ({ value: item.id, @@ -184,7 +182,7 @@ export function CustomerFormSheet({ /> ({ value: item.id, @@ -193,7 +191,7 @@ export function CustomerFormSheet({ /> ({ value: branch.id, label: branch.name @@ -201,7 +199,7 @@ export function CustomerFormSheet({ /> ({ value: item.id, label: item.label @@ -209,7 +207,7 @@ export function CustomerFormSheet({ /> ({ value: item.id, label: item.label @@ -217,10 +215,10 @@ export function CustomerFormSheet({ /> ({ value: item.id, @@ -228,24 +226,24 @@ export function CustomerFormSheet({ }))} />
- +
- - - - + + + +
@@ -254,11 +252,11 @@ export function CustomerFormSheet({ @@ -276,7 +274,7 @@ export function CustomerFormSheetTrigger({ return ( <> diff --git a/src/features/crm/dashboard/components/dashboard-approval-metrics.tsx b/src/features/crm/dashboard/components/dashboard-approval-metrics.tsx index 953806a..cf3fca0 100644 --- a/src/features/crm/dashboard/components/dashboard-approval-metrics.tsx +++ b/src/features/crm/dashboard/components/dashboard-approval-metrics.tsx @@ -24,42 +24,42 @@ export function DashboardApprovalMetrics({ return ( - Approval Analytics - Live workflow counts plus the current approver queue. + ภาพรวมการอนุมัติเอกสาร + สถิติ workflow ปัจจุบันและคิวงานที่รอผู้อนุมัติ
- - - - + + + +
- Document + เอกสาร Workflow - Current Step - Requested At + ขั้นตอนปัจจุบัน + วันที่ส่งอนุมัติ {approvals.myPendingApprovals.length === 0 ? ( - No pending approvals are assigned to your current approver scope. + ไม่มีงานอนุมัติที่อยู่ในขอบเขตการอนุมัติของคุณตอนนี้ ) : ( approvals.myPendingApprovals.map((row) => ( -
{row.entityCode ?? 'Approval'}
+
{row.entityCode ?? 'อนุมัติเอกสาร'}
{row.entityTitle ?? '-'}
{row.workflowName} diff --git a/src/features/crm/dashboard/components/dashboard-filters.tsx b/src/features/crm/dashboard/components/dashboard-filters.tsx index f58806c..b7ba243 100644 --- a/src/features/crm/dashboard/components/dashboard-filters.tsx +++ b/src/features/crm/dashboard/components/dashboard-filters.tsx @@ -61,11 +61,11 @@ export function DashboardFilters({ value={params.branch ?? '__all__'} onValueChange={(value) => void setParams({ branch: value === '__all__' ? null : value })} > - - + + - All Branches + ทุกสาขา {referenceData.branches.map((item) => ( {item.label} @@ -77,11 +77,11 @@ export function DashboardFilters({ value={params.salesman ?? '__all__'} onValueChange={(value) => void setParams({ salesman: value === '__all__' ? null : value })} > - - + + - All Sales + ฝ่ายขายทั้งหมด {referenceData.salesmen.map((item) => ( {item.name} @@ -95,11 +95,11 @@ export function DashboardFilters({ void setParams({ projectPartyRole: value === '__all__' ? null : value }) } > - - + + - All Party Roles + ทุกบทบาท {referenceData.projectPartyRoles.map((item) => ( {item.label} @@ -111,11 +111,11 @@ export function DashboardFilters({ value={params.productType ?? '__all__'} onValueChange={(value) => void setParams({ productType: value === '__all__' ? null : value })} > - - + + - All Product Types + ทุกประเภทสินค้า {referenceData.productTypes.map((item) => ( {item.label} @@ -138,7 +138,7 @@ export function DashboardFilters({ }) } > - Reset Filters + ล้างตัวกรอง {canExport ? ( <> @@ -148,7 +148,7 @@ export function DashboardFilters({ exportQuery ? `&${exportQuery}` : '' }`} > - Export Sales CSV + ส่งออก CSV อันดับฝ่ายขาย diff --git a/src/features/crm/dashboard/components/dashboard-followups.tsx b/src/features/crm/dashboard/components/dashboard-followups.tsx index 2374787..771d4d9 100644 --- a/src/features/crm/dashboard/components/dashboard-followups.tsx +++ b/src/features/crm/dashboard/components/dashboard-followups.tsx @@ -21,31 +21,31 @@ export function DashboardFollowups({ return ( - Follow-up Dashboard - Upcoming and completed CRM follow-ups across enquiries and quotations. + Dashboard งานติดตาม + ภาพรวมงานติดตามที่กำลังจะถึงและที่ดำเนินการแล้วของโอกาสขายและใบเสนอราคา
- - - - + + + +
- Date - Customer - Enquiry - Enquiry Owner - Source + วันที่ + ลูกค้า + โอกาสขาย + ผู้ดูแลโอกาสขาย + ที่มา {followups.upcoming.length === 0 ? ( - No upcoming follow-ups match the current filters. + ไม่พบงานติดตามที่ตรงกับตัวกรองปัจจุบัน ) : ( diff --git a/src/features/crm/dashboard/components/dashboard-funnel.tsx b/src/features/crm/dashboard/components/dashboard-funnel.tsx index 3a144d7..111efa2 100644 --- a/src/features/crm/dashboard/components/dashboard-funnel.tsx +++ b/src/features/crm/dashboard/components/dashboard-funnel.tsx @@ -13,8 +13,8 @@ export function DashboardFunnel({ steps }: { steps: CrmDashboardFunnelStep[] }) return ( - Sales Pipeline Funnel - Lead, enquiry, quotation, approval, and won stages from the current pipeline model. + Funnel การขาย + ภาพรวมตั้งแต่ลีด โอกาสขาย ใบเสนอราคา อนุมัติเอกสาร จนถึงงานที่ชนะ {steps.map((step) => ( @@ -23,12 +23,12 @@ export function DashboardFunnel({ steps }: { steps: CrmDashboardFunnelStep[] })
{step.label}
- {formatNumber(step.count)} items, value {formatNumber(step.value)} + {formatNumber(step.count)} รายการ, มูลค่า {formatNumber(step.value)}
{step.conversionRate === null ? '-' : `${formatNumber(step.conversionRate)}%`}
-
conversion
+
อัตราการเปลี่ยนสถานะ
diff --git a/src/features/crm/dashboard/components/dashboard-hot-projects.tsx b/src/features/crm/dashboard/components/dashboard-hot-projects.tsx index 568b3b1..d42202b 100644 --- a/src/features/crm/dashboard/components/dashboard-hot-projects.tsx +++ b/src/features/crm/dashboard/components/dashboard-hot-projects.tsx @@ -12,25 +12,25 @@ export function DashboardHotProjects({ rows }: { rows: CrmDashboardHotProjectRow return ( - Top Active Enquiries - Sales-owned hot enquiries ranked by estimated commercial value. + โอกาสขายเด่นที่กำลังดำเนินการ + จัดอันดับโอกาสขายสำคัญตามมูลค่าที่ประเมินไว้
- Project - Customer - Value - Enquiry Owner - Probability + โครงการ + ลูกค้า + มูลค่า + ผู้ดูแลโอกาสขาย + ความน่าจะเป็น {rows.length === 0 ? ( - No hot opportunities match the current filters. + ไม่พบโอกาสขายเด่นที่ตรงกับตัวกรองปัจจุบัน ) : ( diff --git a/src/features/crm/dashboard/components/dashboard-revenue-cards.tsx b/src/features/crm/dashboard/components/dashboard-revenue-cards.tsx index b4db79a..7761f8c 100644 --- a/src/features/crm/dashboard/components/dashboard-revenue-cards.tsx +++ b/src/features/crm/dashboard/components/dashboard-revenue-cards.tsx @@ -28,16 +28,16 @@ function RevenueTable({ Customer - Revenue - Quotations - Won Revenue + มูลค่าใบเสนอราคา + จำนวนใบเสนอราคา + มูลค่างานที่ชนะ {rows.length === 0 ? ( - No revenue rows match the current filters. + ไม่พบข้อมูลที่ตรงกับตัวกรองปัจจุบัน ) : ( @@ -68,23 +68,23 @@ export function DashboardRevenueCards({ return (
diff --git a/src/features/crm/dashboard/components/dashboard-sales-ranking.tsx b/src/features/crm/dashboard/components/dashboard-sales-ranking.tsx index 1bd1705..1aa9f29 100644 --- a/src/features/crm/dashboard/components/dashboard-sales-ranking.tsx +++ b/src/features/crm/dashboard/components/dashboard-sales-ranking.tsx @@ -12,27 +12,27 @@ export function DashboardSalesRanking({ rows }: { rows: CrmDashboardSalesRanking return ( - Sales Ranking - Lead and enquiry ownership follow assignment rules. Quotation ownership uses salesman. + อันดับผลงานฝ่ายขาย + การนับลีดและโอกาสขายยึดตามผู้รับผิดชอบ ส่วนใบเสนอราคายึดตามผู้ขาย
- Sales Person - Lead - Enquiry - Quotation - Approved - Won Revenue - Conversion + ฝ่ายขาย + ลีด + โอกาสขาย + ใบเสนอราคา + อนุมัติแล้ว + มูลค่างานที่ชนะ + อัตราการเปลี่ยนสถานะ {rows.length === 0 ? ( - No sales ranking rows match the current filters. + ไม่พบข้อมูลอันดับผลงานที่ตรงกับตัวกรองปัจจุบัน ) : ( diff --git a/src/features/crm/dashboard/components/dashboard-summary-cards.tsx b/src/features/crm/dashboard/components/dashboard-summary-cards.tsx index 4b83234..836333b 100644 --- a/src/features/crm/dashboard/components/dashboard-summary-cards.tsx +++ b/src/features/crm/dashboard/components/dashboard-summary-cards.tsx @@ -52,47 +52,47 @@ export function DashboardSummaryCards({ return (
{canViewCommercialData ? ( ) : null} {canViewCommercialData ? ( ) : null} diff --git a/src/features/crm/dashboard/server/service.ts b/src/features/crm/dashboard/server/service.ts index d16136e..459f839 100644 --- a/src/features/crm/dashboard/server/service.ts +++ b/src/features/crm/dashboard/server/service.ts @@ -30,6 +30,7 @@ import { getRevenueByContractor, getRevenueByEndCustomer } from '@/features/crm/reporting/server/service'; +import { getPipelineStageThaiLabel, getProjectPartyRoleThaiLabel } from '@/features/crm/shared/terminology'; import type { CrmDashboardApprovalRow, CrmDashboardApprovalSummary, @@ -332,19 +333,19 @@ function buildFunnel( const steps = [ { key: 'lead', - label: 'Lead', + label: getPipelineStageThaiLabel('lead'), count: summary.lead.leadCount, value: 0 }, { key: 'enquiry', - label: 'Enquiry', + label: getPipelineStageThaiLabel('enquiry'), count: summary.enquiry.enquiryCount, value: summary.enquiry.enquiryValue }, { key: 'quotation', - label: 'Quotation', + label: 'ใบเสนอราคา', count: scopedQuotations.filter( (row) => (statusMaps.quotationStatusById.get(row.status) ?? '') !== 'cancelled' ).length, @@ -354,7 +355,7 @@ function buildFunnel( }, { key: 'pending_approval', - label: 'Pending Approval', + label: 'รออนุมัติ', count: scopedQuotations.filter( (row) => (statusMaps.quotationStatusById.get(row.status) ?? '') === 'pending_approval' ).length, @@ -364,7 +365,7 @@ function buildFunnel( }, { key: 'approved', - label: 'Approved', + label: 'อนุมัติแล้ว', count: scopedQuotations.filter( (row) => (statusMaps.quotationStatusById.get(row.status) ?? '') === 'approved' ).length, @@ -374,7 +375,7 @@ function buildFunnel( }, { key: 'closed_won', - label: 'Closed Won', + label: getPipelineStageThaiLabel('closed_won'), count: summary.enquiry.wonCount, value: scopedQuotations .filter((row) => (statusMaps.quotationStatusById.get(row.status) ?? '') === 'accepted') diff --git a/src/features/crm/enquiries/components/enquiry-assignment-dialog.tsx b/src/features/crm/enquiries/components/enquiry-assignment-dialog.tsx index 3035957..1e803d1 100644 --- a/src/features/crm/enquiries/components/enquiry-assignment-dialog.tsx +++ b/src/features/crm/enquiries/components/enquiry-assignment-dialog.tsx @@ -57,21 +57,21 @@ export function EnquiryAssignmentDialog({ const assignMutation = useMutation({ ...assignEnquiryMutation, onSuccess: () => { - toast.success('Lead converted to enquiry successfully'); + toast.success('ส่งต่อลีดไปยังฝ่ายขายสำเร็จ'); onOpenChange(false); }, onError: (error) => - toast.error(error instanceof Error ? error.message : 'Failed to assign enquiry') + toast.error(error instanceof Error ? error.message : 'ไม่สามารถมอบหมายโอกาสขายได้') }); const reassignMutation = useMutation({ ...reassignEnquiryMutation, onSuccess: () => { - toast.success('Enquiry reassigned successfully'); + toast.success('เปลี่ยนผู้ดูแลโอกาสขายสำเร็จ'); onOpenChange(false); }, onError: (error) => - toast.error(error instanceof Error ? error.message : 'Failed to reassign enquiry') + toast.error(error instanceof Error ? error.message : 'ไม่สามารถเปลี่ยนผู้ดูแลโอกาสขายได้') }); const form = useAppForm({ @@ -109,11 +109,11 @@ export function EnquiryAssignmentDialog({ - {mode === 'assign' ? 'Convert To Enquiry' : 'Reassign Enquiry Owner'} + {mode === 'assign' ? 'ส่งต่อให้ฝ่ายขาย' : 'เปลี่ยนผู้ดูแลโอกาสขาย'} {mode === 'assign' - ? 'Assign this lead to sales and convert it into an enquiry.' - : 'Move this enquiry to a different sales owner and keep the handoff note visible.'} + ? 'มอบหมายลีดนี้ให้ฝ่ายขายเพื่อเปลี่ยนเป็นโอกาสขาย' + : 'เปลี่ยนผู้ดูแลโอกาสขายและเก็บบันทึกการส่งต่องานไว้'} @@ -121,24 +121,24 @@ export function EnquiryAssignmentDialog({ {noUsersAvailable ? (
- No assignable sales users are available in this workspace yet. + ยังไม่มีผู้ใช้ฝ่ายขายที่สามารถมอบหมายงานได้ใน workspace นี้
) : ( <> ({ value: user.id, label: `${user.name} (${user.businessRole.replaceAll('_', ' ')})` }))} - placeholder='Select sales user' + placeholder='เลือกผู้ใช้ฝ่ายขาย' /> )} @@ -147,7 +147,7 @@ export function EnquiryAssignmentDialog({
diff --git a/src/features/crm/enquiries/components/enquiry-cell-action.tsx b/src/features/crm/enquiries/components/enquiry-cell-action.tsx index 9976b45..bbbf4dd 100644 --- a/src/features/crm/enquiries/components/enquiry-cell-action.tsx +++ b/src/features/crm/enquiries/components/enquiry-cell-action.tsx @@ -42,14 +42,14 @@ export function EnquiryCellAction({ const deleteMutation = useMutation({ ...deleteEnquiryMutation, onSuccess: () => { - toast.success(`${workspace === 'lead' ? 'Lead' : 'Enquiry'} deleted successfully`); + toast.success(`${workspace === 'lead' ? 'ลบลีดสำเร็จ' : 'ลบโอกาสขายสำเร็จ'}`); setDeleteOpen(false); }, onError: (error) => toast.error( error instanceof Error ? error.message - : `Failed to delete ${workspace === 'lead' ? 'lead' : 'enquiry'}` + : `${workspace === 'lead' ? 'ไม่สามารถลบลีดได้' : 'ไม่สามารถลบโอกาสขายได้'}` ) }); @@ -76,7 +76,7 @@ export function EnquiryCellAction({ - Actions + การดำเนินการ router.push( @@ -86,13 +86,13 @@ export function EnquiryCellAction({ ) } > - View + ดูรายละเอียด setEditOpen(true)} disabled={!canUpdate}> - {workspace === 'lead' ? 'Edit Lead' : 'Edit Enquiry'} + {workspace === 'lead' ? 'แก้ไขลีด' : 'แก้ไขโอกาสขาย'} setDeleteOpen(true)} disabled={!canDelete}> - Delete + ลบ diff --git a/src/features/crm/enquiries/components/enquiry-columns.tsx b/src/features/crm/enquiries/components/enquiry-columns.tsx index 477d3b2..6be94b9 100644 --- a/src/features/crm/enquiries/components/enquiry-columns.tsx +++ b/src/features/crm/enquiries/components/enquiry-columns.tsx @@ -203,11 +203,11 @@ function getEnquiryWorkspaceColumns({ id: 'customer', accessorFn: (row) => row.customerId, header: ({ column }: { column: Column }) => ( - + ), cell: ({ row }) => {row.original.customerName}, meta: { - label: 'Billing Customer', + label: 'ลูกค้าผู้รับใบเสนอราคา', variant: 'multiSelect' as const, options: referenceData.customers.map((item) => ({ value: item.id, diff --git a/src/features/crm/enquiries/components/enquiry-detail.tsx b/src/features/crm/enquiries/components/enquiry-detail.tsx index 50d1694..7db1654 100644 --- a/src/features/crm/enquiries/components/enquiry-detail.tsx +++ b/src/features/crm/enquiries/components/enquiry-detail.tsx @@ -10,6 +10,7 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com import { Separator } from '@/components/ui/separator'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import type { QuotationRelationItem } from '@/features/crm/quotations/api/types'; +import { getPipelineStageThaiLabel } from '@/features/crm/shared/terminology'; import { enquiryByIdOptions, enquiryProjectPartiesOptions } from '../api/queries'; import type { EnquiryRecord, EnquiryReferenceData } from '../api/types'; import { EnquiryAssignmentDialog } from './enquiry-assignment-dialog'; @@ -27,18 +28,7 @@ function FieldItem({ label, value }: { label: string; value: string | null | und } function getPipelineStageLabel(stage: EnquiryRecord['pipelineStage']) { - switch (stage) { - case 'lead': - return 'Lead'; - case 'enquiry': - return 'Enquiry'; - case 'closed_won': - return 'Closed Won'; - case 'closed_lost': - return 'Closed Lost'; - default: - return stage; - } + return getPipelineStageThaiLabel(stage); } interface EnquiryDetailProps { @@ -109,26 +99,26 @@ export function EnquiryDetail({ const pipelineStageLabel = getPipelineStageLabel(enquiry.pipelineStage); const isLeadWorkspace = workspace === 'lead'; const backHref = isLeadWorkspace ? '/dashboard/crm/leads' : '/dashboard/crm/enquiries'; - const recordLabel = isLeadWorkspace ? 'Lead' : 'Enquiry'; - const ownerLabel = isLeadWorkspace ? 'Assigned Sales' : 'Enquiry Owner'; - const infoCardTitle = isLeadWorkspace ? 'Lead Information' : 'Enquiry Information'; + const recordLabel = isLeadWorkspace ? 'ลีด' : 'โอกาสขาย'; + const ownerLabel = isLeadWorkspace ? 'ฝ่ายขายที่รับผิดชอบ' : 'ผู้ดูแลโอกาสขาย'; + const infoCardTitle = isLeadWorkspace ? 'ข้อมูลลีด' : 'ข้อมูลโอกาสขาย'; const infoCardDescription = isLeadWorkspace - ? 'Lead source, qualification context, and sales handoff readiness.' - : 'Project scope, ownership context, and active sales execution details.'; + ? 'ที่มาของลีด ข้อมูลการคัดกรอง และความพร้อมในการส่งต่อให้ฝ่ายขาย' + : 'ขอบเขตงาน ข้อมูลผู้รับผิดชอบ และรายละเอียดการดูแลของฝ่ายขาย'; const assignmentButtonLabel = assignmentMode === 'assign' ? isLeadWorkspace - ? 'Assign To Sales' - : 'Assign Enquiry Owner' + ? 'ส่งต่อให้ฝ่ายขาย' + : 'มอบหมายผู้ดูแลโอกาสขาย' : isLeadWorkspace - ? 'Reassign Sales Owner' - : 'Reassign Enquiry Owner'; - const projectSectionLabel = isLeadWorkspace ? 'Lead Scope' : 'Project Information'; - const followupTabLabel = isLeadWorkspace ? 'Lead Follow-ups' : 'Follow-ups'; - const relatedTabLabel = isLeadWorkspace ? 'Related Quotations' : 'Quotations'; + ? 'เปลี่ยนฝ่ายขายที่รับผิดชอบ' + : 'เปลี่ยนผู้ดูแลโอกาสขาย'; + const projectSectionLabel = isLeadWorkspace ? 'ขอบเขตลีด' : 'ข้อมูลโครงการ'; + const followupTabLabel = isLeadWorkspace ? 'งานติดตามลีด' : 'งานติดตาม'; + const relatedTabLabel = isLeadWorkspace ? 'ใบเสนอราคาที่เกี่ยวข้อง' : 'ใบเสนอราคา'; const snapshotDescription = isLeadWorkspace - ? 'Operational metadata and lead ownership handoff state for this record.' - : 'Operational metadata and assignment state for this enquiry record.'; + ? 'ข้อมูลระบบและสถานะการส่งต่อของลีดรายการนี้' + : 'ข้อมูลระบบและสถานะการรับผิดชอบของโอกาสขายรายการนี้'; return (
@@ -147,7 +137,7 @@ export function EnquiryDetail({

{enquiry.title}

- {pipelineStageLabel} | {customer?.name ?? 'Unknown customer'} + {pipelineStageLabel} | {customer?.name ?? 'ไม่ทราบชื่อลูกค้า'} {contact ? ` | ${contact.name}` : ''}

@@ -174,9 +164,9 @@ export function EnquiryDetail({ - Overview + ภาพรวม {followupTabLabel} - Activity + กิจกรรม {canViewRelatedQuotations ? ( {relatedTabLabel} ) : null} @@ -189,29 +179,29 @@ export function EnquiryDetail({ - + - + - - - + + + - - + +
- {isLeadWorkspace ? 'Lead Parties' : 'Project Parties'} + {isLeadWorkspace ? 'ผู้เกี่ยวข้องของลีด' : 'ผู้เกี่ยวข้องในโครงการ'}
{!projectPartiesData.items.length ? (
{isLeadWorkspace - ? 'No lead parties have been added yet.' - : 'No project parties have been added yet.'} + ? 'ยังไม่มีผู้เกี่ยวข้องของลีด' + : 'ยังไม่มีผู้เกี่ยวข้องในโครงการ'}
) : (
@@ -259,7 +249,7 @@ export function EnquiryDetail({
{item.customerName}
- Role: {item.roleLabel ?? item.roleCode} + บทบาท: {item.roleLabel ?? item.roleCode}
{item.remark ?
{item.remark}
: null}
@@ -278,13 +268,13 @@ export function EnquiryDetail({ />
- +
- +
- +
@@ -302,15 +292,13 @@ export function EnquiryDetail({ - {isLeadWorkspace ? 'Lead Activity' : 'Activity'} - - Audit events for this record and its follow-up history. - + {isLeadWorkspace ? 'กิจกรรมของลีด' : 'กิจกรรม'} + ประวัติการเปลี่ยนแปลงของรายการนี้และงานติดตามที่เกี่ยวข้อง {!data.activity.length ? (
- No activity recorded yet. + ยังไม่มีกิจกรรม
) : ( data.activity.map((item, index) => ( @@ -347,13 +335,13 @@ export function EnquiryDetail({ {relatedTabLabel} - Production quotations already linked to this record. + ใบเสนอราคาที่เชื่อมกับรายการนี้แล้ว {!relatedQuotations.length ? (
- No quotations have been linked to this record yet. + ยังไม่มีใบเสนอราคาที่เชื่อมกับรายการนี้
) : (
@@ -388,21 +376,21 @@ export function EnquiryDetail({
- Record Snapshot + ข้อมูลระบบ {snapshotDescription} - + - - - + + + - - + +
diff --git a/src/features/crm/enquiries/components/enquiry-followups-tab.tsx b/src/features/crm/enquiries/components/enquiry-followups-tab.tsx index e225cf0..e004688 100644 --- a/src/features/crm/enquiries/components/enquiry-followups-tab.tsx +++ b/src/features/crm/enquiries/components/enquiry-followups-tab.tsx @@ -38,22 +38,20 @@ export function EnquiryFollowupsTab({ const deleteMutation = useMutation({ ...deleteEnquiryFollowupMutation, onSuccess: () => { - toast.success('Follow-up deleted successfully'); + toast.success('ลบงานติดตามสำเร็จ'); setDeleteOpen(false); setSelectedId(null); }, onError: (error) => - toast.error(error instanceof Error ? error.message : 'Failed to delete follow-up') + toast.error(error instanceof Error ? error.message : 'ไม่สามารถลบงานติดตามได้') }); return (
- Follow-ups - - Track the latest contact moments, outcomes, and next steps for this enquiry. - + งานติดตาม + ติดตามการติดต่อ ผลลัพธ์ และขั้นตอนถัดไปของโอกาสขายนี้
{canCreate ? ( ) : null}
{!data.items.length ? (
- No follow-ups recorded yet. + ยังไม่มีงานติดตาม
) : ( data.items.map((item) => ( @@ -89,11 +87,11 @@ export function EnquiryFollowupsTab({
{item.outcome ?

{item.outcome}

: null} {item.nextAction ? ( -

Next action: {item.nextAction}

+

การดำเนินการถัดไป: {item.nextAction}

) : null} {item.nextFollowupDate ? (

- Next follow-up: {new Date(item.nextFollowupDate).toLocaleDateString()} + นัดติดตามครั้งถัดไป: {new Date(item.nextFollowupDate).toLocaleDateString()}

) : null} {item.notes ?

{item.notes}

: null} @@ -108,7 +106,7 @@ export function EnquiryFollowupsTab({ setOpen(true); }} > - Edit + แก้ไข
diff --git a/src/features/crm/enquiries/components/enquiry-form-sheet.tsx b/src/features/crm/enquiries/components/enquiry-form-sheet.tsx index 48cd907..2b6752e 100644 --- a/src/features/crm/enquiries/components/enquiry-form-sheet.tsx +++ b/src/features/crm/enquiries/components/enquiry-form-sheet.tsx @@ -90,21 +90,21 @@ export function EnquiryFormSheet({ const createMutation = useMutation({ ...createEnquiryMutation, onSuccess: () => { - toast.success(`${recordLabel} created successfully`); + toast.success(`สร้าง${recordLabel}สำเร็จ`); onOpenChange(false); }, onError: (error) => - toast.error(error instanceof Error ? error.message : `Failed to create ${recordLabel.toLowerCase()}`) + toast.error(error instanceof Error ? error.message : `ไม่สามารถสร้าง${recordLabel}ได้`) }); const updateMutation = useMutation({ ...updateEnquiryMutation, onSuccess: () => { - toast.success(`${recordLabel} updated successfully`); + toast.success(`อัปเดต${recordLabel}สำเร็จ`); onOpenChange(false); }, onError: (error) => - toast.error(error instanceof Error ? error.message : `Failed to update ${recordLabel.toLowerCase()}`) + toast.error(error instanceof Error ? error.message : `ไม่สามารถอัปเดต${recordLabel}ได้`) }); const contactsForCustomer = referenceData.contacts.filter( @@ -179,17 +179,17 @@ export function EnquiryFormSheet({ const isPending = createMutation.isPending || updateMutation.isPending; - const recordLabel = workspace === 'lead' ? 'Lead' : 'Enquiry'; + const recordLabel = workspace === 'lead' ? 'ลีด' : 'โอกาสขาย'; return ( - {isEdit ? `Edit ${recordLabel}` : `New ${recordLabel}`} + {isEdit ? `แก้ไข${recordLabel}` : `เพิ่ม${recordLabel}`} {workspace === 'lead' - ? 'Capture marketing lead details before assignment to sales.' - : 'Capture sales enquiry details before quotation work begins.'} + ? 'บันทึกข้อมูลลีดของฝ่ายการตลาดก่อนส่งต่อให้ฝ่ายขาย' + : 'บันทึกข้อมูลโอกาสขายของฝ่ายขายก่อนเริ่มทำใบเสนอราคา'} @@ -201,7 +201,7 @@ export function EnquiryFormSheet({ children={(field) => ( - Billing Customer * + ลูกค้าผู้รับใบเสนอราคา * { @@ -241,19 +241,19 @@ export function EnquiryFormSheet({ field.handleBlur(); }} > - - + + {contactsForCustomer.length ? ( contactsForCustomer.map((contact) => ( {contact.name} - {contact.isPrimary ? ' (Primary)' : ''} + {contact.isPrimary ? ' (ผู้ติดต่อหลัก)' : ''} )) ) : ( - No contact selected + ไม่เลือกผู้ติดต่อ )} @@ -265,28 +265,28 @@ export function EnquiryFormSheet({ ({ value: branch.id, label: branch.name @@ -294,7 +294,7 @@ export function EnquiryFormSheet({ /> ({ value: item.id, @@ -303,7 +303,7 @@ export function EnquiryFormSheet({ /> ({ value: item.id, @@ -312,7 +312,7 @@ export function EnquiryFormSheet({ /> ({ value: item.id, @@ -321,7 +321,7 @@ export function EnquiryFormSheet({ /> ({ value: item.id, label: item.label @@ -329,48 +329,48 @@ export function EnquiryFormSheet({ /> - + - +
@@ -387,11 +387,11 @@ export function EnquiryFormSheet({ @@ -411,7 +411,7 @@ export function EnquiryFormSheetTrigger({ return ( <> { - toast.success('Quotation deleted successfully'); + toast.success('ลบใบเสนอราคาสำเร็จ'); setDeleteOpen(false); }, onError: (error) => - toast.error(error instanceof Error ? error.message : 'Failed to delete quotation') + toast.error(error instanceof Error ? error.message : 'ไม่สามารถลบใบเสนอราคาได้') }); return ( @@ -65,15 +65,15 @@ export function QuotationCellAction({ - Actions + การดำเนินการ router.push(`/dashboard/crm/quotations/${data.id}`)}> - View + ดูรายละเอียด setEditOpen(true)} disabled={!canUpdate}> - Edit + แก้ไข setDeleteOpen(true)} disabled={!canDelete}> - Delete + ลบ diff --git a/src/features/crm/quotations/components/quotation-columns.tsx b/src/features/crm/quotations/components/quotation-columns.tsx index c3c02fb..4eee156 100644 --- a/src/features/crm/quotations/components/quotation-columns.tsx +++ b/src/features/crm/quotations/components/quotation-columns.tsx @@ -27,7 +27,7 @@ export function getQuotationColumns({ id: 'code', accessorKey: 'code', header: ({ column }: { column: Column }) => ( - + ), cell: ({ row }) => (
@@ -41,8 +41,8 @@ export function getQuotationColumns({
), meta: { - label: 'Quotation', - placeholder: 'Search quotations...', + label: 'ใบเสนอราคา', + placeholder: 'ค้นหาใบเสนอราคา...', variant: 'text' as const, icon: Icons.search }, @@ -69,7 +69,7 @@ export function getQuotationColumns({ id: 'quotationType', accessorKey: 'quotationType', header: ({ column }: { column: Column }) => ( - + ), cell: ({ row }) => ( @@ -77,7 +77,7 @@ export function getQuotationColumns({ ), meta: { - label: 'Type', + label: 'ประเภท', variant: 'multiSelect' as const, options: referenceData.quotationTypes.map((item) => ({ value: item.id, label: item.label })) }, @@ -87,13 +87,13 @@ export function getQuotationColumns({ id: 'branch', accessorFn: (row) => row.branchId ?? '', header: ({ column }: { column: Column }) => ( - + ), cell: ({ row }) => ( {row.original.branchId ? (branchMap.get(row.original.branchId)?.name ?? 'Unknown') : 'Unassigned'} ), meta: { - label: 'Branch', + label: 'สาขา', variant: 'multiSelect' as const, options: referenceData.branches.map((item) => ({ value: item.id, label: item.name })) }, @@ -103,11 +103,11 @@ export function getQuotationColumns({ id: 'customer', accessorFn: (row) => row.customerId, header: ({ column }: { column: Column }) => ( - + ), cell: ({ row }) => row.original.customerName, meta: { - label: 'Customer', + label: 'ลูกค้าผู้รับใบเสนอราคา', variant: 'multiSelect' as const, options: referenceData.customers.map((item) => ({ value: item.id, label: item.name })) }, @@ -117,11 +117,11 @@ export function getQuotationColumns({ id: 'enquiry', accessorFn: (row) => row.enquiryId ?? '', header: ({ column }: { column: Column }) => ( - + ), cell: ({ row }) => row.original.enquiryCode ?? '-', meta: { - label: 'Enquiry', + label: 'โอกาสขาย', variant: 'multiSelect' as const, options: referenceData.enquiries.map((item) => ({ value: item.id, label: `${item.code} - ${item.title}` })) }, @@ -131,15 +131,15 @@ export function getQuotationColumns({ id: 'isHotProject', accessorFn: (row) => String(row.isHotProject), header: ({ column }: { column: Column }) => ( - + ), cell: ({ row }) => (row.original.isHotProject ? Hot : -), meta: { - label: 'Hot Project', + label: 'โครงการด่วน', variant: 'multiSelect' as const, options: [ - { value: 'true', label: 'Hot' }, - { value: 'false', label: 'Normal' } + { value: 'true', label: 'ด่วน' }, + { value: 'false', label: 'ปกติ' } ] }, enableColumnFilter: true @@ -148,7 +148,7 @@ export function getQuotationColumns({ id: 'totalAmount', accessorKey: 'totalAmount', header: ({ column }: { column: Column }) => ( - + ), cell: ({ row }) => row.original.totalAmount.toLocaleString() }, diff --git a/src/features/crm/quotations/components/quotation-detail.tsx b/src/features/crm/quotations/components/quotation-detail.tsx index a66f746..9a5e79f 100644 --- a/src/features/crm/quotations/components/quotation-detail.tsx +++ b/src/features/crm/quotations/components/quotation-detail.tsx @@ -1324,7 +1324,7 @@ export function QuotationDetail({ Overview Items - Project Parties + ผู้เกี่ยวข้องในโครงการ Topics Follow-ups Attachments @@ -1343,7 +1343,7 @@ export function QuotationDetail({ @@ -1406,7 +1406,7 @@ export function QuotationDetail({
- Project Parties + ผู้เกี่ยวข้องในโครงการ
{!customersData.items.length ? (
@@ -1534,7 +1534,7 @@ export function QuotationDetail({
- Project Parties + ผู้เกี่ยวข้องในโครงการ Related companies for this quotation, each with a visible role. diff --git a/src/features/crm/quotations/components/quotation-document-preview.tsx b/src/features/crm/quotations/components/quotation-document-preview.tsx index 19f27a5..6726c9d 100644 --- a/src/features/crm/quotations/components/quotation-document-preview.tsx +++ b/src/features/crm/quotations/components/quotation-document-preview.tsx @@ -19,9 +19,9 @@ export function QuotationDocumentPreview({ quotationId }: { quotationId: string const { data } = useSuspenseQuery(quotationDocumentPreviewOptions(quotationId)); const { documentData, template } = data.preview; const topicGroups: Array<{ key: 'scope' | 'exclusion' | 'payment'; title: string }> = [ - { key: 'scope', title: 'Scope' }, - { key: 'exclusion', title: 'Exclusions' }, - { key: 'payment', title: 'Payment Terms' } + { key: 'scope', title: 'ขอบเขตงาน' }, + { key: 'exclusion', title: 'ข้อยกเว้น' }, + { key: 'payment', title: 'เงื่อนไขการชำระเงิน' } ]; return ( @@ -32,14 +32,14 @@ export function QuotationDocumentPreview({ quotationId }: { quotationId: string
{documentData.company.name} - Quotation Preview using {template.template.templateName} v{template.version.version} + ตัวอย่างใบเสนอราคา จากเทมเพลต {template.template.templateName} v{template.version.version}
{documentData.watermarkStatus ? ( {documentData.watermarkStatus} ) : ( - Approved Ready + พร้อมใช้งานหลังอนุมัติ )} {template.template.fileType}
@@ -49,41 +49,41 @@ export function QuotationDocumentPreview({ quotationId }: { quotationId: string
- Quotation Header - Commercial identity and issue information. + ข้อมูลหัวเอกสาร + ข้อมูลอ้างอิงและวันออกเอกสารสำหรับใบเสนอราคา - + - - + + - Customer Block - Bill-to and attention context for the document. + ข้อมูลลูกค้า + ข้อมูลลูกค้าผู้รับใบเสนอราคาและผู้ติดต่อของเอกสาร - - - - + + + +
- +
@@ -91,30 +91,30 @@ export function QuotationDocumentPreview({ quotationId }: { quotationId: string - Project Information - Scope summary and reference data for the customer. + ข้อมูลโครงการ + สรุปข้อมูลอ้างอิงของโครงการสำหรับลูกค้า - - - - + + + + - Items Table - Normalized line items prepared for template mapping. + ตารางรายการ + รายการที่จัดรูปแบบแล้วเพื่อเตรียมใช้กับ template mapping
-
Item
-
Description
-
Qty
-
Unit
-
Unit Price
-
Total
+
ลำดับ
+
รายละเอียด
+
จำนวน
+
หน่วย
+
ราคาต่อหน่วย
+
รวม
{documentData.items.map((item) => (
- Topics - Scope, exclusions, and payment terms from topic records. + หัวข้อเอกสาร + ขอบเขตงาน ข้อยกเว้น และเงื่อนไขการชำระเงินจากหัวข้อในระบบ {topicGroups.map((group) => { @@ -148,7 +148,7 @@ export function QuotationDocumentPreview({ quotationId }: { quotationId: string
{group.title}
{!entries.length ? ( -
No entries
+
ไม่มีข้อมูล
) : ( entries.map((entry) => (
@@ -170,19 +170,19 @@ export function QuotationDocumentPreview({ quotationId }: { quotationId: string
- Price Summary + สรุปมูลค่า
- Subtotal + ก่อนภาษี {documentData.quotation.subtotal.toLocaleString()}
- Tax + ภาษี {documentData.quotation.taxAmount.toLocaleString()}
- Total + รวมทั้งสิ้น {documentData.quotation.totalAmount.toLocaleString()}
@@ -190,24 +190,24 @@ export function QuotationDocumentPreview({ quotationId }: { quotationId: string - Approval Block - Prepared for watermark and approval snapshot flows. + ข้อมูลอนุมัติเอกสาร + ข้อมูลที่ใช้ประกอบ watermark และ approved snapshot - + - + {!documentData.approval.approvers.length ? ( -
No approval actions recorded.
+
ยังไม่มีประวัติการอนุมัติ
) : ( documentData.approval.approvers.map((item) => (
- Step {item.stepNumber} - {item.roleName} + ขั้น {item.stepNumber} - {item.roleName}
- {item.actorName || '-'} {item.actedAt ? `on ${new Date(item.actedAt).toLocaleString()}` : ''} + {item.actorName || '-'} {item.actedAt ? `เมื่อ ${new Date(item.actedAt).toLocaleString()}` : ''}
)) @@ -217,17 +217,17 @@ export function QuotationDocumentPreview({ quotationId }: { quotationId: string - Signature Placeholders + ตำแหน่งลายเซ็น - - - + + + - +
diff --git a/src/features/crm/quotations/components/quotation-form-sheet.tsx b/src/features/crm/quotations/components/quotation-form-sheet.tsx index 6f404a9..8ba0c0c 100644 --- a/src/features/crm/quotations/components/quotation-form-sheet.tsx +++ b/src/features/crm/quotations/components/quotation-form-sheet.tsx @@ -268,7 +268,7 @@ export function QuotationFormSheet({ - +