rozšíření-completeTask

This commit is contained in:
kankys committed 2026-08-09 10:33:11 +02:00
1 parent 6230b7895d
commit b447cc8de7
6 files changed
+178 -5

No files matched your search

+99 -3
View File
@@ -54,6 +54,10 @@ var (
categoryWork = lipgloss.NewStyle().
Foreground(lipgloss.Color("#9B59B6")) // Fialová
completedStyle = lipgloss.NewStyle().
Strikethrough(true).
Foreground(lipgloss.Color("#95A5A6")) // Šedá pro splněné úkoly
)
// Priority levels
@@ -116,6 +120,7 @@ type Todo struct {
Tags []string `json:"tags"`
CreatedAt time.Time `json:"created_at"`
ModifiedAt time.Time `json:"modified_at"`
Completed bool `json:"completed"`
}
type model struct {
@@ -161,6 +166,7 @@ func NewModel() model {
newTodoPriority: PriorityMedium,
newTodoCategory: CategoryPersonal,
}
m.sortTodos()
m.applyFilter()
m.updatePlaceholders()
return m
@@ -170,6 +176,28 @@ func (m model) Init() tea.Cmd {
return tea.Batch(textinput.Blink, tea.EnterAltScreen)
}
func (m *model) sortTodos() {
// Sort todos: completed ones always last, then by ID (newest first)
for i := 0; i < len(m.todos)-1; i++ {
for j := i + 1; j < len(m.todos); j++ {
// If i is completed and j is not, swap
if m.todos[i].Completed && !m.todos[j].Completed {
m.todos[i], m.todos[j] = m.todos[j], m.todos[i]
} else if !m.todos[i].Completed && !m.todos[j].Completed {
// Both not completed, sort by ID (newest first)
if m.todos[i].ID < m.todos[j].ID {
m.todos[i], m.todos[j] = m.todos[j], m.todos[i]
}
} else if m.todos[i].Completed && m.todos[j].Completed {
// Both completed, sort by ID (newest first)
if m.todos[i].ID < m.todos[j].ID {
m.todos[i], m.todos[j] = m.todos[j], m.todos[i]
}
}
}
}
}
func (m *model) applyFilter() {
if m.filterText == "" && m.currentCategoryFilter == -1 {
m.filteredTodos = m.todos
@@ -196,6 +224,25 @@ func (m *model) applyFilter() {
}
}
}
// Sort filtered todos: completed ones always last
for i := 0; i < len(m.filteredTodos)-1; i++ {
for j := i + 1; j < len(m.filteredTodos); j++ {
// If i is completed and j is not, swap
if m.filteredTodos[i].Completed && !m.filteredTodos[j].Completed {
m.filteredTodos[i], m.filteredTodos[j] = m.filteredTodos[j], m.filteredTodos[i]
} else if !m.filteredTodos[i].Completed && !m.filteredTodos[j].Completed {
// Both not completed, sort by ID (newest first)
if m.filteredTodos[i].ID < m.filteredTodos[j].ID {
m.filteredTodos[i], m.filteredTodos[j] = m.filteredTodos[j], m.filteredTodos[i]
}
} else if m.filteredTodos[i].Completed && m.filteredTodos[j].Completed {
// Both completed, sort by ID (newest first)
if m.filteredTodos[i].ID < m.filteredTodos[j].ID {
m.filteredTodos[i], m.filteredTodos[j] = m.filteredTodos[j], m.filteredTodos[i]
}
}
}
}
if m.cursor >= len(m.filteredTodos) {
m.cursor = 0
if len(m.filteredTodos) > 0 {
@@ -492,6 +539,10 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
m.selectedTodoID = m.filteredTodos[m.cursor].ID
m.focusIndex = 0
}
case "t":
if m.state == StateViewTodos && len(m.filteredTodos) > 0 {
m.toggleTodoCompleted()
}
case "+":
if m.state == StateAddTodo || m.state == StateEditTodo {
// Cycle priority up
@@ -735,7 +786,12 @@ func (m model) View() string {
if m.cursor == i {
cursor = selectedStyle.Render("> ")
}
listBuilder.WriteString(fmt.Sprintf("%s%s %s %s%s\n", cursor, priorityIcon, categoryIcon, t.Title, tagStr))
title := t.Title
if t.Completed {
title = completedStyle.Render(t.Title)
}
listBuilder.WriteString(fmt.Sprintf("%s%s %s %s%s\n", cursor, priorityIcon, categoryIcon, title, tagStr))
}
}
@@ -743,8 +799,8 @@ func (m model) View() string {
m.viewport.SetContent(listBuilder.String())
}
b.WriteString(fmt.Sprintf("%s | %s | %s | %s | %s | %s | %s | %s\n\n",
tr.MenuAdd, tr.MenuEdit, tr.MenuExport, tr.MenuDelete, tr.MenuSearch, tr.MenuBrowse, tr.MenuFilter, tr.MenuLang))
b.WriteString(fmt.Sprintf("%s | %s | %s | %s | %s | %s | %s | %s | %s\n\n",
tr.MenuAdd, tr.MenuEdit, tr.MenuExport, tr.MenuDelete, tr.MenuSearch, tr.MenuBrowse, tr.MenuFilter, tr.MenuToggleComplete, tr.MenuLang))
b.WriteString(tr.ListTitle + filterInfo + categoryInfo + "\n")
if m.ready {
@@ -911,8 +967,10 @@ func (m *model) handleAddTodo() {
Tags: tags,
CreatedAt: now,
ModifiedAt: now,
Completed: false,
})
saveTodos(m.todos)
m.sortTodos()
m.applyFilter()
m.statusMsg = tr.StatusSaved
m.state = StateViewTodos
@@ -940,6 +998,7 @@ func (m *model) handleEditTodo() {
}
}
saveTodos(m.todos)
m.sortTodos()
m.applyFilter()
m.statusMsg = tr.StatusEdited
m.state = StateViewTodos
@@ -958,9 +1017,46 @@ func (m *model) handleDeleteTodo() {
m.todos = append(m.todos[:todoIndex], m.todos[todoIndex+1:]...)
saveTodos(m.todos)
}
m.sortTodos()
m.applyFilter()
}
func (m *model) toggleTodoCompleted() {
tr := Translations[m.lang]
selectedID := m.filteredTodos[m.cursor].ID
for i, t := range m.todos {
if t.ID == selectedID {
m.todos[i].Completed = !m.todos[i].Completed
m.todos[i].ModifiedAt = time.Now()
break
}
}
saveTodos(m.todos)
m.sortTodos()
m.applyFilter()
// Find the toggled todo in filtered list to update cursor
for i, t := range m.filteredTodos {
if t.ID == selectedID {
m.cursor = i
break
}
}
// Find the todo to check its completed status for status message
for _, t := range m.todos {
if t.ID == selectedID {
if t.Completed {
m.statusMsg = tr.StatusCompleted
} else {
m.statusMsg = tr.StatusUncompleted
}
break
}
}
}
func exportTodo(todos []Todo, id int64) {
for _, todo := range todos {
if todo.ID == id {