feat: journal UX overhaul — picker, export dialog, events, readability

- Sidebar i18n: added missing nav.journal to backend ru.json
- Export: SaveWorklogReport binding with native SaveFileDialog + os.WriteFile
- Filter: better IncludeChildren label with disabled tooltip
- Filter: renamed billable→К оплате, approximate→Тип времени with hints
- worklog_entry_events table (migration 013) linking entries to activity events
- Suggestion: EventIDs + Events details, expandable cards with timestamps
- Journal rows: expandable with details, source, linked events
- Contrast: improved readability for dates, timestamps, hover states
- i18n: added worklog.*, journal.*, suggest.* keys to ru.js/en.js
This commit is contained in:
2026-06-03 11:24:59 +08:00
parent d34100e2ed
commit 1472bb3e6f
13 changed files with 402 additions and 98 deletions
+23 -10
View File
@@ -7,16 +7,29 @@ const (
ConfidenceHigh = "high"
)
// SuggestionDetail is a lightweight event summary for suggestion display.
type SuggestionDetail struct {
ID string `json:"id"`
EventType string `json:"eventType"`
TargetType string `json:"targetType"`
TargetID string `json:"targetId"`
Title string `json:"title"`
CreatedAt string `json:"createdAt"`
NodeID string `json:"nodeId"`
}
// Suggestion represents a suggested worklog entry derived from today's activity.
type Suggestion struct {
NodeID string `json:"nodeId"`
NodeTitle string `json:"nodeTitle"`
Summary string `json:"summary"`
SuggestedMin int `json:"suggestedMin"`
EventCount int `json:"eventCount"`
NodeKind string `json:"nodeKind"`
Confidence string `json:"confidence"`
ConfidenceReason string `json:"confidenceReason"`
Hidden bool `json:"hidden"`
TimeSpreadMin int `json:"timeSpreadMin"` // minutes between first and last event
NodeID string `json:"nodeId"`
NodeTitle string `json:"nodeTitle"`
Summary string `json:"summary"`
SuggestedMin int `json:"suggestedMin"`
EventCount int `json:"eventCount"`
NodeKind string `json:"nodeKind"`
Confidence string `json:"confidence"`
ConfidenceReason string `json:"confidenceReason"`
Hidden bool `json:"hidden"`
TimeSpreadMin int `json:"timeSpreadMin"` // minutes between first and last event
EventIDs []string `json:"eventIds"`
Events []SuggestionDetail `json:"events,omitempty"`
}
@@ -0,0 +1,12 @@
package storage
// migration013 — worklog_entry_events table linking entries to activity events.
const migration013 = `
CREATE TABLE IF NOT EXISTS worklog_entry_events (
entry_id TEXT NOT NULL REFERENCES worklog_entries(id) ON DELETE CASCADE,
event_id TEXT NOT NULL,
PRIMARY KEY (entry_id, event_id)
);
CREATE INDEX IF NOT EXISTS idx_wle_events ON worklog_entry_events(event_id);
`
+1
View File
@@ -69,6 +69,7 @@ var migrationFiles = map[int]string{
10: migration010,
11: migration011,
12: migration012,
13: migration013,
}
func (db *DB) runInitialSchema() error {
+11
View File
@@ -7,6 +7,17 @@
"nav.recipes": "Recipes",
"nav.documents": "Documents",
"nav.archive": "Archive",
"nav.journal": "Work Log",
"nav.sections": "Sections",
"nav.cases": "Cases",
"nav.noCases": "No cases",
"nav.sync": "Sync",
"nav.syncSettings": "Sync settings",
"nav.syncNow": "Sync now",
"nav.selectPrompt": "Select a section or case",
"nav.brand": "Verstak",
"nav.createNode": "Create node",
"nav.moveToRoot": "Move to root",
"tab.overview": "Overview",
"tab.notes": "Notes",
+3
View File
@@ -10,6 +10,7 @@
"nav.sections": "Разделы",
"nav.cases": "Дела",
"nav.noCases": "Нет дел",
"nav.journal": "Журнал",
"nav.sync": "Синхронизация",
"nav.syncSettings": "Настройки синхронизации",
"nav.syncNow": "Синхронизировать",
@@ -391,6 +392,8 @@
"nav.noNodes": "Нет узлов",
"nav.openFolder": "Открыть папку",
"nav.createInside": "Создать внутри",
"nav.createNode": "Создать элемент",
"nav.moveToRoot": "Переместить в корень",
"template.folder": "Папка",
"template.project": "Проект",