Bỏ qua để đến nội dung

Thêm field vào collection có sẵn

  • Thư mụcadmin/packages/helpdesk/app/
    • data/helpdesk-schema.ts khai báo collection + field (setup wizard, new install)
    • composables/use-helpdesk-field-registry.ts COLLECTIONS allowlist + normalize + tweak
    • config/form-layouts.ts layout khai báo field theo tab/section (dùng cho registry-driven form)
  • Thư mụcbackend/extensions/helpdesk/src/endpoints/controllers/
    • meta.controller.ts ALLOWED allowlist cho GET /fields/:collection

Bước 1 — Khai báo field trong helpdesk-schema.ts (new install)

Phần tiêu đề “Bước 1 — Khai báo field trong helpdesk-schema.ts (new install)”

Field là một object phẳng { collection, field, type, meta, schema }, khai báo tách rời khỏi entry đăng ký collection. Ví dụ 2 field text trên hd_labels:

app/data/helpdesk-schema.ts:187-200
{
collection: "hd_labels",
field: "name",
type: "string",
meta: { interface: "input", width: "half", required: true },
schema: { is_nullable: false },
},
{
collection: "hd_labels",
field: "color",
type: "string",
meta: { interface: "input", width: "half" },
schema: {},
},

Field kiểu M2M (alias, không có cột thật):

app/data/helpdesk-schema.ts:248-252
{
collection: "hd_teams",
field: "agent_ids",
type: "alias",
meta: { interface: "list-m2m", special: ["m2m"], width: "full" },
},

Bước 2 — DB đã có sẵn: tạo field qua Data Studio / API

Phần tiêu đề “Bước 2 — DB đã có sẵn: tạo field qua Data Studio / API”

Nếu module đã chạy production, tạo field trực tiếp qua Data Studio (hoặc API POST /fields/:collection của ODP core) thay vì sửa schema — schema chỉ dùng để tài liệu hoá / cho lần cài mới sau.

Bước 3 — Field cần lộ ra qua field-metadata endpoint?

Phần tiêu đề “Bước 3 — Field cần lộ ra qua field-metadata endpoint?”

Backend chặn bằng allowlist cứng — collection không nằm trong danh sách này trả 404 Unknown collection, không phải lỗi im lặng:

backend/extensions/helpdesk/src/endpoints/controllers/meta.controller.ts
const ALLOWED = new Set<string>([
'hd_inboxes', 'hd_agents', 'hd_teams', 'hd_sla_policies', 'hd_working_hours',
'hd_automation_rules', 'hd_canned_responses', 'hd_templates', 'hd_saved_views',
'hd_connections', 'hd_settings', 'hd_conversations', 'hd_messages',
'c_contacts',
]);
export function fields(ctx: AppContext) {
return async (req: any, res: any) => {
const { collection } = req.params;
if (!ALLOWED.has(collection)) {
return res.status(404).send({ errors: [{ message: 'Unknown collection' }] });
}
// ...
};
}

Nếu field thuộc 1 trong 13 collection đã allowlist thì không cần đụng gì ở đây. Collection mới hoàn toàn (không nằm trong 13 cái này) → xem Thêm sub-collection hd_* mới.

Bước 4 — Client field-registry: 11 collection, không phải 13

Phần tiêu đề “Bước 4 — Client field-registry: 11 collection, không phải 13”

Client tự giới hạn thêm xuống còn 11 collection nó thực sự build form:

app/composables/use-helpdesk-field-registry.ts:3-15
const COLLECTIONS = [
"hd_inboxes", "hd_agents", "hd_teams", "hd_sla_policies", "hd_working_hours",
"hd_automation_rules", "hd_canned_responses", "hd_templates", "hd_saved_views",
"hd_connections", "hd_conversations",
] as const

hd_settings, hd_messages, c_contacts được backend allow (dùng cho việc khác — m2o hiển thị agent contact, singleton settings) nhưng registry client không build form từ chúng.

Registry chuẩn hoá interface (v.d. select-dropdownselect) và có bảng tweak riêng cho từng field:

app/composables/use-helpdesk-field-registry.ts:18-48
const INTERFACE_NORMALIZE: Record<string, string> = {
"select-dropdown": "select",
}
const FIELD_TWEAKS: Partial<Record<CollectionKey, Record<string, { interface?: string, options?: Record<string, any>, readonly?: boolean }>>> = {
hd_inboxes: {
signature: { interface: "input-rich-text-html" },
},
hd_agents: {
// m2o to c_contacts — show contact display name
contact_id: { options: { template: "{{display_name}}" } },
},
hd_working_hours: {
timezone: { interface: "select" },
schedule: { interface: "input-code", options: { language: "json" } },
holidays: { interface: "input-code", options: { language: "json" } },
},
hd_sla_policies: {
applies_to: { interface: "input-code", options: { language: "json" } },
},
hd_automation_rules: {
conditions: { interface: "input-code", options: { language: "json" } },
actions: { interface: "input-code", options: { language: "json" } },
},
hd_saved_views: {
filters: { interface: "input-code", options: { language: "json" } },
},
}

Field JSON mới (kiểu hd_sla_policies.applies_to) hầu như chắc chắn cần thêm entry interface: "input-code", options: { language: "json" } ở đây, nếu không registry sẽ render nó bằng input mặc định theo meta.interface thô từ backend.

Có 2 khả năng tuỳ collection:

  1. Form dùng registry (layout khai báo trong app/config/form-layouts.ts) — chỉ cần thêm tên field vào mảng fields của section liên quan:

    app/config/form-layouts.ts:5-20 — INBOX_FORM_LAYOUT
    export const INBOX_FORM_LAYOUT: FormLayout = {
    tabs: [
    {
    id: "basic",
    labelKey: "helpdesk.form.tabs.basic",
    sections: [
    { id: "identity", grid: 2, fields: ["name", "channel_type"] },
    { id: "status", grid: 1, fields: ["is_active"] },
    ],
    },
    // ...
    ],
    }

    Thêm field mới vào đúng fields: [...] — không cần sửa gì khác trong file này.

  2. Form viết tay (inline) — đa số form helpdesk (composer, SLA policy, automation rule, canned response/template, label, inbox detail) cố ý không dùng FormRoot/registry vì UX bespoke (autocomplete, variable picker, condition builder…). Field mới phải wire trực tiếp vào component Vue của form đó (thêm input + bind vào payload gửi PATCH/POST).