@djangocfg/nextjs 1.0.1
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/LICENSE +21 -0
- package/README.md +449 -0
- package/package.json +133 -0
- package/src/components/HomePage.tsx +73 -0
- package/src/components/index.ts +7 -0
- package/src/config/base-next-config.ts +262 -0
- package/src/config/index.ts +6 -0
- package/src/contact/index.ts +13 -0
- package/src/contact/route.ts +102 -0
- package/src/contact/submit.ts +80 -0
- package/src/errors/ErrorLayout.tsx +228 -0
- package/src/errors/errorConfig.ts +118 -0
- package/src/errors/index.ts +10 -0
- package/src/health/index.ts +7 -0
- package/src/health/route.ts +65 -0
- package/src/health/types.ts +19 -0
- package/src/index.ts +36 -0
- package/src/legal/LegalPage.tsx +85 -0
- package/src/legal/configs.ts +131 -0
- package/src/legal/index.ts +24 -0
- package/src/legal/pages.tsx +58 -0
- package/src/legal/types.ts +15 -0
- package/src/navigation/index.ts +9 -0
- package/src/navigation/types.ts +68 -0
- package/src/navigation/utils.ts +181 -0
- package/src/og-image/README.md +66 -0
- package/src/og-image/components/DefaultTemplate.tsx +369 -0
- package/src/og-image/components/index.ts +9 -0
- package/src/og-image/index.ts +27 -0
- package/src/og-image/route.tsx +253 -0
- package/src/og-image/types.ts +46 -0
- package/src/og-image/utils/fonts.ts +150 -0
- package/src/og-image/utils/index.ts +28 -0
- package/src/og-image/utils/metadata.ts +235 -0
- package/src/og-image/utils/url.ts +327 -0
- package/src/sitemap/generator.ts +64 -0
- package/src/sitemap/index.ts +8 -0
- package/src/sitemap/route.ts +74 -0
- package/src/sitemap/types.ts +20 -0
- package/src/types.ts +35 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Reforms.ai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
# @djangocfg/nextjs
|
|
2
|
+
|
|
3
|
+
> Comprehensive Next.js utilities and components: sitemap generation, health checks, OG images, legal pages, error pages, and more
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/@djangocfg/nextjs)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
8
|
+
**Part of [DjangoCFG](https://djangocfg.com)** — a modern Django framework for building production-ready SaaS applications. All `@djangocfg/*` packages are designed to work together, providing type-safe configuration, real-time features, and beautiful admin interfaces out of the box.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
|
|
14
|
+
`@djangocfg/nextjs` provides a comprehensive set of utilities and components for Next.js applications. From SEO optimization with sitemaps to health monitoring, from dynamic OG images to ready-to-use legal pages, this package covers common Next.js needs out of the box.
|
|
15
|
+
|
|
16
|
+
## Features
|
|
17
|
+
|
|
18
|
+
- **Base Next.js Config** - Universal, reusable Next.js configuration factory for monorepos
|
|
19
|
+
- **Sitemap Generation** - Dynamic XML sitemap generation for SEO
|
|
20
|
+
- **Health Checks** - Production-ready health monitoring endpoints
|
|
21
|
+
- **OG Images** - Dynamic Open Graph image generation with templates
|
|
22
|
+
- **Navigation Utilities** - Route definitions, menu generation, and navigation helpers
|
|
23
|
+
- **Legal Pages** - Pre-built privacy, terms, cookies, and security pages
|
|
24
|
+
- **Error Pages** - Reusable 404 and 500 error layouts
|
|
25
|
+
- **HomePage Component** - Smart authentication redirect component
|
|
26
|
+
- **TypeScript** - Full type safety throughout
|
|
27
|
+
- **Self-Contained** - No external dependencies beyond Next.js
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install @djangocfg/nextjs
|
|
33
|
+
# or
|
|
34
|
+
pnpm add @djangocfg/nextjs
|
|
35
|
+
# or
|
|
36
|
+
yarn add @djangocfg/nextjs
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Quick Start
|
|
40
|
+
|
|
41
|
+
### Base Next.js Configuration
|
|
42
|
+
|
|
43
|
+
Create a reusable base configuration for all your Next.js projects:
|
|
44
|
+
|
|
45
|
+
```tsx
|
|
46
|
+
// next.config.ts
|
|
47
|
+
import { createBaseNextConfig } from '@djangocfg/nextjs/config';
|
|
48
|
+
import bundleAnalyzer from '@next/bundle-analyzer';
|
|
49
|
+
|
|
50
|
+
const withBundleAnalyzer = bundleAnalyzer({
|
|
51
|
+
enabled: process.env.ANALYZE === 'true',
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
export default withBundleAnalyzer(createBaseNextConfig({
|
|
55
|
+
// Base path for static builds
|
|
56
|
+
basePath: process.env.NEXT_PUBLIC_STATIC_BUILD === 'true' ? '/cfg/admin' : undefined,
|
|
57
|
+
|
|
58
|
+
// Add custom transpile packages
|
|
59
|
+
transpilePackages: ['my-custom-package'],
|
|
60
|
+
|
|
61
|
+
// Custom webpack configuration (called after base webpack logic)
|
|
62
|
+
webpack: (config, options) => {
|
|
63
|
+
// Your custom webpack rules
|
|
64
|
+
return config;
|
|
65
|
+
},
|
|
66
|
+
}));
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### Sitemap Generation
|
|
70
|
+
|
|
71
|
+
Generate dynamic XML sitemaps for SEO:
|
|
72
|
+
|
|
73
|
+
```tsx
|
|
74
|
+
// app/sitemap.ts
|
|
75
|
+
import { createSitemapHandler } from '@djangocfg/nextjs/sitemap';
|
|
76
|
+
|
|
77
|
+
export default createSitemapHandler({
|
|
78
|
+
siteUrl: 'https://example.com',
|
|
79
|
+
staticPages: [
|
|
80
|
+
{ loc: '/', changefreq: 'daily', priority: 1.0 },
|
|
81
|
+
{ loc: '/about', changefreq: 'monthly', priority: 0.8 },
|
|
82
|
+
],
|
|
83
|
+
dynamicPages: async () => {
|
|
84
|
+
const posts = await fetchPosts();
|
|
85
|
+
return posts.map(post => ({
|
|
86
|
+
loc: `/posts/${post.slug}`,
|
|
87
|
+
lastmod: post.updatedAt,
|
|
88
|
+
changefreq: 'weekly',
|
|
89
|
+
priority: 0.7,
|
|
90
|
+
}));
|
|
91
|
+
},
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Health Check Endpoint
|
|
96
|
+
|
|
97
|
+
Create a health monitoring endpoint:
|
|
98
|
+
|
|
99
|
+
```tsx
|
|
100
|
+
// app/api/health/route.ts
|
|
101
|
+
import { createHealthHandler } from '@djangocfg/nextjs/health';
|
|
102
|
+
|
|
103
|
+
export const GET = createHealthHandler({
|
|
104
|
+
version: process.env.NEXT_PUBLIC_APP_VERSION || '1.0.0',
|
|
105
|
+
checks: [
|
|
106
|
+
{
|
|
107
|
+
name: 'database',
|
|
108
|
+
check: async () => {
|
|
109
|
+
// Check database connection
|
|
110
|
+
return await checkDatabase();
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
],
|
|
114
|
+
});
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### OG Image Generation
|
|
118
|
+
|
|
119
|
+
Generate dynamic Open Graph images with custom templates:
|
|
120
|
+
|
|
121
|
+
```tsx
|
|
122
|
+
// app/api/og/route.tsx
|
|
123
|
+
import { createOgImageHandler } from '@djangocfg/nextjs/og-image';
|
|
124
|
+
import { OgImageTemplate } from '@/components/OgImageTemplate';
|
|
125
|
+
|
|
126
|
+
export const runtime = 'edge';
|
|
127
|
+
|
|
128
|
+
const handler = createOgImageHandler({
|
|
129
|
+
template: OgImageTemplate,
|
|
130
|
+
defaultProps: {
|
|
131
|
+
siteName: 'My App',
|
|
132
|
+
logo: '/logo.svg',
|
|
133
|
+
},
|
|
134
|
+
fonts: [
|
|
135
|
+
{ family: 'Manrope', weight: 700 },
|
|
136
|
+
{ family: 'Manrope', weight: 500 },
|
|
137
|
+
],
|
|
138
|
+
size: { width: 1200, height: 630 },
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
export async function GET(request: NextRequest) {
|
|
142
|
+
return handler.GET(request);
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Automatically add OG images to page metadata:
|
|
147
|
+
|
|
148
|
+
```tsx
|
|
149
|
+
// app/page.tsx
|
|
150
|
+
import { generateMetadata } from '@core/metadata';
|
|
151
|
+
|
|
152
|
+
export const metadata = generateMetadata({
|
|
153
|
+
title: 'My Page',
|
|
154
|
+
description: 'Page description',
|
|
155
|
+
});
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Legal Pages
|
|
159
|
+
|
|
160
|
+
Use pre-built legal pages:
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
// app/legal/privacy/page.tsx
|
|
164
|
+
import { PrivacyPage } from '@djangocfg/nextjs/legal';
|
|
165
|
+
|
|
166
|
+
export default PrivacyPage;
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Available pages:
|
|
170
|
+
- `PrivacyPage` - Privacy policy
|
|
171
|
+
- `TermsPage` - Terms of service
|
|
172
|
+
- `CookiesPage` - Cookie policy
|
|
173
|
+
- `SecurityPage` - Security policy
|
|
174
|
+
|
|
175
|
+
### Error Pages
|
|
176
|
+
|
|
177
|
+
Use reusable error layouts:
|
|
178
|
+
|
|
179
|
+
```tsx
|
|
180
|
+
// app/not-found.tsx
|
|
181
|
+
import { ErrorLayout } from '@djangocfg/nextjs/errors';
|
|
182
|
+
|
|
183
|
+
export default function NotFound() {
|
|
184
|
+
return <ErrorLayout code={404} supportEmail="support@example.com" />;
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
```tsx
|
|
189
|
+
// app/error.tsx
|
|
190
|
+
'use client';
|
|
191
|
+
|
|
192
|
+
import { useEffect } from 'react';
|
|
193
|
+
import { ErrorLayout } from '@djangocfg/nextjs/errors';
|
|
194
|
+
|
|
195
|
+
export default function Error({ error, reset }: { error: Error; reset: () => void }) {
|
|
196
|
+
useEffect(() => {
|
|
197
|
+
console.error('Error:', error);
|
|
198
|
+
}, [error]);
|
|
199
|
+
|
|
200
|
+
return <ErrorLayout code={500} supportEmail="support@example.com" />;
|
|
201
|
+
}
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### HomePage Component
|
|
205
|
+
|
|
206
|
+
Smart authentication redirect component:
|
|
207
|
+
|
|
208
|
+
```tsx
|
|
209
|
+
// app/page.tsx
|
|
210
|
+
import { HomePage } from '@djangocfg/nextjs/components';
|
|
211
|
+
|
|
212
|
+
export default function Page() {
|
|
213
|
+
return (
|
|
214
|
+
<HomePage
|
|
215
|
+
authenticatedPath="/dashboard"
|
|
216
|
+
unauthenticatedPath="/auth"
|
|
217
|
+
loadingText="Loading..."
|
|
218
|
+
/>
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
## Exports
|
|
224
|
+
|
|
225
|
+
| Path | Description |
|
|
226
|
+
|------|-------------|
|
|
227
|
+
| `@djangocfg/nextjs` | Main exports (all modules) |
|
|
228
|
+
| `@djangocfg/nextjs/config` | Base Next.js configuration factory |
|
|
229
|
+
| `@djangocfg/nextjs/sitemap` | Sitemap generation utilities |
|
|
230
|
+
| `@djangocfg/nextjs/health` | Health check handlers |
|
|
231
|
+
| `@djangocfg/nextjs/og-image` | OG image generation |
|
|
232
|
+
| `@djangocfg/nextjs/og-image/utils` | OG image URL and metadata utilities |
|
|
233
|
+
| `@djangocfg/nextjs/navigation` | Route definitions, menu generation, navigation helpers |
|
|
234
|
+
| `@djangocfg/nextjs/legal` | Legal page components |
|
|
235
|
+
| `@djangocfg/nextjs/errors` | Error page layouts |
|
|
236
|
+
| `@djangocfg/nextjs/components` | Reusable components |
|
|
237
|
+
|
|
238
|
+
## API Reference
|
|
239
|
+
|
|
240
|
+
### Base Configuration
|
|
241
|
+
|
|
242
|
+
#### `createBaseNextConfig(options)`
|
|
243
|
+
|
|
244
|
+
Creates a reusable base Next.js configuration with standard DjangoCFG settings.
|
|
245
|
+
|
|
246
|
+
```tsx
|
|
247
|
+
createBaseNextConfig({
|
|
248
|
+
basePath?: string; // Base path for static builds
|
|
249
|
+
transpilePackages?: string[]; // Additional packages to transpile
|
|
250
|
+
optimizePackageImports?: string[]; // Additional packages to optimize
|
|
251
|
+
webpack?: (config, options) => config; // Custom webpack configuration
|
|
252
|
+
experimental?: NextConfig['experimental']; // Custom experimental options
|
|
253
|
+
env?: Record<string, string>; // Custom environment variables
|
|
254
|
+
// ... any other NextConfig options
|
|
255
|
+
})
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
**Features:**
|
|
259
|
+
- Automatic dev/production optimizations
|
|
260
|
+
- Filesystem caching for faster rebuilds
|
|
261
|
+
- Compression plugins for static builds (Gzip + Brotli)
|
|
262
|
+
- Standard transpile packages for monorepo
|
|
263
|
+
- Type-safe configuration merging
|
|
264
|
+
|
|
265
|
+
### Sitemap
|
|
266
|
+
|
|
267
|
+
#### `createSitemapHandler(options)`
|
|
268
|
+
|
|
269
|
+
Creates a Next.js route handler for sitemap generation.
|
|
270
|
+
|
|
271
|
+
```tsx
|
|
272
|
+
createSitemapHandler({
|
|
273
|
+
siteUrl: string;
|
|
274
|
+
staticPages?: SitemapUrl[];
|
|
275
|
+
dynamicPages?: SitemapUrl[] | (() => Promise<SitemapUrl[]>);
|
|
276
|
+
cacheControl?: string;
|
|
277
|
+
})
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### Health
|
|
281
|
+
|
|
282
|
+
#### `createHealthHandler(config)`
|
|
283
|
+
|
|
284
|
+
Creates a health check route handler.
|
|
285
|
+
|
|
286
|
+
```tsx
|
|
287
|
+
createHealthHandler({
|
|
288
|
+
version?: string;
|
|
289
|
+
checks?: Array<{ name: string; check: () => Promise<boolean> }>;
|
|
290
|
+
customData?: Record<string, any>;
|
|
291
|
+
})
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### OG Image
|
|
295
|
+
|
|
296
|
+
#### `createOgImageHandler(config)`
|
|
297
|
+
|
|
298
|
+
Creates an OG image generation handler with custom template component.
|
|
299
|
+
|
|
300
|
+
```tsx
|
|
301
|
+
createOgImageHandler({
|
|
302
|
+
template: (props: OgImageTemplateProps) => ReactElement; // Template component
|
|
303
|
+
defaultProps?: Partial<OgImageTemplateProps>; // Default props
|
|
304
|
+
fonts?: Array<{ family: string; weight: number }>; // Google Fonts to load
|
|
305
|
+
size?: { width: number; height: number }; // Image dimensions
|
|
306
|
+
debug?: boolean; // Enable debug mode
|
|
307
|
+
})
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
#### `generateOgImageMetadata(metadata, params?, options?)`
|
|
311
|
+
|
|
312
|
+
Automatically adds OG image to Next.js metadata.
|
|
313
|
+
|
|
314
|
+
```tsx
|
|
315
|
+
generateOgImageMetadata(
|
|
316
|
+
metadata: Metadata, // Base metadata
|
|
317
|
+
ogImageParams?: Partial<OgImageUrlParams>, // Optional explicit params
|
|
318
|
+
options?: {
|
|
319
|
+
ogImageBaseUrl?: string; // Default: '/api/og'
|
|
320
|
+
siteUrl?: string; // Auto-detected if not provided
|
|
321
|
+
defaultParams?: Partial<OgImageUrlParams>; // Default params
|
|
322
|
+
useBase64?: boolean; // Default: true
|
|
323
|
+
}
|
|
324
|
+
)
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
#### `generateOgImageUrl(baseUrl, params, useBase64?)`
|
|
328
|
+
|
|
329
|
+
Generates OG image URL with base64-encoded parameters.
|
|
330
|
+
|
|
331
|
+
```tsx
|
|
332
|
+
generateOgImageUrl('/api/og', {
|
|
333
|
+
title: 'My Page',
|
|
334
|
+
description: 'Page description',
|
|
335
|
+
siteName: 'My Site',
|
|
336
|
+
logo: '/logo.svg',
|
|
337
|
+
}, true) // useBase64 = true (default)
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Navigation
|
|
341
|
+
|
|
342
|
+
#### `defineRoute(path, metadata, config?)`
|
|
343
|
+
|
|
344
|
+
Defines a route with metadata.
|
|
345
|
+
|
|
346
|
+
```tsx
|
|
347
|
+
defineRoute('/dashboard', {
|
|
348
|
+
title: 'Dashboard',
|
|
349
|
+
description: 'Dashboard overview',
|
|
350
|
+
}, {
|
|
351
|
+
basePath: '/admin',
|
|
352
|
+
isStaticBuild: false,
|
|
353
|
+
})
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
#### `isActive(current, target, allRoutes?)`
|
|
357
|
+
|
|
358
|
+
Checks if a route is currently active.
|
|
359
|
+
|
|
360
|
+
```tsx
|
|
361
|
+
isActive('/admin/dashboard', '/admin/dashboard') // true
|
|
362
|
+
isActive('/admin/dashboard/settings', '/admin/dashboard') // true
|
|
363
|
+
isActive('/admin/dashboard', '/admin/settings') // false
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
#### `routesToMenuItems(routes)`
|
|
367
|
+
|
|
368
|
+
Converts route definitions to menu items.
|
|
369
|
+
|
|
370
|
+
```tsx
|
|
371
|
+
const menuItems = routesToMenuItems(allRoutes);
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Legal Pages
|
|
375
|
+
|
|
376
|
+
#### `PrivacyPage`, `TermsPage`, `CookiesPage`, `SecurityPage`
|
|
377
|
+
|
|
378
|
+
Pre-built legal page components with default content. Can be customized:
|
|
379
|
+
|
|
380
|
+
```tsx
|
|
381
|
+
import { PrivacyPage, privacyConfig } from '@djangocfg/nextjs/legal';
|
|
382
|
+
|
|
383
|
+
// Use default
|
|
384
|
+
export default PrivacyPage;
|
|
385
|
+
|
|
386
|
+
// Or customize
|
|
387
|
+
export default function CustomPrivacy() {
|
|
388
|
+
return <PrivacyPage config={{
|
|
389
|
+
...privacyConfig,
|
|
390
|
+
lastUpdated: '2024-01-01',
|
|
391
|
+
}} />;
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Error Pages
|
|
396
|
+
|
|
397
|
+
#### `ErrorLayout`
|
|
398
|
+
|
|
399
|
+
Reusable error page layout component.
|
|
400
|
+
|
|
401
|
+
```tsx
|
|
402
|
+
<ErrorLayout
|
|
403
|
+
code={404 | 500 | string | number}
|
|
404
|
+
title?: string
|
|
405
|
+
description?: string
|
|
406
|
+
supportEmail?: string
|
|
407
|
+
actions?: React.ReactNode
|
|
408
|
+
showDefaultActions?: boolean
|
|
409
|
+
/>
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Components
|
|
413
|
+
|
|
414
|
+
#### `HomePage`
|
|
415
|
+
|
|
416
|
+
Authentication redirect component.
|
|
417
|
+
|
|
418
|
+
```tsx
|
|
419
|
+
<HomePage
|
|
420
|
+
authenticatedPath?: string // Default: '/private'
|
|
421
|
+
unauthenticatedPath?: string // Default: '/auth'
|
|
422
|
+
loadingText?: string // Default: 'Loading...'
|
|
423
|
+
/>
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
## Requirements
|
|
427
|
+
|
|
428
|
+
- Next.js >= 13.0.0 (App Router)
|
|
429
|
+
- React >= 18.0.0
|
|
430
|
+
- TypeScript >= 5.0.0
|
|
431
|
+
|
|
432
|
+
## Peer Dependencies
|
|
433
|
+
|
|
434
|
+
- `@djangocfg/layouts` - For auth context in HomePage
|
|
435
|
+
- `@djangocfg/ui` - For UI components in legal/error pages
|
|
436
|
+
- `lucide-react` - For icons
|
|
437
|
+
|
|
438
|
+
## Documentation
|
|
439
|
+
|
|
440
|
+
Full documentation available at [djangocfg.com](https://djangocfg.com)
|
|
441
|
+
|
|
442
|
+
## Contributing
|
|
443
|
+
|
|
444
|
+
Issues and pull requests are welcome at [GitHub](https://github.com/markolofsen/django-cfg)
|
|
445
|
+
|
|
446
|
+
## License
|
|
447
|
+
|
|
448
|
+
MIT - see [LICENSE](./LICENSE) for details
|
|
449
|
+
|
package/package.json
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@djangocfg/nextjs",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "Next.js utilities and components: sitemap, health, OG images, legal pages, error pages",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"nextjs",
|
|
7
|
+
"sitemap",
|
|
8
|
+
"health",
|
|
9
|
+
"og-image",
|
|
10
|
+
"legal-pages",
|
|
11
|
+
"error-pages",
|
|
12
|
+
"react",
|
|
13
|
+
"typescript"
|
|
14
|
+
],
|
|
15
|
+
"author": {
|
|
16
|
+
"name": "DjangoCFG",
|
|
17
|
+
"url": "https://djangocfg.com"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://djangocfg.com",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "https://github.com/markolofsen/django-cfg.git",
|
|
23
|
+
"directory": "packages/nextjs"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"url": "https://github.com/markolofsen/django-cfg/issues"
|
|
27
|
+
},
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"main": "./src/index.ts",
|
|
30
|
+
"types": "./src/index.ts",
|
|
31
|
+
"exports": {
|
|
32
|
+
".": {
|
|
33
|
+
"types": "./src/index.ts",
|
|
34
|
+
"import": "./src/index.ts",
|
|
35
|
+
"default": "./src/index.ts"
|
|
36
|
+
},
|
|
37
|
+
"./sitemap": {
|
|
38
|
+
"types": "./src/sitemap/index.ts",
|
|
39
|
+
"import": "./src/sitemap/index.ts",
|
|
40
|
+
"default": "./src/sitemap/index.ts"
|
|
41
|
+
},
|
|
42
|
+
"./health": {
|
|
43
|
+
"types": "./src/health/index.ts",
|
|
44
|
+
"import": "./src/health/index.ts",
|
|
45
|
+
"default": "./src/health/index.ts"
|
|
46
|
+
},
|
|
47
|
+
"./og-image": {
|
|
48
|
+
"types": "./src/og-image/index.ts",
|
|
49
|
+
"import": "./src/og-image/index.ts",
|
|
50
|
+
"default": "./src/og-image/index.ts"
|
|
51
|
+
},
|
|
52
|
+
"./og-image/utils": {
|
|
53
|
+
"types": "./src/og-image/utils/index.ts",
|
|
54
|
+
"import": "./src/og-image/utils/index.ts",
|
|
55
|
+
"default": "./src/og-image/utils/index.ts"
|
|
56
|
+
},
|
|
57
|
+
"./og-image/components": {
|
|
58
|
+
"types": "./src/og-image/components/index.ts",
|
|
59
|
+
"import": "./src/og-image/components/index.ts",
|
|
60
|
+
"default": "./src/og-image/components/index.ts"
|
|
61
|
+
},
|
|
62
|
+
"./legal": {
|
|
63
|
+
"types": "./src/legal/index.ts",
|
|
64
|
+
"import": "./src/legal/index.ts",
|
|
65
|
+
"default": "./src/legal/index.ts"
|
|
66
|
+
},
|
|
67
|
+
"./errors": {
|
|
68
|
+
"types": "./src/errors/index.ts",
|
|
69
|
+
"import": "./src/errors/index.ts",
|
|
70
|
+
"default": "./src/errors/index.ts"
|
|
71
|
+
},
|
|
72
|
+
"./components": {
|
|
73
|
+
"types": "./src/components/index.ts",
|
|
74
|
+
"import": "./src/components/index.ts",
|
|
75
|
+
"default": "./src/components/index.ts"
|
|
76
|
+
},
|
|
77
|
+
"./contact": {
|
|
78
|
+
"types": "./src/contact/index.ts",
|
|
79
|
+
"import": "./src/contact/index.ts",
|
|
80
|
+
"default": "./src/contact/index.ts"
|
|
81
|
+
},
|
|
82
|
+
"./navigation": {
|
|
83
|
+
"types": "./src/navigation/index.ts",
|
|
84
|
+
"import": "./src/navigation/index.ts",
|
|
85
|
+
"default": "./src/navigation/index.ts"
|
|
86
|
+
},
|
|
87
|
+
"./config": {
|
|
88
|
+
"types": "./src/config/index.ts",
|
|
89
|
+
"import": "./src/config/index.ts",
|
|
90
|
+
"default": "./src/config/index.ts"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"files": [
|
|
94
|
+
"src",
|
|
95
|
+
"README.md",
|
|
96
|
+
"LICENSE"
|
|
97
|
+
],
|
|
98
|
+
"scripts": {
|
|
99
|
+
"build": "tsup",
|
|
100
|
+
"dev": "tsup --watch",
|
|
101
|
+
"clean": "rm -rf dist",
|
|
102
|
+
"lint": "eslint .",
|
|
103
|
+
"check": "tsc --noEmit"
|
|
104
|
+
},
|
|
105
|
+
"dependencies": {
|
|
106
|
+
"@vercel/og": "^0.8.5"
|
|
107
|
+
},
|
|
108
|
+
"peerDependencies": {
|
|
109
|
+
"next": "^15.4.4",
|
|
110
|
+
"react": "^19.1.0",
|
|
111
|
+
"react-dom": "^19.1.0",
|
|
112
|
+
"@djangocfg/ui": "^1.4.31",
|
|
113
|
+
"@djangocfg/layouts": "^2.0.1",
|
|
114
|
+
"@djangocfg/api": "^1.4.31",
|
|
115
|
+
"lucide-react": "^0.469.0"
|
|
116
|
+
},
|
|
117
|
+
"devDependencies": {
|
|
118
|
+
"@djangocfg/typescript-config": "^1.4.31",
|
|
119
|
+
"@djangocfg/layouts": "^2.0.1",
|
|
120
|
+
"@types/node": "^24.7.2",
|
|
121
|
+
"@types/react": "19.2.2",
|
|
122
|
+
"@types/react-dom": "19.2.1",
|
|
123
|
+
"@types/webpack": "^5.28.5",
|
|
124
|
+
"eslint": "^9.37.0",
|
|
125
|
+
"lucide-react": "^0.469.0",
|
|
126
|
+
"tsup": "^8.0.1",
|
|
127
|
+
"typescript": "^5.9.3"
|
|
128
|
+
},
|
|
129
|
+
"publishConfig": {
|
|
130
|
+
"access": "public"
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useEffect } from 'react';
|
|
4
|
+
import { useRouter } from 'next/navigation';
|
|
5
|
+
import { useAuth } from '@djangocfg/layouts';
|
|
6
|
+
import { Loader2 } from 'lucide-react';
|
|
7
|
+
|
|
8
|
+
export interface HomePageProps {
|
|
9
|
+
/**
|
|
10
|
+
* Path to redirect to when user is authenticated
|
|
11
|
+
* @default '/private'
|
|
12
|
+
*/
|
|
13
|
+
authenticatedPath?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Path to redirect to when user is not authenticated
|
|
16
|
+
* @default '/auth'
|
|
17
|
+
*/
|
|
18
|
+
unauthenticatedPath?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Custom loading text
|
|
21
|
+
* @default 'Loading...'
|
|
22
|
+
*/
|
|
23
|
+
loadingText?: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* HomePage - Root page component that handles authentication redirect
|
|
28
|
+
*
|
|
29
|
+
* Redirects authenticated users to authenticatedPath, otherwise to unauthenticatedPath
|
|
30
|
+
*
|
|
31
|
+
* Usage:
|
|
32
|
+
* ```tsx
|
|
33
|
+
* // app/page.tsx
|
|
34
|
+
* import { HomePage } from '@djangocfg/nextjs/components';
|
|
35
|
+
*
|
|
36
|
+
* export default function Page() {
|
|
37
|
+
* return (
|
|
38
|
+
* <HomePage
|
|
39
|
+
* authenticatedPath="/private"
|
|
40
|
+
* unauthenticatedPath="/auth"
|
|
41
|
+
* />
|
|
42
|
+
* );
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*/
|
|
46
|
+
export function HomePage({
|
|
47
|
+
authenticatedPath = '/private',
|
|
48
|
+
unauthenticatedPath = '/auth',
|
|
49
|
+
loadingText = 'Loading...',
|
|
50
|
+
}: HomePageProps) {
|
|
51
|
+
const { isAuthenticated } = useAuth();
|
|
52
|
+
const router = useRouter();
|
|
53
|
+
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (!isAuthenticated) {
|
|
56
|
+
router.push(unauthenticatedPath);
|
|
57
|
+
} else {
|
|
58
|
+
router.push(authenticatedPath);
|
|
59
|
+
}
|
|
60
|
+
}, [isAuthenticated, router, authenticatedPath, unauthenticatedPath]);
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<div className="flex items-center justify-center min-h-screen bg-background">
|
|
64
|
+
<div className="flex flex-col items-center gap-4">
|
|
65
|
+
<Loader2 className="animate-spin text-primary" style={{ width: '48px', height: '48px' }} />
|
|
66
|
+
<span className="text-lg font-medium text-muted-foreground">
|
|
67
|
+
{loadingText}
|
|
68
|
+
</span>
|
|
69
|
+
</div>
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|