create-nextjs-stack 0.1.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 (123) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +60 -0
  3. package/bin/cli.js +187 -0
  4. package/package.json +48 -0
  5. package/templates/admin/.env.example +11 -0
  6. package/templates/admin/README.md +82 -0
  7. package/templates/admin/app/(auth)/login/page.tsx +84 -0
  8. package/templates/admin/app/(dashboard)/[resource]/[id]/page.tsx +45 -0
  9. package/templates/admin/app/(dashboard)/[resource]/new/page.tsx +32 -0
  10. package/templates/admin/app/(dashboard)/[resource]/page.tsx +131 -0
  11. package/templates/admin/app/(dashboard)/categories/[id]/page.tsx +22 -0
  12. package/templates/admin/app/(dashboard)/categories/new/page.tsx +5 -0
  13. package/templates/admin/app/(dashboard)/categories/page.tsx +33 -0
  14. package/templates/admin/app/(dashboard)/clients/[id]/page.tsx +22 -0
  15. package/templates/admin/app/(dashboard)/clients/new/page.tsx +5 -0
  16. package/templates/admin/app/(dashboard)/clients/page.tsx +33 -0
  17. package/templates/admin/app/(dashboard)/dashboard/page.tsx +45 -0
  18. package/templates/admin/app/(dashboard)/layout.tsx +13 -0
  19. package/templates/admin/app/(dashboard)/products/[id]/page.tsx +22 -0
  20. package/templates/admin/app/(dashboard)/products/new/page.tsx +5 -0
  21. package/templates/admin/app/(dashboard)/products/page.tsx +33 -0
  22. package/templates/admin/app/(dashboard)/projects/[id]/page.tsx +22 -0
  23. package/templates/admin/app/(dashboard)/projects/new/page.tsx +5 -0
  24. package/templates/admin/app/(dashboard)/projects/page.tsx +33 -0
  25. package/templates/admin/app/(dashboard)/users/[id]/page.tsx +22 -0
  26. package/templates/admin/app/(dashboard)/users/new/page.tsx +5 -0
  27. package/templates/admin/app/(dashboard)/users/page.tsx +33 -0
  28. package/templates/admin/app/actions/resources.ts +46 -0
  29. package/templates/admin/app/actions/upload.ts +58 -0
  30. package/templates/admin/app/favicon.ico +0 -0
  31. package/templates/admin/app/globals.css +23 -0
  32. package/templates/admin/app/layout.tsx +23 -0
  33. package/templates/admin/app/page.tsx +5 -0
  34. package/templates/admin/components/admin/AdminLayoutClient.tsx +22 -0
  35. package/templates/admin/components/admin/DeleteModal.tsx +90 -0
  36. package/templates/admin/components/admin/FormLayout.tsx +113 -0
  37. package/templates/admin/components/admin/ImageUpload.tsx +137 -0
  38. package/templates/admin/components/admin/ResourceFormClient.tsx +62 -0
  39. package/templates/admin/components/admin/Sidebar.tsx +74 -0
  40. package/templates/admin/components/admin/SubmitButton.tsx +34 -0
  41. package/templates/admin/components/admin/ToastProvider.tsx +8 -0
  42. package/templates/admin/components/categories/CategoryForm.tsx +24 -0
  43. package/templates/admin/components/categories/CategoryList.tsx +113 -0
  44. package/templates/admin/components/clients/ClientForm.tsx +24 -0
  45. package/templates/admin/components/clients/ClientList.tsx +113 -0
  46. package/templates/admin/components/products/ProductForm.tsx +24 -0
  47. package/templates/admin/components/products/ProductList.tsx +117 -0
  48. package/templates/admin/components/projects/ProjectForm.tsx +24 -0
  49. package/templates/admin/components/projects/ProjectList.tsx +121 -0
  50. package/templates/admin/components/users/UserForm.tsx +39 -0
  51. package/templates/admin/components/users/UserList.tsx +101 -0
  52. package/templates/admin/config/resources.ts +123 -0
  53. package/templates/admin/eslint.config.mjs +18 -0
  54. package/templates/admin/hooks/useResource.ts +86 -0
  55. package/templates/admin/lib/services/base.service.ts +106 -0
  56. package/templates/admin/lib/services/categories.service.ts +7 -0
  57. package/templates/admin/lib/services/clients.service.ts +7 -0
  58. package/templates/admin/lib/services/index.ts +27 -0
  59. package/templates/admin/lib/services/products.service.ts +9 -0
  60. package/templates/admin/lib/services/projects.service.ts +22 -0
  61. package/templates/admin/lib/services/resource.service.ts +26 -0
  62. package/templates/admin/lib/services/users.service.ts +9 -0
  63. package/templates/admin/lib/supabase/client.ts +9 -0
  64. package/templates/admin/lib/supabase/middleware.ts +57 -0
  65. package/templates/admin/lib/supabase/server.ts +29 -0
  66. package/templates/admin/middleware.ts +15 -0
  67. package/templates/admin/next.config.ts +10 -0
  68. package/templates/admin/package-lock.json +6768 -0
  69. package/templates/admin/package.json +33 -0
  70. package/templates/admin/postcss.config.mjs +7 -0
  71. package/templates/admin/public/file.svg +1 -0
  72. package/templates/admin/public/globe.svg +1 -0
  73. package/templates/admin/public/next.svg +1 -0
  74. package/templates/admin/public/vercel.svg +1 -0
  75. package/templates/admin/public/window.svg +1 -0
  76. package/templates/admin/supabase_mock_data.sql +57 -0
  77. package/templates/admin/supabase_schema.sql +93 -0
  78. package/templates/admin/tsconfig.json +34 -0
  79. package/templates/web/.env.example +21 -0
  80. package/templates/web/README.md +129 -0
  81. package/templates/web/components.json +22 -0
  82. package/templates/web/eslint.config.mjs +25 -0
  83. package/templates/web/next.config.ts +25 -0
  84. package/templates/web/package-lock.json +6778 -0
  85. package/templates/web/package.json +45 -0
  86. package/templates/web/postcss.config.mjs +5 -0
  87. package/templates/web/src/app/api/contact/route.ts +181 -0
  88. package/templates/web/src/app/api/revalidate/route.ts +95 -0
  89. package/templates/web/src/app/error.tsx +28 -0
  90. package/templates/web/src/app/globals.css +838 -0
  91. package/templates/web/src/app/layout.tsx +126 -0
  92. package/templates/web/src/app/loading.tsx +60 -0
  93. package/templates/web/src/app/not-found.tsx +68 -0
  94. package/templates/web/src/app/page.tsx +106 -0
  95. package/templates/web/src/app/robots.ts +12 -0
  96. package/templates/web/src/app/sitemap.ts +66 -0
  97. package/templates/web/src/components/home/StatsGrid.tsx +89 -0
  98. package/templates/web/src/hooks/useIntersectionObserver.ts +39 -0
  99. package/templates/web/src/lib/providers/StoreProvider.tsx +12 -0
  100. package/templates/web/src/lib/seo/index.ts +4 -0
  101. package/templates/web/src/lib/seo/metadata.ts +103 -0
  102. package/templates/web/src/lib/seo/seo.config.ts +161 -0
  103. package/templates/web/src/lib/seo/seo.types.ts +76 -0
  104. package/templates/web/src/lib/services/categories.service.ts +38 -0
  105. package/templates/web/src/lib/services/categoryService.ts +251 -0
  106. package/templates/web/src/lib/services/clientService.ts +132 -0
  107. package/templates/web/src/lib/services/clients.service.ts +20 -0
  108. package/templates/web/src/lib/services/productService.ts +261 -0
  109. package/templates/web/src/lib/services/products.service.ts +38 -0
  110. package/templates/web/src/lib/services/projectService.ts +234 -0
  111. package/templates/web/src/lib/services/projects.service.ts +38 -0
  112. package/templates/web/src/lib/services/users.service.ts +20 -0
  113. package/templates/web/src/lib/supabase/client.ts +42 -0
  114. package/templates/web/src/lib/supabase/constants.ts +25 -0
  115. package/templates/web/src/lib/supabase/server.ts +29 -0
  116. package/templates/web/src/lib/supabase/types.ts +112 -0
  117. package/templates/web/src/lib/utils/cache.ts +98 -0
  118. package/templates/web/src/lib/utils/rate-limiter.ts +102 -0
  119. package/templates/web/src/store/actions/index.ts +2 -0
  120. package/templates/web/src/store/index.ts +13 -0
  121. package/templates/web/src/store/reducers/index.ts +13 -0
  122. package/templates/web/src/store/types/index.ts +2 -0
  123. package/templates/web/tsconfig.json +41 -0
@@ -0,0 +1,126 @@
1
+ import type { Metadata, Viewport } from "next";
2
+ import {
3
+ Geist,
4
+ Geist_Mono,
5
+ Inter,
6
+ Outfit,
7
+ Raleway,
8
+ Roboto,
9
+ Jersey_10,
10
+ } from "next/font/google";
11
+ import "@/app/globals.css";
12
+ import StoreProvider from "@/lib/providers/StoreProvider";
13
+ import { GoogleAnalytics } from "@next/third-parties/google";
14
+
15
+ const geistSans = Geist({
16
+ variable: "--font-geist-sans",
17
+ subsets: ["latin"],
18
+ });
19
+
20
+ const geistMono = Geist_Mono({
21
+ variable: "--font-geist-mono",
22
+ subsets: ["latin"],
23
+ });
24
+
25
+ const inter = Inter({
26
+ variable: "--font-inter",
27
+ subsets: ["latin"],
28
+ display: "swap",
29
+ });
30
+
31
+ const roboto = Roboto({
32
+ variable: "--font-roboto",
33
+ subsets: ["latin"],
34
+ weight: ["100", "300", "400", "500", "700", "900"],
35
+ display: "swap",
36
+ });
37
+
38
+ const outfit = Outfit({
39
+ variable: "--font-outfit",
40
+ subsets: ["latin"],
41
+ display: "swap",
42
+ });
43
+
44
+ const raleway = Raleway({
45
+ variable: "--font-raleway",
46
+ subsets: ["latin"],
47
+ display: "swap",
48
+ });
49
+
50
+ const jersey10 = Jersey_10({
51
+ variable: "--font-jersey-10",
52
+ subsets: ["latin"],
53
+ weight: "400",
54
+ display: "swap",
55
+ });
56
+
57
+ export const viewport: Viewport = {
58
+ themeColor: [
59
+ { media: "(prefers-color-scheme: light)", color: "#ffffff" },
60
+ { media: "(prefers-color-scheme: dark)", color: "#000000" },
61
+ ],
62
+ width: "device-width",
63
+ initialScale: 1,
64
+ maximumScale: 1,
65
+ };
66
+
67
+ export const metadata: Metadata = {
68
+ metadataBase: new URL("https://create-nextjs-stack.vercel.app"),
69
+ title: {
70
+ default: "Next.js Enterprise Starter",
71
+ template: "%s | Next.js Enterprise Starter",
72
+ },
73
+ description: "A production-ready Next.js starter template with Supabase, Tailwind CSS, and more.",
74
+ manifest: "/manifest.json",
75
+ appleWebApp: {
76
+ capable: true,
77
+ statusBarStyle: "default",
78
+ title: "Next.js Starter",
79
+ },
80
+ icons: {
81
+ icon: [
82
+ { url: "/favicon.png", sizes: "32x32" },
83
+ { url: "/favicon.png", sizes: "16x16" },
84
+ ],
85
+ apple: {
86
+ url: "/favicon.png",
87
+ sizes: "180x180",
88
+ },
89
+ shortcut: "/favicon.png",
90
+ },
91
+ alternates: {
92
+ canonical: "https://create-nextjs-stack.vercel.app",
93
+ },
94
+ openGraph: {
95
+ locale: "en_US",
96
+ type: "website",
97
+ siteName: "Next.js Enterprise Starter",
98
+ images: [
99
+ {
100
+ url: "/logo.png",
101
+ width: 1200,
102
+ height: 630,
103
+ alt: "Next.js Enterprise Starter",
104
+ },
105
+ ],
106
+ },
107
+ };
108
+
109
+ export default function RootLayout({
110
+ children,
111
+ }: {
112
+ children: React.ReactNode;
113
+ }) {
114
+ return (
115
+ <html lang="en">
116
+ <body
117
+ className={`${geistSans.variable} ${geistMono.variable} ${inter.variable} ${outfit.variable} ${roboto.variable} ${raleway.variable} ${jersey10.variable} antialiased`}
118
+ >
119
+ <StoreProvider>
120
+ {children}
121
+ <GoogleAnalytics gaId="G-XYZ1234567" />
122
+ </StoreProvider>
123
+ </body>
124
+ </html>
125
+ );
126
+ }
@@ -0,0 +1,60 @@
1
+ "use client";
2
+
3
+ import { motion } from "framer-motion";
4
+
5
+ export default function Loading() {
6
+ return (
7
+ <div className="flex flex-col items-center justify-center min-h-screen bg-black overflow-hidden relative">
8
+ {/* Background Grid Effect */}
9
+ <div className="absolute inset-0 bg-[linear-gradient(rgba(255,255,255,0.02)_1px,transparent_1px),linear-gradient(90deg,rgba(255,255,255,0.02)_1px,transparent_1px)] bg-[size:40px_40px] [mask-image:radial-gradient(ellipse_60%_60%_at_50%_50%,black,transparent)]" />
10
+
11
+ <motion.div
12
+ className="relative z-10 flex flex-col items-center gap-8"
13
+ initial={{ opacity: 0 }}
14
+ animate={{ opacity: 1 }}
15
+ transition={{ duration: 0.5 }}
16
+ >
17
+ {/* Tech Spinner */}
18
+ <div className="relative w-24 h-24">
19
+ <motion.div
20
+ className="absolute inset-0 border-4 border-red-600/30 rounded-full"
21
+ animate={{ scale: [1, 1.1, 1], opacity: [0.3, 0.6, 0.3] }}
22
+ transition={{ duration: 2, repeat: Infinity, ease: "easeInOut" }}
23
+ />
24
+ <motion.div
25
+ className="absolute inset-0 border-4 border-t-red-600 border-r-transparent border-b-transparent border-l-transparent rounded-full"
26
+ animate={{ rotate: 360 }}
27
+ transition={{ duration: 1, repeat: Infinity, ease: "linear" }}
28
+ />
29
+ <div className="absolute inset-4 border-2 border-white/10 rounded-full" />
30
+ <motion.div
31
+ className="absolute inset-[30%] bg-red-600 rounded-full"
32
+ animate={{ scale: [0.8, 1.2, 0.8], opacity: [0.8, 1, 0.8] }}
33
+ transition={{ duration: 1.5, repeat: Infinity }}
34
+ />
35
+ </div>
36
+
37
+ {/* Text */}
38
+ <div className="flex flex-col items-center gap-2">
39
+ <motion.span
40
+ className="font-jersey-10 text-2xl text-white tracking-[0.2em]"
41
+ animate={{ opacity: [1, 0.5, 1] }}
42
+ transition={{ duration: 1.5, repeat: Infinity }}
43
+ >
44
+ LOADING
45
+ </motion.span>
46
+ <div className="flex gap-1 h-1">
47
+ {[...Array(3)].map((_, i) => (
48
+ <motion.div
49
+ key={i}
50
+ className="w-8 h-full bg-red-600/50"
51
+ animate={{ scaleX: [0, 1, 0], opacity: [0, 1, 0] }}
52
+ transition={{ duration: 1, delay: i * 0.2, repeat: Infinity }}
53
+ />
54
+ ))}
55
+ </div>
56
+ </div>
57
+ </motion.div>
58
+ </div>
59
+ );
60
+ }
@@ -0,0 +1,68 @@
1
+ "use client";
2
+
3
+ import Link from 'next/link';
4
+ import { motion } from 'framer-motion';
5
+
6
+ export default function NotFound() {
7
+ return (
8
+ <div className="flex flex-col items-center justify-center min-h-screen text-center px-4 bg-black text-white relative overflow-hidden">
9
+ {/* Background Grid Effect */}
10
+ <div className="absolute inset-0 bg-[linear-gradient(rgba(255,255,255,0.02)_1px,transparent_1px),linear-gradient(90deg,rgba(255,255,255,0.02)_1px,transparent_1px)] bg-[size:40px_40px] [mask-image:radial-gradient(ellipse_60%_60%_at_50%_50%,black,transparent)] pointer-events-none" />
11
+
12
+ <div className="relative z-10">
13
+ <motion.div
14
+ initial={{ opacity: 0, scale: 0.8 }}
15
+ animate={{ opacity: 1, scale: 1 }}
16
+ transition={{ duration: 0.5 }}
17
+ className="relative"
18
+ >
19
+ <h1 className="text-[150px] md:text-[200px] font-jersey-10 font-bold text-red-600 leading-none select-none relative">
20
+ <span className="relative z-10">404</span>
21
+ <motion.span
22
+ className="absolute top-0 left-0 text-red-600/50 z-0"
23
+ animate={{ x: [-2, 2, -2], opacity: [0.5, 0.8, 0.5] }}
24
+ transition={{ duration: 0.1, repeat: Infinity, repeatType: "mirror" }}
25
+ >
26
+ 404
27
+ </motion.span>
28
+ <motion.span
29
+ className="absolute top-0 left-0 text-white/20 z-0"
30
+ animate={{ x: [2, -2, 2], opacity: [0.3, 0.6, 0.3] }}
31
+ transition={{ duration: 0.15, repeat: Infinity, repeatType: "mirror" }}
32
+ >
33
+ 404
34
+ </motion.span>
35
+ </h1>
36
+ </motion.div>
37
+
38
+ <motion.div
39
+ initial={{ opacity: 0, y: 20 }}
40
+ animate={{ opacity: 1, y: 0 }}
41
+ transition={{ delay: 0.2, duration: 0.5 }}
42
+ >
43
+ <div className="flex items-center justify-center gap-4 mb-6">
44
+ <div className="h-[1px] w-12 bg-red-600/50" />
45
+ <h2 className="text-xl md:text-2xl font-outfit font-bold tracking-widest uppercase text-white/80">
46
+ System Error
47
+ </h2>
48
+ <div className="h-[1px] w-12 bg-red-600/50" />
49
+ </div>
50
+
51
+ <p className="text-white/60 mb-10 max-w-md mx-auto font-roboto">
52
+ An error occurred
53
+ </p>
54
+
55
+ <Link
56
+ href="/"
57
+ className="group relative inline-flex items-center justify-center px-8 py-4 overflow-hidden font-bold text-white transition-all duration-300 bg-red-600 rounded-sm hover:bg-red-700 focus:outline-none focus:ring focus:ring-red-400 focus:ring-offset-2 focus:ring-offset-gray-900"
58
+ >
59
+ <span className="absolute inset-0 w-full h-full -mt-1 rounded-lg opacity-30 bg-gradient-to-b from-transparent via-transparent to-black"></span>
60
+ <span className="relative font-outfit tracking-wider">
61
+ Back to Home Page
62
+ </span>
63
+ </Link>
64
+ </motion.div>
65
+ </div>
66
+ </div>
67
+ );
68
+ }
@@ -0,0 +1,106 @@
1
+ import StatsGrid from "@/components/home/StatsGrid";
2
+ import { Github, Globe } from "lucide-react";
3
+
4
+ export default function Home() {
5
+ return (
6
+ <main className="min-h-screen bg-black text-white">
7
+ {/* Hero Section */}
8
+ <section className="relative py-24 px-6 md:px-12 lg:px-24 flex flex-col items-center text-center">
9
+ <div className="absolute inset-0 bg-gradient-to-b from-blue-900/20 to-transparent pointer-events-none" />
10
+
11
+ <h1 className="text-5xl md:text-7xl font-bold tracking-tight mb-6 bg-gradient-to-r from-white to-zinc-500 bg-clip-text text-transparent">
12
+ Next.js Enterprise Stack
13
+ </h1>
14
+
15
+ <p className="text-lg md:text-xl text-zinc-400 max-w-2xl mb-12">
16
+ A production-ready starter template featuring Next.js 15, Tailwind CSS 4, Supabase, and a fully integrated Admin Dashboard.
17
+ </p>
18
+
19
+ <div className="flex flex-col md:flex-row gap-4">
20
+ <a
21
+ href="https://github.com/burakz-cn/create-nextjs-stack"
22
+ target="_blank"
23
+ className="px-8 py-3 bg-white text-black font-semibold rounded-full hover:bg-zinc-200 transition"
24
+ >
25
+ Get Started
26
+ </a>
27
+ <a
28
+ href="#"
29
+ className="px-8 py-3 border border-zinc-700 text-white font-semibold rounded-full hover:bg-zinc-800 transition"
30
+ >
31
+ Documentation
32
+ </a>
33
+ </div>
34
+ </section>
35
+
36
+ {/* Stats Section */}
37
+ <section className="py-24 px-6 md:px-12 lg:px-24 bg-zinc-950/50 border-t border-zinc-900">
38
+ <div className="max-w-7xl mx-auto">
39
+ <div className="flex flex-col md:flex-row justify-between items-end mb-12">
40
+ <div>
41
+ <h2 className="text-3xl font-bold mb-2">Live Database Stats</h2>
42
+ <p className="text-zinc-500">Real-time data fetched from your Supabase instance.</p>
43
+ </div>
44
+ <div className="hidden md:block">
45
+ <span className="flex items-center text-sm text-green-500">
46
+ <span className="w-2 h-2 bg-green-500 rounded-full mr-2 animate-pulse" />
47
+ System Online
48
+ </span>
49
+ </div>
50
+ </div>
51
+
52
+ <StatsGrid />
53
+ </div>
54
+ </section>
55
+
56
+ {/* Features Grid (Static for demo) */}
57
+ <section className="py-24 px-6 md:px-12 lg:px-24">
58
+ <div className="max-w-7xl mx-auto grid grid-cols-1 md:grid-cols-3 gap-12">
59
+ <div className="space-y-4">
60
+ <h3 className="text-xl font-bold">Type Safe</h3>
61
+ <p className="text-zinc-400">End-to-end type safety with TypeScript, Zod, and generated Supabase types.</p>
62
+ </div>
63
+ <div className="space-y-4">
64
+ <h3 className="text-xl font-bold">Admin Panel</h3>
65
+ <p className="text-zinc-400">Fully functional admin dashboard with CRUD operations, auth, and resource management.</p>
66
+ </div>
67
+ <div className="space-y-4">
68
+ <h3 className="text-xl font-bold">Modern Stack</h3>
69
+ <p className="text-zinc-400">Built with the latest Next.js 15 App Router, React 19, and Tailwind CSS 4.</p>
70
+ </div>
71
+ </div>
72
+ </section>
73
+
74
+ {/* Footer / Credits */}
75
+ <footer className="py-12 px-6 md:px-12 lg:px-24 border-t border-zinc-900 bg-zinc-950">
76
+ <div className="max-w-7xl mx-auto flex flex-col md:flex-row justify-between items-center gap-6">
77
+ <p className="text-zinc-500 text-sm">
78
+ © {new Date().getFullYear()} Next.js Enterprise Stack. All rights reserved.
79
+ </p>
80
+
81
+ <div className="flex items-center gap-6">
82
+ <a
83
+ href="https://github.com/mburakaltiparmak"
84
+ target="_blank"
85
+ rel="noopener noreferrer"
86
+ className="group flex items-center gap-2 text-zinc-400 hover:text-white transition-colors"
87
+ >
88
+ <Github className="w-5 h-5" />
89
+ <span className="text-sm font-medium">mburakaltiparmak</span>
90
+ </a>
91
+
92
+ <a
93
+ href="https://burakaltiparmak.site"
94
+ target="_blank"
95
+ rel="noopener noreferrer"
96
+ className="group flex items-center gap-2 text-zinc-400 hover:text-white transition-colors"
97
+ >
98
+ <Globe className="w-5 h-5" />
99
+ <span className="text-sm font-medium">burakaltiparmak.site</span>
100
+ </a>
101
+ </div>
102
+ </div>
103
+ </footer>
104
+ </main>
105
+ );
106
+ }
@@ -0,0 +1,12 @@
1
+ import { MetadataRoute } from 'next';
2
+
3
+ export default function robots(): MetadataRoute.Robots {
4
+ return {
5
+ rules: {
6
+ userAgent: '*',
7
+ allow: '/',
8
+ disallow: ['/api/', '/_next/'],
9
+ },
10
+ sitemap: 'https://www.DOMAIN_PLACEHOLDER.com/sitemap.xml',
11
+ };
12
+ }
@@ -0,0 +1,66 @@
1
+ import { MetadataRoute } from 'next';
2
+ import { projectService } from '@/lib/services/projectService';
3
+ import { productService } from '@/lib/services/productService';
4
+
5
+ export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
6
+ const baseUrl = 'https://www.DOMAIN_PLACEHOLDER.com';
7
+
8
+ // Static pages - CUSTOMIZE THESE ROUTES FOR YOUR PROJECT
9
+ const routes = [
10
+ '',
11
+ '/about',
12
+ '/products',
13
+ '/innovation',
14
+ '/services',
15
+ '/investors',
16
+ '/references',
17
+ '/contact',
18
+ '/impressum',
19
+ '/datenschutz',
20
+ '/agb'
21
+ ];
22
+
23
+ const sitemap: MetadataRoute.Sitemap = [];
24
+
25
+ // Generate entries for each route
26
+ routes.forEach((route) => {
27
+ sitemap.push({
28
+ url: `${baseUrl}${route}`,
29
+ lastModified: new Date(),
30
+ changeFrequency: route === '' ? 'daily' : 'weekly',
31
+ priority: route === '' ? 1.0 : (route === '/contact' ? 0.9 : 0.8),
32
+ });
33
+ });
34
+
35
+ // Dynamic Projects
36
+ try {
37
+ const projectSlugs = await projectService.getAllSlugs();
38
+ projectSlugs.forEach((slug) => {
39
+ sitemap.push({
40
+ url: `${baseUrl}/references/${slug}`,
41
+ lastModified: new Date(),
42
+ changeFrequency: 'weekly',
43
+ priority: 0.7,
44
+ });
45
+ });
46
+ } catch (error) {
47
+ console.error('[Sitemap] Error fetching project slugs:', error);
48
+ }
49
+
50
+ // Dynamic Products
51
+ try {
52
+ const productSlugs = await productService.getAllSlugs();
53
+ productSlugs.forEach((slug) => {
54
+ sitemap.push({
55
+ url: `${baseUrl}/products/${slug}`,
56
+ lastModified: new Date(),
57
+ changeFrequency: 'weekly',
58
+ priority: 0.8,
59
+ });
60
+ });
61
+ } catch (error) {
62
+ console.error('[Sitemap] Error fetching product slugs:', error);
63
+ }
64
+
65
+ return sitemap;
66
+ }
@@ -0,0 +1,89 @@
1
+ import { CategoryService } from "@/lib/services/categories.service";
2
+ import { ProductService } from "@/lib/services/products.service";
3
+ import { ProjectService } from "@/lib/services/projects.service";
4
+ import { ClientService } from "@/lib/services/clients.service";
5
+ import { UserService } from "@/lib/services/users.service";
6
+ import {
7
+ Layers,
8
+ Package,
9
+ Briefcase,
10
+ Users,
11
+ Settings
12
+ } from "lucide-react";
13
+
14
+ export default async function StatsGrid() {
15
+ const [
16
+ categories,
17
+ products,
18
+ projects,
19
+ clients,
20
+ users
21
+ ] = await Promise.all([
22
+ CategoryService.getAll(),
23
+ ProductService.getAll(),
24
+ ProjectService.getAll(),
25
+ ClientService.getAll(),
26
+ UserService.getAll()
27
+ ]);
28
+
29
+ const stats = [
30
+ {
31
+ label: "Total Products",
32
+ value: products.length,
33
+ icon: Package,
34
+ color: "bg-blue-500",
35
+ },
36
+ {
37
+ label: "Total Categories",
38
+ value: categories.length,
39
+ icon: Layers,
40
+ color: "bg-purple-500",
41
+ },
42
+ {
43
+ label: "Total Projects",
44
+ value: projects.length,
45
+ icon: Briefcase,
46
+ color: "bg-indigo-500",
47
+ },
48
+ {
49
+ label: "Total Clients",
50
+ value: clients.length,
51
+ icon: Users,
52
+ color: "bg-pink-500",
53
+ },
54
+ {
55
+ label: "Registered Users",
56
+ value: users.length,
57
+ icon: Settings,
58
+ color: "bg-orange-500",
59
+ },
60
+ ];
61
+
62
+ return (
63
+ <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-5 gap-6">
64
+ {stats.map((stat) => {
65
+ const Icon = stat.icon;
66
+ return (
67
+ <div
68
+ key={stat.label}
69
+ className="bg-zinc-900 border border-zinc-800 rounded-xl p-6 transition hover:border-zinc-700 hover:bg-zinc-800/50"
70
+ >
71
+ <div className="flex items-center gap-4">
72
+ <div className={`p-3 rounded-lg ${stat.color} bg-opacity-10`}>
73
+ <Icon className={`w-6 h-6 ${stat.color.replace("bg-", "text-")}`} />
74
+ </div>
75
+ <div>
76
+ <p className="text-sm font-medium text-zinc-400">
77
+ {stat.label}
78
+ </p>
79
+ <p className="text-2xl font-bold text-white mt-1">
80
+ {stat.value}
81
+ </p>
82
+ </div>
83
+ </div>
84
+ </div>
85
+ );
86
+ })}
87
+ </div>
88
+ );
89
+ }
@@ -0,0 +1,39 @@
1
+ import { useState, useEffect, RefObject } from 'react';
2
+
3
+ interface UseIntersectionObserverOptions {
4
+ threshold?: number;
5
+ rootMargin?: string;
6
+ }
7
+
8
+ export const useIntersectionObserver = (
9
+ ref: RefObject<Element | null>, // Modified to accept null which is common with useRef
10
+ options: UseIntersectionObserverOptions = {}
11
+ ): boolean => {
12
+ const [isVisible, setIsVisible] = useState(false);
13
+
14
+ useEffect(() => {
15
+ const observer = new IntersectionObserver(
16
+ ([entry]) => {
17
+ if (entry.isIntersecting) {
18
+ setIsVisible(true);
19
+ observer.disconnect(); // Load once, then stop observing
20
+ }
21
+ },
22
+ {
23
+ threshold: options.threshold || 0,
24
+ rootMargin: options.rootMargin || '100px' // Default: load 100px before appearing
25
+ }
26
+ );
27
+
28
+ const currentRef = ref.current;
29
+ if (currentRef) {
30
+ observer.observe(currentRef);
31
+ }
32
+
33
+ return () => {
34
+ observer.disconnect();
35
+ };
36
+ }, [ref, options.threshold, options.rootMargin]);
37
+
38
+ return isVisible;
39
+ };
@@ -0,0 +1,12 @@
1
+ "use client";
2
+
3
+ import { Provider } from "react-redux";
4
+ import store from "@/store";
5
+
6
+ export default function StoreProvider({
7
+ children,
8
+ }: {
9
+ children: React.ReactNode;
10
+ }) {
11
+ return <Provider store={store}>{children}</Provider>;
12
+ }
@@ -0,0 +1,4 @@
1
+ // SEO Library Barrel Export
2
+ export * from './seo.types';
3
+ export * from './seo.config';
4
+ export * from './metadata';