Warnings fixed: - 3.1: _compute_display_name stale-data bug on all-names-clear - 3.3: Location getValue unsafe type cast replaced with typed helper - 3.5: Explicit updated_at timestamp refresh in locations router - 3.6: Drop deprecated relationship column (migration 021, model, schema, TS type) Suggestions fixed: - 4.1: CategoryAutocomplete keyboard navigation (ArrowUp/Down, Enter, Escape) - 4.2: Mobile detail panel backdrop click-to-close on both pages - 4.3: PersonCreate whitespace bypass in require_some_name validator - 4.5/4.6: Extract SortIcon, DataRow, SectionHeader from EntityTable render body - 4.8: PersonForm sends null instead of empty string for birthday - 4.10: Remove unnecessary executeDelete wrapper in EntityDetailPanel Also includes previously completed fixes from prior session: - 2.1: Remove Z suffix from naive timestamp in formatUpdatedAt - 3.2: Drag-then-click conflict prevention in SortableCategoryChip - 3.4: localStorage JSON shape validation in useCategoryOrder - 4.4: Category chip styling consistency (both pages use inline hsl styles) - 4.9: restrictToHorizontalAxis modifier on CategoryFilterBar drag Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
32 lines
1.7 KiB
Python
32 lines
1.7 KiB
Python
from sqlalchemy import String, Text, Date, Boolean, func, text
|
|
from sqlalchemy.orm import Mapped, mapped_column, relationship
|
|
from datetime import datetime, date
|
|
from typing import Optional, List
|
|
from app.database import Base
|
|
|
|
|
|
class Person(Base):
|
|
__tablename__ = "people"
|
|
|
|
id: Mapped[int] = mapped_column(primary_key=True, index=True)
|
|
name: Mapped[str] = mapped_column(String(255), nullable=False)
|
|
email: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
|
phone: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
|
|
address: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
|
birthday: Mapped[Optional[date]] = mapped_column(Date, nullable=True)
|
|
notes: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
|
|
# Extended fields
|
|
first_name: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
|
last_name: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
|
nickname: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
|
is_favourite: Mapped[bool] = mapped_column(Boolean, nullable=False, default=False, server_default=text('false'))
|
|
company: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
|
job_title: Mapped[Optional[str]] = mapped_column(String(255), nullable=True)
|
|
mobile: Mapped[Optional[str]] = mapped_column(String(50), nullable=True)
|
|
category: Mapped[Optional[str]] = mapped_column(String(100), nullable=True)
|
|
created_at: Mapped[datetime] = mapped_column(default=func.now())
|
|
updated_at: Mapped[datetime] = mapped_column(default=func.now(), onupdate=func.now())
|
|
|
|
# Relationships
|
|
assigned_tasks: Mapped[List["ProjectTask"]] = relationship(back_populates="person")
|