package common import ( "encoding/json" "fmt" "hash/fnv" "slices" "strings" "time" "github.com/charmbracelet/lipgloss" "github.com/google/uuid" ) func (todo *Todo) Store(store *BoltStore, user string) error { isOwner := todo.Owner == user isShared := slices.Contains(todo.SharedWith, user) if !isOwner && !isShared { return fmt.Errorf("unauthorized user") } if todo.Id == "" { todo.Id = uuid.New().String() } todo.LastModified = time.Now().UTC() todoJson, err := json.Marshal(todo) if err != nil { return err } return store.SaveValueToBucket(todo.Owner, todo.Id, string(todoJson)) } func (todoRequest StoreTodoRequest) Store(store *BoltStore, user string) error { todo := Todo{ Id: uuid.New().String(), Name: todoRequest.Name, Description: todoRequest.Description, Status: todoRequest.Status, Owner: user, SharedWith: todoRequest.SharedWith, LastModified: time.Now().UTC(), } todoJson, err := json.Marshal(todo) if err != nil { return err } return store.SaveValueToBucket(user, todo.Id, string(todoJson)) } func (todoList TodoList) FindByName(name string) (Todo, bool) { for _, todo := range todoList.Todos { if todo.Name == name { return todo, true } } return Todo{}, false } func (todo Todo) PrintIndexed(index int) { if todo.Deleted { return } var statusColor string // Select color based on the status (case-insensitive) switch strings.ToLower(todo.Status) { case Done: statusColor = ColorGreen case WIP: statusColor = ColorYellow case Pending, NotStarted: statusColor = ColorBlue case Blocked, Failed: statusColor = ColorRed default: statusColor = ColorWhite // No color for unknown statuses } var renderedLabels []string for _, l := range todo.Labels { h := fnv.New32a() h.Write([]byte(l)) colorID := fmt.Sprint(1 + (h.Sum32() % 6)) style := lipgloss.NewStyle(). Bold(true). Foreground(lipgloss.Color("0")). Background(lipgloss.Color(colorID)). Padding(0, 1). MarginRight(1). Border(lipgloss.RoundedBorder()). BorderForeground(lipgloss.Color(colorID)). BorderBackground(lipgloss.Color("")) renderedLabels = append(renderedLabels, style.Render(l)) } fmt.Println(lipgloss.JoinHorizontal(lipgloss.Top, renderedLabels...)) fmt.Printf("%s%d)%s %s%-20s%s", ColorCyan, index, ColorReset, ColorWhite, todo.Name, ColorReset) fmt.Printf(" %s %s%s\n", statusColor, strings.ToUpper(todo.Status), ColorReset) // 5. Description (Indented and slightly dimmer if possible, or standard) fmt.Printf(" %s\n", todo.Description) uuidStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("240")) lastMod := todo.LastModified.Format("2006-01-02 15:04") metaLine := fmt.Sprintf(" Last Modified: %s • ID: %s", lastMod, todo.Id) if len(todo.SharedWith) > 0 { metaLine = fmt.Sprintf("%s • Shared with: %s", metaLine, strings.Join(todo.SharedWith, ", ")) } fmt.Println(uuidStyle.Render(metaLine)) fmt.Printf(" %s----------------------------%s\n", ColorGray, ColorReset) } func (t Todo) IsEqual(other Todo) bool { return t.Id == other.Id && t.Name == other.Name && t.Description == other.Description && t.Status == other.Status && t.Owner == other.Owner && slices.Equal(t.SharedWith, other.SharedWith) && t.Deleted == other.Deleted } func (t Todo) IsEqualIgnoringStatus(other Todo) bool { return t.Id == other.Id && t.Name == other.Name && t.Description == other.Description && t.Owner == other.Owner && slices.Equal(t.SharedWith, other.SharedWith) && t.Deleted == other.Deleted }