feat(ui): one CountBadge for all counters; counters on tab corners
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 2m30s
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 2m30s
- CountBadge component (accent / muted, inline / corner, 99+, showZero); used in the nav bar, task list and list tabs, admin organizations and numeric badges of the mobile menu; old badge-count removed - Counters sit on the top right corner of tabs; "Alle Aufgaben" gets one (desktop tab and mobile menu) - Open/done task counters include tasks in shared lists, matching what "Alle Aufgaben" shows (nav counter was too low) - Theme template derives --accent-dim/--accent-border from the accent - Docs: ui-patterns counters section Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
a354f3d008
commit
a62b204e14
12 files changed
+167
-55
No files matched your search
@@ -4080,8 +4080,10 @@ func (s *Store) ValidateAPIKeyAndGetUser(rawKey string) (*domain.User, error) {
|
||||
func (s *Store) GetDashboardStats(userID string) (*domain.DashboardStats, error) {
|
||||
st := &domain.DashboardStats{}
|
||||
_ = s.db.QueryRow(`SELECT COUNT(*) FROM thoughts WHERE user_id = ? AND status = 'inbox'`, userID).Scan(&st.InboxCount)
|
||||
_ = s.db.QueryRow(`SELECT COUNT(*) FROM thoughts WHERE user_id = ? AND (status = 'kept' OR (status = 'converted' AND converted_type = 'task')) AND is_completed = 0`, userID).Scan(&st.TasksOpenCount)
|
||||
_ = s.db.QueryRow(`SELECT COUNT(*) FROM thoughts WHERE user_id = ? AND (status = 'kept' OR (status = 'converted' AND converted_type = 'task')) AND is_completed = 1`, userID).Scan(&st.TasksDoneCount)
|
||||
// Same tasks as GetTasks without a list ("Alle Aufgaben"): own ones plus those in shared task lists
|
||||
taskScope := `(user_id = ? OR task_list_id IN (` + s.accessibleTaskListIDsQuery() + `)) AND (status = 'kept' OR (status = 'converted' AND converted_type = 'task'))`
|
||||
_ = s.db.QueryRow(`SELECT COUNT(*) FROM thoughts WHERE `+taskScope+` AND is_completed = 0`, userID, userID, userID, userID).Scan(&st.TasksOpenCount)
|
||||
_ = s.db.QueryRow(`SELECT COUNT(*) FROM thoughts WHERE `+taskScope+` AND is_completed = 1`, userID, userID, userID, userID).Scan(&st.TasksDoneCount)
|
||||
_ = s.db.QueryRow(`SELECT COUNT(*) FROM documents WHERE user_id = ? AND is_archived = 0`, userID).Scan(&st.DocsCount)
|
||||
_ = s.db.QueryRow(`SELECT COUNT(*) FROM thoughts WHERE user_id = ? AND status = 'discarded'`, userID).Scan(&st.DiscardedCount)
|
||||
_ = s.db.QueryRow(`
|
||||
|
||||
@@ -120,3 +120,45 @@ func assertAssignees(t *testing.T, store *Store, viewerID, taskID string, want .
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The open-task counter (nav badge, "Alle Aufgaben") counts what "Alle Aufgaben" shows,
|
||||
// including tasks others created in a shared list
|
||||
func TestDashboardCountsTasksOfSharedLists(t *testing.T) {
|
||||
store, err := NewStore(filepath.Join(t.TempDir(), "stats.db"))
|
||||
if err != nil {
|
||||
t.Fatalf("NewStore failed: %v", err)
|
||||
}
|
||||
defer store.Close()
|
||||
openSharing(t, store)
|
||||
owner, _ := store.CreateUser("owner@example.com", "hash", "Owner", "member")
|
||||
member, _ := store.CreateUser("member@example.com", "hash", "Member", "member")
|
||||
tl, _ := store.CreateTaskList(owner.ID, "", "", "Haushalt", "🏠", "")
|
||||
if _, err := store.AddTaskListShare(tl.ID, "user", member.ID, "editor", owner.ID); err != nil {
|
||||
t.Fatalf("share: %v", err)
|
||||
}
|
||||
for i, title := range []string{"Bad putzen", "Müll raus"} {
|
||||
th, _ := store.CreateThoughtWithDetails(owner.ID, title, "normal", "manual", "", tl.ID, "todo", "")
|
||||
_, _ = store.UpdateThoughtTriage(owner.ID, th.ID, "keep", "task", "", "", tl.ID)
|
||||
if i == 1 {
|
||||
done := true
|
||||
_, _ = store.UpdateThoughtDetails(owner.ID, th.ID, nil, &done, nil, nil, nil, nil, nil, nil, nil)
|
||||
}
|
||||
}
|
||||
own, _ := store.CreateThoughtWithDetails(member.ID, "Eigene Aufgabe", "normal", "manual", "", "", "todo", "")
|
||||
_, _ = store.UpdateThoughtTriage(member.ID, own.ID, "keep", "task", "", "", "")
|
||||
|
||||
st, err := store.GetDashboardStats(member.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetDashboardStats: %v", err)
|
||||
}
|
||||
tasks, _ := store.GetTasks(member.ID, "")
|
||||
open := 0
|
||||
for _, task := range tasks {
|
||||
if !task.IsCompleted {
|
||||
open++
|
||||
}
|
||||
}
|
||||
if st.TasksOpenCount != 2 || st.TasksOpenCount != open || st.TasksDoneCount != 1 {
|
||||
t.Fatalf("expected 2 open / 1 done matching GetTasks (%d open), got %d / %d", open, st.TasksOpenCount, st.TasksDoneCount)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user