@nightkatana/kronosys-app 1.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (179) hide show
  1. package/README.md +81 -0
  2. package/app/api/action/route.ts +16 -0
  3. package/app/api/backup/route.ts +84 -0
  4. package/app/api/health/route.ts +22 -0
  5. package/app/api/state/route.ts +27 -0
  6. package/app/apple-icon.png +0 -0
  7. package/app/changelog/page.tsx +122 -0
  8. package/app/globals.css +210 -0
  9. package/app/guide/layout.tsx +11 -0
  10. package/app/guide/page.tsx +278 -0
  11. package/app/icon.png +0 -0
  12. package/app/layout.tsx +77 -0
  13. package/app/licenses/layout.tsx +11 -0
  14. package/app/licenses/page.tsx +246 -0
  15. package/app/manifest.ts +32 -0
  16. package/app/page.tsx +1610 -0
  17. package/app/reporting/page.tsx +2943 -0
  18. package/app/settings/layout.tsx +10 -0
  19. package/app/settings/page.tsx +3518 -0
  20. package/bin/kronosys.mjs +46 -0
  21. package/components/KronosysPackageVersionProvider.tsx +19 -0
  22. package/components/KronosysPayloadProvider.tsx +109 -0
  23. package/components/PwaRegister.tsx +25 -0
  24. package/components/SiteLegalFooter.tsx +21 -0
  25. package/components/ThemeProvider.tsx +78 -0
  26. package/components/dashboard/AppShellLiveSessionDrawer.tsx +394 -0
  27. package/components/dashboard/AppShellRouteNav.tsx +131 -0
  28. package/components/dashboard/AppVersionStamp.tsx +16 -0
  29. package/components/dashboard/DashboardCollapsibleSection.tsx +57 -0
  30. package/components/dashboard/DashboardColumnHintsBanner.tsx +159 -0
  31. package/components/dashboard/DashboardCommandCenter.tsx +470 -0
  32. package/components/dashboard/DashboardLangGateModal.tsx +118 -0
  33. package/components/dashboard/DashboardLoadingOverlay.tsx +42 -0
  34. package/components/dashboard/DashboardSimpleModal.tsx +337 -0
  35. package/components/dashboard/DashboardSuspenseFallback.tsx +52 -0
  36. package/components/dashboard/DashboardToastProvider.tsx +64 -0
  37. package/components/dashboard/DashboardTour.tsx +435 -0
  38. package/components/dashboard/DeferredDescriptionPopoverWrap.tsx +39 -0
  39. package/components/dashboard/DeleteSessionModal.tsx +130 -0
  40. package/components/dashboard/DescriptionTooltipPortaled.tsx +31 -0
  41. package/components/dashboard/GitIdentityQuickSetupModal.tsx +211 -0
  42. package/components/dashboard/HeaderIntegrationBadges.tsx +69 -0
  43. package/components/dashboard/InlineMetricHelpTrigger.tsx +102 -0
  44. package/components/dashboard/IssuePickerModal.tsx +168 -0
  45. package/components/dashboard/KronoFocusPanel.tsx +834 -0
  46. package/components/dashboard/KronosysDatetimePopoverField.tsx +357 -0
  47. package/components/dashboard/KronosysTimePopoverField.tsx +233 -0
  48. package/components/dashboard/LanguageMenu.tsx +123 -0
  49. package/components/dashboard/MongoMirrorSyncLine.tsx +57 -0
  50. package/components/dashboard/NewSessionScopeModal.tsx +410 -0
  51. package/components/dashboard/PageRefreshButton.tsx +130 -0
  52. package/components/dashboard/PlainHelpPopover.tsx +97 -0
  53. package/components/dashboard/ReportingPageToc.tsx +68 -0
  54. package/components/dashboard/ReportingTour.tsx +342 -0
  55. package/components/dashboard/SavedProjectPicker.tsx +92 -0
  56. package/components/dashboard/SavedTagPicker.tsx +115 -0
  57. package/components/dashboard/ScrollToTopFab.tsx +41 -0
  58. package/components/dashboard/SelectedSessionSidebarBlock.tsx +630 -0
  59. package/components/dashboard/SessionEndReasonEditor.tsx +114 -0
  60. package/components/dashboard/SessionListPanel.tsx +320 -0
  61. package/components/dashboard/SessionLocMetricsSection.tsx +128 -0
  62. package/components/dashboard/SettingsTagsProjectsSection.tsx +993 -0
  63. package/components/dashboard/SettingsTour.tsx +332 -0
  64. package/components/dashboard/TagPills.tsx +149 -0
  65. package/components/dashboard/TagsHelpTrigger.tsx +84 -0
  66. package/components/dashboard/TaskFocusPanel.tsx +1261 -0
  67. package/components/dashboard/TaskSessionLiveCard.tsx +832 -0
  68. package/components/dashboard/TaskSubtasksBlock.tsx +748 -0
  69. package/components/dashboard/ThemeToggle.test.tsx +26 -0
  70. package/components/dashboard/ThemeToggle.tsx +36 -0
  71. package/components/dashboard/UserGuideBodyText.tsx +62 -0
  72. package/components/dashboard/WorkspaceGitRepoCard.tsx +191 -0
  73. package/components/dashboard/taskFieldStyles.ts +139 -0
  74. package/components/dashboard/useAnchoredFloatingPortalStyle.ts +71 -0
  75. package/components/dashboard/useDescriptionPopoverAfterMs.ts +220 -0
  76. package/components/dashboard/useKronoFocusLiveSeconds.ts +36 -0
  77. package/components/dashboard/useSmoothStopwatchMs.ts +25 -0
  78. package/lib/appShellHeaderClasses.ts +12 -0
  79. package/lib/backupCsvExport.test.ts +149 -0
  80. package/lib/backupCsvExport.ts +392 -0
  81. package/lib/changelogCopy.ts +34 -0
  82. package/lib/concurrentTaskStartPreference.ts +29 -0
  83. package/lib/dashboardClockFormat.ts +13 -0
  84. package/lib/dashboardColumnChrome.ts +3 -0
  85. package/lib/dashboardColumnHintsStorage.ts +57 -0
  86. package/lib/dashboardCopy.ts +1831 -0
  87. package/lib/dashboardDetachedUrlHintStorage.ts +24 -0
  88. package/lib/dashboardGitIdentityBannerStorage.ts +36 -0
  89. package/lib/dashboardLangStorage.ts +72 -0
  90. package/lib/dashboardQuickSearch.ts +476 -0
  91. package/lib/dashboardQuickSearchQuery.test.ts +63 -0
  92. package/lib/dashboardQuickSearchQuery.ts +179 -0
  93. package/lib/dashboardSessionNav.ts +33 -0
  94. package/lib/dashboardShortcuts.ts +268 -0
  95. package/lib/dashboardTimeZone.ts +91 -0
  96. package/lib/dashboardTourStorage.ts +68 -0
  97. package/lib/dataDir.test.ts +87 -0
  98. package/lib/dataDir.ts +83 -0
  99. package/lib/devDataPreferenceFile.ts +55 -0
  100. package/lib/devDataRuntimeInfo.ts +34 -0
  101. package/lib/formatIsoShort.test.ts +46 -0
  102. package/lib/formatIsoShort.ts +29 -0
  103. package/lib/generatedUserChangelog.ts +34 -0
  104. package/lib/gitlabIssueSearch.ts +8 -0
  105. package/lib/kronoFocusDurationHistory.ts +71 -0
  106. package/lib/kronoFocusRhythm.test.ts +130 -0
  107. package/lib/kronoFocusRhythm.ts +46 -0
  108. package/lib/kronoFocusTimerUrgency.test.ts +74 -0
  109. package/lib/kronoFocusTimerUrgency.ts +24 -0
  110. package/lib/kronosysApi.ts +143 -0
  111. package/lib/legacyEditorPayloadKeys.ts +52 -0
  112. package/lib/legacyKronoFocusStorageKeys.test.ts +29 -0
  113. package/lib/legacyKronoFocusStorageKeys.ts +32 -0
  114. package/lib/licensesCopy.ts +128 -0
  115. package/lib/openPlainTextInNewTab.ts +49 -0
  116. package/lib/readKronosysPackageVersion.ts +10 -0
  117. package/lib/reportingAggregate.test.ts +325 -0
  118. package/lib/reportingAggregate.ts +819 -0
  119. package/lib/reportingDatePresets.ts +41 -0
  120. package/lib/reportingMetricHelp.ts +430 -0
  121. package/lib/reportingNonFinalIndicators.test.ts +157 -0
  122. package/lib/reportingNonFinalIndicators.ts +102 -0
  123. package/lib/reportingStrings.ts +491 -0
  124. package/lib/reportingTagWeekBreakdown.test.ts +141 -0
  125. package/lib/reportingTagWeekBreakdown.ts +181 -0
  126. package/lib/reportingWeekLayout.test.ts +239 -0
  127. package/lib/reportingWeekLayout.ts +313 -0
  128. package/lib/sessionAssiduity.test.ts +25 -0
  129. package/lib/sessionAssiduity.ts +33 -0
  130. package/lib/sessionEndReason.ts +55 -0
  131. package/lib/sessionEndWarnings.test.ts +200 -0
  132. package/lib/sessionEndWarnings.ts +125 -0
  133. package/lib/sessionListMerge.test.ts +101 -0
  134. package/lib/sessionListMerge.ts +70 -0
  135. package/lib/sessionTaskSidebarStats.test.ts +24 -0
  136. package/lib/sessionTaskSidebarStats.ts +54 -0
  137. package/lib/settingsCopy.ts +1276 -0
  138. package/lib/taskParsing.test.ts +153 -0
  139. package/lib/taskParsing.ts +737 -0
  140. package/lib/theme.ts +15 -0
  141. package/lib/translucentButtonClasses.ts +34 -0
  142. package/lib/usageProfile.test.ts +84 -0
  143. package/lib/usageProfile.ts +52 -0
  144. package/lib/userGuideCopy.ts +464 -0
  145. package/lib/workspaceLocDefaults.ts +21 -0
  146. package/next-env.d.ts +6 -0
  147. package/next.config.ts +15 -0
  148. package/package.json +87 -0
  149. package/postcss.config.mjs +12 -0
  150. package/public/apple-icon.png +0 -0
  151. package/public/favicon.ico +0 -0
  152. package/public/file.svg +1 -0
  153. package/public/globe.svg +1 -0
  154. package/public/icon-192.png +0 -0
  155. package/public/icon-512.png +0 -0
  156. package/public/icon.png +0 -0
  157. package/public/next.svg +1 -0
  158. package/public/sw.js +13 -0
  159. package/public/traceback.png +0 -0
  160. package/public/vercel.svg +1 -0
  161. package/public/window.svg +1 -0
  162. package/server/actionDispatch.test.ts +723 -0
  163. package/server/actionDispatch.ts +1476 -0
  164. package/server/actionTaskSession.test.ts +713 -0
  165. package/server/actionTaskSession.ts +717 -0
  166. package/server/db.ts +42 -0
  167. package/server/defaultCfg.ts +87 -0
  168. package/server/gitlabTokenStore.ts +34 -0
  169. package/server/kronoFocusHydrate.test.ts +142 -0
  170. package/server/kronoFocusHydrate.ts +69 -0
  171. package/server/kronoFocusMigrate.test.ts +53 -0
  172. package/server/kronoFocusMigrate.ts +78 -0
  173. package/server/mainTimerHydrate.test.ts +65 -0
  174. package/server/mainTimerHydrate.ts +53 -0
  175. package/server/payloadStore.test.ts +78 -0
  176. package/server/payloadStore.ts +83 -0
  177. package/server/sessionWallHydrate.test.ts +46 -0
  178. package/server/sessionWallHydrate.ts +88 -0
  179. package/tsconfig.json +41 -0
@@ -0,0 +1,102 @@
1
+ import type { KronosysUpdatePayload } from "./kronosysApi";
2
+ import { calendarDateKeyInTimeZone } from "./dashboardTimeZone";
3
+ import {
4
+ UNDATED_KEY,
5
+ collectTasksDeduped,
6
+ dayInRange,
7
+ mergeSessionsFromPayload,
8
+ taskCountsTowardArchivedSessionReporting,
9
+ taskMatchesTags,
10
+ } from "./reportingAggregate";
11
+ import type { LooseSession, LooseTask } from "./reportingAggregate";
12
+
13
+ function taskIncludedInReportingTaskMetrics(session: LooseSession, task: LooseTask): boolean {
14
+ if (session.archived === true) {
15
+ return taskCountsTowardArchivedSessionReporting(task);
16
+ }
17
+ return true;
18
+ }
19
+
20
+ export type ReportingNonFinalFlags = {
21
+ /** Session courante ouverte ou tâches « en cours » visibles dans les agrégats (données encore mouvantes). */
22
+ sessionsNonFinal: boolean;
23
+ /** Au moins une tâche non terminée dans la plage (filtres inclus) avec un @projet. */
24
+ projectsNonFinal: boolean;
25
+ /** Au moins une tâche non terminée dans la plage avec au moins une étiquette #. */
26
+ tagsNonFinal: boolean;
27
+ };
28
+
29
+ function sumActiveTasksAcrossDays(tasksByDayActive: Record<string, number> | undefined): number {
30
+ if (!tasksByDayActive) {
31
+ return 0;
32
+ }
33
+ let s = 0;
34
+ for (const v of Object.values(tasksByDayActive)) {
35
+ s += v ?? 0;
36
+ }
37
+ return s;
38
+ }
39
+
40
+ /**
41
+ * Signaux pour afficher des pastilles « données non finales » (session / projets / étiquettes).
42
+ */
43
+ export function computeReportingNonFinalFlags(
44
+ payload: KronosysUpdatePayload | null,
45
+ tagSet: Set<string>,
46
+ dateFromTrim: string,
47
+ dateToTrim: string,
48
+ tasksByDayActive: Record<string, number> | undefined,
49
+ timeZone: string,
50
+ defaultTagBucketEnabled: boolean = true
51
+ ): ReportingNonFinalFlags {
52
+ const cur = payload?.current as { sessionId?: string; archived?: boolean } | undefined;
53
+ const liveSessionOpen = Boolean(cur?.sessionId?.trim()) && cur?.archived !== true;
54
+ const hasActiveTasksInAgg = sumActiveTasksAcrossDays(tasksByDayActive) > 0;
55
+ /** Session live ou présence de tâches non terminées dans les totaux affichés. */
56
+ const sessionsNonFinal = liveSessionOpen || hasActiveTasksInAgg;
57
+
58
+ let projectsNonFinal = false;
59
+ let tagsNonFinal = false;
60
+ if (!payload) {
61
+ return { sessionsNonFinal, projectsNonFinal, tagsNonFinal };
62
+ }
63
+
64
+ const from = dateFromTrim.trim() || null;
65
+ const to = dateToTrim.trim() || null;
66
+ const unbounded = !from && !to;
67
+ const sessions = mergeSessionsFromPayload(payload);
68
+
69
+ for (const s of sessions) {
70
+ const tasks = collectTasksDeduped(s);
71
+ const matching = tasks.filter((t) => taskMatchesTags(t, tagSet, defaultTagBucketEnabled));
72
+ for (const t of matching) {
73
+ if (!taskIncludedInReportingTaskMetrics(s, t)) {
74
+ continue;
75
+ }
76
+ if (t.isDone === true) {
77
+ continue;
78
+ }
79
+ const taskDay = calendarDateKeyInTimeZone(t.endTime ?? t.startTime, timeZone);
80
+ const bucket = taskDay ?? (unbounded ? UNDATED_KEY : null);
81
+ if (!bucket) {
82
+ continue;
83
+ }
84
+ if (!unbounded && !dayInRange(bucket, from, to)) {
85
+ continue;
86
+ }
87
+ const proj = typeof t.project === "string" ? t.project.trim() : "";
88
+ if (proj) {
89
+ projectsNonFinal = true;
90
+ }
91
+ const tags = t.tags || [];
92
+ if (tags.some((x) => String(x).trim().length > 0)) {
93
+ tagsNonFinal = true;
94
+ }
95
+ if (projectsNonFinal && tagsNonFinal) {
96
+ return { sessionsNonFinal, projectsNonFinal, tagsNonFinal };
97
+ }
98
+ }
99
+ }
100
+
101
+ return { sessionsNonFinal, projectsNonFinal, tagsNonFinal };
102
+ }
@@ -0,0 +1,491 @@
1
+ import type { Lang } from "./dashboardCopy";
2
+ import {
3
+ reportingMetricHelpEn,
4
+ reportingMetricHelpFr,
5
+ type ReportingMetricHelpBlock,
6
+ } from "./reportingMetricHelp";
7
+
8
+ export type ReportingNavStrings = {
9
+ dashboard: string;
10
+ reporting: string;
11
+ settings: string;
12
+ /** Guide d’utilisation in-app (`/guide`). */
13
+ guide: string;
14
+ };
15
+
16
+ export type ReportingStrings = ReportingNavStrings &
17
+ ReportingMetricHelpBlock & {
18
+ title: string;
19
+ subtitle: string;
20
+ /** Règle d’inclusion des tâches des sessions archivées dans les agrégats. */
21
+ archivedSessionsReportingNote: string;
22
+ /**
23
+ * Encart lorsqu’il existe du temps enregistré sur des tâches d’archives exclues des graphiques.
24
+ * Placeholder `{duration}` remplacé par une durée lisible (p. ex. « 6 min »).
25
+ */
26
+ reportingArchivedExcludedAside: string;
27
+ filtersTitle: string;
28
+ dateFrom: string;
29
+ dateTo: string;
30
+ today: string;
31
+ clearDates: string;
32
+ /** Efface la plage de dates et toutes les étiquettes sélectionnées. */
33
+ resetAllFilters: string;
34
+ /** Libellé du groupe de boutons jour / semaine / mois / année. */
35
+ presetPeriodLabel: string;
36
+ presetDay: string;
37
+ presetWeek: string;
38
+ presetMonth: string;
39
+ presetYear: string;
40
+ /** Précision calendrier (ex. semaine lundi–dimanche). */
41
+ presetPeriodHint: string;
42
+ tagsLabel: string;
43
+ tagsHint: string;
44
+ /** aria-label du bouton (?) — portée des filtres dates / étiquettes. */
45
+ filtersHelpAriaLabel: string;
46
+ /** Explication session vs tâche, presets, étiquettes, archives. */
47
+ filtersHelpBody: string;
48
+ /** Légende avant les pastilles vertes « données non finales » (filtres). */
49
+ reportingNonFinalLegend: string;
50
+ /** Pastille : session ou tâches en cours dans la plage. */
51
+ reportingNonFinalSessionsBadge: string;
52
+ /** Pastille : tâches non terminées avec @projet. */
53
+ reportingNonFinalProjectsBadge: string;
54
+ /** Pastille : tâches non terminées avec #étiquettes. */
55
+ reportingNonFinalTagsBadge: string;
56
+ /** Pastille courte à côté des titres de section lorsque dates ou étiquettes filtrent les données. */
57
+ sectionFilteredBadge: string;
58
+ /** Infobulle / précision sur la pastille « filtré ». */
59
+ sectionFilteredBadgeTitle: string;
60
+ summarySessionsInRange: string;
61
+ summaryTaskEvents: string;
62
+ summaryKronoFocusCompleted: string;
63
+ summaryKronoFocusTasksUsed: string;
64
+ summaryKronoFocusCycles: string;
65
+ summaryTaskTimeRecorded: string;
66
+ summarySessionCoding: string;
67
+ summarySessionActive: string;
68
+ summarySessionWallClock: string;
69
+ /** Sessions dont l’hôte a enregistré un début prévu (assiduité). */
70
+ summaryAssiduityWithReference: string;
71
+ /** Nombre de sessions commencées après l’heure de référence. */
72
+ summaryAssiduityLateSessions: string;
73
+ /** Somme des retards (minutes) sur les seules sessions en retard. */
74
+ summaryAssiduityLateTotal: string;
75
+ /** Retard moyen parmi les sessions en retard, ou em dash si aucune. */
76
+ summaryAssiduityAvgLateWhenLate: string;
77
+ chartSessionsPerDay: string;
78
+ chartTasksByStatusPerDay: string;
79
+ chartTaskTimePerDay: string;
80
+ chartSessionWallPerDay: string;
81
+ legendSessions: string;
82
+ legendDone: string;
83
+ legendActive: string;
84
+ tableDay: string;
85
+ tableSessions: string;
86
+ tableDone: string;
87
+ tableActive: string;
88
+ /** Temps enregistré sur les tâches (durationMs), par jour de tâche. */
89
+ tableTaskTime: string;
90
+ /** Temps de codage session (codingMinutesSession), par jour de session. */
91
+ tableSessionCoding: string;
92
+ tableSessionWall: string;
93
+ undatedLabel: string;
94
+ loading: string;
95
+ noRowsInRange: string;
96
+ /** Tableau temps par projet. */
97
+ projectSectionTitle: string;
98
+ /** Sous-titre calendrier hebdo par @projet. */
99
+ projectWeeklyCalendarTitle: string;
100
+ projectColProject: string;
101
+ projectColTasks: string;
102
+ projectColTime: string;
103
+ projectUnassigned: string;
104
+ projectTableHint: string;
105
+ /** Temps tâche par #tag — usage type affectation WBS / NWA (SAP). */
106
+ tagTimeSectionTitle: string;
107
+ tagTimeSectionHint: string;
108
+ tagTimeByDayTitle: string;
109
+ tagTimeByWeekTitle: string;
110
+ tagWeekStartsOnLegend: string;
111
+ weekStartsMonday: string;
112
+ weekStartsSunday: string;
113
+ weekStartsSaturday: string;
114
+ tagWeekSumColumn: string;
115
+ tagTimeColTag: string;
116
+ tagTimeColDay: string;
117
+ tagTimeColMinutes: string;
118
+ tagTimeUntagged: string;
119
+ /** aria-label du bouton chevron : ligne regroupant les étiquettes projet#code sous @projet. */
120
+ tagWeekScopedRollupToggleAria: string;
121
+ summaryLinesWrittenTotal: string;
122
+ summaryLinesWrittenHuman: string;
123
+ summaryLinesWrittenAi: string;
124
+ locByLanguageSectionTitle: string;
125
+ locByLanguageColLang: string;
126
+ locByLanguageColLines: string;
127
+ codingSignalsSectionTitle: string;
128
+ codingSignalsColLang: string;
129
+ codingSignalsColCount: string;
130
+ locMetricsHint: string;
131
+ workspaceSnapshotTitle: string;
132
+ workspaceSnapshotIntro: string;
133
+ workspaceSnapshotRefresh: string;
134
+ workspaceSnapshotRefreshing: string;
135
+ workspaceSnapshotSourceGit: string;
136
+ workspaceSnapshotSourceWalk: string;
137
+ workspaceSnapshotTotalLines: string;
138
+ workspaceSnapshotFileCount: string;
139
+ workspaceSnapshotColLang: string;
140
+ workspaceSnapshotColLines: string;
141
+ workspaceSnapshotColPercent: string;
142
+ workspaceSnapshotNoWorkspace: string;
143
+ workspaceSnapshotEmpty: string;
144
+ workspaceSnapshotError: string;
145
+ /** Indication lorsque l’instantané semble indisponible ou obsolète. */
146
+ workspaceSnapshotRefreshHint: string;
147
+ /** Sommaire de page (navigation interne). */
148
+ tocTitle: string;
149
+ tocNavAria: string;
150
+ /** Libellé court pour la grille d’indicateurs (pas de titre visible dans la section). */
151
+ tocSummaryKpis: string;
152
+ /** Libellé pour le tableau jour / sessions / tâches. */
153
+ tocDailyTable: string;
154
+ tocTagTimeSection: string;
155
+ /** Navigation entre semaines (graphiques + tableau + calendrier par étiquette). */
156
+ weekNavPrev: string;
157
+ weekNavNext: string;
158
+ weekNavAriaLabel: string;
159
+ tocWeekNav: string;
160
+ weekNavHelpHint: string;
161
+ weekNavNoTagDataThisWeek: string;
162
+ weekNavNoProjectDataThisWeek: string;
163
+ /** Section lignes écrites + tableaux par langage (agrégés). */
164
+ tocLocSection: string;
165
+ /** Bouton flottant retour en haut de page. */
166
+ scrollToTopAria: string;
167
+ };
168
+
169
+ const en: ReportingStrings = {
170
+ dashboard: "Dashboard",
171
+ reporting: "Reporting",
172
+ settings: "Settings",
173
+ guide: "User guide",
174
+ title: "Reporting",
175
+ subtitle:
176
+ "Aggregates from Kronosys history (local API). Filter by date range and tags. Task time by @project is included.",
177
+ archivedSessionsReportingNote:
178
+ "Archived sessions: task rows, recorded task time, time by project, and per-task KronoFocus counts include only tasks marked done with every subtask checked. Session-level coding and active time still cover the whole session.",
179
+ reportingArchivedExcludedAside:
180
+ "Besides the KPI totals above, {duration} is logged on archived tasks that charts exclude (task not done, or a checklist item still open). That time does not appear in time-by-project or by-tag views.",
181
+ filtersTitle: "Filters",
182
+ dateFrom: "From",
183
+ dateTo: "To",
184
+ today: "Today",
185
+ clearDates: "All dates",
186
+ resetAllFilters: "Reset filters",
187
+ presetPeriodLabel: "Quick range",
188
+ presetDay: "Day",
189
+ presetWeek: "Week",
190
+ presetMonth: "Month",
191
+ presetYear: "Year",
192
+ presetPeriodHint: "Week is Monday–Sunday (local dates). “Day” is today.",
193
+ tagsLabel: "Tags",
194
+ tagsHint: "Leave empty to include all tasks. Sessions are included only if at least one task matches.",
195
+ filtersHelpAriaLabel: "How reporting filters apply",
196
+ filtersHelpBody: `Where dates and tags apply on this page
197
+
198
+ • Workspace folder snapshot (lines in the open project): ignores filters — not Kronosys history.
199
+
200
+ • Summary KPIs, charts, day-by-day table: filters apply. Session-day side: session count, session coding/active/wall time, KronoFocus sessions completed, lines written, and (when the host stored a planned start) punctuality aggregates: reference count, late session count, cumulative and average late minutes. Task-day side: task rows, recorded task time, done/in-progress counts, task KronoFocus used/cycles.
201
+
202
+ • “Lines by language” and “Coding signals” tables: session day within the range; with tags selected, a whole session is dropped if no task matches (same rule as elsewhere).
203
+
204
+ • Week navigation: does not re-filter; it picks which week to show among data already in range.
205
+
206
+ • Recorded time by #tag (day list + weekly calendars): task day + tag filter.
207
+
208
+ • Recorded time by @project (weekly calendars): task day + tag filter (there is no separate @project filter).
209
+
210
+ Reminder — two “day” notions
211
+
212
+ • Session metrics use the session day (saved or end).
213
+
214
+ • Task metrics use the task day (end or start).
215
+
216
+ Quick presets set the range; “Week” is Monday–Sunday (local), which may differ from “week starts on” for the weekly calendars only.
217
+
218
+ Tags: with at least one tag selected, any session with no matching task is excluded entirely.
219
+
220
+ Archived sessions: task-based metrics only count fully completed tasks (subtasks included) — see the summary indicators help as well.`,
221
+ reportingNonFinalLegend: "Open work —",
222
+ reportingNonFinalSessionsBadge: "Sessions",
223
+ reportingNonFinalProjectsBadge: "Projects",
224
+ reportingNonFinalTagsBadge: "Tags",
225
+ sectionFilteredBadge: "Filtered",
226
+ sectionFilteredBadgeTitle: "These figures reflect the date range and tag filters at the top of the page.",
227
+ summarySessionsInRange: "Sessions (in range)",
228
+ summaryTaskEvents: "Task rows (in range)",
229
+ summaryKronoFocusCompleted: "KronoFocus sessions completed (session total)",
230
+ summaryKronoFocusTasksUsed: "Tasks with KronoFocus used",
231
+ summaryKronoFocusCycles: "KronoFocus cycles (tasks)",
232
+ summaryTaskTimeRecorded: "Recorded task time",
233
+ summarySessionCoding: "Session coding time",
234
+ summarySessionActive: "Session active time",
235
+ summarySessionWallClock: "Session wall-clock time",
236
+ summaryAssiduityWithReference: "Sessions (planned start recorded)",
237
+ summaryAssiduityLateSessions: "Late starts (after planned time)",
238
+ summaryAssiduityLateTotal: "Cumulative late time",
239
+ summaryAssiduityAvgLateWhenLate: "Average late (when late)",
240
+ chartSessionsPerDay: "Sessions per day",
241
+ chartTasksByStatusPerDay: "Tasks per day by status",
242
+ chartTaskTimePerDay: "Recorded task time per day",
243
+ chartSessionWallPerDay: "Session wall-clock time per day",
244
+ legendSessions: "Sessions",
245
+ legendDone: "Done",
246
+ legendActive: "In progress",
247
+ tableDay: "Day",
248
+ tableSessions: "Sessions",
249
+ tableDone: "Done",
250
+ tableActive: "Active",
251
+ tableTaskTime: "Task time",
252
+ tableSessionCoding: "Session coding",
253
+ tableSessionWall: "Session span",
254
+ undatedLabel: "Undated",
255
+ loading: "Loading…",
256
+ noRowsInRange: "No data in this range for the current filters.",
257
+ projectSectionTitle: "Recorded time by project",
258
+ projectWeeklyCalendarTitle: "Weekly calendar",
259
+ projectColProject: "Project",
260
+ projectColTasks: "Tasks",
261
+ projectColTime: "Recorded time",
262
+ projectUnassigned: "(no project)",
263
+ projectTableHint:
264
+ "One line per calendar week, then one table: each @project on its own row, seven day columns and a week total — same week navigation and week-start setting as the tag calendar above.",
265
+ tagTimeSectionTitle: "Recorded task time by tag",
266
+ tagTimeSectionHint:
267
+ "Map each #tag to an external key (e.g. SAP WBS or NWA). A task’s full recorded time is counted under every tag on that task (tag totals can overlap). In the by-day table and the weekly calendar, several `project#code` tags for the same project (same day or same week) appear under one @project row—expand to see each tag line.",
268
+ tagTimeByDayTitle: "By calendar day",
269
+ tagTimeByWeekTitle: "Weekly calendar",
270
+ tagWeekStartsOnLegend: "Week starts on",
271
+ weekStartsMonday: "Monday",
272
+ weekStartsSunday: "Sunday",
273
+ weekStartsSaturday: "Saturday",
274
+ tagWeekSumColumn: "Week Σ",
275
+ tagTimeColTag: "Tag",
276
+ tagTimeColDay: "Task day",
277
+ tagTimeColMinutes: "Recorded time",
278
+ tagTimeUntagged: "default",
279
+ tagWeekScopedRollupToggleAria: "Show or hide per-tag breakdown for this project",
280
+ summaryLinesWrittenTotal: "Lines written (sessions in range)",
281
+ summaryLinesWrittenHuman: "Lines — human (est., sum)",
282
+ summaryLinesWrittenAi: "Lines — AI (est., sum)",
283
+ locByLanguageSectionTitle: "Lines by language (aggregated)",
284
+ locByLanguageColLang: "Language",
285
+ locByLanguageColLines: "Lines",
286
+ codingSignalsSectionTitle: "Coding signals by language (aggregated)",
287
+ codingSignalsColLang: "Language",
288
+ codingSignalsColCount: "Signals",
289
+ locMetricsHint:
290
+ "Totals sum each session’s stored counters for sessions included in the range (same rule as session coding time). Tag filter still excludes whole sessions with no matching task.",
291
+ workspaceSnapshotTitle: "Open folder — lines by language (snapshot)",
292
+ workspaceSnapshotIntro:
293
+ "Independent of sessions and tasks: approximate line counts in the first workspace folder (tracked files via Git when available, otherwise a walk with common extensions). Cached ~5 minutes.",
294
+ workspaceSnapshotRefresh: "Refresh snapshot",
295
+ workspaceSnapshotRefreshing: "Refreshing…",
296
+ workspaceSnapshotSourceGit: "Source: Git tracked files",
297
+ workspaceSnapshotSourceWalk: "Source: folder walk (non-Git or empty ls-files)",
298
+ workspaceSnapshotTotalLines: "Total lines (approx.)",
299
+ workspaceSnapshotFileCount: "Files scanned",
300
+ workspaceSnapshotColLang: "Language",
301
+ workspaceSnapshotColLines: "Lines",
302
+ workspaceSnapshotColPercent: "Share",
303
+ workspaceSnapshotNoWorkspace: "No workspace root configured for the snapshot.",
304
+ workspaceSnapshotEmpty: "No matching source files found.",
305
+ workspaceSnapshotError: "Could not build snapshot.",
306
+ workspaceSnapshotRefreshHint: "Use “Refresh snapshot” after changing workspace paths or exclusions in Settings.",
307
+ tocTitle: "On this page",
308
+ tocNavAria: "Reporting — sections on this page",
309
+ tocSummaryKpis: "Summary indicators",
310
+ tocDailyTable: "Day-by-day table",
311
+ tocTagTimeSection: "Time by tag",
312
+ weekNavPrev: "Previous week",
313
+ weekNavNext: "Next week",
314
+ weekNavAriaLabel: "Week navigation for charts and tables",
315
+ tocWeekNav: "Week navigation",
316
+ weekNavHelpHint:
317
+ "Shown when several weeks in the range have session data (wall-clock time or session count), or otherwise weeks from recorded task time by tag or by project. Charts, the day table, and the weekly calendars follow the selected week.",
318
+ weekNavNoTagDataThisWeek: "No recorded task time for this week.",
319
+ weekNavNoProjectDataThisWeek: "No recorded task time by project for this week.",
320
+ tocLocSection: "Lines & coding signals",
321
+ scrollToTopAria: "Back to top",
322
+ ...reportingMetricHelpEn,
323
+ };
324
+
325
+ const fr: ReportingStrings = {
326
+ dashboard: "Tableau de bord",
327
+ reporting: "Rapports",
328
+ settings: "Paramètres",
329
+ guide: "Guide d’utilisation",
330
+ title: "Rapports",
331
+ subtitle:
332
+ "Agrégats à partir de l’historique Kronosys (API locale). Filtrez par plage de dates et par étiquettes. Le temps par @projet est inclus.",
333
+ archivedSessionsReportingNote:
334
+ "Sessions archivées : les lignes de tâches, le temps enregistré sur les tâches, le temps par projet et les comptes KronoFocus par tâche ne retiennent que les tâches marquées terminées dont toutes les sous-tâches sont cochées. Les temps de codage et d’activité au niveau session restent ceux de la session entière.",
335
+ reportingArchivedExcludedAside:
336
+ "En complément des indicateurs ci-dessus, {duration} figure sur des tâches d’archives que les graphiques excluent (tâche non terminée ou point non coché). Ce temps n’apparaît pas dans le temps par projet ni par étiquette.",
337
+ filtersTitle: "Filtres",
338
+ dateFrom: "Du",
339
+ dateTo: "Au",
340
+ today: "Aujourd’hui",
341
+ clearDates: "Toutes les dates",
342
+ resetAllFilters: "Réinitialiser les filtres",
343
+ presetPeriodLabel: "Période rapide",
344
+ presetDay: "Jour",
345
+ presetWeek: "Semaine",
346
+ presetMonth: "Mois",
347
+ presetYear: "Année",
348
+ presetPeriodHint: "La semaine va du lundi au dimanche (dates locales). « Jour » = aujourd’hui.",
349
+ tagsLabel: "Étiquettes",
350
+ tagsHint:
351
+ "Laissez vide pour toutes les tâches. Une session n’est comptée que si au moins une tâche correspond.",
352
+ filtersHelpAriaLabel: "Comment s’appliquent les filtres des rapports",
353
+ filtersHelpBody: `Où agissent les dates et les étiquettes sur cette page
354
+
355
+ • Instantané « Dossier ouvert » (lignes par langage du workspace) : hors filtres — ce n’est pas l’historique Kronosys.
356
+
357
+ • Indicateurs de synthèse, graphiques, tableau par jour : la plage et les étiquettes s’appliquent. Côté jour de session : nombre de sessions, temps de codage et d’activité, durée murale, KronoFocus « sessions terminées », lignes écrites, et (si l’hôte a enregistré un début prévu) indicateurs d’assiduité : sessions avec référence, sessions en retard, retard cumulé, retard moyen lorsqu’en retard. Côté jour de tâche : lignes de tâches, temps enregistré, tâches terminées / en cours, KronoFocus côté tâches.
358
+
359
+ • Tableaux « Lignes par langage » et « Signaux de codage » : jour de session dans la plage ; avec étiquettes sélectionnées, toute la session est exclue si aucune tâche ne correspond (même règle qu’ailleurs).
360
+
361
+ • Navigation par semaine : ne refiltre pas les données ; elle choisit la semaine affichée parmi celles déjà retenues par la plage.
362
+
363
+ • Temps enregistré par #étiquette (liste par jour + calendriers hebdo) : jour de tâche + filtre d’étiquettes.
364
+
365
+ • Temps enregistré par @projet (calendriers hebdo) : jour de tâche + filtre d’étiquettes (pas de filtre séparé sur le nom de projet).
366
+
367
+ Rappel — deux notions de « jour »
368
+
369
+ • Les indicateurs « session » utilisent le jour de la session (enregistrement ou fin).
370
+
371
+ • Les indicateurs « tâche » utilisent le jour de la tâche (fin ou début).
372
+
373
+ Les périodes rapides fixent la plage ; « Semaine » = lundi–dimanche (local), ce qui peut différer du réglage « La semaine commence » des seuls calendriers hebdomadaires.
374
+
375
+ Étiquettes : dès qu’au moins une étiquette est choisie, toute session sans aucune tâche correspondante est entièrement exclue.
376
+
377
+ Sessions archivées : pour les métriques basées sur les tâches, seules les tâches entièrement bouclées (sous-tâches comprises) sont prises en compte — voir aussi l’aide des indicateurs de synthèse.`,
378
+ reportingNonFinalLegend: "Travail ouvert —",
379
+ reportingNonFinalSessionsBadge: "Sessions",
380
+ reportingNonFinalProjectsBadge: "Projets",
381
+ reportingNonFinalTagsBadge: "Étiquettes",
382
+ sectionFilteredBadge: "Filtré",
383
+ sectionFilteredBadgeTitle:
384
+ "Ces chiffres tiennent compte de la plage de dates et des étiquettes choisies en haut de page.",
385
+ summarySessionsInRange: "Sessions (plage)",
386
+ summaryTaskEvents: "Lignes de tâches (plage)",
387
+ summaryKronoFocusCompleted: "Sessions KronoFocus terminées (total par session)",
388
+ summaryKronoFocusTasksUsed: "Tâches avec KronoFocus utilisé",
389
+ summaryKronoFocusCycles: "Cycles KronoFocus (tâches)",
390
+ summaryTaskTimeRecorded: "Temps enregistré sur les tâches",
391
+ summarySessionCoding: "Temps de codage (sessions)",
392
+ summarySessionActive: "Temps actif (sessions)",
393
+ summarySessionWallClock: "Durée murale des sessions",
394
+ summaryAssiduityWithReference: "Sessions (début prévu enregistré)",
395
+ summaryAssiduityLateSessions: "Départs en retard (après l’heure prévue)",
396
+ summaryAssiduityLateTotal: "Retard cumulé",
397
+ summaryAssiduityAvgLateWhenLate: "Retard moyen (si en retard)",
398
+ chartSessionsPerDay: "Sessions par jour",
399
+ chartTasksByStatusPerDay: "Tâches par jour et par statut",
400
+ chartTaskTimePerDay: "Temps enregistré sur les tâches par jour",
401
+ chartSessionWallPerDay: "Durée murale des sessions par jour",
402
+ legendSessions: "Sessions",
403
+ legendDone: "Terminées",
404
+ legendActive: "En cours",
405
+ tableDay: "Jour",
406
+ tableSessions: "Sessions",
407
+ tableDone: "Terminées",
408
+ tableActive: "En cours",
409
+ tableTaskTime: "Temps (tâches)",
410
+ tableSessionCoding: "Codage (sessions)",
411
+ tableSessionWall: "Durée session",
412
+ undatedLabel: "Sans date",
413
+ loading: "Chargement…",
414
+ noRowsInRange: "Aucune donnée dans cette plage pour les filtres actuels.",
415
+ projectSectionTitle: "Temps enregistré par projet",
416
+ projectWeeklyCalendarTitle: "Calendrier hebdomadaire",
417
+ projectColProject: "Projet",
418
+ projectColTasks: "Tâches",
419
+ projectColTime: "Temps enregistré",
420
+ projectUnassigned: "(sans projet)",
421
+ projectTableHint:
422
+ "Une ligne par semaine calendaire, puis un tableau : chaque @projet sur sa propre ligne, sept colonnes jour et le total de la semaine — même navigation par semaine et même premier jour de semaine que le calendrier par étiquette ci-dessus.",
423
+ tagTimeSectionTitle: "Temps enregistré par étiquette",
424
+ tagTimeSectionHint:
425
+ "Chaque #tag peut correspondre à une clé externe (ex. poste WBS ou NWA dans SAP). La durée enregistrée complète d’une tâche est comptée pour chaque étiquette associée (les totaux par étiquette peuvent se chevaucher). Dans le tableau par jour et le calendrier hebdomadaire, plusieurs étiquettes `projet#code` pour un même projet (même jour ou même semaine) sont regroupées sous une ligne @projet : développez-la pour voir chaque ligne d’étiquette.",
426
+ tagTimeByDayTitle: "Par jour calendaire",
427
+ tagTimeByWeekTitle: "Calendrier hebdomadaire",
428
+ tagWeekStartsOnLegend: "La semaine commence",
429
+ weekStartsMonday: "Lundi",
430
+ weekStartsSunday: "Dimanche",
431
+ weekStartsSaturday: "Samedi",
432
+ tagWeekSumColumn: "Σ semaine",
433
+ tagTimeColTag: "Étiquette",
434
+ tagTimeColDay: "Jour (tâche)",
435
+ tagTimeColMinutes: "Temps enregistré",
436
+ tagTimeUntagged: "défaut",
437
+ tagWeekScopedRollupToggleAria: "Afficher ou masquer le détail des étiquettes pour ce projet",
438
+ summaryLinesWrittenTotal: "Lignes écrites (sessions dans la plage)",
439
+ summaryLinesWrittenHuman: "Lignes — humain (estim., somme)",
440
+ summaryLinesWrittenAi: "Lignes — IA (estim., somme)",
441
+ locByLanguageSectionTitle: "Lignes par langage (agrégé)",
442
+ locByLanguageColLang: "Langage",
443
+ locByLanguageColLines: "Lignes",
444
+ codingSignalsSectionTitle: "Signaux de codage par langage (agrégé)",
445
+ codingSignalsColLang: "Langage",
446
+ codingSignalsColCount: "Signaux",
447
+ locMetricsHint:
448
+ "Les totaux additionnent les compteurs enregistrés par session pour les sessions dans la plage (même règle que le temps de codage des sessions). Le filtre d’étiquettes exclut encore toute la session si aucune tâche ne correspond.",
449
+ workspaceSnapshotTitle: "Dossier ouvert — lignes par langage (instantané)",
450
+ workspaceSnapshotIntro:
451
+ "Indépendant des sessions et des tâches : estimation du nombre de lignes dans le premier dossier du workspace (fichiers suivis par Git si possible, sinon parcours avec extensions courantes). Mise en cache ~5 minutes.",
452
+ workspaceSnapshotRefresh: "Rafraîchir l’instantané",
453
+ workspaceSnapshotRefreshing: "Actualisation…",
454
+ workspaceSnapshotSourceGit: "Source : fichiers suivis par Git",
455
+ workspaceSnapshotSourceWalk: "Source : parcours du dossier (sans Git ou liste Git vide)",
456
+ workspaceSnapshotTotalLines: "Total de lignes (approx.)",
457
+ workspaceSnapshotFileCount: "Fichiers parcourus",
458
+ workspaceSnapshotColLang: "Langage",
459
+ workspaceSnapshotColLines: "Lignes",
460
+ workspaceSnapshotColPercent: "Part",
461
+ workspaceSnapshotNoWorkspace: "Aucune racine de workspace configurée pour l’instantané.",
462
+ workspaceSnapshotEmpty: "Aucun fichier source correspondant trouvé.",
463
+ workspaceSnapshotError: "Impossible de construire l’instantané.",
464
+ workspaceSnapshotRefreshHint:
465
+ "Utilisez « Rafraîchir l’instantané » après un changement de chemins ou d’exclusions du workspace dans les paramètres.",
466
+ tocTitle: "Sur cette page",
467
+ tocNavAria: "Rapports — sections de la page",
468
+ tocSummaryKpis: "Indicateurs de synthèse",
469
+ tocDailyTable: "Tableau par jour",
470
+ tocTagTimeSection: "Temps par étiquette",
471
+ weekNavPrev: "Semaine précédente",
472
+ weekNavNext: "Semaine suivante",
473
+ weekNavAriaLabel: "Navigation par semaine pour les graphiques et tableaux",
474
+ tocWeekNav: "Navigation par semaine",
475
+ weekNavHelpHint:
476
+ "Affiché lorsque plusieurs semaines dans la plage ont des données de session (durée murale ou nombre de sessions), sinon des semaines issues du temps enregistré par étiquette ou par projet. Les graphiques, le tableau par jour et les calendriers hebdomadaires suivent la semaine sélectionnée.",
477
+ weekNavNoTagDataThisWeek: "Aucun temps de tâche enregistré pour cette semaine.",
478
+ weekNavNoProjectDataThisWeek: "Aucun temps de tâche par projet pour cette semaine.",
479
+ tocLocSection: "Lignes et signaux de codage",
480
+ scrollToTopAria: "Retour en haut de la page",
481
+ ...reportingMetricHelpFr,
482
+ };
483
+
484
+ export function reportingStrings(lang: Lang): ReportingStrings {
485
+ return lang === "fr" ? fr : en;
486
+ }
487
+
488
+ export function reportingNav(lang: Lang): ReportingNavStrings {
489
+ const s = reportingStrings(lang);
490
+ return { dashboard: s.dashboard, reporting: s.reporting, settings: s.settings, guide: s.guide };
491
+ }