Files
honeyDueAPI/internal/admin/dto/responses.go
Trey t 0c86611a10 Add IsFree subscription toggle to bypass all tier limitations
- Add IsFree boolean field to UserSubscription model
- When IsFree is true, user sees limitations_enabled=false regardless of global setting
- CheckLimit() bypasses all limit checks for IsFree users
- Add admin endpoint GET /api/admin/subscriptions/user/:user_id
- Add IsFree toggle to admin user detail page under Subscription card
- Add database migration 004_subscription_is_free
- Add integration tests for IsFree functionality
- Add task kanban categorization documentation

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-01 18:05:41 -06:00

272 lines
10 KiB
Go

package dto
// PaginatedResponse represents a paginated API response
type PaginatedResponse struct {
Data interface{} `json:"data"`
Total int64 `json:"total"`
Page int `json:"page"`
PerPage int `json:"per_page"`
TotalPages int `json:"total_pages"`
}
// NewPaginatedResponse creates a new paginated response
func NewPaginatedResponse(data interface{}, total int64, page, perPage int) PaginatedResponse {
totalPages := int(total) / perPage
if int(total)%perPage > 0 {
totalPages++
}
return PaginatedResponse{
Data: data,
Total: total,
Page: page,
PerPage: perPage,
TotalPages: totalPages,
}
}
// UserResponse represents a user in admin 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"`
IsStaff bool `json:"is_staff"`
IsSuperuser bool `json:"is_superuser"`
DateJoined string `json:"date_joined"`
LastLogin *string `json:"last_login,omitempty"`
// Profile info
Verified bool `json:"verified"`
PhoneNumber *string `json:"phone_number,omitempty"`
// Counts
ResidenceCount int `json:"residence_count"`
TaskCount int `json:"task_count"`
}
// UserDetailResponse includes more details for single user view
type UserDetailResponse struct {
UserResponse
Residences []ResidenceSummary `json:"residences,omitempty"`
Devices []DeviceSummary `json:"devices,omitempty"`
}
// ResidenceSummary is a brief residence representation
type ResidenceSummary struct {
ID uint `json:"id"`
Name string `json:"name"`
IsOwner bool `json:"is_owner"`
IsActive bool `json:"is_active"`
}
// DeviceSummary is a brief device representation
type DeviceSummary struct {
ID uint `json:"id"`
Type string `json:"type"` // "apns" or "gcm"
Name string `json:"name"`
IsActive bool `json:"is_active"`
}
// UserSummary is a brief user representation
type UserSummary struct {
ID uint `json:"id"`
Username string `json:"username"`
Email string `json:"email"`
}
// ResidenceResponse represents a residence in admin responses
type ResidenceResponse struct {
ID uint `json:"id"`
Name string `json:"name"`
OwnerID uint `json:"owner_id"`
OwnerName string `json:"owner_name"`
PropertyTypeID *uint `json:"property_type_id,omitempty"`
PropertyType *string `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,omitempty"`
Bathrooms *float64 `json:"bathrooms,omitempty"`
SquareFootage *int `json:"square_footage,omitempty"`
LotSize *float64 `json:"lot_size,omitempty"`
YearBuilt *int `json:"year_built,omitempty"`
Description string `json:"description"`
PurchaseDate *string `json:"purchase_date,omitempty"`
PurchasePrice *float64 `json:"purchase_price,omitempty"`
IsPrimary bool `json:"is_primary"`
IsActive bool `json:"is_active"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// ResidenceDetailResponse includes more details for single residence view
type ResidenceDetailResponse struct {
ResidenceResponse
Owner *UserSummary `json:"owner,omitempty"`
SharedUsers []UserSummary `json:"shared_users,omitempty"`
TaskCount int `json:"task_count"`
DocumentCount int `json:"document_count"`
}
// TaskResponse represents a task in admin responses
type TaskResponse struct {
ID uint `json:"id"`
ResidenceID uint `json:"residence_id"`
ResidenceName string `json:"residence_name"`
CreatedByID uint `json:"created_by_id"`
CreatedByName string `json:"created_by_name"`
AssignedToID *uint `json:"assigned_to_id,omitempty"`
AssignedToName *string `json:"assigned_to_name,omitempty"`
Title string `json:"title"`
Description string `json:"description"`
CategoryID *uint `json:"category_id,omitempty"`
CategoryName *string `json:"category_name,omitempty"`
PriorityID *uint `json:"priority_id,omitempty"`
PriorityName *string `json:"priority_name,omitempty"`
StatusID *uint `json:"status_id,omitempty"`
StatusName *string `json:"status_name,omitempty"`
FrequencyID *uint `json:"frequency_id,omitempty"`
FrequencyName *string `json:"frequency_name,omitempty"`
DueDate *string `json:"due_date,omitempty"`
EstimatedCost *float64 `json:"estimated_cost,omitempty"`
ActualCost *float64 `json:"actual_cost,omitempty"`
ContractorID *uint `json:"contractor_id,omitempty"`
ParentTaskID *uint `json:"parent_task_id,omitempty"`
IsCancelled bool `json:"is_cancelled"`
IsArchived bool `json:"is_archived"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// TaskDetailResponse includes more details for single task view
type TaskDetailResponse struct {
TaskResponse
AssignedTo *UserSummary `json:"assigned_to,omitempty"`
CompletionCount int `json:"completion_count"`
}
// ContractorResponse represents a contractor in admin responses
type ContractorResponse struct {
ID uint `json:"id"`
ResidenceID *uint `json:"residence_id,omitempty"`
ResidenceName string `json:"residence_name,omitempty"`
CreatedByID uint `json:"created_by_id"`
CreatedByName string `json:"created_by_name"`
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"`
Rating *float64 `json:"rating,omitempty"`
Specialties []string `json:"specialties,omitempty"`
SpecialtyIDs []uint `json:"specialty_ids,omitempty"`
IsFavorite bool `json:"is_favorite"`
IsActive bool `json:"is_active"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// ContractorDetailResponse includes more details for single contractor view
type ContractorDetailResponse struct {
ContractorResponse
TaskCount int `json:"task_count"`
}
// DocumentImageResponse represents a document image
type DocumentImageResponse struct {
ID uint `json:"id"`
ImageURL string `json:"image_url"`
Caption string `json:"caption"`
}
// DocumentResponse represents a document in admin responses
type DocumentResponse struct {
ID uint `json:"id"`
ResidenceID uint `json:"residence_id"`
ResidenceName string `json:"residence_name"`
CreatedByID uint `json:"created_by_id"`
CreatedByName string `json:"created_by_name"`
Title string `json:"title"`
Description string `json:"description"`
DocumentType string `json:"document_type"`
FileURL string `json:"file_url"`
FileName string `json:"file_name"`
FileSize *int64 `json:"file_size,omitempty"`
MimeType string `json:"mime_type"`
PurchaseDate *string `json:"purchase_date,omitempty"`
ExpiryDate *string `json:"expiry_date,omitempty"`
PurchasePrice *float64 `json:"purchase_price,omitempty"`
Vendor string `json:"vendor"`
SerialNumber string `json:"serial_number"`
ModelNumber string `json:"model_number"`
Provider string `json:"provider"`
ProviderContact string `json:"provider_contact"`
ClaimPhone string `json:"claim_phone"`
ClaimEmail string `json:"claim_email"`
ClaimWebsite string `json:"claim_website"`
Notes string `json:"notes"`
TaskID *uint `json:"task_id,omitempty"`
IsActive bool `json:"is_active"`
Images []DocumentImageResponse `json:"images"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// DocumentDetailResponse includes more details for single document view
type DocumentDetailResponse struct {
DocumentResponse
TaskTitle *string `json:"task_title,omitempty"`
}
// NotificationResponse represents a notification in admin responses
type NotificationResponse struct {
ID uint `json:"id"`
UserID uint `json:"user_id"`
UserEmail string `json:"user_email"`
Username string `json:"username"`
NotificationType string `json:"notification_type"`
Title string `json:"title"`
Body string `json:"body"`
Sent bool `json:"sent"`
SentAt *string `json:"sent_at,omitempty"`
Read bool `json:"read"`
ReadAt *string `json:"read_at,omitempty"`
CreatedAt string `json:"created_at"`
}
// NotificationDetailResponse includes more details for single notification view
type NotificationDetailResponse struct {
NotificationResponse
Data string `json:"data"`
TaskID *uint `json:"task_id,omitempty"`
}
// SubscriptionResponse represents a subscription in admin responses
type SubscriptionResponse struct {
ID uint `json:"id"`
UserID uint `json:"user_id"`
UserEmail string `json:"user_email"`
Username string `json:"username"`
Tier string `json:"tier"`
Platform string `json:"platform"`
AutoRenew bool `json:"auto_renew"`
IsFree bool `json:"is_free"`
SubscribedAt *string `json:"subscribed_at,omitempty"`
ExpiresAt *string `json:"expires_at,omitempty"`
CancelledAt *string `json:"cancelled_at,omitempty"`
CreatedAt string `json:"created_at"`
}
// SubscriptionDetailResponse includes more details for single subscription view
type SubscriptionDetailResponse struct {
SubscriptionResponse
}