@djangocfg/layouts 2.1.47 → 2.1.49

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/layouts",
3
- "version": "2.1.47",
3
+ "version": "2.1.49",
4
4
  "description": "Simple, straightforward layout components for Next.js - import and use with props",
5
5
  "keywords": [
6
6
  "layouts",
@@ -92,12 +92,13 @@
92
92
  "check": "tsc --noEmit"
93
93
  },
94
94
  "peerDependencies": {
95
- "@djangocfg/api": "^2.1.47",
96
- "@djangocfg/centrifugo": "^2.1.47",
97
- "@djangocfg/ui-nextjs": "^2.1.47",
95
+ "@djangocfg/api": "^2.1.49",
96
+ "@djangocfg/centrifugo": "^2.1.49",
97
+ "@djangocfg/ui-nextjs": "^2.1.49",
98
98
  "@hookform/resolvers": "^5.2.0",
99
99
  "consola": "^3.4.2",
100
100
  "lucide-react": "^0.545.0",
101
+ "moment": "^2.30.1",
101
102
  "next": ">=15.0.0",
102
103
  "p-retry": "^7.0.0",
103
104
  "react": "^19.1.0",
@@ -107,15 +108,15 @@
107
108
  "swr": "^2.3.7",
108
109
  "tailwindcss": "^4.1.14",
109
110
  "tailwindcss-animate": "^1.0.7",
110
- "zod": "^4.1.13",
111
- "moment": "^2.30.1"
111
+ "zod": "^4.1.13"
112
112
  },
113
113
  "dependencies": {
114
+ "nextjs-toploader": "^3.9.17",
114
115
  "react-ga4": "^2.1.0",
115
116
  "uuid": "^11.1.0"
116
117
  },
117
118
  "devDependencies": {
118
- "@djangocfg/typescript-config": "^2.1.47",
119
+ "@djangocfg/typescript-config": "^2.1.49",
119
120
  "@types/node": "^24.7.2",
120
121
  "@types/react": "^19.1.0",
121
122
  "@types/react-dom": "^19.1.0",
@@ -126,4 +127,3 @@
126
127
  "access": "public"
127
128
  }
128
129
  }
129
-
@@ -7,6 +7,5 @@ export type { ClientOnlyProps } from './ClientOnly';
7
7
  export { JsonLd } from './JsonLd';
8
8
  export { LucideIcon } from './LucideIcon';
9
9
  export type { LucideIconProps } from './LucideIcon';
10
- export { PageProgress } from './PageProgress';
11
10
  export { Suspense } from './Suspense';
12
11
 
package/src/index.ts CHANGED
@@ -28,6 +28,10 @@
28
28
  // Layout components
29
29
  export * from './layouts';
30
30
 
31
+ // Re-export useRouter from nextjs-toploader for progress bar support
32
+ // Use this instead of 'next/navigation' useRouter for router.push() to trigger progress
33
+ export { useRouter } from 'nextjs-toploader/app';
34
+
31
35
  // Snippets - Reusable UI components (includes Analytics)
32
36
  export * from './snippets';
33
37
 
@@ -45,6 +45,7 @@
45
45
  'use client';
46
46
 
47
47
  import dynamic from 'next/dynamic';
48
+ import NextTopLoader from 'nextjs-toploader';
48
49
  import { ReactNode } from 'react';
49
50
  import { SWRConfig } from 'swr';
50
51
 
@@ -52,8 +53,6 @@ import { getCentrifugoAuthTokenRetrieve } from '@djangocfg/api';
52
53
  import { AuthProvider } from '@djangocfg/api/auth';
53
54
  import { CentrifugoProvider } from '@djangocfg/centrifugo';
54
55
  import { SonnerToaster, ThemeProvider, TooltipProvider } from '@djangocfg/ui-nextjs';
55
-
56
- import { PageProgress } from '../../components/core/PageProgress';
57
56
  import { ErrorBoundary } from '../../components/errors/ErrorBoundary';
58
57
  import { ErrorTrackingProvider } from '../../components/errors/ErrorsTracker';
59
58
  import { AnalyticsProvider } from '../../snippets/Analytics';
@@ -143,7 +142,12 @@ export function BaseApp({
143
142
  onError={errorTracking?.onError}
144
143
  >
145
144
  {children}
146
- <PageProgress />
145
+ <NextTopLoader
146
+ color="hsl(var(--primary))"
147
+ height={3}
148
+ showSpinner={false}
149
+ shadow="0 0 10px hsl(var(--primary)), 0 0 5px hsl(var(--primary))"
150
+ />
147
151
  <SonnerToaster />
148
152
 
149
153
  {/* PWA Install Hint */}
@@ -10,6 +10,7 @@
10
10
  import { Bell, X } from 'lucide-react';
11
11
  import React, { useEffect, useState } from 'react';
12
12
 
13
+ import { useAuth } from '@djangocfg/api/auth';
13
14
  import { Button } from '@djangocfg/ui-nextjs';
14
15
 
15
16
  import { usePushNotifications } from '../hooks/usePushNotifications';
@@ -60,6 +61,7 @@ export function PushPrompt({
60
61
  onEnabled,
61
62
  onDismissed,
62
63
  }: PushPromptProps) {
64
+ const { isAuthenticated, isLoading: isAuthLoading } = useAuth();
63
65
  const { isSupported, permission, isSubscribed, subscribe } = usePushNotifications({
64
66
  vapidPublicKey,
65
67
  subscribeEndpoint,
@@ -70,6 +72,11 @@ export function PushPrompt({
70
72
 
71
73
  // Check if should show
72
74
  useEffect(() => {
75
+ // Wait for auth to complete, don't show for unauthenticated users
76
+ if (isAuthLoading || !isAuthenticated) {
77
+ return;
78
+ }
79
+
73
80
  if (!isSupported || isSubscribed || permission === 'denied') {
74
81
  return;
75
82
  }
@@ -89,7 +96,7 @@ export function PushPrompt({
89
96
  // Show after delay
90
97
  const timer = setTimeout(() => setShow(true), delayMs);
91
98
  return () => clearTimeout(timer);
92
- }, [isSupported, isSubscribed, permission, requirePWA, resetAfterDays, delayMs]);
99
+ }, [isAuthLoading, isAuthenticated, isSupported, isSubscribed, permission, requirePWA, resetAfterDays, delayMs]);
93
100
 
94
101
  const handleEnable = async () => {
95
102
  setEnabling(true);
@@ -1,127 +0,0 @@
1
- /**
2
- * PageProgress - Loading progress bar
3
- *
4
- * Shows a progress bar at the top of the page during route transitions
5
- */
6
-
7
- 'use client';
8
-
9
- import { usePathname } from 'next/navigation';
10
- import { useEffect, useRef, useState } from 'react';
11
-
12
- export function PageProgress() {
13
- const pathname = usePathname();
14
- const [loading, setLoading] = useState(false);
15
- const [progress, setProgress] = useState(0);
16
- const [mounted, setMounted] = useState(false);
17
- const progressTimer = useRef<NodeJS.Timeout | null>(null);
18
- const prevPathname = useRef<string | null>(null);
19
-
20
- useEffect(() => {
21
- setMounted(true);
22
- }, []);
23
-
24
- // Simulate realistic progress
25
- const startFakeProgress = () => {
26
- // Clear any existing timer
27
- if (progressTimer.current) {
28
- clearInterval(progressTimer.current);
29
- }
30
-
31
- setProgress(0);
32
-
33
- // Quickly go to 20% to show immediate feedback
34
- setTimeout(() => setProgress(20), 50);
35
-
36
- // Then slowly increase to 90% (never reach 100% until actually complete)
37
- progressTimer.current = setInterval(() => {
38
- setProgress((prevProgress) => {
39
- if (prevProgress >= 90) {
40
- if (progressTimer.current) {
41
- clearInterval(progressTimer.current);
42
- }
43
- return 90;
44
- }
45
-
46
- // Slow down as we get closer to 90%
47
- const increment = 90 - prevProgress;
48
- return prevProgress + (increment / 10);
49
- });
50
- }, 300);
51
- };
52
-
53
- const completeProgress = () => {
54
- // Clear any existing timer
55
- if (progressTimer.current) {
56
- clearInterval(progressTimer.current);
57
- progressTimer.current = null;
58
- }
59
-
60
- // Jump to 100% and then hide after showing completion
61
- setProgress(100);
62
- setTimeout(() => {
63
- setLoading(false);
64
- setTimeout(() => {
65
- setProgress(0);
66
- }, 300); // Wait for fade out animation
67
- }, 500); // Show 100% for half a second
68
- };
69
-
70
- // Track pathname changes (App Router equivalent of routeChangeStart/Complete)
71
- useEffect(() => {
72
- // Skip on initial mount
73
- if (prevPathname.current === null) {
74
- prevPathname.current = pathname;
75
- return;
76
- }
77
-
78
- // Only trigger if pathname actually changed
79
- if (prevPathname.current !== pathname) {
80
- setLoading(true);
81
- startFakeProgress();
82
-
83
- // Complete progress after a short delay (simulating route change)
84
- const timeout = setTimeout(() => {
85
- completeProgress();
86
- prevPathname.current = pathname;
87
- }, 100);
88
-
89
- return () => {
90
- clearTimeout(timeout);
91
- if (progressTimer.current) {
92
- clearInterval(progressTimer.current);
93
- }
94
- };
95
- }
96
- }, [pathname]);
97
-
98
- if (!mounted) {
99
- return null;
100
- }
101
-
102
- return (
103
- <div
104
- data-page-progress="root"
105
- data-loading={loading}
106
- data-progress={progress}
107
- className={`fixed top-0 left-0 w-full transition-opacity duration-300 ${
108
- loading ? 'opacity-100' : 'opacity-0'
109
- }`}
110
- style={{
111
- zIndex: 99999,
112
- height: '3px',
113
- }}
114
- >
115
- <div
116
- className="h-full transition-all duration-200 ease-linear"
117
- style={{
118
- width: `${progress}%`,
119
- background: 'linear-gradient(90deg, #3b82f6 0%, #60a5fa 50%, #3b82f6 100%)',
120
- boxShadow: '0 0 10px rgba(59, 130, 246, 0.6), 0 0 20px rgba(59, 130, 246, 0.4), 0 0 30px rgba(59, 130, 246, 0.2)',
121
- filter: 'drop-shadow(0 0 8px rgba(59, 130, 246, 0.8))',
122
- }}
123
- />
124
- </div>
125
- );
126
- }
127
-