@djangocfg/layouts 2.1.488 → 2.1.490

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -171,6 +171,18 @@ Wraps `Boundary` from `@djangocfg/ui-core` — same `variant`/`fallback`/`resetK
171
171
 
172
172
  For local UI widgets that should not report to backend, use `Boundary` from `@djangocfg/ui-core` directly.
173
173
 
174
+ ## Analytics and auth funnels
175
+
176
+ `BaseApp` mounts the first-party `AnalyticsProvider` once and bridges events
177
+ from `@djangocfg/api/auth`. `AuthLayout` adds a small `layouts.auth.step_viewed`
178
+ breadcrumb at each visible step; the server-confirmed `auth_login_success`
179
+ event remains the conversion source of truth.
180
+
181
+ For a custom product goal, use `useAnalytics().event(name, props)` from
182
+ `@djangocfg/analytics`. See that package's README for the typed event catalog,
183
+ browser facade, and extension pattern. Do not create a second route listener or
184
+ send analytics directly from a layout.
185
+
174
186
  ---
175
187
 
176
188
  ## Package exports
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/layouts",
3
- "version": "2.1.488",
3
+ "version": "2.1.490",
4
4
  "description": "Simple, straightforward layout components for Next.js - import and use with props",
5
5
  "keywords": [
6
6
  "layouts",
@@ -89,12 +89,12 @@
89
89
  "check": "tsc --noEmit"
90
90
  },
91
91
  "peerDependencies": {
92
- "@djangocfg/analytics": "^2.1.488",
93
- "@djangocfg/api": "^2.1.488",
94
- "@djangocfg/centrifugo": "^2.1.488",
95
- "@djangocfg/devtools": "^2.1.488",
96
- "@djangocfg/i18n": "^2.1.488",
97
- "@djangocfg/ui-core": "^2.1.488",
92
+ "@djangocfg/analytics": "^2.1.490",
93
+ "@djangocfg/api": "^2.1.490",
94
+ "@djangocfg/centrifugo": "^2.1.490",
95
+ "@djangocfg/devtools": "^2.1.490",
96
+ "@djangocfg/i18n": "^2.1.490",
97
+ "@djangocfg/ui-core": "^2.1.490",
98
98
  "@hookform/resolvers": "^5.2.2",
99
99
  "consola": "^3.4.2",
100
100
  "lucide-react": "^0.545.0",
@@ -124,14 +124,14 @@
124
124
  "uuid": "^11.1.1"
125
125
  },
126
126
  "devDependencies": {
127
- "@djangocfg/analytics": "^2.1.488",
128
- "@djangocfg/api": "^2.1.488",
129
- "@djangocfg/centrifugo": "^2.1.488",
130
- "@djangocfg/devtools": "^2.1.488",
131
- "@djangocfg/i18n": "^2.1.488",
132
- "@djangocfg/typescript-config": "^2.1.488",
133
- "@djangocfg/ui-core": "^2.1.488",
134
- "@djangocfg/ui-tools": "^2.1.488",
127
+ "@djangocfg/analytics": "^2.1.490",
128
+ "@djangocfg/api": "^2.1.490",
129
+ "@djangocfg/centrifugo": "^2.1.490",
130
+ "@djangocfg/devtools": "^2.1.490",
131
+ "@djangocfg/i18n": "^2.1.490",
132
+ "@djangocfg/typescript-config": "^2.1.490",
133
+ "@djangocfg/ui-core": "^2.1.490",
134
+ "@djangocfg/ui-tools": "^2.1.490",
135
135
  "@types/node": "^25.9.5",
136
136
  "@types/react": "19.2.15",
137
137
  "@types/react-dom": "19.2.3",
@@ -13,6 +13,7 @@
13
13
  import React, { createContext, memo, useContext, useMemo } from 'react';
14
14
 
15
15
  import { consumeSavedRedirect, useAuth, useCfgRouter } from '@djangocfg/api/auth';
16
+ import { useAnalytics } from '@djangocfg/analytics';
16
17
  import { useAppT } from '@djangocfg/i18n';
17
18
 
18
19
  import { Suspense } from '../../components';
@@ -81,6 +82,7 @@ export const AuthLayout: React.FC<AuthLayoutProps> = (props) => {
81
82
  <AuthLayoutContext.Provider value={layoutContextValue}>
82
83
  {/* Full-screen success overlay */}
83
84
  <AuthSuccessOverlay />
85
+ <AuthFunnelTracker />
84
86
 
85
87
  <AuthShell variant={variant} mediaSide={mediaSide} background={background} sidebar={sidebar} className={className}>
86
88
  {/* Handle OAuth callback when GitHub auth is enabled */}
@@ -95,6 +97,27 @@ export const AuthLayout: React.FC<AuthLayoutProps> = (props) => {
95
97
  );
96
98
  };
97
99
 
100
+ /**
101
+ * A lightweight UI breadcrumb around the server-confirmed auth events.
102
+ *
103
+ * `auth_login_success` remains server-authoritative. This event only answers
104
+ * where a person stopped in the layout, and quietly becomes a no-op when an
105
+ * app intentionally does not mount AnalyticsProvider.
106
+ */
107
+ const AuthFunnelTracker: React.FC = memo(() => {
108
+ const { step } = useAuthFormContext();
109
+ const { event, isEnabled } = useAnalytics();
110
+ const lastStep = React.useRef<string | null>(null);
111
+
112
+ React.useEffect(() => {
113
+ if (!isEnabled || lastStep.current === step) return;
114
+ lastStep.current = step;
115
+ event('layouts.auth.step_viewed', { step });
116
+ }, [event, isEnabled, step]);
117
+
118
+ return null;
119
+ });
120
+
98
121
  /** Renders custom children only on the identifier step — hides them on otp / 2fa / etc. */
99
122
  const AuthHeaderSlot: React.FC<{ children?: React.ReactNode }> = memo(({ children }) => {
100
123
  const { step } = useAuthFormContext();