@nightkatana/kronosys-app 1.0.0-beta.2 → 1.0.0-beta.21
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.
- package/README.md +28 -1
- package/app/api/action/route.ts +39 -3
- package/app/api/action-logs/route.ts +24 -0
- package/app/api/backup/route.ts +1 -1
- package/app/api/restore/route.ts +145 -0
- package/app/changelog/page.tsx +71 -4
- package/app/globals.css +127 -0
- package/app/guide/page.tsx +61 -15
- package/app/implementation/page.tsx +700 -0
- package/app/layout.tsx +14 -3
- package/app/licenses/page.tsx +99 -37
- package/app/logs/page.tsx +258 -0
- package/app/manifest.ts +5 -5
- package/app/page.tsx +784 -229
- package/app/reporting/page.tsx +1266 -474
- package/app/settings/page.tsx +252 -18
- package/bin/kronosys.mjs +140 -15
- package/components/KronosysPayloadProvider.tsx +2 -0
- package/components/RouteTransition.tsx +18 -0
- package/components/dashboard/AppShellCommandCenterPlaceholder.tsx +17 -0
- package/components/dashboard/AppShellHeaderSessionMeta.tsx +210 -0
- package/components/dashboard/AppShellHeaderWallClock.tsx +54 -0
- package/components/dashboard/AppShellLiveSessionDrawer.tsx +154 -38
- package/components/dashboard/AppShellRouteNav.tsx +323 -48
- package/components/dashboard/DashboardPauseBackdrop.tsx +50 -0
- package/components/dashboard/DashboardSimpleModal.tsx +168 -25
- package/components/dashboard/DashboardTour.tsx +115 -29
- package/components/dashboard/GlobalPauseConfirmModal.tsx +183 -0
- package/components/dashboard/KronosysDatetimePopoverField.tsx +167 -122
- package/components/dashboard/KronosysTimePopoverField.tsx +54 -12
- package/components/dashboard/NewSessionScopeModal.tsx +211 -20
- package/components/dashboard/PlannedTaskBoundaryConflictWatcher.tsx +275 -0
- package/components/dashboard/ReportingTour.tsx +87 -21
- package/components/dashboard/SavedProjectPicker.tsx +16 -3
- package/components/dashboard/SelectedSessionSidebarBlock.tsx +512 -142
- package/components/dashboard/SessionListPanel.tsx +327 -44
- package/components/dashboard/SettingsTagsProjectsSection.tsx +1073 -264
- package/components/dashboard/SettingsTaskTemplatesSection.tsx +316 -0
- package/components/dashboard/SettingsTour.tsx +86 -21
- package/components/dashboard/TagPills.tsx +14 -1
- package/components/dashboard/TaskFocusPanel.tsx +1081 -478
- package/components/dashboard/TaskSessionLiveCard.tsx +650 -135
- package/components/dashboard/TaskTimelineGanttModal.tsx +601 -0
- package/components/dashboard/taskFieldStyles.ts +20 -4
- package/components/dashboard/useReportingInteractionState.ts +80 -0
- package/lib/appShellHeaderClasses.ts +13 -0
- package/lib/businessRulesMatrix.ts +210 -0
- package/lib/copyToClipboard.ts +43 -0
- package/lib/dashboardCopy.ts +494 -84
- package/lib/dashboardQuickSearch.ts +54 -2
- package/lib/dashboardTimeZone.ts +109 -0
- package/lib/formatAppShellWallClock.ts +66 -0
- package/lib/formatSessionNameTemplate.ts +141 -0
- package/lib/generatedUserChangelog.ts +177 -6
- package/lib/globalPausePreview.ts +292 -0
- package/lib/implementationNotes.ts +1188 -0
- package/lib/kronosysApi.ts +6 -0
- package/lib/kronosysDashboardModalGates.ts +24 -0
- package/lib/plannedBoundaryAttention.ts +9 -0
- package/lib/plannedBoundaryConflict.ts +23 -0
- package/lib/reportingAggregate.ts +517 -75
- package/lib/reportingMetricHelp.ts +8 -0
- package/lib/reportingStrings.ts +37 -3
- package/lib/sessionListMerge.ts +4 -0
- package/lib/sessionTaskSidebarStats.ts +182 -21
- package/lib/settingsCopy.ts +178 -4
- package/lib/taskParsing.ts +360 -103
- package/lib/taskTemplateDraft.ts +135 -0
- package/lib/taskTimelineGantt.ts +265 -0
- package/lib/temporalDisplayPlanned.ts +71 -0
- package/lib/userGuideCopy.ts +121 -47
- package/next.config.ts +7 -0
- package/package.json +12 -24
- package/server/actionDispatch.ts +1000 -77
- package/server/actionTaskSession.ts +337 -24
- package/server/db.ts +7 -15
- package/server/dbSchema.ts +24 -0
- package/server/defaultCfg.ts +5 -0
- package/server/gitlabTokenStore.ts +0 -12
- package/server/liveHistorySync.ts +53 -0
- package/server/mainTimerHydrate.ts +38 -2
- package/server/payloadStore.ts +33 -11
- package/server/sessionWallHydrate.ts +66 -3
- package/server/userActionLog.ts +126 -0
- package/sonar-project.properties +11 -0
- package/tsconfig.json +2 -1
- package/components/dashboard/IssuePickerModal.tsx +0 -168
- package/components/dashboard/ThemeToggle.test.tsx +0 -26
- package/lib/backupCsvExport.test.ts +0 -149
- package/lib/dashboardQuickSearchQuery.test.ts +0 -63
- package/lib/dataDir.test.ts +0 -87
- package/lib/formatIsoShort.test.ts +0 -46
- package/lib/kronoFocusRhythm.test.ts +0 -130
- package/lib/kronoFocusTimerUrgency.test.ts +0 -74
- package/lib/legacyKronoFocusStorageKeys.test.ts +0 -29
- package/lib/reportingAggregate.test.ts +0 -325
- package/lib/reportingNonFinalIndicators.test.ts +0 -157
- package/lib/reportingTagWeekBreakdown.test.ts +0 -141
- package/lib/reportingWeekLayout.test.ts +0 -239
- package/lib/sessionAssiduity.test.ts +0 -25
- package/lib/sessionEndWarnings.test.ts +0 -200
- package/lib/sessionListMerge.test.ts +0 -101
- package/lib/sessionTaskSidebarStats.test.ts +0 -24
- package/lib/taskParsing.test.ts +0 -153
- package/lib/usageProfile.test.ts +0 -84
- package/server/actionDispatch.test.ts +0 -723
- package/server/actionTaskSession.test.ts +0 -713
- package/server/kronoFocusHydrate.test.ts +0 -142
- package/server/kronoFocusMigrate.test.ts +0 -53
- package/server/mainTimerHydrate.test.ts +0 -65
- package/server/payloadStore.test.ts +0 -78
- package/server/sessionWallHydrate.test.ts +0 -46
|
@@ -0,0 +1,1188 @@
|
|
|
1
|
+
import type { Lang } from "./dashboardCopy";
|
|
2
|
+
|
|
3
|
+
/** User story : ✓ = livré · ✗ = absent ou explicitement hors périmètre · écartée = intention abandonnée volontairement (reste documentée). */
|
|
4
|
+
export type ImplementationUserStory = {
|
|
5
|
+
implemented: boolean;
|
|
6
|
+
/** Intention produit volontairement abandonnée ; la ligne reste listée pour éviter les rediscussions. */
|
|
7
|
+
discarded?: boolean;
|
|
8
|
+
integrationChecked?: boolean;
|
|
9
|
+
e2eChecked?: boolean;
|
|
10
|
+
text: string;
|
|
11
|
+
/** Précision affichée sous la ligne principale (repliable, police réduite). */
|
|
12
|
+
detail?: string;
|
|
13
|
+
/** Exemple concret ; affiché sous le détail avec une étiquette « Exemple ». */
|
|
14
|
+
example?: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
export type ImplementationUserStoryGroup = {
|
|
18
|
+
id: string;
|
|
19
|
+
title: string;
|
|
20
|
+
lead?: string;
|
|
21
|
+
stories: ImplementationUserStory[];
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export type ImplementationNotesBundle = {
|
|
25
|
+
pageTitle: string;
|
|
26
|
+
pageSubtitle: string;
|
|
27
|
+
/** Ligne courte sous le sous-titre : sens des coches vert / rouge. */
|
|
28
|
+
statusKeyLine: string;
|
|
29
|
+
lastUpdated: string;
|
|
30
|
+
repositoryDocLabel: string;
|
|
31
|
+
storyGroupsHeading: string;
|
|
32
|
+
implementationColumnLabel: string;
|
|
33
|
+
storyColumnLabel: string;
|
|
34
|
+
integrationColumnLabel: string;
|
|
35
|
+
e2eColumnLabel: string;
|
|
36
|
+
openChangelogAria: string;
|
|
37
|
+
openChangelogTooltip: string;
|
|
38
|
+
/** Bouton chevron : états accessibles pour le bloc détail / exemple. */
|
|
39
|
+
storyDetailsToggleExpand: string;
|
|
40
|
+
storyDetailsToggleCollapse: string;
|
|
41
|
+
/** Étiquette devant `example` (ex. « Exemple : »). */
|
|
42
|
+
storyDetailExampleLabel: string;
|
|
43
|
+
/** Court texte d’aide sous le titre du tableau des user stories (chevron). */
|
|
44
|
+
storyExpandHintLine: string;
|
|
45
|
+
/** Pastille sur une ligne dont l’intention a été écartée volontairement. */
|
|
46
|
+
storyDiscardedBadge: string;
|
|
47
|
+
/** Légende : pourquoi ces lignes restent visibles. */
|
|
48
|
+
storyDiscardedLegendLine: string;
|
|
49
|
+
storyGroups: ImplementationUserStoryGroup[];
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const frBundle: ImplementationNotesBundle = {
|
|
53
|
+
pageTitle: "User stories — état d’implémentation",
|
|
54
|
+
pageSubtitle:
|
|
55
|
+
"Liste exhaustive des intentions utilisateur·rice·s : chaque ligne indique si la capacité est livrée (✓) ou absente / hors périmètre (✗). Les cases à droite servent à cocher une ligne « revue » sur cet appareil — le détail technique long reste dans le dépôt.",
|
|
56
|
+
statusKeyLine:
|
|
57
|
+
"✓ vert = implémenté dans le produit · ✗ rouge = non livré ou volontairement exclu.",
|
|
58
|
+
lastUpdated: "Dernière mise à jour : 2026-05-12 (v1.0.0-beta.21)",
|
|
59
|
+
repositoryDocLabel: "Document dépôt : docs/IMPLEMENTATION_DETAILS.md",
|
|
60
|
+
storyGroupsHeading: "Inventaire des user stories",
|
|
61
|
+
implementationColumnLabel: "Implémentation",
|
|
62
|
+
storyColumnLabel: "User story",
|
|
63
|
+
integrationColumnLabel: "Intégration",
|
|
64
|
+
e2eColumnLabel: "E2E",
|
|
65
|
+
openChangelogAria: "Ouvrir le changelog usager",
|
|
66
|
+
openChangelogTooltip: "Ouvrir le changelog",
|
|
67
|
+
storyDetailsToggleExpand: "Afficher précisions et exemple",
|
|
68
|
+
storyDetailsToggleCollapse: "Masquer précisions et exemple",
|
|
69
|
+
storyDetailExampleLabel: "Exemple :",
|
|
70
|
+
storyExpandHintLine:
|
|
71
|
+
"Certaines lignes ont un chevron : ouvrez-les pour une précision et un exemple en police réduite.",
|
|
72
|
+
storyDiscardedBadge: "Écarté",
|
|
73
|
+
storyDiscardedLegendLine:
|
|
74
|
+
"Les lignes marquées « Écarté » documentent une intention volontairement abandonnée ; elles restent dans la liste pour éviter qu’on rouvre le même débat par inadvertance.",
|
|
75
|
+
storyGroups: [
|
|
76
|
+
{
|
|
77
|
+
id: "us-shell",
|
|
78
|
+
title: "Coque, navigation, confort",
|
|
79
|
+
lead: "Barre d’outils commune, pages principales, thème et langue.",
|
|
80
|
+
stories: [
|
|
81
|
+
{
|
|
82
|
+
implemented: true,
|
|
83
|
+
text: "Naviguer entre tableau de bord, guide, rapports, paramètres et cette page depuis les icônes du haut.",
|
|
84
|
+
detail:
|
|
85
|
+
"Les destinations couvrent le tableau de bord, les rapports (graphiques), les paramètres, le guide in-app, cette page d’implémentation, les journaux d’actions, les licences et le changelog usager ; les icônes sont regroupées dans la barre d’outils.",
|
|
86
|
+
example:
|
|
87
|
+
"Sur le tableau de bord, cliquer l’icône livre ouvre /guide tout en conservant le paramètre de session (?session=) lorsqu’il est présent.",
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
implemented: true,
|
|
91
|
+
text: "Voir une horloge murale continue dans la barre d’outils (fuseau et format 12/24 h des paramètres tableau de bord web).",
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
implemented: true,
|
|
95
|
+
text: "Pour une livraison donnée (p. ex. 1.0.0-beta.21), valider le comportement en croisant la section correspondante du **CHANGELOG**, les récits marqués livrés sur cette page et `docs/IMPLEMENTATION_DETAILS.md`, puis cocher au besoin **intégration** / **E2E**.",
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
implemented: true,
|
|
99
|
+
text: "Ouvrir la page Licences depuis l’icône document lorsque je suis déjà sur Licences.",
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
implemented: true,
|
|
103
|
+
text: "Basculer entre thème clair et sombre pour le confort visuel.",
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
implemented: true,
|
|
107
|
+
text: "Actualiser l’état serveur sans recharger toute l’application.",
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
implemented: true,
|
|
111
|
+
text: "Choisir la langue d’interface (FR / EN) avec préférence pouvant être mémorisée.",
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
implemented: true,
|
|
115
|
+
text: "Conserver le paramètre de session du tableau de bord (?session=) quand je passe d’une route à l’autre.",
|
|
116
|
+
},
|
|
117
|
+
],
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
id: "us-sessions",
|
|
121
|
+
title: "Sessions — cadre, horloge murale, pause, fin",
|
|
122
|
+
stories: [
|
|
123
|
+
{
|
|
124
|
+
implemented: true,
|
|
125
|
+
integrationChecked: true,
|
|
126
|
+
e2eChecked: true,
|
|
127
|
+
text: "Créer une nouvelle session maintenant (lancement immédiat) et la voir apparaître dans la liste des sessions.",
|
|
128
|
+
detail:
|
|
129
|
+
"La modale « Nouvelle session » propose le timing « Maintenant » ; après confirmation, la ligne apparaît dans la colonne Sessions avec l’état live.",
|
|
130
|
+
example:
|
|
131
|
+
"Nouvelle session → « Démarrer la session » : une ligne « Live » remonte en tête de liste avec l’heure de début.",
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
implemented: true,
|
|
135
|
+
integrationChecked: true,
|
|
136
|
+
e2eChecked: true,
|
|
137
|
+
text: "Créer une session passée en saisissant le début et la fin (heure locale), puis la voir apparaître dans la liste des sessions.",
|
|
138
|
+
detail:
|
|
139
|
+
"Le mode « Dans le passé » impose début et fin en heure locale ; l’intervalle fermé est historisé sans nécessairement arrêter une session déjà ouverte.",
|
|
140
|
+
example:
|
|
141
|
+
"Hier 14:00–16:00 : une ligne terminée s’affiche avec ces bornes pendant qu’une autre session peut rester active.",
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
implemented: true,
|
|
145
|
+
integrationChecked: true,
|
|
146
|
+
e2eChecked: true,
|
|
147
|
+
text: "Créer une session sans limite optionnelle (ni durée max, ni fenêtre calendrier, ni jours/plage horaire).",
|
|
148
|
+
detail:
|
|
149
|
+
"La portée « Aucune limite » évite tout plafond mural, toute fenêtre calendaire et tout gabarit hebdomadaire pour cette session.",
|
|
150
|
+
example:
|
|
151
|
+
"Laisser « Aucune limite » puis démarrer : seuls des rappels ultérieurs (collecteur / bannières) peuvent signaler un dérapage.",
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
implemented: true,
|
|
155
|
+
integrationChecked: true,
|
|
156
|
+
e2eChecked: true,
|
|
157
|
+
text: "Encadrer une session par une durée maximale d’horloge murale (rappel de cadre).",
|
|
158
|
+
detail:
|
|
159
|
+
"La durée maximale s’exprime en heures de mur accumulé ; l’interface peut signaler le dépassement ou le voisinage du seuil selon les paramètres.",
|
|
160
|
+
example:
|
|
161
|
+
"4 h max : au-delà, la durée affichée peut passer en alerte visuelle (couleur ou clignotement lent).",
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
implemented: true,
|
|
165
|
+
integrationChecked: true,
|
|
166
|
+
e2eChecked: true,
|
|
167
|
+
text: "Définir une fenêtre calendrier (dates de début et de fin) pour une session.",
|
|
168
|
+
detail:
|
|
169
|
+
"Les dates « Du » et « Au » sont inclusives dans le fuseau / affichage local du tableau de bord ; elles encadrent la session pour les rappels.",
|
|
170
|
+
example:
|
|
171
|
+
"Du 3 au 10 du mois pour une série de séances dans une même fenêtre contractuelle.",
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
implemented: true,
|
|
175
|
+
integrationChecked: true,
|
|
176
|
+
e2eChecked: true,
|
|
177
|
+
text: "Définir une semaine avec jours choisis et plage horaire.",
|
|
178
|
+
detail:
|
|
179
|
+
"On coche les jours actifs et, au besoin, une fenêtre horaire quotidienne ; les fenêtres overnight (ex. 22:00–06:00) sont prises en charge.",
|
|
180
|
+
example:
|
|
181
|
+
"Mar/Jeu + 18:30–21:00 pour des créneaux fixes après la journée bureau.",
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
implemented: true,
|
|
185
|
+
integrationChecked: true,
|
|
186
|
+
e2eChecked: true,
|
|
187
|
+
text: "Utiliser les raccourcis « Aujourd’hui » / « Maintenant » dans les sélecteurs de dates et heures.",
|
|
188
|
+
detail:
|
|
189
|
+
"Les boutons posent la date du jour ou l’instant courant dans les champs de la modale sans saisie complète.",
|
|
190
|
+
example:
|
|
191
|
+
"« Aujourd’hui » sur une date « Du » ; « Maintenant » comme fin d’une session passée qui vient de se terminer.",
|
|
192
|
+
},
|
|
193
|
+
{
|
|
194
|
+
implemented: true,
|
|
195
|
+
integrationChecked: true,
|
|
196
|
+
e2eChecked: true,
|
|
197
|
+
text: "Voir la durée de session suivre une horloge murale (temps réel) tant que la session est vivante et non en pause session.",
|
|
198
|
+
detail:
|
|
199
|
+
"La carte session affiche une durée murale qui avance tant que la session n’est ni terminée ni en pause session (pause murale).",
|
|
200
|
+
example:
|
|
201
|
+
"Sans pause session, la ligne « Durée » suit la seconde ; « Pause session » fige l’affichage jusqu’à reprise.",
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
implemented: true,
|
|
205
|
+
integrationChecked: true,
|
|
206
|
+
e2eChecked: true,
|
|
207
|
+
text: "Mettre la session en pause : le mur cesse de s’accumuler sans confondre avec la pause d’une tâche.",
|
|
208
|
+
detail:
|
|
209
|
+
"La pause session agit sur le mur collecteur ; le serveur peut aussi figer les minuteurs de tâches et sous-tâches actifs via un instantané `sessionPauseContext` (reprise ciblée). La pause d’une tâche seule reste sur la carte et n’est pas synonyme de pause globale.",
|
|
210
|
+
example:
|
|
211
|
+
"Pause session → bandeau « Session en pause » ; une tâche peut encore avoir été laissée en pause manuelle avant la pause session.",
|
|
212
|
+
},
|
|
213
|
+
{
|
|
214
|
+
implemented: true,
|
|
215
|
+
integrationChecked: true,
|
|
216
|
+
e2eChecked: true,
|
|
217
|
+
text: "Déclencher une pause globale avec une modale de confirmation qui résume effets, durées et périmètre.",
|
|
218
|
+
detail:
|
|
219
|
+
"La pause globale aligne la session live, les minuteurs de tâches et les sous-tâches concernées ; la modale affiche un résumé (mur, temps enregistrés, volumes) avant d’appliquer ou d’annuler.",
|
|
220
|
+
example:
|
|
221
|
+
"Barre d’outils → contrôle « Pause session, tâches et sous-tâches dans ce contexte » → lecture du résumé → « Pause maintenant » ou retour en arrière.",
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
implemented: true,
|
|
225
|
+
integrationChecked: true,
|
|
226
|
+
e2eChecked: true,
|
|
227
|
+
text: "Quand la session live est en pause (murale ou pause globale), reconnaître l’état sur la carte Session en cours (pastille), voir un rappel central sur fond voilé tout en gardant la barre du haut utilisable, et repérer le bouton de reprise de pause globale mis en évidence lorsque la pause globale est active.",
|
|
228
|
+
detail:
|
|
229
|
+
"Serveur : `setPaused` enregistre `sessionPauseContext` pour les minuteurs actifs et ne restaure à la reprise que ce gel ; une entrée en pause globale efface tout `sessionPauseContext` résiduel. UI : pastilles « En pause » / « Pause globale » (`SelectedSessionSidebarBlock.tsx`), overlay plein écran `DashboardPauseBackdrop` sous l’en-tête (`z-40` vs `z-50`), anneau ambre sur le contrôle pause globale lorsqu’il affiche lecture (`AppShellRouteNav.tsx`).",
|
|
230
|
+
example:
|
|
231
|
+
"Après pause globale : pastille « Pause globale », voile « Pause globale active », bouton lecture cerclé dans la barre d’outils.",
|
|
232
|
+
},
|
|
233
|
+
{
|
|
234
|
+
implemented: true,
|
|
235
|
+
integrationChecked: true,
|
|
236
|
+
e2eChecked: true,
|
|
237
|
+
text: "Terminer une session avec une raison de clôture facultative (dans les temps, avance, dépassement, autre + note).",
|
|
238
|
+
detail:
|
|
239
|
+
"La modale de fin propose catégories facultatives et une note libre ; elles sont attachées à l’instantané d’historique avec l’heure de fin courante.",
|
|
240
|
+
example:
|
|
241
|
+
"« Fin anticipée » + « livraison reportée » puis « Terminer la session » depuis la barre latérale ou la liste.",
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
implemented: true,
|
|
245
|
+
integrationChecked: true,
|
|
246
|
+
e2eChecked: true,
|
|
247
|
+
text: "Ajouter une note libre à une session et la revoir dans la barre latérale de session.",
|
|
248
|
+
detail:
|
|
249
|
+
"Le champ « Note de session » se trouve dans la carte session (colonne Sessions). La valeur est envoyée au serveur lorsque le champ perd le focus et reste visible après rechargement.",
|
|
250
|
+
example:
|
|
251
|
+
"Saisir « Point revue client — préparer démo » avant une réunion pour retrouver le contexte dans la même carte.",
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
implemented: true,
|
|
255
|
+
integrationChecked: true,
|
|
256
|
+
e2eChecked: true,
|
|
257
|
+
text: "Voir un aperçu de note de session (icône + extrait) dans la liste des sessions et dans la liste des archives.",
|
|
258
|
+
detail:
|
|
259
|
+
"Quand une note existe, la liste affiche une icône et un extrait tronqué à côté du titre de session ; même logique pour les entrées archivées pertinentes.",
|
|
260
|
+
example:
|
|
261
|
+
"Une ligne montre « …compte-rendu… » sous le nom lorsque la note contient ce texte.",
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
implemented: true,
|
|
265
|
+
integrationChecked: true,
|
|
266
|
+
e2eChecked: true,
|
|
267
|
+
text: "Voir la session live mise en évidence lorsque l’heure de début officielle est encore dans le futur (cercle d’état et fond distincts dans la liste).",
|
|
268
|
+
detail:
|
|
269
|
+
"Tant que l’horloge réelle n’a pas atteint le début officiel enregistré, la ligne live garde un style « pas encore démarrée » (point et fond différents).",
|
|
270
|
+
example:
|
|
271
|
+
"Session étiquetée pour 18:00 alors qu’il est 14:00 : la ligne reste visuellement distincte jusqu’à 18:00.",
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
implemented: true,
|
|
275
|
+
integrationChecked: true,
|
|
276
|
+
e2eChecked: true,
|
|
277
|
+
text: "Programmer une heure de fin sur une session live : la session se termine automatiquement à l’échéance (instantané en historique).",
|
|
278
|
+
detail:
|
|
279
|
+
"Une échéance de fin session peut être définie ; au passage de l’instant programmé, la session est clôturée : le mur est consolidé jusqu’à cette heure, les tâches ouvertes sont figées comme après une fin de session en mode conserver, sans modale de clôture.",
|
|
280
|
+
example:
|
|
281
|
+
"Fin à 17:00 : sans clic supplémentaire, la session live devient une ligne d’historique à 17:00 si elle est encore ouverte.",
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
implemented: true,
|
|
285
|
+
integrationChecked: true,
|
|
286
|
+
e2eChecked: true,
|
|
287
|
+
text: "À la fermeture d’une session avec tâches encore ouvertes, choisir de les terminer ou de les transposer vers une nouvelle session en pause.",
|
|
288
|
+
detail:
|
|
289
|
+
"La modale de fin liste les tâches non terminées et propose de les laisser dans l’instantané (choix par défaut, équivalent « conserver »), de les marquer terminées ou de les déplacer vers une nouvelle session créée en pause. À distinguer : une fin automatique à l’heure planifiée (`scheduledEndAt`) ne passe pas par cette modale ; côté données, le comportement correspond à conserver les tâches ouvertes dans l’instantané (minuteurs figés), comme le premier choix de la modale. Un réglage produit pour imposer un autre mode par défaut à l’échéance n’est pas livré dans cette version.",
|
|
290
|
+
example:
|
|
291
|
+
"« Transposer vers une nouvelle session en pause » pour reporter un lot de correctifs sans tout fermer au même titre.",
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
implemented: true,
|
|
295
|
+
integrationChecked: true,
|
|
296
|
+
e2eChecked: true,
|
|
297
|
+
text: "Reprendre une session en pause depuis le bandeau ; reprendre une tâche relance aussi l’accumulation du mur.",
|
|
298
|
+
detail:
|
|
299
|
+
"Le bandeau « Session en pause » offre la reprise ; lorsqu’une tâche repasse en suivi actif selon les règles du dashboard, le mur peut à nouveau s’accumuler.",
|
|
300
|
+
example:
|
|
301
|
+
"Reprendre depuis le bandeau puis lancer une tâche : la durée murale augmente à nouveau.",
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
implemented: true,
|
|
305
|
+
integrationChecked: true,
|
|
306
|
+
e2eChecked: true,
|
|
307
|
+
text: "Configurer un gabarit de nom de session initial (codes date/heure, %UUID, fuseau aligné sur le tableau de bord).",
|
|
308
|
+
detail:
|
|
309
|
+
"Sous Paramètres → tableau de bord, un modèle de nom peut référencer la date, l’heure, un UUID court ou le fuseau utilisé pour l’affichage.",
|
|
310
|
+
example:
|
|
311
|
+
"« Session %Y-%m-%d » produit « Session 2026-05-08 » à chaque création.",
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
discarded: true,
|
|
315
|
+
implemented: false,
|
|
316
|
+
text: "Disposer d’une seule action « pause » identique qui applique le même effet au mur de session et à tous les minuteurs comme une primitive unique.",
|
|
317
|
+
detail:
|
|
318
|
+
"Décision : écartée. La pause globale couvre déjà le gel coordonné du mur et des minuteurs du contexte affiché ; la pause minuteur par tâche reste un réglage fin volontairement séparé.",
|
|
319
|
+
example:
|
|
320
|
+
"Ne pas fusionner en un seul bouton universel : conserver la pause globale (barre d’outils) et les pauses ciblées selon l’intention.",
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
discarded: true,
|
|
324
|
+
implemented: false,
|
|
325
|
+
text: "Obtenir la durée murale de session comme somme automatique des durées enregistrées des tâches.",
|
|
326
|
+
detail:
|
|
327
|
+
"Décision : écartée. Le mur suit des segments collecteur ; les totaux tâche sont agrégés à part dans les métriques et rapports.",
|
|
328
|
+
example:
|
|
329
|
+
"Comparer carte session et colonne Tâches : les deux lectures peuvent diverger — c’est attendu.",
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
id: "us-tasks",
|
|
335
|
+
title: "Tâches, étiquettes, projets, minuteurs",
|
|
336
|
+
stories: [
|
|
337
|
+
{
|
|
338
|
+
implemented: true,
|
|
339
|
+
integrationChecked: true,
|
|
340
|
+
e2eChecked: true,
|
|
341
|
+
text: "Créer une tâche avec titre et utiliser # pour étiquettes, @ pour projets productifs et ! pour projets personnels, avec aide à la saisie.",
|
|
342
|
+
detail:
|
|
343
|
+
"Le champ « sur quoi travaillez-vous » accepte dans le libellé des jetons #étiquette, @projet (travail) et !projet (personnel) ; ils sont interprétés au lancement (`startTask`), alimentent les listes d’étiquettes, `knownProjects` / `knownPersonalProjects`, et s’affichent sur la carte tâche. Depuis **1.0.0-beta.21**, le **`!`** peut être **collé** au mot précédent (sans espace), comme certaines formes de **`#`** — ex. `Courrier!perso#dentiste` ; le **`@`** reste en général après **espace** ou en **début** de segment pour limiter les faux positifs sur les courriels. Les suggestions (modèles, historique) complètent la saisie.",
|
|
344
|
+
example:
|
|
345
|
+
"Saisir « Correctifs auth #bugfix @mobile » ou « Repos #repos !perso », lancer le suivi : pastille projet ciel ou rose selon le jeton ; les rubriques connues se mettent à jour pour les prochains choix.",
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
implemented: true,
|
|
349
|
+
integrationChecked: true,
|
|
350
|
+
e2eChecked: false,
|
|
351
|
+
text: "Déclarer un projet personnel avec le jeton `!` (temps de suivi distinct des projets `@`), y compris en tâche passée, au renommage côté serveur et dans les modèles de tâche.",
|
|
352
|
+
detail:
|
|
353
|
+
"`lib/taskParsing.ts` (`parseTaskWithAutoTags`, `buildStartTaskFromDraft`, `formatProjectDisplay`, jetons `!p#t`) ; persistance `personalProject` sur les tâches et `knownPersonalProjects` sur le payload ; `server/actionDispatch.ts` et `server/actionTaskSession.ts` (`startTask`, `addHistoricalTask`, `updateTask`, `saveTaskTemplate`, `renameProjectMetadata` avec `personalOnly` côté API) ; UI : `TaskFocusPanel`, `TaskSessionLiveCard`, `SavedProjectPicker`, `taskFieldStyles` (pastilles rose). Tests unitaires : `lib/taskTemplateDraft.test.ts` (modèles `!`).",
|
|
354
|
+
example:
|
|
355
|
+
"« Lecture !perso » en session live : la carte affiche le projet en rose ; enregistrer comme modèle conserve `!perso` dans Paramètres → Modèles de tâche.",
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
implemented: true,
|
|
359
|
+
integrationChecked: true,
|
|
360
|
+
e2eChecked: false,
|
|
361
|
+
text: "Utiliser des étiquettes sous portée projet personnel au format `!projet#sous-étiquette` (équivalent de `@projet#code` pour le travail).",
|
|
362
|
+
detail:
|
|
363
|
+
"`TagPills`, `formatTagDisplayForTask` et le parsing scoped dans `lib/taskParsing.ts` propagent le contexte `personalProject` pour l’affichage. Le jeton **`!`** de portée peut être **collé** au mot précédent (**1.0.0-beta.21**, `findProjectTokens`).",
|
|
364
|
+
example:
|
|
365
|
+
"« Suivi !dentiste#urgent » : l’étiquette scoped s’affiche avec le préfixe `!`.",
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
implemented: true,
|
|
369
|
+
integrationChecked: true,
|
|
370
|
+
e2eChecked: false,
|
|
371
|
+
text: "Sur la modale Gantt d’une tâche, distinguer le temps productif (`@`) et le temps personnel (`!`) : barres de couleurs différentes et totaux séparés.",
|
|
372
|
+
detail:
|
|
373
|
+
"`lib/taskTimelineGantt.ts`, `components/dashboard/TaskTimelineGanttModal.tsx`, chaînes `lib/dashboardCopy.ts` (légende et résumés productif / personnel).",
|
|
374
|
+
},
|
|
375
|
+
{
|
|
376
|
+
implemented: true,
|
|
377
|
+
integrationChecked: true,
|
|
378
|
+
e2eChecked: true,
|
|
379
|
+
text: "Démarrer, mettre en pause et terminer le minuteur d’une tâche indépendamment du mur de session.",
|
|
380
|
+
detail:
|
|
381
|
+
"Le cycle du minuteur sur une tâche (démarrage, pause volontaire sur la ligne, fin) est distinct du temps mural : la pause ou la reprise du mur de session (`setPaused`) ne se confond pas avec la pause du minuteur d’une tâche (`setTaskTimerPaused` / reprise) ; les segments et contextes côté serveur gardent cette séparation. Un scénario Playwright clique sur la pause depuis la carte et vérifie le passage en « Reprendre » (`e2e/task-lifecycle.spec.ts`).",
|
|
382
|
+
example:
|
|
383
|
+
"Mettre une tâche en pause depuis sa carte puis reprendre uniquement la session : le minuteur de cette tâche reste en pause jusqu’à une reprise explicite sur la ligne. « Terminer » depuis la carte enregistre la fin du suivi pour cette tâche.",
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
implemented: true,
|
|
387
|
+
text: "Lorsqu’un autre minuteur tourne, choisir pause, fin ou parallélisme et mémoriser le choix pour les prochains lancements.",
|
|
388
|
+
},
|
|
389
|
+
{
|
|
390
|
+
implemented: true,
|
|
391
|
+
text: "Ajouter des sous-tâches, les cocher et les réordonner par glisser-déposer.",
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
implemented: true,
|
|
395
|
+
text: "Enregistrer un modèle de tâche depuis une tâche en cours, en pause ou terminée, le gérer dans Paramètres, et le retrouver (saisie avec debounce, suggestions « Modèle : », palette Ctrl+K), y compris avec projet personnel `!`.",
|
|
396
|
+
detail:
|
|
397
|
+
"`saveTaskTemplate` enregistre `personalProject` ; `lib/taskTemplateDraft.ts` (`formatTaskTemplateDraftLine`, `parseTaskTemplatesFromPayload`, `buildTaskTemplateSignature` avec segment travail/personnel) ; `SettingsTaskTemplatesSection.tsx`.",
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
implemented: true,
|
|
401
|
+
text: "Saisir une tâche en direct ou la reporter dans le passé avec début et fin explicites.",
|
|
402
|
+
},
|
|
403
|
+
{
|
|
404
|
+
implemented: true,
|
|
405
|
+
text: "Ajouter une note libre sur une tâche (directe ou passée) pour documenter le contexte de l’activité.",
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
implemented: true,
|
|
409
|
+
text: "Afficher la note de tâche avant la liste des sous-tâches pour garder le contexte visible pendant le découpage.",
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
implemented: true,
|
|
413
|
+
text: "Corriger les bornes temporelles d’une tâche en choisissant de conserver la durée système, une durée manuelle ou un recalcul depuis début/fin.",
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
implemented: true,
|
|
417
|
+
text: "Voir les tâches dont le début (non terminée) est encore dans le futur regroupées sous « Planifiées » avec un style distinct, sans minuteur « en direct » ni apparence « terminée barrée » prématurée ; pour une tâche terminée, le même affichage « planifié » seulement si début et fin sont encore tous deux dans le futur.",
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
implemented: true,
|
|
421
|
+
text: "Lorsqu’une tâche sort de « Planifiées » et devient en cours alors qu’un autre minuteur tournait déjà, résoudre pause / terminer / parallèle sur les tâches déjà au minuteur (même préférence mémorisée que pour un lancement manuel) et faire pulser l’icône Tableau de bord hors route pour signaler l’attention.",
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
implemented: true,
|
|
425
|
+
text: "Programmer une heure de fin sur une tâche en cours : la tâche se termine automatiquement à l’échéance (carte passée en terminée à cet instant).",
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
implemented: true,
|
|
429
|
+
text: "Lancer KronoFocus depuis une tâche pour alterner travail et pauses sans perdre le lien avec la tâche.",
|
|
430
|
+
},
|
|
431
|
+
{
|
|
432
|
+
discarded: true,
|
|
433
|
+
implemented: false,
|
|
434
|
+
text: "Traiter en rapport le temps mur de session et le temps tâche comme une seule mesure interchangeable sans les distinguer.",
|
|
435
|
+
detail:
|
|
436
|
+
"Décision : écartée. Les rapports et agrégats distinguent volontairement le temps mur (segments collecteur) du temps tâche ; les deux lectures complètent une analyse honnête.",
|
|
437
|
+
example:
|
|
438
|
+
"Comparer les totaux « mur » et « tâche » dans les vues reporting — les écarts sont informatifs, pas des erreurs à effacer.",
|
|
439
|
+
},
|
|
440
|
+
],
|
|
441
|
+
},
|
|
442
|
+
{
|
|
443
|
+
id: "us-kronofocus",
|
|
444
|
+
title: "KronoFocus",
|
|
445
|
+
stories: [
|
|
446
|
+
{
|
|
447
|
+
implemented: true,
|
|
448
|
+
text: "Activer l’affichage du minuteur KronoFocus dans l’en-tête et/ou près des actions de tâche selon les réglages.",
|
|
449
|
+
},
|
|
450
|
+
{
|
|
451
|
+
implemented: true,
|
|
452
|
+
text: "Enchaîner phases de travail, pause et longue pause avec durée au format HH:MM:SS.",
|
|
453
|
+
},
|
|
454
|
+
{
|
|
455
|
+
implemented: true,
|
|
456
|
+
text: "Réutiliser une durée depuis l’historique des durées récentes.",
|
|
457
|
+
},
|
|
458
|
+
{
|
|
459
|
+
implemented: true,
|
|
460
|
+
text: "Attacher le focus à la tâche active pour retrouver le lien dans l’historique.",
|
|
461
|
+
},
|
|
462
|
+
],
|
|
463
|
+
},
|
|
464
|
+
{
|
|
465
|
+
id: "us-search-shortcuts",
|
|
466
|
+
title: "Recherche dans les données et raccourcis",
|
|
467
|
+
stories: [
|
|
468
|
+
{
|
|
469
|
+
implemented: true,
|
|
470
|
+
text: "Ouvrir la palette de recherche Spotlight (Ctrl+K / ⌘K) et filtrer sessions, tâches, modèles de tâche, étiquettes et projets ; choisir un modèle remplit le brouillon nouvelle tâche.",
|
|
471
|
+
},
|
|
472
|
+
{
|
|
473
|
+
implemented: true,
|
|
474
|
+
text: "Filtrer les rubriques du guide utilisateur sur /guide avec le champ en tête de page.",
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
implemented: true,
|
|
478
|
+
text: "Consulter et personnaliser les raccourcis clavier du tableau de bord depuis la modale dédiée.",
|
|
479
|
+
},
|
|
480
|
+
],
|
|
481
|
+
},
|
|
482
|
+
{
|
|
483
|
+
id: "us-reporting",
|
|
484
|
+
title: "Rapports et agrégats",
|
|
485
|
+
stories: [
|
|
486
|
+
{
|
|
487
|
+
implemented: true,
|
|
488
|
+
text: "Choisir une plage de dates avec préréglages jour / semaine / mois / année selon ce que l’interface expose.",
|
|
489
|
+
},
|
|
490
|
+
{
|
|
491
|
+
implemented: true,
|
|
492
|
+
text: "Restreindre les vues par une ou plusieurs étiquettes lorsque les données sont basées sur les tâches.",
|
|
493
|
+
},
|
|
494
|
+
{
|
|
495
|
+
implemented: true,
|
|
496
|
+
text: "Lire résumés, tableaux par jour, répartition par projet et par étiquette.",
|
|
497
|
+
},
|
|
498
|
+
{
|
|
499
|
+
implemented: true,
|
|
500
|
+
integrationChecked: true,
|
|
501
|
+
e2eChecked: false,
|
|
502
|
+
text: "Sur la page Rapports, consulter les grilles par projet basées sur le temps productif (`@`) sans y mélanger le temps des projets personnels (`!`).",
|
|
503
|
+
detail:
|
|
504
|
+
"`app/reporting/page.tsx` appelle `aggregateProjectTaskMinutesByDay` avec le scope par défaut `work` (`lib/reportingAggregate.ts`, `taskMatchesReportingProjectScope`) : les minutes des tâches `personalProject: true` sont exclues de ces grilles.",
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
implemented: false,
|
|
508
|
+
text: "Afficher sur la page Rapports un bloc ou calendrier dédié au temps personnel (`!`), parallèle aux vues projets `@`, avec les mêmes filtres de plage et d’étiquettes.",
|
|
509
|
+
detail:
|
|
510
|
+
"L’agrégat côté bibliothèque accepte `taskProjectScope: \"personal\"` (`aggregateProjectTaskMinutesByDay`, `aggregateReportingByProject`, `aggregateTagTaskMinutesByDayAndWeek`) ; aucune seconde grille « personnel » n’est branchée dans l’UI au 2026-05-12.",
|
|
511
|
+
},
|
|
512
|
+
{
|
|
513
|
+
implemented: true,
|
|
514
|
+
text: "Voir un total de temps tâche non concurrent (fusion des chevauchements) par jour pour comparaison.",
|
|
515
|
+
},
|
|
516
|
+
{
|
|
517
|
+
implemented: true,
|
|
518
|
+
text: "Configurer le jour de début de semaine pour les vues calendaires cohérentes avec mon organisation.",
|
|
519
|
+
},
|
|
520
|
+
{
|
|
521
|
+
implemented: true,
|
|
522
|
+
text: "Relancer la visite guidée des rapports depuis les paramètres.",
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
implemented: true,
|
|
526
|
+
text: "Lorsque l’environnement fournit une heure de début prévue, voir des indicateurs d’assiduité / d’écart sur les sessions.",
|
|
527
|
+
},
|
|
528
|
+
{
|
|
529
|
+
implemented: true,
|
|
530
|
+
text: "Voir des signaux honnêtes sur le travail encore non finalisé dans la plage sélectionnée.",
|
|
531
|
+
},
|
|
532
|
+
],
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
id: "us-settings-data",
|
|
536
|
+
title: "Paramètres, export, intégrations, danger",
|
|
537
|
+
stories: [
|
|
538
|
+
{
|
|
539
|
+
implemented: true,
|
|
540
|
+
text: "Adapter le profil d’usage pour masquer des blocs peu pertinents (ex. lignes de code pour un profil non développeur).",
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
implemented: true,
|
|
544
|
+
text: "Configurer horaire de travail, sessions planifiées, collecte et tampons.",
|
|
545
|
+
},
|
|
546
|
+
{
|
|
547
|
+
implemented: true,
|
|
548
|
+
text: "Gérer étiquettes avancées et migration d’étiquettes entre portée globale et portée projet.",
|
|
549
|
+
},
|
|
550
|
+
{
|
|
551
|
+
implemented: true,
|
|
552
|
+
text: "Renommer une étiquette ou un projet en inline (crayon, saisie directe, Entrée/Échap), avec avertissement d’impact reporting et option « ne plus afficher ».",
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
implemented: false,
|
|
556
|
+
text: "Gérer et renommer depuis Paramètres la liste des projets personnels connus (`knownPersonalProjects`, jetons `!`) de la même façon que les projets `@`.",
|
|
557
|
+
detail:
|
|
558
|
+
"Le payload et les actions serveur maintiennent `knownPersonalProjects` et `renameProjectMetadata` avec `personalOnly` ; la section Projets / étiquettes avancées n’expose pas encore d’inline dédié aux `!` au 2026-05-12.",
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
implemented: true,
|
|
562
|
+
text: "Activer des commutateurs de correction d’horodatage pour les bornes de tâches et de sessions.",
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
implemented: true,
|
|
566
|
+
text: "Brancher Git et MongoDB de façon opt-in.",
|
|
567
|
+
},
|
|
568
|
+
{
|
|
569
|
+
implemented: true,
|
|
570
|
+
text: "Obtenir une sauvegarde via l’API d’export et les options prévues dans l’interface.",
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
implemented: true,
|
|
574
|
+
text: "Effacer l’historique dans une zone dangereuse protégée par confirmation explicite.",
|
|
575
|
+
},
|
|
576
|
+
{
|
|
577
|
+
implemented: true,
|
|
578
|
+
text: "En développement local (next dev), utiliser un coffre de données distinct et optionnellement partagé avec la prod.",
|
|
579
|
+
},
|
|
580
|
+
{
|
|
581
|
+
implemented: true,
|
|
582
|
+
text: "Configurer le Web, l’API et la relance de visites guidées depuis les paramètres.",
|
|
583
|
+
},
|
|
584
|
+
],
|
|
585
|
+
},
|
|
586
|
+
{
|
|
587
|
+
id: "us-architecture-quality",
|
|
588
|
+
title: "Architecture, qualité, documentation",
|
|
589
|
+
stories: [
|
|
590
|
+
{
|
|
591
|
+
implemented: true,
|
|
592
|
+
text: "Bénéficier de mutations qui passent par un point d’entrée serveur unique pour les invariants.",
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
implemented: true,
|
|
596
|
+
text: "Recharger la page et retrouver l’état depuis le payload JSON persisté.",
|
|
597
|
+
},
|
|
598
|
+
{
|
|
599
|
+
implemented: true,
|
|
600
|
+
text: "Disposer de chaînes d’interface en français et en anglais pour les surfaces visibles.",
|
|
601
|
+
},
|
|
602
|
+
{
|
|
603
|
+
implemented: true,
|
|
604
|
+
text: "S’appuyer sur une suite de tests de bout en bout Playwright pour les parcours critiques.",
|
|
605
|
+
},
|
|
606
|
+
{
|
|
607
|
+
implemented: true,
|
|
608
|
+
text: "Consulter cette liste de user stories sur /implementation pour suivre le périmètre livré.",
|
|
609
|
+
},
|
|
610
|
+
{
|
|
611
|
+
implemented: false,
|
|
612
|
+
text: "Remplacer le guide utilisateur (/guide) par cette page pour apprendre le produit.",
|
|
613
|
+
},
|
|
614
|
+
],
|
|
615
|
+
},
|
|
616
|
+
],
|
|
617
|
+
};
|
|
618
|
+
|
|
619
|
+
const enBundle: ImplementationNotesBundle = {
|
|
620
|
+
pageTitle: "User stories — implementation status",
|
|
621
|
+
pageSubtitle:
|
|
622
|
+
"An exhaustive list of user intentions: each row shows whether the capability is shipped (✓) or missing / out of scope (✗). Checkboxes on the right mark a row as reviewed on this device — long technical detail stays in the repo doc.",
|
|
623
|
+
statusKeyLine:
|
|
624
|
+
"Green ✓ = implemented in the product · Red ✗ = not shipped or intentionally excluded.",
|
|
625
|
+
lastUpdated: "Last updated: 2026-05-12 (v1.0.0-beta.21)",
|
|
626
|
+
repositoryDocLabel: "Repository document: docs/IMPLEMENTATION_DETAILS.md",
|
|
627
|
+
storyGroupsHeading: "User story inventory",
|
|
628
|
+
implementationColumnLabel: "Implementation",
|
|
629
|
+
storyColumnLabel: "User story",
|
|
630
|
+
integrationColumnLabel: "Integration",
|
|
631
|
+
e2eColumnLabel: "E2E",
|
|
632
|
+
openChangelogAria: "Open user changelog",
|
|
633
|
+
openChangelogTooltip: "Open changelog",
|
|
634
|
+
storyDetailsToggleExpand: "Show details and example",
|
|
635
|
+
storyDetailsToggleCollapse: "Hide details and example",
|
|
636
|
+
storyDetailExampleLabel: "Example:",
|
|
637
|
+
storyExpandHintLine:
|
|
638
|
+
"Some rows include a chevron — expand for extra detail and a smaller-type example.",
|
|
639
|
+
storyDiscardedBadge: "Discarded",
|
|
640
|
+
storyDiscardedLegendLine:
|
|
641
|
+
"Rows tagged “Discarded” record intents we deliberately closed; they stay listed so the same debate does not reopen by mistake.",
|
|
642
|
+
storyGroups: [
|
|
643
|
+
{
|
|
644
|
+
id: "us-shell",
|
|
645
|
+
title: "Chrome, navigation, comfort",
|
|
646
|
+
lead: "Shared toolbar, main routes, theme and language.",
|
|
647
|
+
stories: [
|
|
648
|
+
{
|
|
649
|
+
implemented: true,
|
|
650
|
+
text: "Move between dashboard, guide, reporting, settings, and this page from the top icons.",
|
|
651
|
+
detail:
|
|
652
|
+
"Targets include the dashboard, reporting charts, settings, the in-app guide, this implementation page, action logs, licenses, and the user changelog; icons sit in the shared header toolbar.",
|
|
653
|
+
example:
|
|
654
|
+
"From the dashboard, clicking the book icon opens /guide while keeping the session query (?session=) when it is set.",
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
implemented: true,
|
|
658
|
+
text: "See a live-updating wall clock in the header toolbar (time zone and 12/24 h from web dashboard settings).",
|
|
659
|
+
},
|
|
660
|
+
{
|
|
661
|
+
implemented: true,
|
|
662
|
+
text: "For a given release (e.g. 1.0.0-beta.21), cross-check the matching **CHANGELOG** section, the shipped stories on this page and `docs/IMPLEMENTATION_DETAILS.md`, then tick **Integration** / **E2E** as appropriate.",
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
implemented: true,
|
|
666
|
+
text: "Open the Licenses page from the document icon when I am already on Licenses.",
|
|
667
|
+
},
|
|
668
|
+
{
|
|
669
|
+
implemented: true,
|
|
670
|
+
text: "Switch between light and dark theme for visual comfort.",
|
|
671
|
+
},
|
|
672
|
+
{
|
|
673
|
+
implemented: true,
|
|
674
|
+
text: "Refresh server state without reloading the whole app.",
|
|
675
|
+
},
|
|
676
|
+
{
|
|
677
|
+
implemented: true,
|
|
678
|
+
text: "Pick UI language (FR / EN) with an optional persisted preference.",
|
|
679
|
+
},
|
|
680
|
+
{
|
|
681
|
+
implemented: true,
|
|
682
|
+
text: "Keep the dashboard session query (?session=) when navigating between routes.",
|
|
683
|
+
},
|
|
684
|
+
],
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
id: "us-sessions",
|
|
688
|
+
title: "Sessions — scope, wall clock, pause, end",
|
|
689
|
+
stories: [
|
|
690
|
+
{
|
|
691
|
+
implemented: true,
|
|
692
|
+
integrationChecked: true,
|
|
693
|
+
e2eChecked: true,
|
|
694
|
+
text: "Create a new session now (immediate start) and see it in the session list.",
|
|
695
|
+
detail:
|
|
696
|
+
"The New session modal offers Start now; after confirm, the row appears in the Sessions column as live.",
|
|
697
|
+
example:
|
|
698
|
+
"New session → Start session: a Live row appears at the top with your start time.",
|
|
699
|
+
},
|
|
700
|
+
{
|
|
701
|
+
implemented: true,
|
|
702
|
+
integrationChecked: true,
|
|
703
|
+
e2eChecked: true,
|
|
704
|
+
text: "Create a past session by entering start and end (local time), then see it in the session list.",
|
|
705
|
+
detail:
|
|
706
|
+
"In the past mode requires both bounds in browser-local time; the closed interval is saved to history without necessarily stopping an already running session.",
|
|
707
|
+
example:
|
|
708
|
+
"Yesterday 2:00–4:00 pm — a completed row shows those bounds while another session can stay active.",
|
|
709
|
+
},
|
|
710
|
+
{
|
|
711
|
+
implemented: true,
|
|
712
|
+
integrationChecked: true,
|
|
713
|
+
e2eChecked: true,
|
|
714
|
+
text: "Create a session with no optional limits (no max duration, no calendar window, no weekdays/time window).",
|
|
715
|
+
detail:
|
|
716
|
+
"No limit skips max wall duration, calendar bounds, and weekday/time-window scope for that session.",
|
|
717
|
+
example:
|
|
718
|
+
"Leave No limit checked and start — only later heartbeats/banners may warn if work runs long.",
|
|
719
|
+
},
|
|
720
|
+
{
|
|
721
|
+
implemented: true,
|
|
722
|
+
integrationChecked: true,
|
|
723
|
+
e2eChecked: true,
|
|
724
|
+
text: "Cap a session with a maximum wall-clock duration (nudge to reframe).",
|
|
725
|
+
detail:
|
|
726
|
+
"Max duration is expressed in hours of accumulated wall time; the UI can warn when you approach or exceed the cap depending on settings.",
|
|
727
|
+
example:
|
|
728
|
+
"4 h max — beyond that, duration styling may turn alert (color or slow blink).",
|
|
729
|
+
},
|
|
730
|
+
{
|
|
731
|
+
implemented: true,
|
|
732
|
+
integrationChecked: true,
|
|
733
|
+
e2eChecked: true,
|
|
734
|
+
text: "Set a calendar window (start and end dates) for a session.",
|
|
735
|
+
detail:
|
|
736
|
+
"From / To dates are inclusive in your dashboard display timezone; they frame expectations for reminders.",
|
|
737
|
+
example:
|
|
738
|
+
"March 3–10 for a fixed delivery sprint window.",
|
|
739
|
+
},
|
|
740
|
+
{
|
|
741
|
+
implemented: true,
|
|
742
|
+
integrationChecked: true,
|
|
743
|
+
e2eChecked: true,
|
|
744
|
+
text: "Define a week pattern with selected weekdays and a time window.",
|
|
745
|
+
detail:
|
|
746
|
+
"Pick active weekdays and optionally a daily time slot; overnight windows (e.g. 10:00 pm–6:00 am) are supported.",
|
|
747
|
+
example:
|
|
748
|
+
"Tue/Thu + 6:30–9:00 pm for after-hours sessions.",
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
implemented: true,
|
|
752
|
+
integrationChecked: true,
|
|
753
|
+
e2eChecked: true,
|
|
754
|
+
text: "Use “Today” / “Now” shortcuts in date and time pickers.",
|
|
755
|
+
detail:
|
|
756
|
+
"Buttons stamp today’s date or the current instant into modal fields without full typing.",
|
|
757
|
+
example:
|
|
758
|
+
"Today on a From date; Now as the end of a past session that just finished.",
|
|
759
|
+
},
|
|
760
|
+
{
|
|
761
|
+
implemented: true,
|
|
762
|
+
integrationChecked: true,
|
|
763
|
+
e2eChecked: true,
|
|
764
|
+
text: "See session duration follow wall-clock time while the session is live and not session-paused.",
|
|
765
|
+
detail:
|
|
766
|
+
"The session card shows wall duration advancing while the session is neither ended nor session-paused.",
|
|
767
|
+
example:
|
|
768
|
+
"Without session pause, Duration ticks each second; session pause freezes display until resume.",
|
|
769
|
+
},
|
|
770
|
+
{
|
|
771
|
+
implemented: true,
|
|
772
|
+
integrationChecked: true,
|
|
773
|
+
e2eChecked: true,
|
|
774
|
+
text: "Pause the session so wall time stops accruing, distinct from pausing a task timer.",
|
|
775
|
+
detail:
|
|
776
|
+
"Session pause targets the collector wall; the server may also freeze active task and subtask timers using a `sessionPauseContext` snapshot (selective restore). Task-only pause stays on the card and is not the same as global pause.",
|
|
777
|
+
example:
|
|
778
|
+
"Session pause → Live session paused banner; a task you left manually paused before session pause stays consistent with the snapshot rules.",
|
|
779
|
+
},
|
|
780
|
+
{
|
|
781
|
+
implemented: true,
|
|
782
|
+
integrationChecked: true,
|
|
783
|
+
e2eChecked: true,
|
|
784
|
+
text: "Start a global pause with a confirmation modal summarizing effects, durations, and scope.",
|
|
785
|
+
detail:
|
|
786
|
+
"Global pause aligns the live session with affected task and subtask timers; the modal summarizes wall time, recorded times, and counts before you confirm or cancel.",
|
|
787
|
+
example:
|
|
788
|
+
"Toolbar → “Pause session, tasks, and subtasks in this context” → read the summary → “Pause now” or dismiss.",
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
implemented: true,
|
|
792
|
+
integrationChecked: true,
|
|
793
|
+
e2eChecked: true,
|
|
794
|
+
text: "When the live session is paused (wall-clock or global pause), recognize it on the Active session card (chip), see a centered reminder on a dimmed overlay while the top bar stays usable, and spot the global resume control highlighted when global pause is active.",
|
|
795
|
+
detail:
|
|
796
|
+
"Server: `setPaused` stores `sessionPauseContext` for active timers and restores only that snapshot on resume; starting global pause clears any leftover `sessionPauseContext`. UI: “Paused” / “Global pause” chips (`SelectedSessionSidebarBlock.tsx`), full-screen `DashboardPauseBackdrop` under the header (`z-40` vs `z-50`), amber ring on the global pause control when it shows play (`AppShellRouteNav.tsx`).",
|
|
797
|
+
example:
|
|
798
|
+
"After global pause: “Global pause” chip, “Global pause is on” overlay, circled play button in the toolbar.",
|
|
799
|
+
},
|
|
800
|
+
{
|
|
801
|
+
implemented: true,
|
|
802
|
+
integrationChecked: true,
|
|
803
|
+
e2eChecked: true,
|
|
804
|
+
text: "End a session with an optional closure reason (on time, early, overrun, other + note).",
|
|
805
|
+
detail:
|
|
806
|
+
"The end-session modal offers optional categories and a free-text note; both are stored on the history snapshot with the current end time.",
|
|
807
|
+
example:
|
|
808
|
+
"Ended early + “release slipped” then End session from the sidebar or row controls.",
|
|
809
|
+
},
|
|
810
|
+
{
|
|
811
|
+
implemented: true,
|
|
812
|
+
integrationChecked: true,
|
|
813
|
+
e2eChecked: true,
|
|
814
|
+
text: "Add a free-text note to a session and review it in the session sidebar.",
|
|
815
|
+
detail:
|
|
816
|
+
"The “Session note” field lives in the session card (Sessions column). It is saved on blur and survives a full page reload.",
|
|
817
|
+
example:
|
|
818
|
+
"Type “Client review checkpoint — prep demo” before a meeting to keep context in the same card.",
|
|
819
|
+
},
|
|
820
|
+
{
|
|
821
|
+
implemented: true,
|
|
822
|
+
integrationChecked: true,
|
|
823
|
+
e2eChecked: true,
|
|
824
|
+
text: "See a session-note preview (icon + excerpt) in the sessions list and in archived sessions.",
|
|
825
|
+
detail:
|
|
826
|
+
"When a note exists, the list shows an icon and truncated excerpt next to the session title; archived rows follow the same pattern where applicable.",
|
|
827
|
+
example:
|
|
828
|
+
"A row shows “…stand-up notes…” under the title when that text is in the note.",
|
|
829
|
+
},
|
|
830
|
+
{
|
|
831
|
+
implemented: true,
|
|
832
|
+
integrationChecked: true,
|
|
833
|
+
e2eChecked: true,
|
|
834
|
+
text: "See the live session row highlighted when the official start time is still in the future (status dot and distinct row styling in the list).",
|
|
835
|
+
detail:
|
|
836
|
+
"Until real time reaches the recorded official start, the live row keeps a “not yet started” look (dot + background).",
|
|
837
|
+
example:
|
|
838
|
+
"Official start at 6:00 pm while it is 2:00 pm — styling stays distinct until 6:00 pm.",
|
|
839
|
+
},
|
|
840
|
+
{
|
|
841
|
+
implemented: true,
|
|
842
|
+
integrationChecked: true,
|
|
843
|
+
e2eChecked: true,
|
|
844
|
+
text: "Schedule an end time on a live session so it automatically ends when the deadline is reached (snapshot moves to history).",
|
|
845
|
+
detail:
|
|
846
|
+
"You can set a scheduled session end time; when real time reaches it, the session closes: wall time is finalized up to that instant, open tasks are frozen like ending with keep (no closure modal).",
|
|
847
|
+
example:
|
|
848
|
+
"End at 5:00 pm — without extra clicks, the live session becomes a history row at 5:00 pm if it was still open.",
|
|
849
|
+
},
|
|
850
|
+
{
|
|
851
|
+
implemented: true,
|
|
852
|
+
integrationChecked: true,
|
|
853
|
+
e2eChecked: true,
|
|
854
|
+
text: "When closing a session with open tasks, finish them or move them to a new paused session.",
|
|
855
|
+
detail:
|
|
856
|
+
"The end-session modal lists unfinished work and lets you keep it in the snapshot (default, same semantics as leaving tasks open), finish it with this session, or move it into a newly created paused session. Contrast: a scheduled automatic end (`scheduledEndAt`) does not show this modal; server-side it matches keep (open tasks stay in the snapshot with timers stopped). A user preference to pick a different default at scheduled end is not shipped in this version.",
|
|
857
|
+
example:
|
|
858
|
+
"Move open tasks to a new paused session to carry a bugfix batch without closing every card.",
|
|
859
|
+
},
|
|
860
|
+
{
|
|
861
|
+
implemented: true,
|
|
862
|
+
integrationChecked: true,
|
|
863
|
+
e2eChecked: true,
|
|
864
|
+
text: "Resume a paused session from the banner; resuming a task also resumes wall accumulation.",
|
|
865
|
+
detail:
|
|
866
|
+
"The Live session paused banner offers resume; when a task returns to active tracking under dashboard rules, wall time can accrue again.",
|
|
867
|
+
example:
|
|
868
|
+
"Resume from the banner then start a task — wall duration increases again.",
|
|
869
|
+
},
|
|
870
|
+
{
|
|
871
|
+
implemented: true,
|
|
872
|
+
integrationChecked: true,
|
|
873
|
+
e2eChecked: true,
|
|
874
|
+
text: "Configure an initial session name template (date/time codes, %UUID, timezone aligned with the dashboard).",
|
|
875
|
+
detail:
|
|
876
|
+
"Under Settings → web dashboard, a name pattern can include date, time, short UUID, or the display timezone token.",
|
|
877
|
+
example:
|
|
878
|
+
"“Session %Y-%m-%d” becomes “Session 2026-05-08” on each create.",
|
|
879
|
+
},
|
|
880
|
+
{
|
|
881
|
+
discarded: true,
|
|
882
|
+
implemented: false,
|
|
883
|
+
text: "Have a single “pause” action that applies the same effect to wall time and all task timers as one primitive.",
|
|
884
|
+
detail:
|
|
885
|
+
"Decision: discarded. Global pause already freezes the wall and timers for the displayed context together; per-task pause stays a separate fine control on purpose.",
|
|
886
|
+
example:
|
|
887
|
+
"We keep global pause (toolbar) and targeted pauses instead of one universal button.",
|
|
888
|
+
},
|
|
889
|
+
{
|
|
890
|
+
discarded: true,
|
|
891
|
+
implemented: false,
|
|
892
|
+
text: "Get wall session duration as the automatic sum of recorded task durations.",
|
|
893
|
+
detail:
|
|
894
|
+
"Decision: discarded. Wall time follows collector segments; task totals are aggregated separately in metrics and reporting.",
|
|
895
|
+
example:
|
|
896
|
+
"Compare the session card with tasks — both views may differ; that is expected.",
|
|
897
|
+
},
|
|
898
|
+
],
|
|
899
|
+
},
|
|
900
|
+
{
|
|
901
|
+
id: "us-tasks",
|
|
902
|
+
title: "Tasks, tags, projects, timers",
|
|
903
|
+
stories: [
|
|
904
|
+
{
|
|
905
|
+
implemented: true,
|
|
906
|
+
integrationChecked: true,
|
|
907
|
+
e2eChecked: true,
|
|
908
|
+
text: "Create a task with a title and use # for tags, @ for work projects, and ! for personal projects with typing assistance.",
|
|
909
|
+
detail:
|
|
910
|
+
"The “what are you working on” field accepts #tag, @project (work), and !project (personal) tokens in the title; they are parsed on start (`startTask`), feed known tag lists plus `knownProjects` / `knownPersonalProjects`, and render on the task card. Since **1.0.0-beta.21**, **`!`** may be **glued** to the previous word (no space), like some **`#`** forms — e.g. `Errands!me#dentist`; **`@`** still expects **leading whitespace** or **string start** so mid-token emails are not parsed as projects. Templates and suggestions complement typing.",
|
|
911
|
+
example:
|
|
912
|
+
"Enter “Auth fixes #bugfix @mobile” or “Rest #rest !me”, start tracking: the project chip is sky or rose depending on the token; known lists update for the next picks.",
|
|
913
|
+
},
|
|
914
|
+
{
|
|
915
|
+
implemented: true,
|
|
916
|
+
integrationChecked: true,
|
|
917
|
+
e2eChecked: false,
|
|
918
|
+
text: "Declare a personal project with the `!` token (tracking separate from `@` work projects), including past tasks, server rename paths, and task templates.",
|
|
919
|
+
detail:
|
|
920
|
+
"`lib/taskParsing.ts` (`parseTaskWithAutoTags`, `buildStartTaskFromDraft`, `formatProjectDisplay`, `!p#t` tokens) ; `personalProject` on tasks and `knownPersonalProjects` on the payload ; `server/actionDispatch.ts` and `server/actionTaskSession.ts` (`startTask`, `addHistoricalTask`, `updateTask`, `saveTaskTemplate`, `renameProjectMetadata` with `personalOnly` on the API) ; UI: `TaskFocusPanel`, `TaskSessionLiveCard`, `SavedProjectPicker`, `taskFieldStyles` (rose chips). Unit tests: `lib/taskTemplateDraft.test.ts` (`!` templates).",
|
|
921
|
+
example:
|
|
922
|
+
"“Reading !me” while live: the card shows the rose project chip; saving as a template keeps `!me` under Settings → Task templates.",
|
|
923
|
+
},
|
|
924
|
+
{
|
|
925
|
+
implemented: true,
|
|
926
|
+
integrationChecked: true,
|
|
927
|
+
e2eChecked: false,
|
|
928
|
+
text: "Use per-personal-project scoped tags in the `!project#subtag` form (same idea as `@project#code` for work).",
|
|
929
|
+
detail:
|
|
930
|
+
"`TagPills`, `formatTagDisplayForTask`, and scoped parsing in `lib/taskParsing.ts` carry `personalProject` for display. The scoped **`!`** token may be **glued** to the previous word (**1.0.0-beta.21**, `findProjectTokens`).",
|
|
931
|
+
example:
|
|
932
|
+
"“Follow-up !dentist#urgent”: scoped tag renders with the `!` prefix.",
|
|
933
|
+
},
|
|
934
|
+
{
|
|
935
|
+
implemented: true,
|
|
936
|
+
integrationChecked: true,
|
|
937
|
+
e2eChecked: false,
|
|
938
|
+
text: "On a task’s Gantt modal, separate productive (`@`) vs personal (`!`) time with distinct bar colors and split totals.",
|
|
939
|
+
detail:
|
|
940
|
+
"`lib/taskTimelineGantt.ts`, `components/dashboard/TaskTimelineGanttModal.tsx`, `lib/dashboardCopy.ts` strings (legend and summaries).",
|
|
941
|
+
},
|
|
942
|
+
{
|
|
943
|
+
implemented: true,
|
|
944
|
+
integrationChecked: true,
|
|
945
|
+
e2eChecked: true,
|
|
946
|
+
text: "Start, pause, and finish a task timer independently of session wall time.",
|
|
947
|
+
detail:
|
|
948
|
+
"Each task timer cycle (start, manual pause on the row, finish) is separate from wall time: pausing or resuming the session wall (`setPaused`) is not the same as pausing that task’s timer (`setTaskTimerPaused` / resume); server-side segments and pause contexts preserve the distinction. A Playwright scenario clicks pause on the card and checks the resume control (`e2e/task-lifecycle.spec.ts`).",
|
|
949
|
+
example:
|
|
950
|
+
"Pause a task from its card, then resume only the session: that task’s timer stays paused until you explicitly resume the row. “Finish” on the card ends tracking for that task.",
|
|
951
|
+
},
|
|
952
|
+
{
|
|
953
|
+
implemented: true,
|
|
954
|
+
text: "When another timer is running, choose pause, finish, or parallel run and save the default for next time.",
|
|
955
|
+
},
|
|
956
|
+
{
|
|
957
|
+
implemented: true,
|
|
958
|
+
text: "Add subtasks, check them off, and reorder by drag and drop.",
|
|
959
|
+
},
|
|
960
|
+
{
|
|
961
|
+
implemented: true,
|
|
962
|
+
text: "Save a task template from a running, paused, or completed task, manage it in Settings, and find it again (debounced input, “Template:” hints, Ctrl+K palette), including personal `!` projects.",
|
|
963
|
+
detail:
|
|
964
|
+
"`saveTaskTemplate` stores `personalProject` ; `lib/taskTemplateDraft.ts` (`formatTaskTemplateDraftLine`, `parseTaskTemplatesFromPayload`, `buildTaskTemplateSignature` with work/personal segment) ; `SettingsTaskTemplatesSection.tsx`.",
|
|
965
|
+
},
|
|
966
|
+
{
|
|
967
|
+
implemented: true,
|
|
968
|
+
text: "Log a task live or backfill it with explicit start and end times.",
|
|
969
|
+
},
|
|
970
|
+
{
|
|
971
|
+
implemented: true,
|
|
972
|
+
text: "Add a free-text note on a task (live or past entry) to keep activity context.",
|
|
973
|
+
},
|
|
974
|
+
{
|
|
975
|
+
implemented: true,
|
|
976
|
+
text: "Render task notes before the subtasks block so context stays visible while breaking down work.",
|
|
977
|
+
},
|
|
978
|
+
{
|
|
979
|
+
implemented: true,
|
|
980
|
+
text: "Edit task time bounds by keeping system duration, entering manual duration, or recalculating from bounds.",
|
|
981
|
+
},
|
|
982
|
+
{
|
|
983
|
+
implemented: true,
|
|
984
|
+
text: "See not-done tasks whose start is still in the future grouped under Planned with distinct styling, no live-running timer, and no crossed-out completed look too early; for completed tasks, the same Planned styling only when both start and end are still strictly in the future.",
|
|
985
|
+
},
|
|
986
|
+
{
|
|
987
|
+
implemented: true,
|
|
988
|
+
text: "When a planned task becomes live tracking while another timer was already running, resolve pause/finish/parallel for the previously running tasks (same remembered preference as manual starts) and pulse the dashboard nav icon when attention is needed off-route.",
|
|
989
|
+
},
|
|
990
|
+
{
|
|
991
|
+
implemented: true,
|
|
992
|
+
text: "Schedule an end time on a running task so it automatically completes when the deadline is reached.",
|
|
993
|
+
},
|
|
994
|
+
{
|
|
995
|
+
implemented: true,
|
|
996
|
+
text: "Launch KronoFocus from a task to alternate work and breaks without losing task context.",
|
|
997
|
+
},
|
|
998
|
+
{
|
|
999
|
+
discarded: true,
|
|
1000
|
+
implemented: false,
|
|
1001
|
+
text: "Treat wall session time and task time as one interchangeable reporting measure without separating them.",
|
|
1002
|
+
detail:
|
|
1003
|
+
"Decision: discarded. Reporting and aggregates intentionally keep wall time (collector segments) separate from task time; both views support an honest read.",
|
|
1004
|
+
example:
|
|
1005
|
+
"Compare wall vs task totals in reporting — gaps are informative, not mistakes to merge away.",
|
|
1006
|
+
},
|
|
1007
|
+
],
|
|
1008
|
+
},
|
|
1009
|
+
{
|
|
1010
|
+
id: "us-kronofocus",
|
|
1011
|
+
title: "KronoFocus",
|
|
1012
|
+
stories: [
|
|
1013
|
+
{
|
|
1014
|
+
implemented: true,
|
|
1015
|
+
text: "Show the KronoFocus timer in the header and/or near task actions per settings.",
|
|
1016
|
+
},
|
|
1017
|
+
{
|
|
1018
|
+
implemented: true,
|
|
1019
|
+
text: "Run work, break, and long-break phases with duration in HH:MM:SS.",
|
|
1020
|
+
},
|
|
1021
|
+
{
|
|
1022
|
+
implemented: true,
|
|
1023
|
+
text: "Reuse a duration from recent duration history.",
|
|
1024
|
+
},
|
|
1025
|
+
{
|
|
1026
|
+
implemented: true,
|
|
1027
|
+
text: "Attach focus to the active task so history stays linked.",
|
|
1028
|
+
},
|
|
1029
|
+
],
|
|
1030
|
+
},
|
|
1031
|
+
{
|
|
1032
|
+
id: "us-search-shortcuts",
|
|
1033
|
+
title: "Data search and shortcuts",
|
|
1034
|
+
stories: [
|
|
1035
|
+
{
|
|
1036
|
+
implemented: true,
|
|
1037
|
+
text: "Open the Spotlight data-search palette (Ctrl+K / ⌘K) and filter sessions, tasks, task templates, tags, and projects; choosing a template fills the new-task draft.",
|
|
1038
|
+
},
|
|
1039
|
+
{
|
|
1040
|
+
implemented: true,
|
|
1041
|
+
text: "Filter user guide sections on /guide with the field at the top of the page.",
|
|
1042
|
+
},
|
|
1043
|
+
{
|
|
1044
|
+
implemented: true,
|
|
1045
|
+
text: "View and customize dashboard keyboard shortcuts in the dedicated modal.",
|
|
1046
|
+
},
|
|
1047
|
+
],
|
|
1048
|
+
},
|
|
1049
|
+
{
|
|
1050
|
+
id: "us-reporting",
|
|
1051
|
+
title: "Reporting and aggregates",
|
|
1052
|
+
stories: [
|
|
1053
|
+
{
|
|
1054
|
+
implemented: true,
|
|
1055
|
+
text: "Pick a date range with day / week / month / year presets as exposed by the UI.",
|
|
1056
|
+
},
|
|
1057
|
+
{
|
|
1058
|
+
implemented: true,
|
|
1059
|
+
text: "Restrict views by one or more tags when data is task-based.",
|
|
1060
|
+
},
|
|
1061
|
+
{
|
|
1062
|
+
implemented: true,
|
|
1063
|
+
text: "Read summaries, per-day tables, and breakdowns by project and tag.",
|
|
1064
|
+
},
|
|
1065
|
+
{
|
|
1066
|
+
implemented: true,
|
|
1067
|
+
integrationChecked: true,
|
|
1068
|
+
e2eChecked: false,
|
|
1069
|
+
text: "On the Reporting page, read per-project grids that reflect productive (`@`) time without mixing in personal (`!`) project minutes.",
|
|
1070
|
+
detail:
|
|
1071
|
+
"`app/reporting/page.tsx` calls `aggregateProjectTaskMinutesByDay` with the default `work` scope (`lib/reportingAggregate.ts`, `taskMatchesReportingProjectScope`): tasks with `personalProject: true` are excluded from those grids.",
|
|
1072
|
+
},
|
|
1073
|
+
{
|
|
1074
|
+
implemented: false,
|
|
1075
|
+
text: "Show a dedicated personal-time (`!`) block or calendar on Reporting, parallel to `@` project views, with the same date-range and tag filters.",
|
|
1076
|
+
detail:
|
|
1077
|
+
"Library aggregates accept `taskProjectScope: \"personal\"` (`aggregateProjectTaskMinutesByDay`, `aggregateReportingByProject`, `aggregateTagTaskMinutesByDayAndWeek`); no second “personal” grid is wired in the UI as of 2026-05-12.",
|
|
1078
|
+
},
|
|
1079
|
+
{
|
|
1080
|
+
implemented: true,
|
|
1081
|
+
text: "See non-overlapping task minutes per day for comparison with raw totals.",
|
|
1082
|
+
},
|
|
1083
|
+
{
|
|
1084
|
+
implemented: true,
|
|
1085
|
+
text: "Set the week start day for calendar views to match how I work.",
|
|
1086
|
+
},
|
|
1087
|
+
{
|
|
1088
|
+
implemented: true,
|
|
1089
|
+
text: "Relaunch the reporting tour from settings.",
|
|
1090
|
+
},
|
|
1091
|
+
{
|
|
1092
|
+
implemented: true,
|
|
1093
|
+
text: "When the host supplies a planned start time, see punctuality / variance indicators on sessions.",
|
|
1094
|
+
},
|
|
1095
|
+
{
|
|
1096
|
+
implemented: true,
|
|
1097
|
+
text: "See honest signals about work not yet finalized in the selected range.",
|
|
1098
|
+
},
|
|
1099
|
+
],
|
|
1100
|
+
},
|
|
1101
|
+
{
|
|
1102
|
+
id: "us-settings-data",
|
|
1103
|
+
title: "Settings, export, integrations, danger zone",
|
|
1104
|
+
stories: [
|
|
1105
|
+
{
|
|
1106
|
+
implemented: true,
|
|
1107
|
+
text: "Tune usage profile to hide blocks that are not relevant (e.g. lines of code for a non-dev profile).",
|
|
1108
|
+
},
|
|
1109
|
+
{
|
|
1110
|
+
implemented: true,
|
|
1111
|
+
text: "Configure work schedule, planned sessions, collection, and buffers.",
|
|
1112
|
+
},
|
|
1113
|
+
{
|
|
1114
|
+
implemented: true,
|
|
1115
|
+
text: "Manage advanced tags and migrate tags between global and per-project scope.",
|
|
1116
|
+
},
|
|
1117
|
+
{
|
|
1118
|
+
implemented: true,
|
|
1119
|
+
text: "Rename a tag or project inline (pencil, direct edit, Enter/Escape), with a reporting-impact warning and a “don’t show again” option.",
|
|
1120
|
+
},
|
|
1121
|
+
{
|
|
1122
|
+
implemented: false,
|
|
1123
|
+
text: "Manage and rename known personal projects (`knownPersonalProjects`, `!` tokens) from Settings the same way as `@` projects.",
|
|
1124
|
+
detail:
|
|
1125
|
+
"Payload + server actions maintain `knownPersonalProjects` and `renameProjectMetadata` with `personalOnly`; the Settings projects/tags area does not yet expose dedicated `!` inline management as of 2026-05-12.",
|
|
1126
|
+
},
|
|
1127
|
+
{
|
|
1128
|
+
implemented: true,
|
|
1129
|
+
text: "Toggle correction switches for task and session time bounds.",
|
|
1130
|
+
},
|
|
1131
|
+
{
|
|
1132
|
+
implemented: true,
|
|
1133
|
+
text: "Opt in to Git and MongoDB integrations.",
|
|
1134
|
+
},
|
|
1135
|
+
{
|
|
1136
|
+
implemented: true,
|
|
1137
|
+
text: "Download backups via the export API and UI options.",
|
|
1138
|
+
},
|
|
1139
|
+
{
|
|
1140
|
+
implemented: true,
|
|
1141
|
+
text: "Erase history in a danger zone gated by explicit confirmation.",
|
|
1142
|
+
},
|
|
1143
|
+
{
|
|
1144
|
+
implemented: true,
|
|
1145
|
+
text: "In local dev (next dev), use a separate dataset vault and optionally share with prod.",
|
|
1146
|
+
},
|
|
1147
|
+
{
|
|
1148
|
+
implemented: true,
|
|
1149
|
+
text: "Configure web, API, and guided-tour relaunches from settings.",
|
|
1150
|
+
},
|
|
1151
|
+
],
|
|
1152
|
+
},
|
|
1153
|
+
{
|
|
1154
|
+
id: "us-architecture-quality",
|
|
1155
|
+
title: "Architecture, quality, documentation",
|
|
1156
|
+
stories: [
|
|
1157
|
+
{
|
|
1158
|
+
implemented: true,
|
|
1159
|
+
text: "Rely on mutations going through a single server entry point for invariants.",
|
|
1160
|
+
},
|
|
1161
|
+
{
|
|
1162
|
+
implemented: true,
|
|
1163
|
+
text: "Reload the page and restore state from persisted JSON payload.",
|
|
1164
|
+
},
|
|
1165
|
+
{
|
|
1166
|
+
implemented: true,
|
|
1167
|
+
text: "Use French and English UI strings on visible surfaces.",
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
implemented: true,
|
|
1171
|
+
text: "Benefit from Playwright end-to-end tests on critical flows.",
|
|
1172
|
+
},
|
|
1173
|
+
{
|
|
1174
|
+
implemented: true,
|
|
1175
|
+
text: "Open this user-story list on /implementation to track shipped scope.",
|
|
1176
|
+
},
|
|
1177
|
+
{
|
|
1178
|
+
implemented: false,
|
|
1179
|
+
text: "Replace the user guide (/guide) with this page for learning the product.",
|
|
1180
|
+
},
|
|
1181
|
+
],
|
|
1182
|
+
},
|
|
1183
|
+
],
|
|
1184
|
+
};
|
|
1185
|
+
|
|
1186
|
+
export function implementationNotesBundle(lang: Lang): ImplementationNotesBundle {
|
|
1187
|
+
return lang === "fr" ? frBundle : enBundle;
|
|
1188
|
+
}
|