package responses import ( "time" "github.com/shopspring/decimal" "github.com/treytartt/honeydue-api/internal/models" ) // ResidenceTypeResponse represents a residence type in the API response type ResidenceTypeResponse struct { ID uint `json:"id"` Name string `json:"name"` } // ResidenceUserResponse represents a user with access to a residence type ResidenceUserResponse struct { ID uint `json:"id"` Username string `json:"username"` Email string `json:"email"` FirstName string `json:"first_name"` LastName string `json:"last_name"` } // ResidenceResponse represents a residence in the API response type ResidenceResponse struct { ID uint `json:"id"` OwnerID uint `json:"owner_id"` Owner *ResidenceUserResponse `json:"owner,omitempty"` Users []ResidenceUserResponse `json:"users,omitempty"` Name string `json:"name"` PropertyTypeID *uint `json:"property_type_id"` PropertyType *ResidenceTypeResponse `json:"property_type,omitempty"` StreetAddress string `json:"street_address"` ApartmentUnit string `json:"apartment_unit"` City string `json:"city"` StateProvince string `json:"state_province"` PostalCode string `json:"postal_code"` Country string `json:"country"` Bedrooms *int `json:"bedrooms"` Bathrooms *decimal.Decimal `json:"bathrooms"` SquareFootage *int `json:"square_footage"` LotSize *decimal.Decimal `json:"lot_size"` YearBuilt *int `json:"year_built"` Description string `json:"description"` PurchaseDate *time.Time `json:"purchase_date"` PurchasePrice *decimal.Decimal `json:"purchase_price"` // Home Profile HeatingType *string `json:"heating_type"` CoolingType *string `json:"cooling_type"` WaterHeaterType *string `json:"water_heater_type"` RoofType *string `json:"roof_type"` HasPool bool `json:"has_pool"` HasSprinklerSystem bool `json:"has_sprinkler_system"` HasSeptic bool `json:"has_septic"` HasFireplace bool `json:"has_fireplace"` HasGarage bool `json:"has_garage"` HasBasement bool `json:"has_basement"` HasAttic bool `json:"has_attic"` ExteriorType *string `json:"exterior_type"` FlooringPrimary *string `json:"flooring_primary"` LandscapingType *string `json:"landscaping_type"` IsPrimary bool `json:"is_primary"` IsActive bool `json:"is_active"` OverdueCount int `json:"overdue_count"` CompletionSummary *CompletionSummary `json:"completion_summary,omitempty"` CreatedAt time.Time `json:"created_at"` UpdatedAt time.Time `json:"updated_at"` } // TotalSummary represents summary statistics for all residences type TotalSummary struct { TotalResidences int `json:"total_residences"` TotalTasks int `json:"total_tasks"` TotalPending int `json:"total_pending"` TotalOverdue int `json:"total_overdue"` TasksDueNextWeek int `json:"tasks_due_next_week"` TasksDueNextMonth int `json:"tasks_due_next_month"` } // MyResidencesResponse represents the response for my-residences endpoint // NOTE: Summary statistics are calculated client-side from kanban data type MyResidencesResponse struct { Residences []ResidenceResponse `json:"residences"` } // ResidenceWithSummaryResponse wraps ResidenceResponse with TotalSummary for CRUD operations type ResidenceWithSummaryResponse struct { Data ResidenceResponse `json:"data"` Summary TotalSummary `json:"summary"` } // ResidenceDeleteWithSummaryResponse for delete operations type ResidenceDeleteWithSummaryResponse struct { Data string `json:"data"` Summary TotalSummary `json:"summary"` } // ShareCodeResponse represents a share code in the API response type ShareCodeResponse struct { ID uint `json:"id"` Code string `json:"code"` ResidenceID uint `json:"residence_id"` CreatedByID uint `json:"created_by_id"` IsActive bool `json:"is_active"` ExpiresAt *time.Time `json:"expires_at"` CreatedAt time.Time `json:"created_at"` } // JoinResidenceResponse represents the response after joining a residence type JoinResidenceResponse struct { Message string `json:"message"` Residence ResidenceResponse `json:"residence"` Summary TotalSummary `json:"summary"` } // GenerateShareCodeResponse represents the response after generating a share code type GenerateShareCodeResponse struct { Message string `json:"message"` ShareCode ShareCodeResponse `json:"share_code"` } // SharePackageResponse represents the response for generating a share package // This contains the share code plus metadata for the .honeydue file type SharePackageResponse struct { ShareCode string `json:"share_code"` ResidenceName string `json:"residence_name"` SharedBy string `json:"shared_by"` ExpiresAt *time.Time `json:"expires_at,omitempty"` } // ColumnCompletionCount represents completions from a specific kanban column type ColumnCompletionCount struct { Column string `json:"column"` Color string `json:"color"` Count int `json:"count"` } // MonthlyCompletionSummary represents completions for a single month type MonthlyCompletionSummary struct { Month string `json:"month"` // "2025-04" format Completions []ColumnCompletionCount `json:"completions"` Total int `json:"total"` Overflow int `json:"overflow"` // completions beyond the display cap } // CompletionSummary represents task completion data for the honeycomb grid type CompletionSummary struct { TotalAllTime int `json:"total_all_time"` TotalLast12Months int `json:"total_last_12_months"` Months []MonthlyCompletionSummary `json:"months"` } // === Factory Functions === // NewResidenceUserResponse creates a ResidenceUserResponse from a User model func NewResidenceUserResponse(user *models.User) *ResidenceUserResponse { if user == nil { return nil } return &ResidenceUserResponse{ ID: user.ID, Username: user.Username, Email: user.Email, FirstName: user.FirstName, LastName: user.LastName, } } // NewResidenceTypeResponse creates a ResidenceTypeResponse from a ResidenceType model func NewResidenceTypeResponse(rt *models.ResidenceType) *ResidenceTypeResponse { if rt == nil { return nil } return &ResidenceTypeResponse{ ID: rt.ID, Name: rt.Name, } } // NewResidenceResponse creates a ResidenceResponse from a Residence model func NewResidenceResponse(residence *models.Residence) ResidenceResponse { resp := ResidenceResponse{ ID: residence.ID, OwnerID: residence.OwnerID, Name: residence.Name, PropertyTypeID: residence.PropertyTypeID, StreetAddress: residence.StreetAddress, ApartmentUnit: residence.ApartmentUnit, City: residence.City, StateProvince: residence.StateProvince, PostalCode: residence.PostalCode, Country: residence.Country, Bedrooms: residence.Bedrooms, Bathrooms: residence.Bathrooms, SquareFootage: residence.SquareFootage, LotSize: residence.LotSize, YearBuilt: residence.YearBuilt, Description: residence.Description, PurchaseDate: residence.PurchaseDate, PurchasePrice: residence.PurchasePrice, HeatingType: residence.HeatingType, CoolingType: residence.CoolingType, WaterHeaterType: residence.WaterHeaterType, RoofType: residence.RoofType, HasPool: residence.HasPool, HasSprinklerSystem: residence.HasSprinklerSystem, HasSeptic: residence.HasSeptic, HasFireplace: residence.HasFireplace, HasGarage: residence.HasGarage, HasBasement: residence.HasBasement, HasAttic: residence.HasAttic, ExteriorType: residence.ExteriorType, FlooringPrimary: residence.FlooringPrimary, LandscapingType: residence.LandscapingType, IsPrimary: residence.IsPrimary, IsActive: residence.IsActive, CreatedAt: residence.CreatedAt, UpdatedAt: residence.UpdatedAt, } // Include owner if loaded if residence.Owner.ID != 0 { resp.Owner = NewResidenceUserResponse(&residence.Owner) } // Include property type if loaded if residence.PropertyType != nil { resp.PropertyType = NewResidenceTypeResponse(residence.PropertyType) } // Include shared users if loaded if len(residence.Users) > 0 { resp.Users = make([]ResidenceUserResponse, len(residence.Users)) for i, user := range residence.Users { resp.Users[i] = *NewResidenceUserResponse(&user) } } else { resp.Users = []ResidenceUserResponse{} } return resp } // NewResidenceListResponse creates a list of residence responses func NewResidenceListResponse(residences []models.Residence) []ResidenceResponse { results := make([]ResidenceResponse, len(residences)) for i, r := range residences { results[i] = NewResidenceResponse(&r) } return results } // NewShareCodeResponse creates a ShareCodeResponse from a ResidenceShareCode model func NewShareCodeResponse(sc *models.ResidenceShareCode) ShareCodeResponse { return ShareCodeResponse{ ID: sc.ID, Code: sc.Code, ResidenceID: sc.ResidenceID, CreatedByID: sc.CreatedByID, IsActive: sc.IsActive, ExpiresAt: sc.ExpiresAt, CreatedAt: sc.CreatedAt, } }