diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 842adf9..f27f611 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -102,6 +102,7 @@ export const App: React.FC = () => {
tasksOpenCount: 0,
tasksDoneCount: 0,
docsCount: 0,
+ docsTodosOpenCount: 0,
listsCount: 0,
discardedCount: 0,
});
@@ -478,18 +479,14 @@ export const App: React.FC = () => {
className={`tab-btn ${activeTab === 'triage' ? 'tab-btn-active' : ''}`}
>
- Triage
- {stats.inboxCount > 0 && (
-
- {stats.inboxCount}
-
- )}
+ 0 ? 'pr-2' : ''}`}>
+ Triage
+ {stats.inboxCount > 0 && (
+
+ {stats.inboxCount}
+
+ )}
+
diff --git a/frontend/src/features/workspace/WorkspaceView.tsx b/frontend/src/features/workspace/WorkspaceView.tsx
index 486345f..2c26d0a 100644
--- a/frontend/src/features/workspace/WorkspaceView.tsx
+++ b/frontend/src/features/workspace/WorkspaceView.tsx
@@ -260,6 +260,7 @@ export const WorkspaceView: React.FC = ({
}
: prev
);
+ onStatsChange();
} catch (e: any) {
if (e.message && e.message.includes('423')) {
api.getDocLock(docToSave.id).then((res) => {
@@ -2162,9 +2163,15 @@ export const WorkspaceView: React.FC = ({
type="checkbox"
disabled={isReadOnly}
checked={!!block.checked}
- onChange={(e) =>
- !isReadOnly && updateBlock(block.id, { checked: e.target.checked })
- }
+ onChange={(e) => {
+ if (!isReadOnly && selectedDoc) {
+ const nextBlocks = selectedDoc.blocks.map((b) =>
+ b.id === block.id ? { ...b, checked: e.target.checked } : b
+ );
+ persistDocument({ ...selectedDoc, blocks: nextBlocks }, true);
+ onStatsChange();
+ }
+ }}
className={`w-4 h-4 checkbox-square flex-shrink-0 ${
isReadOnly ? 'cursor-not-allowed opacity-60' : 'cursor-pointer'
}`}
diff --git a/frontend/src/lib/types.ts b/frontend/src/lib/types.ts
index 6e2da2b..ea5ebc5 100644
--- a/frontend/src/lib/types.ts
+++ b/frontend/src/lib/types.ts
@@ -233,6 +233,7 @@ export interface DashboardStats {
tasksOpenCount: number;
tasksDoneCount: number;
docsCount: number;
+ docsTodosOpenCount?: number;
listsCount: number;
discardedCount: number;
}
diff --git a/internal/domain/models.go b/internal/domain/models.go
index ce2563b..5967949 100644
--- a/internal/domain/models.go
+++ b/internal/domain/models.go
@@ -330,12 +330,13 @@ type WSMessage struct {
}
type DashboardStats struct {
- InboxCount int `json:"inboxCount"`
- TasksOpenCount int `json:"tasksOpenCount"`
- TasksDoneCount int `json:"tasksDoneCount"`
- DocsCount int `json:"docsCount"`
- ListsCount int `json:"listsCount"`
- DiscardedCount int `json:"discardedCount"`
+ InboxCount int `json:"inboxCount"`
+ TasksOpenCount int `json:"tasksOpenCount"`
+ TasksDoneCount int `json:"tasksDoneCount"`
+ DocsCount int `json:"docsCount"`
+ DocsTodosOpenCount int `json:"docsTodosOpenCount"`
+ ListsCount int `json:"listsCount"`
+ DiscardedCount int `json:"discardedCount"`
}
type FullExport struct {
diff --git a/internal/storage/sqlite.go b/internal/storage/sqlite.go
index b696e2e..29f885f 100644
--- a/internal/storage/sqlite.go
+++ b/internal/storage/sqlite.go
@@ -1910,6 +1910,31 @@ func (s *Store) GetDashboardStats(userID string) (*domain.DashboardStats, error)
_ = 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(`SELECT COUNT(*) FROM lists l JOIN space_members sm ON sm.space_id = l.space_id WHERE sm.user_id = ?`, userID).Scan(&st.ListsCount)
+
+ // Count open (uncompleted) To-Dos across accessible workspace documents (owned or shared)
+ docRows, err := s.db.Query(`
+ SELECT blocks_json FROM documents
+ WHERE (user_id = ? OR id IN (SELECT doc_id FROM doc_shares WHERE user_id = ?))
+ AND is_archived = 0`,
+ userID, userID,
+ )
+ if err == nil {
+ for docRows.Next() {
+ var bj string
+ if err := docRows.Scan(&bj); err == nil && bj != "" {
+ var blocks []domain.DocumentBlock
+ if err := json.Unmarshal([]byte(bj), &blocks); err == nil {
+ for _, b := range blocks {
+ if b.Type == "todo" && !b.Checked {
+ st.DocsTodosOpenCount++
+ }
+ }
+ }
+ }
+ }
+ _ = docRows.Close()
+ }
+
return st, nil
}