@carlonicora/nextjs-jsonapi 3.24.2 → 3.26.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 (220) hide show
  1. package/dist/{AssistantMessageInterface-1Kbz8RVs.d.ts → AssistantMessageInterface-CubxuBIJ.d.ts} +1 -1
  2. package/dist/{AssistantMessageInterface-CoxJyuFM.d.mts → AssistantMessageInterface-sHjI2zPN.d.mts} +1 -1
  3. package/dist/{BlockNoteEditor-35D4R7HJ.js → BlockNoteEditor-4EIKMNY3.js} +31 -21
  4. package/dist/BlockNoteEditor-4EIKMNY3.js.map +1 -0
  5. package/dist/{BlockNoteEditor-65HR6ULV.mjs → BlockNoteEditor-AXYVDDXM.mjs} +16 -6
  6. package/dist/{BlockNoteEditor-65HR6ULV.mjs.map → BlockNoteEditor-AXYVDDXM.mjs.map} +1 -1
  7. package/dist/{BlockNoteViewer-QDXDVYEM.js → BlockNoteViewer-7BFNTN4E.js} +4 -4
  8. package/dist/{BlockNoteViewer-QDXDVYEM.js.map → BlockNoteViewer-7BFNTN4E.js.map} +1 -1
  9. package/dist/{BlockNoteViewer-KDKGUIG5.mjs → BlockNoteViewer-KRTAFTUX.mjs} +3 -3
  10. package/dist/{HandbookSectionInterface-OePKPbqN.d.ts → HandbookSectionInterface-Ck1wZJ81.d.ts} +1 -1
  11. package/dist/{HandbookSectionInterface-BYsmnPEz.d.mts → HandbookSectionInterface-DgYH--Ik.d.mts} +1 -1
  12. package/dist/JsonApiRequest-TE5WD3WS.js +26 -0
  13. package/dist/{JsonApiRequest-G625QAH5.js.map → JsonApiRequest-TE5WD3WS.js.map} +1 -1
  14. package/dist/{JsonApiRequest-RRMNV7YE.mjs → JsonApiRequest-UHOWNI3O.mjs} +2 -2
  15. package/dist/analytics.modules-CfczU12P.d.ts +25 -0
  16. package/dist/analytics.modules-pzGwMASI.d.mts +25 -0
  17. package/dist/{auth.interface-BC1jp_ni.d.ts → auth.interface-BzI4onI_.d.ts} +1 -1
  18. package/dist/{auth.interface-dR2-6gPM.d.mts → auth.interface-Df4vXbNz.d.mts} +1 -1
  19. package/dist/billing/index.js +402 -402
  20. package/dist/billing/index.mjs +4 -4
  21. package/dist/{chunk-YXRVPEEJ.js → chunk-26MQIJNB.js} +1 -1
  22. package/dist/{chunk-YXRVPEEJ.js.map → chunk-26MQIJNB.js.map} +1 -1
  23. package/dist/{chunk-T76WLDE5.mjs → chunk-37DMFVLN.mjs} +1 -1
  24. package/dist/{chunk-3AR4KA5D.js → chunk-4Z45NYPR.js} +13 -13
  25. package/dist/{chunk-3AR4KA5D.js.map → chunk-4Z45NYPR.js.map} +1 -1
  26. package/dist/chunk-7B4E42R5.mjs +280 -0
  27. package/dist/chunk-7B4E42R5.mjs.map +1 -0
  28. package/dist/{chunk-2XCUAHQT.js → chunk-ABVTYQIJ.js} +1563 -1336
  29. package/dist/chunk-ABVTYQIJ.js.map +1 -0
  30. package/dist/{chunk-U7QVISQS.js → chunk-CAKOFJZD.js} +450 -40
  31. package/dist/chunk-CAKOFJZD.js.map +1 -0
  32. package/dist/{chunk-IBVKPLHT.mjs → chunk-E3I4MT53.mjs} +422 -12
  33. package/dist/chunk-E3I4MT53.mjs.map +1 -0
  34. package/dist/chunk-GHFMKH3M.js +280 -0
  35. package/dist/chunk-GHFMKH3M.js.map +1 -0
  36. package/dist/chunk-GKDHYB2R.js +110 -0
  37. package/dist/chunk-GKDHYB2R.js.map +1 -0
  38. package/dist/{chunk-ZJI5ZZW5.mjs → chunk-Q6J2ICKO.mjs} +3 -3
  39. package/dist/{chunk-4O7R2Z2O.mjs → chunk-QWZAGQWJ.mjs} +2175 -1948
  40. package/dist/chunk-QWZAGQWJ.mjs.map +1 -0
  41. package/dist/chunk-U65JPKO3.mjs +110 -0
  42. package/dist/chunk-U65JPKO3.mjs.map +1 -0
  43. package/dist/client/index.d.mts +6 -5
  44. package/dist/client/index.d.ts +6 -5
  45. package/dist/client/index.js +5 -5
  46. package/dist/client/index.js.map +1 -1
  47. package/dist/client/index.mjs +4 -4
  48. package/dist/components/index.d.mts +33 -10
  49. package/dist/components/index.d.ts +33 -10
  50. package/dist/components/index.js +7 -5
  51. package/dist/components/index.js.map +1 -1
  52. package/dist/components/index.mjs +6 -4
  53. package/dist/{config-B6UB7j69.d.ts → config-3N1LSrcC.d.ts} +1 -1
  54. package/dist/{config-Cd5xTEfZ.d.ts → config-AVtuvfM1.d.ts} +1 -1
  55. package/dist/{config-n-ZpPmOL.d.mts → config-BSKTYDoh.d.mts} +1 -1
  56. package/dist/{config-YyhFHxkU.d.mts → config-BmufyEj6.d.mts} +1 -1
  57. package/dist/contexts/index.d.mts +5 -4
  58. package/dist/contexts/index.d.ts +5 -4
  59. package/dist/contexts/index.js +5 -5
  60. package/dist/contexts/index.mjs +4 -4
  61. package/dist/core/index.d.mts +31 -14
  62. package/dist/core/index.d.ts +31 -14
  63. package/dist/core/index.js +9 -5
  64. package/dist/core/index.js.map +1 -1
  65. package/dist/core/index.mjs +8 -4
  66. package/dist/features/analytics/index.css +30 -0
  67. package/dist/features/analytics/index.css.map +1 -0
  68. package/dist/features/analytics/index.d.mts +508 -0
  69. package/dist/features/analytics/index.d.ts +508 -0
  70. package/dist/features/analytics/index.js +1166 -0
  71. package/dist/features/analytics/index.js.map +1 -0
  72. package/dist/features/analytics/index.mjs +1166 -0
  73. package/dist/features/analytics/index.mjs.map +1 -0
  74. package/dist/features/analytics/server.d.mts +28 -0
  75. package/dist/features/analytics/server.d.ts +28 -0
  76. package/dist/features/analytics/server.js +41 -0
  77. package/dist/features/analytics/server.js.map +1 -0
  78. package/dist/features/analytics/server.mjs +41 -0
  79. package/dist/features/analytics/server.mjs.map +1 -0
  80. package/dist/features/help/index.d.mts +2 -2
  81. package/dist/features/help/index.d.ts +2 -2
  82. package/dist/features/help/index.js +43 -43
  83. package/dist/features/help/index.js.map +1 -1
  84. package/dist/features/help/index.mjs +9 -9
  85. package/dist/features/help/index.mjs.map +1 -1
  86. package/dist/features/tokenusage/index.d.mts +2 -2
  87. package/dist/features/tokenusage/index.d.ts +2 -2
  88. package/dist/features/tokenusage/index.js +141 -223
  89. package/dist/features/tokenusage/index.js.map +1 -1
  90. package/dist/features/tokenusage/index.mjs +28 -110
  91. package/dist/features/tokenusage/index.mjs.map +1 -1
  92. package/dist/index.d.mts +11 -9
  93. package/dist/index.d.ts +11 -9
  94. package/dist/index.js +10 -6
  95. package/dist/index.js.map +1 -1
  96. package/dist/index.mjs +9 -5
  97. package/dist/notification.interface-BNCvnVCW.d.ts +34 -0
  98. package/dist/notification.interface-P_pNfzZQ.d.mts +34 -0
  99. package/dist/{s3.service-Bqr-fQmi.d.ts → s3.service-CbUa6a-8.d.ts} +3 -2
  100. package/dist/{s3.service-Buiy1ncj.d.mts → s3.service-DAp8FbuG.d.mts} +3 -2
  101. package/dist/server/index.d.mts +5 -4
  102. package/dist/server/index.d.ts +5 -4
  103. package/dist/server/index.js +39 -279
  104. package/dist/server/index.js.map +1 -1
  105. package/dist/server/index.mjs +20 -260
  106. package/dist/server/index.mjs.map +1 -1
  107. package/dist/{types-bsm8Fp55.d.ts → types-Cn153-mH.d.ts} +1 -1
  108. package/dist/{types-Bq_UA8Lg.d.mts → types-D0uLYV2P.d.mts} +1 -1
  109. package/dist/{usePageUrlGenerator-J_ECv-oQ.d.ts → usePageUrlGenerator-B-3FRKBD.d.ts} +1 -1
  110. package/dist/{usePageUrlGenerator-uKBjAKqC.d.mts → usePageUrlGenerator-CCJ_cdQy.d.mts} +1 -1
  111. package/dist/{useSocket-BcH7l7B8.d.mts → useSocket-B9gTbuCj.d.mts} +1 -1
  112. package/dist/{useSocket-DCZd_eJk.d.ts → useSocket-BHVIyigI.d.ts} +1 -1
  113. package/dist/{notification.interface-MnRt1Tjm.d.mts → user.interface-CSaGWFbg.d.mts} +1 -31
  114. package/dist/{notification.interface-CjzV_KII.d.ts → user.interface-CrmwJLYW.d.ts} +1 -31
  115. package/package.json +11 -1
  116. package/src/client/index.ts +2 -0
  117. package/src/components/containers/PageContainer.tsx +1 -1
  118. package/src/components/containers/RoundPageContainer.tsx +60 -16
  119. package/src/components/containers/RoundPageContainerTitle.tsx +75 -35
  120. package/src/components/editors/BlockNoteEditor.tsx +13 -1
  121. package/src/components/forms/EntityMultiSelector.tsx +4 -4
  122. package/src/components/forms/EntitySelector.tsx +4 -4
  123. package/src/components/forms/FormBlockNote.tsx +21 -1
  124. package/src/components/forms/FormCheckbox.tsx +6 -2
  125. package/src/components/forms/__tests__/useEditorDialog.test.ts +47 -0
  126. package/src/components/forms/useEditorDialog.ts +22 -0
  127. package/src/components/navigations/Breadcrumb.tsx +45 -16
  128. package/src/components/navigations/Header.tsx +16 -3
  129. package/src/components/navigations/MobileNavigationBar.tsx +12 -0
  130. package/src/components/navigations/__tests__/MobileNavigationBar.spec.tsx +17 -0
  131. package/src/components/tables/ContentListTable.tsx +52 -32
  132. package/src/core/index.ts +4 -0
  133. package/src/core/registry/ModuleRegistry.ts +7 -0
  134. package/src/features/__tests__/i18n-keys.contract.spec.ts +1 -0
  135. package/src/features/analytics/__tests__/server-entry.spec.tsx +64 -0
  136. package/src/features/analytics/analytics.modules.ts +30 -0
  137. package/src/features/analytics/components/AnalyticsAdminContainer.tsx +97 -0
  138. package/src/features/analytics/components/AnalyticsAdminFilterBar.tsx +120 -0
  139. package/src/features/analytics/components/AnalyticsAdminPageContainer.tsx +25 -0
  140. package/src/features/analytics/components/AnalyticsAdminTiles.tsx +191 -0
  141. package/src/features/analytics/components/AnalyticsBreakdownTable.tsx +105 -0
  142. package/src/features/analytics/components/AnalyticsJourneyContainer.tsx +246 -0
  143. package/src/features/analytics/components/AnalyticsJourneyPageContainer.tsx +14 -0
  144. package/src/features/analytics/components/AnalyticsSessionsTable.tsx +107 -0
  145. package/src/features/analytics/components/AnalyticsTimelineChart.tsx +175 -0
  146. package/src/features/analytics/components/AnalyticsTracker.tsx +112 -0
  147. package/src/features/analytics/components/__tests__/AnalyticsAdminTiles.spec.tsx +39 -0
  148. package/src/features/analytics/components/__tests__/AnalyticsBreakdownTable.spec.tsx +39 -0
  149. package/src/features/analytics/components/__tests__/AnalyticsJourneyContainer.spec.tsx +124 -0
  150. package/src/features/analytics/components/__tests__/AnalyticsPageContainers.spec.tsx +117 -0
  151. package/src/features/analytics/components/__tests__/AnalyticsSessionsTable.spec.tsx +121 -0
  152. package/src/features/analytics/components/__tests__/AnalyticsTimelineChart.spec.tsx +35 -0
  153. package/src/features/analytics/components/__tests__/AnalyticsTracker.spec.tsx +248 -0
  154. package/src/features/analytics/components/server/AnalyticsAdminPage.tsx +28 -0
  155. package/src/features/analytics/components/server/AnalyticsJourneyPage.tsx +25 -0
  156. package/src/features/analytics/contexts/AnalyticsAdminContext.tsx +273 -0
  157. package/src/features/analytics/contexts/__tests__/AnalyticsAdminContext.spec.tsx +140 -0
  158. package/src/features/analytics/data/AnalyticsAdminService.ts +91 -0
  159. package/src/features/analytics/data/AnalyticsService.ts +23 -0
  160. package/src/features/analytics/data/__tests__/AnalyticsAdminService.spec.ts +117 -0
  161. package/src/features/analytics/data/__tests__/AnalyticsService.spec.ts +56 -0
  162. package/src/features/analytics/data/__tests__/models.spec.ts +300 -0
  163. package/src/features/analytics/data/analytics-breakdown.interface.ts +9 -0
  164. package/src/features/analytics/data/analytics-breakdown.ts +65 -0
  165. package/src/features/analytics/data/analytics-event.interface.ts +7 -0
  166. package/src/features/analytics/data/analytics-event.ts +63 -0
  167. package/src/features/analytics/data/analytics-page-view.interface.ts +9 -0
  168. package/src/features/analytics/data/analytics-page-view.ts +57 -0
  169. package/src/features/analytics/data/analytics-session.interface.ts +23 -0
  170. package/src/features/analytics/data/analytics-session.ts +144 -0
  171. package/src/features/analytics/data/analytics-summary.interface.ts +11 -0
  172. package/src/features/analytics/data/analytics-summary.ts +79 -0
  173. package/src/features/analytics/data/analytics-timeline.interface.ts +10 -0
  174. package/src/features/analytics/data/analytics-timeline.ts +67 -0
  175. package/src/features/analytics/data/analytics.types.ts +97 -0
  176. package/src/features/analytics/data/index.ts +15 -0
  177. package/src/features/analytics/i18n-keys.ts +76 -0
  178. package/src/features/analytics/index.ts +31 -0
  179. package/src/features/analytics/lib/__tests__/utm.spec.ts +20 -0
  180. package/src/features/analytics/lib/__tests__/visitorCookie.spec.ts +63 -0
  181. package/src/features/analytics/lib/utm.ts +29 -0
  182. package/src/features/analytics/lib/visitorCookie.ts +32 -0
  183. package/src/features/analytics/server-entry.ts +8 -0
  184. package/src/features/assistant/components/parts/AssistantBlockNoteComposer.tsx +1 -1
  185. package/src/features/assistant/components/parts/AssistantComposer.tsx +1 -1
  186. package/src/features/assistant-message/components/MessageItem.tsx +2 -2
  187. package/src/features/assistant-message/components/parts/tabs/CitationsTab.tsx +23 -5
  188. package/src/features/auth/components/containers/AuthContainer.tsx +8 -1
  189. package/src/features/company/components/details/CompanyContent.tsx +3 -1
  190. package/src/features/company/components/details/CompanyDetails.tsx +9 -1
  191. package/src/features/company/components/details/TokenStatusIndicator.tsx +42 -13
  192. package/src/features/help/components/HelpHeader.tsx +7 -5
  193. package/src/features/tokenusage/components/TokenUsageAdminFilterBar.tsx +9 -6
  194. package/src/features/tokenusage/lib/__tests__/formatters.spec.ts +21 -0
  195. package/src/features/tokenusage/lib/formatters.ts +5 -1
  196. package/src/features/waitlist/components/lists/WaitlistList.tsx +68 -121
  197. package/src/features/waitlist/data/WaitlistService.ts +2 -2
  198. package/src/features/waitlist/data/index.ts +1 -0
  199. package/src/features/waitlist/data/waitlist.fields.ts +8 -0
  200. package/src/features/waitlist/hooks/useWaitlistTableStructure.tsx +69 -30
  201. package/src/shadcnui/ui/button.tsx +1 -1
  202. package/src/shadcnui/ui/checkbox.tsx +1 -1
  203. package/src/shadcnui/ui/command.tsx +4 -4
  204. package/src/shadcnui/ui/dialog.tsx +2 -0
  205. package/src/shadcnui/ui/input.tsx +1 -1
  206. package/src/shadcnui/ui/popover.tsx +9 -0
  207. package/src/shadcnui/ui/select.tsx +1 -1
  208. package/src/shadcnui/ui/sheet.tsx +8 -1
  209. package/src/shadcnui/ui/table.tsx +2 -2
  210. package/src/shadcnui/ui/textarea.tsx +1 -1
  211. package/dist/BlockNoteEditor-35D4R7HJ.js.map +0 -1
  212. package/dist/JsonApiRequest-G625QAH5.js +0 -26
  213. package/dist/chunk-2XCUAHQT.js.map +0 -1
  214. package/dist/chunk-4O7R2Z2O.mjs.map +0 -1
  215. package/dist/chunk-IBVKPLHT.mjs.map +0 -1
  216. package/dist/chunk-U7QVISQS.js.map +0 -1
  217. /package/dist/{BlockNoteViewer-KDKGUIG5.mjs.map → BlockNoteViewer-KRTAFTUX.mjs.map} +0 -0
  218. /package/dist/{JsonApiRequest-RRMNV7YE.mjs.map → JsonApiRequest-UHOWNI3O.mjs.map} +0 -0
  219. /package/dist/{chunk-T76WLDE5.mjs.map → chunk-37DMFVLN.mjs.map} +0 -0
  220. /package/dist/{chunk-ZJI5ZZW5.mjs.map → chunk-Q6J2ICKO.mjs.map} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/features/analytics/components/AnalyticsTracker.tsx","../../../src/features/analytics/data/AnalyticsService.ts","../../../src/features/analytics/lib/utm.ts","../../../src/features/analytics/lib/visitorCookie.ts","../../../src/features/analytics/contexts/AnalyticsAdminContext.tsx","../../../src/features/analytics/components/AnalyticsAdminFilterBar.tsx","../../../src/features/analytics/data/analytics.types.ts","../../../src/features/analytics/data/AnalyticsAdminService.ts","../../../src/features/analytics/components/AnalyticsAdminContainer.tsx","../../../src/features/analytics/components/AnalyticsAdminTiles.tsx","../../../src/features/analytics/components/AnalyticsBreakdownTable.tsx","../../../src/features/analytics/components/AnalyticsSessionsTable.tsx","../../../src/features/analytics/components/AnalyticsTimelineChart.tsx","../../../src/features/analytics/components/AnalyticsJourneyContainer.tsx","../../../src/features/analytics/components/AnalyticsAdminPageContainer.tsx","../../../src/features/analytics/components/AnalyticsJourneyPageContainer.tsx","../../../src/features/analytics/i18n-keys.ts"],"sourcesContent":["\"use client\";\n\nimport { usePathname } from \"next/navigation\";\nimport { useEffect, useRef } from \"react\";\nimport { v4 } from \"uuid\";\nimport { AnalyticsService } from \"../data/AnalyticsService\";\nimport type { AnalyticsEventInput } from \"../data/analytics.types\";\nimport { readUtm } from \"../lib/utm\";\nimport { clearVisitorCookie, ensureVisitorCookie } from \"../lib/visitorCookie\";\n\n// The dashboard itself is never tracked, so administrators reading it do not inflate it.\nconst EXCLUDED_PREFIX = \"/administration/analytics\";\n\n// The API rejects longer referrers.\nconst MAX_REFERRER_LENGTH = 2048;\n\ntype Attribution = Pick<\n AnalyticsEventInput,\n \"referrer\" | \"utmSource\" | \"utmMedium\" | \"utmCampaign\" | \"utmTerm\" | \"utmContent\"\n>;\n\ntype AnalyticsTrackerProps = {\n hasConsent: boolean;\n resolveSection: (pathname: string) => \"public\" | \"app\";\n sensitivePrefixes?: string[];\n};\n\n/**\n * Returns the referrer only when it comes from another host, so internal\n * navigation is never stored as a traffic source.\n */\nfunction readExternalReferrer(): string | undefined {\n if (!document.referrer) return undefined;\n try {\n if (new URL(document.referrer).host === window.location.host) return undefined;\n } catch {\n return undefined;\n }\n return document.referrer.slice(0, MAX_REFERRER_LENGTH);\n}\n\n/**\n * Replaces every segment after a sensitive prefix with \":code\", so single-use\n * account codes in the URL never leave the browser.\n */\nfunction maskSensitivePath(path: string, sensitivePrefixes: string[] | undefined): string {\n if (!sensitivePrefixes) return path;\n\n for (const prefix of sensitivePrefixes) {\n if (path === prefix) return path;\n if (path.startsWith(`${prefix}/`)) {\n const segments = path\n .slice(prefix.length + 1)\n .split(\"/\")\n .filter((segment) => segment.length > 0);\n return [prefix, ...segments.map(() => \":code\")].join(\"/\");\n }\n }\n\n return path;\n}\n\nexport function AnalyticsTracker({ hasConsent, resolveSection, sensitivePrefixes }: AnalyticsTrackerProps): null {\n const pathname = usePathname();\n const lastPath = useRef<string | null>(null);\n const lastConsent = useRef<boolean>(false);\n const attribution = useRef<Attribution | null>(null);\n\n useEffect(() => {\n if (!hasConsent) clearVisitorCookie();\n\n if (!pathname) return;\n if (typeof navigator !== \"undefined\" && navigator.webdriver) return;\n\n const path = pathname.split(/[?#]/)[0] || \"/\";\n if (path.startsWith(EXCLUDED_PREFIX)) return;\n\n // One event per distinct path. The same path is sent again only when consent\n // was just granted, so the processor starts a new visitor on the cookie id.\n const isNewPath = lastPath.current !== path;\n const consentJustGranted = hasConsent && !lastConsent.current;\n lastConsent.current = hasConsent;\n if (!isNewPath && !consentJustGranted) return;\n lastPath.current = path;\n\n const sentPath = maskSensitivePath(path, sensitivePrefixes);\n\n const input: AnalyticsEventInput = {\n id: v4(),\n path: sentPath,\n section: resolveSection(sentPath),\n screenWidth: window.innerWidth,\n };\n\n if (hasConsent) input.visitorId = ensureVisitorCookie();\n\n // The first event of the page load carries the attribution. The event sent\n // because consent was just granted carries it again, so the session opened\n // on the cookie id keeps the source the visitor arrived from.\n const isFirstEvent = attribution.current === null;\n if (isFirstEvent) {\n const referrer = readExternalReferrer();\n attribution.current = { ...(referrer ? { referrer } : {}), ...readUtm(window.location.search) };\n }\n if (isFirstEvent || consentJustGranted) Object.assign(input, attribution.current);\n\n // AnalyticsService.track never rejects: it logs and swallows its own failures.\n void AnalyticsService.track(input);\n }, [pathname, hasConsent, resolveSection, sensitivePrefixes]);\n\n return null;\n}\n","import { AbstractService, EndpointCreator, HttpMethod, Modules } from \"../../../core\";\nimport { AnalyticsEventInput } from \"./analytics.types\";\n\nexport class AnalyticsService extends AbstractService {\n /**\n * Posts one page view. Fire-and-forget: a lost page view must never surface\n * an error to the visitor, so the global handler is suppressed and any\n * rejection is logged at debug level and swallowed here.\n */\n static async track(input: AnalyticsEventInput): Promise<void> {\n try {\n await this.callApi({\n type: Modules.AnalyticsEvent,\n method: HttpMethod.POST,\n endpoint: new EndpointCreator({ endpoint: Modules.AnalyticsEvent }).generate(),\n input,\n suppressGlobalError: true,\n });\n } catch (error) {\n console.debug(\"Analytics event not sent\", error);\n }\n }\n}\n","import { AnalyticsEventInput } from \"../data/analytics.types\";\n\ntype UtmFields = Pick<AnalyticsEventInput, \"utmSource\" | \"utmMedium\" | \"utmCampaign\" | \"utmTerm\" | \"utmContent\">;\n\nconst MAX_LENGTH = 255;\n\nconst UTM_PARAMS: [string, keyof UtmFields][] = [\n [\"utm_source\", \"utmSource\"],\n [\"utm_medium\", \"utmMedium\"],\n [\"utm_campaign\", \"utmCampaign\"],\n [\"utm_term\", \"utmTerm\"],\n [\"utm_content\", \"utmContent\"],\n];\n\n/**\n * Reads the five utm_* parameters from a query string. Absent or empty values\n * are left out; every value is cut to 255 characters.\n */\nexport function readUtm(search: string): UtmFields {\n const params = new URLSearchParams(search);\n const response: UtmFields = {};\n\n for (const [param, field] of UTM_PARAMS) {\n const value = params.get(param);\n if (value) response[field] = value.slice(0, MAX_LENGTH);\n }\n\n return response;\n}\n","import { deleteCookie, getCookie, setCookie } from \"cookies-next\";\nimport { v4 } from \"uuid\";\n\nexport const ANALYTICS_VISITOR_COOKIE = \"analytics_visitor\";\n\n/** 395 days: the 13-month retention window. */\nconst MAX_AGE_SECONDS = 395 * 24 * 3600;\n\nexport function readVisitorCookie(): string | undefined {\n const value = getCookie(ANALYTICS_VISITOR_COOKIE);\n return typeof value === \"string\" && value !== \"\" ? value : undefined;\n}\n\n/** Returns the existing visitor id, or creates and stores a new one. */\nexport function ensureVisitorCookie(): string {\n const existing = readVisitorCookie();\n if (existing) return existing;\n\n const id = v4();\n setCookie(ANALYTICS_VISITOR_COOKIE, id, {\n maxAge: MAX_AGE_SECONDS,\n sameSite: \"lax\",\n path: \"/\",\n secure: typeof location !== \"undefined\" && location.protocol === \"https:\",\n });\n\n return id;\n}\n\nexport function clearVisitorCookie(): void {\n deleteCookie(ANALYTICS_VISITOR_COOKIE, { path: \"/\" });\n}\n","\"use client\";\n\nimport { useTranslations } from \"next-intl\";\nimport { createContext, ReactNode, useCallback, useContext, useEffect, useMemo, useState } from \"react\";\nimport { Modules, NextRef } from \"../../../core\";\nimport { SharedProvider } from \"../../../contexts\";\nimport { usePageUrlGenerator } from \"../../../hooks\";\nimport { BreadcrumbItemData } from \"../../../interfaces\";\nimport { AnalyticsAdminFilterBar } from \"../components/AnalyticsAdminFilterBar\";\nimport type { AnalyticsBreakdownInterface } from \"../data/analytics-breakdown.interface\";\nimport type { AnalyticsSessionInterface } from \"../data/analytics-session.interface\";\nimport type { AnalyticsSummaryInterface } from \"../data/analytics-summary.interface\";\nimport type { AnalyticsTimelineInterface } from \"../data/analytics-timeline.interface\";\nimport {\n ANALYTICS_DIMENSIONS,\n type AnalyticsDimension,\n type AnalyticsGranularity,\n type AnalyticsSection,\n} from \"../data/analytics.types\";\nimport { AnalyticsAdminService } from \"../data/AnalyticsAdminService\";\n\n/**\n * Default route the breadcrumb links back to. A host app that mounts the page\n * elsewhere overrides it with the `pageUrl` prop — the constant stays here\n * because the breadcrumb is built by this provider, not by the app.\n */\nconst ANALYTICS_ADMIN_PAGE_URL = \"/administration/analytics\";\n\n/** Default base route of the journey page each session row links to. */\nconst ANALYTICS_SESSION_PAGE_URL = \"/administration/analytics/sessions\";\n\n/** Rows kept per breakdown tab before the repository folds the tail into \"other\". */\nconst BREAKDOWN_LIMIT = 20;\n\n/** The window the page opens on, in days. */\nconst DEFAULT_RANGE_DAYS = 30;\n\nconst EMPTY_BREAKDOWNS: Record<AnalyticsDimension, AnalyticsBreakdownInterface[]> = {\n source: [],\n medium: [],\n campaign: [],\n referrer: [],\n route: [],\n landing: [],\n};\n\nexport type AnalyticsAdminFilterState = {\n /** ISO 8601 instant. */\n from: string;\n /** ISO 8601 instant. */\n to: string;\n section: AnalyticsSection;\n granularity: AnalyticsGranularity;\n};\n\nexport interface AnalyticsAdminContextType {\n /** The six summary rows: {public, app, total} × {current, previous}. */\n summary: AnalyticsSummaryInterface[];\n timeline: AnalyticsTimelineInterface[];\n breakdowns: Record<AnalyticsDimension, AnalyticsBreakdownInterface[]>;\n sessions: AnalyticsSessionInterface[];\n filters: AnalyticsAdminFilterState;\n /** Merges a partial patch into the current filters; every key is optional. */\n setFilters: (next: Partial<AnalyticsAdminFilterState>) => void;\n /** Appends the next page of sessions; a no-op when there is none. */\n loadMoreSessions: () => Promise<void>;\n hasMoreSessions: boolean;\n /** Base route of the journey page, as passed to the provider. */\n sessionPageUrl: string;\n isLoading: boolean;\n error: string | null;\n}\n\nconst AnalyticsAdminContext = createContext<AnalyticsAdminContextType | undefined>(undefined);\n\n/** The last 30 days up to now, which is the window the page opens on. */\nfunction defaultRange(): { from: string; to: string } {\n const now = new Date();\n const start = new Date(now.getTime() - DEFAULT_RANGE_DAYS * 24 * 60 * 60 * 1000);\n return { from: start.toISOString(), to: now.toISOString() };\n}\n\ntype AnalyticsAdminProviderProps = {\n children: ReactNode;\n /** ISO 8601 instant. Defaults to 30 days ago. */\n initialFrom?: string;\n /** ISO 8601 instant. Defaults to now. */\n initialTo?: string;\n /** Route the breadcrumb links back to. */\n pageUrl?: string;\n /** Base route of the journey page each session row links to. */\n sessionPageUrl?: string;\n};\n\n/**\n * Owns every filter the administrative analytics page reads, fetches the panels\n * behind it, and publishes the filter bar into the page title bar.\n *\n * The filter bar is rendered into `title.functions` here — NOT in the container —\n * because `RoundPageContainer`'s title bar reads `title.functions` from\n * `SharedContext`, and a descendant cannot inject nodes into an ancestor's\n * provider value. That is why the filter state lives at this level.\n */\nexport const AnalyticsAdminProvider = ({\n children,\n initialFrom,\n initialTo,\n pageUrl = ANALYTICS_ADMIN_PAGE_URL,\n sessionPageUrl = ANALYTICS_SESSION_PAGE_URL,\n}: AnalyticsAdminProviderProps) => {\n const t = useTranslations();\n const generateUrl = usePageUrlGenerator();\n\n const [filters, setFilterState] = useState<AnalyticsAdminFilterState>(() => {\n const range = defaultRange();\n return {\n from: initialFrom ?? range.from,\n to: initialTo ?? range.to,\n section: \"all\",\n granularity: \"day\",\n };\n });\n\n const [summary, setSummary] = useState<AnalyticsSummaryInterface[]>([]);\n const [timeline, setTimeline] = useState<AnalyticsTimelineInterface[]>([]);\n const [breakdowns, setBreakdowns] =\n useState<Record<AnalyticsDimension, AnalyticsBreakdownInterface[]>>(EMPTY_BREAKDOWNS);\n const [sessions, setSessions] = useState<AnalyticsSessionInterface[]>([]);\n /** The cursor URL of the next sessions page, as the API returned it. */\n const [sessionsNextPage, setSessionsNextPage] = useState<string | undefined>(undefined);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n const { from, to, section, granularity } = filters;\n\n useEffect(() => {\n // `cancelled` is the out-of-order guard: a filter change fires a new request\n // while the previous one is still in flight, and without this flag the slower\n // (older) response would land last and overwrite the fresher state.\n let cancelled = false;\n\n setIsLoading(true);\n setError(null);\n\n const base = { from, to, section };\n // callApi writes the response's next-page link into this ref.\n const sessionsNext: NextRef = {};\n\n Promise.all([\n AnalyticsAdminService.getSummary(base),\n AnalyticsAdminService.getTimeline({ ...base, granularity }),\n Promise.all(\n ANALYTICS_DIMENSIONS.map((dimension) =>\n AnalyticsAdminService.getBreakdown({ ...base, dimension, limit: BREAKDOWN_LIMIT }),\n ),\n ),\n AnalyticsAdminService.getSessions({ ...base, next: sessionsNext }),\n ])\n .then(([nextSummary, nextTimeline, nextBreakdowns, nextSessions]) => {\n if (cancelled) return;\n setSummary(nextSummary ?? []);\n setTimeline(nextTimeline ?? []);\n setBreakdowns(\n Object.fromEntries(\n ANALYTICS_DIMENSIONS.map((dimension, index) => [dimension, nextBreakdowns[index] ?? []]),\n ) as Record<AnalyticsDimension, AnalyticsBreakdownInterface[]>,\n );\n setSessions(nextSessions ?? []);\n setSessionsNextPage(sessionsNext.next);\n })\n .catch((err) => {\n if (cancelled) return;\n console.error(\"Failed to load analytics:\", err);\n setError(err instanceof Error ? err.message : String(err));\n })\n .finally(() => {\n if (cancelled) return;\n setIsLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n }, [from, to, section, granularity]);\n\n const setFilters = useCallback((next: Partial<AnalyticsAdminFilterState>) => {\n setFilterState((prev) => ({ ...prev, ...next }));\n }, []);\n\n const loadMoreSessions = useCallback(async () => {\n if (!sessionsNextPage) return;\n\n const sessionsNext: NextRef = {};\n try {\n const nextSessions = await AnalyticsAdminService.next<AnalyticsSessionInterface[]>({\n type: Modules.AnalyticsSession,\n endpoint: sessionsNextPage,\n next: sessionsNext,\n });\n setSessions((prev) => [...prev, ...(nextSessions ?? [])]);\n setSessionsNextPage(sessionsNext.next);\n } catch (err) {\n console.error(\"Failed to load more analytics sessions:\", err);\n setError(err instanceof Error ? err.message : String(err));\n }\n }, [sessionsNextPage]);\n\n const breadcrumb = (): BreadcrumbItemData[] => [\n {\n name: t(\"analytics.admin.title\"),\n href: generateUrl({ page: pageUrl }),\n },\n ];\n\n const title = () => ({\n type: t(\"analytics.admin.title\"),\n functions: (\n <AnalyticsAdminFilterBar\n key=\"analyticsAdminFilterBar\"\n from={from}\n to={to}\n section={section}\n granularity={granularity}\n onChange={setFilters}\n />\n ),\n });\n\n const hasMoreSessions = Boolean(sessionsNextPage);\n\n const contextValue = useMemo<AnalyticsAdminContextType>(\n () => ({\n summary,\n timeline,\n breakdowns,\n sessions,\n filters,\n setFilters,\n loadMoreSessions,\n hasMoreSessions,\n sessionPageUrl,\n isLoading,\n error,\n }),\n [\n summary,\n timeline,\n breakdowns,\n sessions,\n filters,\n setFilters,\n loadMoreSessions,\n hasMoreSessions,\n sessionPageUrl,\n isLoading,\n error,\n ],\n );\n\n return (\n <SharedProvider value={{ breadcrumbs: breadcrumb(), title: title() }}>\n <AnalyticsAdminContext.Provider value={contextValue}>{children}</AnalyticsAdminContext.Provider>\n </SharedProvider>\n );\n};\n\nexport const useAnalyticsAdmin = (): AnalyticsAdminContextType => {\n const ctx = useContext(AnalyticsAdminContext);\n if (!ctx) {\n throw new Error(\"useAnalyticsAdmin() called outside <AnalyticsAdminProvider>.\");\n }\n return ctx;\n};\n","\"use client\";\n\nimport { useTranslations } from \"next-intl\";\nimport { DateRangeSelector } from \"../../../components/forms/DateRangeSelector\";\nimport { Button } from \"../../../shadcnui\";\nimport { cn } from \"../../../utils\";\nimport {\n ANALYTICS_GRANULARITIES,\n ANALYTICS_SECTIONS,\n type AnalyticsGranularity,\n type AnalyticsSection,\n} from \"../data/analytics.types\";\n\ntype FilterState = {\n from: string;\n to: string;\n section: AnalyticsSection;\n granularity: AnalyticsGranularity;\n};\n\ntype Props = FilterState & {\n /** Receives ONLY the keys that changed. */\n onChange: (next: Partial<FilterState>) => void;\n};\n\n/**\n * The single control row above the KPI tiles.\n *\n * It is deliberately stateless: every control reports the one key it changed and\n * the owning context re-fetches. Keeping the whole filter state in one place is\n * what lets the page issue a single coordinated batch of requests instead of one\n * per control.\n */\nexport function AnalyticsAdminFilterBar({ section, granularity, onChange }: Props) {\n const t = useTranslations();\n\n const sectionLabels: Record<AnalyticsSection, string> = {\n all: t(\"analytics.admin.filter.section_all\"),\n public: t(\"analytics.admin.filter.section_public\"),\n app: t(\"analytics.admin.filter.section_app\"),\n };\n\n const granularityLabels: Record<AnalyticsGranularity, string> = {\n day: t(\"analytics.admin.filter.granularity_day\"),\n week: t(\"analytics.admin.filter.granularity_week\"),\n month: t(\"analytics.admin.filter.granularity_month\"),\n };\n\n return (\n <div className=\"flex flex-wrap items-center gap-2\">\n {/* The date button is a fixed 300px; on a phone it takes the full row instead. */}\n <div className=\"max-md:w-full max-md:[&_#date]:w-full\">\n <DateRangeSelector\n onDateChange={(range) => {\n if (!range?.from || !range?.to) return;\n onChange({ from: range.from.toISOString(), to: range.to.toISOString() });\n }}\n />\n </div>\n\n <Segmented\n ariaLabel={t(\"analytics.admin.filter.section\")}\n value={section}\n options={ANALYTICS_SECTIONS.map((value) => ({ value, label: sectionLabels[value] }))}\n onSelect={(value) => onChange({ section: value })}\n />\n\n <Segmented\n ariaLabel={t(\"analytics.admin.filter.granularity\")}\n value={granularity}\n options={ANALYTICS_GRANULARITIES.map((value) => ({ value, label: granularityLabels[value] }))}\n onSelect={(value) => onChange({ granularity: value })}\n />\n </div>\n );\n}\n\n/**\n * A segmented control built from plain Buttons.\n *\n * The design system has no ToggleGroup primitive, and Base UI triggers may never\n * wrap a Button, so the segmented look is composed from Buttons directly — the\n * selected segment takes the solid variant, the rest stay ghosts.\n */\nfunction Segmented<T extends string>({\n ariaLabel,\n value,\n options,\n onSelect,\n}: {\n ariaLabel: string;\n value: T;\n options: { value: T; label: string }[];\n onSelect: (value: T) => void;\n}) {\n return (\n <div\n role=\"group\"\n aria-label={ariaLabel}\n className=\"border-border inline-flex items-center gap-0.5 rounded-md border p-0.5\"\n >\n {options.map((option) => {\n const selected = option.value === value;\n return (\n <Button\n key={option.value}\n type=\"button\"\n size=\"sm\"\n variant={selected ? \"default\" : \"ghost\"}\n aria-pressed={selected}\n className={cn(!selected && \"text-muted-foreground\")}\n onClick={() => onSelect(option.value)}\n >\n {option.label}\n </Button>\n );\n })}\n </div>\n );\n}\n","// Each value list is the single source of its union type: components iterate\n// the constant, and the type is derived from it so the two cannot drift.\n\nexport const ANALYTICS_SECTIONS = [\"all\", \"public\", \"app\"] as const;\nexport type AnalyticsSection = (typeof ANALYTICS_SECTIONS)[number];\n\nexport const ANALYTICS_GRANULARITIES = [\"day\", \"week\", \"month\"] as const;\nexport type AnalyticsGranularity = (typeof ANALYTICS_GRANULARITIES)[number];\n\nexport const ANALYTICS_DIMENSIONS = [\"source\", \"medium\", \"campaign\", \"referrer\", \"route\", \"landing\"] as const;\nexport type AnalyticsDimension = (typeof ANALYTICS_DIMENSIONS)[number];\n\nexport type AnalyticsFilters = {\n /** ISO 8601 instant. */\n from: string;\n /** ISO 8601 instant. */\n to: string;\n section: AnalyticsSection;\n};\n\n/** One page view, as the tracker posts it to `analytics/events`. */\nexport type AnalyticsEventInput = {\n id: string;\n path: string;\n section: \"public\" | \"app\";\n referrer?: string;\n utmSource?: string;\n utmMedium?: string;\n utmCampaign?: string;\n utmTerm?: string;\n utmContent?: string;\n visitorId?: string;\n screenWidth?: number;\n};\n\n// Input types for the read-only models' createJsonApi(). `id` is always\n// required — JSON:API resource objects are identified.\n\nexport type AnalyticsSummaryInput = {\n id: string;\n section: string;\n window: string;\n visitors: number;\n sessions: number;\n pageViews: number;\n pagesPerSession: number;\n consentShare: number;\n};\n\nexport type AnalyticsTimelineInput = {\n id: string;\n /** Backend field type is \"date\" — emitted via formatLocalDate, never raw. */\n bucket: Date;\n section: string;\n visitors: number;\n sessions: number;\n pageViews: number;\n};\n\nexport type AnalyticsBreakdownInput = {\n id: string;\n dimension: string;\n key: string;\n visitors: number;\n sessions: number;\n pageViews: number;\n};\n\nexport type AnalyticsSessionInput = {\n id: string;\n visitorId: string;\n consented: boolean;\n /** Backend field type is \"datetime\" — emitted via toISOString(). */\n startedAt: Date;\n /** Backend field type is \"datetime\" — emitted via toISOString(). */\n lastSeenAt: Date;\n pageViews: number;\n section: string;\n landingRoute: string;\n referrerHost?: string;\n utmSource?: string;\n utmMedium?: string;\n utmCampaign?: string;\n utmTerm?: string;\n utmContent?: string;\n deviceType: string;\n userId?: string;\n};\n\nexport type AnalyticsPageViewInput = {\n id: string;\n /** Backend field type is \"datetime\" — emitted via toISOString(). */\n createdAt: Date;\n path: string;\n route: string;\n section: string;\n};\n","import { AbstractService, EndpointCreator, HttpMethod, Modules, NextRef } from \"../../../core\";\nimport { AnalyticsBreakdownInterface } from \"./analytics-breakdown.interface\";\nimport { AnalyticsPageViewInterface } from \"./analytics-page-view.interface\";\nimport { AnalyticsSessionInterface } from \"./analytics-session.interface\";\nimport { AnalyticsSummaryInterface } from \"./analytics-summary.interface\";\nimport { AnalyticsTimelineInterface } from \"./analytics-timeline.interface\";\nimport { AnalyticsDimension, AnalyticsFilters, AnalyticsGranularity } from \"./analytics.types\";\n\nfunction withFilters(endpoint: EndpointCreator, filters: AnalyticsFilters): EndpointCreator {\n endpoint.addAdditionalParam(\"from\", filters.from);\n endpoint.addAdditionalParam(\"to\", filters.to);\n endpoint.addAdditionalParam(\"section\", filters.section);\n return endpoint;\n}\n\nexport class AnalyticsAdminService extends AbstractService {\n /** Headline tiles: one row per window (current, previous). */\n static async getSummary(filters: AnalyticsFilters): Promise<AnalyticsSummaryInterface[]> {\n const endpoint = withFilters(new EndpointCreator({ endpoint: Modules.AnalyticsSummary }), filters);\n\n return this.callApi<AnalyticsSummaryInterface[]>({\n type: Modules.AnalyticsSummary,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n static async getTimeline(\n params: AnalyticsFilters & { granularity: AnalyticsGranularity },\n ): Promise<AnalyticsTimelineInterface[]> {\n const endpoint = withFilters(new EndpointCreator({ endpoint: Modules.AnalyticsTimeline }), params);\n endpoint.addAdditionalParam(\"granularity\", params.granularity);\n\n return this.callApi<AnalyticsTimelineInterface[]>({\n type: Modules.AnalyticsTimeline,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n static async getBreakdown(\n params: AnalyticsFilters & { dimension: AnalyticsDimension; limit?: number },\n ): Promise<AnalyticsBreakdownInterface[]> {\n const endpoint = withFilters(new EndpointCreator({ endpoint: Modules.AnalyticsBreakdown }), params);\n endpoint.addAdditionalParam(\"dimension\", params.dimension);\n if (params.limit !== undefined) endpoint.addAdditionalParam(\"limit\", String(params.limit));\n\n return this.callApi<AnalyticsBreakdownInterface[]>({\n type: Modules.AnalyticsBreakdown,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n });\n }\n\n /** Newest sessions first, cursor-paginated through `next`. */\n static async getSessions(\n params: AnalyticsFilters & { visitorId?: string; userId?: string; next?: NextRef },\n ): Promise<AnalyticsSessionInterface[]> {\n const endpoint = withFilters(new EndpointCreator({ endpoint: Modules.AnalyticsSession }), params);\n if (params.visitorId) endpoint.addAdditionalParam(\"visitorId\", params.visitorId);\n if (params.userId) endpoint.addAdditionalParam(\"userId\", params.userId);\n\n return this.callApi<AnalyticsSessionInterface[]>({\n type: Modules.AnalyticsSession,\n method: HttpMethod.GET,\n endpoint: endpoint.generate(),\n next: params.next,\n });\n }\n\n static async getSession(params: { id: string }): Promise<AnalyticsSessionInterface> {\n return this.callApi<AnalyticsSessionInterface>({\n type: Modules.AnalyticsSession,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({ endpoint: Modules.AnalyticsSession, id: params.id }).generate(),\n });\n }\n\n /** The journey: every page view of one session, oldest first. */\n static async getSessionPageViews(params: { sessionId: string }): Promise<AnalyticsPageViewInterface[]> {\n return this.callApi<AnalyticsPageViewInterface[]>({\n type: Modules.AnalyticsPageView,\n method: HttpMethod.GET,\n endpoint: new EndpointCreator({\n endpoint: Modules.AnalyticsSession,\n id: params.sessionId,\n childEndpoint: Modules.AnalyticsPageView,\n }).generate(),\n });\n }\n}\n","\"use client\";\n\nimport { useTranslations } from \"next-intl\";\nimport { RoundPageContainer } from \"../../../components\";\nimport { Card, CardContent, CardHeader, CardTitle } from \"../../../shadcnui\";\nimport { useAnalyticsAdmin } from \"../contexts/AnalyticsAdminContext\";\nimport { AnalyticsAdminTiles } from \"./AnalyticsAdminTiles\";\nimport { AnalyticsBreakdownTable } from \"./AnalyticsBreakdownTable\";\nimport { AnalyticsSessionsTable } from \"./AnalyticsSessionsTable\";\nimport { AnalyticsTimelineChart } from \"./AnalyticsTimelineChart\";\n\n/**\n * Page body for the administrative analytics dashboard.\n *\n * Stateless by design — every value it renders comes from\n * `useAnalyticsAdmin()`. The filter bar is deliberately NOT here: it belongs to\n * the page title bar, which `RoundPageContainer` fills from `SharedContext`, so\n * the provider publishes it (see AnalyticsAdminContext).\n */\nexport function AnalyticsAdminContainer() {\n const t = useTranslations();\n const {\n summary,\n timeline,\n breakdowns,\n sessions,\n loadMoreSessions,\n hasMoreSessions,\n sessionPageUrl,\n isLoading,\n error,\n } = useAnalyticsAdmin();\n\n if (error) {\n return (\n <RoundPageContainer fullWidth forceHeader>\n <div className=\"p-4\">\n <Card>\n <CardContent>\n <p className=\"text-destructive text-xs/relaxed\">{error}</p>\n </CardContent>\n </Card>\n </div>\n </RoundPageContainer>\n );\n }\n\n // Loading renders nothing in the body: the title bar (with the filter bar) is\n // already mounted, so a spinner would only make the controls jump on arrival.\n if (isLoading) return <RoundPageContainer fullWidth forceHeader />;\n\n return (\n <RoundPageContainer fullWidth forceHeader>\n <div className=\"flex w-full flex-col gap-4 p-4\">\n <div className=\"flex flex-col gap-2\">\n <AnalyticsAdminTiles summary={summary} />\n {/* Days past the raw-event retention are served from daily summaries.\n Whether the window reaches them is the backend's knowledge, not the\n client's, so the caption is always shown. */}\n <p className=\"text-muted-foreground text-xs\">{t(\"analytics.admin.tiles.summarised_note\")}</p>\n </div>\n\n <Card>\n <CardHeader>\n <CardTitle>{t(\"analytics.admin.panels.over_time\")}</CardTitle>\n </CardHeader>\n <CardContent>\n <AnalyticsTimelineChart rows={timeline} />\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>{t(\"analytics.admin.panels.breakdown\")}</CardTitle>\n </CardHeader>\n <CardContent>\n <AnalyticsBreakdownTable breakdowns={breakdowns} />\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>{t(\"analytics.admin.panels.sessions\")}</CardTitle>\n </CardHeader>\n <CardContent>\n <AnalyticsSessionsTable\n rows={sessions}\n sessionPageUrl={sessionPageUrl}\n onLoadMore={loadMoreSessions}\n hasMore={hasMoreSessions}\n />\n </CardContent>\n </Card>\n </div>\n </RoundPageContainer>\n );\n}\n","\"use client\";\n\nimport { ArrowDownIcon, ArrowUpIcon } from \"lucide-react\";\nimport { useTranslations } from \"next-intl\";\nimport { MicroLabel } from \"../../../components\";\nimport { Card, CardContent } from \"../../../shadcnui\";\nimport { cn } from \"../../../utils\";\nimport { useUsageFormatters } from \"../../tokenusage/lib/formatters\";\nimport { percentageDelta } from \"../../tokenusage/lib/metrics\";\nimport type { AnalyticsSummaryInterface } from \"../data/analytics-summary.interface\";\n\ntype Props = {\n /** The six summary rows: {public, app, total} × {current, previous}. */\n summary: AnalyticsSummaryInterface[];\n};\n\ntype SummaryMetrics = Pick<\n AnalyticsSummaryInterface,\n \"visitors\" | \"sessions\" | \"pageViews\" | \"pagesPerSession\" | \"consentShare\"\n>;\n\nconst ZERO: SummaryMetrics = { visitors: 0, sessions: 0, pageViews: 0, pagesPerSession: 0, consentShare: 0 };\n\n/**\n * The KPI header of the administrative analytics page.\n *\n * Three lead tiles carry the traffic volumes — visitors, sessions, page views —\n * each with its delta against the equal-length preceding window. Two supporting\n * tiles below carry the ratios that give those numbers context.\n *\n * The tiles read the `total` row: the backend already applies the section filter\n * to it, so it is the figure for whatever the filter bar selected. The backend\n * always returns both windows, which is why no tile has to branch on a missing\n * row: an absent one is simply zero-filled here.\n */\nexport function AnalyticsAdminTiles({ summary }: Props) {\n const t = useTranslations();\n const { decimal } = useUsageFormatters();\n\n const rowFor = (window: string): SummaryMetrics =>\n summary.find((r) => r.section === \"total\" && r.window === window) ?? ZERO;\n\n const current = rowFor(\"current\");\n const previous = rowFor(\"previous\");\n\n const previousLabel = t(\"analytics.admin.tiles.vs_previous\");\n\n return (\n <div className=\"grid gap-3\">\n <div className=\"grid gap-3 sm:grid-cols-3\">\n <LeadTile\n testId=\"analytics-tile-visitors\"\n label={t(\"analytics.admin.tiles.visitors\")}\n value={decimal(current.visitors, 0)}\n delta={percentageDelta(current.visitors, previous.visitors)}\n previousLabel={previousLabel}\n decimal={decimal}\n />\n <LeadTile\n testId=\"analytics-tile-sessions\"\n label={t(\"analytics.admin.tiles.sessions\")}\n value={decimal(current.sessions, 0)}\n delta={percentageDelta(current.sessions, previous.sessions)}\n previousLabel={previousLabel}\n decimal={decimal}\n />\n <LeadTile\n testId=\"analytics-tile-page-views\"\n label={t(\"analytics.admin.tiles.page_views\")}\n value={decimal(current.pageViews, 0)}\n delta={percentageDelta(current.pageViews, previous.pageViews)}\n previousLabel={previousLabel}\n decimal={decimal}\n />\n </div>\n\n <div className=\"grid gap-3 sm:grid-cols-2\">\n <SupportingTile\n testId=\"analytics-tile-pages-per-session\"\n label={t(\"analytics.admin.tiles.pages_per_session\")}\n value={decimal(current.pagesPerSession, 1)}\n delta={percentageDelta(current.pagesPerSession, previous.pagesPerSession)}\n decimal={decimal}\n />\n <SupportingTile\n testId=\"analytics-tile-consent-share\"\n label={t(\"analytics.admin.tiles.consent_share\")}\n // consentShare is a 0..1 ratio on the wire.\n value={`${decimal(current.consentShare * 100, 0)}%`}\n decimal={decimal}\n />\n </div>\n </div>\n );\n}\n\nfunction LeadTile({\n testId,\n label,\n value,\n delta,\n previousLabel,\n decimal,\n}: {\n testId: string;\n label: string;\n value: string;\n delta: number | undefined;\n previousLabel: string;\n /** Passed down because `Delta` is a plain function, not a component: it cannot call the hook itself. */\n decimal: (value: number, decimals: number) => string;\n}) {\n return (\n <Card data-testid={testId}>\n <CardContent className=\"grid gap-1\">\n <MicroLabel>{label}</MicroLabel>\n <span className=\"text-primary text-xl font-semibold tabular-nums\">{value}</span>\n <span className=\"flex items-center gap-1\">\n <Delta testId={`${testId}-delta`} delta={delta} decimal={decimal} />\n <span className=\"text-muted-foreground text-xs\">{previousLabel}</span>\n </span>\n </CardContent>\n </Card>\n );\n}\n\nfunction SupportingTile({\n testId,\n label,\n value,\n delta,\n decimal,\n}: {\n testId: string;\n label: string;\n value: string;\n delta?: number | undefined;\n /** Passed down because `Delta` is a plain function, not a component: it cannot call the hook itself. */\n decimal: (value: number, decimals: number) => string;\n}) {\n return (\n <Card data-testid={testId} size=\"sm\">\n <CardContent className=\"grid gap-1\">\n <MicroLabel>{label}</MicroLabel>\n <span className=\"flex items-center gap-2\">\n <span className=\"text-sm font-medium tabular-nums\">{value}</span>\n {delta !== undefined && <Delta testId={`${testId}-delta`} delta={delta} decimal={decimal} />}\n </span>\n </CardContent>\n </Card>\n );\n}\n\n/**\n * The delta slot. An undefined delta means the previous window was zero: an\n * em dash says \"not comparable\" where a percentage would say \"infinite growth\".\n */\nfunction Delta({\n testId,\n delta,\n decimal,\n}: {\n testId: string;\n delta: number | undefined;\n decimal: (value: number, decimals: number) => string;\n}) {\n if (delta === undefined) {\n return (\n <span data-testid={testId} className=\"text-muted-foreground text-xs\">\n —\n </span>\n );\n }\n\n const increased = delta >= 0;\n const Icon = increased ? ArrowUpIcon : ArrowDownIcon;\n const sign = delta > 0 ? \"+\" : delta < 0 ? \"-\" : \"\";\n\n return (\n <span\n data-testid={testId}\n className={cn(\n \"inline-flex items-center gap-0.5 text-xs tabular-nums\",\n increased ? \"text-success\" : \"text-destructive\",\n )}\n >\n <Icon aria-hidden className=\"size-3\" />\n {`${sign}${decimal(Math.abs(delta), 0)}%`}\n </span>\n );\n}\n","\"use client\";\n\nimport { useTranslations } from \"next-intl\";\nimport { useMemo } from \"react\";\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n Tabs,\n TabsContent,\n TabsList,\n TabsTrigger,\n} from \"../../../shadcnui\";\nimport { useUsageFormatters } from \"../../tokenusage/lib/formatters\";\nimport type { AnalyticsBreakdownInterface } from \"../data/analytics-breakdown.interface\";\nimport { ANALYTICS_DIMENSIONS, type AnalyticsDimension } from \"../data/analytics.types\";\n\ntype Props = {\n breakdowns: Record<AnalyticsDimension, AnalyticsBreakdownInterface[]>;\n};\n\n/** The key the repository folds the tail past the limit into. */\nconst OTHER_KEY = \"other\";\n\n/**\n * Attribution and content breakdowns, one tab per dimension.\n *\n * Each tab is the same four-column table — key, visitors, sessions, page views —\n * sorted by visitors. Sorting is client-side because the whole ranked set is\n * already in memory, so a round trip would buy nothing. Keys are data, not\n * vocabulary: `(none)` and the routes are rendered as the backend wrote them;\n * only the `other` rollup is translated.\n */\nexport function AnalyticsBreakdownTable({ breakdowns }: Props) {\n const t = useTranslations();\n\n const labels: Record<AnalyticsDimension, string> = {\n source: t(\"analytics.admin.dimensions.source\"),\n medium: t(\"analytics.admin.dimensions.medium\"),\n campaign: t(\"analytics.admin.dimensions.campaign\"),\n referrer: t(\"analytics.admin.dimensions.referrer\"),\n route: t(\"analytics.admin.dimensions.route\"),\n landing: t(\"analytics.admin.dimensions.landing\"),\n };\n\n return (\n <Tabs defaultValue={ANALYTICS_DIMENSIONS[0]}>\n {/* Six tabs are wider than a phone-width card: scroll them sideways there. */}\n <TabsList className=\"max-md:w-full max-md:justify-start max-md:overflow-x-auto\">\n {ANALYTICS_DIMENSIONS.map((dimension) => (\n <TabsTrigger key={dimension} value={dimension}>\n {labels[dimension]}\n </TabsTrigger>\n ))}\n </TabsList>\n {ANALYTICS_DIMENSIONS.map((dimension) => (\n <TabsContent key={dimension} value={dimension}>\n <BreakdownRows rows={breakdowns[dimension] ?? []} />\n </TabsContent>\n ))}\n </Tabs>\n );\n}\n\nfunction BreakdownRows({ rows }: { rows: AnalyticsBreakdownInterface[] }) {\n const t = useTranslations();\n const { decimal } = useUsageFormatters();\n\n const sortedRows = useMemo(() => [...rows].sort((a, b) => b.visitors - a.visitors), [rows]);\n\n if (sortedRows.length === 0) {\n return <p className=\"text-muted-foreground text-sm\">{t(\"analytics.admin.no_data\")}</p>;\n }\n\n return (\n // The package's <Table> wraps itself in an `overflow-x-clip` container, which\n // would swallow the overflow before this scroller ever saw it — hence the\n // child override. Wide tables must scroll, never widen the page.\n <div className=\"overflow-x-auto [&_[data-slot=table-container]]:overflow-x-visible\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"text-xs font-medium\">{t(\"analytics.admin.columns.key\")}</TableHead>\n <TableHead className=\"text-end text-xs font-medium\">{t(\"analytics.admin.columns.visitors\")}</TableHead>\n <TableHead className=\"text-end text-xs font-medium\">{t(\"analytics.admin.columns.sessions\")}</TableHead>\n <TableHead className=\"text-end text-xs font-medium\">{t(\"analytics.admin.columns.page_views\")}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {sortedRows.map((row) => (\n <TableRow key={row.id} data-testid={`analytics-breakdown-row-${row.id}`}>\n <TableCell className=\"text-xs\">{row.key === OTHER_KEY ? t(\"analytics.admin.other\") : row.key}</TableCell>\n <TableCell className=\"text-end text-xs tabular-nums\">{decimal(row.visitors, 0)}</TableCell>\n <TableCell className=\"text-end text-xs tabular-nums\">{decimal(row.sessions, 0)}</TableCell>\n <TableCell className=\"text-end text-xs tabular-nums\">{decimal(row.pageViews, 0)}</TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n );\n}\n","\"use client\";\n\nimport { useLocale, useTranslations } from \"next-intl\";\nimport { useMemo } from \"react\";\nimport { usePageUrlGenerator } from \"../../../hooks\";\nimport { Badge, Button, Link, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from \"../../../shadcnui\";\nimport { useUsageFormatters } from \"../../tokenusage/lib/formatters\";\nimport type { AnalyticsSessionInterface } from \"../data/analytics-session.interface\";\n\ntype Props = {\n rows: AnalyticsSessionInterface[];\n /** Base route of the journey page; each row links to `${sessionPageUrl}/${id}`. */\n sessionPageUrl: string;\n onLoadMore: () => void;\n hasMore: boolean;\n};\n\n/** How many trailing characters of the visitor id identify an anonymous visitor. */\nconst VISITOR_SUFFIX_LENGTH = 6;\n\n/**\n * The sessions in the selected window, newest first as the backend returns them.\n *\n * Paginated by cursor: the owning context holds the next-page reference and\n * appends on `onLoadMore`, so this table stays stateless. A session linked to a\n * signed-in user shows the user; any other shows a short visitor suffix, enough\n * to tell two anonymous visitors apart without displaying the whole id.\n */\nexport function AnalyticsSessionsTable({ rows, sessionPageUrl, onLoadMore, hasMore }: Props) {\n const t = useTranslations();\n const locale = useLocale();\n const generateUrl = usePageUrlGenerator();\n const { decimal } = useUsageFormatters();\n\n const dateTime = useMemo(() => new Intl.DateTimeFormat(locale, { dateStyle: \"short\", timeStyle: \"short\" }), [locale]);\n\n if (rows.length === 0) {\n return <p className=\"text-muted-foreground text-sm\">{t(\"analytics.admin.no_data\")}</p>;\n }\n\n return (\n <div className=\"flex flex-col gap-3\">\n {/* The package's <Table> wraps itself in an `overflow-x-clip` container,\n which would swallow the overflow before this scroller ever saw it —\n hence the child override. Wide tables must scroll, never widen the page. */}\n <div className=\"overflow-x-auto [&_[data-slot=table-container]]:overflow-x-visible\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"text-xs font-medium\">{t(\"analytics.admin.columns.started\")}</TableHead>\n <TableHead className=\"text-xs font-medium\">{t(\"analytics.admin.columns.visitor\")}</TableHead>\n <TableHead className=\"text-xs font-medium\">{t(\"analytics.admin.columns.source\")}</TableHead>\n <TableHead className=\"text-xs font-medium\">{t(\"analytics.admin.columns.landing\")}</TableHead>\n <TableHead className=\"text-end text-xs font-medium\">{t(\"analytics.admin.columns.pages\")}</TableHead>\n <TableHead className=\"text-xs font-medium\">{t(\"analytics.admin.columns.device\")}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {rows.map((row) => {\n const source = [row.utmSource ?? row.referrerHost, row.utmMedium].filter(Boolean).join(\" / \");\n\n return (\n <TableRow key={row.id} data-testid={`analytics-session-row-${row.id}`}>\n <TableCell className=\"text-xs tabular-nums\">\n <Link\n href={generateUrl({ page: sessionPageUrl, id: row.id })}\n data-testid={`analytics-session-link-${row.id}`}\n >\n {dateTime.format(row.startedAt)}\n </Link>\n </TableCell>\n <TableCell className=\"text-xs\">\n {row.user ? (\n <span className=\"flex flex-col\">\n <span>{row.user.name}</span>\n <span className=\"text-muted-foreground text-xs\">{row.user.email}</span>\n </span>\n ) : (\n <span className=\"flex items-center gap-2\">\n <Badge variant=\"softGray\">{t(\"analytics.admin.anonymous\")}</Badge>\n <span className=\"text-muted-foreground text-xs tabular-nums\">\n {row.visitorId.slice(-VISITOR_SUFFIX_LENGTH)}\n </span>\n </span>\n )}\n </TableCell>\n <TableCell className=\"text-xs\">{source}</TableCell>\n <TableCell className=\"text-xs\">{row.landingRoute}</TableCell>\n <TableCell className=\"text-end text-xs tabular-nums\">{decimal(row.pageViews, 0)}</TableCell>\n <TableCell className=\"text-muted-foreground text-xs\">{row.deviceType}</TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n\n {hasMore && (\n <div className=\"flex justify-center\">\n <Button type=\"button\" variant=\"outline\" size=\"sm\" onClick={onLoadMore}>\n {t(\"analytics.admin.load_more\")}\n </Button>\n </div>\n )}\n </div>\n );\n}\n","\"use client\";\n\nimport { useTranslations } from \"next-intl\";\nimport { useTheme } from \"next-themes\";\nimport { useMemo } from \"react\";\nimport { Bar, BarChart, CartesianGrid, XAxis, YAxis } from \"recharts\";\nimport {\n ChartConfig,\n ChartContainer,\n ChartLegend,\n ChartLegendContent,\n ChartTooltip,\n ChartTooltipContent,\n} from \"../../../shadcnui\";\nimport { useUsageFormatters } from \"../../tokenusage/lib/formatters\";\nimport { ChartMode, seriesColor } from \"../../tokenusage/lib/palette\";\nimport { AnalyticsTimelineInterface } from \"../data/analytics-timeline.interface\";\n\n/** The two fixed series, in palette order: slot 0 visitors, slot 1 page views. */\nconst SERIES = [\"visitors\", \"pageViews\"] as const;\n\ntype AnalyticsTimelineChartProps = {\n rows: AnalyticsTimelineInterface[];\n className?: string;\n};\n\ntype TimelineBucket = { bucket: string; visitors: number; pageViews: number };\n\n/**\n * The bucket key, derived with UTC getters.\n *\n * The backend field is `type: \"date\"` — a calendar day with no time — and the\n * model parses the `YYYY-MM-DD` wire value with `new Date(...)`, which lands on\n * UTC midnight. Reading it back with local getters would shift the bucket a day\n * early west of UTC, so the key (and every label built from it) stays in UTC.\n */\nconst bucketKey = (date: Date): string => {\n const y = date.getUTCFullYear();\n const m = `${date.getUTCMonth() + 1}`.padStart(2, \"0\");\n const d = `${date.getUTCDate()}`.padStart(2, \"0\");\n return `${y}-${m}-${d}`;\n};\n\n/**\n * The granularity the data was bucketed at, inferred from the spacing between\n * consecutive buckets. The backend chooses the granularity and the rows carry\n * the consequence, so the axis reads it off the data rather than taking a prop\n * that could disagree with what was actually fetched.\n */\nconst inferGranularity = (buckets: string[]): \"day\" | \"week\" | \"month\" => {\n if (buckets.length < 2) return \"day\";\n\n const dayMs = 24 * 60 * 60 * 1000;\n let smallestGap = Number.POSITIVE_INFINITY;\n for (let i = 1; i < buckets.length; i += 1) {\n const gap = (Date.parse(buckets[i]) - Date.parse(buckets[i - 1])) / dayMs;\n if (gap > 0 && gap < smallestGap) smallestGap = gap;\n }\n\n if (smallestGap >= 28) return \"month\";\n if (smallestGap >= 7) return \"week\";\n return \"day\";\n};\n\n/**\n * Traffic over time as a grouped bar chart: visitors and page views per bucket.\n *\n * The backend returns one row per bucket per section; the chart sums the\n * sections into one entry per bucket. The two series measure different things,\n * so they sit side by side rather than stacked.\n *\n * Colour does an IDENTITY job here — each bar is a series, not a magnitude — so\n * it draws the fixed categorical order from the token-usage palette (slots 0 and\n * 1). The palette's light-mode validator run carries a sub-3:1 contrast WARN,\n * which obliges a relief channel: the legend and the value-carrying tooltip\n * below are what make the fills readable.\n */\nexport function AnalyticsTimelineChart({ rows, className }: AnalyticsTimelineChartProps) {\n const t = useTranslations();\n const { compact, bucketDate, decimal } = useUsageFormatters();\n const { resolvedTheme } = useTheme();\n const mode: ChartMode = resolvedTheme === \"dark\" ? \"dark\" : \"light\";\n\n const chartData = useMemo(() => {\n const byBucket = new Map<string, TimelineBucket>();\n for (const row of rows) {\n const key = bucketKey(row.bucket);\n let entry = byBucket.get(key);\n if (!entry) {\n entry = { bucket: key, visitors: 0, pageViews: 0 };\n byBucket.set(key, entry);\n }\n entry.visitors += row.visitors;\n entry.pageViews += row.pageViews;\n }\n return [...byBucket.values()].sort((a, b) => a.bucket.localeCompare(b.bucket));\n }, [rows]);\n\n const chartConfig = useMemo(\n () =>\n ({\n visitors: { label: t(\"analytics.admin.series.visitors\") },\n pageViews: { label: t(\"analytics.admin.series.page_views\") },\n }) as ChartConfig,\n [t],\n );\n\n if (chartData.length === 0) {\n return (\n <p className={className ? `text-muted-foreground text-sm ${className}` : \"text-muted-foreground text-sm\"}>\n {t(\"analytics.admin.no_data\")}\n </p>\n );\n }\n\n const granularity = inferGranularity(chartData.map((entry) => entry.bucket));\n\n return (\n // The chart is a coordinate system, not prose: recharts lays the axes and the\n // bars out left-to-right, so the whole chart stays an LTR island even under\n // dir=\"rtl\". Only the labels inside it are translated.\n // rtl-ok: deliberate LTR island (chart)\n <div className={className} dir=\"ltr\">\n {/* The pivot, exposed for assertions and for screen readers that would\n otherwise get nothing from the SVG. */}\n <span className=\"sr-only\" data-testid=\"analytics-timeline-data\">\n {JSON.stringify(chartData)}\n </span>\n\n <ChartContainer config={chartConfig} className=\"aspect-auto h-72 w-full\">\n <BarChart accessibilityLayer data={chartData} margin={{ top: 8, right: 8, bottom: 0, left: 0 }}>\n <CartesianGrid vertical={false} />\n <XAxis\n dataKey=\"bucket\"\n tickLine={false}\n axisLine={false}\n tickMargin={8}\n minTickGap={16}\n tickFormatter={(value: string) => bucketDate(value, granularity)}\n />\n <YAxis\n tickLine={false}\n axisLine={false}\n tickMargin={8}\n width={48}\n tickFormatter={(value: number) => compact(value)}\n />\n <ChartTooltip\n content={\n <ChartTooltipContent\n labelFormatter={(value) => bucketDate(String(value), granularity)}\n valueFormatter={(value) => decimal(Number(value), 0)}\n />\n }\n />\n <ChartLegend content={<ChartLegendContent />} />\n {SERIES.map((series, index) => (\n <Bar\n key={series}\n dataKey={series}\n maxBarSize={28}\n fill={seriesColor(index, mode)}\n // A 2px stroke in the surface colour is the gap between\n // neighbouring bars — the palette's own secondary-encoding\n // channel, not a border.\n stroke=\"var(--background)\"\n strokeWidth={2}\n radius={[4, 4, 0, 0]}\n />\n ))}\n </BarChart>\n </ChartContainer>\n </div>\n );\n}\n","\"use client\";\n\nimport { useLocale, useTranslations } from \"next-intl\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { DetailField, RoundPageContainer } from \"../../../components\";\nimport { SharedProvider } from \"../../../contexts\";\nimport { usePageUrlGenerator } from \"../../../hooks\";\nimport { BreadcrumbItemData } from \"../../../interfaces\";\nimport {\n Badge,\n Card,\n CardContent,\n CardHeader,\n CardTitle,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"../../../shadcnui\";\nimport type { AnalyticsPageViewInterface } from \"../data/analytics-page-view.interface\";\nimport type { AnalyticsSessionInterface } from \"../data/analytics-session.interface\";\nimport { AnalyticsAdminService } from \"../data/AnalyticsAdminService\";\nimport { AnalyticsSessionsTable } from \"./AnalyticsSessionsTable\";\n\n/** Default route of the dashboard the breadcrumb links back to. */\nconst ANALYTICS_ADMIN_PAGE_URL = \"/administration/analytics\";\n\n/**\n * How far back the visitor's other sessions are looked up: the 13-month raw\n * retention, which sits inside the API's 400-day maximum range.\n */\nconst OTHER_SESSIONS_WINDOW_DAYS = 395;\n\ntype Props = {\n sessionId: string;\n /** Route of the dashboard; the journey pages live under `${pageUrl}/sessions`. */\n pageUrl?: string;\n};\n\n/**\n * One session, followed page by page.\n *\n * The attribution header says where the visitor came from, the page-view table\n * replays the session in order with the time spent before each step, and the\n * visitor's other sessions close the page. Unlike the dashboard this page has no\n * filters, so it fetches on mount without a provider and publishes its own\n * breadcrumb back to the dashboard.\n */\nexport function AnalyticsJourneyContainer({ sessionId, pageUrl = ANALYTICS_ADMIN_PAGE_URL }: Props) {\n const t = useTranslations();\n const locale = useLocale();\n const generateUrl = usePageUrlGenerator();\n\n const [session, setSession] = useState<AnalyticsSessionInterface | undefined>(undefined);\n const [pageViews, setPageViews] = useState<AnalyticsPageViewInterface[]>([]);\n const [otherSessions, setOtherSessions] = useState<AnalyticsSessionInterface[]>([]);\n const [isLoading, setIsLoading] = useState(true);\n const [error, setError] = useState<string | null>(null);\n\n useEffect(() => {\n // Out-of-order guard, as in AnalyticsAdminContext: a new sessionId must not\n // be overwritten by the slower response for the previous one.\n let cancelled = false;\n\n setIsLoading(true);\n setError(null);\n\n Promise.all([\n AnalyticsAdminService.getSession({ id: sessionId }),\n AnalyticsAdminService.getSessionPageViews({ sessionId }),\n ])\n .then(async ([nextSession, nextPageViews]) => {\n if (cancelled) return;\n setSession(nextSession);\n setPageViews(nextPageViews ?? []);\n\n const to = new Date();\n const from = new Date(to.getTime() - OTHER_SESSIONS_WINDOW_DAYS * 24 * 60 * 60 * 1000);\n const visitorSessions = await AnalyticsAdminService.getSessions({\n from: from.toISOString(),\n to: to.toISOString(),\n section: \"all\",\n visitorId: nextSession.visitorId,\n });\n if (cancelled) return;\n setOtherSessions((visitorSessions ?? []).filter((other) => other.id !== sessionId));\n })\n .catch((err) => {\n if (cancelled) return;\n console.error(\"Failed to load the analytics journey:\", err);\n setError(err instanceof Error ? err.message : String(err));\n })\n .finally(() => {\n if (cancelled) return;\n setIsLoading(false);\n });\n\n return () => {\n cancelled = true;\n };\n }, [sessionId]);\n\n const dateTime = useMemo(\n () => new Intl.DateTimeFormat(locale, { dateStyle: \"short\", timeStyle: \"medium\" }),\n [locale],\n );\n\n /** Time since the previous page view, in the largest whole unit that fits. */\n const formatGap = (milliseconds: number): string => {\n const seconds = Math.max(0, Math.round(milliseconds / 1000));\n if (seconds < 60) return unit(locale, \"second\", seconds);\n if (seconds < 3600) return unit(locale, \"minute\", Math.round(seconds / 60));\n return unit(locale, \"hour\", Math.round(seconds / 3600));\n };\n\n const breadcrumb: BreadcrumbItemData[] = [\n { name: t(\"analytics.admin.title\"), href: generateUrl({ page: pageUrl }) },\n { name: t(\"analytics.admin.panels.journey\") },\n ];\n\n const title = { type: t(\"analytics.admin.panels.journey\") };\n\n const renderBody = () => {\n if (error) {\n return (\n <RoundPageContainer fullWidth forceHeader>\n <div className=\"p-4\">\n <Card>\n <CardContent>\n <p className=\"text-destructive text-xs/relaxed\">{error}</p>\n </CardContent>\n </Card>\n </div>\n </RoundPageContainer>\n );\n }\n\n // Loading renders nothing in the body, as on the dashboard.\n if (isLoading || !session) return <RoundPageContainer fullWidth forceHeader />;\n\n const source = [session.utmSource ?? session.referrerHost, session.utmMedium, session.utmCampaign]\n .filter(Boolean)\n .join(\" / \");\n\n return (\n <RoundPageContainer fullWidth forceHeader>\n <div className=\"flex w-full flex-col gap-4 p-4\">\n <Card data-testid=\"analytics-journey-attribution\">\n <CardContent className=\"flex flex-col gap-3\">\n <div className=\"flex flex-wrap items-center gap-2\">\n {session.user ? (\n <span className=\"flex flex-col\">\n <span className=\"text-sm\">{session.user.name}</span>\n <span className=\"text-muted-foreground text-xs\">{session.user.email}</span>\n </span>\n ) : (\n <Badge variant=\"softGray\">{t(\"analytics.admin.anonymous\")}</Badge>\n )}\n {session.consented ? (\n <Badge variant=\"softGreen\">{t(\"analytics.admin.consented\")}</Badge>\n ) : (\n <Badge variant=\"softGray\">{t(\"analytics.admin.not_consented\")}</Badge>\n )}\n </div>\n <div className=\"grid gap-3 sm:grid-cols-3\">\n <DetailField label={t(\"analytics.admin.columns.started\")} value={dateTime.format(session.startedAt)} />\n <DetailField label={t(\"analytics.admin.columns.source\")} value={source} />\n <DetailField label={t(\"analytics.admin.columns.landing\")} value={session.landingRoute} />\n <DetailField label={t(\"analytics.admin.columns.device\")} value={session.deviceType} />\n <DetailField\n label={t(\"analytics.admin.columns.visitor\")}\n value={<span className=\"tabular-nums\">{session.visitorId}</span>}\n />\n </div>\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>{t(\"analytics.admin.columns.pages\")}</CardTitle>\n </CardHeader>\n <CardContent>\n {pageViews.length === 0 ? (\n <p className=\"text-muted-foreground text-sm\">{t(\"analytics.admin.no_data\")}</p>\n ) : (\n // The package's <Table> wraps itself in an `overflow-x-clip`\n // container — hence the child override, so wide rows scroll.\n <div className=\"overflow-x-auto [&_[data-slot=table-container]]:overflow-x-visible\">\n <Table>\n <TableHeader>\n <TableRow>\n <TableHead className=\"text-xs font-medium\">{t(\"analytics.admin.columns.time\")}</TableHead>\n <TableHead className=\"text-end text-xs font-medium\">\n {t(\"analytics.admin.columns.gap\")}\n </TableHead>\n <TableHead className=\"text-xs font-medium\">{t(\"analytics.admin.columns.path\")}</TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {pageViews.map((pageView, index) => {\n const previous = index > 0 ? pageViews[index - 1] : undefined;\n return (\n <TableRow key={pageView.id} data-testid={`analytics-pageview-row-${pageView.id}`}>\n <TableCell className=\"text-xs tabular-nums\">\n {dateTime.format(pageView.createdAt)}\n </TableCell>\n <TableCell className=\"text-end text-xs tabular-nums\">\n {previous ? formatGap(pageView.createdAt.getTime() - previous.createdAt.getTime()) : \"—\"}\n </TableCell>\n <TableCell className=\"text-xs\">{pageView.path}</TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n )}\n </CardContent>\n </Card>\n\n <Card>\n <CardHeader>\n <CardTitle>{t(\"analytics.admin.panels.other_sessions\")}</CardTitle>\n </CardHeader>\n <CardContent>\n <AnalyticsSessionsTable\n rows={otherSessions}\n sessionPageUrl={`${pageUrl}/sessions`}\n onLoadMore={() => undefined}\n hasMore={false}\n />\n </CardContent>\n </Card>\n </div>\n </RoundPageContainer>\n );\n };\n\n return <SharedProvider value={{ breadcrumbs: breadcrumb, title }}>{renderBody()}</SharedProvider>;\n}\n\nfunction unit(locale: string, name: \"second\" | \"minute\" | \"hour\", value: number): string {\n return new Intl.NumberFormat(locale, { style: \"unit\", unit: name, unitDisplay: \"short\" }).format(value);\n}\n","\"use client\";\n\nimport { AnalyticsAdminProvider } from \"../contexts/AnalyticsAdminContext\";\nimport { AnalyticsAdminContainer } from \"./AnalyticsAdminContainer\";\n\ntype Props = {\n /** Route the breadcrumb links back to. Defaults to the provider's default. */\n pageUrl?: string;\n /** Base route of the journey page each session row links to. */\n sessionPageUrl?: string;\n};\n\n/**\n * The whole administrative analytics page as one client component, so a host\n * app's server page renders a single element. `AnalyticsAdminContainer` owns\n * the `RoundPageContainer`; the provider sits above it because it publishes the\n * filter bar into the title bar.\n */\nexport function AnalyticsAdminPageContainer({ pageUrl, sessionPageUrl }: Props) {\n return (\n <AnalyticsAdminProvider pageUrl={pageUrl} sessionPageUrl={sessionPageUrl}>\n <AnalyticsAdminContainer />\n </AnalyticsAdminProvider>\n );\n}\n","\"use client\";\n\nimport { AnalyticsJourneyContainer } from \"./AnalyticsJourneyContainer\";\n\ntype Props = {\n sessionId: string;\n /** Route the breadcrumb links back to. Defaults to the container's default. */\n pageUrl?: string;\n};\n\n/** The single-session journey page as one client component. */\nexport function AnalyticsJourneyPageContainer({ sessionId, pageUrl }: Props) {\n return <AnalyticsJourneyContainer sessionId={sessionId} pageUrl={pageUrl} />;\n}\n","/**\n * The fixed namespace of i18n keys the administrative analytics feature reads\n * via useTranslations. Consuming apps must define each entry in their\n * messages/<locale>.json — this list is the contract between the package and\n * the app.\n *\n * The description key is read by the consuming app's page metadata only, not by\n * a component in this package, which is why the contract spec lists it among\n * the keys declared for app-rendered surfaces.\n */\nexport const ANALYTICS_ADMIN_I18N_KEYS = [\n // Page\n \"analytics.admin.title\",\n \"analytics.admin.description\",\n\n // KPI tiles\n \"analytics.admin.tiles.visitors\",\n \"analytics.admin.tiles.sessions\",\n \"analytics.admin.tiles.page_views\",\n \"analytics.admin.tiles.pages_per_session\",\n \"analytics.admin.tiles.consent_share\",\n \"analytics.admin.tiles.vs_previous\",\n \"analytics.admin.tiles.summarised_note\",\n\n // Filter bar\n \"analytics.admin.filter.section\",\n \"analytics.admin.filter.section_all\",\n \"analytics.admin.filter.section_public\",\n \"analytics.admin.filter.section_app\",\n \"analytics.admin.filter.granularity\",\n \"analytics.admin.filter.granularity_day\",\n \"analytics.admin.filter.granularity_week\",\n \"analytics.admin.filter.granularity_month\",\n\n // Panel titles\n \"analytics.admin.panels.over_time\",\n \"analytics.admin.panels.breakdown\",\n \"analytics.admin.panels.sessions\",\n \"analytics.admin.panels.journey\",\n \"analytics.admin.panels.other_sessions\",\n\n // Breakdown tabs\n \"analytics.admin.dimensions.source\",\n \"analytics.admin.dimensions.medium\",\n \"analytics.admin.dimensions.campaign\",\n \"analytics.admin.dimensions.referrer\",\n \"analytics.admin.dimensions.route\",\n \"analytics.admin.dimensions.landing\",\n\n // Table columns\n \"analytics.admin.columns.key\",\n \"analytics.admin.columns.visitors\",\n \"analytics.admin.columns.sessions\",\n \"analytics.admin.columns.page_views\",\n \"analytics.admin.columns.started\",\n \"analytics.admin.columns.visitor\",\n \"analytics.admin.columns.source\",\n \"analytics.admin.columns.landing\",\n \"analytics.admin.columns.pages\",\n \"analytics.admin.columns.device\",\n \"analytics.admin.columns.time\",\n \"analytics.admin.columns.gap\",\n \"analytics.admin.columns.path\",\n\n // Shared states and markers\n \"analytics.admin.anonymous\",\n \"analytics.admin.consented\",\n \"analytics.admin.not_consented\",\n \"analytics.admin.load_more\",\n \"analytics.admin.no_data\",\n \"analytics.admin.other\",\n\n // Timeline chart series\n \"analytics.admin.series.visitors\",\n \"analytics.admin.series.page_views\",\n] as const;\n"],"mappings":"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","names":["v4","v4","useTranslations","useEffect","jsx","useTranslations","useEffect","useTranslations","useTranslations","jsx","jsxs","useTranslations","window","useTranslations","useMemo","jsx","jsxs","useTranslations","useMemo","useTranslations","useMemo","jsx","jsxs","useTranslations","useMemo","useTranslations","useMemo","jsx","jsxs","useTranslations","useMemo","jsx","jsxs","useTranslations","useLocale","useTranslations","useEffect","useMemo","useState","jsx","jsxs","ANALYTICS_ADMIN_PAGE_URL","useTranslations","useLocale","useState","useEffect","useMemo","jsx","jsx"]}
@@ -0,0 +1,28 @@
1
+ import * as React from 'react';
2
+
3
+ type Props$1 = {
4
+ /** Role id the viewer must hold. The package has no role constants, so the host app passes its own. */
5
+ adminRoleId: string;
6
+ /** Route the breadcrumb links back to. */
7
+ pageUrl?: string;
8
+ /** Base route of the journey page each session row links to. */
9
+ sessionPageUrl?: string;
10
+ /** Where a viewer without the role is sent. */
11
+ deniedRedirect?: string;
12
+ };
13
+ /** Server page for the administrative analytics dashboard: role gate, then the client container. */
14
+ declare function AnalyticsAdminPage({ adminRoleId, pageUrl, sessionPageUrl, deniedRedirect }: Props$1): Promise<React.JSX.Element>;
15
+
16
+ type Props = {
17
+ /** Role id the viewer must hold. The package has no role constants, so the host app passes its own. */
18
+ adminRoleId: string;
19
+ sessionId: string;
20
+ /** Route of the dashboard; the journey pages live under `${pageUrl}/sessions`. */
21
+ pageUrl?: string;
22
+ /** Where a viewer without the role is sent. */
23
+ deniedRedirect?: string;
24
+ };
25
+ /** Server page for one session's journey: role gate, then the client container. */
26
+ declare function AnalyticsJourneyPage({ adminRoleId, sessionId, pageUrl, deniedRedirect }: Props): Promise<React.JSX.Element>;
27
+
28
+ export { AnalyticsAdminPage, AnalyticsJourneyPage };
@@ -0,0 +1,28 @@
1
+ import * as React from 'react';
2
+
3
+ type Props$1 = {
4
+ /** Role id the viewer must hold. The package has no role constants, so the host app passes its own. */
5
+ adminRoleId: string;
6
+ /** Route the breadcrumb links back to. */
7
+ pageUrl?: string;
8
+ /** Base route of the journey page each session row links to. */
9
+ sessionPageUrl?: string;
10
+ /** Where a viewer without the role is sent. */
11
+ deniedRedirect?: string;
12
+ };
13
+ /** Server page for the administrative analytics dashboard: role gate, then the client container. */
14
+ declare function AnalyticsAdminPage({ adminRoleId, pageUrl, sessionPageUrl, deniedRedirect }: Props$1): Promise<React.JSX.Element>;
15
+
16
+ type Props = {
17
+ /** Role id the viewer must hold. The package has no role constants, so the host app passes its own. */
18
+ adminRoleId: string;
19
+ sessionId: string;
20
+ /** Route of the dashboard; the journey pages live under `${pageUrl}/sessions`. */
21
+ pageUrl?: string;
22
+ /** Where a viewer without the role is sent. */
23
+ deniedRedirect?: string;
24
+ };
25
+ /** Server page for one session's journey: role gate, then the client container. */
26
+ declare function AnalyticsJourneyPage({ adminRoleId, sessionId, pageUrl, deniedRedirect }: Props): Promise<React.JSX.Element>;
27
+
28
+ export { AnalyticsAdminPage, AnalyticsJourneyPage };
@@ -0,0 +1,41 @@
1
+ "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
+
3
+ var _chunkGHFMKH3Mjs = require('../../chunk-GHFMKH3M.js');
4
+ require('../../chunk-5TRFNKJS.js');
5
+ require('../../chunk-YUO55Q5A.js');
6
+ require('../../chunk-CAKOFJZD.js');
7
+ require('../../chunk-26MQIJNB.js');
8
+ require('../../chunk-LXKSUWAV.js');
9
+ require('../../chunk-JTPRJNT2.js');
10
+
11
+
12
+ var _chunk7QVYU63Ejs = require('../../chunk-7QVYU63E.js');
13
+
14
+ // src/features/analytics/components/server/AnalyticsAdminPage.tsx
15
+ var _navigation = require('next/navigation');
16
+ var _analytics = require('@carlonicora/nextjs-jsonapi/analytics');
17
+ var _jsxruntime = require('react/jsx-runtime');
18
+ async function AnalyticsAdminPage({ adminRoleId, pageUrl, sessionPageUrl, deniedRedirect = "/" }) {
19
+ if (!await _chunkGHFMKH3Mjs.ServerSession.hasRole(adminRoleId)) {
20
+ _navigation.redirect.call(void 0, deniedRedirect);
21
+ }
22
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _analytics.AnalyticsAdminPageContainer, { pageUrl, sessionPageUrl });
23
+ }
24
+ _chunk7QVYU63Ejs.__name.call(void 0, AnalyticsAdminPage, "AnalyticsAdminPage");
25
+
26
+ // src/features/analytics/components/server/AnalyticsJourneyPage.tsx
27
+
28
+
29
+
30
+ async function AnalyticsJourneyPage({ adminRoleId, sessionId, pageUrl, deniedRedirect = "/" }) {
31
+ if (!await _chunkGHFMKH3Mjs.ServerSession.hasRole(adminRoleId)) {
32
+ _navigation.redirect.call(void 0, deniedRedirect);
33
+ }
34
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _analytics.AnalyticsJourneyPageContainer, { sessionId, pageUrl });
35
+ }
36
+ _chunk7QVYU63Ejs.__name.call(void 0, AnalyticsJourneyPage, "AnalyticsJourneyPage");
37
+
38
+
39
+
40
+ exports.AnalyticsAdminPage = AnalyticsAdminPage; exports.AnalyticsJourneyPage = AnalyticsJourneyPage;
41
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/features/analytics/server.js","../../../src/features/analytics/components/server/AnalyticsAdminPage.tsx","../../../src/features/analytics/components/server/AnalyticsJourneyPage.tsx"],"names":["redirect","jsx"],"mappings":"AAAA;AACE;AACF,0DAAgC;AAChC,mCAAgC;AAChC,mCAAgC;AAChC,mCAAgC;AAChC,mCAAgC;AAChC,mCAAgC;AAChC,mCAAgC;AAChC;AACE;AACF,0DAAgC;AAChC;AACA;ACbA,6CAAyB;AAMzB,kEAA4C;AAoBnC,+CAAA;AALT,MAAA,SAAsB,kBAAA,CAAmB,EAAE,WAAA,EAAa,OAAA,EAAS,cAAA,EAAgB,eAAA,EAAiB,IAAI,CAAA,EAAU;AAC9G,EAAA,GAAA,CAAI,CAAE,MAAM,8BAAA,CAAc,OAAA,CAAQ,WAAW,CAAA,EAAI;AAC/C,IAAA,kCAAA,cAAuB,CAAA;AAAA,EACzB;AAEA,EAAA,uBAAO,6BAAA,sCAAC,EAAA,EAA4B,OAAA,EAAkB,eAAA,CAAgC,CAAA;AACxF;AANsB,qCAAA,kBAAA,EAAA,oBAAA,CAAA;ADGtB;AACA;AEzBA;AAIA;AAmBS;AALT,MAAA,SAAsB,oBAAA,CAAqB,EAAE,WAAA,EAAa,SAAA,EAAW,OAAA,EAAS,eAAA,EAAiB,IAAI,CAAA,EAAU;AAC3G,EAAA,GAAA,CAAI,CAAE,MAAM,8BAAA,CAAc,OAAA,CAAQ,WAAW,CAAA,EAAI;AAC/C,IAAAA,kCAAAA,cAAuB,CAAA;AAAA,EACzB;AAEA,EAAA,uBAAOC,6BAAAA,wCAAC,EAAA,EAA8B,SAAA,EAAsB,QAAA,CAAkB,CAAA;AAChF;AANsB,qCAAA,oBAAA,EAAA,sBAAA,CAAA;AFkBtB;AACE;AACA;AACF,qGAAC","file":"/home/runner/work/nextjs-jsonapi/nextjs-jsonapi/dist/features/analytics/server.js","sourcesContent":[null,"import { redirect } from \"next/navigation\";\n// The container is imported through the package's own client subpath, never by\n// relative path. tsup keeps this specifier external, so the built server entry\n// reaches the container through `features/analytics/index`, which carries the\n// \"use client\" banner. A relative import would inline the container's hooks and\n// contexts into this server bundle, outside any client boundary.\nimport { AnalyticsAdminPageContainer } from \"@carlonicora/nextjs-jsonapi/analytics\";\nimport { ServerSession } from \"../../../../server\";\n\ntype Props = {\n /** Role id the viewer must hold. The package has no role constants, so the host app passes its own. */\n adminRoleId: string;\n /** Route the breadcrumb links back to. */\n pageUrl?: string;\n /** Base route of the journey page each session row links to. */\n sessionPageUrl?: string;\n /** Where a viewer without the role is sent. */\n deniedRedirect?: string;\n};\n\n/** Server page for the administrative analytics dashboard: role gate, then the client container. */\nexport async function AnalyticsAdminPage({ adminRoleId, pageUrl, sessionPageUrl, deniedRedirect = \"/\" }: Props) {\n if (!(await ServerSession.hasRole(adminRoleId))) {\n redirect(deniedRedirect);\n }\n\n return <AnalyticsAdminPageContainer pageUrl={pageUrl} sessionPageUrl={sessionPageUrl} />;\n}\n","import { redirect } from \"next/navigation\";\n// Imported through the package's own client subpath for the same reason as in\n// AnalyticsAdminPage: the built server entry must reach the container through\n// the \"use client\" bundle, not inline it.\nimport { AnalyticsJourneyPageContainer } from \"@carlonicora/nextjs-jsonapi/analytics\";\nimport { ServerSession } from \"../../../../server\";\n\ntype Props = {\n /** Role id the viewer must hold. The package has no role constants, so the host app passes its own. */\n adminRoleId: string;\n sessionId: string;\n /** Route of the dashboard; the journey pages live under `${pageUrl}/sessions`. */\n pageUrl?: string;\n /** Where a viewer without the role is sent. */\n deniedRedirect?: string;\n};\n\n/** Server page for one session's journey: role gate, then the client container. */\nexport async function AnalyticsJourneyPage({ adminRoleId, sessionId, pageUrl, deniedRedirect = \"/\" }: Props) {\n if (!(await ServerSession.hasRole(adminRoleId))) {\n redirect(deniedRedirect);\n }\n\n return <AnalyticsJourneyPageContainer sessionId={sessionId} pageUrl={pageUrl} />;\n}\n"]}
@@ -0,0 +1,41 @@
1
+ import {
2
+ ServerSession
3
+ } from "../../chunk-7B4E42R5.mjs";
4
+ import "../../chunk-SJLXPHK5.mjs";
5
+ import "../../chunk-F5UNXZ3J.mjs";
6
+ import "../../chunk-E3I4MT53.mjs";
7
+ import "../../chunk-37DMFVLN.mjs";
8
+ import "../../chunk-AUXK7QSA.mjs";
9
+ import "../../chunk-PI56A4DG.mjs";
10
+ import {
11
+ __name
12
+ } from "../../chunk-PAWJFY3S.mjs";
13
+
14
+ // src/features/analytics/components/server/AnalyticsAdminPage.tsx
15
+ import { redirect } from "next/navigation";
16
+ import { AnalyticsAdminPageContainer } from "@carlonicora/nextjs-jsonapi/analytics";
17
+ import { jsx } from "react/jsx-runtime";
18
+ async function AnalyticsAdminPage({ adminRoleId, pageUrl, sessionPageUrl, deniedRedirect = "/" }) {
19
+ if (!await ServerSession.hasRole(adminRoleId)) {
20
+ redirect(deniedRedirect);
21
+ }
22
+ return /* @__PURE__ */ jsx(AnalyticsAdminPageContainer, { pageUrl, sessionPageUrl });
23
+ }
24
+ __name(AnalyticsAdminPage, "AnalyticsAdminPage");
25
+
26
+ // src/features/analytics/components/server/AnalyticsJourneyPage.tsx
27
+ import { redirect as redirect2 } from "next/navigation";
28
+ import { AnalyticsJourneyPageContainer } from "@carlonicora/nextjs-jsonapi/analytics";
29
+ import { jsx as jsx2 } from "react/jsx-runtime";
30
+ async function AnalyticsJourneyPage({ adminRoleId, sessionId, pageUrl, deniedRedirect = "/" }) {
31
+ if (!await ServerSession.hasRole(adminRoleId)) {
32
+ redirect2(deniedRedirect);
33
+ }
34
+ return /* @__PURE__ */ jsx2(AnalyticsJourneyPageContainer, { sessionId, pageUrl });
35
+ }
36
+ __name(AnalyticsJourneyPage, "AnalyticsJourneyPage");
37
+ export {
38
+ AnalyticsAdminPage,
39
+ AnalyticsJourneyPage
40
+ };
41
+ //# sourceMappingURL=server.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/features/analytics/components/server/AnalyticsAdminPage.tsx","../../../src/features/analytics/components/server/AnalyticsJourneyPage.tsx"],"sourcesContent":["import { redirect } from \"next/navigation\";\n// The container is imported through the package's own client subpath, never by\n// relative path. tsup keeps this specifier external, so the built server entry\n// reaches the container through `features/analytics/index`, which carries the\n// \"use client\" banner. A relative import would inline the container's hooks and\n// contexts into this server bundle, outside any client boundary.\nimport { AnalyticsAdminPageContainer } from \"@carlonicora/nextjs-jsonapi/analytics\";\nimport { ServerSession } from \"../../../../server\";\n\ntype Props = {\n /** Role id the viewer must hold. The package has no role constants, so the host app passes its own. */\n adminRoleId: string;\n /** Route the breadcrumb links back to. */\n pageUrl?: string;\n /** Base route of the journey page each session row links to. */\n sessionPageUrl?: string;\n /** Where a viewer without the role is sent. */\n deniedRedirect?: string;\n};\n\n/** Server page for the administrative analytics dashboard: role gate, then the client container. */\nexport async function AnalyticsAdminPage({ adminRoleId, pageUrl, sessionPageUrl, deniedRedirect = \"/\" }: Props) {\n if (!(await ServerSession.hasRole(adminRoleId))) {\n redirect(deniedRedirect);\n }\n\n return <AnalyticsAdminPageContainer pageUrl={pageUrl} sessionPageUrl={sessionPageUrl} />;\n}\n","import { redirect } from \"next/navigation\";\n// Imported through the package's own client subpath for the same reason as in\n// AnalyticsAdminPage: the built server entry must reach the container through\n// the \"use client\" bundle, not inline it.\nimport { AnalyticsJourneyPageContainer } from \"@carlonicora/nextjs-jsonapi/analytics\";\nimport { ServerSession } from \"../../../../server\";\n\ntype Props = {\n /** Role id the viewer must hold. The package has no role constants, so the host app passes its own. */\n adminRoleId: string;\n sessionId: string;\n /** Route of the dashboard; the journey pages live under `${pageUrl}/sessions`. */\n pageUrl?: string;\n /** Where a viewer without the role is sent. */\n deniedRedirect?: string;\n};\n\n/** Server page for one session's journey: role gate, then the client container. */\nexport async function AnalyticsJourneyPage({ adminRoleId, sessionId, pageUrl, deniedRedirect = \"/\" }: Props) {\n if (!(await ServerSession.hasRole(adminRoleId))) {\n redirect(deniedRedirect);\n }\n\n return <AnalyticsJourneyPageContainer sessionId={sessionId} pageUrl={pageUrl} />;\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA,SAAS,gBAAgB;AAMzB,SAAS,mCAAmC;AAoBnC;AALT,eAAsB,mBAAmB,EAAE,aAAa,SAAS,gBAAgB,iBAAiB,IAAI,GAAU;AAC9G,MAAI,CAAE,MAAM,cAAc,QAAQ,WAAW,GAAI;AAC/C,aAAS,cAAc;AAAA,EACzB;AAEA,SAAO,oBAAC,+BAA4B,SAAkB,gBAAgC;AACxF;AANsB;;;ACrBtB,SAAS,YAAAA,iBAAgB;AAIzB,SAAS,qCAAqC;AAmBrC,gBAAAC,YAAA;AALT,eAAsB,qBAAqB,EAAE,aAAa,WAAW,SAAS,iBAAiB,IAAI,GAAU;AAC3G,MAAI,CAAE,MAAM,cAAc,QAAQ,WAAW,GAAI;AAC/C,IAAAC,UAAS,cAAc;AAAA,EACzB;AAEA,SAAO,gBAAAD,KAAC,iCAA8B,WAAsB,SAAkB;AAChF;AANsB;","names":["redirect","jsx","redirect"]}
@@ -4,8 +4,8 @@ import { H as HelpContentConfig } from '../../help-content-config.interface-C5ES
4
4
  export { a as HelpBrandConfig } from '../../help-content-config.interface-C5ESIod9.mjs';
5
5
  import { HelpHeading, HelpArticle, HelpMode } from './server.mjs';
6
6
  export { HELP_MODES, HelpRedirect } from './server.mjs';
7
- import { u as usePageUrlGenerator } from '../../usePageUrlGenerator-uKBjAKqC.mjs';
8
- import '../../types-Bq_UA8Lg.mjs';
7
+ import { u as usePageUrlGenerator } from '../../usePageUrlGenerator-CCJ_cdQy.mjs';
8
+ import '../../types-D0uLYV2P.mjs';
9
9
  import 'lucide-react';
10
10
  import '../../ApiRequestDataTypeInterface-CYEcRUrh.mjs';
11
11
 
@@ -4,8 +4,8 @@ import { H as HelpContentConfig } from '../../help-content-config.interface-C5ES
4
4
  export { a as HelpBrandConfig } from '../../help-content-config.interface-C5ESIod9.js';
5
5
  import { HelpHeading, HelpArticle, HelpMode } from './server.js';
6
6
  export { HELP_MODES, HelpRedirect } from './server.js';
7
- import { u as usePageUrlGenerator } from '../../usePageUrlGenerator-J_ECv-oQ.js';
8
- import '../../types-bsm8Fp55.js';
7
+ import { u as usePageUrlGenerator } from '../../usePageUrlGenerator-B-3FRKBD.js';
8
+ import '../../types-Cn153-mH.js';
9
9
  import 'lucide-react';
10
10
  import '../../ApiRequestDataTypeInterface-CYEcRUrh.js';
11
11