feat(5.20): Custom Fields — plugin-defined fields in Contact UI
- Add CustomFieldDefinition to PluginManifest (text/number/date/select/multiselect/boolean)
- Add GET/PATCH /api/v1/contacts/{id}/custom-fields routes
- Merge plugin definitions with stored values in contacts.custom JSONB
- Add CustomFieldRenderer component (read + edit modes)
- Integrate into ContactDetail (read-only) and ContactEditModal (editable)
- Add custom_fields to pluginStore and active manifests API
- Add useCustomFields/useUpdateCustomFields React Query hooks
- Tests: test_custom_fields.py (9 tests) + CustomFieldRenderer.test.tsx (5 tests)
- i18n keys already present (contacts.customFields)
This commit is contained in:
@@ -138,6 +138,21 @@ class FrontendDashboardWidget(BaseModel):
|
||||
permission: str = Field(default="", description="Optional permission required")
|
||||
|
||||
|
||||
class CustomFieldDefinition(BaseModel):
|
||||
"""A custom field definition contributed by a plugin manifest."""
|
||||
|
||||
name: str = Field(..., min_length=1, max_length=80, description="Unique field name (snake_case)")
|
||||
label: str = Field(default="", description="Human-readable label (fallback if i18n key missing)")
|
||||
label_key: str = Field(default="", description="i18n key for the field label")
|
||||
field_type: str = Field(
|
||||
..., pattern="^(text|number|date|select|multiselect|boolean)$", description="Field type"
|
||||
)
|
||||
options: list[str] = Field(default_factory=list, description="Options for select/multiselect types")
|
||||
default_value: Any = Field(default=None, description="Default value for the field")
|
||||
required: bool = Field(default=False, description="Whether the field is required")
|
||||
entity: str = Field(default="contact", description="Entity type this field applies to (contact/file/etc)")
|
||||
|
||||
|
||||
class MiniAppContribution(BaseModel):
|
||||
"""A MiniApp contributed by a plugin manifest."""
|
||||
|
||||
@@ -214,6 +229,9 @@ class PluginManifest(BaseModel):
|
||||
miniapps: list[MiniAppContribution] = Field(
|
||||
default_factory=list, description="MiniApps contributed by this plugin"
|
||||
)
|
||||
custom_fields: list[CustomFieldDefinition] = Field(
|
||||
default_factory=list, description="Custom field definitions contributed by this plugin"
|
||||
)
|
||||
|
||||
|
||||
@field_validator("name")
|
||||
|
||||
Reference in New Issue
Block a user