@carlonicora/nextjs-jsonapi 6.18.0 → 6.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (96) hide show
  1. package/dist/{BlockNoteEditor-KSOVYIOL.mjs → BlockNoteEditor-7JRJ3XWV.mjs} +5 -5
  2. package/dist/{BlockNoteEditor-HHTAZKRO.js → BlockNoteEditor-MH2D5L4A.js} +20 -20
  3. package/dist/{BlockNoteEditor-HHTAZKRO.js.map → BlockNoteEditor-MH2D5L4A.js.map} +1 -1
  4. package/dist/{BlockNoteViewer-XYKFY77F.js → BlockNoteViewer-72J3CBAH.js} +4 -4
  5. package/dist/{BlockNoteViewer-XYKFY77F.js.map → BlockNoteViewer-72J3CBAH.js.map} +1 -1
  6. package/dist/{BlockNoteViewer-X4UEVI2A.mjs → BlockNoteViewer-UKYEU3E3.mjs} +3 -3
  7. package/dist/{analytics.modules-Bsacu6ik.d.ts → analytics.modules-C4OT7fVa.d.ts} +1 -0
  8. package/dist/{analytics.modules-txdCLNG-.d.mts → analytics.modules-DEnXnRGt.d.mts} +1 -0
  9. package/dist/billing/index.js +492 -492
  10. package/dist/billing/index.mjs +4 -4
  11. package/dist/{chunk-OKBJEIOP.mjs → chunk-2U57XVNF.mjs} +1 -1
  12. package/dist/chunk-2U57XVNF.mjs.map +1 -0
  13. package/dist/{chunk-KMPBSOFB.mjs → chunk-ASORDSWQ.mjs} +50 -2
  14. package/dist/chunk-ASORDSWQ.mjs.map +1 -0
  15. package/dist/{chunk-B6WMZXRY.js → chunk-IN2F6HI2.js} +1242 -1206
  16. package/dist/chunk-IN2F6HI2.js.map +1 -0
  17. package/dist/{chunk-DFA566KY.js → chunk-JAOBCIE7.js} +4 -4
  18. package/dist/{chunk-DFA566KY.js.map → chunk-JAOBCIE7.js.map} +1 -1
  19. package/dist/{chunk-YVKWACDP.mjs → chunk-LSZZIQJ5.mjs} +2 -2
  20. package/dist/{chunk-I5BQLY7V.js → chunk-MVAIYPIN.js} +9 -9
  21. package/dist/{chunk-I5BQLY7V.js.map → chunk-MVAIYPIN.js.map} +1 -1
  22. package/dist/{chunk-MBLF236G.mjs → chunk-NKXNOGMH.mjs} +47 -11
  23. package/dist/chunk-NKXNOGMH.mjs.map +1 -0
  24. package/dist/{chunk-OO2DBP6L.js → chunk-SGLFQ55U.js} +942 -894
  25. package/dist/chunk-SGLFQ55U.js.map +1 -0
  26. package/dist/{chunk-TY5YWBLL.mjs → chunk-SJ3K3ATF.mjs} +3 -3
  27. package/dist/{chunk-A7Y5YW7Q.js → chunk-V7CHD7QT.js} +1 -1
  28. package/dist/{chunk-A7Y5YW7Q.js.map → chunk-V7CHD7QT.js.map} +1 -1
  29. package/dist/client/index.js +5 -5
  30. package/dist/client/index.mjs +4 -4
  31. package/dist/components/index.d.mts +7 -1
  32. package/dist/components/index.d.ts +7 -1
  33. package/dist/components/index.js +5 -5
  34. package/dist/components/index.mjs +4 -4
  35. package/dist/contexts/index.js +5 -5
  36. package/dist/contexts/index.mjs +4 -4
  37. package/dist/core/index.d.mts +2 -1
  38. package/dist/core/index.d.ts +2 -1
  39. package/dist/core/index.js +3 -3
  40. package/dist/core/index.mjs +2 -2
  41. package/dist/email-automations/index.js +118 -118
  42. package/dist/email-automations/index.mjs +4 -4
  43. package/dist/features/analytics/index.d.mts +60 -3
  44. package/dist/features/analytics/index.d.ts +60 -3
  45. package/dist/features/analytics/index.js +320 -128
  46. package/dist/features/analytics/index.js.map +1 -1
  47. package/dist/features/analytics/index.mjs +314 -122
  48. package/dist/features/analytics/index.mjs.map +1 -1
  49. package/dist/features/analytics/server.js +5 -5
  50. package/dist/features/analytics/server.mjs +3 -3
  51. package/dist/features/blog/index.js +151 -151
  52. package/dist/features/blog/index.mjs +4 -4
  53. package/dist/features/blog/server.js +17 -17
  54. package/dist/features/blog/server.mjs +2 -2
  55. package/dist/features/help/index.js +38 -38
  56. package/dist/features/help/index.mjs +4 -4
  57. package/dist/features/tokenusage/index.js +127 -127
  58. package/dist/features/tokenusage/index.mjs +4 -4
  59. package/dist/index.d.mts +1 -1
  60. package/dist/index.d.ts +1 -1
  61. package/dist/index.js +4 -4
  62. package/dist/index.mjs +3 -3
  63. package/dist/offline/index.js +14 -14
  64. package/dist/offline/index.mjs +1 -1
  65. package/dist/server/index.js +4 -4
  66. package/dist/server/index.mjs +3 -3
  67. package/messages/en.json +8 -0
  68. package/messages/it.json +8 -0
  69. package/package.json +1 -1
  70. package/src/components/forms/FormDateTime.tsx +55 -3
  71. package/src/core/registry/ModuleRegistry.ts +1 -0
  72. package/src/features/analytics/analytics.modules.ts +6 -0
  73. package/src/features/analytics/components/AnalyticsAdminContainer.tsx +20 -0
  74. package/src/features/analytics/components/AnalyticsDailyUniquesChart.tsx +224 -0
  75. package/src/features/analytics/components/__tests__/AnalyticsDailyUniquesChart.spec.tsx +88 -0
  76. package/src/features/analytics/components/__tests__/AnalyticsPageContainers.spec.tsx +22 -0
  77. package/src/features/analytics/contexts/AnalyticsAdminContext.tsx +15 -2
  78. package/src/features/analytics/contexts/__tests__/AnalyticsAdminContext.spec.tsx +38 -0
  79. package/src/features/analytics/data/AnalyticsAdminService.ts +20 -0
  80. package/src/features/analytics/data/__tests__/AnalyticsAdminService.spec.ts +15 -0
  81. package/src/features/analytics/data/__tests__/models.spec.ts +29 -0
  82. package/src/features/analytics/data/analytics-daily-unique-timeline.interface.ts +11 -0
  83. package/src/features/analytics/data/analytics-daily-unique-timeline.ts +53 -0
  84. package/src/features/analytics/data/analytics.types.ts +10 -0
  85. package/src/features/analytics/data/index.ts +2 -0
  86. package/src/features/analytics/i18n-keys.ts +8 -0
  87. package/src/features/analytics/index.ts +1 -0
  88. package/dist/chunk-B6WMZXRY.js.map +0 -1
  89. package/dist/chunk-KMPBSOFB.mjs.map +0 -1
  90. package/dist/chunk-MBLF236G.mjs.map +0 -1
  91. package/dist/chunk-OKBJEIOP.mjs.map +0 -1
  92. package/dist/chunk-OO2DBP6L.js.map +0 -1
  93. /package/dist/{BlockNoteEditor-KSOVYIOL.mjs.map → BlockNoteEditor-7JRJ3XWV.mjs.map} +0 -0
  94. /package/dist/{BlockNoteViewer-X4UEVI2A.mjs.map → BlockNoteViewer-UKYEU3E3.mjs.map} +0 -0
  95. /package/dist/{chunk-YVKWACDP.mjs.map → chunk-LSZZIQJ5.mjs.map} +0 -0
  96. /package/dist/{chunk-TY5YWBLL.mjs.map → chunk-SJ3K3ATF.mjs.map} +0 -0
@@ -31,26 +31,28 @@ import {
31
31
  seriesColor,
32
32
  usePageUrlGenerator,
33
33
  useUsageFormatters
34
- } from "../../chunk-MBLF236G.mjs";
34
+ } from "../../chunk-NKXNOGMH.mjs";
35
35
  import "../../chunk-VOXD3ZLY.mjs";
36
- import "../../chunk-TY5YWBLL.mjs";
36
+ import "../../chunk-SJ3K3ATF.mjs";
37
37
  import "../../chunk-PW42LRSL.mjs";
38
38
  import {
39
39
  AbstractService,
40
40
  AnalyticsBreakdown,
41
+ AnalyticsDailyUniqueTimeline,
41
42
  AnalyticsEvent,
42
43
  AnalyticsPageView,
43
44
  AnalyticsSession,
44
45
  AnalyticsSummary,
45
46
  AnalyticsTimeline,
46
47
  analyticsModules,
47
- cn
48
- } from "../../chunk-KMPBSOFB.mjs";
48
+ cn,
49
+ formatLocalDate
50
+ } from "../../chunk-ASORDSWQ.mjs";
49
51
  import "../../chunk-OY65RYD3.mjs";
50
52
  import {
51
53
  EndpointCreator,
52
54
  Modules
53
- } from "../../chunk-OKBJEIOP.mjs";
55
+ } from "../../chunk-2U57XVNF.mjs";
54
56
  import "../../chunk-AUD6ZLBB.mjs";
55
57
  import "../../chunk-M3N7LPHX.mjs";
56
58
  import "../../chunk-4ZJIUTIV.mjs";
@@ -321,6 +323,20 @@ var AnalyticsAdminService = class extends AbstractService {
321
323
  endpoint: endpoint.generate()
322
324
  });
323
325
  }
326
+ /**
327
+ * One row per calendar day from fromDate to toDate (YYYY-MM-DD, local dates).
328
+ * Always daily and never filtered by section, so it skips withFilters.
329
+ */
330
+ static async getDailyUniques(params) {
331
+ const endpoint = new EndpointCreator({ endpoint: Modules.AnalyticsDailyUniqueTimeline });
332
+ endpoint.addAdditionalParam("fromDate", params.fromDate);
333
+ endpoint.addAdditionalParam("toDate", params.toDate);
334
+ return this.callApi({
335
+ type: Modules.AnalyticsDailyUniqueTimeline,
336
+ method: "GET" /* GET */,
337
+ endpoint: endpoint.generate()
338
+ });
339
+ }
324
340
  static async getBreakdown(params) {
325
341
  const endpoint = withFilters(new EndpointCreator({ endpoint: Modules.AnalyticsBreakdown }), params);
326
342
  endpoint.addAdditionalParam("dimension", params.dimension);
@@ -404,6 +420,7 @@ var AnalyticsAdminProvider = /* @__PURE__ */ __name(({
404
420
  const [timeline, setTimeline] = useState([]);
405
421
  const [breakdowns, setBreakdowns] = useState(EMPTY_BREAKDOWNS);
406
422
  const [sessions, setSessions] = useState([]);
423
+ const [dailyUniques, setDailyUniques] = useState([]);
407
424
  const [sessionsNextPage, setSessionsNextPage] = useState(void 0);
408
425
  const [isLoading, setIsLoading] = useState(true);
409
426
  const [error, setError] = useState(null);
@@ -422,8 +439,14 @@ var AnalyticsAdminProvider = /* @__PURE__ */ __name(({
422
439
  (dimension) => AnalyticsAdminService.getBreakdown({ ...base, dimension, limit: BREAKDOWN_LIMIT })
423
440
  )
424
441
  ),
425
- AnalyticsAdminService.getSessions({ ...base, next: sessionsNext })
426
- ]).then(([nextSummary, nextTimeline, nextBreakdowns, nextSessions]) => {
442
+ AnalyticsAdminService.getSessions({ ...base, next: sessionsNext }),
443
+ // The local calendar days the admin picked: the picker emits local-midnight
444
+ // instants, so slicing the UTC instant would shift the range a day east of UTC.
445
+ AnalyticsAdminService.getDailyUniques({
446
+ fromDate: formatLocalDate(new Date(from)),
447
+ toDate: formatLocalDate(new Date(to))
448
+ })
449
+ ]).then(([nextSummary, nextTimeline, nextBreakdowns, nextSessions, nextDailyUniques]) => {
427
450
  if (cancelled) return;
428
451
  setSummary(nextSummary ?? []);
429
452
  setTimeline(nextTimeline ?? []);
@@ -434,6 +457,7 @@ var AnalyticsAdminProvider = /* @__PURE__ */ __name(({
434
457
  );
435
458
  setSessions(nextSessions ?? []);
436
459
  setSessionsNextPage(sessionsNext.next);
460
+ setDailyUniques(nextDailyUniques ?? []);
437
461
  }).catch((err) => {
438
462
  if (cancelled) return;
439
463
  console.error("Failed to load analytics:", err);
@@ -487,6 +511,7 @@ var AnalyticsAdminProvider = /* @__PURE__ */ __name(({
487
511
  timeline,
488
512
  breakdowns,
489
513
  sessions,
514
+ dailyUniques,
490
515
  filters,
491
516
  setFilters,
492
517
  loadMoreSessions,
@@ -500,6 +525,7 @@ var AnalyticsAdminProvider = /* @__PURE__ */ __name(({
500
525
  timeline,
501
526
  breakdowns,
502
527
  sessions,
528
+ dailyUniques,
503
529
  filters,
504
530
  setFilters,
505
531
  loadMoreSessions,
@@ -520,7 +546,7 @@ var useAnalyticsAdmin = /* @__PURE__ */ __name(() => {
520
546
  }, "useAnalyticsAdmin");
521
547
 
522
548
  // src/features/analytics/components/AnalyticsAdminContainer.tsx
523
- import { useTranslations as useTranslations7 } from "next-intl";
549
+ import { useTranslations as useTranslations8 } from "next-intl";
524
550
 
525
551
  // src/features/analytics/components/AnalyticsAdminTiles.tsx
526
552
  import { ArrowDownIcon, ArrowUpIcon } from "lucide-react";
@@ -706,34 +732,182 @@ function BreakdownRows({ rows }) {
706
732
  }
707
733
  __name(BreakdownRows, "BreakdownRows");
708
734
 
709
- // src/features/analytics/components/AnalyticsSessionsTable.tsx
710
- import { useLocale, useTranslations as useTranslations5 } from "next-intl";
735
+ // src/features/analytics/components/AnalyticsDailyUniquesChart.tsx
736
+ import { useTranslations as useTranslations5 } from "next-intl";
737
+ import { useTheme } from "next-themes";
711
738
  import { useMemo as useMemo3 } from "react";
712
- import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
739
+ import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";
740
+ import { Fragment, jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
741
+ var METRIC_SLOT = { visitors: 0, users: 2 };
742
+ var DOT_RADIUS = 4;
743
+ var bucketKey = /* @__PURE__ */ __name((date) => {
744
+ const y = date.getUTCFullYear();
745
+ const m = `${date.getUTCMonth() + 1}`.padStart(2, "0");
746
+ const d = `${date.getUTCDate()}`.padStart(2, "0");
747
+ return `${y}-${m}-${d}`;
748
+ }, "bucketKey");
749
+ function buildDailyUniquesSeries(rows, metric, todayKey) {
750
+ const entries = rows.map((row) => {
751
+ const day = bucketKey(row.day);
752
+ const value = row[metric] ?? null;
753
+ return { day, value, line: value, todayLine: null, isToday: day === todayKey };
754
+ });
755
+ const last = entries.length - 1;
756
+ if (last >= 1 && entries[last].isToday && entries[last].value !== null && entries[last - 1].value !== null) {
757
+ entries[last - 1].todayLine = entries[last - 1].value;
758
+ entries[last].todayLine = entries[last].value;
759
+ entries[last].line = null;
760
+ }
761
+ return entries;
762
+ }
763
+ __name(buildDailyUniquesSeries, "buildDailyUniquesSeries");
764
+ function AnalyticsDailyUniquesChart({ rows, metric, className }) {
765
+ const t = useTranslations5();
766
+ const { compact, bucketDate, decimal } = useUsageFormatters();
767
+ const { resolvedTheme } = useTheme();
768
+ const mode = resolvedTheme === "dark" ? "dark" : "light";
769
+ const color = seriesColor(METRIC_SLOT[metric], mode);
770
+ const chartData = useMemo3(() => buildDailyUniquesSeries(rows, metric, bucketKey(/* @__PURE__ */ new Date())), [rows, metric]);
771
+ const points = useMemo3(() => chartData.map(({ day, value }) => ({ day, value })), [chartData]);
772
+ const seriesLabel = t(`analytics.admin.daily_uniques.${metric}`);
773
+ const chartConfig = useMemo3(() => ({ line: { label: seriesLabel, color } }), [seriesLabel, color]);
774
+ if (chartData.length === 0) {
775
+ return /* @__PURE__ */ jsx5("p", { className: className ? `text-muted-foreground text-xs ${className}` : "text-muted-foreground text-xs", children: t("analytics.admin.no_data") });
776
+ }
777
+ const renderDot = /* @__PURE__ */ __name(({ cx, cy, index, payload }) => {
778
+ if (cx == null || cy == null || payload?.value == null) return /* @__PURE__ */ jsx5("g", {}, `dot-${index}`);
779
+ return /* @__PURE__ */ jsx5(
780
+ "circle",
781
+ {
782
+ cx,
783
+ cy,
784
+ r: DOT_RADIUS,
785
+ fill: payload.isToday ? "var(--card)" : color,
786
+ stroke: payload.isToday ? color : "var(--card)",
787
+ strokeWidth: 2
788
+ },
789
+ `dot-${index}`
790
+ );
791
+ }, "renderDot");
792
+ const renderTodayDot = /* @__PURE__ */ __name((props) => props.payload?.isToday ? renderDot(props) : /* @__PURE__ */ jsx5("g", {}, `today-dot-${props.index}`), "renderTodayDot");
793
+ return (
794
+ // The chart is a coordinate system, not prose: it stays an LTR island.
795
+ // rtl-ok: deliberate LTR island (chart)
796
+ /* @__PURE__ */ jsxs4("div", { className, dir: "ltr", children: [
797
+ /* @__PURE__ */ jsx5("span", { className: "sr-only", "data-testid": `analytics-daily-${metric}-data`, children: JSON.stringify(points) }),
798
+ /* @__PURE__ */ jsx5(ChartContainer, { config: chartConfig, className: "aspect-auto h-72 w-full", children: /* @__PURE__ */ jsxs4(LineChart, { accessibilityLayer: true, data: chartData, margin: { top: 8, right: 16, bottom: 0, left: 0 }, children: [
799
+ /* @__PURE__ */ jsx5(CartesianGrid, { vertical: false }),
800
+ /* @__PURE__ */ jsx5(
801
+ XAxis,
802
+ {
803
+ dataKey: "day",
804
+ tickLine: false,
805
+ axisLine: false,
806
+ tickMargin: 8,
807
+ minTickGap: 16,
808
+ tickFormatter: (value) => bucketDate(value, "day")
809
+ }
810
+ ),
811
+ /* @__PURE__ */ jsx5(
812
+ YAxis,
813
+ {
814
+ domain: [0, "auto"],
815
+ allowDecimals: false,
816
+ tickLine: false,
817
+ axisLine: false,
818
+ tickMargin: 8,
819
+ width: 48,
820
+ tickFormatter: (value) => compact(value)
821
+ }
822
+ ),
823
+ /* @__PURE__ */ jsx5(
824
+ ChartTooltip,
825
+ {
826
+ filterNull: false,
827
+ content: /* @__PURE__ */ jsx5(
828
+ ChartTooltipContent,
829
+ {
830
+ labelFormatter: (value, payload) => {
831
+ const entry = payload?.[0]?.payload;
832
+ return /* @__PURE__ */ jsxs4(Fragment, { children: [
833
+ bucketDate(String(value), "day"),
834
+ entry?.isToday && /* @__PURE__ */ jsx5("div", { className: "text-muted-foreground text-xs", children: t("analytics.admin.daily_uniques.today") })
835
+ ] });
836
+ },
837
+ formatter: (_value, _name, item) => {
838
+ const entry = item.payload;
839
+ return /* @__PURE__ */ jsxs4("div", { className: "flex flex-1 justify-between gap-4 leading-none", children: [
840
+ /* @__PURE__ */ jsx5("span", { className: "text-muted-foreground", children: seriesLabel }),
841
+ /* @__PURE__ */ jsx5("span", { className: "text-xs tabular-nums text-end", children: entry.value === null ? t("analytics.admin.daily_uniques.no_data") : decimal(entry.value, 0) })
842
+ ] });
843
+ }
844
+ }
845
+ )
846
+ }
847
+ ),
848
+ /* @__PURE__ */ jsx5(
849
+ Line,
850
+ {
851
+ dataKey: "line",
852
+ type: "linear",
853
+ stroke: color,
854
+ strokeWidth: 2,
855
+ connectNulls: false,
856
+ dot: renderDot,
857
+ activeDot: { r: DOT_RADIUS, strokeWidth: 2, stroke: "var(--card)" },
858
+ isAnimationActive: false
859
+ }
860
+ ),
861
+ /* @__PURE__ */ jsx5(
862
+ Line,
863
+ {
864
+ dataKey: "todayLine",
865
+ type: "linear",
866
+ stroke: color,
867
+ strokeWidth: 2,
868
+ strokeDasharray: "2 5",
869
+ connectNulls: false,
870
+ dot: renderTodayDot,
871
+ activeDot: false,
872
+ tooltipType: "none",
873
+ legendType: "none",
874
+ isAnimationActive: false
875
+ }
876
+ )
877
+ ] }) })
878
+ ] })
879
+ );
880
+ }
881
+ __name(AnalyticsDailyUniquesChart, "AnalyticsDailyUniquesChart");
882
+
883
+ // src/features/analytics/components/AnalyticsSessionsTable.tsx
884
+ import { useLocale, useTranslations as useTranslations6 } from "next-intl";
885
+ import { useMemo as useMemo4 } from "react";
886
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
713
887
  var VISITOR_SUFFIX_LENGTH = 6;
714
888
  function AnalyticsSessionsTable({ rows, sessionPageUrl, onLoadMore, hasMore }) {
715
- const t = useTranslations5();
889
+ const t = useTranslations6();
716
890
  const locale = useLocale();
717
891
  const generateUrl = usePageUrlGenerator();
718
892
  const { decimal } = useUsageFormatters();
719
- const dateTime = useMemo3(() => new Intl.DateTimeFormat(locale, { dateStyle: "short", timeStyle: "short" }), [locale]);
893
+ const dateTime = useMemo4(() => new Intl.DateTimeFormat(locale, { dateStyle: "short", timeStyle: "short" }), [locale]);
720
894
  if (rows.length === 0) {
721
- return /* @__PURE__ */ jsx5("p", { className: "text-muted-foreground text-sm", children: t("analytics.admin.no_data") });
895
+ return /* @__PURE__ */ jsx6("p", { className: "text-muted-foreground text-sm", children: t("analytics.admin.no_data") });
722
896
  }
723
- return /* @__PURE__ */ jsxs4("div", { className: "flex flex-col gap-3", children: [
724
- /* @__PURE__ */ jsx5("div", { className: "overflow-x-auto [&_[data-slot=table-container]]:overflow-x-visible", children: /* @__PURE__ */ jsxs4(Table, { children: [
725
- /* @__PURE__ */ jsx5(TableHeader, { children: /* @__PURE__ */ jsxs4(TableRow, { children: [
726
- /* @__PURE__ */ jsx5(TableHead, { className: "text-xs font-medium", children: t("analytics.admin.columns.started") }),
727
- /* @__PURE__ */ jsx5(TableHead, { className: "text-xs font-medium", children: t("analytics.admin.columns.visitor") }),
728
- /* @__PURE__ */ jsx5(TableHead, { className: "text-xs font-medium", children: t("analytics.admin.columns.source") }),
729
- /* @__PURE__ */ jsx5(TableHead, { className: "text-xs font-medium", children: t("analytics.admin.columns.landing") }),
730
- /* @__PURE__ */ jsx5(TableHead, { className: "text-end text-xs font-medium", children: t("analytics.admin.columns.pages") }),
731
- /* @__PURE__ */ jsx5(TableHead, { className: "text-xs font-medium", children: t("analytics.admin.columns.device") })
897
+ return /* @__PURE__ */ jsxs5("div", { className: "flex flex-col gap-3", children: [
898
+ /* @__PURE__ */ jsx6("div", { className: "overflow-x-auto [&_[data-slot=table-container]]:overflow-x-visible", children: /* @__PURE__ */ jsxs5(Table, { children: [
899
+ /* @__PURE__ */ jsx6(TableHeader, { children: /* @__PURE__ */ jsxs5(TableRow, { children: [
900
+ /* @__PURE__ */ jsx6(TableHead, { className: "text-xs font-medium", children: t("analytics.admin.columns.started") }),
901
+ /* @__PURE__ */ jsx6(TableHead, { className: "text-xs font-medium", children: t("analytics.admin.columns.visitor") }),
902
+ /* @__PURE__ */ jsx6(TableHead, { className: "text-xs font-medium", children: t("analytics.admin.columns.source") }),
903
+ /* @__PURE__ */ jsx6(TableHead, { className: "text-xs font-medium", children: t("analytics.admin.columns.landing") }),
904
+ /* @__PURE__ */ jsx6(TableHead, { className: "text-end text-xs font-medium", children: t("analytics.admin.columns.pages") }),
905
+ /* @__PURE__ */ jsx6(TableHead, { className: "text-xs font-medium", children: t("analytics.admin.columns.device") })
732
906
  ] }) }),
733
- /* @__PURE__ */ jsx5(TableBody, { children: rows.map((row) => {
907
+ /* @__PURE__ */ jsx6(TableBody, { children: rows.map((row) => {
734
908
  const source = [row.utmSource ?? row.referrerHost, row.utmMedium].filter(Boolean).join(" / ");
735
- return /* @__PURE__ */ jsxs4(TableRow, { "data-testid": `analytics-session-row-${row.id}`, children: [
736
- /* @__PURE__ */ jsx5(TableCell, { className: "text-xs tabular-nums", children: /* @__PURE__ */ jsx5(
909
+ return /* @__PURE__ */ jsxs5(TableRow, { "data-testid": `analytics-session-row-${row.id}`, children: [
910
+ /* @__PURE__ */ jsx6(TableCell, { className: "text-xs tabular-nums", children: /* @__PURE__ */ jsx6(
737
911
  Link,
738
912
  {
739
913
  href: generateUrl({ page: sessionPageUrl, id: row.id }),
@@ -741,33 +915,33 @@ function AnalyticsSessionsTable({ rows, sessionPageUrl, onLoadMore, hasMore }) {
741
915
  children: dateTime.format(row.startedAt)
742
916
  }
743
917
  ) }),
744
- /* @__PURE__ */ jsx5(TableCell, { className: "text-xs", children: row.user ? /* @__PURE__ */ jsxs4("span", { className: "flex flex-col", children: [
745
- /* @__PURE__ */ jsx5("span", { children: row.user.name }),
746
- /* @__PURE__ */ jsx5("span", { className: "text-muted-foreground text-xs", children: row.user.email })
747
- ] }) : /* @__PURE__ */ jsxs4("span", { className: "flex items-center gap-2", children: [
748
- /* @__PURE__ */ jsx5(Badge, { variant: "softGray", children: t("analytics.admin.anonymous") }),
749
- /* @__PURE__ */ jsx5("span", { className: "text-muted-foreground text-xs tabular-nums", children: row.visitorId.slice(-VISITOR_SUFFIX_LENGTH) })
918
+ /* @__PURE__ */ jsx6(TableCell, { className: "text-xs", children: row.user ? /* @__PURE__ */ jsxs5("span", { className: "flex flex-col", children: [
919
+ /* @__PURE__ */ jsx6("span", { children: row.user.name }),
920
+ /* @__PURE__ */ jsx6("span", { className: "text-muted-foreground text-xs", children: row.user.email })
921
+ ] }) : /* @__PURE__ */ jsxs5("span", { className: "flex items-center gap-2", children: [
922
+ /* @__PURE__ */ jsx6(Badge, { variant: "softGray", children: t("analytics.admin.anonymous") }),
923
+ /* @__PURE__ */ jsx6("span", { className: "text-muted-foreground text-xs tabular-nums", children: row.visitorId.slice(-VISITOR_SUFFIX_LENGTH) })
750
924
  ] }) }),
751
- /* @__PURE__ */ jsx5(TableCell, { className: "text-xs", children: source }),
752
- /* @__PURE__ */ jsx5(TableCell, { className: "text-xs", children: row.landingRoute }),
753
- /* @__PURE__ */ jsx5(TableCell, { className: "text-end text-xs tabular-nums", children: decimal(row.pageViews, 0) }),
754
- /* @__PURE__ */ jsx5(TableCell, { className: "text-muted-foreground text-xs", children: row.deviceType })
925
+ /* @__PURE__ */ jsx6(TableCell, { className: "text-xs", children: source }),
926
+ /* @__PURE__ */ jsx6(TableCell, { className: "text-xs", children: row.landingRoute }),
927
+ /* @__PURE__ */ jsx6(TableCell, { className: "text-end text-xs tabular-nums", children: decimal(row.pageViews, 0) }),
928
+ /* @__PURE__ */ jsx6(TableCell, { className: "text-muted-foreground text-xs", children: row.deviceType })
755
929
  ] }, row.id);
756
930
  }) })
757
931
  ] }) }),
758
- hasMore && /* @__PURE__ */ jsx5("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx5(Button, { type: "button", variant: "outline", size: "sm", onClick: onLoadMore, children: t("analytics.admin.load_more") }) })
932
+ hasMore && /* @__PURE__ */ jsx6("div", { className: "flex justify-center", children: /* @__PURE__ */ jsx6(Button, { type: "button", variant: "outline", size: "sm", onClick: onLoadMore, children: t("analytics.admin.load_more") }) })
759
933
  ] });
760
934
  }
761
935
  __name(AnalyticsSessionsTable, "AnalyticsSessionsTable");
762
936
 
763
937
  // src/features/analytics/components/AnalyticsTimelineChart.tsx
764
- import { useTranslations as useTranslations6 } from "next-intl";
765
- import { useTheme } from "next-themes";
766
- import { useMemo as useMemo4 } from "react";
767
- import { Bar, BarChart, CartesianGrid, XAxis, YAxis } from "recharts";
768
- import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
938
+ import { useTranslations as useTranslations7 } from "next-intl";
939
+ import { useTheme as useTheme2 } from "next-themes";
940
+ import { useMemo as useMemo5 } from "react";
941
+ import { Bar, BarChart, CartesianGrid as CartesianGrid2, XAxis as XAxis2, YAxis as YAxis2 } from "recharts";
942
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
769
943
  var SERIES = ["visitors", "pageViews"];
770
- var bucketKey = /* @__PURE__ */ __name((date) => {
944
+ var bucketKey2 = /* @__PURE__ */ __name((date) => {
771
945
  const y = date.getUTCFullYear();
772
946
  const m = `${date.getUTCMonth() + 1}`.padStart(2, "0");
773
947
  const d = `${date.getUTCDate()}`.padStart(2, "0");
@@ -786,14 +960,14 @@ var inferGranularity = /* @__PURE__ */ __name((buckets) => {
786
960
  return "day";
787
961
  }, "inferGranularity");
788
962
  function AnalyticsTimelineChart({ rows, className }) {
789
- const t = useTranslations6();
963
+ const t = useTranslations7();
790
964
  const { compact, bucketDate, decimal } = useUsageFormatters();
791
- const { resolvedTheme } = useTheme();
965
+ const { resolvedTheme } = useTheme2();
792
966
  const mode = resolvedTheme === "dark" ? "dark" : "light";
793
- const chartData = useMemo4(() => {
967
+ const chartData = useMemo5(() => {
794
968
  const byBucket = /* @__PURE__ */ new Map();
795
969
  for (const row of rows) {
796
- const key = bucketKey(row.bucket);
970
+ const key = bucketKey2(row.bucket);
797
971
  let entry = byBucket.get(key);
798
972
  if (!entry) {
799
973
  entry = { bucket: key, visitors: 0, pageViews: 0 };
@@ -804,7 +978,7 @@ function AnalyticsTimelineChart({ rows, className }) {
804
978
  }
805
979
  return [...byBucket.values()].sort((a, b) => a.bucket.localeCompare(b.bucket));
806
980
  }, [rows]);
807
- const chartConfig = useMemo4(
981
+ const chartConfig = useMemo5(
808
982
  () => ({
809
983
  visitors: { label: t("analytics.admin.series.visitors") },
810
984
  pageViews: { label: t("analytics.admin.series.page_views") }
@@ -812,7 +986,7 @@ function AnalyticsTimelineChart({ rows, className }) {
812
986
  [t]
813
987
  );
814
988
  if (chartData.length === 0) {
815
- return /* @__PURE__ */ jsx6("p", { className: className ? `text-muted-foreground text-sm ${className}` : "text-muted-foreground text-sm", children: t("analytics.admin.no_data") });
989
+ return /* @__PURE__ */ jsx7("p", { className: className ? `text-muted-foreground text-sm ${className}` : "text-muted-foreground text-sm", children: t("analytics.admin.no_data") });
816
990
  }
817
991
  const granularity = inferGranularity(chartData.map((entry) => entry.bucket));
818
992
  return (
@@ -820,12 +994,12 @@ function AnalyticsTimelineChart({ rows, className }) {
820
994
  // bars out left-to-right, so the whole chart stays an LTR island even under
821
995
  // dir="rtl". Only the labels inside it are translated.
822
996
  // rtl-ok: deliberate LTR island (chart)
823
- /* @__PURE__ */ jsxs5("div", { className, dir: "ltr", children: [
824
- /* @__PURE__ */ jsx6("span", { className: "sr-only", "data-testid": "analytics-timeline-data", children: JSON.stringify(chartData) }),
825
- /* @__PURE__ */ jsx6(ChartContainer, { config: chartConfig, className: "aspect-auto h-72 w-full", children: /* @__PURE__ */ jsxs5(BarChart, { accessibilityLayer: true, data: chartData, margin: { top: 8, right: 8, bottom: 0, left: 0 }, children: [
826
- /* @__PURE__ */ jsx6(CartesianGrid, { vertical: false }),
827
- /* @__PURE__ */ jsx6(
828
- XAxis,
997
+ /* @__PURE__ */ jsxs6("div", { className, dir: "ltr", children: [
998
+ /* @__PURE__ */ jsx7("span", { className: "sr-only", "data-testid": "analytics-timeline-data", children: JSON.stringify(chartData) }),
999
+ /* @__PURE__ */ jsx7(ChartContainer, { config: chartConfig, className: "aspect-auto h-72 w-full", children: /* @__PURE__ */ jsxs6(BarChart, { accessibilityLayer: true, data: chartData, margin: { top: 8, right: 8, bottom: 0, left: 0 }, children: [
1000
+ /* @__PURE__ */ jsx7(CartesianGrid2, { vertical: false }),
1001
+ /* @__PURE__ */ jsx7(
1002
+ XAxis2,
829
1003
  {
830
1004
  dataKey: "bucket",
831
1005
  tickLine: false,
@@ -835,8 +1009,8 @@ function AnalyticsTimelineChart({ rows, className }) {
835
1009
  tickFormatter: (value) => bucketDate(value, granularity)
836
1010
  }
837
1011
  ),
838
- /* @__PURE__ */ jsx6(
839
- YAxis,
1012
+ /* @__PURE__ */ jsx7(
1013
+ YAxis2,
840
1014
  {
841
1015
  tickLine: false,
842
1016
  axisLine: false,
@@ -845,10 +1019,10 @@ function AnalyticsTimelineChart({ rows, className }) {
845
1019
  tickFormatter: (value) => compact(value)
846
1020
  }
847
1021
  ),
848
- /* @__PURE__ */ jsx6(
1022
+ /* @__PURE__ */ jsx7(
849
1023
  ChartTooltip,
850
1024
  {
851
- content: /* @__PURE__ */ jsx6(
1025
+ content: /* @__PURE__ */ jsx7(
852
1026
  ChartTooltipContent,
853
1027
  {
854
1028
  labelFormatter: (value) => bucketDate(String(value), granularity),
@@ -857,8 +1031,8 @@ function AnalyticsTimelineChart({ rows, className }) {
857
1031
  )
858
1032
  }
859
1033
  ),
860
- /* @__PURE__ */ jsx6(ChartLegend, { content: /* @__PURE__ */ jsx6(ChartLegendContent, {}) }),
861
- SERIES.map((series, index) => /* @__PURE__ */ jsx6(
1034
+ /* @__PURE__ */ jsx7(ChartLegend, { content: /* @__PURE__ */ jsx7(ChartLegendContent, {}) }),
1035
+ SERIES.map((series, index) => /* @__PURE__ */ jsx7(
862
1036
  Bar,
863
1037
  {
864
1038
  dataKey: series,
@@ -877,14 +1051,15 @@ function AnalyticsTimelineChart({ rows, className }) {
877
1051
  __name(AnalyticsTimelineChart, "AnalyticsTimelineChart");
878
1052
 
879
1053
  // src/features/analytics/components/AnalyticsAdminContainer.tsx
880
- import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1054
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
881
1055
  function AnalyticsAdminContainer() {
882
- const t = useTranslations7();
1056
+ const t = useTranslations8();
883
1057
  const {
884
1058
  summary,
885
1059
  timeline,
886
1060
  breakdowns,
887
1061
  sessions,
1062
+ dailyUniques,
888
1063
  loadMoreSessions,
889
1064
  hasMoreSessions,
890
1065
  sessionPageUrl,
@@ -892,25 +1067,33 @@ function AnalyticsAdminContainer() {
892
1067
  error
893
1068
  } = useAnalyticsAdmin();
894
1069
  if (error) {
895
- return /* @__PURE__ */ jsx7(RoundPageContainer, { module: Modules.AnalyticsSummary, fullWidth: true, forceHeader: true, children: /* @__PURE__ */ jsx7("div", { className: "p-4", children: /* @__PURE__ */ jsx7(Card, { children: /* @__PURE__ */ jsx7(CardContent, { children: /* @__PURE__ */ jsx7("p", { className: "text-destructive text-xs/relaxed", children: error }) }) }) }) });
1070
+ return /* @__PURE__ */ jsx8(RoundPageContainer, { module: Modules.AnalyticsSummary, fullWidth: true, forceHeader: true, children: /* @__PURE__ */ jsx8("div", { className: "p-4", children: /* @__PURE__ */ jsx8(Card, { children: /* @__PURE__ */ jsx8(CardContent, { children: /* @__PURE__ */ jsx8("p", { className: "text-destructive text-xs/relaxed", children: error }) }) }) }) });
896
1071
  }
897
- if (isLoading) return /* @__PURE__ */ jsx7(RoundPageContainer, { module: Modules.AnalyticsSummary, fullWidth: true, forceHeader: true });
898
- return /* @__PURE__ */ jsx7(RoundPageContainer, { module: Modules.AnalyticsSummary, fullWidth: true, forceHeader: true, children: /* @__PURE__ */ jsxs6("div", { className: "flex w-full flex-col gap-4 p-4", children: [
899
- /* @__PURE__ */ jsxs6("div", { className: "flex flex-col gap-2", children: [
900
- /* @__PURE__ */ jsx7(AnalyticsAdminTiles, { summary }),
901
- /* @__PURE__ */ jsx7("p", { className: "text-muted-foreground text-xs", children: t("analytics.admin.tiles.summarised_note") })
1072
+ if (isLoading) return /* @__PURE__ */ jsx8(RoundPageContainer, { module: Modules.AnalyticsSummary, fullWidth: true, forceHeader: true });
1073
+ return /* @__PURE__ */ jsx8(RoundPageContainer, { module: Modules.AnalyticsSummary, fullWidth: true, forceHeader: true, children: /* @__PURE__ */ jsxs7("div", { className: "flex w-full flex-col gap-4 p-4", children: [
1074
+ /* @__PURE__ */ jsxs7("div", { className: "flex flex-col gap-2", children: [
1075
+ /* @__PURE__ */ jsx8(AnalyticsAdminTiles, { summary }),
1076
+ /* @__PURE__ */ jsx8("p", { className: "text-muted-foreground text-xs", children: t("analytics.admin.tiles.summarised_note") })
1077
+ ] }),
1078
+ /* @__PURE__ */ jsxs7(Card, { children: [
1079
+ /* @__PURE__ */ jsx8(CardHeader, { children: /* @__PURE__ */ jsx8(CardTitle, { children: t("analytics.admin.panels.over_time") }) }),
1080
+ /* @__PURE__ */ jsx8(CardContent, { children: /* @__PURE__ */ jsx8(AnalyticsTimelineChart, { rows: timeline }) })
902
1081
  ] }),
903
- /* @__PURE__ */ jsxs6(Card, { children: [
904
- /* @__PURE__ */ jsx7(CardHeader, { children: /* @__PURE__ */ jsx7(CardTitle, { children: t("analytics.admin.panels.over_time") }) }),
905
- /* @__PURE__ */ jsx7(CardContent, { children: /* @__PURE__ */ jsx7(AnalyticsTimelineChart, { rows: timeline }) })
1082
+ /* @__PURE__ */ jsxs7(Card, { "data-testid": "analytics-daily-visitors-card", children: [
1083
+ /* @__PURE__ */ jsx8(CardHeader, { children: /* @__PURE__ */ jsx8(CardTitle, { children: t("analytics.admin.panels.daily_visitors") }) }),
1084
+ /* @__PURE__ */ jsx8(CardContent, { children: /* @__PURE__ */ jsx8(AnalyticsDailyUniquesChart, { rows: dailyUniques, metric: "visitors" }) })
906
1085
  ] }),
907
- /* @__PURE__ */ jsxs6(Card, { children: [
908
- /* @__PURE__ */ jsx7(CardHeader, { children: /* @__PURE__ */ jsx7(CardTitle, { children: t("analytics.admin.panels.breakdown") }) }),
909
- /* @__PURE__ */ jsx7(CardContent, { children: /* @__PURE__ */ jsx7(AnalyticsBreakdownTable, { breakdowns }) })
1086
+ /* @__PURE__ */ jsxs7(Card, { "data-testid": "analytics-daily-users-card", children: [
1087
+ /* @__PURE__ */ jsx8(CardHeader, { children: /* @__PURE__ */ jsx8(CardTitle, { children: t("analytics.admin.panels.daily_users") }) }),
1088
+ /* @__PURE__ */ jsx8(CardContent, { children: /* @__PURE__ */ jsx8(AnalyticsDailyUniquesChart, { rows: dailyUniques, metric: "users" }) })
910
1089
  ] }),
911
- /* @__PURE__ */ jsxs6(Card, { children: [
912
- /* @__PURE__ */ jsx7(CardHeader, { children: /* @__PURE__ */ jsx7(CardTitle, { children: t("analytics.admin.panels.sessions") }) }),
913
- /* @__PURE__ */ jsx7(CardContent, { children: /* @__PURE__ */ jsx7(
1090
+ /* @__PURE__ */ jsxs7(Card, { children: [
1091
+ /* @__PURE__ */ jsx8(CardHeader, { children: /* @__PURE__ */ jsx8(CardTitle, { children: t("analytics.admin.panels.breakdown") }) }),
1092
+ /* @__PURE__ */ jsx8(CardContent, { children: /* @__PURE__ */ jsx8(AnalyticsBreakdownTable, { breakdowns }) })
1093
+ ] }),
1094
+ /* @__PURE__ */ jsxs7(Card, { children: [
1095
+ /* @__PURE__ */ jsx8(CardHeader, { children: /* @__PURE__ */ jsx8(CardTitle, { children: t("analytics.admin.panels.sessions") }) }),
1096
+ /* @__PURE__ */ jsx8(CardContent, { children: /* @__PURE__ */ jsx8(
914
1097
  AnalyticsSessionsTable,
915
1098
  {
916
1099
  rows: sessions,
@@ -925,13 +1108,13 @@ function AnalyticsAdminContainer() {
925
1108
  __name(AnalyticsAdminContainer, "AnalyticsAdminContainer");
926
1109
 
927
1110
  // src/features/analytics/components/AnalyticsJourneyContainer.tsx
928
- import { useLocale as useLocale2, useTranslations as useTranslations8 } from "next-intl";
929
- import { useEffect as useEffect3, useMemo as useMemo5, useState as useState2 } from "react";
930
- import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1111
+ import { useLocale as useLocale2, useTranslations as useTranslations9 } from "next-intl";
1112
+ import { useEffect as useEffect3, useMemo as useMemo6, useState as useState2 } from "react";
1113
+ import { jsx as jsx9, jsxs as jsxs8 } from "react/jsx-runtime";
931
1114
  var ANALYTICS_ADMIN_PAGE_URL = "/administration/analytics";
932
1115
  var OTHER_SESSIONS_WINDOW_DAYS = 395;
933
1116
  function AnalyticsJourneyContainer({ sessionId, pageUrl = ANALYTICS_ADMIN_PAGE_URL }) {
934
- const t = useTranslations8();
1117
+ const t = useTranslations9();
935
1118
  const locale = useLocale2();
936
1119
  const generateUrl = usePageUrlGenerator();
937
1120
  const [session, setSession] = useState2(void 0);
@@ -972,7 +1155,7 @@ function AnalyticsJourneyContainer({ sessionId, pageUrl = ANALYTICS_ADMIN_PAGE_U
972
1155
  cancelled = true;
973
1156
  };
974
1157
  }, [sessionId]);
975
- const dateTime = useMemo5(
1158
+ const dateTime = useMemo6(
976
1159
  () => new Intl.DateTimeFormat(locale, { dateStyle: "short", timeStyle: "medium" }),
977
1160
  [locale]
978
1161
  );
@@ -989,58 +1172,58 @@ function AnalyticsJourneyContainer({ sessionId, pageUrl = ANALYTICS_ADMIN_PAGE_U
989
1172
  const title = { type: t("analytics.admin.panels.journey") };
990
1173
  const renderBody = /* @__PURE__ */ __name(() => {
991
1174
  if (error) {
992
- return /* @__PURE__ */ jsx8(RoundPageContainer, { module: Modules.AnalyticsSession, fullWidth: true, forceHeader: true, children: /* @__PURE__ */ jsx8("div", { className: "p-4", children: /* @__PURE__ */ jsx8(Card, { children: /* @__PURE__ */ jsx8(CardContent, { children: /* @__PURE__ */ jsx8("p", { className: "text-destructive text-xs/relaxed", children: error }) }) }) }) });
1175
+ return /* @__PURE__ */ jsx9(RoundPageContainer, { module: Modules.AnalyticsSession, fullWidth: true, forceHeader: true, children: /* @__PURE__ */ jsx9("div", { className: "p-4", children: /* @__PURE__ */ jsx9(Card, { children: /* @__PURE__ */ jsx9(CardContent, { children: /* @__PURE__ */ jsx9("p", { className: "text-destructive text-xs/relaxed", children: error }) }) }) }) });
993
1176
  }
994
- if (isLoading || !session) return /* @__PURE__ */ jsx8(RoundPageContainer, { module: Modules.AnalyticsSession, fullWidth: true, forceHeader: true });
1177
+ if (isLoading || !session) return /* @__PURE__ */ jsx9(RoundPageContainer, { module: Modules.AnalyticsSession, fullWidth: true, forceHeader: true });
995
1178
  const source = [session.utmSource ?? session.referrerHost, session.utmMedium, session.utmCampaign].filter(Boolean).join(" / ");
996
- return /* @__PURE__ */ jsx8(RoundPageContainer, { module: Modules.AnalyticsSession, fullWidth: true, forceHeader: true, children: /* @__PURE__ */ jsxs7("div", { className: "flex w-full flex-col gap-4 p-4", children: [
997
- /* @__PURE__ */ jsx8(Card, { "data-testid": "analytics-journey-attribution", children: /* @__PURE__ */ jsxs7(CardContent, { className: "flex flex-col gap-3", children: [
998
- /* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap items-center gap-2", children: [
999
- session.user ? /* @__PURE__ */ jsxs7("span", { className: "flex flex-col", children: [
1000
- /* @__PURE__ */ jsx8("span", { className: "text-sm", children: session.user.name }),
1001
- /* @__PURE__ */ jsx8("span", { className: "text-muted-foreground text-xs", children: session.user.email })
1002
- ] }) : /* @__PURE__ */ jsx8(Badge, { variant: "softGray", children: t("analytics.admin.anonymous") }),
1003
- session.consented ? /* @__PURE__ */ jsx8(Badge, { variant: "softGreen", children: t("analytics.admin.consented") }) : /* @__PURE__ */ jsx8(Badge, { variant: "softGray", children: t("analytics.admin.not_consented") })
1179
+ return /* @__PURE__ */ jsx9(RoundPageContainer, { module: Modules.AnalyticsSession, fullWidth: true, forceHeader: true, children: /* @__PURE__ */ jsxs8("div", { className: "flex w-full flex-col gap-4 p-4", children: [
1180
+ /* @__PURE__ */ jsx9(Card, { "data-testid": "analytics-journey-attribution", children: /* @__PURE__ */ jsxs8(CardContent, { className: "flex flex-col gap-3", children: [
1181
+ /* @__PURE__ */ jsxs8("div", { className: "flex flex-wrap items-center gap-2", children: [
1182
+ session.user ? /* @__PURE__ */ jsxs8("span", { className: "flex flex-col", children: [
1183
+ /* @__PURE__ */ jsx9("span", { className: "text-sm", children: session.user.name }),
1184
+ /* @__PURE__ */ jsx9("span", { className: "text-muted-foreground text-xs", children: session.user.email })
1185
+ ] }) : /* @__PURE__ */ jsx9(Badge, { variant: "softGray", children: t("analytics.admin.anonymous") }),
1186
+ session.consented ? /* @__PURE__ */ jsx9(Badge, { variant: "softGreen", children: t("analytics.admin.consented") }) : /* @__PURE__ */ jsx9(Badge, { variant: "softGray", children: t("analytics.admin.not_consented") })
1004
1187
  ] }),
1005
- /* @__PURE__ */ jsxs7("div", { className: "grid gap-3 sm:grid-cols-3", children: [
1006
- /* @__PURE__ */ jsx8(DetailField, { label: t("analytics.admin.columns.started"), value: dateTime.format(session.startedAt) }),
1007
- /* @__PURE__ */ jsx8(DetailField, { label: t("analytics.admin.columns.source"), value: source }),
1008
- /* @__PURE__ */ jsx8(DetailField, { label: t("analytics.admin.columns.landing"), value: session.landingRoute }),
1009
- /* @__PURE__ */ jsx8(DetailField, { label: t("analytics.admin.columns.device"), value: session.deviceType }),
1010
- /* @__PURE__ */ jsx8(
1188
+ /* @__PURE__ */ jsxs8("div", { className: "grid gap-3 sm:grid-cols-3", children: [
1189
+ /* @__PURE__ */ jsx9(DetailField, { label: t("analytics.admin.columns.started"), value: dateTime.format(session.startedAt) }),
1190
+ /* @__PURE__ */ jsx9(DetailField, { label: t("analytics.admin.columns.source"), value: source }),
1191
+ /* @__PURE__ */ jsx9(DetailField, { label: t("analytics.admin.columns.landing"), value: session.landingRoute }),
1192
+ /* @__PURE__ */ jsx9(DetailField, { label: t("analytics.admin.columns.device"), value: session.deviceType }),
1193
+ /* @__PURE__ */ jsx9(
1011
1194
  DetailField,
1012
1195
  {
1013
1196
  label: t("analytics.admin.columns.visitor"),
1014
- value: /* @__PURE__ */ jsx8("span", { className: "tabular-nums", children: session.visitorId })
1197
+ value: /* @__PURE__ */ jsx9("span", { className: "tabular-nums", children: session.visitorId })
1015
1198
  }
1016
1199
  )
1017
1200
  ] })
1018
1201
  ] }) }),
1019
- /* @__PURE__ */ jsxs7(Card, { children: [
1020
- /* @__PURE__ */ jsx8(CardHeader, { children: /* @__PURE__ */ jsx8(CardTitle, { children: t("analytics.admin.columns.pages") }) }),
1021
- /* @__PURE__ */ jsx8(CardContent, { children: pageViews.length === 0 ? /* @__PURE__ */ jsx8("p", { className: "text-muted-foreground text-sm", children: t("analytics.admin.no_data") }) : (
1202
+ /* @__PURE__ */ jsxs8(Card, { children: [
1203
+ /* @__PURE__ */ jsx9(CardHeader, { children: /* @__PURE__ */ jsx9(CardTitle, { children: t("analytics.admin.columns.pages") }) }),
1204
+ /* @__PURE__ */ jsx9(CardContent, { children: pageViews.length === 0 ? /* @__PURE__ */ jsx9("p", { className: "text-muted-foreground text-sm", children: t("analytics.admin.no_data") }) : (
1022
1205
  // The package's <Table> wraps itself in an `overflow-x-clip`
1023
1206
  // container — hence the child override, so wide rows scroll.
1024
- /* @__PURE__ */ jsx8("div", { className: "overflow-x-auto [&_[data-slot=table-container]]:overflow-x-visible", children: /* @__PURE__ */ jsxs7(Table, { children: [
1025
- /* @__PURE__ */ jsx8(TableHeader, { children: /* @__PURE__ */ jsxs7(TableRow, { children: [
1026
- /* @__PURE__ */ jsx8(TableHead, { className: "text-xs font-medium", children: t("analytics.admin.columns.time") }),
1027
- /* @__PURE__ */ jsx8(TableHead, { className: "text-end text-xs font-medium", children: t("analytics.admin.columns.gap") }),
1028
- /* @__PURE__ */ jsx8(TableHead, { className: "text-xs font-medium", children: t("analytics.admin.columns.path") })
1207
+ /* @__PURE__ */ jsx9("div", { className: "overflow-x-auto [&_[data-slot=table-container]]:overflow-x-visible", children: /* @__PURE__ */ jsxs8(Table, { children: [
1208
+ /* @__PURE__ */ jsx9(TableHeader, { children: /* @__PURE__ */ jsxs8(TableRow, { children: [
1209
+ /* @__PURE__ */ jsx9(TableHead, { className: "text-xs font-medium", children: t("analytics.admin.columns.time") }),
1210
+ /* @__PURE__ */ jsx9(TableHead, { className: "text-end text-xs font-medium", children: t("analytics.admin.columns.gap") }),
1211
+ /* @__PURE__ */ jsx9(TableHead, { className: "text-xs font-medium", children: t("analytics.admin.columns.path") })
1029
1212
  ] }) }),
1030
- /* @__PURE__ */ jsx8(TableBody, { children: pageViews.map((pageView, index) => {
1213
+ /* @__PURE__ */ jsx9(TableBody, { children: pageViews.map((pageView, index) => {
1031
1214
  const previous = index > 0 ? pageViews[index - 1] : void 0;
1032
- return /* @__PURE__ */ jsxs7(TableRow, { "data-testid": `analytics-pageview-row-${pageView.id}`, children: [
1033
- /* @__PURE__ */ jsx8(TableCell, { className: "text-xs tabular-nums", children: dateTime.format(pageView.createdAt) }),
1034
- /* @__PURE__ */ jsx8(TableCell, { className: "text-end text-xs tabular-nums", children: previous ? formatGap(pageView.createdAt.getTime() - previous.createdAt.getTime()) : "\u2014" }),
1035
- /* @__PURE__ */ jsx8(TableCell, { className: "text-xs", children: pageView.path })
1215
+ return /* @__PURE__ */ jsxs8(TableRow, { "data-testid": `analytics-pageview-row-${pageView.id}`, children: [
1216
+ /* @__PURE__ */ jsx9(TableCell, { className: "text-xs tabular-nums", children: dateTime.format(pageView.createdAt) }),
1217
+ /* @__PURE__ */ jsx9(TableCell, { className: "text-end text-xs tabular-nums", children: previous ? formatGap(pageView.createdAt.getTime() - previous.createdAt.getTime()) : "\u2014" }),
1218
+ /* @__PURE__ */ jsx9(TableCell, { className: "text-xs", children: pageView.path })
1036
1219
  ] }, pageView.id);
1037
1220
  }) })
1038
1221
  ] }) })
1039
1222
  ) })
1040
1223
  ] }),
1041
- /* @__PURE__ */ jsxs7(Card, { children: [
1042
- /* @__PURE__ */ jsx8(CardHeader, { children: /* @__PURE__ */ jsx8(CardTitle, { children: t("analytics.admin.panels.other_sessions") }) }),
1043
- /* @__PURE__ */ jsx8(CardContent, { children: /* @__PURE__ */ jsx8(
1224
+ /* @__PURE__ */ jsxs8(Card, { children: [
1225
+ /* @__PURE__ */ jsx9(CardHeader, { children: /* @__PURE__ */ jsx9(CardTitle, { children: t("analytics.admin.panels.other_sessions") }) }),
1226
+ /* @__PURE__ */ jsx9(CardContent, { children: /* @__PURE__ */ jsx9(
1044
1227
  AnalyticsSessionsTable,
1045
1228
  {
1046
1229
  rows: otherSessions,
@@ -1052,7 +1235,7 @@ function AnalyticsJourneyContainer({ sessionId, pageUrl = ANALYTICS_ADMIN_PAGE_U
1052
1235
  ] })
1053
1236
  ] }) });
1054
1237
  }, "renderBody");
1055
- return /* @__PURE__ */ jsx8(SharedProvider, { value: { breadcrumbs: breadcrumb, title }, children: renderBody() });
1238
+ return /* @__PURE__ */ jsx9(SharedProvider, { value: { breadcrumbs: breadcrumb, title }, children: renderBody() });
1056
1239
  }
1057
1240
  __name(AnalyticsJourneyContainer, "AnalyticsJourneyContainer");
1058
1241
  function unit(locale, name, value) {
@@ -1061,16 +1244,16 @@ function unit(locale, name, value) {
1061
1244
  __name(unit, "unit");
1062
1245
 
1063
1246
  // src/features/analytics/components/AnalyticsAdminPageContainer.tsx
1064
- import { jsx as jsx9 } from "react/jsx-runtime";
1247
+ import { jsx as jsx10 } from "react/jsx-runtime";
1065
1248
  function AnalyticsAdminPageContainer({ sessionPageUrl }) {
1066
- return /* @__PURE__ */ jsx9(AnalyticsAdminProvider, { sessionPageUrl, children: /* @__PURE__ */ jsx9(AnalyticsAdminContainer, {}) });
1249
+ return /* @__PURE__ */ jsx10(AnalyticsAdminProvider, { sessionPageUrl, children: /* @__PURE__ */ jsx10(AnalyticsAdminContainer, {}) });
1067
1250
  }
1068
1251
  __name(AnalyticsAdminPageContainer, "AnalyticsAdminPageContainer");
1069
1252
 
1070
1253
  // src/features/analytics/components/AnalyticsJourneyPageContainer.tsx
1071
- import { jsx as jsx10 } from "react/jsx-runtime";
1254
+ import { jsx as jsx11 } from "react/jsx-runtime";
1072
1255
  function AnalyticsJourneyPageContainer({ sessionId, pageUrl }) {
1073
- return /* @__PURE__ */ jsx10(AnalyticsJourneyContainer, { sessionId, pageUrl });
1256
+ return /* @__PURE__ */ jsx11(AnalyticsJourneyContainer, { sessionId, pageUrl });
1074
1257
  }
1075
1258
  __name(AnalyticsJourneyPageContainer, "AnalyticsJourneyPageContainer");
1076
1259
 
@@ -1102,6 +1285,8 @@ var ANALYTICS_ADMIN_I18N_KEYS = [
1102
1285
  "analytics.admin.panels.sessions",
1103
1286
  "analytics.admin.panels.journey",
1104
1287
  "analytics.admin.panels.other_sessions",
1288
+ "analytics.admin.panels.daily_visitors",
1289
+ "analytics.admin.panels.daily_users",
1105
1290
  // Breakdown tabs
1106
1291
  "analytics.admin.dimensions.source",
1107
1292
  "analytics.admin.dimensions.medium",
@@ -1132,7 +1317,12 @@ var ANALYTICS_ADMIN_I18N_KEYS = [
1132
1317
  "analytics.admin.other",
1133
1318
  // Timeline chart series
1134
1319
  "analytics.admin.series.visitors",
1135
- "analytics.admin.series.page_views"
1320
+ "analytics.admin.series.page_views",
1321
+ // Daily uniques charts
1322
+ "analytics.admin.daily_uniques.visitors",
1323
+ "analytics.admin.daily_uniques.users",
1324
+ "analytics.admin.daily_uniques.today",
1325
+ "analytics.admin.daily_uniques.no_data"
1136
1326
  ];
1137
1327
  export {
1138
1328
  ANALYTICS_ADMIN_I18N_KEYS,
@@ -1147,6 +1337,8 @@ export {
1147
1337
  AnalyticsAdminTiles,
1148
1338
  AnalyticsBreakdown,
1149
1339
  AnalyticsBreakdownTable,
1340
+ AnalyticsDailyUniqueTimeline,
1341
+ AnalyticsDailyUniquesChart,
1150
1342
  AnalyticsEvent,
1151
1343
  AnalyticsJourneyContainer,
1152
1344
  AnalyticsJourneyPageContainer,