mirror of
https://github.com/TracksApp/tracks.git
synced 2025-12-18 00:00:12 +01:00
221 lines
5.5 KiB
Go
221 lines
5.5 KiB
Go
|
|
package services
|
||
|
|
|
||
|
|
import (
|
||
|
|
"errors"
|
||
|
|
"fmt"
|
||
|
|
|
||
|
|
"github.com/TracksApp/tracks/internal/database"
|
||
|
|
"github.com/TracksApp/tracks/internal/models"
|
||
|
|
"gorm.io/gorm"
|
||
|
|
)
|
||
|
|
|
||
|
|
// ContextService handles context business logic
|
||
|
|
type ContextService struct{}
|
||
|
|
|
||
|
|
// NewContextService creates a new ContextService
|
||
|
|
func NewContextService() *ContextService {
|
||
|
|
return &ContextService{}
|
||
|
|
}
|
||
|
|
|
||
|
|
// CreateContextRequest represents a context creation request
|
||
|
|
type CreateContextRequest struct {
|
||
|
|
Name string `json:"name" binding:"required"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// UpdateContextRequest represents a context update request
|
||
|
|
type UpdateContextRequest struct {
|
||
|
|
Name *string `json:"name"`
|
||
|
|
Position *int `json:"position"`
|
||
|
|
State *string `json:"state"`
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetContexts returns all contexts for a user
|
||
|
|
func (s *ContextService) GetContexts(userID uint, state models.ContextState) ([]models.Context, error) {
|
||
|
|
var contexts []models.Context
|
||
|
|
|
||
|
|
query := database.DB.Where("user_id = ?", userID)
|
||
|
|
|
||
|
|
if state != "" {
|
||
|
|
query = query.Where("state = ?", state)
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := query.
|
||
|
|
Order("position ASC, name ASC").
|
||
|
|
Find(&contexts).Error; err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return contexts, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetContext returns a single context by ID
|
||
|
|
func (s *ContextService) GetContext(userID, contextID uint) (*models.Context, error) {
|
||
|
|
var context models.Context
|
||
|
|
|
||
|
|
if err := database.DB.
|
||
|
|
Where("id = ? AND user_id = ?", contextID, userID).
|
||
|
|
First(&context).Error; err != nil {
|
||
|
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||
|
|
return nil, fmt.Errorf("context not found")
|
||
|
|
}
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return &context, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
// CreateContext creates a new context
|
||
|
|
func (s *ContextService) CreateContext(userID uint, req CreateContextRequest) (*models.Context, error) {
|
||
|
|
context := models.Context{
|
||
|
|
UserID: userID,
|
||
|
|
Name: req.Name,
|
||
|
|
State: models.ContextStateActive,
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := database.DB.Create(&context).Error; err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return s.GetContext(userID, context.ID)
|
||
|
|
}
|
||
|
|
|
||
|
|
// UpdateContext updates a context
|
||
|
|
func (s *ContextService) UpdateContext(userID, contextID uint, req UpdateContextRequest) (*models.Context, error) {
|
||
|
|
context, err := s.GetContext(userID, contextID)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
if req.Name != nil {
|
||
|
|
context.Name = *req.Name
|
||
|
|
}
|
||
|
|
if req.Position != nil {
|
||
|
|
context.Position = *req.Position
|
||
|
|
}
|
||
|
|
if req.State != nil {
|
||
|
|
context.State = models.ContextState(*req.State)
|
||
|
|
}
|
||
|
|
|
||
|
|
if err := database.DB.Save(&context).Error; err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return s.GetContext(userID, contextID)
|
||
|
|
}
|
||
|
|
|
||
|
|
// DeleteContext deletes a context
|
||
|
|
func (s *ContextService) DeleteContext(userID, contextID uint) error {
|
||
|
|
context, err := s.GetContext(userID, contextID)
|
||
|
|
if err != nil {
|
||
|
|
return err
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check if context has active todos
|
||
|
|
var activeTodoCount int64
|
||
|
|
database.DB.Model(&models.Todo{}).
|
||
|
|
Where("context_id = ? AND state = ?", contextID, models.TodoStateActive).
|
||
|
|
Count(&activeTodoCount)
|
||
|
|
|
||
|
|
if activeTodoCount > 0 {
|
||
|
|
return fmt.Errorf("cannot delete context with active todos")
|
||
|
|
}
|
||
|
|
|
||
|
|
return database.DB.Delete(&context).Error
|
||
|
|
}
|
||
|
|
|
||
|
|
// HideContext marks a context as hidden
|
||
|
|
func (s *ContextService) HideContext(userID, contextID uint) (*models.Context, error) {
|
||
|
|
context, err := s.GetContext(userID, contextID)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
context.Hide()
|
||
|
|
|
||
|
|
if err := database.DB.Save(&context).Error; err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return s.GetContext(userID, contextID)
|
||
|
|
}
|
||
|
|
|
||
|
|
// ActivateContext marks a context as active
|
||
|
|
func (s *ContextService) ActivateContext(userID, contextID uint) (*models.Context, error) {
|
||
|
|
context, err := s.GetContext(userID, contextID)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
context.Activate()
|
||
|
|
|
||
|
|
if err := database.DB.Save(&context).Error; err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return s.GetContext(userID, contextID)
|
||
|
|
}
|
||
|
|
|
||
|
|
// CloseContext marks a context as closed
|
||
|
|
func (s *ContextService) CloseContext(userID, contextID uint) (*models.Context, error) {
|
||
|
|
context, err := s.GetContext(userID, contextID)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check if context has active todos
|
||
|
|
var activeTodoCount int64
|
||
|
|
database.DB.Model(&models.Todo{}).
|
||
|
|
Where("context_id = ? AND state = ?", contextID, models.TodoStateActive).
|
||
|
|
Count(&activeTodoCount)
|
||
|
|
|
||
|
|
if activeTodoCount > 0 {
|
||
|
|
return nil, fmt.Errorf("cannot close context with active todos")
|
||
|
|
}
|
||
|
|
|
||
|
|
context.Close()
|
||
|
|
|
||
|
|
if err := database.DB.Save(&context).Error; err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
return s.GetContext(userID, contextID)
|
||
|
|
}
|
||
|
|
|
||
|
|
// GetContextStats returns statistics for a context
|
||
|
|
func (s *ContextService) GetContextStats(userID, contextID uint) (map[string]interface{}, error) {
|
||
|
|
context, err := s.GetContext(userID, contextID)
|
||
|
|
if err != nil {
|
||
|
|
return nil, err
|
||
|
|
}
|
||
|
|
|
||
|
|
stats := make(map[string]interface{})
|
||
|
|
|
||
|
|
// Count todos by state
|
||
|
|
var activeTodos, completedTodos, deferredTodos, pendingTodos int64
|
||
|
|
|
||
|
|
database.DB.Model(&models.Todo{}).
|
||
|
|
Where("context_id = ? AND state = ?", contextID, models.TodoStateActive).
|
||
|
|
Count(&activeTodos)
|
||
|
|
|
||
|
|
database.DB.Model(&models.Todo{}).
|
||
|
|
Where("context_id = ? AND state = ?", contextID, models.TodoStateCompleted).
|
||
|
|
Count(&completedTodos)
|
||
|
|
|
||
|
|
database.DB.Model(&models.Todo{}).
|
||
|
|
Where("context_id = ? AND state = ?", contextID, models.TodoStateDeferred).
|
||
|
|
Count(&deferredTodos)
|
||
|
|
|
||
|
|
database.DB.Model(&models.Todo{}).
|
||
|
|
Where("context_id = ? AND state = ?", contextID, models.TodoStatePending).
|
||
|
|
Count(&pendingTodos)
|
||
|
|
|
||
|
|
stats["context"] = context
|
||
|
|
stats["active_todos"] = activeTodos
|
||
|
|
stats["completed_todos"] = completedTodos
|
||
|
|
stats["deferred_todos"] = deferredTodos
|
||
|
|
stats["pending_todos"] = pendingTodos
|
||
|
|
stats["total_todos"] = activeTodos + completedTodos + deferredTodos + pendingTodos
|
||
|
|
|
||
|
|
return stats, nil
|
||
|
|
}
|