@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
package/lib/taskParsing.test.ts
DELETED
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
buildStartTaskFromDraft,
|
|
5
|
-
buildStartTaskFromInput,
|
|
6
|
-
DEFAULT_FALLBACK_TASK_TAG,
|
|
7
|
-
displayRawTaskTitle,
|
|
8
|
-
filterTaskTagsForProject,
|
|
9
|
-
formatDuration,
|
|
10
|
-
formatTagDisplay,
|
|
11
|
-
formatTagDisplayForTask,
|
|
12
|
-
formatWallDurationMs,
|
|
13
|
-
formatStopwatchMs,
|
|
14
|
-
mergeQuickAddFromOptionValues,
|
|
15
|
-
normalizeProjectKey,
|
|
16
|
-
normalizeTagKey,
|
|
17
|
-
normalizeTaskTagsForStorage,
|
|
18
|
-
parseProjectScopedTag,
|
|
19
|
-
parseQuickAddTagOrProjectLine,
|
|
20
|
-
parseTaskWithAutoTags,
|
|
21
|
-
readTaskDefaultTagBucketEnabled,
|
|
22
|
-
rebuildTaskRawString,
|
|
23
|
-
removeSavedTagFromDraft,
|
|
24
|
-
resolveProjectForTaskUpdate,
|
|
25
|
-
resolveProjectFromFullTaskLine,
|
|
26
|
-
splitTaskTitleSegments,
|
|
27
|
-
stripProjectsAndTakeFirst,
|
|
28
|
-
stripScopedProjectAtTokens,
|
|
29
|
-
} from "./taskParsing";
|
|
30
|
-
|
|
31
|
-
describe("taskParsing core", () => {
|
|
32
|
-
it("normalizeProjectKey retire les @", () => {
|
|
33
|
-
expect(normalizeProjectKey("@@acme")).toBe("acme");
|
|
34
|
-
expect(normalizeProjectKey("@")).toBe("");
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("normalizeTagKey retire les #", () => {
|
|
38
|
-
expect(normalizeTagKey("#bug")).toBe("bug");
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
it("parseProjectScopedTag identifie proj#tag", () => {
|
|
42
|
-
expect(parseProjectScopedTag("acme#dev")).toEqual({ projectKey: "acme", localTag: "dev" });
|
|
43
|
-
expect(parseProjectScopedTag("global")).toBeNull();
|
|
44
|
-
expect(parseProjectScopedTag("#tag")).toBeNull();
|
|
45
|
-
expect(parseProjectScopedTag("acme#")).toBeNull();
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
describe("title parsing & projects", () => {
|
|
50
|
-
it("stripScopedProjectAtTokens extrait @p#t", () => {
|
|
51
|
-
const r = stripScopedProjectAtTokens("Hello @p#t world");
|
|
52
|
-
expect(r.core).toBe("Hello world");
|
|
53
|
-
expect(r.scopedTags).toEqual(["p#t"]);
|
|
54
|
-
expect(r.scopedProjectHint).toBe("p");
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
it("stripProjectsAndTakeFirst gère @p seul et @p#t", () => {
|
|
58
|
-
const r = stripProjectsAndTakeFirst("Task @acme and @ignored#tag");
|
|
59
|
-
expect(r.project).toBe("acme");
|
|
60
|
-
expect(r.scopedTags).toEqual(["ignored#tag"]);
|
|
61
|
-
expect(r.core).toBe("Task and");
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
it("resolveProjectForTaskUpdate", () => {
|
|
65
|
-
expect(resolveProjectForTaskUpdate("No project here")).toBeUndefined();
|
|
66
|
-
expect(resolveProjectForTaskUpdate("Project @demo")).toBe("demo");
|
|
67
|
-
expect(resolveProjectForTaskUpdate("Project @")).toBeNull();
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("resolveProjectFromFullTaskLine", () => {
|
|
71
|
-
expect(resolveProjectFromFullTaskLine("Hello @world")).toBe("world");
|
|
72
|
-
expect(resolveProjectFromFullTaskLine("Hello")).toBeNull();
|
|
73
|
-
});
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
describe("tag handling", () => {
|
|
77
|
-
it("normalizeTaskTagsForStorage déduplique et gère default", () => {
|
|
78
|
-
expect(normalizeTaskTagsForStorage(["A", "a", ""])).toEqual(["A"]);
|
|
79
|
-
expect(normalizeTaskTagsForStorage([])).toEqual([DEFAULT_FALLBACK_TASK_TAG]);
|
|
80
|
-
expect(normalizeTaskTagsForStorage([], { assignDefaultTagBucket: false })).toEqual([]);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
it("filterTaskTagsForProject nettoie les tags incompatibles", () => {
|
|
84
|
-
const tags = ["global", "acme#dev", "beta#design"];
|
|
85
|
-
expect(filterTaskTagsForProject(tags, "acme")).toEqual(["global", "acme#dev"]);
|
|
86
|
-
expect(filterTaskTagsForProject(tags, null)).toEqual(["global"]);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
it("splitTaskTitleSegments découpe texte et tags", () => {
|
|
90
|
-
const segs = splitTaskTitleSegments("Fix #bug now #urgent");
|
|
91
|
-
expect(segs).toHaveLength(4);
|
|
92
|
-
expect(segs[1]).toEqual({ kind: "tag", value: "#bug" });
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
it("parseTaskWithAutoTags moteur complet", () => {
|
|
96
|
-
const r = parseTaskWithAutoTags("Fix #bug @acme");
|
|
97
|
-
expect(r.name).toBe("Fix");
|
|
98
|
-
expect(r.tags).toEqual(["bug"]);
|
|
99
|
-
expect(r.project).toBe("acme");
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
describe("rebuild & display", () => {
|
|
104
|
-
it("rebuildTaskRawString", () => {
|
|
105
|
-
expect(rebuildTaskRawString("Name", ["tag"], "proj")).toBe("Name#tag@proj");
|
|
106
|
-
expect(rebuildTaskRawString("Name", ["p#t"], "p")).toBe("Name@p#t");
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it("displayRawTaskTitle", () => {
|
|
110
|
-
expect(displayRawTaskTitle("Name #t", ["t"], "p")).toBe("Name #t@p");
|
|
111
|
-
expect(displayRawTaskTitle("Name", ["t"], "p")).toBe("Name #t@p");
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
it("formatTagDisplay", () => {
|
|
115
|
-
expect(formatTagDisplay("dev")).toBe("#dev");
|
|
116
|
-
expect(formatTagDisplay("acme#dev")).toBe("@acme#dev");
|
|
117
|
-
});
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
describe("quick add & draft builders", () => {
|
|
121
|
-
it("parseQuickAddTagOrProjectLine", () => {
|
|
122
|
-
expect(parseQuickAddTagOrProjectLine("just text")).toEqual({ tags: ["justtext"], project: undefined });
|
|
123
|
-
expect(parseQuickAddTagOrProjectLine("#tag")).toEqual({ tags: ["tag"], project: undefined });
|
|
124
|
-
expect(parseQuickAddTagOrProjectLine("@proj")).toEqual({ tags: [], project: "proj" });
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
it("buildStartTaskFromInput", () => {
|
|
128
|
-
expect(buildStartTaskFromInput("#tag @proj")).toEqual({ name: "tag", tags: ["tag"], project: "proj" });
|
|
129
|
-
expect(buildStartTaskFromInput("Task #tag")).toEqual({ name: "Task", tags: ["tag"], project: undefined });
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it("buildStartTaskFromDraft", () => {
|
|
133
|
-
const r = buildStartTaskFromDraft("Title", ["tag"], "proj");
|
|
134
|
-
expect(r.name).toBe("Title");
|
|
135
|
-
expect(r.tags).toEqual(["tag"]);
|
|
136
|
-
expect(r.project).toBe("proj");
|
|
137
|
-
});
|
|
138
|
-
});
|
|
139
|
-
|
|
140
|
-
describe("durations", () => {
|
|
141
|
-
it("formatDuration", () => {
|
|
142
|
-
expect(formatDuration(1.5)).toBe("1 min 30 s");
|
|
143
|
-
expect(formatDuration(120)).toBe("2h");
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
it("formatWallDurationMs", () => {
|
|
147
|
-
expect(formatWallDurationMs(61000)).toBe("1 min 1 s");
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
it("formatStopwatchMs", () => {
|
|
151
|
-
expect(formatStopwatchMs(61000)).toBe("1:01.00");
|
|
152
|
-
});
|
|
153
|
-
});
|
package/lib/usageProfile.test.ts
DELETED
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
|
|
3
|
-
import type { KronosysUpdatePayload } from "./kronosysApi";
|
|
4
|
-
import {
|
|
5
|
-
extensionLinkedDeveloperMetrics,
|
|
6
|
-
showIdeLinkedCodeTimingMetrics,
|
|
7
|
-
showWorkspaceFoldersEmptyMessage,
|
|
8
|
-
trackCodeMetricsFromCfg,
|
|
9
|
-
workspaceCodeMetricsEnabled,
|
|
10
|
-
} from "./usageProfile";
|
|
11
|
-
|
|
12
|
-
describe("trackCodeMetricsFromCfg", () => {
|
|
13
|
-
it("gestionnaire : pas de métriques code", () => {
|
|
14
|
-
expect(trackCodeMetricsFromCfg({ usageProfile: "manager" })).toBe(false);
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
it("défaut (vide) : pas de métriques code", () => {
|
|
18
|
-
expect(trackCodeMetricsFromCfg({})).toBe(false);
|
|
19
|
-
expect(trackCodeMetricsFromCfg(undefined)).toBe(false);
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
it("développeur : métriques code", () => {
|
|
23
|
-
expect(trackCodeMetricsFromCfg({ usageProfile: "developer" })).toBe(true);
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
describe("showWorkspaceFoldersEmptyMessage", () => {
|
|
28
|
-
const devPayload = (origin: KronosysUpdatePayload["dashboardDataOrigin"]): KronosysUpdatePayload =>
|
|
29
|
-
({
|
|
30
|
-
viewType: "dashboard",
|
|
31
|
-
cfg: { usageProfile: "developer" },
|
|
32
|
-
dashboardDataOrigin: origin,
|
|
33
|
-
}) as KronosysUpdatePayload;
|
|
34
|
-
|
|
35
|
-
it("affiche en développeur + local_next + aucun chemin", () => {
|
|
36
|
-
expect(showWorkspaceFoldersEmptyMessage(devPayload("local_next"), 0)).toBe(true);
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
it("masque en profil gestionnaire même avec origine héritée", () => {
|
|
40
|
-
const p = {
|
|
41
|
-
viewType: "dashboard",
|
|
42
|
-
cfg: { usageProfile: "manager" },
|
|
43
|
-
dashboardDataOrigin: "legacy_external" as const,
|
|
44
|
-
} as KronosysUpdatePayload;
|
|
45
|
-
expect(showWorkspaceFoldersEmptyMessage(p, 0)).toBe(false);
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
it("affiche en développeur + legacy_external + aucun chemin", () => {
|
|
49
|
-
expect(showWorkspaceFoldersEmptyMessage(devPayload("legacy_external"), 0)).toBe(true);
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
it("masque dès qu’au moins un chemin est résolu", () => {
|
|
53
|
-
expect(showWorkspaceFoldersEmptyMessage(devPayload("legacy_external"), 1)).toBe(false);
|
|
54
|
-
});
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
describe("workspaceCodeMetricsEnabled / showIdeLinkedCodeTimingMetrics", () => {
|
|
58
|
-
const dev = {
|
|
59
|
-
viewType: "dashboard",
|
|
60
|
-
cfg: { usageProfile: "developer" },
|
|
61
|
-
dashboardDataOrigin: "local_next" as const,
|
|
62
|
-
} as KronosysUpdatePayload;
|
|
63
|
-
|
|
64
|
-
it("workspaceCodeMetricsEnabled est vrai en profil développeur", () => {
|
|
65
|
-
expect(workspaceCodeMetricsEnabled(dev, dev.cfg as Record<string, unknown>)).toBe(true);
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it("extensionLinkedDeveloperMetrics reste aligné (alias)", () => {
|
|
69
|
-
expect(extensionLinkedDeveloperMetrics(dev, dev.cfg as Record<string, unknown>)).toBe(true);
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
it("showIdeLinkedCodeTimingMetrics suit la même règle", () => {
|
|
73
|
-
expect(showIdeLinkedCodeTimingMetrics(dev, dev.cfg as Record<string, unknown>)).toBe(true);
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
it("faux en profil gestionnaire", () => {
|
|
77
|
-
const p = {
|
|
78
|
-
viewType: "dashboard",
|
|
79
|
-
cfg: { usageProfile: "manager" },
|
|
80
|
-
dashboardDataOrigin: "local_next" as const,
|
|
81
|
-
} as KronosysUpdatePayload;
|
|
82
|
-
expect(showIdeLinkedCodeTimingMetrics(p, p.cfg as Record<string, unknown>)).toBe(false);
|
|
83
|
-
});
|
|
84
|
-
});
|