Custom Fields
Custom fields allow extending task (or other entity) data with user-defined attributes beyond the built-in title, description, status, and priority.
Model
CustomField
├── entityType (e.g. "task", "project")
├── name, type, config (JSON)
└── values (CustomFieldValue[])
CustomFieldValue
├── entityId (references the entity instance)
├── fieldId (references the CustomField definition)
└── value (JSON)Field Types
| Type | Config | Value Format |
|---|---|---|
text | — | string |
number | { min?, max? } | number |
select | { options: string[] } | string (one of options) |
date | — | ISO date string |
checkbox | — | boolean |
API Endpoints
Field Definitions
| Method | Path | Description |
|---|---|---|
GET | /api/custom-fields/:entityType | List fields for entity type |
POST | /api/custom-fields | Create field definition |
PATCH | /api/custom-fields/:id | Update field definition |
DELETE | /api/custom-fields/:id | Delete field definition |
Field Values
| Method | Path | Description |
|---|---|---|
GET | /api/custom-field-values/:entityId | Get values for entity |
POST | /api/custom-field-values/:entityId | Set values (upsert) |
Frontend
Custom Fields Store
typescript
const store = useCustomFieldsStore()
await store.fetchFields(entityType)
await store.createField({ entityType, name, type, config })
await store.updateField(id, { name, type, config })
await store.deleteField(id)
await store.fetchValues(entityId)
await store.setValues(entityId, [{ fieldId, value }])CustomFieldEditor Component
The CustomFieldEditor.vue component renders a dynamic form based on field definitions:
text→ text inputnumber→ number input with min/maxselect→ dropdown from configured optionsdate→ date pickercheckbox→ toggle switch
Key Implementation Details
- Field names must be unique per entity type (enforced by
@@unique([entityType, name])) - Values are stored as JSON, allowing flexible types
- Deleting a field cascades to delete all its values
- Custom fields are displayed on the task detail sidebar