from pydantic import BaseModel, EmailStr, Field from typing import Optional, List from datetime import datetime from uuid import UUID # ============================================ # User Schemas # ============================================ class UserBase(BaseModel): email: EmailStr class UserCreate(UserBase): pass class User(UserBase): id: UUID created_at: datetime class Config: from_attributes = True # ============================================ # Event Schemas # ============================================ class EventBase(BaseModel): name: str date: Optional[datetime] = None location: Optional[str] = None partner1_name: Optional[str] = None partner2_name: Optional[str] = None venue: Optional[str] = None event_time: Optional[str] = None guest_link: Optional[str] = None class EventCreate(EventBase): pass class EventUpdate(BaseModel): name: Optional[str] = None date: Optional[datetime] = None location: Optional[str] = None partner1_name: Optional[str] = None partner2_name: Optional[str] = None venue: Optional[str] = None event_time: Optional[str] = None guest_link: Optional[str] = None class Event(EventBase): id: UUID created_at: datetime updated_at: Optional[datetime] = None class Config: from_attributes = True class EventWithMembers(Event): members: List["EventMember"] = [] # ============================================ # Event Member Schemas # ============================================ class EventMemberBase(BaseModel): role: str = "admin" # admin, editor, viewer display_name: Optional[str] = None class EventMemberCreate(BaseModel): user_email: str = Field(..., description="Email address of the user to invite") role: str = "admin" display_name: Optional[str] = None class EventMember(EventMemberBase): id: UUID event_id: UUID user_id: UUID created_at: datetime user: Optional[User] = None class Config: from_attributes = True # ============================================ # Guest Schemas # ============================================ class GuestBase(BaseModel): first_name: str last_name: str email: Optional[str] = None phone_number: Optional[str] = None rsvp_status: str = "invited" # invited, confirmed, declined meal_preference: Optional[str] = None has_plus_one: bool = False plus_one_name: Optional[str] = None table_number: Optional[str] = None side: Optional[str] = None # e.g., "groom side", "bride side" notes: Optional[str] = None class GuestCreate(GuestBase): pass class GuestUpdate(BaseModel): first_name: Optional[str] = None last_name: Optional[str] = None email: Optional[str] = None phone_number: Optional[str] = None rsvp_status: Optional[str] = None meal_preference: Optional[str] = None has_plus_one: Optional[bool] = None plus_one_name: Optional[str] = None table_number: Optional[str] = None side: Optional[str] = None notes: Optional[str] = None class Guest(GuestBase): id: UUID event_id: UUID added_by_user_id: UUID owner_email: Optional[str] = None source: str = "manual" created_at: datetime updated_at: Optional[datetime] = None class Config: from_attributes = True # ============================================ # Bulk Import Schemas # ============================================ class GuestImportItem(BaseModel): first_name: str last_name: str email: Optional[str] = None phone_number: Optional[str] = None side: Optional[str] = None notes: Optional[str] = None class GuestBulkImport(BaseModel): guests: List[GuestImportItem] # ============================================ # Filter/Search Schemas # ============================================ class GuestFilter(BaseModel): search: Optional[str] = None side: Optional[str] = None status: Optional[str] = None added_by: Optional[str] = None # "me" for current user # ============================================ # WhatsApp Schemas # ============================================ class WhatsAppMessage(BaseModel): message: str phone: Optional[str] = None # Optional: override guest's phone class WhatsAppStatus(BaseModel): message_id: str status: str timestamp: datetime class WhatsAppWeddingInviteRequest(BaseModel): """Request to send wedding invitation template to guest(s)""" guest_ids: Optional[List[str]] = None # For bulk sending phone_override: Optional[str] = None # Optional: override phone number class Config: from_attributes = True class WhatsAppSendResult(BaseModel): """Result of sending WhatsApp message to a guest""" guest_id: str guest_name: Optional[str] = None phone: str status: str # "sent", "failed" message_id: Optional[str] = None error: Optional[str] = None class Config: from_attributes = True class WhatsAppBulkResult(BaseModel): """Result of bulk WhatsApp sending""" total: int succeeded: int failed: int results: List[WhatsAppSendResult] class Config: from_attributes = True # ============================================ # Google Contacts Import Schema # ============================================ class GoogleContactsImport(BaseModel): access_token: str owner: Optional[str] = "Google Import" # ============================================ # Public Guest Self-Service Schema # ============================================ class GuestPublicUpdate(BaseModel): """Schema for public guest self-service updates (phone-based lookup)""" first_name: Optional[str] = None last_name: Optional[str] = None rsvp_status: Optional[str] = None meal_preference: Optional[str] = None has_plus_one: Optional[bool] = None plus_one_name: Optional[str] = None