@nightkatana/kronosys-app 1.0.0-beta.21 → 1.0.0-beta.22

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 (46) hide show
  1. package/README.md +1 -1
  2. package/app/changelog/page.tsx +87 -19
  3. package/app/globals.css +10 -8
  4. package/app/guide/page.tsx +71 -34
  5. package/app/implementation/page.tsx +70 -60
  6. package/app/licenses/page.tsx +79 -47
  7. package/app/logs/page.tsx +103 -47
  8. package/app/page.tsx +104 -169
  9. package/app/reporting/page.tsx +1918 -1436
  10. package/app/settings/page.tsx +66 -44
  11. package/components/KronosysPayloadProvider.tsx +19 -5
  12. package/components/dashboard/AppShellHeaderKronoFocus.tsx +78 -0
  13. package/components/dashboard/AppShellHeaderToolbarLayout.tsx +36 -0
  14. package/components/dashboard/AppShellHeaderUtilityRibbon.tsx +19 -0
  15. package/components/dashboard/AppShellHeaderWallClock.tsx +23 -17
  16. package/components/dashboard/AppShellRouteNav.tsx +336 -209
  17. package/components/dashboard/AppShellToolbarCommandCenter.tsx +225 -0
  18. package/components/dashboard/AppShellToolbarRouteNav.tsx +204 -0
  19. package/components/dashboard/DashboardCommandCenter.tsx +119 -30
  20. package/components/dashboard/KronoFocusPanel.tsx +287 -260
  21. package/components/dashboard/LanguageMenu.tsx +23 -7
  22. package/components/dashboard/PageRefreshButton.tsx +42 -16
  23. package/components/dashboard/ReportingTour.tsx +20 -2
  24. package/components/dashboard/SessionListPanel.tsx +4 -4
  25. package/components/dashboard/ThemeToggle.tsx +4 -3
  26. package/components/dashboard/useAnchoredFloatingPortalStyle.ts +9 -2
  27. package/components/dashboard/useKronoFocusLiveSeconds.ts +4 -2
  28. package/lib/appShellHeaderClasses.ts +22 -3
  29. package/lib/appShellToolbarChrome.ts +112 -0
  30. package/lib/appShellToolbarDeferredIntents.ts +112 -0
  31. package/lib/appShellToolbarSessionSlices.ts +67 -0
  32. package/lib/dashboardCopy.ts +78 -29
  33. package/lib/dashboardQuickSearch.ts +37 -6
  34. package/lib/dashboardUrlSession.ts +36 -0
  35. package/lib/generatedUserChangelog.ts +14 -0
  36. package/lib/implementationNotes.ts +18 -14
  37. package/lib/reportingAggregate.ts +68 -9
  38. package/lib/reportingMetricHelp.ts +8 -8
  39. package/lib/reportingStrings.ts +118 -9
  40. package/lib/reportingTagWeekBreakdown.ts +55 -13
  41. package/lib/settingsCopy.ts +6 -7
  42. package/lib/userGuideCopy.ts +29 -26
  43. package/package.json +7 -5
  44. package/server/db.ts +6 -4
  45. package/server/dbSchema.ts +2 -2
  46. package/components/dashboard/AppShellCommandCenterPlaceholder.tsx +0 -17
@@ -17,6 +17,11 @@ import {
17
17
  } from "@/lib/dashboardShortcuts";
18
18
  import type { DashboardSearchItem } from "@/lib/dashboardQuickSearch";
19
19
  import { dataSearchItemMatches } from "@/lib/dashboardQuickSearchQuery";
20
+ import {
21
+ appShellToolbarIconLinkClass,
22
+ appShellToolbarRaisedWideTriggerClass,
23
+ appShellToolbarRibbonGroupClass,
24
+ } from "@/lib/appShellToolbarChrome";
20
25
 
21
26
  export type DashboardCommandHandlers = {
22
27
  newSession: () => void;
@@ -49,7 +54,7 @@ const DATA_SEARCH_TIMER_BASE =
49
54
  "block w-full min-w-0 truncate text-[0.7rem] leading-snug tabular-nums";
50
55
 
51
56
  function classForDataSearchTimer(
52
- state: DashboardSearchItem["searchTimerState"]
57
+ state: DashboardSearchItem["searchTimerState"],
53
58
  ): string {
54
59
  if (state === "active") {
55
60
  return `${DATA_SEARCH_TIMER_BASE} text-emerald-600 dark:text-emerald-400/95`;
@@ -88,23 +93,64 @@ export function DashboardCommandCenter({
88
93
  const combos = useMemo(() => mergeCombos(overrides), [overrides]);
89
94
 
90
95
  const isMac =
91
- typeof navigator !== "undefined" && /mac|iphone|ipad|ipod/i.test(navigator.platform ?? "");
96
+ typeof navigator !== "undefined" &&
97
+ /mac|iphone|ipad|ipod/i.test(navigator.platform ?? "");
92
98
 
93
99
  const actions = useMemo(() => {
94
- const list: Array<{ id: string; label: string; kw: string; run: () => void }> = [
95
- { id: "new-session", label: dt.commandNewSession, kw: "new session nouvelle", run: handlers.newSession },
96
- { id: "refresh", label: dt.commandRefresh, kw: "reload actualiser rafraîchir", run: handlers.refresh },
97
- { id: "reporting", label: dt.commandOpenReporting, kw: "reporting graph bilan stats", run: handlers.openReporting },
98
- { id: "settings", label: dt.commandOpenSettings, kw: "settings paramètres", run: handlers.openSettings },
100
+ const list: Array<{
101
+ id: string;
102
+ label: string;
103
+ kw: string;
104
+ run: () => void;
105
+ }> = [
106
+ {
107
+ id: "new-session",
108
+ label: dt.commandNewSession,
109
+ kw: "new session nouvelle",
110
+ run: handlers.newSession,
111
+ },
112
+ {
113
+ id: "refresh",
114
+ label: dt.commandRefresh,
115
+ kw: "reload actualiser rafraîchir",
116
+ run: handlers.refresh,
117
+ },
118
+ {
119
+ id: "reporting",
120
+ label: dt.commandOpenReporting,
121
+ kw: "reporting graph bilan stats",
122
+ run: handlers.openReporting,
123
+ },
124
+ {
125
+ id: "settings",
126
+ label: dt.commandOpenSettings,
127
+ kw: "settings paramètres",
128
+ run: handlers.openSettings,
129
+ },
99
130
  {
100
131
  id: "user-guide",
101
132
  label: dt.commandOpenUserGuide,
102
133
  kw: "help aide manuel guide book livre",
103
134
  run: handlers.openUserGuide,
104
135
  },
105
- { id: "focus-sessions", label: dt.commandFocusSessions, kw: "sessions colonne liste", run: handlers.focusSessions },
106
- { id: "focus-tasks", label: dt.commandFocusTasks, kw: "tasks tâches", run: handlers.focusTasks },
107
- { id: "focus-tags", label: dt.commandFocusTags, kw: "tags étiquettes projects projets", run: handlers.focusTags },
136
+ {
137
+ id: "focus-sessions",
138
+ label: dt.commandFocusSessions,
139
+ kw: "sessions colonne liste",
140
+ run: handlers.focusSessions,
141
+ },
142
+ {
143
+ id: "focus-tasks",
144
+ label: dt.commandFocusTasks,
145
+ kw: "tasks tâches",
146
+ run: handlers.focusTasks,
147
+ },
148
+ {
149
+ id: "focus-tags",
150
+ label: dt.commandFocusTags,
151
+ kw: "tags étiquettes projects projets",
152
+ run: handlers.focusTags,
153
+ },
108
154
  {
109
155
  id: "toggle-theme",
110
156
  label: dt.commandToggleTheme,
@@ -171,7 +217,7 @@ export function DashboardCommandCenter({
171
217
  row.onSelect();
172
218
  closePalette();
173
219
  },
174
- [closePalette, filtered]
220
+ [closePalette, filtered],
175
221
  );
176
222
 
177
223
  useEffect(() => {
@@ -239,7 +285,15 @@ export function DashboardCommandCenter({
239
285
  };
240
286
  window.addEventListener("keydown", onKey, true);
241
287
  return () => window.removeEventListener("keydown", onKey, true);
242
- }, [actions, closePalette, combos, handlers, paletteOpen, recordingId, shortcutsOpen]);
288
+ }, [
289
+ actions,
290
+ closePalette,
291
+ combos,
292
+ handlers,
293
+ paletteOpen,
294
+ recordingId,
295
+ shortcutsOpen,
296
+ ]);
243
297
 
244
298
  const onPaletteKeyDown = (e: React.KeyboardEvent) => {
245
299
  if (e.key === "ArrowDown") {
@@ -278,7 +332,11 @@ export function DashboardCommandCenter({
278
332
  onKeyDown={onPaletteKeyDown}
279
333
  >
280
334
  <div className="flex items-center gap-2 border-b border-zinc-200 px-3 py-2 dark:border-zinc-700">
281
- <Search className="shrink-0 text-zinc-400" size={18} aria-hidden />
335
+ <Search
336
+ className="shrink-0 text-zinc-400"
337
+ size={18}
338
+ aria-hidden
339
+ />
282
340
  <input
283
341
  ref={inputRef}
284
342
  type="search"
@@ -293,11 +351,18 @@ export function DashboardCommandCenter({
293
351
  {isMac ? "⌘K" : "Ctrl+K"}
294
352
  </kbd>
295
353
  </div>
296
- <ul id="dash-palette-results" className="max-h-[min(50vh,22rem)] list-none overflow-y-auto py-1">
354
+ <ul
355
+ id="dash-palette-results"
356
+ className="max-h-[min(50vh,22rem)] list-none overflow-y-auto py-1"
357
+ >
297
358
  {query.trim() === "" ? (
298
- <li className="px-3 py-4 text-center text-sm text-zinc-500">{dt.dataSearchTypePrompt}</li>
359
+ <li className="px-3 py-4 text-center text-sm text-zinc-500">
360
+ {dt.dataSearchTypePrompt}
361
+ </li>
299
362
  ) : filtered.length === 0 ? (
300
- <li className="px-3 py-4 text-center text-sm text-zinc-500">{dt.dataSearchEmpty}</li>
363
+ <li className="px-3 py-4 text-center text-sm text-zinc-500">
364
+ {dt.dataSearchEmpty}
365
+ </li>
301
366
  ) : (
302
367
  filtered.map((row, idx) => (
303
368
  <li key={row.id} className="list-none">
@@ -312,7 +377,9 @@ export function DashboardCommandCenter({
312
377
  onClick={() => runSearchRow(idx)}
313
378
  >
314
379
  <span className="flex w-full min-w-0 items-center justify-between gap-2">
315
- <span className="min-w-0 truncate font-medium">{row.title}</span>
380
+ <span className="min-w-0 truncate font-medium">
381
+ {row.title}
382
+ </span>
316
383
  {row.subtitle ? (
317
384
  <span className="shrink-0 text-[0.65rem] uppercase tracking-wide text-zinc-500 dark:text-zinc-400">
318
385
  {row.subtitle}
@@ -320,7 +387,11 @@ export function DashboardCommandCenter({
320
387
  ) : null}
321
388
  </span>
322
389
  {row.searchTimerLine ? (
323
- <span className={classForDataSearchTimer(row.searchTimerState)}>
390
+ <span
391
+ className={classForDataSearchTimer(
392
+ row.searchTimerState,
393
+ )}
394
+ >
324
395
  {row.searchTimerLine}
325
396
  </span>
326
397
  ) : null}
@@ -339,7 +410,7 @@ export function DashboardCommandCenter({
339
410
  </p>
340
411
  </div>
341
412
  </div>,
342
- document.body
413
+ document.body,
343
414
  )
344
415
  : null;
345
416
 
@@ -359,7 +430,10 @@ export function DashboardCommandCenter({
359
430
  >
360
431
  <div className="max-h-[min(90vh,36rem)] w-full max-w-lg overflow-hidden rounded-xl border border-zinc-300 bg-white shadow-2xl dark:border-zinc-600 dark:bg-zinc-900">
361
432
  <div className="flex items-center justify-between border-b border-zinc-200 px-4 py-3 dark:border-zinc-700">
362
- <h2 id="dash-shortcuts-title" className="text-base font-semibold text-zinc-900 dark:text-zinc-100">
433
+ <h2
434
+ id="dash-shortcuts-title"
435
+ className="text-base font-semibold text-zinc-900 dark:text-zinc-100"
436
+ >
363
437
  {dt.shortcutsModalTitle}
364
438
  </h2>
365
439
  <button
@@ -388,16 +462,26 @@ export function DashboardCommandCenter({
388
462
  </thead>
389
463
  <tbody>
390
464
  {actions
391
- .filter((a) => a.id !== "end-live-session" || handlers.endLiveSession)
465
+ .filter(
466
+ (a) =>
467
+ a.id !== "end-live-session" || handlers.endLiveSession,
468
+ )
392
469
  .map((a) => {
393
470
  const combo = combos[a.id] ?? "";
394
471
  const recording = recordingId === a.id;
395
472
  return (
396
- <tr key={a.id} className="border-t border-zinc-100 dark:border-zinc-800">
397
- <td className="px-2 py-2 text-zinc-800 dark:text-zinc-200">{a.label}</td>
473
+ <tr
474
+ key={a.id}
475
+ className="border-t border-zinc-100 dark:border-zinc-800"
476
+ >
477
+ <td className="px-2 py-2 text-zinc-800 dark:text-zinc-200">
478
+ {a.label}
479
+ </td>
398
480
  <td className="px-2 py-2 font-mono text-xs text-zinc-600 dark:text-zinc-400">
399
481
  {recording ? (
400
- <span className="text-violet-600 dark:text-violet-300">{dt.shortcutsRecordPrompt}</span>
482
+ <span className="text-violet-600 dark:text-violet-300">
483
+ {dt.shortcutsRecordPrompt}
484
+ </span>
401
485
  ) : (
402
486
  formatComboForDisplay(combo, isMac)
403
487
  )}
@@ -433,29 +517,34 @@ export function DashboardCommandCenter({
433
517
  </div>
434
518
  </div>
435
519
  </div>,
436
- document.body
520
+ document.body,
437
521
  )
438
522
  : null;
439
523
 
440
524
  return (
441
525
  <>
442
- <div id={toolbarDomId} className="flex shrink-0 items-center gap-1.5">
526
+ <div
527
+ id={toolbarDomId}
528
+ className={`${appShellToolbarRibbonGroupClass} shrink-0`}
529
+ >
443
530
  <button
444
531
  type="button"
445
- className="inline-flex h-10 w-[9.5rem] max-w-[min(42vw,11rem)] shrink-0 items-center gap-1.5 rounded-lg border border-zinc-300 bg-white px-2 text-left text-xs text-zinc-500 shadow-sm transition hover:border-zinc-400 hover:bg-zinc-50 sm:w-44 sm:max-w-none sm:px-2.5 sm:text-sm dark:border-zinc-600 dark:bg-zinc-800/90 dark:text-zinc-400 dark:hover:border-zinc-500 dark:hover:bg-zinc-800"
532
+ className={`${appShellToolbarRaisedWideTriggerClass} w-[9.5rem] max-w-[min(42vw,11rem)] gap-1.5 px-2 text-left text-xs text-zinc-500 sm:w-44 sm:max-w-none sm:px-2.5 sm:text-sm dark:text-zinc-400`}
446
533
  onClick={openPalette}
447
534
  aria-haspopup="dialog"
448
535
  aria-expanded={paletteOpen ? "true" : "false"}
449
536
  >
450
537
  <Search className="shrink-0" size={16} aria-hidden />
451
- <span className="min-w-0 flex-1 truncate">{dt.dataSearchTrigger}</span>
452
- <kbd className="hidden shrink-0 rounded border border-zinc-300 px-1 py-0.5 font-mono text-[0.6rem] text-zinc-500 sm:inline dark:border-zinc-600">
538
+ <span className="min-w-0 flex-1 truncate">
539
+ {dt.dataSearchTrigger}
540
+ </span>
541
+ <kbd className="hidden shrink-0 rounded border border-zinc-300/90 bg-gradient-to-b from-white to-zinc-100 px-1 py-0.5 font-mono text-[0.6rem] text-zinc-500 shadow-[0_1px_1px_rgba(15,23,42,0.08)] sm:inline dark:border-zinc-600 dark:from-zinc-700 dark:to-zinc-800 dark:text-zinc-400">
453
542
  {isMac ? "⌘K" : "Ctrl+K"}
454
543
  </kbd>
455
544
  </button>
456
545
  <button
457
546
  type="button"
458
- className="inline-flex size-10 shrink-0 items-center justify-center rounded-lg border border-zinc-300 bg-white text-zinc-700 shadow-sm transition hover:border-zinc-400 hover:bg-zinc-50 dark:border-zinc-600 dark:bg-zinc-800/90 dark:text-zinc-200 dark:hover:border-zinc-500 dark:hover:bg-zinc-800"
547
+ className={appShellToolbarIconLinkClass}
459
548
  onClick={() => setShortcutsOpen(true)}
460
549
  title={dt.shortcutsModalTitle}
461
550
  aria-label={dt.shortcutsModalTitle}