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:
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