Files
honeyDueAPI/internal/dto/responses/residence.go
Trey t 156741f1ad Remove summary from list API responses
Summary statistics are now calculated client-side from kanban data.
This removes the summary field from MyResidencesResponse and
KanbanBoardResponse. Mutation endpoints still return summary via
WithSummaryResponse<T> for immediate cache updates.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-16 17:05:52 -06:00

215 lines
7.3 KiB
Go

package responses
import (
"time"
"github.com/shopspring/decimal"
"github.com/treytartt/casera-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"`
IsPrimary bool `json:"is_primary"`
IsActive bool `json:"is_active"`
OverdueCount int `json:"overdue_count"`
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 .casera 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"`
}
// === 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,
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,
}
}