Phase 2: Tags UI, Custom Fields UI, Notifications Bell

- Tags UI: TagsPage (CRUD, color picker), TagBadge, TagSelector (multi-select, inline creation)
- Custom Fields Backend: model, schema, service, routes, migration 0041
- Custom Fields Frontend: CustomFieldsPage (definitions CRUD), CustomFieldRenderer (dynamic field rendering)
- Custom Fields: _collect_custom_field_definitions() extended to merge DB definitions with plugin definitions
- Notifications Bell: NotificationBell (30s polling, unread badge), NotificationDropdown, NotificationItem
- NotificationBell integrated into TopBar
- Routes: /tags, /settings/custom-fields registered
- Settings nav: Custom Fields entry added
- Menu items: Tags added to automation plugin manifest
This commit is contained in:
Agent Zero
2026-07-26 03:02:25 +02:00
parent 444c7fdb88
commit a7e3890634
22 changed files with 2684 additions and 8 deletions
+64
View File
@@ -0,0 +1,64 @@
"""Pydantic schemas for CustomFieldDefinition CRUD."""
from __future__ import annotations
import uuid
from datetime import datetime
from typing import Any
from pydantic import BaseModel, ConfigDict, Field
class CustomFieldDefinitionCreate(BaseModel):
"""Schema for creating a new custom field definition."""
entity: str = Field(..., description="Entity type (e.g. 'contact', 'company')")
name: str = Field(..., min_length=1, max_length=100, description="Unique field name (snake_case)")
label: str = Field(..., min_length=1, max_length=200, description="Human-readable label")
field_type: str = Field(
...,
pattern=r"^(text|number|date|select|multiselect|boolean)$",
description="Field type",
)
options: list[str] | None = Field(default=None, description="Options for select/multiselect types")
default_value: Any = Field(default=None, description="Default value")
required: bool = Field(default=False, description="Whether the field is required")
is_active: bool = Field(default=True, description="Whether the field is active")
sort_order: int = Field(default=0, description="Sort order")
class CustomFieldDefinitionUpdate(BaseModel):
"""Schema for updating an existing custom field definition."""
label: str | None = Field(default=None, max_length=200)
field_type: str | None = Field(
default=None,
pattern=r"^(text|number|date|select|multiselect|boolean)$",
)
options: list[str] | None = Field(default=None)
default_value: Any = Field(default=None)
required: bool | None = Field(default=None)
is_active: bool | None = Field(default=None)
sort_order: int | None = Field(default=None)
class CustomFieldDefinitionResponse(BaseModel):
"""Schema for returning a custom field definition."""
model_config = ConfigDict(from_attributes=True)
id: uuid.UUID
tenant_id: uuid.UUID
entity: str
name: str
label: str
field_type: str
options: list[str] | None = None
default_value: Any = None
required: bool = False
is_active: bool = True
sort_order: int = 0
created_by: uuid.UUID | None = None
updated_by: uuid.UUID | None = None
created_at: datetime
updated_at: datetime