Initial commit: MyCrib API in Go
Complete rewrite of Django REST API to Go with: - Gin web framework for HTTP routing - GORM for database operations - GoAdmin for admin panel - Gorush integration for push notifications - Redis for caching and job queues Features implemented: - User authentication (login, register, logout, password reset) - Residence management (CRUD, sharing, share codes) - Task management (CRUD, kanban board, completions) - Contractor management (CRUD, specialties) - Document management (CRUD, warranties) - Notifications (preferences, push notifications) - Subscription management (tiers, limits) Infrastructure: - Docker Compose for local development - Database migrations and seed data - Admin panel for data management 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
51
internal/dto/requests/auth.go
Normal file
51
internal/dto/requests/auth.go
Normal file
@@ -0,0 +1,51 @@
|
||||
package requests
|
||||
|
||||
// LoginRequest represents the login request body
|
||||
type LoginRequest struct {
|
||||
Username string `json:"username" binding:"required_without=Email"`
|
||||
Email string `json:"email" binding:"required_without=Username,omitempty,email"`
|
||||
Password string `json:"password" binding:"required,min=1"`
|
||||
}
|
||||
|
||||
// RegisterRequest represents the registration request body
|
||||
type RegisterRequest struct {
|
||||
Username string `json:"username" binding:"required,min=3,max=150"`
|
||||
Email string `json:"email" binding:"required,email,max=254"`
|
||||
Password string `json:"password" binding:"required,min=8"`
|
||||
FirstName string `json:"first_name" binding:"max=150"`
|
||||
LastName string `json:"last_name" binding:"max=150"`
|
||||
}
|
||||
|
||||
// VerifyEmailRequest represents the email verification request body
|
||||
type VerifyEmailRequest struct {
|
||||
Code string `json:"code" binding:"required,len=6"`
|
||||
}
|
||||
|
||||
// ForgotPasswordRequest represents the forgot password request body
|
||||
type ForgotPasswordRequest struct {
|
||||
Email string `json:"email" binding:"required,email"`
|
||||
}
|
||||
|
||||
// VerifyResetCodeRequest represents the verify reset code request body
|
||||
type VerifyResetCodeRequest struct {
|
||||
Email string `json:"email" binding:"required,email"`
|
||||
Code string `json:"code" binding:"required,len=6"`
|
||||
}
|
||||
|
||||
// ResetPasswordRequest represents the reset password request body
|
||||
type ResetPasswordRequest struct {
|
||||
ResetToken string `json:"reset_token" binding:"required"`
|
||||
NewPassword string `json:"new_password" binding:"required,min=8"`
|
||||
}
|
||||
|
||||
// UpdateProfileRequest represents the profile update request body
|
||||
type UpdateProfileRequest struct {
|
||||
Email *string `json:"email" binding:"omitempty,email,max=254"`
|
||||
FirstName *string `json:"first_name" binding:"omitempty,max=150"`
|
||||
LastName *string `json:"last_name" binding:"omitempty,max=150"`
|
||||
}
|
||||
|
||||
// ResendVerificationRequest represents the resend verification email request
|
||||
type ResendVerificationRequest struct {
|
||||
// No body needed - uses authenticated user's email
|
||||
}
|
||||
36
internal/dto/requests/contractor.go
Normal file
36
internal/dto/requests/contractor.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package requests
|
||||
|
||||
// CreateContractorRequest represents the request to create a contractor
|
||||
type CreateContractorRequest struct {
|
||||
ResidenceID uint `json:"residence_id" binding:"required"`
|
||||
Name string `json:"name" binding:"required,min=1,max=200"`
|
||||
Company string `json:"company" binding:"max=200"`
|
||||
Phone string `json:"phone" binding:"max=20"`
|
||||
Email string `json:"email" binding:"omitempty,email,max=254"`
|
||||
Website string `json:"website" binding:"max=200"`
|
||||
Notes string `json:"notes"`
|
||||
StreetAddress string `json:"street_address" binding:"max=255"`
|
||||
City string `json:"city" binding:"max=100"`
|
||||
StateProvince string `json:"state_province" binding:"max=100"`
|
||||
PostalCode string `json:"postal_code" binding:"max=20"`
|
||||
SpecialtyIDs []uint `json:"specialty_ids"`
|
||||
Rating *float64 `json:"rating"`
|
||||
IsFavorite *bool `json:"is_favorite"`
|
||||
}
|
||||
|
||||
// UpdateContractorRequest represents the request to update a contractor
|
||||
type UpdateContractorRequest struct {
|
||||
Name *string `json:"name" binding:"omitempty,min=1,max=200"`
|
||||
Company *string `json:"company" binding:"omitempty,max=200"`
|
||||
Phone *string `json:"phone" binding:"omitempty,max=20"`
|
||||
Email *string `json:"email" binding:"omitempty,email,max=254"`
|
||||
Website *string `json:"website" binding:"omitempty,max=200"`
|
||||
Notes *string `json:"notes"`
|
||||
StreetAddress *string `json:"street_address" binding:"omitempty,max=255"`
|
||||
City *string `json:"city" binding:"omitempty,max=100"`
|
||||
StateProvince *string `json:"state_province" binding:"omitempty,max=100"`
|
||||
PostalCode *string `json:"postal_code" binding:"omitempty,max=20"`
|
||||
SpecialtyIDs []uint `json:"specialty_ids"`
|
||||
Rating *float64 `json:"rating"`
|
||||
IsFavorite *bool `json:"is_favorite"`
|
||||
}
|
||||
46
internal/dto/requests/document.go
Normal file
46
internal/dto/requests/document.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package requests
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
|
||||
"github.com/treytartt/mycrib-api/internal/models"
|
||||
)
|
||||
|
||||
// CreateDocumentRequest represents the request to create a document
|
||||
type CreateDocumentRequest struct {
|
||||
ResidenceID uint `json:"residence_id" binding:"required"`
|
||||
Title string `json:"title" binding:"required,min=1,max=200"`
|
||||
Description string `json:"description"`
|
||||
DocumentType models.DocumentType `json:"document_type"`
|
||||
FileURL string `json:"file_url" binding:"max=500"`
|
||||
FileName string `json:"file_name" binding:"max=255"`
|
||||
FileSize *int64 `json:"file_size"`
|
||||
MimeType string `json:"mime_type" binding:"max=100"`
|
||||
PurchaseDate *time.Time `json:"purchase_date"`
|
||||
ExpiryDate *time.Time `json:"expiry_date"`
|
||||
PurchasePrice *decimal.Decimal `json:"purchase_price"`
|
||||
Vendor string `json:"vendor" binding:"max=200"`
|
||||
SerialNumber string `json:"serial_number" binding:"max=100"`
|
||||
ModelNumber string `json:"model_number" binding:"max=100"`
|
||||
TaskID *uint `json:"task_id"`
|
||||
}
|
||||
|
||||
// UpdateDocumentRequest represents the request to update a document
|
||||
type UpdateDocumentRequest struct {
|
||||
Title *string `json:"title" binding:"omitempty,min=1,max=200"`
|
||||
Description *string `json:"description"`
|
||||
DocumentType *models.DocumentType `json:"document_type"`
|
||||
FileURL *string `json:"file_url" binding:"omitempty,max=500"`
|
||||
FileName *string `json:"file_name" binding:"omitempty,max=255"`
|
||||
FileSize *int64 `json:"file_size"`
|
||||
MimeType *string `json:"mime_type" binding:"omitempty,max=100"`
|
||||
PurchaseDate *time.Time `json:"purchase_date"`
|
||||
ExpiryDate *time.Time `json:"expiry_date"`
|
||||
PurchasePrice *decimal.Decimal `json:"purchase_price"`
|
||||
Vendor *string `json:"vendor" binding:"omitempty,max=200"`
|
||||
SerialNumber *string `json:"serial_number" binding:"omitempty,max=100"`
|
||||
ModelNumber *string `json:"model_number" binding:"omitempty,max=100"`
|
||||
TaskID *uint `json:"task_id"`
|
||||
}
|
||||
59
internal/dto/requests/residence.go
Normal file
59
internal/dto/requests/residence.go
Normal file
@@ -0,0 +1,59 @@
|
||||
package requests
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
// CreateResidenceRequest represents the request to create a residence
|
||||
type CreateResidenceRequest struct {
|
||||
Name string `json:"name" binding:"required,min=1,max=200"`
|
||||
PropertyTypeID *uint `json:"property_type_id"`
|
||||
StreetAddress string `json:"street_address" binding:"max=255"`
|
||||
ApartmentUnit string `json:"apartment_unit" binding:"max=50"`
|
||||
City string `json:"city" binding:"max=100"`
|
||||
StateProvince string `json:"state_province" binding:"max=100"`
|
||||
PostalCode string `json:"postal_code" binding:"max=20"`
|
||||
Country string `json:"country" binding:"max=100"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// UpdateResidenceRequest represents the request to update a residence
|
||||
type UpdateResidenceRequest struct {
|
||||
Name *string `json:"name" binding:"omitempty,min=1,max=200"`
|
||||
PropertyTypeID *uint `json:"property_type_id"`
|
||||
StreetAddress *string `json:"street_address" binding:"omitempty,max=255"`
|
||||
ApartmentUnit *string `json:"apartment_unit" binding:"omitempty,max=50"`
|
||||
City *string `json:"city" binding:"omitempty,max=100"`
|
||||
StateProvince *string `json:"state_province" binding:"omitempty,max=100"`
|
||||
PostalCode *string `json:"postal_code" binding:"omitempty,max=20"`
|
||||
Country *string `json:"country" binding:"omitempty,max=100"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// JoinWithCodeRequest represents the request to join a residence via share code
|
||||
type JoinWithCodeRequest struct {
|
||||
Code string `json:"code" binding:"required,len=6"`
|
||||
}
|
||||
|
||||
// GenerateShareCodeRequest represents the request to generate a share code
|
||||
type GenerateShareCodeRequest struct {
|
||||
ExpiresInHours int `json:"expires_in_hours"` // Default: 24 hours
|
||||
}
|
||||
46
internal/dto/requests/task.go
Normal file
46
internal/dto/requests/task.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package requests
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
)
|
||||
|
||||
// CreateTaskRequest represents the request to create a task
|
||||
type CreateTaskRequest struct {
|
||||
ResidenceID uint `json:"residence_id" binding:"required"`
|
||||
Title string `json:"title" binding:"required,min=1,max=200"`
|
||||
Description string `json:"description"`
|
||||
CategoryID *uint `json:"category_id"`
|
||||
PriorityID *uint `json:"priority_id"`
|
||||
StatusID *uint `json:"status_id"`
|
||||
FrequencyID *uint `json:"frequency_id"`
|
||||
AssignedToID *uint `json:"assigned_to_id"`
|
||||
DueDate *time.Time `json:"due_date"`
|
||||
EstimatedCost *decimal.Decimal `json:"estimated_cost"`
|
||||
ContractorID *uint `json:"contractor_id"`
|
||||
}
|
||||
|
||||
// UpdateTaskRequest represents the request to update a task
|
||||
type UpdateTaskRequest struct {
|
||||
Title *string `json:"title" binding:"omitempty,min=1,max=200"`
|
||||
Description *string `json:"description"`
|
||||
CategoryID *uint `json:"category_id"`
|
||||
PriorityID *uint `json:"priority_id"`
|
||||
StatusID *uint `json:"status_id"`
|
||||
FrequencyID *uint `json:"frequency_id"`
|
||||
AssignedToID *uint `json:"assigned_to_id"`
|
||||
DueDate *time.Time `json:"due_date"`
|
||||
EstimatedCost *decimal.Decimal `json:"estimated_cost"`
|
||||
ActualCost *decimal.Decimal `json:"actual_cost"`
|
||||
ContractorID *uint `json:"contractor_id"`
|
||||
}
|
||||
|
||||
// CreateTaskCompletionRequest represents the request to create a task completion
|
||||
type CreateTaskCompletionRequest struct {
|
||||
TaskID uint `json:"task_id" binding:"required"`
|
||||
CompletedAt *time.Time `json:"completed_at"` // Defaults to now
|
||||
Notes string `json:"notes"`
|
||||
ActualCost *decimal.Decimal `json:"actual_cost"`
|
||||
PhotoURL string `json:"photo_url"`
|
||||
}
|
||||
151
internal/dto/responses/auth.go
Normal file
151
internal/dto/responses/auth.go
Normal file
@@ -0,0 +1,151 @@
|
||||
package responses
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/treytartt/mycrib-api/internal/models"
|
||||
)
|
||||
|
||||
// UserResponse represents a user in API responses
|
||||
type UserResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
IsActive bool `json:"is_active"`
|
||||
DateJoined time.Time `json:"date_joined"`
|
||||
LastLogin *time.Time `json:"last_login,omitempty"`
|
||||
}
|
||||
|
||||
// UserProfileResponse represents a user profile in API responses
|
||||
type UserProfileResponse struct {
|
||||
ID uint `json:"id"`
|
||||
UserID uint `json:"user_id"`
|
||||
Verified bool `json:"verified"`
|
||||
Bio string `json:"bio"`
|
||||
PhoneNumber string `json:"phone_number"`
|
||||
DateOfBirth *time.Time `json:"date_of_birth,omitempty"`
|
||||
ProfilePicture string `json:"profile_picture"`
|
||||
}
|
||||
|
||||
// LoginResponse represents the login response
|
||||
type LoginResponse struct {
|
||||
Token string `json:"token"`
|
||||
User UserResponse `json:"user"`
|
||||
}
|
||||
|
||||
// RegisterResponse represents the registration response
|
||||
type RegisterResponse struct {
|
||||
Token string `json:"token"`
|
||||
User UserResponse `json:"user"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// CurrentUserResponse represents the /auth/me/ response
|
||||
type CurrentUserResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
IsActive bool `json:"is_active"`
|
||||
DateJoined time.Time `json:"date_joined"`
|
||||
LastLogin *time.Time `json:"last_login,omitempty"`
|
||||
Profile *UserProfileResponse `json:"profile,omitempty"`
|
||||
}
|
||||
|
||||
// VerifyEmailResponse represents the email verification response
|
||||
type VerifyEmailResponse struct {
|
||||
Message string `json:"message"`
|
||||
Verified bool `json:"verified"`
|
||||
}
|
||||
|
||||
// ForgotPasswordResponse represents the forgot password response
|
||||
type ForgotPasswordResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// VerifyResetCodeResponse represents the verify reset code response
|
||||
type VerifyResetCodeResponse struct {
|
||||
Message string `json:"message"`
|
||||
ResetToken string `json:"reset_token"`
|
||||
}
|
||||
|
||||
// ResetPasswordResponse represents the reset password response
|
||||
type ResetPasswordResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// MessageResponse represents a simple message response
|
||||
type MessageResponse struct {
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
// ErrorResponse represents an error response
|
||||
type ErrorResponse struct {
|
||||
Error string `json:"error"`
|
||||
Details map[string]string `json:"details,omitempty"`
|
||||
}
|
||||
|
||||
// NewUserResponse creates a UserResponse from a User model
|
||||
func NewUserResponse(user *models.User) UserResponse {
|
||||
return UserResponse{
|
||||
ID: user.ID,
|
||||
Username: user.Username,
|
||||
Email: user.Email,
|
||||
FirstName: user.FirstName,
|
||||
LastName: user.LastName,
|
||||
IsActive: user.IsActive,
|
||||
DateJoined: user.DateJoined,
|
||||
LastLogin: user.LastLogin,
|
||||
}
|
||||
}
|
||||
|
||||
// NewUserProfileResponse creates a UserProfileResponse from a UserProfile model
|
||||
func NewUserProfileResponse(profile *models.UserProfile) *UserProfileResponse {
|
||||
if profile == nil {
|
||||
return nil
|
||||
}
|
||||
return &UserProfileResponse{
|
||||
ID: profile.ID,
|
||||
UserID: profile.UserID,
|
||||
Verified: profile.Verified,
|
||||
Bio: profile.Bio,
|
||||
PhoneNumber: profile.PhoneNumber,
|
||||
DateOfBirth: profile.DateOfBirth,
|
||||
ProfilePicture: profile.ProfilePicture,
|
||||
}
|
||||
}
|
||||
|
||||
// NewCurrentUserResponse creates a CurrentUserResponse from a User model
|
||||
func NewCurrentUserResponse(user *models.User) CurrentUserResponse {
|
||||
return CurrentUserResponse{
|
||||
ID: user.ID,
|
||||
Username: user.Username,
|
||||
Email: user.Email,
|
||||
FirstName: user.FirstName,
|
||||
LastName: user.LastName,
|
||||
IsActive: user.IsActive,
|
||||
DateJoined: user.DateJoined,
|
||||
LastLogin: user.LastLogin,
|
||||
Profile: NewUserProfileResponse(user.Profile),
|
||||
}
|
||||
}
|
||||
|
||||
// NewLoginResponse creates a LoginResponse
|
||||
func NewLoginResponse(token string, user *models.User) LoginResponse {
|
||||
return LoginResponse{
|
||||
Token: token,
|
||||
User: NewUserResponse(user),
|
||||
}
|
||||
}
|
||||
|
||||
// NewRegisterResponse creates a RegisterResponse
|
||||
func NewRegisterResponse(token string, user *models.User) RegisterResponse {
|
||||
return RegisterResponse{
|
||||
Token: token,
|
||||
User: NewUserResponse(user),
|
||||
Message: "Registration successful. Please check your email to verify your account.",
|
||||
}
|
||||
}
|
||||
139
internal/dto/responses/contractor.go
Normal file
139
internal/dto/responses/contractor.go
Normal file
@@ -0,0 +1,139 @@
|
||||
package responses
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/treytartt/mycrib-api/internal/models"
|
||||
)
|
||||
|
||||
// ContractorSpecialtyResponse represents a contractor specialty
|
||||
type ContractorSpecialtyResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"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"`
|
||||
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"`
|
||||
}
|
||||
|
||||
// ContractorListResponse represents a paginated list of contractors
|
||||
type ContractorListResponse struct {
|
||||
Count int `json:"count"`
|
||||
Next *string `json:"next"`
|
||||
Previous *string `json:"previous"`
|
||||
Results []ContractorResponse `json:"results"`
|
||||
}
|
||||
|
||||
// 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,
|
||||
CreatedByID: c.CreatedByID,
|
||||
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 ContractorListResponse from a slice of contractors
|
||||
func NewContractorListResponse(contractors []models.Contractor) ContractorListResponse {
|
||||
results := make([]ContractorResponse, len(contractors))
|
||||
for i, c := range contractors {
|
||||
results[i] = NewContractorResponse(&c)
|
||||
}
|
||||
return ContractorListResponse{
|
||||
Count: len(contractors),
|
||||
Next: nil,
|
||||
Previous: nil,
|
||||
Results: results,
|
||||
}
|
||||
}
|
||||
111
internal/dto/responses/document.go
Normal file
111
internal/dto/responses/document.go
Normal file
@@ -0,0 +1,111 @@
|
||||
package responses
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
|
||||
"github.com/treytartt/mycrib-api/internal/models"
|
||||
)
|
||||
|
||||
// DocumentUserResponse represents a user in document context
|
||||
type DocumentUserResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Username string `json:"username"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
}
|
||||
|
||||
// DocumentResponse represents a document in the API response
|
||||
type DocumentResponse struct {
|
||||
ID uint `json:"id"`
|
||||
ResidenceID uint `json:"residence_id"`
|
||||
CreatedByID uint `json:"created_by_id"`
|
||||
CreatedBy *DocumentUserResponse `json:"created_by,omitempty"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
DocumentType models.DocumentType `json:"document_type"`
|
||||
FileURL string `json:"file_url"`
|
||||
FileName string `json:"file_name"`
|
||||
FileSize *int64 `json:"file_size"`
|
||||
MimeType string `json:"mime_type"`
|
||||
PurchaseDate *time.Time `json:"purchase_date"`
|
||||
ExpiryDate *time.Time `json:"expiry_date"`
|
||||
PurchasePrice *decimal.Decimal `json:"purchase_price"`
|
||||
Vendor string `json:"vendor"`
|
||||
SerialNumber string `json:"serial_number"`
|
||||
ModelNumber string `json:"model_number"`
|
||||
TaskID *uint `json:"task_id"`
|
||||
IsActive bool `json:"is_active"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// DocumentListResponse represents a paginated list of documents
|
||||
type DocumentListResponse struct {
|
||||
Count int `json:"count"`
|
||||
Next *string `json:"next"`
|
||||
Previous *string `json:"previous"`
|
||||
Results []DocumentResponse `json:"results"`
|
||||
}
|
||||
|
||||
// === Factory Functions ===
|
||||
|
||||
// NewDocumentUserResponse creates a DocumentUserResponse from a User model
|
||||
func NewDocumentUserResponse(u *models.User) *DocumentUserResponse {
|
||||
if u == nil {
|
||||
return nil
|
||||
}
|
||||
return &DocumentUserResponse{
|
||||
ID: u.ID,
|
||||
Username: u.Username,
|
||||
FirstName: u.FirstName,
|
||||
LastName: u.LastName,
|
||||
}
|
||||
}
|
||||
|
||||
// NewDocumentResponse creates a DocumentResponse from a Document model
|
||||
func NewDocumentResponse(d *models.Document) DocumentResponse {
|
||||
resp := DocumentResponse{
|
||||
ID: d.ID,
|
||||
ResidenceID: d.ResidenceID,
|
||||
CreatedByID: d.CreatedByID,
|
||||
Title: d.Title,
|
||||
Description: d.Description,
|
||||
DocumentType: d.DocumentType,
|
||||
FileURL: d.FileURL,
|
||||
FileName: d.FileName,
|
||||
FileSize: d.FileSize,
|
||||
MimeType: d.MimeType,
|
||||
PurchaseDate: d.PurchaseDate,
|
||||
ExpiryDate: d.ExpiryDate,
|
||||
PurchasePrice: d.PurchasePrice,
|
||||
Vendor: d.Vendor,
|
||||
SerialNumber: d.SerialNumber,
|
||||
ModelNumber: d.ModelNumber,
|
||||
TaskID: d.TaskID,
|
||||
IsActive: d.IsActive,
|
||||
CreatedAt: d.CreatedAt,
|
||||
UpdatedAt: d.UpdatedAt,
|
||||
}
|
||||
|
||||
if d.CreatedBy.ID != 0 {
|
||||
resp.CreatedBy = NewDocumentUserResponse(&d.CreatedBy)
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
// NewDocumentListResponse creates a DocumentListResponse from a slice of documents
|
||||
func NewDocumentListResponse(documents []models.Document) DocumentListResponse {
|
||||
results := make([]DocumentResponse, len(documents))
|
||||
for i, d := range documents {
|
||||
results[i] = NewDocumentResponse(&d)
|
||||
}
|
||||
return DocumentListResponse{
|
||||
Count: len(documents),
|
||||
Next: nil,
|
||||
Previous: nil,
|
||||
Results: results,
|
||||
}
|
||||
}
|
||||
189
internal/dto/responses/residence.go
Normal file
189
internal/dto/responses/residence.go
Normal file
@@ -0,0 +1,189 @@
|
||||
package responses
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
|
||||
"github.com/treytartt/mycrib-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"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// ResidenceListResponse represents the paginated list of residences
|
||||
type ResidenceListResponse struct {
|
||||
Count int `json:"count"`
|
||||
Next *string `json:"next"`
|
||||
Previous *string `json:"previous"`
|
||||
Results []ResidenceResponse `json:"results"`
|
||||
}
|
||||
|
||||
// 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"`
|
||||
}
|
||||
|
||||
// GenerateShareCodeResponse represents the response after generating a share code
|
||||
type GenerateShareCodeResponse struct {
|
||||
Message string `json:"message"`
|
||||
ShareCode ShareCodeResponse `json:"share_code"`
|
||||
}
|
||||
|
||||
// === 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 paginated list response
|
||||
func NewResidenceListResponse(residences []models.Residence) ResidenceListResponse {
|
||||
results := make([]ResidenceResponse, len(residences))
|
||||
for i, r := range residences {
|
||||
results[i] = NewResidenceResponse(&r)
|
||||
}
|
||||
|
||||
return ResidenceListResponse{
|
||||
Count: len(residences),
|
||||
Next: nil, // Pagination not implemented yet
|
||||
Previous: nil,
|
||||
Results: 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,
|
||||
}
|
||||
}
|
||||
324
internal/dto/responses/task.go
Normal file
324
internal/dto/responses/task.go
Normal file
@@ -0,0 +1,324 @@
|
||||
package responses
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/shopspring/decimal"
|
||||
|
||||
"github.com/treytartt/mycrib-api/internal/models"
|
||||
)
|
||||
|
||||
// TaskCategoryResponse represents a task category
|
||||
type TaskCategoryResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Icon string `json:"icon"`
|
||||
Color string `json:"color"`
|
||||
DisplayOrder int `json:"display_order"`
|
||||
}
|
||||
|
||||
// TaskPriorityResponse represents a task priority
|
||||
type TaskPriorityResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Level int `json:"level"`
|
||||
Color string `json:"color"`
|
||||
DisplayOrder int `json:"display_order"`
|
||||
}
|
||||
|
||||
// TaskStatusResponse represents a task status
|
||||
type TaskStatusResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
Color string `json:"color"`
|
||||
DisplayOrder int `json:"display_order"`
|
||||
}
|
||||
|
||||
// TaskFrequencyResponse represents a task frequency
|
||||
type TaskFrequencyResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
Days *int `json:"days"`
|
||||
DisplayOrder int `json:"display_order"`
|
||||
}
|
||||
|
||||
// TaskUserResponse represents a user in task context
|
||||
type TaskUserResponse struct {
|
||||
ID uint `json:"id"`
|
||||
Username string `json:"username"`
|
||||
Email string `json:"email"`
|
||||
FirstName string `json:"first_name"`
|
||||
LastName string `json:"last_name"`
|
||||
}
|
||||
|
||||
// TaskCompletionResponse represents a task completion
|
||||
type TaskCompletionResponse struct {
|
||||
ID uint `json:"id"`
|
||||
TaskID uint `json:"task_id"`
|
||||
CompletedBy *TaskUserResponse `json:"completed_by,omitempty"`
|
||||
CompletedAt time.Time `json:"completed_at"`
|
||||
Notes string `json:"notes"`
|
||||
ActualCost *decimal.Decimal `json:"actual_cost"`
|
||||
PhotoURL string `json:"photo_url"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
}
|
||||
|
||||
// TaskResponse represents a task in the API response
|
||||
type TaskResponse struct {
|
||||
ID uint `json:"id"`
|
||||
ResidenceID uint `json:"residence_id"`
|
||||
CreatedByID uint `json:"created_by_id"`
|
||||
CreatedBy *TaskUserResponse `json:"created_by,omitempty"`
|
||||
AssignedToID *uint `json:"assigned_to_id"`
|
||||
AssignedTo *TaskUserResponse `json:"assigned_to,omitempty"`
|
||||
Title string `json:"title"`
|
||||
Description string `json:"description"`
|
||||
CategoryID *uint `json:"category_id"`
|
||||
Category *TaskCategoryResponse `json:"category,omitempty"`
|
||||
PriorityID *uint `json:"priority_id"`
|
||||
Priority *TaskPriorityResponse `json:"priority,omitempty"`
|
||||
StatusID *uint `json:"status_id"`
|
||||
Status *TaskStatusResponse `json:"status,omitempty"`
|
||||
FrequencyID *uint `json:"frequency_id"`
|
||||
Frequency *TaskFrequencyResponse `json:"frequency,omitempty"`
|
||||
DueDate *time.Time `json:"due_date"`
|
||||
EstimatedCost *decimal.Decimal `json:"estimated_cost"`
|
||||
ActualCost *decimal.Decimal `json:"actual_cost"`
|
||||
ContractorID *uint `json:"contractor_id"`
|
||||
IsCancelled bool `json:"is_cancelled"`
|
||||
IsArchived bool `json:"is_archived"`
|
||||
ParentTaskID *uint `json:"parent_task_id"`
|
||||
Completions []TaskCompletionResponse `json:"completions,omitempty"`
|
||||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
}
|
||||
|
||||
// TaskListResponse represents a paginated list of tasks
|
||||
type TaskListResponse struct {
|
||||
Count int `json:"count"`
|
||||
Next *string `json:"next"`
|
||||
Previous *string `json:"previous"`
|
||||
Results []TaskResponse `json:"results"`
|
||||
}
|
||||
|
||||
// KanbanColumnResponse represents a kanban column
|
||||
type KanbanColumnResponse struct {
|
||||
Name string `json:"name"`
|
||||
DisplayName string `json:"display_name"`
|
||||
ButtonTypes []string `json:"button_types"`
|
||||
Icons map[string]string `json:"icons"`
|
||||
Color string `json:"color"`
|
||||
Tasks []TaskResponse `json:"tasks"`
|
||||
Count int `json:"count"`
|
||||
}
|
||||
|
||||
// KanbanBoardResponse represents the kanban board
|
||||
type KanbanBoardResponse struct {
|
||||
Columns []KanbanColumnResponse `json:"columns"`
|
||||
DaysThreshold int `json:"days_threshold"`
|
||||
ResidenceID string `json:"residence_id"`
|
||||
}
|
||||
|
||||
// TaskCompletionListResponse represents a list of completions
|
||||
type TaskCompletionListResponse struct {
|
||||
Count int `json:"count"`
|
||||
Next *string `json:"next"`
|
||||
Previous *string `json:"previous"`
|
||||
Results []TaskCompletionResponse `json:"results"`
|
||||
}
|
||||
|
||||
// === Factory Functions ===
|
||||
|
||||
// NewTaskCategoryResponse creates a TaskCategoryResponse from a model
|
||||
func NewTaskCategoryResponse(c *models.TaskCategory) *TaskCategoryResponse {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
return &TaskCategoryResponse{
|
||||
ID: c.ID,
|
||||
Name: c.Name,
|
||||
Description: c.Description,
|
||||
Icon: c.Icon,
|
||||
Color: c.Color,
|
||||
DisplayOrder: c.DisplayOrder,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTaskPriorityResponse creates a TaskPriorityResponse from a model
|
||||
func NewTaskPriorityResponse(p *models.TaskPriority) *TaskPriorityResponse {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return &TaskPriorityResponse{
|
||||
ID: p.ID,
|
||||
Name: p.Name,
|
||||
Level: p.Level,
|
||||
Color: p.Color,
|
||||
DisplayOrder: p.DisplayOrder,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTaskStatusResponse creates a TaskStatusResponse from a model
|
||||
func NewTaskStatusResponse(s *models.TaskStatus) *TaskStatusResponse {
|
||||
if s == nil {
|
||||
return nil
|
||||
}
|
||||
return &TaskStatusResponse{
|
||||
ID: s.ID,
|
||||
Name: s.Name,
|
||||
Description: s.Description,
|
||||
Color: s.Color,
|
||||
DisplayOrder: s.DisplayOrder,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTaskFrequencyResponse creates a TaskFrequencyResponse from a model
|
||||
func NewTaskFrequencyResponse(f *models.TaskFrequency) *TaskFrequencyResponse {
|
||||
if f == nil {
|
||||
return nil
|
||||
}
|
||||
return &TaskFrequencyResponse{
|
||||
ID: f.ID,
|
||||
Name: f.Name,
|
||||
Days: f.Days,
|
||||
DisplayOrder: f.DisplayOrder,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTaskUserResponse creates a TaskUserResponse from a User model
|
||||
func NewTaskUserResponse(u *models.User) *TaskUserResponse {
|
||||
if u == nil {
|
||||
return nil
|
||||
}
|
||||
return &TaskUserResponse{
|
||||
ID: u.ID,
|
||||
Username: u.Username,
|
||||
Email: u.Email,
|
||||
FirstName: u.FirstName,
|
||||
LastName: u.LastName,
|
||||
}
|
||||
}
|
||||
|
||||
// NewTaskCompletionResponse creates a TaskCompletionResponse from a model
|
||||
func NewTaskCompletionResponse(c *models.TaskCompletion) TaskCompletionResponse {
|
||||
resp := TaskCompletionResponse{
|
||||
ID: c.ID,
|
||||
TaskID: c.TaskID,
|
||||
CompletedAt: c.CompletedAt,
|
||||
Notes: c.Notes,
|
||||
ActualCost: c.ActualCost,
|
||||
PhotoURL: c.PhotoURL,
|
||||
CreatedAt: c.CreatedAt,
|
||||
}
|
||||
if c.CompletedBy.ID != 0 {
|
||||
resp.CompletedBy = NewTaskUserResponse(&c.CompletedBy)
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
// NewTaskResponse creates a TaskResponse from a Task model
|
||||
func NewTaskResponse(t *models.Task) TaskResponse {
|
||||
resp := TaskResponse{
|
||||
ID: t.ID,
|
||||
ResidenceID: t.ResidenceID,
|
||||
CreatedByID: t.CreatedByID,
|
||||
Title: t.Title,
|
||||
Description: t.Description,
|
||||
CategoryID: t.CategoryID,
|
||||
PriorityID: t.PriorityID,
|
||||
StatusID: t.StatusID,
|
||||
FrequencyID: t.FrequencyID,
|
||||
AssignedToID: t.AssignedToID,
|
||||
DueDate: t.DueDate,
|
||||
EstimatedCost: t.EstimatedCost,
|
||||
ActualCost: t.ActualCost,
|
||||
ContractorID: t.ContractorID,
|
||||
IsCancelled: t.IsCancelled,
|
||||
IsArchived: t.IsArchived,
|
||||
ParentTaskID: t.ParentTaskID,
|
||||
CreatedAt: t.CreatedAt,
|
||||
UpdatedAt: t.UpdatedAt,
|
||||
}
|
||||
|
||||
if t.CreatedBy.ID != 0 {
|
||||
resp.CreatedBy = NewTaskUserResponse(&t.CreatedBy)
|
||||
}
|
||||
if t.AssignedTo != nil {
|
||||
resp.AssignedTo = NewTaskUserResponse(t.AssignedTo)
|
||||
}
|
||||
if t.Category != nil {
|
||||
resp.Category = NewTaskCategoryResponse(t.Category)
|
||||
}
|
||||
if t.Priority != nil {
|
||||
resp.Priority = NewTaskPriorityResponse(t.Priority)
|
||||
}
|
||||
if t.Status != nil {
|
||||
resp.Status = NewTaskStatusResponse(t.Status)
|
||||
}
|
||||
if t.Frequency != nil {
|
||||
resp.Frequency = NewTaskFrequencyResponse(t.Frequency)
|
||||
}
|
||||
|
||||
resp.Completions = make([]TaskCompletionResponse, len(t.Completions))
|
||||
for i, c := range t.Completions {
|
||||
resp.Completions[i] = NewTaskCompletionResponse(&c)
|
||||
}
|
||||
|
||||
return resp
|
||||
}
|
||||
|
||||
// NewTaskListResponse creates a TaskListResponse from a slice of tasks
|
||||
func NewTaskListResponse(tasks []models.Task) TaskListResponse {
|
||||
results := make([]TaskResponse, len(tasks))
|
||||
for i, t := range tasks {
|
||||
results[i] = NewTaskResponse(&t)
|
||||
}
|
||||
return TaskListResponse{
|
||||
Count: len(tasks),
|
||||
Next: nil,
|
||||
Previous: nil,
|
||||
Results: results,
|
||||
}
|
||||
}
|
||||
|
||||
// NewKanbanBoardResponse creates a KanbanBoardResponse from a KanbanBoard model
|
||||
func NewKanbanBoardResponse(board *models.KanbanBoard, residenceID uint) KanbanBoardResponse {
|
||||
columns := make([]KanbanColumnResponse, len(board.Columns))
|
||||
for i, col := range board.Columns {
|
||||
tasks := make([]TaskResponse, len(col.Tasks))
|
||||
for j, t := range col.Tasks {
|
||||
tasks[j] = NewTaskResponse(&t)
|
||||
}
|
||||
columns[i] = KanbanColumnResponse{
|
||||
Name: col.Name,
|
||||
DisplayName: col.DisplayName,
|
||||
ButtonTypes: col.ButtonTypes,
|
||||
Icons: col.Icons,
|
||||
Color: col.Color,
|
||||
Tasks: tasks,
|
||||
Count: col.Count,
|
||||
}
|
||||
}
|
||||
return KanbanBoardResponse{
|
||||
Columns: columns,
|
||||
DaysThreshold: board.DaysThreshold,
|
||||
ResidenceID: fmt.Sprintf("%d", residenceID),
|
||||
}
|
||||
}
|
||||
|
||||
// NewTaskCompletionListResponse creates a TaskCompletionListResponse
|
||||
func NewTaskCompletionListResponse(completions []models.TaskCompletion) TaskCompletionListResponse {
|
||||
results := make([]TaskCompletionResponse, len(completions))
|
||||
for i, c := range completions {
|
||||
results[i] = NewTaskCompletionResponse(&c)
|
||||
}
|
||||
return TaskCompletionListResponse{
|
||||
Count: len(completions),
|
||||
Next: nil,
|
||||
Previous: nil,
|
||||
Results: results,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user