Files
resolutionflow/backend/app/schemas/category.py
chihlasm fafdaa50a5 Add tree organization system with categories, tags, and folders
Features:
- Categories: Global and team-specific tree categorization (admin-managed)
- Tags: Flexible tree tagging with autocomplete (author + admin)
- User folders: Personal tree collections with subfolder support
  - Hierarchical structure (max 3 levels deep)
  - Right-click context menu for folder management
  - Cascade delete for subfolders
- Filter trees by category, tags, and folder in library view

Backend:
- New models: Category, Tag, UserFolder with relationships
- New API endpoints for categories, tags, and folders
- Tree organization migrations (005, 006)

Frontend:
- FolderSidebar with hierarchical folder tree
- FolderEditModal for create/edit with color picker
- AddToFolderMenu for quick tree organization
- TagInput with autocomplete and TagBadges display
- Updated TreeMetadataForm and TreeLibraryPage

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-02 01:31:13 -05:00

59 lines
1.4 KiB
Python

from datetime import datetime
from typing import Optional
from uuid import UUID
from pydantic import BaseModel, Field
import re
def slugify(name: str) -> str:
"""Convert a name to a URL-safe slug."""
# Remove non-alphanumeric chars except spaces, convert to lowercase
slug = re.sub(r'[^a-zA-Z0-9 ]', '', name.lower())
# Replace spaces with hyphens
slug = re.sub(r' +', '-', slug.strip())
return slug
class CategoryBase(BaseModel):
name: str = Field(..., min_length=1, max_length=100)
description: Optional[str] = None
class CategoryCreate(CategoryBase):
team_id: Optional[UUID] = Field(None, description="Team ID for team-specific category. NULL for global.")
class CategoryUpdate(BaseModel):
name: Optional[str] = Field(None, min_length=1, max_length=100)
description: Optional[str] = None
display_order: Optional[int] = None
is_active: Optional[bool] = None
class CategoryResponse(CategoryBase):
id: UUID
slug: str
team_id: Optional[UUID] = None
display_order: int
is_active: bool
created_at: datetime
updated_at: datetime
tree_count: int = 0 # Computed field
class Config:
from_attributes = True
class CategoryListResponse(BaseModel):
id: UUID
name: str
slug: str
description: Optional[str] = None
team_id: Optional[UUID] = None
display_order: int
is_active: bool
tree_count: int = 0
class Config:
from_attributes = True