fix(step16.1): review fixes — acceptance, filters, sorting, export

- Remove dead acceptSuggestion, unify into refreshAfterSuggestion()
- Journal: nodeID picker, includeChildren only with selected node
- Journal: billable/approximate filters (all/yes/no selects)
- Summary: ByDay sorted by date desc, ByNode by minutes desc
- CSV: proper encoding/csv writer (was manual fmt.Sprintf)
- Markdown: escape pipes and newlines via escMD()
- After suggestion: refresh suggestions + count + worklog + journal
- Add GetNodeTitle binding
- i18n: common.all/no/date/search
This commit is contained in:
2026-06-03 10:30:48 +08:00
parent c25e75f839
commit 5732264fc5
8 changed files with 143 additions and 58 deletions
+8
View File
@@ -81,6 +81,14 @@ func (a *App) GetNodeDetail(nodeID string) (*NodeDTO, error) {
return &dto, nil
}
func (a *App) GetNodeTitle(nodeID string) (string, error) {
n, err := a.nodes.GetActive(nodeID)
if err != nil {
return "", err
}
return n.Title, nil
}
func (a *App) CreateNodeFromTemplate(parentID, title, templateID string) (*NodeDTO, error) {
tmpl, ok := a.templates.Get(templateID)
if !ok {
+28 -24
View File
@@ -36,13 +36,8 @@ func (a *App) CreateWorklogFull(nodeID, summary, details, date string, minutes i
// --- report bindings ---
func (a *App) ListWorklogReport(dateFrom, dateTo, nodeID string, includeChildren bool) ([]worklog.ReportRow, error) {
f := worklog.ReportFilter{
DateFrom: dateFrom,
DateTo: dateTo,
NodeID: nodeID,
IncludeChildren: includeChildren,
}
func (a *App) ListWorklogReport(dateFrom, dateTo, nodeID string, includeChildren bool, billableFilter, approxFilter string) ([]worklog.ReportRow, error) {
f := buildWorklogFilter(dateFrom, dateTo, nodeID, includeChildren, billableFilter, approxFilter)
rows, err := a.worklog.ListReport(f)
if err != nil {
return nil, err
@@ -51,34 +46,43 @@ func (a *App) ListWorklogReport(dateFrom, dateTo, nodeID string, includeChildren
return rows, nil
}
func (a *App) WorklogReportSummary(dateFrom, dateTo, nodeID string, includeChildren bool) (*worklog.ReportSummary, error) {
f := worklog.ReportFilter{
DateFrom: dateFrom,
DateTo: dateTo,
NodeID: nodeID,
IncludeChildren: includeChildren,
}
func (a *App) WorklogReportSummary(dateFrom, dateTo, nodeID string, includeChildren bool, billableFilter, approxFilter string) (*worklog.ReportSummary, error) {
f := buildWorklogFilter(dateFrom, dateTo, nodeID, includeChildren, billableFilter, approxFilter)
return a.worklog.Summary(f)
}
func (a *App) ExportWorklogCSV(dateFrom, dateTo, nodeID string, includeChildren bool) (string, error) {
f := worklog.ReportFilter{
DateFrom: dateFrom,
DateTo: dateTo,
NodeID: nodeID,
IncludeChildren: includeChildren,
}
func (a *App) ExportWorklogCSV(dateFrom, dateTo, nodeID string, includeChildren bool, billableFilter, approxFilter string) (string, error) {
f := buildWorklogFilter(dateFrom, dateTo, nodeID, includeChildren, billableFilter, approxFilter)
return a.worklog.ExportCSV(f)
}
func (a *App) ExportWorklogMarkdown(dateFrom, dateTo, nodeID string, includeChildren bool) (string, error) {
f := worklog.ReportFilter{
func (a *App) ExportWorklogMarkdown(dateFrom, dateTo, nodeID string, includeChildren bool, billableFilter, approxFilter string) (string, error) {
f := buildWorklogFilter(dateFrom, dateTo, nodeID, includeChildren, billableFilter, approxFilter)
return a.worklog.ExportMarkdown(f)
}
func boolPtr(s string) *bool {
switch s {
case "yes":
v := true
return &v
case "no":
v := false
return &v
default:
return nil
}
}
func buildWorklogFilter(dateFrom, dateTo, nodeID string, includeChildren bool, billableFilter, approxFilter string) worklog.ReportFilter {
return worklog.ReportFilter{
DateFrom: dateFrom,
DateTo: dateTo,
NodeID: nodeID,
IncludeChildren: includeChildren,
Billable: boolPtr(billableFilter),
Approximate: boolPtr(approxFilter),
}
return a.worklog.ExportMarkdown(f)
}
// --- helpers ---
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -16,7 +16,7 @@
background: #13131f;
}
</style>
<script type="module" crossorigin src="/assets/main-wlKdkTmp.js"></script>
<script type="module" crossorigin src="/assets/main-DU1CFPIY.js"></script>
<link rel="stylesheet" crossorigin href="/assets/main-B4G76NhT.css">
</head>
<body>