@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.
- package/README.md +1 -1
- package/app/changelog/page.tsx +87 -19
- package/app/globals.css +10 -8
- package/app/guide/page.tsx +71 -34
- package/app/implementation/page.tsx +70 -60
- package/app/licenses/page.tsx +79 -47
- package/app/logs/page.tsx +103 -47
- package/app/page.tsx +104 -169
- package/app/reporting/page.tsx +1918 -1436
- package/app/settings/page.tsx +66 -44
- package/components/KronosysPayloadProvider.tsx +19 -5
- package/components/dashboard/AppShellHeaderKronoFocus.tsx +78 -0
- package/components/dashboard/AppShellHeaderToolbarLayout.tsx +36 -0
- package/components/dashboard/AppShellHeaderUtilityRibbon.tsx +19 -0
- package/components/dashboard/AppShellHeaderWallClock.tsx +23 -17
- package/components/dashboard/AppShellRouteNav.tsx +336 -209
- package/components/dashboard/AppShellToolbarCommandCenter.tsx +225 -0
- package/components/dashboard/AppShellToolbarRouteNav.tsx +204 -0
- package/components/dashboard/DashboardCommandCenter.tsx +119 -30
- package/components/dashboard/KronoFocusPanel.tsx +287 -260
- package/components/dashboard/LanguageMenu.tsx +23 -7
- package/components/dashboard/PageRefreshButton.tsx +42 -16
- package/components/dashboard/ReportingTour.tsx +20 -2
- package/components/dashboard/SessionListPanel.tsx +4 -4
- package/components/dashboard/ThemeToggle.tsx +4 -3
- package/components/dashboard/useAnchoredFloatingPortalStyle.ts +9 -2
- package/components/dashboard/useKronoFocusLiveSeconds.ts +4 -2
- package/lib/appShellHeaderClasses.ts +22 -3
- package/lib/appShellToolbarChrome.ts +112 -0
- package/lib/appShellToolbarDeferredIntents.ts +112 -0
- package/lib/appShellToolbarSessionSlices.ts +67 -0
- package/lib/dashboardCopy.ts +78 -29
- package/lib/dashboardQuickSearch.ts +37 -6
- package/lib/dashboardUrlSession.ts +36 -0
- package/lib/generatedUserChangelog.ts +14 -0
- package/lib/implementationNotes.ts +18 -14
- package/lib/reportingAggregate.ts +68 -9
- package/lib/reportingMetricHelp.ts +8 -8
- package/lib/reportingStrings.ts +118 -9
- package/lib/reportingTagWeekBreakdown.ts +55 -13
- package/lib/settingsCopy.ts +6 -7
- package/lib/userGuideCopy.ts +29 -26
- package/package.json +7 -5
- package/server/db.ts +6 -4
- package/server/dbSchema.ts +2 -2
- package/components/dashboard/AppShellCommandCenterPlaceholder.tsx +0 -17
package/app/reporting/page.tsx
CHANGED
|
@@ -48,11 +48,13 @@ import { AppVersionStamp } from "@/components/dashboard/AppVersionStamp";
|
|
|
48
48
|
import {
|
|
49
49
|
appShellHeaderClassName,
|
|
50
50
|
appShellHeaderTitleMetaRowClassName,
|
|
51
|
-
appShellHeaderToolbarClassName,
|
|
52
51
|
} from "@/lib/appShellHeaderClasses";
|
|
53
|
-
import {
|
|
52
|
+
import { AppShellHeaderToolbarLayout } from "@/components/dashboard/AppShellHeaderToolbarLayout";
|
|
53
|
+
import { AppShellToolbarCommandCenter } from "@/components/dashboard/AppShellToolbarCommandCenter";
|
|
54
54
|
import { AppShellHeaderSessionMeta } from "@/components/dashboard/AppShellHeaderSessionMeta";
|
|
55
55
|
import { AppShellHeaderWallClock } from "@/components/dashboard/AppShellHeaderWallClock";
|
|
56
|
+
import { AppShellHeaderKronoFocus } from "@/components/dashboard/AppShellHeaderKronoFocus";
|
|
57
|
+
import { AppShellHeaderUtilityRibbon } from "@/components/dashboard/AppShellHeaderUtilityRibbon";
|
|
56
58
|
import { dashboardStrings, type Lang } from "@/lib/dashboardCopy";
|
|
57
59
|
import {
|
|
58
60
|
reportingPresetDay,
|
|
@@ -65,7 +67,7 @@ import {
|
|
|
65
67
|
ReportingPageTocDesktop,
|
|
66
68
|
ReportingPageTocMobile,
|
|
67
69
|
} from "@/components/dashboard/ReportingPageToc";
|
|
68
|
-
import { reportingStrings } from "@/lib/reportingStrings";
|
|
70
|
+
import { reportingNav, reportingStrings } from "@/lib/reportingStrings";
|
|
69
71
|
import { trackCodeMetricsFromCfg } from "@/lib/usageProfile";
|
|
70
72
|
import {
|
|
71
73
|
addDaysYmd,
|
|
@@ -85,7 +87,7 @@ import { ReportingTour } from "@/components/dashboard/ReportingTour";
|
|
|
85
87
|
import { ScrollToTopFab } from "@/components/dashboard/ScrollToTopFab";
|
|
86
88
|
import { ThemeToggle } from "@/components/dashboard/ThemeToggle";
|
|
87
89
|
import { PageRefreshButton } from "@/components/dashboard/PageRefreshButton";
|
|
88
|
-
import {
|
|
90
|
+
import { AppShellToolbarRouteNav } from "@/components/dashboard/AppShellToolbarRouteNav";
|
|
89
91
|
import {
|
|
90
92
|
calendarDateKeyInTimeZone,
|
|
91
93
|
readDashboardTimeZoneFromCfg,
|
|
@@ -102,6 +104,8 @@ import {
|
|
|
102
104
|
} from "@/components/dashboard/TaskSessionLiveCard";
|
|
103
105
|
import { useReportingInteractionState } from "@/components/dashboard/useReportingInteractionState";
|
|
104
106
|
|
|
107
|
+
type ReportingMainViewTab = "billing" | "rhythm" | "advanced";
|
|
108
|
+
|
|
105
109
|
type LiveShape = { language?: string };
|
|
106
110
|
type ReportingTaskInspectScope = {
|
|
107
111
|
kind: "tag" | "project";
|
|
@@ -177,7 +181,7 @@ function reportingArchivedExcludedRichText(
|
|
|
177
181
|
return (
|
|
178
182
|
<>
|
|
179
183
|
{template.slice(0, i)}
|
|
180
|
-
<strong className="font-semibold text-amber-
|
|
184
|
+
<strong className="font-semibold text-amber-900 tabular-nums dark:text-amber-50">
|
|
181
185
|
{label}
|
|
182
186
|
</strong>
|
|
183
187
|
{template.slice(i + marker.length)}
|
|
@@ -268,7 +272,7 @@ function StackedTaskBars({
|
|
|
268
272
|
className="flex w-8 shrink-0 flex-col items-center gap-1"
|
|
269
273
|
>
|
|
270
274
|
<div
|
|
271
|
-
className="flex h-[100px] w-full flex-col justify-end rounded-t bg-zinc-800/80"
|
|
275
|
+
className="flex h-[100px] w-full flex-col justify-end rounded-t bg-zinc-200/90 dark:bg-zinc-800/80"
|
|
272
276
|
title={`${label}: ${b} / ${a}`}
|
|
273
277
|
>
|
|
274
278
|
{donePx > 0 ? (
|
|
@@ -415,7 +419,7 @@ function ReportingFilteredBadge({
|
|
|
415
419
|
}
|
|
416
420
|
return (
|
|
417
421
|
<span
|
|
418
|
-
className="inline-flex shrink-0 items-center rounded border border-amber-
|
|
422
|
+
className="inline-flex shrink-0 items-center rounded border border-amber-400/50 bg-amber-100/90 px-1.5 py-px text-[0.65rem] font-semibold uppercase tracking-wide text-amber-900 dark:border-amber-500/35 dark:bg-amber-950/40 dark:text-amber-200/90"
|
|
419
423
|
title={titleText}
|
|
420
424
|
>
|
|
421
425
|
{label}
|
|
@@ -432,6 +436,10 @@ function ReportingContent() {
|
|
|
432
436
|
const [dateFrom, setDateFrom] = useState("");
|
|
433
437
|
const [dateTo, setDateTo] = useState("");
|
|
434
438
|
const [selectedTags, setSelectedTags] = useState<string[]>([]);
|
|
439
|
+
/** Grille « temps par projet » : `work` = jetons @ (défaut) ; `personal` = temps `!` (`personalProject`). */
|
|
440
|
+
const [projectMinutesScope, setProjectMinutesScope] = useState<
|
|
441
|
+
"work" | "personal"
|
|
442
|
+
>("work");
|
|
435
443
|
const [workspaceSnapBusy, setWorkspaceSnapBusy] = useState(false);
|
|
436
444
|
const {
|
|
437
445
|
weekStartsOn,
|
|
@@ -453,6 +461,8 @@ function ReportingContent() {
|
|
|
453
461
|
portalReady,
|
|
454
462
|
taskInspectModalRef,
|
|
455
463
|
} = useReportingInteractionState<ReportingTaskInspectScope>();
|
|
464
|
+
const [reportingViewTab, setReportingViewTab] =
|
|
465
|
+
useState<ReportingMainViewTab>("billing");
|
|
456
466
|
|
|
457
467
|
useEffect(() => {
|
|
458
468
|
if (!taskInspectScope || !portalReady) {
|
|
@@ -559,6 +569,14 @@ function ReportingContent() {
|
|
|
559
569
|
return await refresh({ routerInvalidate: true });
|
|
560
570
|
}, [refresh]);
|
|
561
571
|
|
|
572
|
+
const postHeaderAction = useCallback(
|
|
573
|
+
async (body: Record<string, unknown>) => {
|
|
574
|
+
await postKronosysAction(body);
|
|
575
|
+
await refresh({ routerInvalidate: true });
|
|
576
|
+
},
|
|
577
|
+
[refresh],
|
|
578
|
+
);
|
|
579
|
+
|
|
562
580
|
const reportLocale = lang === "fr" ? "fr-CA" : "en-CA";
|
|
563
581
|
const tagDescriptions = (payload?.tagDescriptions ?? {}) as Record<
|
|
564
582
|
string,
|
|
@@ -738,6 +756,7 @@ function ReportingContent() {
|
|
|
738
756
|
dateToFilter,
|
|
739
757
|
reportTimeZone,
|
|
740
758
|
taskDefaultTagBucketEnabled,
|
|
759
|
+
projectMinutesScope,
|
|
741
760
|
);
|
|
742
761
|
}, [
|
|
743
762
|
mergedSessions,
|
|
@@ -746,8 +765,14 @@ function ReportingContent() {
|
|
|
746
765
|
dateToFilter,
|
|
747
766
|
reportTimeZone,
|
|
748
767
|
taskDefaultTagBucketEnabled,
|
|
768
|
+
projectMinutesScope,
|
|
749
769
|
]);
|
|
750
770
|
|
|
771
|
+
const projectNameCellToneClass =
|
|
772
|
+
projectMinutesScope === "personal"
|
|
773
|
+
? "text-rose-800 dark:text-rose-200/90"
|
|
774
|
+
: "text-sky-800 dark:text-sky-200/90";
|
|
775
|
+
|
|
751
776
|
const projectWeekCalendarRows = useMemo(
|
|
752
777
|
() => buildProjectWeekCalendarRows(projectTaskMinutesByDay, weekStartsOn),
|
|
753
778
|
[projectTaskMinutesByDay, weekStartsOn],
|
|
@@ -947,6 +972,13 @@ function ReportingContent() {
|
|
|
947
972
|
if (!projectKeyLower) {
|
|
948
973
|
continue;
|
|
949
974
|
}
|
|
975
|
+
const tagLedgerPersonal = row.tagKey.trim().toLowerCase().startsWith("!");
|
|
976
|
+
if (projectMinutesScope === "work" && tagLedgerPersonal) {
|
|
977
|
+
continue;
|
|
978
|
+
}
|
|
979
|
+
if (projectMinutesScope === "personal" && !tagLedgerPersonal) {
|
|
980
|
+
continue;
|
|
981
|
+
}
|
|
950
982
|
const bucketKey = `${row.weekStart}:::${projectKeyLower}`;
|
|
951
983
|
const list = byWeekProject.get(bucketKey) ?? [];
|
|
952
984
|
list.push(row);
|
|
@@ -961,7 +993,7 @@ function ReportingContent() {
|
|
|
961
993
|
);
|
|
962
994
|
}
|
|
963
995
|
return byWeekProject;
|
|
964
|
-
}, [tagWeekCalendarRowsVisible]);
|
|
996
|
+
}, [tagWeekCalendarRowsVisible, projectMinutesScope]);
|
|
965
997
|
|
|
966
998
|
const projectCalendarWeekGroups = useMemo(() => {
|
|
967
999
|
const byWeek = new Map<string, ProjectWeekCalendarRow[]>();
|
|
@@ -1132,7 +1164,7 @@ function ReportingContent() {
|
|
|
1132
1164
|
return (
|
|
1133
1165
|
<button
|
|
1134
1166
|
type="button"
|
|
1135
|
-
className={`${className} underline decoration-dotted underline-offset-2 hover:text-violet-
|
|
1167
|
+
className={`${className} underline decoration-dotted underline-offset-2 hover:text-violet-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-violet-400/60 dark:hover:text-violet-200`}
|
|
1136
1168
|
onClick={(event) => {
|
|
1137
1169
|
const rect = event.currentTarget.getBoundingClientRect();
|
|
1138
1170
|
openTaskInspectScope(scope, {
|
|
@@ -1334,6 +1366,66 @@ function ReportingContent() {
|
|
|
1334
1366
|
tagWeekCalendarRows.length > 0 ||
|
|
1335
1367
|
projectWeekCalendarRows.length > 0);
|
|
1336
1368
|
|
|
1369
|
+
const syncReportingTourTab = useCallback(
|
|
1370
|
+
(step: number) => {
|
|
1371
|
+
if (!reportingTourOpen) {
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1374
|
+
if (!hasReportingChartData) {
|
|
1375
|
+
if (step === 2) {
|
|
1376
|
+
setReportingViewTab("advanced");
|
|
1377
|
+
}
|
|
1378
|
+
return;
|
|
1379
|
+
}
|
|
1380
|
+
if (step <= 1) {
|
|
1381
|
+
setReportingViewTab("billing");
|
|
1382
|
+
} else if (step === 2) {
|
|
1383
|
+
setReportingViewTab("advanced");
|
|
1384
|
+
} else if (step === 3) {
|
|
1385
|
+
setReportingViewTab("rhythm");
|
|
1386
|
+
} else if (step <= 5) {
|
|
1387
|
+
setReportingViewTab("billing");
|
|
1388
|
+
}
|
|
1389
|
+
},
|
|
1390
|
+
[reportingTourOpen, hasReportingChartData],
|
|
1391
|
+
);
|
|
1392
|
+
|
|
1393
|
+
useEffect(() => {
|
|
1394
|
+
const applyHash = () => {
|
|
1395
|
+
if (typeof window === "undefined") {
|
|
1396
|
+
return;
|
|
1397
|
+
}
|
|
1398
|
+
const id = window.location.hash.slice(1);
|
|
1399
|
+
if (!id) {
|
|
1400
|
+
return;
|
|
1401
|
+
}
|
|
1402
|
+
const toAdvanced = new Set([
|
|
1403
|
+
"report-workspace-snapshot",
|
|
1404
|
+
"report-summary-kpis",
|
|
1405
|
+
"report-closure-by-kind",
|
|
1406
|
+
"report-loc-metrics",
|
|
1407
|
+
"report-daily-table",
|
|
1408
|
+
]);
|
|
1409
|
+
const toRhythm = new Set([
|
|
1410
|
+
"report-chart-sessions",
|
|
1411
|
+
"report-chart-tasks",
|
|
1412
|
+
"report-chart-task-time",
|
|
1413
|
+
"report-chart-session-wall",
|
|
1414
|
+
]);
|
|
1415
|
+
const toBilling = new Set(["report-tag-time", "report-projects"]);
|
|
1416
|
+
if (toAdvanced.has(id)) {
|
|
1417
|
+
setReportingViewTab("advanced");
|
|
1418
|
+
} else if (toRhythm.has(id)) {
|
|
1419
|
+
setReportingViewTab("rhythm");
|
|
1420
|
+
} else if (toBilling.has(id)) {
|
|
1421
|
+
setReportingViewTab("billing");
|
|
1422
|
+
}
|
|
1423
|
+
};
|
|
1424
|
+
applyHash();
|
|
1425
|
+
window.addEventListener("hashchange", applyHash);
|
|
1426
|
+
return () => window.removeEventListener("hashchange", applyHash);
|
|
1427
|
+
}, []);
|
|
1428
|
+
|
|
1337
1429
|
useEffect(() => {
|
|
1338
1430
|
if (!payload || !agg) {
|
|
1339
1431
|
return;
|
|
@@ -1348,44 +1440,59 @@ function ReportingContent() {
|
|
|
1348
1440
|
}
|
|
1349
1441
|
}, [payload, agg, searchParams, stripReportingTourReplayParam]);
|
|
1350
1442
|
|
|
1351
|
-
const reportingTocEntries = (() => {
|
|
1443
|
+
const reportingTocEntries = useMemo(() => {
|
|
1352
1444
|
if (!payload) {
|
|
1353
1445
|
return [] as { id: string; label: string }[];
|
|
1354
1446
|
}
|
|
1355
1447
|
const rows: { id: string; label: string }[] = [
|
|
1356
1448
|
{ id: "report-filters", label: t.filtersTitle },
|
|
1357
1449
|
];
|
|
1358
|
-
if (trackCodeMetrics) {
|
|
1359
|
-
rows.push({
|
|
1360
|
-
id: "report-workspace-snapshot",
|
|
1361
|
-
label: t.workspaceSnapshotTitle,
|
|
1362
|
-
});
|
|
1363
|
-
}
|
|
1364
1450
|
if (!agg) {
|
|
1451
|
+
if (trackCodeMetrics) {
|
|
1452
|
+
rows.push({
|
|
1453
|
+
id: "report-workspace-snapshot",
|
|
1454
|
+
label: t.workspaceSnapshotTitle,
|
|
1455
|
+
});
|
|
1456
|
+
}
|
|
1365
1457
|
return rows;
|
|
1366
1458
|
}
|
|
1367
|
-
rows.push({ id: "
|
|
1368
|
-
|
|
1369
|
-
if (trackCodeMetrics) {
|
|
1370
|
-
rows.push({ id: "report-loc-metrics", label: t.tocLocSection });
|
|
1371
|
-
}
|
|
1372
|
-
if (!hasReportingChartData) {
|
|
1373
|
-
return rows;
|
|
1374
|
-
}
|
|
1375
|
-
if (navigableWeekStarts.length > 1) {
|
|
1459
|
+
rows.push({ id: "reporting-view-tabs", label: t.tocViewTabs });
|
|
1460
|
+
if (hasReportingChartData && navigableWeekStarts.length > 1) {
|
|
1376
1461
|
rows.push({ id: "report-week-nav", label: t.tocWeekNav });
|
|
1377
1462
|
}
|
|
1378
1463
|
rows.push(
|
|
1379
|
-
{ id: "report-chart-sessions", label: t.chartSessionsPerDay },
|
|
1380
|
-
{ id: "report-chart-tasks", label: t.chartTasksByStatusPerDay },
|
|
1381
|
-
{ id: "report-chart-task-time", label: t.chartTaskTimePerDay },
|
|
1382
|
-
{ id: "report-chart-session-wall", label: t.chartSessionWallPerDay },
|
|
1383
|
-
{ id: "report-daily-table", label: t.tocDailyTable },
|
|
1384
1464
|
{ id: "report-tag-time", label: t.tocTagTimeSection },
|
|
1385
1465
|
{ id: "report-projects", label: t.projectSectionTitle },
|
|
1386
1466
|
);
|
|
1467
|
+
if (hasReportingChartData) {
|
|
1468
|
+
rows.push(
|
|
1469
|
+
{ id: "report-chart-sessions", label: t.chartSessionsPerDay },
|
|
1470
|
+
{ id: "report-chart-tasks", label: t.chartTasksByStatusPerDay },
|
|
1471
|
+
{ id: "report-chart-task-time", label: t.chartTaskTimePerDay },
|
|
1472
|
+
{ id: "report-chart-session-wall", label: t.chartSessionWallPerDay },
|
|
1473
|
+
{ id: "report-daily-table", label: t.tocDailyTable },
|
|
1474
|
+
);
|
|
1475
|
+
}
|
|
1476
|
+
rows.push(
|
|
1477
|
+
{ id: "report-summary-kpis", label: t.tocSummaryKpis },
|
|
1478
|
+
{ id: "report-closure-by-kind", label: t.tocClosureBreakdown },
|
|
1479
|
+
);
|
|
1480
|
+
if (trackCodeMetrics) {
|
|
1481
|
+
rows.push({ id: "report-loc-metrics", label: t.tocLocSection });
|
|
1482
|
+
rows.push({
|
|
1483
|
+
id: "report-workspace-snapshot",
|
|
1484
|
+
label: t.workspaceSnapshotTitle,
|
|
1485
|
+
});
|
|
1486
|
+
}
|
|
1387
1487
|
return rows;
|
|
1388
|
-
}
|
|
1488
|
+
}, [
|
|
1489
|
+
payload,
|
|
1490
|
+
agg,
|
|
1491
|
+
trackCodeMetrics,
|
|
1492
|
+
hasReportingChartData,
|
|
1493
|
+
navigableWeekStarts.length,
|
|
1494
|
+
t,
|
|
1495
|
+
]);
|
|
1389
1496
|
|
|
1390
1497
|
return (
|
|
1391
1498
|
<div className="min-h-screen bg-zinc-100 text-zinc-900 dark:bg-zinc-900 dark:text-zinc-100">
|
|
@@ -1414,46 +1521,61 @@ function ReportingContent() {
|
|
|
1414
1521
|
</div>
|
|
1415
1522
|
<AppShellHeaderSessionMeta payload={payload} dt={dt} />
|
|
1416
1523
|
</div>
|
|
1417
|
-
<
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1524
|
+
<AppShellHeaderToolbarLayout
|
|
1525
|
+
leading={
|
|
1526
|
+
<>
|
|
1527
|
+
<AppShellHeaderWallClock lang={lang} dt={dt} />
|
|
1528
|
+
<AppShellHeaderKronoFocus
|
|
1529
|
+
payload={payload}
|
|
1530
|
+
dt={dt}
|
|
1531
|
+
post={postHeaderAction}
|
|
1532
|
+
/>
|
|
1533
|
+
<AppShellToolbarCommandCenter
|
|
1534
|
+
dt={dt}
|
|
1535
|
+
lang={lang}
|
|
1536
|
+
dashboardSessionNavId={dashboardSessionNavId}
|
|
1537
|
+
onManualRefresh={handleManualRefresh}
|
|
1538
|
+
/>
|
|
1539
|
+
</>
|
|
1540
|
+
}
|
|
1541
|
+
nav={
|
|
1542
|
+
<AppShellToolbarRouteNav
|
|
1422
1543
|
current="reporting"
|
|
1423
|
-
labels={
|
|
1424
|
-
dashboard: t.dashboard,
|
|
1425
|
-
reporting: t.reporting,
|
|
1426
|
-
settings: t.settings,
|
|
1427
|
-
logs: t.logs,
|
|
1428
|
-
guide: t.guide,
|
|
1429
|
-
}}
|
|
1544
|
+
labels={reportingNav(lang)}
|
|
1430
1545
|
navAriaLabel={dt.appShellRouteNavAria}
|
|
1431
1546
|
dashboardSessionId={dashboardSessionNavId}
|
|
1432
|
-
reserveGlobalPauseSlot
|
|
1433
|
-
/>
|
|
1434
|
-
<ThemeToggle lang={lang} />
|
|
1435
|
-
<PageRefreshButton
|
|
1436
|
-
title={dt.pageRefreshTitle}
|
|
1437
|
-
ariaLabel={dt.pageRefreshAriaLabel}
|
|
1438
|
-
inlineMessages={{
|
|
1439
|
-
loading: dt.pageRefreshProgressLabel,
|
|
1440
|
-
success: dt.pageRefreshDoneToast,
|
|
1441
|
-
error: dt.pageRefreshFailedToast,
|
|
1442
|
-
}}
|
|
1443
|
-
onRefresh={handleManualRefresh}
|
|
1444
|
-
/>
|
|
1445
|
-
<LanguageMenu
|
|
1446
1547
|
lang={lang}
|
|
1447
|
-
|
|
1448
|
-
labelFr="Français"
|
|
1449
|
-
menuHeading={lang === "fr" ? "Langue" : "Language"}
|
|
1450
|
-
triggerAriaLabel={
|
|
1451
|
-
lang === "fr" ? "Langue de l’interface" : "Interface language"
|
|
1452
|
-
}
|
|
1453
|
-
onSelect={(next) => void postLang(next)}
|
|
1548
|
+
dt={dt}
|
|
1454
1549
|
/>
|
|
1455
|
-
|
|
1456
|
-
|
|
1550
|
+
}
|
|
1551
|
+
trailing={
|
|
1552
|
+
<AppShellHeaderUtilityRibbon
|
|
1553
|
+
ariaLabel={dt.appShellUtilityToolbarGroupAria}
|
|
1554
|
+
>
|
|
1555
|
+
<ThemeToggle lang={lang} />
|
|
1556
|
+
<PageRefreshButton
|
|
1557
|
+
title={dt.pageRefreshTitle}
|
|
1558
|
+
ariaLabel={dt.pageRefreshAriaLabel}
|
|
1559
|
+
inlineMessages={{
|
|
1560
|
+
loading: dt.pageRefreshProgressLabel,
|
|
1561
|
+
success: dt.pageRefreshDoneToast,
|
|
1562
|
+
error: dt.pageRefreshFailedToast,
|
|
1563
|
+
}}
|
|
1564
|
+
onRefresh={handleManualRefresh}
|
|
1565
|
+
/>
|
|
1566
|
+
<LanguageMenu
|
|
1567
|
+
lang={lang}
|
|
1568
|
+
labelEn="English"
|
|
1569
|
+
labelFr="Français"
|
|
1570
|
+
menuHeading={lang === "fr" ? "Langue" : "Language"}
|
|
1571
|
+
triggerAriaLabel={
|
|
1572
|
+
lang === "fr" ? "Langue de l’interface" : "Interface language"
|
|
1573
|
+
}
|
|
1574
|
+
onSelect={(next) => void postLang(next)}
|
|
1575
|
+
/>
|
|
1576
|
+
</AppShellHeaderUtilityRibbon>
|
|
1577
|
+
}
|
|
1578
|
+
/>
|
|
1457
1579
|
</header>
|
|
1458
1580
|
|
|
1459
1581
|
<div className="w-full px-5 py-8 sm:px-8 lg:px-10 xl:px-12">
|
|
@@ -1483,12 +1605,14 @@ function ReportingContent() {
|
|
|
1483
1605
|
|
|
1484
1606
|
{error && (
|
|
1485
1607
|
<div
|
|
1486
|
-
className="mb-8 rounded-lg border border-red-
|
|
1608
|
+
className="mb-8 rounded-lg border border-red-300 bg-red-50 px-4 py-3 text-sm text-red-900 dark:border-red-900/60 dark:bg-red-950/40 dark:text-red-100"
|
|
1487
1609
|
role="alert"
|
|
1488
1610
|
>
|
|
1489
|
-
<strong className="block text-red-200">
|
|
1611
|
+
<strong className="block text-red-800 dark:text-red-200">
|
|
1612
|
+
API
|
|
1613
|
+
</strong>
|
|
1490
1614
|
{headerApiError}
|
|
1491
|
-
<pre className="mt-2 overflow-x-auto text-xs text-red-200/80">
|
|
1615
|
+
<pre className="mt-2 overflow-x-auto text-xs text-red-800/90 dark:text-red-200/80">
|
|
1492
1616
|
{error}
|
|
1493
1617
|
</pre>
|
|
1494
1618
|
</div>
|
|
@@ -1511,10 +1635,10 @@ function ReportingContent() {
|
|
|
1511
1635
|
/>
|
|
1512
1636
|
<section
|
|
1513
1637
|
id="report-filters"
|
|
1514
|
-
className="mb-10 scroll-mt-28 rounded-xl border border-zinc-800 bg-zinc-900/50 p-4 sm:p-6"
|
|
1638
|
+
className="mb-10 scroll-mt-28 rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4 sm:p-6"
|
|
1515
1639
|
>
|
|
1516
1640
|
<div className="flex flex-wrap items-center gap-2 gap-y-2">
|
|
1517
|
-
<h2 className="text-sm font-semibold uppercase tracking-wide text-zinc-400">
|
|
1641
|
+
<h2 className="text-sm font-semibold uppercase tracking-wide text-zinc-600 dark:text-zinc-400">
|
|
1518
1642
|
{t.filtersTitle}
|
|
1519
1643
|
</h2>
|
|
1520
1644
|
<InlineMetricHelpTrigger
|
|
@@ -1555,7 +1679,7 @@ function ReportingContent() {
|
|
|
1555
1679
|
type="date"
|
|
1556
1680
|
value={dateFrom}
|
|
1557
1681
|
onChange={(e) => setDateFrom(e.target.value)}
|
|
1558
|
-
className="rounded-lg border border-zinc-
|
|
1682
|
+
className="rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm text-zinc-900 shadow-inner shadow-zinc-900/[0.03] dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100 dark:shadow-none"
|
|
1559
1683
|
/>
|
|
1560
1684
|
</label>
|
|
1561
1685
|
<label className="flex flex-col gap-1 text-xs text-zinc-500">
|
|
@@ -1564,12 +1688,12 @@ function ReportingContent() {
|
|
|
1564
1688
|
type="date"
|
|
1565
1689
|
value={dateTo}
|
|
1566
1690
|
onChange={(e) => setDateTo(e.target.value)}
|
|
1567
|
-
className="rounded-lg border border-zinc-
|
|
1691
|
+
className="rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm text-zinc-900 shadow-inner shadow-zinc-900/[0.03] dark:border-zinc-700 dark:bg-zinc-950 dark:text-zinc-100 dark:shadow-none"
|
|
1568
1692
|
/>
|
|
1569
1693
|
</label>
|
|
1570
1694
|
<button
|
|
1571
1695
|
type="button"
|
|
1572
|
-
className="rounded-lg border border-zinc-
|
|
1696
|
+
className="rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm text-zinc-700 hover:bg-zinc-100 dark:border-zinc-600 dark:bg-transparent dark:text-zinc-300 dark:hover:bg-zinc-800"
|
|
1573
1697
|
onClick={() => {
|
|
1574
1698
|
const p = reportingPresetDay();
|
|
1575
1699
|
setDateFrom(p.from);
|
|
@@ -1580,7 +1704,7 @@ function ReportingContent() {
|
|
|
1580
1704
|
</button>
|
|
1581
1705
|
<button
|
|
1582
1706
|
type="button"
|
|
1583
|
-
className="rounded-lg border border-zinc-
|
|
1707
|
+
className="rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm text-zinc-700 hover:bg-zinc-100 dark:border-zinc-600 dark:bg-transparent dark:text-zinc-300 dark:hover:bg-zinc-800"
|
|
1584
1708
|
onClick={() => {
|
|
1585
1709
|
setDateFrom("");
|
|
1586
1710
|
setDateTo("");
|
|
@@ -1600,7 +1724,7 @@ function ReportingContent() {
|
|
|
1600
1724
|
<div className="flex flex-wrap gap-2">
|
|
1601
1725
|
<button
|
|
1602
1726
|
type="button"
|
|
1603
|
-
className="rounded-lg border border-zinc-
|
|
1727
|
+
className="rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm text-zinc-700 hover:border-zinc-400 hover:bg-zinc-100 dark:border-zinc-600 dark:bg-transparent dark:text-zinc-300 dark:hover:border-zinc-500 dark:hover:bg-zinc-800"
|
|
1604
1728
|
onClick={() => {
|
|
1605
1729
|
const p = reportingPresetDay();
|
|
1606
1730
|
setDateFrom(p.from);
|
|
@@ -1611,7 +1735,7 @@ function ReportingContent() {
|
|
|
1611
1735
|
</button>
|
|
1612
1736
|
<button
|
|
1613
1737
|
type="button"
|
|
1614
|
-
className="rounded-lg border border-zinc-
|
|
1738
|
+
className="rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm text-zinc-700 hover:border-zinc-400 hover:bg-zinc-100 dark:border-zinc-600 dark:bg-transparent dark:text-zinc-300 dark:hover:border-zinc-500 dark:hover:bg-zinc-800"
|
|
1615
1739
|
onClick={() => {
|
|
1616
1740
|
const p = reportingPresetWeek();
|
|
1617
1741
|
setDateFrom(p.from);
|
|
@@ -1622,7 +1746,7 @@ function ReportingContent() {
|
|
|
1622
1746
|
</button>
|
|
1623
1747
|
<button
|
|
1624
1748
|
type="button"
|
|
1625
|
-
className="rounded-lg border border-zinc-
|
|
1749
|
+
className="rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm text-zinc-700 hover:border-zinc-400 hover:bg-zinc-100 dark:border-zinc-600 dark:bg-transparent dark:text-zinc-300 dark:hover:border-zinc-500 dark:hover:bg-zinc-800"
|
|
1626
1750
|
onClick={() => {
|
|
1627
1751
|
const p = reportingPresetMonth();
|
|
1628
1752
|
setDateFrom(p.from);
|
|
@@ -1633,7 +1757,7 @@ function ReportingContent() {
|
|
|
1633
1757
|
</button>
|
|
1634
1758
|
<button
|
|
1635
1759
|
type="button"
|
|
1636
|
-
className="rounded-lg border border-zinc-
|
|
1760
|
+
className="rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm text-zinc-700 hover:border-zinc-400 hover:bg-zinc-100 dark:border-zinc-600 dark:bg-transparent dark:text-zinc-300 dark:hover:border-zinc-500 dark:hover:bg-zinc-800"
|
|
1637
1761
|
onClick={() => {
|
|
1638
1762
|
const p = reportingPresetYear();
|
|
1639
1763
|
setDateFrom(p.from);
|
|
@@ -1669,8 +1793,8 @@ function ReportingContent() {
|
|
|
1669
1793
|
onClick={() => toggleTag(tag)}
|
|
1670
1794
|
className={`rounded-full border px-2.5 py-1 text-xs font-medium transition-colors ${
|
|
1671
1795
|
on
|
|
1672
|
-
? "border-violet-500 bg-violet-600/30 text-violet-100"
|
|
1673
|
-
: "border-zinc-600 bg-zinc-950 text-zinc-400 hover:border-zinc-500"
|
|
1796
|
+
? "border-violet-500 bg-violet-200/90 text-violet-900 dark:bg-violet-600/30 dark:text-violet-100"
|
|
1797
|
+
: "border-zinc-300 bg-white text-zinc-600 hover:border-zinc-400 hover:bg-zinc-50 dark:border-zinc-600 dark:bg-zinc-950 dark:text-zinc-400 dark:hover:border-zinc-500"
|
|
1674
1798
|
}`}
|
|
1675
1799
|
>
|
|
1676
1800
|
{formatTagDisplay(tag)}
|
|
@@ -1684,7 +1808,7 @@ function ReportingContent() {
|
|
|
1684
1808
|
<button
|
|
1685
1809
|
type="button"
|
|
1686
1810
|
disabled={!reportingFiltersActive}
|
|
1687
|
-
className="rounded-lg border border-zinc-
|
|
1811
|
+
className="rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm text-zinc-700 hover:border-zinc-400 hover:bg-zinc-100 disabled:cursor-not-allowed disabled:opacity-40 dark:border-zinc-600 dark:bg-transparent dark:text-zinc-300 dark:hover:border-zinc-500 dark:hover:bg-zinc-800"
|
|
1688
1812
|
onClick={() => {
|
|
1689
1813
|
setDateFrom("");
|
|
1690
1814
|
setDateTo("");
|
|
@@ -1696,14 +1820,14 @@ function ReportingContent() {
|
|
|
1696
1820
|
</div>
|
|
1697
1821
|
</section>
|
|
1698
1822
|
|
|
1699
|
-
{trackCodeMetrics ? (
|
|
1823
|
+
{trackCodeMetrics && !agg ? (
|
|
1700
1824
|
<section
|
|
1701
1825
|
id="report-workspace-snapshot"
|
|
1702
|
-
className="mb-10 scroll-mt-28 rounded-xl border border-zinc-800 bg-zinc-900/50 p-4 sm:p-6"
|
|
1826
|
+
className="mb-10 scroll-mt-28 rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4 sm:p-6"
|
|
1703
1827
|
>
|
|
1704
1828
|
<div className="flex flex-wrap items-start justify-between gap-3">
|
|
1705
1829
|
<div className="flex min-w-0 max-w-full items-center gap-1">
|
|
1706
|
-
<h2 className="text-sm font-semibold uppercase tracking-wide text-zinc-400">
|
|
1830
|
+
<h2 className="text-sm font-semibold uppercase tracking-wide text-zinc-600 dark:text-zinc-400">
|
|
1707
1831
|
{t.workspaceSnapshotTitle}
|
|
1708
1832
|
</h2>
|
|
1709
1833
|
<InlineMetricHelpTrigger
|
|
@@ -1714,7 +1838,7 @@ function ReportingContent() {
|
|
|
1714
1838
|
<button
|
|
1715
1839
|
type="button"
|
|
1716
1840
|
disabled={workspaceSnapBusy}
|
|
1717
|
-
className="shrink-0 rounded-lg border border-zinc-
|
|
1841
|
+
className="shrink-0 rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm text-zinc-700 hover:bg-zinc-100 disabled:cursor-not-allowed disabled:opacity-50 dark:border-zinc-600 dark:bg-transparent dark:text-zinc-300 dark:hover:bg-zinc-800"
|
|
1718
1842
|
onClick={() => void refreshWorkspaceSnapshot()}
|
|
1719
1843
|
>
|
|
1720
1844
|
{workspaceSnapBusy
|
|
@@ -1755,7 +1879,7 @@ function ReportingContent() {
|
|
|
1755
1879
|
body={t.metricHelpWsTotalLinesBody}
|
|
1756
1880
|
/>
|
|
1757
1881
|
</div>
|
|
1758
|
-
<div className="tabular-nums text-lg font-semibold text-zinc-100">
|
|
1882
|
+
<div className="tabular-nums text-lg font-semibold text-zinc-900 dark:text-zinc-100">
|
|
1759
1883
|
{ws.totalLines}
|
|
1760
1884
|
</div>
|
|
1761
1885
|
</div>
|
|
@@ -1769,7 +1893,7 @@ function ReportingContent() {
|
|
|
1769
1893
|
body={t.metricHelpWsFileCountBody}
|
|
1770
1894
|
/>
|
|
1771
1895
|
</div>
|
|
1772
|
-
<div className="tabular-nums text-lg font-semibold text-zinc-100">
|
|
1896
|
+
<div className="tabular-nums text-lg font-semibold text-zinc-900 dark:text-zinc-100">
|
|
1773
1897
|
{ws.fileCount}
|
|
1774
1898
|
</div>
|
|
1775
1899
|
</div>
|
|
@@ -1779,7 +1903,7 @@ function ReportingContent() {
|
|
|
1779
1903
|
) : (
|
|
1780
1904
|
<div className="mt-4 overflow-x-auto">
|
|
1781
1905
|
<table className="w-full min-w-[22rem] text-left text-sm">
|
|
1782
|
-
<thead className="border-b border-zinc-
|
|
1906
|
+
<thead className="border-b border-zinc-200 text-xs uppercase text-zinc-500 dark:border-zinc-800">
|
|
1783
1907
|
<tr>
|
|
1784
1908
|
<th className="py-2 pr-3 font-medium">
|
|
1785
1909
|
<div className="flex min-h-5 items-center gap-0.5">
|
|
@@ -1823,17 +1947,17 @@ function ReportingContent() {
|
|
|
1823
1947
|
{ws.byLanguage.map((row) => (
|
|
1824
1948
|
<tr
|
|
1825
1949
|
key={row.languageId}
|
|
1826
|
-
className="border-b border-zinc-
|
|
1950
|
+
className="border-b border-zinc-200/90 last:border-0 dark:border-zinc-800/80"
|
|
1827
1951
|
>
|
|
1828
1952
|
<td className="py-2 pr-3 font-mono text-xs text-zinc-400">
|
|
1829
1953
|
{row.languageId}
|
|
1830
1954
|
</td>
|
|
1831
|
-
<td className="py-2 pr-3 tabular-nums text-zinc-200">
|
|
1955
|
+
<td className="py-2 pr-3 tabular-nums text-zinc-800 dark:text-zinc-200">
|
|
1832
1956
|
{row.lines}
|
|
1833
1957
|
</td>
|
|
1834
1958
|
<td className="py-2">
|
|
1835
1959
|
<div className="flex items-center gap-2">
|
|
1836
|
-
<div className="h-2 min-w-[4rem] flex-1 overflow-hidden rounded bg-zinc-800">
|
|
1960
|
+
<div className="h-2 min-w-[4rem] flex-1 overflow-hidden rounded bg-zinc-200 dark:bg-zinc-800">
|
|
1837
1961
|
<div
|
|
1838
1962
|
className="h-full rounded bg-sky-600/85"
|
|
1839
1963
|
style={{
|
|
@@ -1859,7 +1983,7 @@ function ReportingContent() {
|
|
|
1859
1983
|
);
|
|
1860
1984
|
}
|
|
1861
1985
|
return (
|
|
1862
|
-
<p className="mt-4 text-sm text-amber-200/90">
|
|
1986
|
+
<p className="mt-4 text-sm text-amber-800 dark:text-amber-200/90">
|
|
1863
1987
|
{ws.reason === "no_workspace"
|
|
1864
1988
|
? t.workspaceSnapshotNoWorkspace
|
|
1865
1989
|
: ws.reason === "empty"
|
|
@@ -1871,7 +1995,7 @@ function ReportingContent() {
|
|
|
1871
1995
|
{archivedExcludedTaskMinutes > 1e-9 ? (
|
|
1872
1996
|
<div
|
|
1873
1997
|
role="status"
|
|
1874
|
-
className="rounded-lg border border-amber-
|
|
1998
|
+
className="rounded-lg border border-amber-500/40 bg-amber-50 px-3 py-2.5 text-sm leading-relaxed text-amber-950 dark:border-amber-500/40 dark:bg-amber-950/30 dark:text-amber-100/95"
|
|
1875
1999
|
>
|
|
1876
2000
|
{reportingArchivedExcludedRichText(
|
|
1877
2001
|
t.reportingArchivedExcludedAside,
|
|
@@ -1884,259 +2008,156 @@ function ReportingContent() {
|
|
|
1884
2008
|
|
|
1885
2009
|
{agg && (
|
|
1886
2010
|
<>
|
|
1887
|
-
<
|
|
1888
|
-
id="
|
|
1889
|
-
className="mb-
|
|
2011
|
+
<div
|
|
2012
|
+
id="reporting-view-tabs"
|
|
2013
|
+
className="mb-8 flex flex-wrap gap-2 border-b border-zinc-200 pb-4 dark:border-zinc-700/80"
|
|
2014
|
+
role="tablist"
|
|
2015
|
+
aria-label={t.reportingViewTabGroupAria}
|
|
1890
2016
|
>
|
|
1891
|
-
<
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
/>
|
|
1940
|
-
</div>
|
|
1941
|
-
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
1942
|
-
{agg.kronoFocusSessionsCompleted}
|
|
1943
|
-
</div>
|
|
1944
|
-
</div>
|
|
1945
|
-
<div className="rounded-xl border border-zinc-800 bg-zinc-900/50 p-4">
|
|
1946
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
1947
|
-
<span className="text-xs uppercase text-zinc-500">
|
|
1948
|
-
{t.summaryKronoFocusTasksUsed}
|
|
1949
|
-
</span>
|
|
1950
|
-
<InlineMetricHelpTrigger
|
|
1951
|
-
ariaLabel={t.metricHelpKronoFocusTasksUsedAria}
|
|
1952
|
-
body={t.metricHelpKronoFocusTasksUsedBody}
|
|
1953
|
-
/>
|
|
1954
|
-
</div>
|
|
1955
|
-
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
1956
|
-
{agg.kronoFocusTasksUsedCount}
|
|
1957
|
-
</div>
|
|
1958
|
-
</div>
|
|
1959
|
-
<div className="rounded-xl border border-zinc-800 bg-zinc-900/50 p-4">
|
|
1960
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
1961
|
-
<span className="text-xs uppercase text-zinc-500">
|
|
1962
|
-
{t.summaryKronoFocusCycles}
|
|
1963
|
-
</span>
|
|
1964
|
-
<InlineMetricHelpTrigger
|
|
1965
|
-
ariaLabel={t.metricHelpKronoFocusCyclesAria}
|
|
1966
|
-
body={t.metricHelpKronoFocusCyclesBody}
|
|
1967
|
-
/>
|
|
1968
|
-
</div>
|
|
1969
|
-
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
1970
|
-
{agg.kronoFocusTaskCyclesSum}
|
|
1971
|
-
</div>
|
|
1972
|
-
</div>
|
|
1973
|
-
<div className="rounded-xl border border-zinc-800 bg-zinc-900/50 p-4">
|
|
1974
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
1975
|
-
<span className="text-xs uppercase text-zinc-500">
|
|
1976
|
-
{t.summaryTaskTimeRecorded}
|
|
1977
|
-
</span>
|
|
1978
|
-
<InlineMetricHelpTrigger
|
|
1979
|
-
align="end"
|
|
1980
|
-
ariaLabel={t.metricHelpTaskTimeAria}
|
|
1981
|
-
body={t.metricHelpTaskTimeBody}
|
|
1982
|
-
/>
|
|
1983
|
-
</div>
|
|
1984
|
-
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
1985
|
-
{formatDuration(agg.taskMinutesTotal)}
|
|
1986
|
-
</div>
|
|
1987
|
-
</div>
|
|
1988
|
-
<div className="rounded-xl border border-zinc-800 bg-zinc-900/50 p-4">
|
|
1989
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
1990
|
-
<span className="text-xs uppercase text-zinc-500">
|
|
1991
|
-
{t.summarySessionCoding}
|
|
1992
|
-
</span>
|
|
1993
|
-
<InlineMetricHelpTrigger
|
|
1994
|
-
ariaLabel={t.metricHelpSessionCodingAria}
|
|
1995
|
-
body={t.metricHelpSessionCodingBody}
|
|
1996
|
-
/>
|
|
1997
|
-
</div>
|
|
1998
|
-
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
1999
|
-
{formatDuration(agg.sessionCodingMinutesTotal)}
|
|
2000
|
-
</div>
|
|
2001
|
-
</div>
|
|
2002
|
-
<div className="rounded-xl border border-zinc-800 bg-zinc-900/50 p-4">
|
|
2003
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2004
|
-
<span className="text-xs uppercase text-zinc-500">
|
|
2005
|
-
{t.summarySessionActive}
|
|
2006
|
-
</span>
|
|
2007
|
-
<InlineMetricHelpTrigger
|
|
2008
|
-
ariaLabel={t.metricHelpSessionActiveAria}
|
|
2009
|
-
body={t.metricHelpSessionActiveBody}
|
|
2010
|
-
/>
|
|
2011
|
-
</div>
|
|
2012
|
-
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
2013
|
-
{formatDuration(agg.sessionActiveMinutesTotal)}
|
|
2014
|
-
</div>
|
|
2015
|
-
</div>
|
|
2016
|
-
<div className="rounded-xl border border-zinc-800 bg-zinc-900/50 p-4">
|
|
2017
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2018
|
-
<span className="text-xs uppercase text-zinc-500">
|
|
2019
|
-
{t.summarySessionWallClock}
|
|
2020
|
-
</span>
|
|
2021
|
-
<InlineMetricHelpTrigger
|
|
2022
|
-
align="end"
|
|
2023
|
-
ariaLabel={t.metricHelpSessionWallSummaryAria}
|
|
2024
|
-
body={t.metricHelpSessionWallSummaryBody}
|
|
2025
|
-
/>
|
|
2026
|
-
</div>
|
|
2027
|
-
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
2028
|
-
{formatDuration(agg.sessionWallClockMinutesTotal)}
|
|
2029
|
-
</div>
|
|
2030
|
-
</div>
|
|
2031
|
-
<div className="rounded-xl border border-zinc-800 bg-zinc-900/50 p-4">
|
|
2032
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2033
|
-
<span className="text-xs uppercase text-zinc-500">
|
|
2034
|
-
{t.summaryAssiduityWithReference}
|
|
2035
|
-
</span>
|
|
2036
|
-
<InlineMetricHelpTrigger
|
|
2037
|
-
align="end"
|
|
2038
|
-
ariaLabel={t.metricHelpAssiduityRefAria}
|
|
2039
|
-
body={t.metricHelpAssiduityRefBody}
|
|
2040
|
-
/>
|
|
2041
|
-
</div>
|
|
2042
|
-
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
2043
|
-
{agg.assiduityReferenceSessionCount}
|
|
2044
|
-
</div>
|
|
2045
|
-
</div>
|
|
2046
|
-
<div className="rounded-xl border border-zinc-800 bg-zinc-900/50 p-4">
|
|
2047
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2048
|
-
<span className="text-xs uppercase text-zinc-500">
|
|
2049
|
-
{t.summaryAssiduityLateSessions}
|
|
2050
|
-
</span>
|
|
2051
|
-
<InlineMetricHelpTrigger
|
|
2052
|
-
align="end"
|
|
2053
|
-
ariaLabel={t.metricHelpAssiduityLateCountAria}
|
|
2054
|
-
body={t.metricHelpAssiduityLateCountBody}
|
|
2055
|
-
/>
|
|
2056
|
-
</div>
|
|
2057
|
-
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
2058
|
-
{agg.assiduityLateSessionCount}
|
|
2059
|
-
</div>
|
|
2060
|
-
</div>
|
|
2061
|
-
<div className="rounded-xl border border-zinc-800 bg-zinc-900/50 p-4">
|
|
2062
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2063
|
-
<span className="text-xs uppercase text-zinc-500">
|
|
2064
|
-
{t.summaryAssiduityLateTotal}
|
|
2065
|
-
</span>
|
|
2066
|
-
<InlineMetricHelpTrigger
|
|
2067
|
-
align="end"
|
|
2068
|
-
ariaLabel={t.metricHelpAssiduityLateTotalAria}
|
|
2069
|
-
body={t.metricHelpAssiduityLateTotalBody}
|
|
2070
|
-
/>
|
|
2071
|
-
</div>
|
|
2072
|
-
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
2073
|
-
{formatDuration(agg.assiduityLateMinutesTotal)}
|
|
2074
|
-
</div>
|
|
2075
|
-
</div>
|
|
2076
|
-
<div className="rounded-xl border border-zinc-800 bg-zinc-900/50 p-4">
|
|
2077
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2078
|
-
<span className="text-xs uppercase text-zinc-500">
|
|
2079
|
-
{t.summaryAssiduityAvgLateWhenLate}
|
|
2080
|
-
</span>
|
|
2081
|
-
<InlineMetricHelpTrigger
|
|
2082
|
-
align="end"
|
|
2083
|
-
ariaLabel={t.metricHelpAssiduityAvgLateAria}
|
|
2084
|
-
body={t.metricHelpAssiduityAvgLateBody}
|
|
2085
|
-
/>
|
|
2086
|
-
</div>
|
|
2087
|
-
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
2088
|
-
{agg.assiduityAverageLateMinutesWhenLate == null
|
|
2089
|
-
? "—"
|
|
2090
|
-
: formatDuration(
|
|
2091
|
-
agg.assiduityAverageLateMinutesWhenLate,
|
|
2092
|
-
)}
|
|
2093
|
-
</div>
|
|
2094
|
-
</div>
|
|
2095
|
-
</div>
|
|
2096
|
-
<div
|
|
2097
|
-
id="report-closure-by-kind"
|
|
2098
|
-
className="mt-4 rounded-xl border border-zinc-800 bg-zinc-900/50 p-4"
|
|
2017
|
+
<button
|
|
2018
|
+
type="button"
|
|
2019
|
+
role="tab"
|
|
2020
|
+
id="reporting-tab-billing"
|
|
2021
|
+
aria-selected={reportingViewTab === "billing"}
|
|
2022
|
+
className={`rounded-lg border px-3 py-2 text-sm font-medium transition ${
|
|
2023
|
+
reportingViewTab === "billing"
|
|
2024
|
+
? "border-violet-500 bg-violet-100 text-violet-900 dark:bg-violet-950/40 dark:text-violet-100"
|
|
2025
|
+
: "border-zinc-300 bg-zinc-50 text-zinc-600 hover:border-zinc-400 hover:bg-zinc-100 hover:text-zinc-900 dark:border-zinc-600 dark:bg-zinc-900/60 dark:text-zinc-400 dark:hover:border-zinc-500 dark:hover:text-zinc-200"
|
|
2026
|
+
}`}
|
|
2027
|
+
onClick={() => setReportingViewTab("billing")}
|
|
2028
|
+
>
|
|
2029
|
+
{t.reportingViewTabBilling}
|
|
2030
|
+
</button>
|
|
2031
|
+
<button
|
|
2032
|
+
type="button"
|
|
2033
|
+
role="tab"
|
|
2034
|
+
id="reporting-tab-rhythm"
|
|
2035
|
+
aria-selected={reportingViewTab === "rhythm"}
|
|
2036
|
+
className={`rounded-lg border px-3 py-2 text-sm font-medium transition ${
|
|
2037
|
+
reportingViewTab === "rhythm"
|
|
2038
|
+
? "border-violet-500 bg-violet-100 text-violet-900 dark:bg-violet-950/40 dark:text-violet-100"
|
|
2039
|
+
: "border-zinc-300 bg-zinc-50 text-zinc-600 hover:border-zinc-400 hover:bg-zinc-100 hover:text-zinc-900 dark:border-zinc-600 dark:bg-zinc-900/60 dark:text-zinc-400 dark:hover:border-zinc-500 dark:hover:text-zinc-200"
|
|
2040
|
+
}`}
|
|
2041
|
+
onClick={() => setReportingViewTab("rhythm")}
|
|
2042
|
+
>
|
|
2043
|
+
{t.reportingViewTabRhythm}
|
|
2044
|
+
</button>
|
|
2045
|
+
<button
|
|
2046
|
+
type="button"
|
|
2047
|
+
role="tab"
|
|
2048
|
+
id="reporting-tab-advanced"
|
|
2049
|
+
aria-selected={reportingViewTab === "advanced"}
|
|
2050
|
+
className={`rounded-lg border px-3 py-2 text-sm font-medium transition ${
|
|
2051
|
+
reportingViewTab === "advanced"
|
|
2052
|
+
? "border-violet-500 bg-violet-100 text-violet-900 dark:bg-violet-950/40 dark:text-violet-100"
|
|
2053
|
+
: "border-zinc-300 bg-zinc-50 text-zinc-600 hover:border-zinc-400 hover:bg-zinc-100 hover:text-zinc-900 dark:border-zinc-600 dark:bg-zinc-900/60 dark:text-zinc-400 dark:hover:border-zinc-500 dark:hover:text-zinc-200"
|
|
2054
|
+
}`}
|
|
2055
|
+
onClick={() => setReportingViewTab("advanced")}
|
|
2056
|
+
>
|
|
2057
|
+
{t.reportingViewTabAdvanced}
|
|
2058
|
+
</button>
|
|
2059
|
+
</div>
|
|
2060
|
+
{hasReportingChartData && navigableWeekStarts.length > 1 ? (
|
|
2061
|
+
<section
|
|
2062
|
+
id="report-week-nav"
|
|
2063
|
+
className="mb-8 scroll-mt-28 rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4 sm:p-5"
|
|
2064
|
+
aria-label={t.weekNavAriaLabel}
|
|
2099
2065
|
>
|
|
2100
|
-
<div className="mb-
|
|
2101
|
-
<
|
|
2102
|
-
{t.
|
|
2103
|
-
</
|
|
2066
|
+
<div className="mb-2 flex flex-wrap items-center gap-2">
|
|
2067
|
+
<h2 className="text-sm font-semibold uppercase tracking-wide text-zinc-600 dark:text-zinc-400">
|
|
2068
|
+
{t.tocWeekNav}
|
|
2069
|
+
</h2>
|
|
2104
2070
|
<ReportingFilteredBadge
|
|
2105
2071
|
active={reportingFiltersActive}
|
|
2106
2072
|
label={t.sectionFilteredBadge}
|
|
2107
2073
|
titleText={t.sectionFilteredBadgeTitle}
|
|
2108
2074
|
/>
|
|
2109
|
-
<InlineMetricHelpTrigger
|
|
2110
|
-
ariaLabel={t.metricHelpClosureBreakdownAria}
|
|
2111
|
-
body={t.metricHelpClosureBreakdownBody}
|
|
2112
|
-
/>
|
|
2113
2075
|
</div>
|
|
2114
|
-
<div className="
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2076
|
+
<div className="flex flex-col items-stretch gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
2077
|
+
<p className="max-w-xl text-xs text-zinc-500">
|
|
2078
|
+
{t.weekNavHelpHint}
|
|
2079
|
+
</p>
|
|
2080
|
+
<div className="flex flex-wrap items-center justify-center gap-2 sm:justify-end">
|
|
2081
|
+
<InlineMetricHelpTrigger
|
|
2082
|
+
ariaLabel={t.metricHelpWeekNavAria}
|
|
2083
|
+
body={t.metricHelpWeekNavBody}
|
|
2084
|
+
/>
|
|
2085
|
+
<button
|
|
2086
|
+
type="button"
|
|
2087
|
+
className="inline-flex items-center gap-1 rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm text-zinc-800 hover:bg-zinc-100 disabled:cursor-not-allowed disabled:opacity-40 dark:border-zinc-600 dark:bg-transparent dark:text-zinc-200 dark:hover:bg-zinc-800"
|
|
2088
|
+
disabled={chartWeekNavIndexSafe <= 0}
|
|
2089
|
+
aria-label={t.weekNavPrev}
|
|
2090
|
+
onClick={() =>
|
|
2091
|
+
setChartWeekNavIndex((i) => {
|
|
2092
|
+
const cur =
|
|
2093
|
+
i < 0 ? navigableWeekStarts.length - 1 : i;
|
|
2094
|
+
return Math.max(0, cur - 1);
|
|
2095
|
+
})
|
|
2096
|
+
}
|
|
2097
|
+
>
|
|
2098
|
+
<ChevronLeft
|
|
2099
|
+
className="h-4 w-4 shrink-0"
|
|
2100
|
+
aria-hidden
|
|
2101
|
+
/>
|
|
2102
|
+
{t.weekNavPrev}
|
|
2103
|
+
</button>
|
|
2104
|
+
<span className="min-w-[10rem] px-2 text-center text-sm font-medium tabular-nums text-zinc-900 dark:text-zinc-100">
|
|
2105
|
+
{formatWeekRangeLabel(
|
|
2106
|
+
navigableWeekStarts[chartWeekNavIndexSafe],
|
|
2107
|
+
reportLocale,
|
|
2108
|
+
)}
|
|
2109
|
+
</span>
|
|
2110
|
+
<button
|
|
2111
|
+
type="button"
|
|
2112
|
+
className="inline-flex items-center gap-1 rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm text-zinc-800 hover:bg-zinc-100 disabled:cursor-not-allowed disabled:opacity-40 dark:border-zinc-600 dark:bg-transparent dark:text-zinc-200 dark:hover:bg-zinc-800"
|
|
2113
|
+
disabled={
|
|
2114
|
+
chartWeekNavIndexSafe >=
|
|
2115
|
+
navigableWeekStarts.length - 1
|
|
2116
|
+
}
|
|
2117
|
+
aria-label={t.weekNavNext}
|
|
2118
|
+
onClick={() =>
|
|
2119
|
+
setChartWeekNavIndex((i) => {
|
|
2120
|
+
const cur =
|
|
2121
|
+
i < 0 ? navigableWeekStarts.length - 1 : i;
|
|
2122
|
+
return Math.min(
|
|
2123
|
+
navigableWeekStarts.length - 1,
|
|
2124
|
+
cur + 1,
|
|
2125
|
+
);
|
|
2126
|
+
})
|
|
2127
|
+
}
|
|
2128
|
+
>
|
|
2129
|
+
{t.weekNavNext}
|
|
2130
|
+
<ChevronRight
|
|
2131
|
+
className="h-4 w-4 shrink-0"
|
|
2132
|
+
aria-hidden
|
|
2133
|
+
/>
|
|
2134
|
+
</button>
|
|
2135
|
+
</div>
|
|
2134
2136
|
</div>
|
|
2135
|
-
</
|
|
2137
|
+
</section>
|
|
2138
|
+
) : null}
|
|
2139
|
+
{!hasReportingChartData ? (
|
|
2140
|
+
<p className="mb-6 text-sm text-zinc-500">
|
|
2141
|
+
{t.noRowsInRange}
|
|
2142
|
+
</p>
|
|
2143
|
+
) : null}
|
|
2144
|
+
<div
|
|
2145
|
+
id="report-view-panel-billing"
|
|
2146
|
+
role="tabpanel"
|
|
2147
|
+
aria-labelledby="reporting-tab-billing"
|
|
2148
|
+
className={
|
|
2149
|
+
reportingViewTab !== "billing"
|
|
2150
|
+
? "hidden"
|
|
2151
|
+
: "mb-10 space-y-6"
|
|
2152
|
+
}
|
|
2153
|
+
>
|
|
2154
|
+
<p className="text-sm leading-relaxed text-zinc-600 dark:text-zinc-400">
|
|
2155
|
+
{t.reportingViewBillingIntro}
|
|
2156
|
+
</p>
|
|
2136
2157
|
{archivedExcludedTaskMinutes > 1e-9 ? (
|
|
2137
2158
|
<div
|
|
2138
2159
|
role="status"
|
|
2139
|
-
className="rounded-lg border border-amber-
|
|
2160
|
+
className="rounded-lg border border-amber-500/40 bg-amber-50 px-3 py-2.5 text-sm leading-relaxed text-amber-950 dark:border-amber-500/40 dark:bg-amber-950/30 dark:text-amber-100/95"
|
|
2140
2161
|
>
|
|
2141
2162
|
{reportingArchivedExcludedRichText(
|
|
2142
2163
|
t.reportingArchivedExcludedAside,
|
|
@@ -2144,76 +2165,29 @@ function ReportingContent() {
|
|
|
2144
2165
|
)}
|
|
2145
2166
|
</div>
|
|
2146
2167
|
) : null}
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
<h2 className="text-sm font-semibold uppercase tracking-wide text-zinc-400">
|
|
2156
|
-
{t.tocLocSection}
|
|
2157
|
-
</h2>
|
|
2158
|
-
<ReportingFilteredBadge
|
|
2159
|
-
active={reportingFiltersActive}
|
|
2160
|
-
label={t.sectionFilteredBadge}
|
|
2161
|
-
titleText={t.sectionFilteredBadgeTitle}
|
|
2168
|
+
<div className="rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4 sm:max-w-md">
|
|
2169
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
2170
|
+
<span className="text-xs uppercase text-zinc-500">
|
|
2171
|
+
{t.summaryTaskTimeRecorded}
|
|
2172
|
+
</span>
|
|
2173
|
+
<InlineMetricHelpTrigger
|
|
2174
|
+
ariaLabel={t.metricHelpTaskTimeAria}
|
|
2175
|
+
body={t.metricHelpTaskTimeBody}
|
|
2162
2176
|
/>
|
|
2163
2177
|
</div>
|
|
2164
|
-
<
|
|
2165
|
-
{
|
|
2166
|
-
</p>
|
|
2167
|
-
<div className="grid gap-3 sm:grid-cols-3">
|
|
2168
|
-
<div className="rounded-lg border border-zinc-800/80 bg-zinc-950/40 p-3">
|
|
2169
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2170
|
-
<span className="text-xs uppercase text-zinc-500">
|
|
2171
|
-
{t.summaryLinesWrittenTotal}
|
|
2172
|
-
</span>
|
|
2173
|
-
<InlineMetricHelpTrigger
|
|
2174
|
-
ariaLabel={t.metricHelpLinesTotalAria}
|
|
2175
|
-
body={t.metricHelpLinesTotalBody}
|
|
2176
|
-
/>
|
|
2177
|
-
</div>
|
|
2178
|
-
<div className="mt-1 text-xl font-semibold tabular-nums text-zinc-100">
|
|
2179
|
-
{agg.linesWrittenTotalSum}
|
|
2180
|
-
</div>
|
|
2181
|
-
</div>
|
|
2182
|
-
<div className="rounded-lg border border-zinc-800/80 bg-zinc-950/40 p-3">
|
|
2183
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2184
|
-
<span className="text-xs uppercase text-zinc-500">
|
|
2185
|
-
{t.summaryLinesWrittenHuman}
|
|
2186
|
-
</span>
|
|
2187
|
-
<InlineMetricHelpTrigger
|
|
2188
|
-
ariaLabel={t.metricHelpLinesHumanAria}
|
|
2189
|
-
body={t.metricHelpLinesHumanBody}
|
|
2190
|
-
/>
|
|
2191
|
-
</div>
|
|
2192
|
-
<div className="mt-1 text-xl font-semibold tabular-nums text-emerald-400/90">
|
|
2193
|
-
{agg.linesWrittenHumanSum}
|
|
2194
|
-
</div>
|
|
2195
|
-
</div>
|
|
2196
|
-
<div className="rounded-lg border border-zinc-800/80 bg-zinc-950/40 p-3">
|
|
2197
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2198
|
-
<span className="text-xs uppercase text-zinc-500">
|
|
2199
|
-
{t.summaryLinesWrittenAi}
|
|
2200
|
-
</span>
|
|
2201
|
-
<InlineMetricHelpTrigger
|
|
2202
|
-
align="end"
|
|
2203
|
-
ariaLabel={t.metricHelpLinesAiAria}
|
|
2204
|
-
body={t.metricHelpLinesAiBody}
|
|
2205
|
-
/>
|
|
2206
|
-
</div>
|
|
2207
|
-
<div className="mt-1 text-xl font-semibold tabular-nums text-violet-400/90">
|
|
2208
|
-
{agg.linesWrittenAiSum}
|
|
2209
|
-
</div>
|
|
2210
|
-
</div>
|
|
2178
|
+
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
2179
|
+
{formatDuration(agg.taskMinutesTotal)}
|
|
2211
2180
|
</div>
|
|
2212
|
-
|
|
2213
|
-
|
|
2181
|
+
</div>
|
|
2182
|
+
{hasReportingChartData ? (
|
|
2183
|
+
<>
|
|
2184
|
+
<section
|
|
2185
|
+
id="report-tag-time"
|
|
2186
|
+
className="mt-10 scroll-mt-28 space-y-8 rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4 sm:p-6"
|
|
2187
|
+
>
|
|
2214
2188
|
<div className="flex min-h-6 flex-wrap items-center gap-x-2 gap-y-1">
|
|
2215
|
-
<h3 className="text-sm font-semibold text-zinc-200">
|
|
2216
|
-
{t.
|
|
2189
|
+
<h3 className="text-sm font-semibold text-zinc-900 dark:text-zinc-200">
|
|
2190
|
+
{t.tagTimeSectionTitle}
|
|
2217
2191
|
</h3>
|
|
2218
2192
|
<ReportingFilteredBadge
|
|
2219
2193
|
active={reportingFiltersActive}
|
|
@@ -2221,679 +2195,166 @@ function ReportingContent() {
|
|
|
2221
2195
|
titleText={t.sectionFilteredBadgeTitle}
|
|
2222
2196
|
/>
|
|
2223
2197
|
<InlineMetricHelpTrigger
|
|
2224
|
-
ariaLabel={t.
|
|
2225
|
-
body={t.
|
|
2198
|
+
ariaLabel={t.metricHelpTagTimeSectionAria}
|
|
2199
|
+
body={t.metricHelpTagTimeSectionBody}
|
|
2226
2200
|
/>
|
|
2227
2201
|
</div>
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
<table className="mt-3 w-full min-w-[12rem] text-left text-sm">
|
|
2232
|
-
<thead className="border-b border-zinc-800 text-xs uppercase text-zinc-500">
|
|
2233
|
-
<tr>
|
|
2234
|
-
<th className="py-2 pr-3 font-medium">
|
|
2235
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2236
|
-
<span>{t.locByLanguageColLang}</span>
|
|
2237
|
-
<InlineMetricHelpTrigger
|
|
2238
|
-
ariaLabel={
|
|
2239
|
-
t.metricHelpAggLocColLangAria
|
|
2240
|
-
}
|
|
2241
|
-
body={t.metricHelpAggLocColLangBody}
|
|
2242
|
-
/>
|
|
2243
|
-
</div>
|
|
2244
|
-
</th>
|
|
2245
|
-
<th className="py-2 font-medium tabular-nums">
|
|
2246
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2247
|
-
<span>{t.locByLanguageColLines}</span>
|
|
2248
|
-
<InlineMetricHelpTrigger
|
|
2249
|
-
align="end"
|
|
2250
|
-
ariaLabel={
|
|
2251
|
-
t.metricHelpAggLocColLinesAria
|
|
2252
|
-
}
|
|
2253
|
-
body={t.metricHelpAggLocColLinesBody}
|
|
2254
|
-
/>
|
|
2255
|
-
</div>
|
|
2256
|
-
</th>
|
|
2257
|
-
</tr>
|
|
2258
|
-
</thead>
|
|
2259
|
-
<tbody>
|
|
2260
|
-
{agg.locByLanguageMerged.map(([lang, n]) => (
|
|
2261
|
-
<tr
|
|
2262
|
-
key={lang}
|
|
2263
|
-
className="border-b border-zinc-800/80 last:border-0"
|
|
2264
|
-
>
|
|
2265
|
-
<td className="py-2 pr-3 font-mono text-xs text-zinc-400">
|
|
2266
|
-
{lang}
|
|
2267
|
-
</td>
|
|
2268
|
-
<td className="py-2 tabular-nums text-zinc-200">
|
|
2269
|
-
{n}
|
|
2270
|
-
</td>
|
|
2271
|
-
</tr>
|
|
2272
|
-
))}
|
|
2273
|
-
</tbody>
|
|
2274
|
-
</table>
|
|
2275
|
-
)}
|
|
2276
|
-
</div>
|
|
2277
|
-
<div>
|
|
2278
|
-
<div className="flex min-h-6 flex-wrap items-center gap-x-2 gap-y-1">
|
|
2279
|
-
<h3 className="text-sm font-semibold text-zinc-200">
|
|
2280
|
-
{t.codingSignalsSectionTitle}
|
|
2281
|
-
</h3>
|
|
2282
|
-
<ReportingFilteredBadge
|
|
2283
|
-
active={reportingFiltersActive}
|
|
2284
|
-
label={t.sectionFilteredBadge}
|
|
2285
|
-
titleText={t.sectionFilteredBadgeTitle}
|
|
2286
|
-
/>
|
|
2287
|
-
<InlineMetricHelpTrigger
|
|
2288
|
-
ariaLabel={t.metricHelpCodingSignalsTitleAria}
|
|
2289
|
-
body={t.metricHelpCodingSignalsTitleBody}
|
|
2290
|
-
/>
|
|
2291
|
-
</div>
|
|
2292
|
-
{agg.codingSignalsByLanguageMerged.length === 0 ? (
|
|
2293
|
-
<p className="mt-3 text-sm text-zinc-500">—</p>
|
|
2294
|
-
) : (
|
|
2295
|
-
<table className="mt-3 w-full min-w-[12rem] text-left text-sm">
|
|
2296
|
-
<thead className="border-b border-zinc-800 text-xs uppercase text-zinc-500">
|
|
2297
|
-
<tr>
|
|
2298
|
-
<th className="py-2 pr-3 font-medium">
|
|
2299
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2300
|
-
<span>{t.codingSignalsColLang}</span>
|
|
2301
|
-
<InlineMetricHelpTrigger
|
|
2302
|
-
ariaLabel={
|
|
2303
|
-
t.metricHelpAggSigColLangAria
|
|
2304
|
-
}
|
|
2305
|
-
body={t.metricHelpAggSigColLangBody}
|
|
2306
|
-
/>
|
|
2307
|
-
</div>
|
|
2308
|
-
</th>
|
|
2309
|
-
<th className="py-2 font-medium tabular-nums">
|
|
2310
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2311
|
-
<span>{t.codingSignalsColCount}</span>
|
|
2312
|
-
<InlineMetricHelpTrigger
|
|
2313
|
-
align="end"
|
|
2314
|
-
ariaLabel={
|
|
2315
|
-
t.metricHelpAggSigColCountAria
|
|
2316
|
-
}
|
|
2317
|
-
body={t.metricHelpAggSigColCountBody}
|
|
2318
|
-
/>
|
|
2319
|
-
</div>
|
|
2320
|
-
</th>
|
|
2321
|
-
</tr>
|
|
2322
|
-
</thead>
|
|
2323
|
-
<tbody>
|
|
2324
|
-
{agg.codingSignalsByLanguageMerged.map(
|
|
2325
|
-
([lang, n]) => (
|
|
2326
|
-
<tr
|
|
2327
|
-
key={lang}
|
|
2328
|
-
className="border-b border-zinc-800/80 last:border-0"
|
|
2329
|
-
>
|
|
2330
|
-
<td className="py-2 pr-3 font-mono text-xs text-zinc-400">
|
|
2331
|
-
{lang}
|
|
2332
|
-
</td>
|
|
2333
|
-
<td className="py-2 tabular-nums text-zinc-200">
|
|
2334
|
-
{n}
|
|
2335
|
-
</td>
|
|
2336
|
-
</tr>
|
|
2337
|
-
),
|
|
2338
|
-
)}
|
|
2339
|
-
</tbody>
|
|
2340
|
-
</table>
|
|
2341
|
-
)}
|
|
2342
|
-
</div>
|
|
2343
|
-
</div>
|
|
2344
|
-
</section>
|
|
2345
|
-
) : null}
|
|
2346
|
-
|
|
2347
|
-
{!hasReportingChartData ? (
|
|
2348
|
-
<p className="text-sm text-zinc-500">{t.noRowsInRange}</p>
|
|
2349
|
-
) : (
|
|
2350
|
-
<>
|
|
2351
|
-
{navigableWeekStarts.length > 1 ? (
|
|
2352
|
-
<section
|
|
2353
|
-
id="report-week-nav"
|
|
2354
|
-
className="mb-8 scroll-mt-28 rounded-xl border border-zinc-800 bg-zinc-900/50 p-4 sm:p-5"
|
|
2355
|
-
aria-label={t.weekNavAriaLabel}
|
|
2356
|
-
>
|
|
2357
|
-
<div className="mb-2 flex flex-wrap items-center gap-2">
|
|
2358
|
-
<h2 className="text-sm font-semibold uppercase tracking-wide text-zinc-400">
|
|
2359
|
-
{t.tocWeekNav}
|
|
2360
|
-
</h2>
|
|
2361
|
-
<ReportingFilteredBadge
|
|
2362
|
-
active={reportingFiltersActive}
|
|
2363
|
-
label={t.sectionFilteredBadge}
|
|
2364
|
-
titleText={t.sectionFilteredBadgeTitle}
|
|
2365
|
-
/>
|
|
2366
|
-
</div>
|
|
2367
|
-
<div className="flex flex-col items-stretch gap-3 sm:flex-row sm:items-center sm:justify-between">
|
|
2368
|
-
<p className="max-w-xl text-xs text-zinc-500">
|
|
2369
|
-
{t.weekNavHelpHint}
|
|
2370
|
-
</p>
|
|
2371
|
-
<div className="flex flex-wrap items-center justify-center gap-2 sm:justify-end">
|
|
2372
|
-
<InlineMetricHelpTrigger
|
|
2373
|
-
ariaLabel={t.metricHelpWeekNavAria}
|
|
2374
|
-
body={t.metricHelpWeekNavBody}
|
|
2375
|
-
/>
|
|
2376
|
-
<button
|
|
2377
|
-
type="button"
|
|
2378
|
-
className="inline-flex items-center gap-1 rounded-lg border border-zinc-600 px-3 py-2 text-sm text-zinc-200 hover:bg-zinc-800 disabled:cursor-not-allowed disabled:opacity-40"
|
|
2379
|
-
disabled={chartWeekNavIndexSafe <= 0}
|
|
2380
|
-
aria-label={t.weekNavPrev}
|
|
2381
|
-
onClick={() =>
|
|
2382
|
-
setChartWeekNavIndex((i) => {
|
|
2383
|
-
const cur =
|
|
2384
|
-
i < 0
|
|
2385
|
-
? navigableWeekStarts.length - 1
|
|
2386
|
-
: i;
|
|
2387
|
-
return Math.max(0, cur - 1);
|
|
2388
|
-
})
|
|
2389
|
-
}
|
|
2390
|
-
>
|
|
2391
|
-
<ChevronLeft
|
|
2392
|
-
className="h-4 w-4 shrink-0"
|
|
2393
|
-
aria-hidden
|
|
2394
|
-
/>
|
|
2395
|
-
{t.weekNavPrev}
|
|
2396
|
-
</button>
|
|
2397
|
-
<span className="min-w-[10rem] px-2 text-center text-sm font-medium tabular-nums text-zinc-100">
|
|
2398
|
-
{formatWeekRangeLabel(
|
|
2399
|
-
navigableWeekStarts[chartWeekNavIndexSafe],
|
|
2400
|
-
reportLocale,
|
|
2401
|
-
)}
|
|
2402
|
-
</span>
|
|
2403
|
-
<button
|
|
2404
|
-
type="button"
|
|
2405
|
-
className="inline-flex items-center gap-1 rounded-lg border border-zinc-600 px-3 py-2 text-sm text-zinc-200 hover:bg-zinc-800 disabled:cursor-not-allowed disabled:opacity-40"
|
|
2406
|
-
disabled={
|
|
2407
|
-
chartWeekNavIndexSafe >=
|
|
2408
|
-
navigableWeekStarts.length - 1
|
|
2409
|
-
}
|
|
2410
|
-
aria-label={t.weekNavNext}
|
|
2411
|
-
onClick={() =>
|
|
2412
|
-
setChartWeekNavIndex((i) => {
|
|
2413
|
-
const cur =
|
|
2414
|
-
i < 0
|
|
2415
|
-
? navigableWeekStarts.length - 1
|
|
2416
|
-
: i;
|
|
2417
|
-
return Math.min(
|
|
2418
|
-
navigableWeekStarts.length - 1,
|
|
2419
|
-
cur + 1,
|
|
2420
|
-
);
|
|
2421
|
-
})
|
|
2422
|
-
}
|
|
2423
|
-
>
|
|
2424
|
-
{t.weekNavNext}
|
|
2425
|
-
<ChevronRight
|
|
2426
|
-
className="h-4 w-4 shrink-0"
|
|
2427
|
-
aria-hidden
|
|
2428
|
-
/>
|
|
2429
|
-
</button>
|
|
2430
|
-
</div>
|
|
2431
|
-
</div>
|
|
2432
|
-
</section>
|
|
2433
|
-
) : null}
|
|
2434
|
-
<div className="mb-10 grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
2435
|
-
<section
|
|
2436
|
-
id="report-chart-sessions"
|
|
2437
|
-
className="scroll-mt-28 rounded-xl border border-zinc-800 bg-zinc-900/50 p-4 sm:p-6"
|
|
2438
|
-
>
|
|
2439
|
-
<div className="flex min-h-6 flex-wrap items-center gap-x-2 gap-y-1">
|
|
2440
|
-
<h3 className="text-sm font-semibold text-zinc-200">
|
|
2441
|
-
{t.chartSessionsPerDay}
|
|
2442
|
-
</h3>
|
|
2443
|
-
<ReportingFilteredBadge
|
|
2444
|
-
active={reportingFiltersActive}
|
|
2445
|
-
label={t.sectionFilteredBadge}
|
|
2446
|
-
titleText={t.sectionFilteredBadgeTitle}
|
|
2447
|
-
/>
|
|
2448
|
-
<InlineMetricHelpTrigger
|
|
2449
|
-
ariaLabel={t.metricHelpChartSessionsAria}
|
|
2450
|
-
body={t.metricHelpChartSessionsBody}
|
|
2451
|
-
/>
|
|
2452
|
-
</div>
|
|
2453
|
-
<div className="mt-1 flex flex-wrap items-center gap-1 text-xs text-zinc-500">
|
|
2454
|
-
<span>{t.legendSessions}</span>
|
|
2455
|
-
<InlineMetricHelpTrigger
|
|
2456
|
-
ariaLabel={t.metricHelpLegendSessionsAria}
|
|
2457
|
-
body={t.metricHelpLegendSessionsBody}
|
|
2458
|
-
/>
|
|
2459
|
-
</div>
|
|
2460
|
-
<div className="mt-4">
|
|
2461
|
-
<MiniBars
|
|
2462
|
-
days={reportingDayKeys}
|
|
2463
|
-
values={agg.sessionsByDay}
|
|
2464
|
-
max={peak}
|
|
2465
|
-
className="bg-violet-500/90"
|
|
2466
|
-
undatedLabel={t.undatedLabel}
|
|
2467
|
-
/>
|
|
2468
|
-
</div>
|
|
2469
|
-
</section>
|
|
2202
|
+
<p className="text-xs text-zinc-500">
|
|
2203
|
+
{t.tagTimeSectionHint}
|
|
2204
|
+
</p>
|
|
2470
2205
|
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
<h3 className="text-sm font-semibold text-zinc-200">
|
|
2477
|
-
{t.chartTasksByStatusPerDay}
|
|
2478
|
-
</h3>
|
|
2479
|
-
<ReportingFilteredBadge
|
|
2480
|
-
active={reportingFiltersActive}
|
|
2481
|
-
label={t.sectionFilteredBadge}
|
|
2482
|
-
titleText={t.sectionFilteredBadgeTitle}
|
|
2483
|
-
/>
|
|
2484
|
-
<InlineMetricHelpTrigger
|
|
2485
|
-
ariaLabel={t.metricHelpChartTasksAria}
|
|
2486
|
-
body={t.metricHelpChartTasksBody}
|
|
2487
|
-
/>
|
|
2488
|
-
</div>
|
|
2489
|
-
<div className="mt-2 flex flex-wrap items-center gap-x-4 gap-y-2 text-xs text-zinc-500">
|
|
2490
|
-
<div className="inline-flex items-center gap-1">
|
|
2491
|
-
<span className="mr-0.5 inline-block h-2 w-2 shrink-0 rounded-sm bg-emerald-600" />
|
|
2492
|
-
{t.legendDone}
|
|
2206
|
+
<div id="report-tag-time-week" className="space-y-4">
|
|
2207
|
+
<div className="flex min-h-6 flex-wrap items-center gap-x-2 gap-y-1">
|
|
2208
|
+
<h4 className="text-xs font-semibold uppercase tracking-wide text-zinc-600 dark:text-zinc-400">
|
|
2209
|
+
{t.tagTimeByWeekTitle}
|
|
2210
|
+
</h4>
|
|
2493
2211
|
<InlineMetricHelpTrigger
|
|
2494
|
-
ariaLabel={t.
|
|
2495
|
-
body={t.
|
|
2212
|
+
ariaLabel={t.metricHelpTagTimeWeekTableAria}
|
|
2213
|
+
body={t.metricHelpTagTimeWeekTableBody}
|
|
2496
2214
|
/>
|
|
2497
|
-
</div>
|
|
2498
|
-
<div className="inline-flex items-center gap-1">
|
|
2499
|
-
<span className="mr-0.5 inline-block h-2 w-2 shrink-0 rounded-sm bg-amber-500" />
|
|
2500
|
-
{t.legendActive}
|
|
2501
2215
|
<InlineMetricHelpTrigger
|
|
2502
|
-
ariaLabel={t.
|
|
2503
|
-
body={t.
|
|
2216
|
+
ariaLabel={t.metricHelpTagWeekCalendarAria}
|
|
2217
|
+
body={t.metricHelpTagWeekCalendarBody}
|
|
2504
2218
|
/>
|
|
2505
2219
|
</div>
|
|
2506
|
-
</div>
|
|
2507
|
-
<div className="mt-4">
|
|
2508
|
-
<StackedTaskBars
|
|
2509
|
-
days={reportingDayKeys}
|
|
2510
|
-
done={agg.tasksByDayDone}
|
|
2511
|
-
active={agg.tasksByDayActive}
|
|
2512
|
-
max={peakTasks}
|
|
2513
|
-
undatedLabel={t.undatedLabel}
|
|
2514
|
-
/>
|
|
2515
|
-
</div>
|
|
2516
|
-
</section>
|
|
2517
|
-
|
|
2518
|
-
<section
|
|
2519
|
-
id="report-chart-task-time"
|
|
2520
|
-
className="scroll-mt-28 rounded-xl border border-zinc-800 bg-zinc-900/50 p-4 sm:p-6"
|
|
2521
|
-
>
|
|
2522
|
-
<div className="flex min-h-6 flex-wrap items-center gap-x-2 gap-y-1">
|
|
2523
|
-
<h3 className="text-sm font-semibold text-zinc-200">
|
|
2524
|
-
{t.chartTaskTimePerDay}
|
|
2525
|
-
</h3>
|
|
2526
|
-
<ReportingFilteredBadge
|
|
2527
|
-
active={reportingFiltersActive}
|
|
2528
|
-
label={t.sectionFilteredBadge}
|
|
2529
|
-
titleText={t.sectionFilteredBadgeTitle}
|
|
2530
|
-
/>
|
|
2531
|
-
<InlineMetricHelpTrigger
|
|
2532
|
-
ariaLabel={t.metricHelpChartTaskTimeAria}
|
|
2533
|
-
body={t.metricHelpChartTaskTimeBody}
|
|
2534
|
-
/>
|
|
2535
|
-
</div>
|
|
2536
|
-
<div className="mt-1 flex flex-wrap items-center gap-1 text-xs text-zinc-500">
|
|
2537
|
-
<span>{t.summaryTaskTimeRecorded}</span>
|
|
2538
|
-
<InlineMetricHelpTrigger
|
|
2539
|
-
ariaLabel={t.metricHelpTaskTimeAria}
|
|
2540
|
-
body={t.metricHelpTaskTimeBody}
|
|
2541
|
-
/>
|
|
2542
|
-
</div>
|
|
2543
|
-
<div className="mt-4">
|
|
2544
|
-
<MiniBars
|
|
2545
|
-
days={reportingDayKeys}
|
|
2546
|
-
values={agg.taskMinutesByDay}
|
|
2547
|
-
max={peakTaskMinutes}
|
|
2548
|
-
className="bg-sky-500/90"
|
|
2549
|
-
undatedLabel={t.undatedLabel}
|
|
2550
|
-
valueTitle={(m) => formatDuration(m)}
|
|
2551
|
-
/>
|
|
2552
|
-
</div>
|
|
2553
|
-
</section>
|
|
2554
|
-
|
|
2555
|
-
<section
|
|
2556
|
-
id="report-chart-session-wall"
|
|
2557
|
-
className="scroll-mt-28 rounded-xl border border-zinc-800 bg-zinc-900/50 p-4 sm:p-6"
|
|
2558
|
-
>
|
|
2559
|
-
<div className="flex min-h-6 flex-wrap items-center gap-x-2 gap-y-1">
|
|
2560
|
-
<h3 className="text-sm font-semibold text-zinc-200">
|
|
2561
|
-
{t.chartSessionWallPerDay}
|
|
2562
|
-
</h3>
|
|
2563
|
-
<ReportingFilteredBadge
|
|
2564
|
-
active={reportingFiltersActive}
|
|
2565
|
-
label={t.sectionFilteredBadge}
|
|
2566
|
-
titleText={t.sectionFilteredBadgeTitle}
|
|
2567
|
-
/>
|
|
2568
|
-
<InlineMetricHelpTrigger
|
|
2569
|
-
ariaLabel={t.metricHelpChartSessionWallAria}
|
|
2570
|
-
body={t.metricHelpChartSessionWallBody}
|
|
2571
|
-
/>
|
|
2572
|
-
</div>
|
|
2573
|
-
<div className="mt-1 flex flex-wrap items-center gap-1 text-xs text-zinc-500">
|
|
2574
|
-
<span>{t.summarySessionWallClock}</span>
|
|
2575
|
-
<InlineMetricHelpTrigger
|
|
2576
|
-
ariaLabel={t.metricHelpSessionWallSummaryAria}
|
|
2577
|
-
body={t.metricHelpSessionWallSummaryBody}
|
|
2578
|
-
/>
|
|
2579
|
-
</div>
|
|
2580
|
-
<div className="mt-4">
|
|
2581
|
-
<MiniBars
|
|
2582
|
-
days={reportingDayKeys}
|
|
2583
|
-
values={agg.sessionWallClockMinutesByDay}
|
|
2584
|
-
max={peakSessionWallMinutes}
|
|
2585
|
-
className="bg-fuchsia-500/85"
|
|
2586
|
-
undatedLabel={t.undatedLabel}
|
|
2587
|
-
valueTitle={(m) => formatDuration(m)}
|
|
2588
|
-
/>
|
|
2589
|
-
</div>
|
|
2590
|
-
</section>
|
|
2591
|
-
</div>
|
|
2592
2220
|
|
|
2593
|
-
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
<div className="flex flex-wrap items-center gap-2 border-b border-zinc-800 px-4 py-3">
|
|
2598
|
-
<h3 className="text-sm font-semibold text-zinc-200">
|
|
2599
|
-
{t.tocDailyTable}
|
|
2600
|
-
</h3>
|
|
2601
|
-
<ReportingFilteredBadge
|
|
2602
|
-
active={reportingFiltersActive}
|
|
2603
|
-
label={t.sectionFilteredBadge}
|
|
2604
|
-
titleText={t.sectionFilteredBadgeTitle}
|
|
2605
|
-
/>
|
|
2606
|
-
</div>
|
|
2607
|
-
<table className="w-full min-w-[48rem] text-left text-sm">
|
|
2608
|
-
<thead className="border-b border-zinc-800 text-xs uppercase text-zinc-500">
|
|
2609
|
-
<tr>
|
|
2610
|
-
<th className="px-4 py-3 font-medium">
|
|
2611
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2612
|
-
<span>{t.tableDay}</span>
|
|
2613
|
-
<InlineMetricHelpTrigger
|
|
2614
|
-
ariaLabel={t.metricHelpTblDayAria}
|
|
2615
|
-
body={t.metricHelpTblDayBody}
|
|
2616
|
-
/>
|
|
2617
|
-
</div>
|
|
2618
|
-
</th>
|
|
2619
|
-
<th className="px-4 py-3 font-medium tabular-nums">
|
|
2620
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2621
|
-
<span>{t.tableSessions}</span>
|
|
2622
|
-
<InlineMetricHelpTrigger
|
|
2623
|
-
ariaLabel={t.metricHelpTblSessionsAria}
|
|
2624
|
-
body={t.metricHelpTblSessionsBody}
|
|
2625
|
-
/>
|
|
2626
|
-
</div>
|
|
2627
|
-
</th>
|
|
2628
|
-
<th className="px-4 py-3 font-medium tabular-nums">
|
|
2629
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2630
|
-
<span>{t.tableSessionWall}</span>
|
|
2221
|
+
<fieldset className="rounded-lg border border-zinc-200/90 bg-zinc-50/90 dark:border-zinc-800/90 dark:bg-zinc-950/30 p-3">
|
|
2222
|
+
<legend className="px-1 text-xs text-zinc-500">
|
|
2223
|
+
<span className="inline-flex flex-wrap items-center gap-1">
|
|
2224
|
+
{t.tagWeekStartsOnLegend}
|
|
2631
2225
|
<InlineMetricHelpTrigger
|
|
2632
|
-
ariaLabel={t.
|
|
2633
|
-
body={t.
|
|
2634
|
-
/>
|
|
2635
|
-
</div>
|
|
2636
|
-
</th>
|
|
2637
|
-
<th className="px-4 py-3 font-medium tabular-nums">
|
|
2638
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2639
|
-
<span>{t.tableDone}</span>
|
|
2640
|
-
<InlineMetricHelpTrigger
|
|
2641
|
-
ariaLabel={t.metricHelpTblDoneAria}
|
|
2642
|
-
body={t.metricHelpTblDoneBody}
|
|
2643
|
-
/>
|
|
2644
|
-
</div>
|
|
2645
|
-
</th>
|
|
2646
|
-
<th className="px-4 py-3 font-medium tabular-nums">
|
|
2647
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2648
|
-
<span>{t.tableActive}</span>
|
|
2649
|
-
<InlineMetricHelpTrigger
|
|
2650
|
-
align="end"
|
|
2651
|
-
ariaLabel={t.metricHelpTblActiveAria}
|
|
2652
|
-
body={t.metricHelpTblActiveBody}
|
|
2653
|
-
/>
|
|
2654
|
-
</div>
|
|
2655
|
-
</th>
|
|
2656
|
-
<th className="px-4 py-3 font-medium tabular-nums">
|
|
2657
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2658
|
-
<span>{t.tableTaskTime}</span>
|
|
2659
|
-
<InlineMetricHelpTrigger
|
|
2660
|
-
ariaLabel={t.metricHelpTblTaskTimeAria}
|
|
2661
|
-
body={t.metricHelpTblTaskTimeBody}
|
|
2662
|
-
/>
|
|
2663
|
-
</div>
|
|
2664
|
-
</th>
|
|
2665
|
-
<th className="px-4 py-3 font-medium tabular-nums">
|
|
2666
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2667
|
-
<span>{t.tableTaskTimeNonConcurrent}</span>
|
|
2668
|
-
</div>
|
|
2669
|
-
</th>
|
|
2670
|
-
<th className="px-4 py-3 font-medium tabular-nums">
|
|
2671
|
-
<div className="flex min-h-5 items-center gap-0.5">
|
|
2672
|
-
<span>{t.tableSessionCoding}</span>
|
|
2673
|
-
<InlineMetricHelpTrigger
|
|
2674
|
-
align="end"
|
|
2675
|
-
ariaLabel={t.metricHelpTblSessionCodingAria}
|
|
2676
|
-
body={t.metricHelpTblSessionCodingBody}
|
|
2226
|
+
ariaLabel={t.metricHelpTagWeekStartsOnAria}
|
|
2227
|
+
body={t.metricHelpTagWeekStartsOnBody}
|
|
2677
2228
|
/>
|
|
2678
|
-
</
|
|
2679
|
-
</
|
|
2680
|
-
|
|
2681
|
-
|
|
2682
|
-
|
|
2683
|
-
|
|
2684
|
-
<tr
|
|
2685
|
-
key={d}
|
|
2686
|
-
className="border-b border-zinc-800/80 last:border-0"
|
|
2229
|
+
</span>
|
|
2230
|
+
</legend>
|
|
2231
|
+
<div
|
|
2232
|
+
className="mt-2 flex flex-wrap gap-2"
|
|
2233
|
+
role="radiogroup"
|
|
2234
|
+
aria-label={t.tagWeekStartsOnLegend}
|
|
2687
2235
|
>
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
</td>
|
|
2713
|
-
<td className="px-4 py-2.5 tabular-nums text-zinc-300">
|
|
2714
|
-
{formatMinutesCell(
|
|
2715
|
-
agg.sessionCodingMinutesByDay[d],
|
|
2716
|
-
)}
|
|
2717
|
-
</td>
|
|
2718
|
-
</tr>
|
|
2719
|
-
))}
|
|
2720
|
-
</tbody>
|
|
2721
|
-
</table>
|
|
2722
|
-
</section>
|
|
2723
|
-
|
|
2724
|
-
<section
|
|
2725
|
-
id="report-tag-time"
|
|
2726
|
-
className="mt-10 scroll-mt-28 space-y-8 rounded-xl border border-zinc-800 bg-zinc-900/50 p-4 sm:p-6"
|
|
2727
|
-
>
|
|
2728
|
-
<div className="flex min-h-6 flex-wrap items-center gap-x-2 gap-y-1">
|
|
2729
|
-
<h3 className="text-sm font-semibold text-zinc-200">
|
|
2730
|
-
{t.tagTimeSectionTitle}
|
|
2731
|
-
</h3>
|
|
2732
|
-
<ReportingFilteredBadge
|
|
2733
|
-
active={reportingFiltersActive}
|
|
2734
|
-
label={t.sectionFilteredBadge}
|
|
2735
|
-
titleText={t.sectionFilteredBadgeTitle}
|
|
2736
|
-
/>
|
|
2737
|
-
<InlineMetricHelpTrigger
|
|
2738
|
-
ariaLabel={t.metricHelpTagTimeSectionAria}
|
|
2739
|
-
body={t.metricHelpTagTimeSectionBody}
|
|
2740
|
-
/>
|
|
2741
|
-
</div>
|
|
2742
|
-
<p className="text-xs text-zinc-500">
|
|
2743
|
-
{t.tagTimeSectionHint}
|
|
2744
|
-
</p>
|
|
2745
|
-
|
|
2746
|
-
<div id="report-tag-time-week" className="space-y-4">
|
|
2747
|
-
<div className="flex min-h-6 flex-wrap items-center gap-x-2 gap-y-1">
|
|
2748
|
-
<h4 className="text-xs font-semibold uppercase tracking-wide text-zinc-400">
|
|
2749
|
-
{t.tagTimeByWeekTitle}
|
|
2750
|
-
</h4>
|
|
2751
|
-
<InlineMetricHelpTrigger
|
|
2752
|
-
ariaLabel={t.metricHelpTagTimeWeekTableAria}
|
|
2753
|
-
body={t.metricHelpTagTimeWeekTableBody}
|
|
2754
|
-
/>
|
|
2755
|
-
<InlineMetricHelpTrigger
|
|
2756
|
-
ariaLabel={t.metricHelpTagWeekCalendarAria}
|
|
2757
|
-
body={t.metricHelpTagWeekCalendarBody}
|
|
2758
|
-
/>
|
|
2759
|
-
</div>
|
|
2760
|
-
|
|
2761
|
-
<fieldset className="rounded-lg border border-zinc-800/90 bg-zinc-950/30 p-3">
|
|
2762
|
-
<legend className="px-1 text-xs text-zinc-500">
|
|
2763
|
-
<span className="inline-flex flex-wrap items-center gap-1">
|
|
2764
|
-
{t.tagWeekStartsOnLegend}
|
|
2765
|
-
<InlineMetricHelpTrigger
|
|
2766
|
-
ariaLabel={t.metricHelpTagWeekStartsOnAria}
|
|
2767
|
-
body={t.metricHelpTagWeekStartsOnBody}
|
|
2768
|
-
/>
|
|
2769
|
-
</span>
|
|
2770
|
-
</legend>
|
|
2771
|
-
<div
|
|
2772
|
-
className="mt-2 flex flex-wrap gap-2"
|
|
2773
|
-
role="radiogroup"
|
|
2774
|
-
aria-label={t.tagWeekStartsOnLegend}
|
|
2775
|
-
>
|
|
2776
|
-
{(
|
|
2777
|
-
[
|
|
2778
|
-
["monday", t.weekStartsMonday],
|
|
2779
|
-
["sunday", t.weekStartsSunday],
|
|
2780
|
-
["saturday", t.weekStartsSaturday],
|
|
2781
|
-
] as const
|
|
2782
|
-
).map(([value, label]) => (
|
|
2783
|
-
<button
|
|
2784
|
-
key={value}
|
|
2785
|
-
type="button"
|
|
2786
|
-
role="radio"
|
|
2787
|
-
aria-checked={weekStartsOn === value}
|
|
2788
|
-
onClick={() => persistWeekStartsOn(value)}
|
|
2789
|
-
className={`rounded-md border px-3 py-1.5 text-xs font-medium transition-colors ${
|
|
2790
|
-
weekStartsOn === value
|
|
2791
|
-
? "border-violet-500/80 bg-violet-950/50 text-violet-200"
|
|
2792
|
-
: "border-zinc-700 bg-zinc-900/60 text-zinc-400 hover:border-zinc-600 hover:text-zinc-200"
|
|
2793
|
-
}`}
|
|
2794
|
-
>
|
|
2795
|
-
{label}
|
|
2796
|
-
</button>
|
|
2797
|
-
))}
|
|
2798
|
-
</div>
|
|
2799
|
-
</fieldset>
|
|
2236
|
+
{(
|
|
2237
|
+
[
|
|
2238
|
+
["monday", t.weekStartsMonday],
|
|
2239
|
+
["sunday", t.weekStartsSunday],
|
|
2240
|
+
["saturday", t.weekStartsSaturday],
|
|
2241
|
+
] as const
|
|
2242
|
+
).map(([value, label]) => (
|
|
2243
|
+
<button
|
|
2244
|
+
key={value}
|
|
2245
|
+
type="button"
|
|
2246
|
+
role="radio"
|
|
2247
|
+
aria-checked={weekStartsOn === value}
|
|
2248
|
+
onClick={() => persistWeekStartsOn(value)}
|
|
2249
|
+
className={`rounded-md border px-3 py-1.5 text-xs font-medium transition-colors ${
|
|
2250
|
+
weekStartsOn === value
|
|
2251
|
+
? "border-violet-500/80 bg-violet-100 text-violet-900 dark:bg-violet-950/50 dark:text-violet-200"
|
|
2252
|
+
: "border-zinc-300 bg-white text-zinc-600 hover:border-zinc-400 hover:bg-zinc-50 hover:text-zinc-900 dark:border-zinc-700 dark:bg-zinc-900/60 dark:text-zinc-400 dark:hover:border-zinc-600 dark:hover:text-zinc-200"
|
|
2253
|
+
}`}
|
|
2254
|
+
>
|
|
2255
|
+
{label}
|
|
2256
|
+
</button>
|
|
2257
|
+
))}
|
|
2258
|
+
</div>
|
|
2259
|
+
</fieldset>
|
|
2800
2260
|
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2805
|
-
|
|
2806
|
-
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2823
|
-
|
|
2824
|
-
|
|
2825
|
-
|
|
2826
|
-
|
|
2827
|
-
|
|
2828
|
-
|
|
2829
|
-
|
|
2830
|
-
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2842
|
-
|
|
2843
|
-
|
|
2844
|
-
|
|
2845
|
-
|
|
2846
|
-
|
|
2847
|
-
|
|
2848
|
-
|
|
2849
|
-
|
|
2850
|
-
|
|
2851
|
-
|
|
2852
|
-
|
|
2853
|
-
|
|
2854
|
-
|
|
2855
|
-
|
|
2856
|
-
|
|
2857
|
-
|
|
2858
|
-
|
|
2859
|
-
|
|
2860
|
-
|
|
2861
|
-
|
|
2862
|
-
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2261
|
+
{tagWeekCalendarRows.length === 0 ? (
|
|
2262
|
+
<p className="text-sm text-zinc-500">—</p>
|
|
2263
|
+
) : tagWeekCalendarRowsVisible.length === 0 ? (
|
|
2264
|
+
<div className="space-y-2 text-sm text-zinc-500">
|
|
2265
|
+
<p>{t.weekNavNoTagDataThisWeek}</p>
|
|
2266
|
+
{archivedExcludedTaskMinutes > 1e-9 ? (
|
|
2267
|
+
<p className="text-amber-800 dark:text-amber-200/90">
|
|
2268
|
+
{reportingArchivedExcludedRichText(
|
|
2269
|
+
t.reportingArchivedExcludedAside,
|
|
2270
|
+
archivedExcludedTaskMinutes,
|
|
2271
|
+
)}
|
|
2272
|
+
</p>
|
|
2273
|
+
) : null}
|
|
2274
|
+
</div>
|
|
2275
|
+
) : (
|
|
2276
|
+
<div className="space-y-8">
|
|
2277
|
+
{tagCalendarWeekGroups.map(
|
|
2278
|
+
({ weekStart, rows }) => (
|
|
2279
|
+
<div key={weekStart}>
|
|
2280
|
+
<div className="mb-2 flex flex-wrap items-center gap-2">
|
|
2281
|
+
<p className="text-sm font-medium text-zinc-800 dark:text-zinc-300">
|
|
2282
|
+
{formatWeekRangeLabel(
|
|
2283
|
+
weekStart,
|
|
2284
|
+
reportLocale,
|
|
2285
|
+
)}
|
|
2286
|
+
</p>
|
|
2287
|
+
<InlineMetricHelpTrigger
|
|
2288
|
+
ariaLabel={
|
|
2289
|
+
t.metricHelpTagTimeColWeekAria
|
|
2290
|
+
}
|
|
2291
|
+
body={t.metricHelpTagTimeColWeekBody}
|
|
2292
|
+
/>
|
|
2293
|
+
</div>
|
|
2294
|
+
<div className="overflow-x-auto rounded-lg border border-zinc-200/80 dark:border-zinc-800/80">
|
|
2295
|
+
<table className="w-full min-w-[28rem] text-left text-sm">
|
|
2296
|
+
<thead className="border-b border-zinc-200 bg-zinc-100/80 dark:border-zinc-800 dark:bg-zinc-950/40 text-xs uppercase text-zinc-500">
|
|
2297
|
+
<tr>
|
|
2298
|
+
<th className="px-2 py-2 pl-3 font-medium">
|
|
2299
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
2300
|
+
<span>{t.tagTimeColTag}</span>
|
|
2301
|
+
<InlineMetricHelpTrigger
|
|
2302
|
+
ariaLabel={
|
|
2303
|
+
t.metricHelpTagTimeColTagAria
|
|
2304
|
+
}
|
|
2305
|
+
body={
|
|
2306
|
+
t.metricHelpTagTimeColTagBody
|
|
2307
|
+
}
|
|
2308
|
+
/>
|
|
2309
|
+
</div>
|
|
2310
|
+
</th>
|
|
2311
|
+
{weekdayDateColumnHeaders(
|
|
2312
|
+
weekStart,
|
|
2313
|
+
reportLocale,
|
|
2314
|
+
).map((col) => (
|
|
2315
|
+
<th
|
|
2316
|
+
key={col.dateKey}
|
|
2317
|
+
className="px-1 py-2 text-center font-medium normal-case"
|
|
2318
|
+
title={col.dateKey}
|
|
2319
|
+
>
|
|
2320
|
+
<div className="flex flex-col items-center gap-0.5 leading-tight">
|
|
2321
|
+
<span className="text-[0.65rem] font-semibold uppercase tracking-wide text-zinc-500">
|
|
2322
|
+
{col.weekdayShort}
|
|
2323
|
+
</span>
|
|
2324
|
+
<span className="text-[0.7rem] font-medium tabular-nums text-zinc-700 dark:text-zinc-300">
|
|
2325
|
+
{col.calendarDateShort}
|
|
2326
|
+
</span>
|
|
2327
|
+
</div>
|
|
2328
|
+
</th>
|
|
2329
|
+
))}
|
|
2330
|
+
<th className="px-2 py-2 pr-3 text-center font-medium">
|
|
2331
|
+
<div className="flex min-h-5 items-center justify-center gap-0.5">
|
|
2332
|
+
<span>
|
|
2333
|
+
{t.tagWeekSumColumn}
|
|
2866
2334
|
</span>
|
|
2335
|
+
<InlineMetricHelpTrigger
|
|
2336
|
+
align="end"
|
|
2337
|
+
ariaLabel={
|
|
2338
|
+
t.metricHelpTagTimeColMinutesAria
|
|
2339
|
+
}
|
|
2340
|
+
body={
|
|
2341
|
+
t.metricHelpTagTimeColMinutesBody
|
|
2342
|
+
}
|
|
2343
|
+
/>
|
|
2867
2344
|
</div>
|
|
2868
2345
|
</th>
|
|
2869
|
-
|
|
2870
|
-
|
|
2871
|
-
|
|
2872
|
-
|
|
2873
|
-
|
|
2874
|
-
|
|
2875
|
-
<InlineMetricHelpTrigger
|
|
2876
|
-
align="end"
|
|
2877
|
-
ariaLabel={
|
|
2878
|
-
t.metricHelpTagTimeColMinutesAria
|
|
2879
|
-
}
|
|
2880
|
-
body={
|
|
2881
|
-
t.metricHelpTagTimeColMinutesBody
|
|
2882
|
-
}
|
|
2883
|
-
/>
|
|
2884
|
-
</div>
|
|
2885
|
-
</th>
|
|
2886
|
-
</tr>
|
|
2887
|
-
</thead>
|
|
2888
|
-
<tbody>
|
|
2889
|
-
{buildTagWeekDisplayBlocks(rows).map(
|
|
2890
|
-
(block) => {
|
|
2346
|
+
</tr>
|
|
2347
|
+
</thead>
|
|
2348
|
+
<tbody>
|
|
2349
|
+
{buildTagWeekDisplayBlocks(
|
|
2350
|
+
rows,
|
|
2351
|
+
).map((block) => {
|
|
2891
2352
|
if (block.kind === "leaf") {
|
|
2892
2353
|
const row = block.row;
|
|
2893
2354
|
return (
|
|
2894
2355
|
<tr
|
|
2895
2356
|
key={`${weekStart}\0${row.tagKey}`}
|
|
2896
|
-
className="border-b border-zinc-
|
|
2357
|
+
className="border-b border-zinc-200/90 last:border-0 dark:border-zinc-800/70"
|
|
2897
2358
|
>
|
|
2898
2359
|
<ReportingTagNameCell
|
|
2899
2360
|
tagKey={row.tagKey}
|
|
@@ -2907,7 +2368,7 @@ function ReportingContent() {
|
|
|
2907
2368
|
descriptions={
|
|
2908
2369
|
tagDescriptions
|
|
2909
2370
|
}
|
|
2910
|
-
className="px-2 py-2 pl-3 text-violet-200/90"
|
|
2371
|
+
className="px-2 py-2 pl-3 text-violet-800 dark:text-violet-200/90"
|
|
2911
2372
|
/>
|
|
2912
2373
|
{row.slots.map(
|
|
2913
2374
|
(mins, i) => {
|
|
@@ -2919,7 +2380,7 @@ function ReportingContent() {
|
|
|
2919
2380
|
return (
|
|
2920
2381
|
<td
|
|
2921
2382
|
key={dateKey}
|
|
2922
|
-
className="px-1 py-2 text-center tabular-nums text-zinc-200"
|
|
2383
|
+
className="px-1 py-2 text-center tabular-nums text-zinc-800 dark:text-zinc-200"
|
|
2923
2384
|
title={dateKey}
|
|
2924
2385
|
>
|
|
2925
2386
|
{mins > 0
|
|
@@ -2940,14 +2401,14 @@ function ReportingContent() {
|
|
|
2940
2401
|
t.undatedLabel,
|
|
2941
2402
|
),
|
|
2942
2403
|
},
|
|
2943
|
-
"text-zinc-200",
|
|
2404
|
+
"text-zinc-800 dark:text-zinc-200",
|
|
2944
2405
|
)
|
|
2945
2406
|
: "—"}
|
|
2946
2407
|
</td>
|
|
2947
2408
|
);
|
|
2948
2409
|
},
|
|
2949
2410
|
)}
|
|
2950
|
-
<td className="px-2 py-2 pr-3 text-center tabular-nums font-semibold text-zinc-100">
|
|
2411
|
+
<td className="px-2 py-2 pr-3 text-center tabular-nums font-semibold text-zinc-950 dark:text-zinc-100">
|
|
2951
2412
|
{renderReportingDurationButton(
|
|
2952
2413
|
row.total,
|
|
2953
2414
|
{
|
|
@@ -2965,13 +2426,13 @@ function ReportingContent() {
|
|
|
2965
2426
|
reportLocale,
|
|
2966
2427
|
),
|
|
2967
2428
|
},
|
|
2968
|
-
"text-zinc-100",
|
|
2429
|
+
"text-zinc-950 dark:text-zinc-100",
|
|
2969
2430
|
)}
|
|
2970
2431
|
</td>
|
|
2971
2432
|
</tr>
|
|
2972
2433
|
);
|
|
2973
2434
|
}
|
|
2974
|
-
const rollupKey = `${weekStart}:::${block.
|
|
2435
|
+
const rollupKey = `${weekStart}:::${block.rollupStableKey}`;
|
|
2975
2436
|
const open =
|
|
2976
2437
|
tagWeekRollupOpenKeys.has(
|
|
2977
2438
|
rollupKey,
|
|
@@ -2983,8 +2444,8 @@ function ReportingContent() {
|
|
|
2983
2444
|
) ?? "";
|
|
2984
2445
|
return (
|
|
2985
2446
|
<Fragment key={rollupKey}>
|
|
2986
|
-
<tr className="border-b border-zinc-800/70 bg-zinc-900/40">
|
|
2987
|
-
<td className="px-2 py-2 pl-3 align-top text-violet-200/90">
|
|
2447
|
+
<tr className="border-b border-zinc-200/90 bg-zinc-100/70 dark:border-zinc-800/70 dark:bg-zinc-900/40">
|
|
2448
|
+
<td className="px-2 py-2 pl-3 align-top text-violet-800 dark:text-violet-200/90">
|
|
2988
2449
|
<div className="flex items-start gap-1.5">
|
|
2989
2450
|
<button
|
|
2990
2451
|
type="button"
|
|
@@ -2996,7 +2457,7 @@ function ReportingContent() {
|
|
|
2996
2457
|
aria-label={
|
|
2997
2458
|
t.tagWeekScopedRollupToggleAria
|
|
2998
2459
|
}
|
|
2999
|
-
className="mt-0.5 shrink-0 rounded p-0.5 text-zinc-
|
|
2460
|
+
className="mt-0.5 shrink-0 rounded p-0.5 text-zinc-500 transition hover:bg-zinc-200 hover:text-zinc-900 dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-zinc-200"
|
|
3000
2461
|
onClick={() =>
|
|
3001
2462
|
toggleTagWeekRollup(
|
|
3002
2463
|
rollupKey,
|
|
@@ -3016,7 +2477,6 @@ function ReportingContent() {
|
|
|
3016
2477
|
<div className="min-w-0">
|
|
3017
2478
|
<div className="flex flex-wrap items-baseline gap-x-1.5 font-medium">
|
|
3018
2479
|
<span>
|
|
3019
|
-
@
|
|
3020
2480
|
{
|
|
3021
2481
|
block.displayProject
|
|
3022
2482
|
}
|
|
@@ -3048,7 +2508,7 @@ function ReportingContent() {
|
|
|
3048
2508
|
return (
|
|
3049
2509
|
<td
|
|
3050
2510
|
key={dateKey}
|
|
3051
|
-
className="px-1 py-2 text-center tabular-nums text-zinc-200"
|
|
2511
|
+
className="px-1 py-2 text-center tabular-nums text-zinc-800 dark:text-zinc-200"
|
|
3052
2512
|
title={dateKey}
|
|
3053
2513
|
>
|
|
3054
2514
|
{mins > 0
|
|
@@ -3056,7 +2516,8 @@ function ReportingContent() {
|
|
|
3056
2516
|
mins,
|
|
3057
2517
|
{
|
|
3058
2518
|
kind: "project",
|
|
3059
|
-
title:
|
|
2519
|
+
title:
|
|
2520
|
+
block.displayProject,
|
|
3060
2521
|
dayKey:
|
|
3061
2522
|
dateKey,
|
|
3062
2523
|
projectKey:
|
|
@@ -3067,19 +2528,20 @@ function ReportingContent() {
|
|
|
3067
2528
|
t.undatedLabel,
|
|
3068
2529
|
),
|
|
3069
2530
|
},
|
|
3070
|
-
"text-zinc-200",
|
|
2531
|
+
"text-zinc-800 dark:text-zinc-200",
|
|
3071
2532
|
)
|
|
3072
2533
|
: "—"}
|
|
3073
2534
|
</td>
|
|
3074
2535
|
);
|
|
3075
2536
|
},
|
|
3076
2537
|
)}
|
|
3077
|
-
<td className="px-2 py-2 pr-3 text-center tabular-nums font-semibold text-zinc-100">
|
|
2538
|
+
<td className="px-2 py-2 pr-3 text-center tabular-nums font-semibold text-zinc-950 dark:text-zinc-100">
|
|
3078
2539
|
{renderReportingDurationButton(
|
|
3079
2540
|
block.parentTotal,
|
|
3080
2541
|
{
|
|
3081
2542
|
kind: "project",
|
|
3082
|
-
title:
|
|
2543
|
+
title:
|
|
2544
|
+
block.displayProject,
|
|
3083
2545
|
weekStart:
|
|
3084
2546
|
block.weekStart,
|
|
3085
2547
|
projectKey:
|
|
@@ -3091,7 +2553,7 @@ function ReportingContent() {
|
|
|
3091
2553
|
reportLocale,
|
|
3092
2554
|
),
|
|
3093
2555
|
},
|
|
3094
|
-
"text-zinc-100",
|
|
2556
|
+
"text-zinc-950 dark:text-zinc-100",
|
|
3095
2557
|
)}
|
|
3096
2558
|
</td>
|
|
3097
2559
|
</tr>
|
|
@@ -3100,7 +2562,7 @@ function ReportingContent() {
|
|
|
3100
2562
|
(child) => (
|
|
3101
2563
|
<tr
|
|
3102
2564
|
key={`${weekStart}\0${child.tagKey}`}
|
|
3103
|
-
className="border-b border-zinc-800/55 bg-zinc-950/30"
|
|
2565
|
+
className="border-b border-zinc-200/80 bg-zinc-50/90 dark:border-zinc-800/55 dark:bg-zinc-950/30"
|
|
3104
2566
|
>
|
|
3105
2567
|
<ReportingTagNameCell
|
|
3106
2568
|
tagKey={
|
|
@@ -3116,7 +2578,7 @@ function ReportingContent() {
|
|
|
3116
2578
|
descriptions={
|
|
3117
2579
|
tagDescriptions
|
|
3118
2580
|
}
|
|
3119
|
-
className="px-2 py-2 pl-10 text-violet-200/85"
|
|
2581
|
+
className="px-2 py-2 pl-10 text-violet-800 dark:text-violet-200/85"
|
|
3120
2582
|
/>
|
|
3121
2583
|
{child.slots.map(
|
|
3122
2584
|
(mins, i) => {
|
|
@@ -3130,7 +2592,7 @@ function ReportingContent() {
|
|
|
3130
2592
|
key={
|
|
3131
2593
|
dateKey
|
|
3132
2594
|
}
|
|
3133
|
-
className="px-1 py-2 text-center tabular-nums text-zinc-300/95"
|
|
2595
|
+
className="px-1 py-2 text-center tabular-nums text-zinc-700 dark:text-zinc-300/95"
|
|
3134
2596
|
title={
|
|
3135
2597
|
dateKey
|
|
3136
2598
|
}
|
|
@@ -3153,14 +2615,14 @@ function ReportingContent() {
|
|
|
3153
2615
|
t.undatedLabel,
|
|
3154
2616
|
),
|
|
3155
2617
|
},
|
|
3156
|
-
"text-zinc-300/95",
|
|
2618
|
+
"text-zinc-700 dark:text-zinc-300/95",
|
|
3157
2619
|
)
|
|
3158
2620
|
: "—"}
|
|
3159
2621
|
</td>
|
|
3160
2622
|
);
|
|
3161
2623
|
},
|
|
3162
2624
|
)}
|
|
3163
|
-
<td className="px-2 py-2 pr-3 text-center tabular-nums font-medium text-zinc-200">
|
|
2625
|
+
<td className="px-2 py-2 pr-3 text-center tabular-nums font-medium text-zinc-800 dark:text-zinc-200">
|
|
3164
2626
|
{renderReportingDurationButton(
|
|
3165
2627
|
child.total,
|
|
3166
2628
|
{
|
|
@@ -3179,7 +2641,7 @@ function ReportingContent() {
|
|
|
3179
2641
|
reportLocale,
|
|
3180
2642
|
),
|
|
3181
2643
|
},
|
|
3182
|
-
"text-zinc-200",
|
|
2644
|
+
"text-zinc-800 dark:text-zinc-200",
|
|
3183
2645
|
)}
|
|
3184
2646
|
</td>
|
|
3185
2647
|
</tr>
|
|
@@ -3188,379 +2650,1398 @@ function ReportingContent() {
|
|
|
3188
2650
|
: null}
|
|
3189
2651
|
</Fragment>
|
|
3190
2652
|
);
|
|
3191
|
-
}
|
|
3192
|
-
|
|
3193
|
-
</
|
|
3194
|
-
</
|
|
2653
|
+
})}
|
|
2654
|
+
</tbody>
|
|
2655
|
+
</table>
|
|
2656
|
+
</div>
|
|
3195
2657
|
</div>
|
|
3196
|
-
|
|
3197
|
-
)
|
|
3198
|
-
|
|
2658
|
+
),
|
|
2659
|
+
)}
|
|
2660
|
+
</div>
|
|
2661
|
+
)}
|
|
2662
|
+
</div>
|
|
2663
|
+
</section>
|
|
2664
|
+
|
|
2665
|
+
<section
|
|
2666
|
+
id="report-projects"
|
|
2667
|
+
className="mt-10 scroll-mt-28 space-y-4 rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4 sm:p-6"
|
|
2668
|
+
>
|
|
2669
|
+
<div className="flex min-h-6 flex-wrap items-center gap-x-2 gap-y-1">
|
|
2670
|
+
<h3 className="text-sm font-semibold text-zinc-900 dark:text-zinc-200">
|
|
2671
|
+
{t.projectSectionTitle}
|
|
2672
|
+
</h3>
|
|
2673
|
+
<ReportingFilteredBadge
|
|
2674
|
+
active={reportingFiltersActive}
|
|
2675
|
+
label={t.sectionFilteredBadge}
|
|
2676
|
+
titleText={t.sectionFilteredBadgeTitle}
|
|
2677
|
+
/>
|
|
2678
|
+
<InlineMetricHelpTrigger
|
|
2679
|
+
ariaLabel={t.metricHelpProjectTitleAria}
|
|
2680
|
+
body={t.metricHelpProjectTitleBody}
|
|
2681
|
+
/>
|
|
2682
|
+
</div>
|
|
2683
|
+
<p className="text-xs text-zinc-500">
|
|
2684
|
+
{t.projectTableHint}
|
|
2685
|
+
</p>
|
|
2686
|
+
|
|
2687
|
+
<fieldset className="rounded-lg border border-zinc-200/90 bg-zinc-50/90 dark:border-zinc-800/90 dark:bg-zinc-950/30 p-3">
|
|
2688
|
+
<legend className="px-1 text-xs text-zinc-500">
|
|
2689
|
+
{t.projectScopeLegend}
|
|
2690
|
+
</legend>
|
|
2691
|
+
<div
|
|
2692
|
+
className="mt-2 flex flex-wrap gap-2"
|
|
2693
|
+
role="radiogroup"
|
|
2694
|
+
aria-label={t.projectScopeGroupAria}
|
|
2695
|
+
>
|
|
2696
|
+
<button
|
|
2697
|
+
type="button"
|
|
2698
|
+
role="radio"
|
|
2699
|
+
aria-checked={projectMinutesScope === "work"}
|
|
2700
|
+
onClick={() => setProjectMinutesScope("work")}
|
|
2701
|
+
className={`rounded-md border px-3 py-1.5 text-xs font-medium transition-colors ${
|
|
2702
|
+
projectMinutesScope === "work"
|
|
2703
|
+
? "border-violet-500/80 bg-violet-100 text-violet-900 dark:bg-violet-950/50 dark:text-violet-200"
|
|
2704
|
+
: "border-zinc-300 bg-white text-zinc-600 hover:border-zinc-400 hover:bg-zinc-50 hover:text-zinc-900 dark:border-zinc-700 dark:bg-zinc-900/60 dark:text-zinc-400 dark:hover:border-zinc-600 dark:hover:text-zinc-200"
|
|
2705
|
+
}`}
|
|
2706
|
+
>
|
|
2707
|
+
{t.projectScopeWork}
|
|
2708
|
+
</button>
|
|
2709
|
+
<button
|
|
2710
|
+
type="button"
|
|
2711
|
+
role="radio"
|
|
2712
|
+
aria-checked={
|
|
2713
|
+
projectMinutesScope === "personal"
|
|
2714
|
+
}
|
|
2715
|
+
onClick={() =>
|
|
2716
|
+
setProjectMinutesScope("personal")
|
|
2717
|
+
}
|
|
2718
|
+
className={`rounded-md border px-3 py-1.5 text-xs font-medium transition-colors ${
|
|
2719
|
+
projectMinutesScope === "personal"
|
|
2720
|
+
? "border-rose-500/70 bg-rose-100 text-rose-900 dark:bg-rose-950/40 dark:text-rose-200"
|
|
2721
|
+
: "border-zinc-300 bg-white text-zinc-600 hover:border-zinc-400 hover:bg-zinc-50 hover:text-zinc-900 dark:border-zinc-700 dark:bg-zinc-900/60 dark:text-zinc-400 dark:hover:border-zinc-600 dark:hover:text-zinc-200"
|
|
2722
|
+
}`}
|
|
2723
|
+
>
|
|
2724
|
+
{t.projectScopePersonal}
|
|
2725
|
+
</button>
|
|
3199
2726
|
</div>
|
|
3200
|
-
|
|
3201
|
-
|
|
3202
|
-
|
|
2727
|
+
</fieldset>
|
|
2728
|
+
|
|
2729
|
+
<div className="flex min-h-6 flex-wrap items-center gap-x-2 gap-y-1">
|
|
2730
|
+
<h4 className="text-xs font-semibold uppercase tracking-wide text-zinc-600 dark:text-zinc-400">
|
|
2731
|
+
{t.projectWeeklyCalendarTitle}
|
|
2732
|
+
</h4>
|
|
2733
|
+
<InlineMetricHelpTrigger
|
|
2734
|
+
ariaLabel={t.metricHelpProjectCalendarAria}
|
|
2735
|
+
body={t.metricHelpProjectCalendarBody}
|
|
2736
|
+
/>
|
|
2737
|
+
</div>
|
|
3203
2738
|
|
|
2739
|
+
{projectWeekCalendarRows.length === 0 ? (
|
|
2740
|
+
<p className="text-sm text-zinc-500">—</p>
|
|
2741
|
+
) : projectWeekCalendarRowsVisible.length === 0 ? (
|
|
2742
|
+
<div className="space-y-2 text-sm text-zinc-500">
|
|
2743
|
+
<p>{t.weekNavNoProjectDataThisWeek}</p>
|
|
2744
|
+
{archivedExcludedTaskMinutes > 1e-9 ? (
|
|
2745
|
+
<p className="text-amber-800 dark:text-amber-200/90">
|
|
2746
|
+
{reportingArchivedExcludedRichText(
|
|
2747
|
+
t.reportingArchivedExcludedAside,
|
|
2748
|
+
archivedExcludedTaskMinutes,
|
|
2749
|
+
)}
|
|
2750
|
+
</p>
|
|
2751
|
+
) : null}
|
|
2752
|
+
</div>
|
|
2753
|
+
) : (
|
|
2754
|
+
<div className="space-y-8">
|
|
2755
|
+
{projectCalendarWeekGroups.map(
|
|
2756
|
+
({ weekStart, rows }) => (
|
|
2757
|
+
<div key={weekStart}>
|
|
2758
|
+
<div className="mb-2 flex flex-wrap items-center gap-2">
|
|
2759
|
+
<p className="text-sm font-medium text-zinc-800 dark:text-zinc-300">
|
|
2760
|
+
{formatWeekRangeLabel(
|
|
2761
|
+
weekStart,
|
|
2762
|
+
reportLocale,
|
|
2763
|
+
)}
|
|
2764
|
+
</p>
|
|
2765
|
+
<InlineMetricHelpTrigger
|
|
2766
|
+
ariaLabel={
|
|
2767
|
+
t.metricHelpTagTimeColWeekAria
|
|
2768
|
+
}
|
|
2769
|
+
body={t.metricHelpTagTimeColWeekBody}
|
|
2770
|
+
/>
|
|
2771
|
+
</div>
|
|
2772
|
+
<div className="overflow-x-auto rounded-lg border border-zinc-200/80 dark:border-zinc-800/80">
|
|
2773
|
+
<table className="w-full min-w-[28rem] text-left text-sm">
|
|
2774
|
+
<thead className="border-b border-zinc-200 bg-zinc-100/80 dark:border-zinc-800 dark:bg-zinc-950/40 text-xs uppercase text-zinc-500">
|
|
2775
|
+
<tr>
|
|
2776
|
+
<th className="px-2 py-2 pl-3 font-medium">
|
|
2777
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
2778
|
+
<span>
|
|
2779
|
+
{t.projectColProject}
|
|
2780
|
+
</span>
|
|
2781
|
+
<InlineMetricHelpTrigger
|
|
2782
|
+
ariaLabel={
|
|
2783
|
+
t.metricHelpProjectColProjAria
|
|
2784
|
+
}
|
|
2785
|
+
body={
|
|
2786
|
+
t.metricHelpProjectColProjBody
|
|
2787
|
+
}
|
|
2788
|
+
/>
|
|
2789
|
+
</div>
|
|
2790
|
+
</th>
|
|
2791
|
+
{weekdayDateColumnHeaders(
|
|
2792
|
+
weekStart,
|
|
2793
|
+
reportLocale,
|
|
2794
|
+
).map((col) => (
|
|
2795
|
+
<th
|
|
2796
|
+
key={`proj-${col.dateKey}`}
|
|
2797
|
+
className="px-1 py-2 text-center font-medium normal-case"
|
|
2798
|
+
title={col.dateKey}
|
|
2799
|
+
>
|
|
2800
|
+
<div className="flex flex-col items-center gap-0.5 leading-tight">
|
|
2801
|
+
<span className="text-[0.65rem] font-semibold uppercase tracking-wide text-zinc-500">
|
|
2802
|
+
{col.weekdayShort}
|
|
2803
|
+
</span>
|
|
2804
|
+
<span className="text-[0.7rem] font-medium tabular-nums text-zinc-700 dark:text-zinc-300">
|
|
2805
|
+
{col.calendarDateShort}
|
|
2806
|
+
</span>
|
|
2807
|
+
</div>
|
|
2808
|
+
</th>
|
|
2809
|
+
))}
|
|
2810
|
+
<th className="px-2 py-2 pr-3 text-center font-medium">
|
|
2811
|
+
<div className="flex min-h-5 items-center justify-center gap-0.5">
|
|
2812
|
+
<span>
|
|
2813
|
+
{t.tagWeekSumColumn}
|
|
2814
|
+
</span>
|
|
2815
|
+
<InlineMetricHelpTrigger
|
|
2816
|
+
align="end"
|
|
2817
|
+
ariaLabel={
|
|
2818
|
+
t.metricHelpProjectColTimeAria
|
|
2819
|
+
}
|
|
2820
|
+
body={
|
|
2821
|
+
t.metricHelpProjectColTimeBody
|
|
2822
|
+
}
|
|
2823
|
+
/>
|
|
2824
|
+
</div>
|
|
2825
|
+
</th>
|
|
2826
|
+
</tr>
|
|
2827
|
+
</thead>
|
|
2828
|
+
<tbody>
|
|
2829
|
+
{rows.map((row) => {
|
|
2830
|
+
const projectKeyLower =
|
|
2831
|
+
normalizeProjectKey(
|
|
2832
|
+
row.projectKey,
|
|
2833
|
+
).toLowerCase();
|
|
2834
|
+
const breakdownKey = `${weekStart}:::${projectKeyLower}`;
|
|
2835
|
+
const childTagRows =
|
|
2836
|
+
projectScopedTagRowsByWeekProject.get(
|
|
2837
|
+
breakdownKey,
|
|
2838
|
+
) ?? [];
|
|
2839
|
+
const canExpand =
|
|
2840
|
+
childTagRows.length > 0;
|
|
2841
|
+
const isOpen =
|
|
2842
|
+
canExpand &&
|
|
2843
|
+
projectWeekTagBreakdownOpenKeys.has(
|
|
2844
|
+
breakdownKey,
|
|
2845
|
+
);
|
|
2846
|
+
const rowTitle =
|
|
2847
|
+
row.displayProject ||
|
|
2848
|
+
row.projectKey;
|
|
2849
|
+
const rowProjectDescription =
|
|
2850
|
+
reportingProjectDescriptionLine(
|
|
2851
|
+
row.projectKey,
|
|
2852
|
+
projectDescriptions,
|
|
2853
|
+
) ?? "";
|
|
2854
|
+
return (
|
|
2855
|
+
<Fragment
|
|
2856
|
+
key={`${weekStart}\0${row.projectKey}`}
|
|
2857
|
+
>
|
|
2858
|
+
<tr className="border-b border-zinc-200/90 last:border-0 dark:border-zinc-800/70">
|
|
2859
|
+
{canExpand ? (
|
|
2860
|
+
<td
|
|
2861
|
+
className={`align-top px-2 py-2 pl-3 ${projectNameCellToneClass}`}
|
|
2862
|
+
>
|
|
2863
|
+
<div className="flex items-start gap-1.5">
|
|
2864
|
+
<button
|
|
2865
|
+
type="button"
|
|
2866
|
+
aria-expanded={isOpen}
|
|
2867
|
+
aria-label={
|
|
2868
|
+
t.tagWeekScopedRollupToggleAria
|
|
2869
|
+
}
|
|
2870
|
+
className="mt-0.5 shrink-0 rounded p-0.5 text-zinc-500 transition hover:bg-zinc-200 hover:text-zinc-900 dark:text-zinc-400 dark:hover:bg-zinc-800 dark:hover:text-zinc-200"
|
|
2871
|
+
onClick={() =>
|
|
2872
|
+
toggleProjectWeekTagBreakdown(
|
|
2873
|
+
breakdownKey,
|
|
2874
|
+
)
|
|
2875
|
+
}
|
|
2876
|
+
>
|
|
2877
|
+
<ChevronRight
|
|
2878
|
+
className={`size-4 transition-transform duration-200 ${
|
|
2879
|
+
isOpen
|
|
2880
|
+
? "rotate-90"
|
|
2881
|
+
: ""
|
|
2882
|
+
}`}
|
|
2883
|
+
strokeWidth={2}
|
|
2884
|
+
aria-hidden
|
|
2885
|
+
/>
|
|
2886
|
+
</button>
|
|
2887
|
+
<div className="min-w-0">
|
|
2888
|
+
<div>
|
|
2889
|
+
{row.projectKey ===
|
|
2890
|
+
""
|
|
2891
|
+
? t.projectUnassigned
|
|
2892
|
+
: row.displayProject ||
|
|
2893
|
+
row.projectKey}
|
|
2894
|
+
</div>
|
|
2895
|
+
{rowProjectDescription ? (
|
|
2896
|
+
<p className="mt-1 max-w-[min(22rem,55vw)] whitespace-pre-line text-[0.65rem] font-normal leading-snug text-zinc-500">
|
|
2897
|
+
{
|
|
2898
|
+
rowProjectDescription
|
|
2899
|
+
}
|
|
2900
|
+
</p>
|
|
2901
|
+
) : null}
|
|
2902
|
+
<p className="mt-1 text-[0.65rem] font-normal tabular-nums text-zinc-500">
|
|
2903
|
+
{
|
|
2904
|
+
childTagRows.length
|
|
2905
|
+
}{" "}
|
|
2906
|
+
{lang === "fr"
|
|
2907
|
+
? "étiquette(s) liée(s)"
|
|
2908
|
+
: "linked tag(s)"}
|
|
2909
|
+
</p>
|
|
2910
|
+
</div>
|
|
2911
|
+
</div>
|
|
2912
|
+
</td>
|
|
2913
|
+
) : (
|
|
2914
|
+
<ReportingProjectNameCell
|
|
2915
|
+
projectKey={
|
|
2916
|
+
row.projectKey
|
|
2917
|
+
}
|
|
2918
|
+
displayLabel={
|
|
2919
|
+
row.displayProject ||
|
|
2920
|
+
row.projectKey
|
|
2921
|
+
}
|
|
2922
|
+
unassignedLabel={
|
|
2923
|
+
t.projectUnassigned
|
|
2924
|
+
}
|
|
2925
|
+
descriptions={
|
|
2926
|
+
projectDescriptions
|
|
2927
|
+
}
|
|
2928
|
+
className={`px-2 py-2 pl-3 ${projectNameCellToneClass}`}
|
|
2929
|
+
/>
|
|
2930
|
+
)}
|
|
2931
|
+
{row.slots.map((mins, i) => {
|
|
2932
|
+
const dateKey = addDaysYmd(
|
|
2933
|
+
row.weekStart,
|
|
2934
|
+
i,
|
|
2935
|
+
);
|
|
2936
|
+
return (
|
|
2937
|
+
<td
|
|
2938
|
+
key={dateKey}
|
|
2939
|
+
className="px-1 py-2 text-center tabular-nums text-zinc-800 dark:text-zinc-200"
|
|
2940
|
+
title={dateKey}
|
|
2941
|
+
>
|
|
2942
|
+
{mins > 0
|
|
2943
|
+
? renderReportingDurationButton(
|
|
2944
|
+
mins,
|
|
2945
|
+
{
|
|
2946
|
+
kind: "project",
|
|
2947
|
+
title: rowTitle,
|
|
2948
|
+
dayKey: dateKey,
|
|
2949
|
+
projectKey:
|
|
2950
|
+
row.projectKey,
|
|
2951
|
+
sourceLabel:
|
|
2952
|
+
dayLabel(
|
|
2953
|
+
dateKey,
|
|
2954
|
+
t.undatedLabel,
|
|
2955
|
+
),
|
|
2956
|
+
},
|
|
2957
|
+
"text-zinc-800 dark:text-zinc-200",
|
|
2958
|
+
)
|
|
2959
|
+
: "—"}
|
|
2960
|
+
</td>
|
|
2961
|
+
);
|
|
2962
|
+
})}
|
|
2963
|
+
<td className="px-2 py-2 pr-3 text-center tabular-nums font-semibold text-zinc-950 dark:text-zinc-100">
|
|
2964
|
+
{renderReportingDurationButton(
|
|
2965
|
+
row.total,
|
|
2966
|
+
{
|
|
2967
|
+
kind: "project",
|
|
2968
|
+
title: rowTitle,
|
|
2969
|
+
weekStart:
|
|
2970
|
+
row.weekStart,
|
|
2971
|
+
projectKey:
|
|
2972
|
+
row.projectKey,
|
|
2973
|
+
sourceLabel:
|
|
2974
|
+
weekRangeLabelForSource(
|
|
2975
|
+
row.weekStart,
|
|
2976
|
+
lang,
|
|
2977
|
+
reportLocale,
|
|
2978
|
+
),
|
|
2979
|
+
},
|
|
2980
|
+
"text-zinc-950 dark:text-zinc-100",
|
|
2981
|
+
)}
|
|
2982
|
+
</td>
|
|
2983
|
+
</tr>
|
|
2984
|
+
{isOpen
|
|
2985
|
+
? childTagRows.map(
|
|
2986
|
+
(child) => (
|
|
2987
|
+
<tr
|
|
2988
|
+
key={`${weekStart}\0${row.projectKey}\0${child.tagKey}`}
|
|
2989
|
+
className="border-b border-zinc-200/80 bg-zinc-50/90 dark:border-zinc-800/55 dark:bg-zinc-950/30"
|
|
2990
|
+
>
|
|
2991
|
+
<ReportingTagNameCell
|
|
2992
|
+
tagKey={
|
|
2993
|
+
child.tagKey
|
|
2994
|
+
}
|
|
2995
|
+
displayLabel={
|
|
2996
|
+
child.displayTag ||
|
|
2997
|
+
child.tagKey
|
|
2998
|
+
}
|
|
2999
|
+
untaggedLabel={
|
|
3000
|
+
t.tagTimeUntagged
|
|
3001
|
+
}
|
|
3002
|
+
descriptions={
|
|
3003
|
+
tagDescriptions
|
|
3004
|
+
}
|
|
3005
|
+
className="px-2 py-2 pl-10 text-violet-800 dark:text-violet-200/85"
|
|
3006
|
+
/>
|
|
3007
|
+
{child.slots.map(
|
|
3008
|
+
(mins, i) => {
|
|
3009
|
+
const dateKey =
|
|
3010
|
+
addDaysYmd(
|
|
3011
|
+
child.weekStart,
|
|
3012
|
+
i,
|
|
3013
|
+
);
|
|
3014
|
+
return (
|
|
3015
|
+
<td
|
|
3016
|
+
key={dateKey}
|
|
3017
|
+
className="px-1 py-2 text-center tabular-nums text-zinc-700 dark:text-zinc-300/95"
|
|
3018
|
+
title={
|
|
3019
|
+
dateKey
|
|
3020
|
+
}
|
|
3021
|
+
>
|
|
3022
|
+
{mins > 0
|
|
3023
|
+
? renderReportingDurationButton(
|
|
3024
|
+
mins,
|
|
3025
|
+
{
|
|
3026
|
+
kind: "tag",
|
|
3027
|
+
title:
|
|
3028
|
+
child.displayTag ||
|
|
3029
|
+
child.tagKey,
|
|
3030
|
+
dayKey:
|
|
3031
|
+
dateKey,
|
|
3032
|
+
tagKey:
|
|
3033
|
+
child.tagKey,
|
|
3034
|
+
sourceLabel:
|
|
3035
|
+
dayLabel(
|
|
3036
|
+
dateKey,
|
|
3037
|
+
t.undatedLabel,
|
|
3038
|
+
),
|
|
3039
|
+
},
|
|
3040
|
+
"text-zinc-700 dark:text-zinc-300/95",
|
|
3041
|
+
)
|
|
3042
|
+
: "—"}
|
|
3043
|
+
</td>
|
|
3044
|
+
);
|
|
3045
|
+
},
|
|
3046
|
+
)}
|
|
3047
|
+
<td className="px-2 py-2 pr-3 text-center tabular-nums font-medium text-zinc-800 dark:text-zinc-200">
|
|
3048
|
+
{renderReportingDurationButton(
|
|
3049
|
+
child.total,
|
|
3050
|
+
{
|
|
3051
|
+
kind: "tag",
|
|
3052
|
+
title:
|
|
3053
|
+
child.displayTag ||
|
|
3054
|
+
child.tagKey,
|
|
3055
|
+
weekStart:
|
|
3056
|
+
child.weekStart,
|
|
3057
|
+
tagKey:
|
|
3058
|
+
child.tagKey,
|
|
3059
|
+
sourceLabel:
|
|
3060
|
+
weekRangeLabelForSource(
|
|
3061
|
+
child.weekStart,
|
|
3062
|
+
lang,
|
|
3063
|
+
reportLocale,
|
|
3064
|
+
),
|
|
3065
|
+
},
|
|
3066
|
+
"text-zinc-800 dark:text-zinc-200",
|
|
3067
|
+
)}
|
|
3068
|
+
</td>
|
|
3069
|
+
</tr>
|
|
3070
|
+
),
|
|
3071
|
+
)
|
|
3072
|
+
: null}
|
|
3073
|
+
</Fragment>
|
|
3074
|
+
);
|
|
3075
|
+
})}
|
|
3076
|
+
</tbody>
|
|
3077
|
+
</table>
|
|
3078
|
+
</div>
|
|
3079
|
+
</div>
|
|
3080
|
+
),
|
|
3081
|
+
)}
|
|
3082
|
+
</div>
|
|
3083
|
+
)}
|
|
3084
|
+
</section>
|
|
3085
|
+
</>
|
|
3086
|
+
) : null}
|
|
3087
|
+
</div>
|
|
3088
|
+
<div
|
|
3089
|
+
id="report-view-panel-rhythm"
|
|
3090
|
+
role="tabpanel"
|
|
3091
|
+
aria-labelledby="reporting-tab-rhythm"
|
|
3092
|
+
className={
|
|
3093
|
+
reportingViewTab !== "rhythm"
|
|
3094
|
+
? "hidden"
|
|
3095
|
+
: "mb-10 space-y-6"
|
|
3096
|
+
}
|
|
3097
|
+
>
|
|
3098
|
+
{hasReportingChartData ? (
|
|
3099
|
+
<>
|
|
3100
|
+
<div className="mb-10 grid grid-cols-1 gap-4 md:grid-cols-2">
|
|
3101
|
+
<section
|
|
3102
|
+
id="report-chart-sessions"
|
|
3103
|
+
className="scroll-mt-28 rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4 sm:p-6"
|
|
3104
|
+
>
|
|
3105
|
+
<div className="flex min-h-6 flex-wrap items-center gap-x-2 gap-y-1">
|
|
3106
|
+
<h3 className="text-sm font-semibold text-zinc-900 dark:text-zinc-200">
|
|
3107
|
+
{t.chartSessionsPerDay}
|
|
3108
|
+
</h3>
|
|
3109
|
+
<ReportingFilteredBadge
|
|
3110
|
+
active={reportingFiltersActive}
|
|
3111
|
+
label={t.sectionFilteredBadge}
|
|
3112
|
+
titleText={t.sectionFilteredBadgeTitle}
|
|
3113
|
+
/>
|
|
3114
|
+
<InlineMetricHelpTrigger
|
|
3115
|
+
ariaLabel={t.metricHelpChartSessionsAria}
|
|
3116
|
+
body={t.metricHelpChartSessionsBody}
|
|
3117
|
+
/>
|
|
3118
|
+
</div>
|
|
3119
|
+
<div className="mt-1 flex flex-wrap items-center gap-1 text-xs text-zinc-500">
|
|
3120
|
+
<span>{t.legendSessions}</span>
|
|
3121
|
+
<InlineMetricHelpTrigger
|
|
3122
|
+
ariaLabel={t.metricHelpLegendSessionsAria}
|
|
3123
|
+
body={t.metricHelpLegendSessionsBody}
|
|
3124
|
+
/>
|
|
3125
|
+
</div>
|
|
3126
|
+
<div className="mt-4">
|
|
3127
|
+
<MiniBars
|
|
3128
|
+
days={reportingDayKeys}
|
|
3129
|
+
values={agg.sessionsByDay}
|
|
3130
|
+
max={peak}
|
|
3131
|
+
className="bg-violet-500/90"
|
|
3132
|
+
undatedLabel={t.undatedLabel}
|
|
3133
|
+
/>
|
|
3134
|
+
</div>
|
|
3135
|
+
</section>
|
|
3136
|
+
|
|
3137
|
+
<section
|
|
3138
|
+
id="report-chart-tasks"
|
|
3139
|
+
className="scroll-mt-28 rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4 sm:p-6"
|
|
3140
|
+
>
|
|
3141
|
+
<div className="flex min-h-6 flex-wrap items-center gap-x-2 gap-y-1">
|
|
3142
|
+
<h3 className="text-sm font-semibold text-zinc-900 dark:text-zinc-200">
|
|
3143
|
+
{t.chartTasksByStatusPerDay}
|
|
3144
|
+
</h3>
|
|
3145
|
+
<ReportingFilteredBadge
|
|
3146
|
+
active={reportingFiltersActive}
|
|
3147
|
+
label={t.sectionFilteredBadge}
|
|
3148
|
+
titleText={t.sectionFilteredBadgeTitle}
|
|
3149
|
+
/>
|
|
3150
|
+
<InlineMetricHelpTrigger
|
|
3151
|
+
ariaLabel={t.metricHelpChartTasksAria}
|
|
3152
|
+
body={t.metricHelpChartTasksBody}
|
|
3153
|
+
/>
|
|
3154
|
+
</div>
|
|
3155
|
+
<div className="mt-2 flex flex-wrap items-center gap-x-4 gap-y-2 text-xs text-zinc-500">
|
|
3156
|
+
<div className="inline-flex items-center gap-1">
|
|
3157
|
+
<span className="mr-0.5 inline-block h-2 w-2 shrink-0 rounded-sm bg-emerald-600" />
|
|
3158
|
+
{t.legendDone}
|
|
3159
|
+
<InlineMetricHelpTrigger
|
|
3160
|
+
ariaLabel={t.metricHelpLegendDoneAria}
|
|
3161
|
+
body={t.metricHelpLegendDoneBody}
|
|
3162
|
+
/>
|
|
3163
|
+
</div>
|
|
3164
|
+
<div className="inline-flex items-center gap-1">
|
|
3165
|
+
<span className="mr-0.5 inline-block h-2 w-2 shrink-0 rounded-sm bg-amber-500" />
|
|
3166
|
+
{t.legendActive}
|
|
3167
|
+
<InlineMetricHelpTrigger
|
|
3168
|
+
ariaLabel={t.metricHelpLegendActiveAria}
|
|
3169
|
+
body={t.metricHelpLegendActiveBody}
|
|
3170
|
+
/>
|
|
3171
|
+
</div>
|
|
3172
|
+
</div>
|
|
3173
|
+
<div className="mt-4">
|
|
3174
|
+
<StackedTaskBars
|
|
3175
|
+
days={reportingDayKeys}
|
|
3176
|
+
done={agg.tasksByDayDone}
|
|
3177
|
+
active={agg.tasksByDayActive}
|
|
3178
|
+
max={peakTasks}
|
|
3179
|
+
undatedLabel={t.undatedLabel}
|
|
3180
|
+
/>
|
|
3181
|
+
</div>
|
|
3182
|
+
</section>
|
|
3183
|
+
|
|
3184
|
+
<section
|
|
3185
|
+
id="report-chart-task-time"
|
|
3186
|
+
className="scroll-mt-28 rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4 sm:p-6"
|
|
3187
|
+
>
|
|
3188
|
+
<div className="flex min-h-6 flex-wrap items-center gap-x-2 gap-y-1">
|
|
3189
|
+
<h3 className="text-sm font-semibold text-zinc-900 dark:text-zinc-200">
|
|
3190
|
+
{t.chartTaskTimePerDay}
|
|
3191
|
+
</h3>
|
|
3192
|
+
<ReportingFilteredBadge
|
|
3193
|
+
active={reportingFiltersActive}
|
|
3194
|
+
label={t.sectionFilteredBadge}
|
|
3195
|
+
titleText={t.sectionFilteredBadgeTitle}
|
|
3196
|
+
/>
|
|
3197
|
+
<InlineMetricHelpTrigger
|
|
3198
|
+
ariaLabel={t.metricHelpChartTaskTimeAria}
|
|
3199
|
+
body={t.metricHelpChartTaskTimeBody}
|
|
3200
|
+
/>
|
|
3201
|
+
</div>
|
|
3202
|
+
<div className="mt-1 flex flex-wrap items-center gap-1 text-xs text-zinc-500">
|
|
3203
|
+
<span>{t.summaryTaskTimeRecorded}</span>
|
|
3204
|
+
<InlineMetricHelpTrigger
|
|
3205
|
+
ariaLabel={t.metricHelpTaskTimeAria}
|
|
3206
|
+
body={t.metricHelpTaskTimeBody}
|
|
3207
|
+
/>
|
|
3208
|
+
</div>
|
|
3209
|
+
<div className="mt-4">
|
|
3210
|
+
<MiniBars
|
|
3211
|
+
days={reportingDayKeys}
|
|
3212
|
+
values={agg.taskMinutesByDay}
|
|
3213
|
+
max={peakTaskMinutes}
|
|
3214
|
+
className="bg-sky-500/90"
|
|
3215
|
+
undatedLabel={t.undatedLabel}
|
|
3216
|
+
valueTitle={(m) => formatDuration(m)}
|
|
3217
|
+
/>
|
|
3218
|
+
</div>
|
|
3219
|
+
</section>
|
|
3220
|
+
|
|
3221
|
+
<section
|
|
3222
|
+
id="report-chart-session-wall"
|
|
3223
|
+
className="scroll-mt-28 rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4 sm:p-6"
|
|
3224
|
+
>
|
|
3225
|
+
<div className="flex min-h-6 flex-wrap items-center gap-x-2 gap-y-1">
|
|
3226
|
+
<h3 className="text-sm font-semibold text-zinc-900 dark:text-zinc-200">
|
|
3227
|
+
{t.chartSessionWallPerDay}
|
|
3228
|
+
</h3>
|
|
3229
|
+
<ReportingFilteredBadge
|
|
3230
|
+
active={reportingFiltersActive}
|
|
3231
|
+
label={t.sectionFilteredBadge}
|
|
3232
|
+
titleText={t.sectionFilteredBadgeTitle}
|
|
3233
|
+
/>
|
|
3234
|
+
<InlineMetricHelpTrigger
|
|
3235
|
+
ariaLabel={t.metricHelpChartSessionWallAria}
|
|
3236
|
+
body={t.metricHelpChartSessionWallBody}
|
|
3237
|
+
/>
|
|
3238
|
+
</div>
|
|
3239
|
+
<div className="mt-1 flex flex-wrap items-center gap-1 text-xs text-zinc-500">
|
|
3240
|
+
<span>{t.summarySessionWallClock}</span>
|
|
3241
|
+
<InlineMetricHelpTrigger
|
|
3242
|
+
ariaLabel={t.metricHelpSessionWallSummaryAria}
|
|
3243
|
+
body={t.metricHelpSessionWallSummaryBody}
|
|
3244
|
+
/>
|
|
3245
|
+
</div>
|
|
3246
|
+
<div className="mt-4">
|
|
3247
|
+
<MiniBars
|
|
3248
|
+
days={reportingDayKeys}
|
|
3249
|
+
values={agg.sessionWallClockMinutesByDay}
|
|
3250
|
+
max={peakSessionWallMinutes}
|
|
3251
|
+
className="bg-fuchsia-500/85"
|
|
3252
|
+
undatedLabel={t.undatedLabel}
|
|
3253
|
+
valueTitle={(m) => formatDuration(m)}
|
|
3254
|
+
/>
|
|
3255
|
+
</div>
|
|
3256
|
+
</section>
|
|
3257
|
+
</div>
|
|
3258
|
+
</>
|
|
3259
|
+
) : null}
|
|
3260
|
+
</div>
|
|
3261
|
+
<div
|
|
3262
|
+
id="report-view-panel-advanced"
|
|
3263
|
+
role="tabpanel"
|
|
3264
|
+
aria-labelledby="reporting-tab-advanced"
|
|
3265
|
+
className={
|
|
3266
|
+
reportingViewTab !== "advanced"
|
|
3267
|
+
? "hidden"
|
|
3268
|
+
: "mb-10 space-y-10"
|
|
3269
|
+
}
|
|
3270
|
+
>
|
|
3271
|
+
{trackCodeMetrics ? (
|
|
3272
|
+
<>
|
|
3273
|
+
<section
|
|
3274
|
+
id="report-workspace-snapshot"
|
|
3275
|
+
className="mb-10 scroll-mt-28 rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4 sm:p-6"
|
|
3276
|
+
>
|
|
3277
|
+
<div className="flex flex-wrap items-start justify-between gap-3">
|
|
3278
|
+
<div className="flex min-w-0 max-w-full items-center gap-1">
|
|
3279
|
+
<h2 className="text-sm font-semibold uppercase tracking-wide text-zinc-600 dark:text-zinc-400">
|
|
3280
|
+
{t.workspaceSnapshotTitle}
|
|
3281
|
+
</h2>
|
|
3282
|
+
<InlineMetricHelpTrigger
|
|
3283
|
+
ariaLabel={t.metricHelpWorkspaceTitleAria}
|
|
3284
|
+
body={t.metricHelpWorkspaceTitleBody}
|
|
3285
|
+
/>
|
|
3286
|
+
</div>
|
|
3287
|
+
<button
|
|
3288
|
+
type="button"
|
|
3289
|
+
disabled={workspaceSnapBusy}
|
|
3290
|
+
className="shrink-0 rounded-lg border border-zinc-300 bg-white px-3 py-2 text-sm text-zinc-700 hover:bg-zinc-100 disabled:cursor-not-allowed disabled:opacity-50 dark:border-zinc-600 dark:bg-transparent dark:text-zinc-300 dark:hover:bg-zinc-800"
|
|
3291
|
+
onClick={() => void refreshWorkspaceSnapshot()}
|
|
3292
|
+
>
|
|
3293
|
+
{workspaceSnapBusy
|
|
3294
|
+
? t.workspaceSnapshotRefreshing
|
|
3295
|
+
: t.workspaceSnapshotRefresh}
|
|
3296
|
+
</button>
|
|
3297
|
+
</div>
|
|
3298
|
+
<p className="mt-2 text-xs text-zinc-500">
|
|
3299
|
+
{t.workspaceSnapshotIntro}
|
|
3300
|
+
</p>
|
|
3301
|
+
{(() => {
|
|
3302
|
+
const ws = payload.workspaceCodeSnapshot as
|
|
3303
|
+
| WorkspaceCodeSnapshotPayload
|
|
3304
|
+
| undefined;
|
|
3305
|
+
if (!ws) {
|
|
3306
|
+
return (
|
|
3307
|
+
<p className="mt-4 text-sm text-zinc-500">
|
|
3308
|
+
{t.workspaceSnapshotRefreshHint}
|
|
3309
|
+
</p>
|
|
3310
|
+
);
|
|
3311
|
+
}
|
|
3312
|
+
if (ws.ok) {
|
|
3313
|
+
return (
|
|
3314
|
+
<>
|
|
3315
|
+
<p className="mt-2 text-xs text-zinc-500">
|
|
3316
|
+
{ws.source === "git"
|
|
3317
|
+
? t.workspaceSnapshotSourceGit
|
|
3318
|
+
: t.workspaceSnapshotSourceWalk}
|
|
3319
|
+
</p>
|
|
3320
|
+
<div className="mt-3 flex flex-wrap gap-6 text-sm">
|
|
3321
|
+
<div>
|
|
3322
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3323
|
+
<span className="text-xs uppercase text-zinc-500">
|
|
3324
|
+
{t.workspaceSnapshotTotalLines}
|
|
3325
|
+
</span>
|
|
3326
|
+
<InlineMetricHelpTrigger
|
|
3327
|
+
ariaLabel={
|
|
3328
|
+
t.metricHelpWsTotalLinesAria
|
|
3329
|
+
}
|
|
3330
|
+
body={t.metricHelpWsTotalLinesBody}
|
|
3331
|
+
/>
|
|
3332
|
+
</div>
|
|
3333
|
+
<div className="tabular-nums text-lg font-semibold text-zinc-900 dark:text-zinc-100">
|
|
3334
|
+
{ws.totalLines}
|
|
3335
|
+
</div>
|
|
3336
|
+
</div>
|
|
3337
|
+
<div>
|
|
3338
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3339
|
+
<span className="text-xs uppercase text-zinc-500">
|
|
3340
|
+
{t.workspaceSnapshotFileCount}
|
|
3341
|
+
</span>
|
|
3342
|
+
<InlineMetricHelpTrigger
|
|
3343
|
+
ariaLabel={
|
|
3344
|
+
t.metricHelpWsFileCountAria
|
|
3345
|
+
}
|
|
3346
|
+
body={t.metricHelpWsFileCountBody}
|
|
3347
|
+
/>
|
|
3348
|
+
</div>
|
|
3349
|
+
<div className="tabular-nums text-lg font-semibold text-zinc-900 dark:text-zinc-100">
|
|
3350
|
+
{ws.fileCount}
|
|
3351
|
+
</div>
|
|
3352
|
+
</div>
|
|
3353
|
+
</div>
|
|
3354
|
+
{ws.byLanguage.length === 0 ? (
|
|
3355
|
+
<p className="mt-4 text-sm text-zinc-500">
|
|
3356
|
+
—
|
|
3357
|
+
</p>
|
|
3358
|
+
) : (
|
|
3359
|
+
<div className="mt-4 overflow-x-auto">
|
|
3360
|
+
<table className="w-full min-w-[22rem] text-left text-sm">
|
|
3361
|
+
<thead className="border-b border-zinc-200 text-xs uppercase text-zinc-500 dark:border-zinc-800">
|
|
3362
|
+
<tr>
|
|
3363
|
+
<th className="py-2 pr-3 font-medium">
|
|
3364
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3365
|
+
<span>
|
|
3366
|
+
{t.workspaceSnapshotColLang}
|
|
3367
|
+
</span>
|
|
3368
|
+
<InlineMetricHelpTrigger
|
|
3369
|
+
ariaLabel={
|
|
3370
|
+
t.metricHelpWsColLangAria
|
|
3371
|
+
}
|
|
3372
|
+
body={
|
|
3373
|
+
t.metricHelpWsColLangBody
|
|
3374
|
+
}
|
|
3375
|
+
/>
|
|
3376
|
+
</div>
|
|
3377
|
+
</th>
|
|
3378
|
+
<th className="py-2 pr-3 font-medium tabular-nums">
|
|
3379
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3380
|
+
<span>
|
|
3381
|
+
{t.workspaceSnapshotColLines}
|
|
3382
|
+
</span>
|
|
3383
|
+
<InlineMetricHelpTrigger
|
|
3384
|
+
ariaLabel={
|
|
3385
|
+
t.metricHelpWsColLinesAria
|
|
3386
|
+
}
|
|
3387
|
+
body={
|
|
3388
|
+
t.metricHelpWsColLinesBody
|
|
3389
|
+
}
|
|
3390
|
+
/>
|
|
3391
|
+
</div>
|
|
3392
|
+
</th>
|
|
3393
|
+
<th className="py-2 font-medium">
|
|
3394
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3395
|
+
<span>
|
|
3396
|
+
{
|
|
3397
|
+
t.workspaceSnapshotColPercent
|
|
3398
|
+
}
|
|
3399
|
+
</span>
|
|
3400
|
+
<InlineMetricHelpTrigger
|
|
3401
|
+
align="end"
|
|
3402
|
+
ariaLabel={
|
|
3403
|
+
t.metricHelpWsColPercentAria
|
|
3404
|
+
}
|
|
3405
|
+
body={
|
|
3406
|
+
t.metricHelpWsColPercentBody
|
|
3407
|
+
}
|
|
3408
|
+
/>
|
|
3409
|
+
</div>
|
|
3410
|
+
</th>
|
|
3411
|
+
</tr>
|
|
3412
|
+
</thead>
|
|
3413
|
+
<tbody>
|
|
3414
|
+
{ws.byLanguage.map((row) => (
|
|
3415
|
+
<tr
|
|
3416
|
+
key={row.languageId}
|
|
3417
|
+
className="border-b border-zinc-200/90 last:border-0 dark:border-zinc-800/80"
|
|
3418
|
+
>
|
|
3419
|
+
<td className="py-2 pr-3 font-mono text-xs text-zinc-400">
|
|
3420
|
+
{row.languageId}
|
|
3421
|
+
</td>
|
|
3422
|
+
<td className="py-2 pr-3 tabular-nums text-zinc-800 dark:text-zinc-200">
|
|
3423
|
+
{row.lines}
|
|
3424
|
+
</td>
|
|
3425
|
+
<td className="py-2">
|
|
3426
|
+
<div className="flex items-center gap-2">
|
|
3427
|
+
<div className="h-2 min-w-[4rem] flex-1 overflow-hidden rounded bg-zinc-200 dark:bg-zinc-800">
|
|
3428
|
+
<div
|
|
3429
|
+
className="h-full rounded bg-sky-600/85"
|
|
3430
|
+
style={{
|
|
3431
|
+
width: `${Math.min(
|
|
3432
|
+
100,
|
|
3433
|
+
row.percent,
|
|
3434
|
+
)}%`,
|
|
3435
|
+
}}
|
|
3436
|
+
/>
|
|
3437
|
+
</div>
|
|
3438
|
+
<span className="w-14 shrink-0 tabular-nums text-zinc-400">
|
|
3439
|
+
{row.percent.toFixed(1)}%
|
|
3440
|
+
</span>
|
|
3441
|
+
</div>
|
|
3442
|
+
</td>
|
|
3443
|
+
</tr>
|
|
3444
|
+
))}
|
|
3445
|
+
</tbody>
|
|
3446
|
+
</table>
|
|
3447
|
+
</div>
|
|
3448
|
+
)}
|
|
3449
|
+
</>
|
|
3450
|
+
);
|
|
3451
|
+
}
|
|
3452
|
+
return (
|
|
3453
|
+
<p className="mt-4 text-sm text-amber-800 dark:text-amber-200/90">
|
|
3454
|
+
{ws.reason === "no_workspace"
|
|
3455
|
+
? t.workspaceSnapshotNoWorkspace
|
|
3456
|
+
: ws.reason === "empty"
|
|
3457
|
+
? ws.message || t.workspaceSnapshotEmpty
|
|
3458
|
+
: ws.message || t.workspaceSnapshotError}
|
|
3459
|
+
</p>
|
|
3460
|
+
);
|
|
3461
|
+
})()}
|
|
3462
|
+
</section>
|
|
3463
|
+
</>
|
|
3464
|
+
) : null}
|
|
3465
|
+
<section
|
|
3466
|
+
id="report-summary-kpis"
|
|
3467
|
+
className="mb-10 scroll-mt-28 flex flex-col gap-3"
|
|
3468
|
+
>
|
|
3469
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
3470
|
+
<h2 className="text-sm font-semibold uppercase tracking-wide text-zinc-600 dark:text-zinc-400">
|
|
3471
|
+
{t.tocSummaryKpis}
|
|
3472
|
+
</h2>
|
|
3473
|
+
<ReportingFilteredBadge
|
|
3474
|
+
active={reportingFiltersActive}
|
|
3475
|
+
label={t.sectionFilteredBadge}
|
|
3476
|
+
titleText={t.sectionFilteredBadgeTitle}
|
|
3477
|
+
/>
|
|
3478
|
+
</div>
|
|
3479
|
+
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
|
|
3480
|
+
<div className="rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4">
|
|
3481
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3482
|
+
<span className="text-xs uppercase text-zinc-500">
|
|
3483
|
+
{t.summarySessionsInRange}
|
|
3484
|
+
</span>
|
|
3485
|
+
<InlineMetricHelpTrigger
|
|
3486
|
+
ariaLabel={t.metricHelpSessionsAria}
|
|
3487
|
+
body={t.metricHelpSessionsBody}
|
|
3488
|
+
/>
|
|
3489
|
+
</div>
|
|
3490
|
+
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
3491
|
+
{agg.sessionCountContributing}
|
|
3492
|
+
</div>
|
|
3493
|
+
</div>
|
|
3494
|
+
<div className="rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4">
|
|
3495
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3496
|
+
<span className="text-xs uppercase text-zinc-500">
|
|
3497
|
+
{t.summaryTaskEvents}
|
|
3498
|
+
</span>
|
|
3499
|
+
<InlineMetricHelpTrigger
|
|
3500
|
+
ariaLabel={t.metricHelpTaskRowsAria}
|
|
3501
|
+
body={t.metricHelpTaskRowsBody}
|
|
3502
|
+
/>
|
|
3503
|
+
</div>
|
|
3504
|
+
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
3505
|
+
{agg.taskCountContributing}
|
|
3506
|
+
</div>
|
|
3507
|
+
</div>
|
|
3508
|
+
<div className="rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4">
|
|
3509
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3510
|
+
<span className="text-xs uppercase text-zinc-500">
|
|
3511
|
+
{t.summaryKronoFocusCompleted}
|
|
3512
|
+
</span>
|
|
3513
|
+
<InlineMetricHelpTrigger
|
|
3514
|
+
align="end"
|
|
3515
|
+
ariaLabel={t.metricHelpKronoFocusCompletedAria}
|
|
3516
|
+
body={t.metricHelpKronoFocusCompletedBody}
|
|
3517
|
+
/>
|
|
3518
|
+
</div>
|
|
3519
|
+
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
3520
|
+
{agg.kronoFocusSessionsCompleted}
|
|
3521
|
+
</div>
|
|
3522
|
+
</div>
|
|
3523
|
+
<div className="rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4">
|
|
3524
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3525
|
+
<span className="text-xs uppercase text-zinc-500">
|
|
3526
|
+
{t.summaryKronoFocusTasksUsed}
|
|
3527
|
+
</span>
|
|
3528
|
+
<InlineMetricHelpTrigger
|
|
3529
|
+
ariaLabel={t.metricHelpKronoFocusTasksUsedAria}
|
|
3530
|
+
body={t.metricHelpKronoFocusTasksUsedBody}
|
|
3531
|
+
/>
|
|
3532
|
+
</div>
|
|
3533
|
+
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
3534
|
+
{agg.kronoFocusTasksUsedCount}
|
|
3535
|
+
</div>
|
|
3536
|
+
</div>
|
|
3537
|
+
<div className="rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4">
|
|
3538
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3539
|
+
<span className="text-xs uppercase text-zinc-500">
|
|
3540
|
+
{t.summaryKronoFocusCycles}
|
|
3541
|
+
</span>
|
|
3542
|
+
<InlineMetricHelpTrigger
|
|
3543
|
+
ariaLabel={t.metricHelpKronoFocusCyclesAria}
|
|
3544
|
+
body={t.metricHelpKronoFocusCyclesBody}
|
|
3545
|
+
/>
|
|
3546
|
+
</div>
|
|
3547
|
+
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
3548
|
+
{agg.kronoFocusTaskCyclesSum}
|
|
3549
|
+
</div>
|
|
3550
|
+
</div>
|
|
3551
|
+
<div className="rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4">
|
|
3552
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3553
|
+
<span className="text-xs uppercase text-zinc-500">
|
|
3554
|
+
{t.summaryTaskTimeRecorded}
|
|
3555
|
+
</span>
|
|
3556
|
+
<InlineMetricHelpTrigger
|
|
3557
|
+
align="end"
|
|
3558
|
+
ariaLabel={t.metricHelpTaskTimeAria}
|
|
3559
|
+
body={t.metricHelpTaskTimeBody}
|
|
3560
|
+
/>
|
|
3561
|
+
</div>
|
|
3562
|
+
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
3563
|
+
{formatDuration(agg.taskMinutesTotal)}
|
|
3564
|
+
</div>
|
|
3565
|
+
</div>
|
|
3566
|
+
<div className="rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4">
|
|
3567
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3568
|
+
<span className="text-xs uppercase text-zinc-500">
|
|
3569
|
+
{t.summarySessionCoding}
|
|
3570
|
+
</span>
|
|
3571
|
+
<InlineMetricHelpTrigger
|
|
3572
|
+
ariaLabel={t.metricHelpSessionCodingAria}
|
|
3573
|
+
body={t.metricHelpSessionCodingBody}
|
|
3574
|
+
/>
|
|
3575
|
+
</div>
|
|
3576
|
+
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
3577
|
+
{formatDuration(agg.sessionCodingMinutesTotal)}
|
|
3578
|
+
</div>
|
|
3579
|
+
</div>
|
|
3580
|
+
<div className="rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4">
|
|
3581
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3582
|
+
<span className="text-xs uppercase text-zinc-500">
|
|
3583
|
+
{t.summarySessionActive}
|
|
3584
|
+
</span>
|
|
3585
|
+
<InlineMetricHelpTrigger
|
|
3586
|
+
ariaLabel={t.metricHelpSessionActiveAria}
|
|
3587
|
+
body={t.metricHelpSessionActiveBody}
|
|
3588
|
+
/>
|
|
3589
|
+
</div>
|
|
3590
|
+
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
3591
|
+
{formatDuration(agg.sessionActiveMinutesTotal)}
|
|
3592
|
+
</div>
|
|
3593
|
+
</div>
|
|
3594
|
+
<div className="rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4">
|
|
3595
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3596
|
+
<span className="text-xs uppercase text-zinc-500">
|
|
3597
|
+
{t.summarySessionWallClock}
|
|
3598
|
+
</span>
|
|
3599
|
+
<InlineMetricHelpTrigger
|
|
3600
|
+
align="end"
|
|
3601
|
+
ariaLabel={t.metricHelpSessionWallSummaryAria}
|
|
3602
|
+
body={t.metricHelpSessionWallSummaryBody}
|
|
3603
|
+
/>
|
|
3604
|
+
</div>
|
|
3605
|
+
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
3606
|
+
{formatDuration(agg.sessionWallClockMinutesTotal)}
|
|
3607
|
+
</div>
|
|
3608
|
+
</div>
|
|
3609
|
+
<div className="rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4">
|
|
3610
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3611
|
+
<span className="text-xs uppercase text-zinc-500">
|
|
3612
|
+
{t.summaryAssiduityWithReference}
|
|
3613
|
+
</span>
|
|
3614
|
+
<InlineMetricHelpTrigger
|
|
3615
|
+
align="end"
|
|
3616
|
+
ariaLabel={t.metricHelpAssiduityRefAria}
|
|
3617
|
+
body={t.metricHelpAssiduityRefBody}
|
|
3618
|
+
/>
|
|
3619
|
+
</div>
|
|
3620
|
+
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
3621
|
+
{agg.assiduityReferenceSessionCount}
|
|
3622
|
+
</div>
|
|
3623
|
+
</div>
|
|
3624
|
+
<div className="rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4">
|
|
3625
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3626
|
+
<span className="text-xs uppercase text-zinc-500">
|
|
3627
|
+
{t.summaryAssiduityLateSessions}
|
|
3628
|
+
</span>
|
|
3629
|
+
<InlineMetricHelpTrigger
|
|
3630
|
+
align="end"
|
|
3631
|
+
ariaLabel={t.metricHelpAssiduityLateCountAria}
|
|
3632
|
+
body={t.metricHelpAssiduityLateCountBody}
|
|
3633
|
+
/>
|
|
3634
|
+
</div>
|
|
3635
|
+
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
3636
|
+
{agg.assiduityLateSessionCount}
|
|
3637
|
+
</div>
|
|
3638
|
+
</div>
|
|
3639
|
+
<div className="rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4">
|
|
3640
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3641
|
+
<span className="text-xs uppercase text-zinc-500">
|
|
3642
|
+
{t.summaryAssiduityLateTotal}
|
|
3643
|
+
</span>
|
|
3644
|
+
<InlineMetricHelpTrigger
|
|
3645
|
+
align="end"
|
|
3646
|
+
ariaLabel={t.metricHelpAssiduityLateTotalAria}
|
|
3647
|
+
body={t.metricHelpAssiduityLateTotalBody}
|
|
3648
|
+
/>
|
|
3649
|
+
</div>
|
|
3650
|
+
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
3651
|
+
{formatDuration(agg.assiduityLateMinutesTotal)}
|
|
3652
|
+
</div>
|
|
3653
|
+
</div>
|
|
3654
|
+
<div className="rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4">
|
|
3655
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3656
|
+
<span className="text-xs uppercase text-zinc-500">
|
|
3657
|
+
{t.summaryAssiduityAvgLateWhenLate}
|
|
3658
|
+
</span>
|
|
3659
|
+
<InlineMetricHelpTrigger
|
|
3660
|
+
align="end"
|
|
3661
|
+
ariaLabel={t.metricHelpAssiduityAvgLateAria}
|
|
3662
|
+
body={t.metricHelpAssiduityAvgLateBody}
|
|
3663
|
+
/>
|
|
3664
|
+
</div>
|
|
3665
|
+
<div className="mt-1 text-2xl font-semibold tabular-nums">
|
|
3666
|
+
{agg.assiduityAverageLateMinutesWhenLate == null
|
|
3667
|
+
? "—"
|
|
3668
|
+
: formatDuration(
|
|
3669
|
+
agg.assiduityAverageLateMinutesWhenLate,
|
|
3670
|
+
)}
|
|
3671
|
+
</div>
|
|
3672
|
+
</div>
|
|
3673
|
+
</div>
|
|
3674
|
+
<div
|
|
3675
|
+
id="report-closure-by-kind"
|
|
3676
|
+
className="mt-4 rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4"
|
|
3677
|
+
>
|
|
3678
|
+
<div className="mb-3 flex flex-wrap items-center gap-2">
|
|
3679
|
+
<h3 className="text-xs font-semibold uppercase tracking-wide text-zinc-500">
|
|
3680
|
+
{t.closureBreakdownTitle}
|
|
3681
|
+
</h3>
|
|
3682
|
+
<ReportingFilteredBadge
|
|
3683
|
+
active={reportingFiltersActive}
|
|
3684
|
+
label={t.sectionFilteredBadge}
|
|
3685
|
+
titleText={t.sectionFilteredBadgeTitle}
|
|
3686
|
+
/>
|
|
3687
|
+
<InlineMetricHelpTrigger
|
|
3688
|
+
ariaLabel={t.metricHelpClosureBreakdownAria}
|
|
3689
|
+
body={t.metricHelpClosureBreakdownBody}
|
|
3690
|
+
/>
|
|
3691
|
+
</div>
|
|
3692
|
+
<div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-5">
|
|
3693
|
+
{REPORTING_SESSION_CLOSURE_DISPLAY_ORDER.map(
|
|
3694
|
+
(closureKey) => {
|
|
3695
|
+
const count =
|
|
3696
|
+
agg.sessionCountByClosureKind[closureKey] ?? 0;
|
|
3697
|
+
return (
|
|
3698
|
+
<div
|
|
3699
|
+
key={closureKey}
|
|
3700
|
+
className="rounded-lg border border-zinc-200/80 bg-zinc-100/70 dark:border-zinc-800/80 dark:bg-zinc-950/40 p-3"
|
|
3701
|
+
>
|
|
3702
|
+
<div className="text-xs uppercase text-zinc-500">
|
|
3703
|
+
{reportingClosureKindLabels[closureKey]}
|
|
3704
|
+
</div>
|
|
3705
|
+
<div className="mt-1 text-xl font-semibold tabular-nums text-zinc-900 dark:text-zinc-100">
|
|
3706
|
+
{count}
|
|
3707
|
+
</div>
|
|
3708
|
+
</div>
|
|
3709
|
+
);
|
|
3710
|
+
},
|
|
3711
|
+
)}
|
|
3712
|
+
</div>
|
|
3713
|
+
</div>
|
|
3714
|
+
</section>
|
|
3715
|
+
{trackCodeMetrics ? (
|
|
3204
3716
|
<section
|
|
3205
|
-
id="report-
|
|
3206
|
-
className="
|
|
3717
|
+
id="report-loc-metrics"
|
|
3718
|
+
className="mb-10 scroll-mt-28 space-y-4 rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none p-4 sm:p-6"
|
|
3207
3719
|
>
|
|
3208
|
-
<div className="flex
|
|
3209
|
-
<
|
|
3210
|
-
{t.
|
|
3211
|
-
</
|
|
3720
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
3721
|
+
<h2 className="text-sm font-semibold uppercase tracking-wide text-zinc-600 dark:text-zinc-400">
|
|
3722
|
+
{t.tocLocSection}
|
|
3723
|
+
</h2>
|
|
3212
3724
|
<ReportingFilteredBadge
|
|
3213
3725
|
active={reportingFiltersActive}
|
|
3214
3726
|
label={t.sectionFilteredBadge}
|
|
3215
3727
|
titleText={t.sectionFilteredBadgeTitle}
|
|
3216
3728
|
/>
|
|
3217
|
-
<InlineMetricHelpTrigger
|
|
3218
|
-
ariaLabel={t.metricHelpProjectTitleAria}
|
|
3219
|
-
body={t.metricHelpProjectTitleBody}
|
|
3220
|
-
/>
|
|
3221
3729
|
</div>
|
|
3222
3730
|
<p className="text-xs text-zinc-500">
|
|
3223
|
-
{t.
|
|
3731
|
+
{t.locMetricsHint}
|
|
3224
3732
|
</p>
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3733
|
+
<div className="grid gap-3 sm:grid-cols-3">
|
|
3734
|
+
<div className="rounded-lg border border-zinc-200/80 bg-zinc-100/70 dark:border-zinc-800/80 dark:bg-zinc-950/40 p-3">
|
|
3735
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3736
|
+
<span className="text-xs uppercase text-zinc-500">
|
|
3737
|
+
{t.summaryLinesWrittenTotal}
|
|
3738
|
+
</span>
|
|
3739
|
+
<InlineMetricHelpTrigger
|
|
3740
|
+
ariaLabel={t.metricHelpLinesTotalAria}
|
|
3741
|
+
body={t.metricHelpLinesTotalBody}
|
|
3742
|
+
/>
|
|
3743
|
+
</div>
|
|
3744
|
+
<div className="mt-1 text-xl font-semibold tabular-nums text-zinc-900 dark:text-zinc-100">
|
|
3745
|
+
{agg.linesWrittenTotalSum}
|
|
3746
|
+
</div>
|
|
3747
|
+
</div>
|
|
3748
|
+
<div className="rounded-lg border border-zinc-200/80 bg-zinc-100/70 dark:border-zinc-800/80 dark:bg-zinc-950/40 p-3">
|
|
3749
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3750
|
+
<span className="text-xs uppercase text-zinc-500">
|
|
3751
|
+
{t.summaryLinesWrittenHuman}
|
|
3752
|
+
</span>
|
|
3753
|
+
<InlineMetricHelpTrigger
|
|
3754
|
+
ariaLabel={t.metricHelpLinesHumanAria}
|
|
3755
|
+
body={t.metricHelpLinesHumanBody}
|
|
3756
|
+
/>
|
|
3757
|
+
</div>
|
|
3758
|
+
<div className="mt-1 text-xl font-semibold tabular-nums text-emerald-400/90">
|
|
3759
|
+
{agg.linesWrittenHumanSum}
|
|
3760
|
+
</div>
|
|
3761
|
+
</div>
|
|
3762
|
+
<div className="rounded-lg border border-zinc-200/80 bg-zinc-100/70 dark:border-zinc-800/80 dark:bg-zinc-950/40 p-3">
|
|
3763
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3764
|
+
<span className="text-xs uppercase text-zinc-500">
|
|
3765
|
+
{t.summaryLinesWrittenAi}
|
|
3766
|
+
</span>
|
|
3767
|
+
<InlineMetricHelpTrigger
|
|
3768
|
+
align="end"
|
|
3769
|
+
ariaLabel={t.metricHelpLinesAiAria}
|
|
3770
|
+
body={t.metricHelpLinesAiBody}
|
|
3771
|
+
/>
|
|
3772
|
+
</div>
|
|
3773
|
+
<div className="mt-1 text-xl font-semibold tabular-nums text-violet-400/90">
|
|
3774
|
+
{agg.linesWrittenAiSum}
|
|
3775
|
+
</div>
|
|
3776
|
+
</div>
|
|
3234
3777
|
</div>
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
{
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3778
|
+
<div className="grid gap-6 lg:grid-cols-2">
|
|
3779
|
+
<div>
|
|
3780
|
+
<div className="flex min-h-6 flex-wrap items-center gap-x-2 gap-y-1">
|
|
3781
|
+
<h3 className="text-sm font-semibold text-zinc-900 dark:text-zinc-200">
|
|
3782
|
+
{t.locByLanguageSectionTitle}
|
|
3783
|
+
</h3>
|
|
3784
|
+
<ReportingFilteredBadge
|
|
3785
|
+
active={reportingFiltersActive}
|
|
3786
|
+
label={t.sectionFilteredBadge}
|
|
3787
|
+
titleText={t.sectionFilteredBadgeTitle}
|
|
3788
|
+
/>
|
|
3789
|
+
<InlineMetricHelpTrigger
|
|
3790
|
+
ariaLabel={t.metricHelpLocByLangTitleAria}
|
|
3791
|
+
body={t.metricHelpLocByLangTitleBody}
|
|
3792
|
+
/>
|
|
3793
|
+
</div>
|
|
3794
|
+
{agg.locByLanguageMerged.length === 0 ? (
|
|
3795
|
+
<p className="mt-3 text-sm text-zinc-500">—</p>
|
|
3796
|
+
) : (
|
|
3797
|
+
<table className="mt-3 w-full min-w-[12rem] text-left text-sm">
|
|
3798
|
+
<thead className="border-b border-zinc-200 text-xs uppercase text-zinc-500 dark:border-zinc-800">
|
|
3799
|
+
<tr>
|
|
3800
|
+
<th className="py-2 pr-3 font-medium">
|
|
3801
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3802
|
+
<span>{t.locByLanguageColLang}</span>
|
|
3803
|
+
<InlineMetricHelpTrigger
|
|
3804
|
+
ariaLabel={
|
|
3805
|
+
t.metricHelpAggLocColLangAria
|
|
3806
|
+
}
|
|
3807
|
+
body={t.metricHelpAggLocColLangBody}
|
|
3808
|
+
/>
|
|
3809
|
+
</div>
|
|
3810
|
+
</th>
|
|
3811
|
+
<th className="py-2 font-medium tabular-nums">
|
|
3812
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3813
|
+
<span>{t.locByLanguageColLines}</span>
|
|
3814
|
+
<InlineMetricHelpTrigger
|
|
3815
|
+
align="end"
|
|
3816
|
+
ariaLabel={
|
|
3817
|
+
t.metricHelpAggLocColLinesAria
|
|
3818
|
+
}
|
|
3819
|
+
body={t.metricHelpAggLocColLinesBody}
|
|
3820
|
+
/>
|
|
3821
|
+
</div>
|
|
3822
|
+
</th>
|
|
3823
|
+
</tr>
|
|
3824
|
+
</thead>
|
|
3825
|
+
<tbody>
|
|
3826
|
+
{agg.locByLanguageMerged.map(([lang, n]) => (
|
|
3827
|
+
<tr
|
|
3828
|
+
key={lang}
|
|
3829
|
+
className="border-b border-zinc-200/90 last:border-0 dark:border-zinc-800/80"
|
|
3830
|
+
>
|
|
3831
|
+
<td className="py-2 pr-3 font-mono text-xs text-zinc-400">
|
|
3832
|
+
{lang}
|
|
3833
|
+
</td>
|
|
3834
|
+
<td className="py-2 tabular-nums text-zinc-800 dark:text-zinc-200">
|
|
3835
|
+
{n}
|
|
3836
|
+
</td>
|
|
3837
|
+
</tr>
|
|
3838
|
+
))}
|
|
3839
|
+
</tbody>
|
|
3840
|
+
</table>
|
|
3841
|
+
)}
|
|
3249
3842
|
</div>
|
|
3250
|
-
|
|
3251
|
-
|
|
3252
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
3255
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
|
|
3268
|
-
|
|
3269
|
-
|
|
3270
|
-
|
|
3271
|
-
|
|
3272
|
-
|
|
3273
|
-
|
|
3274
|
-
|
|
3275
|
-
|
|
3276
|
-
|
|
3277
|
-
|
|
3278
|
-
|
|
3279
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
3286
|
-
|
|
3287
|
-
|
|
3288
|
-
|
|
3289
|
-
|
|
3290
|
-
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
/>
|
|
3315
|
-
</div>
|
|
3316
|
-
</th>
|
|
3317
|
-
</tr>
|
|
3318
|
-
</thead>
|
|
3319
|
-
<tbody>
|
|
3320
|
-
{rows.map((row) => {
|
|
3321
|
-
const projectKeyLower =
|
|
3322
|
-
normalizeProjectKey(
|
|
3323
|
-
row.projectKey,
|
|
3324
|
-
).toLowerCase();
|
|
3325
|
-
const breakdownKey = `${weekStart}:::${projectKeyLower}`;
|
|
3326
|
-
const childTagRows =
|
|
3327
|
-
projectScopedTagRowsByWeekProject.get(
|
|
3328
|
-
breakdownKey,
|
|
3329
|
-
) ?? [];
|
|
3330
|
-
const canExpand =
|
|
3331
|
-
childTagRows.length > 0;
|
|
3332
|
-
const isOpen =
|
|
3333
|
-
canExpand &&
|
|
3334
|
-
projectWeekTagBreakdownOpenKeys.has(
|
|
3335
|
-
breakdownKey,
|
|
3336
|
-
);
|
|
3337
|
-
const rowTitle =
|
|
3338
|
-
row.displayProject ||
|
|
3339
|
-
row.projectKey;
|
|
3340
|
-
const rowProjectDescription =
|
|
3341
|
-
reportingProjectDescriptionLine(
|
|
3342
|
-
row.projectKey,
|
|
3343
|
-
projectDescriptions,
|
|
3344
|
-
) ?? "";
|
|
3345
|
-
return (
|
|
3346
|
-
<Fragment
|
|
3347
|
-
key={`${weekStart}\0${row.projectKey}`}
|
|
3348
|
-
>
|
|
3349
|
-
<tr className="border-b border-zinc-800/70 last:border-0">
|
|
3350
|
-
{canExpand ? (
|
|
3351
|
-
<td className="align-top px-2 py-2 pl-3 text-sky-200/90">
|
|
3352
|
-
<div className="flex items-start gap-1.5">
|
|
3353
|
-
<button
|
|
3354
|
-
type="button"
|
|
3355
|
-
aria-expanded={isOpen}
|
|
3356
|
-
aria-label={
|
|
3357
|
-
t.tagWeekScopedRollupToggleAria
|
|
3358
|
-
}
|
|
3359
|
-
className="mt-0.5 shrink-0 rounded p-0.5 text-zinc-400 transition hover:bg-zinc-800 hover:text-zinc-200"
|
|
3360
|
-
onClick={() =>
|
|
3361
|
-
toggleProjectWeekTagBreakdown(
|
|
3362
|
-
breakdownKey,
|
|
3363
|
-
)
|
|
3364
|
-
}
|
|
3365
|
-
>
|
|
3366
|
-
<ChevronRight
|
|
3367
|
-
className={`size-4 transition-transform duration-200 ${
|
|
3368
|
-
isOpen
|
|
3369
|
-
? "rotate-90"
|
|
3370
|
-
: ""
|
|
3371
|
-
}`}
|
|
3372
|
-
strokeWidth={2}
|
|
3373
|
-
aria-hidden
|
|
3374
|
-
/>
|
|
3375
|
-
</button>
|
|
3376
|
-
<div className="min-w-0">
|
|
3377
|
-
<div>
|
|
3378
|
-
{row.projectKey === ""
|
|
3379
|
-
? t.projectUnassigned
|
|
3380
|
-
: row.displayProject ||
|
|
3381
|
-
row.projectKey}
|
|
3382
|
-
</div>
|
|
3383
|
-
{rowProjectDescription ? (
|
|
3384
|
-
<p className="mt-1 max-w-[min(22rem,55vw)] whitespace-pre-line text-[0.65rem] font-normal leading-snug text-zinc-500">
|
|
3385
|
-
{
|
|
3386
|
-
rowProjectDescription
|
|
3387
|
-
}
|
|
3388
|
-
</p>
|
|
3389
|
-
) : null}
|
|
3390
|
-
<p className="mt-1 text-[0.65rem] font-normal tabular-nums text-zinc-500">
|
|
3391
|
-
{childTagRows.length}{" "}
|
|
3392
|
-
{lang === "fr"
|
|
3393
|
-
? "étiquette(s) liée(s)"
|
|
3394
|
-
: "linked tag(s)"}
|
|
3395
|
-
</p>
|
|
3396
|
-
</div>
|
|
3397
|
-
</div>
|
|
3398
|
-
</td>
|
|
3399
|
-
) : (
|
|
3400
|
-
<ReportingProjectNameCell
|
|
3401
|
-
projectKey={row.projectKey}
|
|
3402
|
-
displayLabel={
|
|
3403
|
-
row.displayProject ||
|
|
3404
|
-
row.projectKey
|
|
3405
|
-
}
|
|
3406
|
-
unassignedLabel={
|
|
3407
|
-
t.projectUnassigned
|
|
3408
|
-
}
|
|
3409
|
-
descriptions={
|
|
3410
|
-
projectDescriptions
|
|
3411
|
-
}
|
|
3412
|
-
className="px-2 py-2 pl-3 text-sky-200/90"
|
|
3413
|
-
/>
|
|
3414
|
-
)}
|
|
3415
|
-
{row.slots.map((mins, i) => {
|
|
3416
|
-
const dateKey = addDaysYmd(
|
|
3417
|
-
row.weekStart,
|
|
3418
|
-
i,
|
|
3419
|
-
);
|
|
3420
|
-
return (
|
|
3421
|
-
<td
|
|
3422
|
-
key={dateKey}
|
|
3423
|
-
className="px-1 py-2 text-center tabular-nums text-zinc-200"
|
|
3424
|
-
title={dateKey}
|
|
3425
|
-
>
|
|
3426
|
-
{mins > 0
|
|
3427
|
-
? renderReportingDurationButton(
|
|
3428
|
-
mins,
|
|
3429
|
-
{
|
|
3430
|
-
kind: "project",
|
|
3431
|
-
title: rowTitle,
|
|
3432
|
-
dayKey: dateKey,
|
|
3433
|
-
projectKey:
|
|
3434
|
-
row.projectKey,
|
|
3435
|
-
sourceLabel:
|
|
3436
|
-
dayLabel(
|
|
3437
|
-
dateKey,
|
|
3438
|
-
t.undatedLabel,
|
|
3439
|
-
),
|
|
3440
|
-
},
|
|
3441
|
-
"text-zinc-200",
|
|
3442
|
-
)
|
|
3443
|
-
: "—"}
|
|
3444
|
-
</td>
|
|
3445
|
-
);
|
|
3446
|
-
})}
|
|
3447
|
-
<td className="px-2 py-2 pr-3 text-center tabular-nums font-semibold text-zinc-100">
|
|
3448
|
-
{renderReportingDurationButton(
|
|
3449
|
-
row.total,
|
|
3450
|
-
{
|
|
3451
|
-
kind: "project",
|
|
3452
|
-
title: rowTitle,
|
|
3453
|
-
weekStart: row.weekStart,
|
|
3454
|
-
projectKey:
|
|
3455
|
-
row.projectKey,
|
|
3456
|
-
sourceLabel:
|
|
3457
|
-
weekRangeLabelForSource(
|
|
3458
|
-
row.weekStart,
|
|
3459
|
-
lang,
|
|
3460
|
-
reportLocale,
|
|
3461
|
-
),
|
|
3462
|
-
},
|
|
3463
|
-
"text-zinc-100",
|
|
3464
|
-
)}
|
|
3465
|
-
</td>
|
|
3466
|
-
</tr>
|
|
3467
|
-
{isOpen
|
|
3468
|
-
? childTagRows.map((child) => (
|
|
3469
|
-
<tr
|
|
3470
|
-
key={`${weekStart}\0${row.projectKey}\0${child.tagKey}`}
|
|
3471
|
-
className="border-b border-zinc-800/55 bg-zinc-950/30"
|
|
3472
|
-
>
|
|
3473
|
-
<ReportingTagNameCell
|
|
3474
|
-
tagKey={child.tagKey}
|
|
3475
|
-
displayLabel={
|
|
3476
|
-
child.displayTag ||
|
|
3477
|
-
child.tagKey
|
|
3478
|
-
}
|
|
3479
|
-
untaggedLabel={
|
|
3480
|
-
t.tagTimeUntagged
|
|
3481
|
-
}
|
|
3482
|
-
descriptions={
|
|
3483
|
-
tagDescriptions
|
|
3484
|
-
}
|
|
3485
|
-
className="px-2 py-2 pl-10 text-violet-200/85"
|
|
3486
|
-
/>
|
|
3487
|
-
{child.slots.map(
|
|
3488
|
-
(mins, i) => {
|
|
3489
|
-
const dateKey =
|
|
3490
|
-
addDaysYmd(
|
|
3491
|
-
child.weekStart,
|
|
3492
|
-
i,
|
|
3493
|
-
);
|
|
3494
|
-
return (
|
|
3495
|
-
<td
|
|
3496
|
-
key={dateKey}
|
|
3497
|
-
className="px-1 py-2 text-center tabular-nums text-zinc-300/95"
|
|
3498
|
-
title={dateKey}
|
|
3499
|
-
>
|
|
3500
|
-
{mins > 0
|
|
3501
|
-
? renderReportingDurationButton(
|
|
3502
|
-
mins,
|
|
3503
|
-
{
|
|
3504
|
-
kind: "tag",
|
|
3505
|
-
title:
|
|
3506
|
-
child.displayTag ||
|
|
3507
|
-
child.tagKey,
|
|
3508
|
-
dayKey:
|
|
3509
|
-
dateKey,
|
|
3510
|
-
tagKey:
|
|
3511
|
-
child.tagKey,
|
|
3512
|
-
sourceLabel:
|
|
3513
|
-
dayLabel(
|
|
3514
|
-
dateKey,
|
|
3515
|
-
t.undatedLabel,
|
|
3516
|
-
),
|
|
3517
|
-
},
|
|
3518
|
-
"text-zinc-300/95",
|
|
3519
|
-
)
|
|
3520
|
-
: "—"}
|
|
3521
|
-
</td>
|
|
3522
|
-
);
|
|
3523
|
-
},
|
|
3524
|
-
)}
|
|
3525
|
-
<td className="px-2 py-2 pr-3 text-center tabular-nums font-medium text-zinc-200">
|
|
3526
|
-
{renderReportingDurationButton(
|
|
3527
|
-
child.total,
|
|
3528
|
-
{
|
|
3529
|
-
kind: "tag",
|
|
3530
|
-
title:
|
|
3531
|
-
child.displayTag ||
|
|
3532
|
-
child.tagKey,
|
|
3533
|
-
weekStart:
|
|
3534
|
-
child.weekStart,
|
|
3535
|
-
tagKey:
|
|
3536
|
-
child.tagKey,
|
|
3537
|
-
sourceLabel:
|
|
3538
|
-
weekRangeLabelForSource(
|
|
3539
|
-
child.weekStart,
|
|
3540
|
-
lang,
|
|
3541
|
-
reportLocale,
|
|
3542
|
-
),
|
|
3543
|
-
},
|
|
3544
|
-
"text-zinc-200",
|
|
3545
|
-
)}
|
|
3546
|
-
</td>
|
|
3547
|
-
</tr>
|
|
3548
|
-
))
|
|
3549
|
-
: null}
|
|
3550
|
-
</Fragment>
|
|
3551
|
-
);
|
|
3552
|
-
})}
|
|
3553
|
-
</tbody>
|
|
3554
|
-
</table>
|
|
3555
|
-
</div>
|
|
3556
|
-
</div>
|
|
3557
|
-
),
|
|
3843
|
+
<div>
|
|
3844
|
+
<div className="flex min-h-6 flex-wrap items-center gap-x-2 gap-y-1">
|
|
3845
|
+
<h3 className="text-sm font-semibold text-zinc-900 dark:text-zinc-200">
|
|
3846
|
+
{t.codingSignalsSectionTitle}
|
|
3847
|
+
</h3>
|
|
3848
|
+
<ReportingFilteredBadge
|
|
3849
|
+
active={reportingFiltersActive}
|
|
3850
|
+
label={t.sectionFilteredBadge}
|
|
3851
|
+
titleText={t.sectionFilteredBadgeTitle}
|
|
3852
|
+
/>
|
|
3853
|
+
<InlineMetricHelpTrigger
|
|
3854
|
+
ariaLabel={t.metricHelpCodingSignalsTitleAria}
|
|
3855
|
+
body={t.metricHelpCodingSignalsTitleBody}
|
|
3856
|
+
/>
|
|
3857
|
+
</div>
|
|
3858
|
+
{agg.codingSignalsByLanguageMerged.length === 0 ? (
|
|
3859
|
+
<p className="mt-3 text-sm text-zinc-500">—</p>
|
|
3860
|
+
) : (
|
|
3861
|
+
<table className="mt-3 w-full min-w-[12rem] text-left text-sm">
|
|
3862
|
+
<thead className="border-b border-zinc-200 text-xs uppercase text-zinc-500 dark:border-zinc-800">
|
|
3863
|
+
<tr>
|
|
3864
|
+
<th className="py-2 pr-3 font-medium">
|
|
3865
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3866
|
+
<span>{t.codingSignalsColLang}</span>
|
|
3867
|
+
<InlineMetricHelpTrigger
|
|
3868
|
+
ariaLabel={
|
|
3869
|
+
t.metricHelpAggSigColLangAria
|
|
3870
|
+
}
|
|
3871
|
+
body={t.metricHelpAggSigColLangBody}
|
|
3872
|
+
/>
|
|
3873
|
+
</div>
|
|
3874
|
+
</th>
|
|
3875
|
+
<th className="py-2 font-medium tabular-nums">
|
|
3876
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3877
|
+
<span>{t.codingSignalsColCount}</span>
|
|
3878
|
+
<InlineMetricHelpTrigger
|
|
3879
|
+
align="end"
|
|
3880
|
+
ariaLabel={
|
|
3881
|
+
t.metricHelpAggSigColCountAria
|
|
3882
|
+
}
|
|
3883
|
+
body={t.metricHelpAggSigColCountBody}
|
|
3884
|
+
/>
|
|
3885
|
+
</div>
|
|
3886
|
+
</th>
|
|
3887
|
+
</tr>
|
|
3888
|
+
</thead>
|
|
3889
|
+
<tbody>
|
|
3890
|
+
{agg.codingSignalsByLanguageMerged.map(
|
|
3891
|
+
([lang, n]) => (
|
|
3892
|
+
<tr
|
|
3893
|
+
key={lang}
|
|
3894
|
+
className="border-b border-zinc-200/90 last:border-0 dark:border-zinc-800/80"
|
|
3895
|
+
>
|
|
3896
|
+
<td className="py-2 pr-3 font-mono text-xs text-zinc-400">
|
|
3897
|
+
{lang}
|
|
3898
|
+
</td>
|
|
3899
|
+
<td className="py-2 tabular-nums text-zinc-800 dark:text-zinc-200">
|
|
3900
|
+
{n}
|
|
3901
|
+
</td>
|
|
3902
|
+
</tr>
|
|
3903
|
+
),
|
|
3904
|
+
)}
|
|
3905
|
+
</tbody>
|
|
3906
|
+
</table>
|
|
3558
3907
|
)}
|
|
3559
3908
|
</div>
|
|
3560
|
-
|
|
3909
|
+
</div>
|
|
3561
3910
|
</section>
|
|
3562
|
-
|
|
3563
|
-
|
|
3911
|
+
) : null}
|
|
3912
|
+
{hasReportingChartData ? (
|
|
3913
|
+
<section
|
|
3914
|
+
id="report-daily-table"
|
|
3915
|
+
className="scroll-mt-28 overflow-x-auto rounded-xl border border-zinc-200 bg-white/95 shadow-sm shadow-zinc-900/[0.06] dark:border-zinc-800 dark:bg-zinc-900/50 dark:shadow-none"
|
|
3916
|
+
>
|
|
3917
|
+
<div className="flex flex-wrap items-center gap-2 border-b border-zinc-200 px-4 py-3 dark:border-zinc-800">
|
|
3918
|
+
<h3 className="text-sm font-semibold text-zinc-900 dark:text-zinc-200">
|
|
3919
|
+
{t.tocDailyTable}
|
|
3920
|
+
</h3>
|
|
3921
|
+
<ReportingFilteredBadge
|
|
3922
|
+
active={reportingFiltersActive}
|
|
3923
|
+
label={t.sectionFilteredBadge}
|
|
3924
|
+
titleText={t.sectionFilteredBadgeTitle}
|
|
3925
|
+
/>
|
|
3926
|
+
</div>
|
|
3927
|
+
<table className="w-full min-w-[48rem] text-left text-sm">
|
|
3928
|
+
<thead className="border-b border-zinc-200 text-xs uppercase text-zinc-500 dark:border-zinc-800">
|
|
3929
|
+
<tr>
|
|
3930
|
+
<th className="px-4 py-3 font-medium">
|
|
3931
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3932
|
+
<span>{t.tableDay}</span>
|
|
3933
|
+
<InlineMetricHelpTrigger
|
|
3934
|
+
ariaLabel={t.metricHelpTblDayAria}
|
|
3935
|
+
body={t.metricHelpTblDayBody}
|
|
3936
|
+
/>
|
|
3937
|
+
</div>
|
|
3938
|
+
</th>
|
|
3939
|
+
<th className="px-4 py-3 font-medium tabular-nums">
|
|
3940
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3941
|
+
<span>{t.tableSessions}</span>
|
|
3942
|
+
<InlineMetricHelpTrigger
|
|
3943
|
+
ariaLabel={t.metricHelpTblSessionsAria}
|
|
3944
|
+
body={t.metricHelpTblSessionsBody}
|
|
3945
|
+
/>
|
|
3946
|
+
</div>
|
|
3947
|
+
</th>
|
|
3948
|
+
<th className="px-4 py-3 font-medium tabular-nums">
|
|
3949
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3950
|
+
<span>{t.tableSessionWall}</span>
|
|
3951
|
+
<InlineMetricHelpTrigger
|
|
3952
|
+
ariaLabel={t.metricHelpTblSessionWallAria}
|
|
3953
|
+
body={t.metricHelpTblSessionWallBody}
|
|
3954
|
+
/>
|
|
3955
|
+
</div>
|
|
3956
|
+
</th>
|
|
3957
|
+
<th className="px-4 py-3 font-medium tabular-nums">
|
|
3958
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3959
|
+
<span>{t.tableDone}</span>
|
|
3960
|
+
<InlineMetricHelpTrigger
|
|
3961
|
+
ariaLabel={t.metricHelpTblDoneAria}
|
|
3962
|
+
body={t.metricHelpTblDoneBody}
|
|
3963
|
+
/>
|
|
3964
|
+
</div>
|
|
3965
|
+
</th>
|
|
3966
|
+
<th className="px-4 py-3 font-medium tabular-nums">
|
|
3967
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3968
|
+
<span>{t.tableActive}</span>
|
|
3969
|
+
<InlineMetricHelpTrigger
|
|
3970
|
+
align="end"
|
|
3971
|
+
ariaLabel={t.metricHelpTblActiveAria}
|
|
3972
|
+
body={t.metricHelpTblActiveBody}
|
|
3973
|
+
/>
|
|
3974
|
+
</div>
|
|
3975
|
+
</th>
|
|
3976
|
+
<th className="px-4 py-3 font-medium tabular-nums">
|
|
3977
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3978
|
+
<span>{t.tableTaskTime}</span>
|
|
3979
|
+
<InlineMetricHelpTrigger
|
|
3980
|
+
ariaLabel={t.metricHelpTblTaskTimeAria}
|
|
3981
|
+
body={t.metricHelpTblTaskTimeBody}
|
|
3982
|
+
/>
|
|
3983
|
+
</div>
|
|
3984
|
+
</th>
|
|
3985
|
+
<th className="px-4 py-3 font-medium tabular-nums">
|
|
3986
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3987
|
+
<span>{t.tableTaskTimeNonConcurrent}</span>
|
|
3988
|
+
</div>
|
|
3989
|
+
</th>
|
|
3990
|
+
<th className="px-4 py-3 font-medium tabular-nums">
|
|
3991
|
+
<div className="flex min-h-5 items-center gap-0.5">
|
|
3992
|
+
<span>{t.tableSessionCoding}</span>
|
|
3993
|
+
<InlineMetricHelpTrigger
|
|
3994
|
+
align="end"
|
|
3995
|
+
ariaLabel={t.metricHelpTblSessionCodingAria}
|
|
3996
|
+
body={t.metricHelpTblSessionCodingBody}
|
|
3997
|
+
/>
|
|
3998
|
+
</div>
|
|
3999
|
+
</th>
|
|
4000
|
+
</tr>
|
|
4001
|
+
</thead>
|
|
4002
|
+
<tbody>
|
|
4003
|
+
{reportingDayKeys.map((d) => (
|
|
4004
|
+
<tr
|
|
4005
|
+
key={d}
|
|
4006
|
+
className="border-b border-zinc-200/90 last:border-0 dark:border-zinc-800/80"
|
|
4007
|
+
>
|
|
4008
|
+
<td className="px-4 py-2.5 text-zinc-900 dark:text-zinc-200">
|
|
4009
|
+
{dayLabel(d, t.undatedLabel)}
|
|
4010
|
+
</td>
|
|
4011
|
+
<td className="px-4 py-2.5 tabular-nums text-zinc-700 dark:text-zinc-300">
|
|
4012
|
+
{agg.sessionsByDay[d] ?? 0}
|
|
4013
|
+
</td>
|
|
4014
|
+
<td className="px-4 py-2.5 tabular-nums text-fuchsia-800 dark:text-fuchsia-300/90">
|
|
4015
|
+
{formatMinutesCell(
|
|
4016
|
+
agg.sessionWallClockMinutesByDay[d],
|
|
4017
|
+
)}
|
|
4018
|
+
</td>
|
|
4019
|
+
<td className="px-4 py-2.5 tabular-nums text-emerald-800 dark:text-emerald-400/90">
|
|
4020
|
+
{agg.tasksByDayDone[d] ?? 0}
|
|
4021
|
+
</td>
|
|
4022
|
+
<td className="px-4 py-2.5 tabular-nums text-amber-800 dark:text-amber-400/90">
|
|
4023
|
+
{agg.tasksByDayActive[d] ?? 0}
|
|
4024
|
+
</td>
|
|
4025
|
+
<td className="px-4 py-2.5 tabular-nums text-sky-800 dark:text-sky-300/90">
|
|
4026
|
+
{formatMinutesCell(agg.taskMinutesByDay[d])}
|
|
4027
|
+
</td>
|
|
4028
|
+
<td className="px-4 py-2.5 tabular-nums text-cyan-800 dark:text-cyan-300/90">
|
|
4029
|
+
{formatMinutesCell(
|
|
4030
|
+
agg.nonConcurrentTaskMinutesByDay[d],
|
|
4031
|
+
)}
|
|
4032
|
+
</td>
|
|
4033
|
+
<td className="px-4 py-2.5 tabular-nums text-zinc-700 dark:text-zinc-300">
|
|
4034
|
+
{formatMinutesCell(
|
|
4035
|
+
agg.sessionCodingMinutesByDay[d],
|
|
4036
|
+
)}
|
|
4037
|
+
</td>
|
|
4038
|
+
</tr>
|
|
4039
|
+
))}
|
|
4040
|
+
</tbody>
|
|
4041
|
+
</table>
|
|
4042
|
+
</section>
|
|
4043
|
+
) : null}
|
|
4044
|
+
</div>
|
|
3564
4045
|
</>
|
|
3565
4046
|
)}
|
|
3566
4047
|
</div>
|
|
@@ -3614,7 +4095,7 @@ function ReportingContent() {
|
|
|
3614
4095
|
) : null}
|
|
3615
4096
|
<div
|
|
3616
4097
|
ref={taskInspectModalRef}
|
|
3617
|
-
className="max-h-[72vh] overflow-hidden rounded-xl border border-zinc-700 bg-zinc-900
|
|
4098
|
+
className="max-h-[72vh] overflow-hidden rounded-xl border border-zinc-200 bg-white shadow-2xl dark:border-zinc-700 dark:bg-zinc-900"
|
|
3618
4099
|
style={
|
|
3619
4100
|
taskInspectPlacement
|
|
3620
4101
|
? {
|
|
@@ -3634,15 +4115,15 @@ function ReportingContent() {
|
|
|
3634
4115
|
}
|
|
3635
4116
|
}
|
|
3636
4117
|
>
|
|
3637
|
-
<div className="flex items-center justify-between gap-3 border-b border-zinc-
|
|
4118
|
+
<div className="flex items-center justify-between gap-3 border-b border-zinc-200 px-4 py-3 dark:border-zinc-700">
|
|
3638
4119
|
<div className="min-w-0">
|
|
3639
|
-
<h2 className="min-w-0 truncate text-sm font-semibold text-zinc-
|
|
4120
|
+
<h2 className="min-w-0 truncate text-sm font-semibold text-zinc-900 sm:text-base dark:text-zinc-100">
|
|
3640
4121
|
{lang === "fr"
|
|
3641
4122
|
? `Détails des tâches - ${taskInspectScope.title}`
|
|
3642
4123
|
: `Task details - ${taskInspectScope.title}`}
|
|
3643
4124
|
</h2>
|
|
3644
4125
|
{taskInspectScope.sourceLabel ? (
|
|
3645
|
-
<p className="mt-0.5 truncate text-[0.7rem] text-zinc-400">
|
|
4126
|
+
<p className="mt-0.5 truncate text-[0.7rem] text-zinc-600 dark:text-zinc-400">
|
|
3646
4127
|
{lang === "fr"
|
|
3647
4128
|
? `Source : ${
|
|
3648
4129
|
taskInspectScope.kind === "tag"
|
|
@@ -3659,7 +4140,7 @@ function ReportingContent() {
|
|
|
3659
4140
|
</div>
|
|
3660
4141
|
<button
|
|
3661
4142
|
type="button"
|
|
3662
|
-
className="rounded border border-zinc-
|
|
4143
|
+
className="rounded border border-zinc-300 px-2 py-1 text-xs text-zinc-800 hover:bg-zinc-100 dark:border-zinc-600 dark:text-zinc-200 dark:hover:bg-zinc-800"
|
|
3663
4144
|
onClick={() => {
|
|
3664
4145
|
setTaskInspectScope(null);
|
|
3665
4146
|
setTaskInspectAnchor(null);
|
|
@@ -3698,7 +4179,7 @@ function ReportingContent() {
|
|
|
3698
4179
|
))}
|
|
3699
4180
|
</div>
|
|
3700
4181
|
) : (
|
|
3701
|
-
<p className="text-sm text-zinc-400">
|
|
4182
|
+
<p className="text-sm text-zinc-600 dark:text-zinc-400">
|
|
3702
4183
|
{lang === "fr"
|
|
3703
4184
|
? "Aucune tâche correspondante pour ce segment de temps."
|
|
3704
4185
|
: "No matching tasks for this time segment."}
|
|
@@ -3713,6 +4194,7 @@ function ReportingContent() {
|
|
|
3713
4194
|
<ReportingTour
|
|
3714
4195
|
open={reportingTourOpen}
|
|
3715
4196
|
onOpenChange={setReportingTourOpen}
|
|
4197
|
+
onStepChange={syncReportingTourTab}
|
|
3716
4198
|
dt={dt}
|
|
3717
4199
|
hasReportingChartData={hasReportingChartData}
|
|
3718
4200
|
/>
|