Files
honeyDueAPI/internal/dto/responses/contractor.go
T
Trey T 12de5a230a
Backend CI / Test (push) Has been cancelled
Backend CI / Contract Tests (push) Has been cancelled
Backend CI / Lint (push) Has been cancelled
Backend CI / Secret Scanning (push) Has been cancelled
Backend CI / Build (push) Has been cancelled
i18n: backend-localized lookups, suggestions, and static data (10 languages)
- suggestion_service: fix scorer (stringList unmarshal accepts scalar|array;
  anchor scoring on base universal score so bool matches no longer tie); add
  localizeReasons for human-readable, Accept-Language-localized match reasons
- lookup_i18n: localize lookup display names, home-profile options, document
  types/categories via internal/i18n
- static_data_handler: per-locale seeded-data response (display_name, home
  profile options, document types/categories) with per-locale cache + ETag
- settings_handler: invalidate per-locale seeded-data cache on lookup change
  instead of pre-warming a single non-localized blob
- cache_service: per-locale seeded-data keys + ETag
- DTOs: add DisplayName fields (task/residence/contractor)
- translations: add suggestion.reason.* and lookup.* keys across all 10 langs
- cmd/api: extract startup helpers + tests

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 20:54:54 -05:00

134 lines
4.6 KiB
Go

package responses
import (
"time"
"github.com/treytartt/honeydue-api/internal/models"
)
// ContractorSpecialtyResponse represents a contractor specialty
type ContractorSpecialtyResponse struct {
ID uint `json:"id"`
// Name is the stable English identifier (clients match on this).
Name string `json:"name"`
// DisplayName is the localized label for the request's Accept-Language.
DisplayName string `json:"display_name"`
Description string `json:"description"`
Icon string `json:"icon"`
DisplayOrder int `json:"display_order"`
}
// ContractorUserResponse represents a user in contractor context
type ContractorUserResponse struct {
ID uint `json:"id"`
Username string `json:"username"`
FirstName string `json:"first_name"`
LastName string `json:"last_name"`
}
// ContractorResponse represents a contractor in the API response
type ContractorResponse struct {
ID uint `json:"id"`
ResidenceID *uint `json:"residence_id"`
CreatedByID uint `json:"created_by_id"`
AddedBy uint `json:"added_by"` // Alias for created_by_id (KMM compatibility)
CreatedBy *ContractorUserResponse `json:"created_by,omitempty"`
Name string `json:"name"`
Company string `json:"company"`
Phone string `json:"phone"`
Email string `json:"email"`
Website string `json:"website"`
Notes string `json:"notes"`
StreetAddress string `json:"street_address"`
City string `json:"city"`
StateProvince string `json:"state_province"`
PostalCode string `json:"postal_code"`
Specialties []ContractorSpecialtyResponse `json:"specialties"`
Rating *float64 `json:"rating"`
IsFavorite bool `json:"is_favorite"`
IsActive bool `json:"is_active"`
TaskCount int `json:"task_count,omitempty"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
// Note: Pagination removed - list endpoints now return arrays directly
// ToggleFavoriteResponse represents the response after toggling favorite
type ToggleFavoriteResponse struct {
Message string `json:"message"`
IsFavorite bool `json:"is_favorite"`
}
// === Factory Functions ===
// NewContractorSpecialtyResponse creates a ContractorSpecialtyResponse from a model
func NewContractorSpecialtyResponse(s *models.ContractorSpecialty) ContractorSpecialtyResponse {
return ContractorSpecialtyResponse{
ID: s.ID,
Name: s.Name,
Description: s.Description,
Icon: s.Icon,
DisplayOrder: s.DisplayOrder,
}
}
// NewContractorUserResponse creates a ContractorUserResponse from a User model
func NewContractorUserResponse(u *models.User) *ContractorUserResponse {
if u == nil {
return nil
}
return &ContractorUserResponse{
ID: u.ID,
Username: u.Username,
FirstName: u.FirstName,
LastName: u.LastName,
}
}
// NewContractorResponse creates a ContractorResponse from a Contractor model
func NewContractorResponse(c *models.Contractor) ContractorResponse {
resp := ContractorResponse{
ID: c.ID,
ResidenceID: c.ResidenceID, // Already a pointer
CreatedByID: c.CreatedByID,
AddedBy: c.CreatedByID, // Alias for KMM compatibility
Name: c.Name,
Company: c.Company,
Phone: c.Phone,
Email: c.Email,
Website: c.Website,
Notes: c.Notes,
StreetAddress: c.StreetAddress,
City: c.City,
StateProvince: c.StateProvince,
PostalCode: c.PostalCode,
Rating: c.Rating,
IsFavorite: c.IsFavorite,
IsActive: c.IsActive,
TaskCount: len(c.Tasks),
CreatedAt: c.CreatedAt,
UpdatedAt: c.UpdatedAt,
}
if c.CreatedBy.ID != 0 {
resp.CreatedBy = NewContractorUserResponse(&c.CreatedBy)
}
resp.Specialties = make([]ContractorSpecialtyResponse, len(c.Specialties))
for i, s := range c.Specialties {
resp.Specialties[i] = NewContractorSpecialtyResponse(&s)
}
return resp
}
// NewContractorListResponse creates a list of contractor responses
func NewContractorListResponse(contractors []models.Contractor) []ContractorResponse {
results := make([]ContractorResponse, len(contractors))
for i, c := range contractors {
results[i] = NewContractorResponse(&c)
}
return results
}