@devvistatech/devvista-kit 0.0.12 → 0.0.13

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 (84) hide show
  1. package/README.md +40 -0
  2. package/app/ClientLayout.tsx +66 -0
  3. package/app/about/page.tsx +11 -248
  4. package/app/adRequest/page.tsx +101 -25
  5. package/app/admin-profile/page.tsx +123 -0
  6. package/app/analytics/page.tsx +41 -5
  7. package/app/api/about/route.ts +2 -18
  8. package/app/api/adRequest/route.ts +7 -27
  9. package/app/api/analytics/[reportType]/route.ts +1 -64
  10. package/app/api/bio/route.ts +1 -17
  11. package/app/api/blog/route.ts +1 -19
  12. package/app/api/contacts/route.ts +1 -46
  13. package/app/api/files/route.ts +1 -15
  14. package/app/api/gallery-data/route.ts +53 -61
  15. package/app/api/schedule/route.ts +5 -21
  16. package/app/api/signup/route.ts +129 -0
  17. package/app/api/sync-user/route.ts +268 -94
  18. package/app/api/verify-admin/route.ts +46 -0
  19. package/app/blog/[id]/page.tsx +71 -52
  20. package/app/blog/page.tsx +43 -10
  21. package/app/favicon.ico +0 -0
  22. package/app/gallery/page.tsx +27 -6
  23. package/app/layout.tsx +31 -82
  24. package/app/page.tsx +20 -311
  25. package/app/products/constants/product.ts +27 -0
  26. package/app/products/page.tsx +296 -0
  27. package/app/products/productOne/page.tsx +266 -0
  28. package/app/products/productTwo/page.tsx +272 -0
  29. package/app/schedule/page.tsx +78 -40
  30. package/bin/init.js +0 -12
  31. package/components/addOns/functional/ClassList.tsx +21 -17
  32. package/components/addOns/functional/ProductList.tsx +1027 -0
  33. package/components/addOns/functional/aboutSections/AboutSection.tsx +107 -70
  34. package/components/addOns/functional/aboutSections/constants/aboutSection.ts +9 -4
  35. package/components/addOns/functional/banner/Banner.tsx +150 -0
  36. package/components/addOns/functional/banner/BannerDashboard.tsx +283 -0
  37. package/components/addOns/functional/bioSections/BioEditor.tsx +471 -0
  38. package/components/addOns/functional/bioSections/constants/bioEditor.ts +36 -0
  39. package/components/addOns/functional/blogSections/BlogDashboard.tsx +1 -1
  40. package/components/addOns/functional/blogSections/BlogFormPopUp.tsx +2 -1
  41. package/components/addOns/functional/{ImageDescCarousel.tsx → carousels/ImageDescCarousel.tsx} +166 -57
  42. package/components/addOns/functional/carousels/ProductDescCarousel.tsx +1129 -0
  43. package/components/addOns/functional/{ScheduleCarousel.tsx → carousels/ScheduleCarousel.tsx} +110 -50
  44. package/components/addOns/functional/carousels/constants.ts/productDescCarousel.ts +197 -0
  45. package/components/addOns/functional/carousels/constants.ts/scheduleCarousel.ts +20 -0
  46. package/components/addOns/functional/contactsDashboard/ContactsDashboard.tsx +1 -1
  47. package/components/addOns/functional/fileUploaders/FileUploader.tsx +437 -0
  48. package/components/addOns/functional/fileUploaders/constants/fileUploader.ts +45 -0
  49. package/components/addOns/functional/galleries/GalleryComplex.tsx +468 -267
  50. package/components/addOns/functional/galleries/GallerySimple.tsx +78 -50
  51. package/components/addOns/functional/galleries/ThreeSetGallery.tsx +260 -0
  52. package/components/addOns/functional/schedules/ScheduleGridOne.tsx +22 -8
  53. package/components/addOns/functional/schedules/ScheduleGridTwo.tsx +12 -7
  54. package/components/addOns/functional/schedules/ScheduleGridTwoBasic.tsx +12 -7
  55. package/components/addOns/non-functional/SampleCarousel.tsx +3 -3
  56. package/components/addOns/non-functional/ThreeSetGallery.tsx +3 -3
  57. package/components/addOns/non-functional/featureSections/FeaturesSection.tsx +74 -0
  58. package/components/addOns/non-functional/featureSections/constants/featuresSection.ts +30 -0
  59. package/components/addOns/non-functional/{Heros/HeroSection.tsx → heros/HomeHero.tsx} +17 -15
  60. package/components/addOns/non-functional/heros/ProductHero.tsx +111 -0
  61. package/components/addOns/non-functional/heros/constants/hero.ts +62 -0
  62. package/components/addOns/non-functional/imageCarousels/ProductSlider.tsx +6 -6
  63. package/components/addOns/non-functional/imageCarousels/ProgramCarousel.tsx +10 -10
  64. package/components/footers/footer.tsx +161 -198
  65. package/components/other/admin-menu.tsx +1 -1
  66. package/lib/auth/auth-context.tsx +225 -0
  67. package/lib/auth/auth-utils.tsx +30 -0
  68. package/lib/constants/adRequest.ts +199 -56
  69. package/lib/constants/admin-profile.ts +12 -0
  70. package/lib/constants/page.ts +15 -15
  71. package/lib/google/google-analytics-tracking.tsx +44 -0
  72. package/lib/types.ts +235 -0
  73. package/lib/utils/compressImage.tsx +32 -0
  74. package/middleware.ts +9 -5
  75. package/next.config.js +1 -1
  76. package/package.json +3 -2
  77. package/public/images/test.png +0 -0
  78. package/components/addOns/functional/BioEditor.tsx +0 -447
  79. package/components/addOns/functional/FileUploader.tsx +0 -295
  80. package/components/addOns/non-functional/FeaturesSection.tsx +0 -63
  81. package/components/types.ts +0 -50
  82. package/lib/auth-context.tsx +0 -131
  83. package/lib/verify-user.ts +0 -118
  84. /package/lib/{google-analytics.tsx → google/google-analytics.tsx} +0 -0
@@ -1,15 +1,16 @@
1
- 'use client';
1
+ "use client";
2
2
 
3
- import React, { useState, useEffect } from 'react';
4
- import { useRouter } from 'next/navigation';
5
- import { Card, CardContent } from '@/components/other/card';
6
- import { BackButton, EditToggleButton, UpdateButton } from '@/components/other/button';
7
- import { ArrowLeft, Calendar } from 'lucide-react';
8
- import { useAuth, useUser } from '@clerk/nextjs';
9
- import { useStrapiAuth } from '@/lib/auth-context';
10
- import BlogSidebar from '@/components/addOns/functional/blogSections/BlogSidebar';
11
- import ClassList from '@/components/addOns/functional/ClassList';
12
- import Spinner from '@/components/addOns/non-functional/spinner';
3
+ import React, { useState, useEffect } from "react";
4
+ import { useRouter } from "next/navigation";
5
+ import { Card, CardContent } from "@/components/other/card";
6
+ import { BackButton, EditToggleButton, UpdateButton } from "@/components/other/button";
7
+ import { ArrowLeft, Calendar } from "lucide-react";
8
+ import { useAuth, useUser } from "@clerk/nextjs";
9
+ import { useStrapiAuth } from "@/lib/auth/auth-context";
10
+ import BlogSidebar from "@/components/addOns/functional/blogSections/BlogSidebar";
11
+ import ClassList from "@/components/addOns/functional/ClassList";
12
+ import Spinner from "@/components/addOns/non-functional/spinner";
13
+ import { isAdminUser } from "@/lib/auth/auth-utils";
13
14
 
14
15
  interface BlogPost {
15
16
  id: number;
@@ -25,8 +26,8 @@ interface BlogPostPageProps {
25
26
  }
26
27
 
27
28
  export default function BlogPostPage({ params }: BlogPostPageProps) {
28
- const resolvedParams = React.use(params);
29
- const id = resolvedParams.id;
29
+ const resolvedParams = React.use(params);
30
+ const id = resolvedParams.id;
30
31
  const router = useRouter();
31
32
  const { user, authLoading, checkSession } = useStrapiAuth();
32
33
  const { getToken, isSignedIn } = useAuth();
@@ -36,23 +37,41 @@ export default function BlogPostPage({ params }: BlogPostPageProps) {
36
37
  const [isLoading, setIsLoading] = useState(true);
37
38
  const [error, setError] = useState<string | null>(null);
38
39
  const [isEditing, setIsEditing] = useState(false);
39
- const [editTitle, setEditTitle] = useState('');
40
- const [editDescription, setEditDescription] = useState('');
40
+ const [editTitle, setEditTitle] = useState("");
41
+ const [editDescription, setEditDescription] = useState("");
41
42
  const [titleCharCount, setTitleCharCount] = useState(0);
42
43
  const [titleWordCount, setTitleWordCount] = useState(0);
43
44
  const [descriptionCharCount, setDescriptionCharCount] = useState(0);
44
45
  const [descriptionWordCount, setDescriptionWordCount] = useState(0);
46
+ const [isAdmin, setIsAdmin] = useState(false);
45
47
 
46
- const isAdmin = isSignedIn && !!user?.businessAdminId;
48
+ useEffect(() => {
49
+ let isMounted = true;
50
+ const checkAdmin = async () => {
51
+ if (!isSignedIn || !user?.authId) {
52
+ if (isMounted) setIsAdmin(false);
53
+ return;
54
+ }
55
+
56
+ const adminStatus = await isAdminUser(isSignedIn, user);
57
+ if (isMounted) {
58
+ setIsAdmin(adminStatus);
59
+ }
60
+ };
61
+ checkAdmin();
62
+ return () => {
63
+ isMounted = false;
64
+ };
65
+ }, [isSignedIn, user]);
47
66
 
48
67
  useEffect(() => {
49
68
  const handleLogin = () => checkSession();
50
69
  const handleLogout = () => checkSession();
51
- window.addEventListener('user-login', handleLogin);
52
- window.addEventListener('user-logout', handleLogout);
70
+ window.addEventListener("user-login", handleLogin);
71
+ window.addEventListener("user-logout", handleLogout);
53
72
  return () => {
54
- window.removeEventListener('user-login', handleLogin);
55
- window.removeEventListener('user-logout', handleLogout);
73
+ window.removeEventListener("user-login", handleLogin);
74
+ window.removeEventListener("user-logout", handleLogout);
56
75
  };
57
76
  }, [checkSession]);
58
77
 
@@ -60,34 +79,34 @@ export default function BlogPostPage({ params }: BlogPostPageProps) {
60
79
  const fetchPost = async () => {
61
80
  setIsLoading(true);
62
81
  try {
63
- const headers: HeadersInit = { 'Content-Type': 'application/json' };
82
+ const headers: HeadersInit = { "Content-Type": "application/json" };
64
83
  if (isSignedIn) {
65
84
  const token = await getToken();
66
- if (token) headers['Authorization'] = `Bearer ${token}`;
85
+ if (token) headers["Authorization"] = `Bearer ${token}`;
67
86
  }
68
87
 
69
- const response = await fetch('/api/blog', { headers, cache: 'no-store' });
88
+ const response = await fetch("/api/blog", { headers, cache: "no-store" });
70
89
  if (!response.ok) {
71
90
  const errorData = await response.json();
72
- throw new Error(errorData.error || 'Failed to fetch blog posts');
91
+ throw new Error(errorData.error || "Failed to fetch blog posts");
73
92
  }
74
93
  const { data } = await response.json();
75
94
  const blogPosts = data.blogPosts.map((p: BlogPost) => ({
76
95
  ...p,
77
- title: p.title.length > 50 ? p.title.substring(0, 50) + '...' : p.title,
78
- description: p.description.length > 1500 ? p.description.substring(0, 1500) + '...' : p.description,
96
+ title: p.title.length > 50 ? p.title.substring(0, 50) + "..." : p.title,
97
+ description: p.description.length > 1500 ? p.description.substring(0, 1500) + "..." : p.description,
79
98
  publishedAt: new Date(p.publishedAt).toISOString(),
80
99
  }));
81
100
 
82
101
  const parsedId = parseInt(id);
83
102
  if (isNaN(parsedId)) {
84
- router.push('/404');
103
+ router.push("/404");
85
104
  return;
86
105
  }
87
106
 
88
107
  const foundPost = blogPosts.find((p: BlogPost) => p.id === parsedId);
89
108
  if (!foundPost) {
90
- router.push('/404');
109
+ router.push("/404");
91
110
  return;
92
111
  }
93
112
 
@@ -100,7 +119,7 @@ export default function BlogPostPage({ params }: BlogPostPageProps) {
100
119
  setDescriptionCharCount(foundPost.description.length);
101
120
  setDescriptionWordCount(foundPost.description.trim().split(/\s+/).filter(Boolean).length);
102
121
  } catch (err) {
103
- router.push('/404');
122
+ router.push("/404");
104
123
  } finally {
105
124
  setIsLoading(false);
106
125
  }
@@ -126,16 +145,16 @@ export default function BlogPostPage({ params }: BlogPostPageProps) {
126
145
  try {
127
146
  const token = await getToken();
128
147
  if (!token) {
129
- throw new Error('Unauthorized: No token provided');
148
+ throw new Error("Unauthorized: No token provided");
130
149
  }
131
150
  if (!post?.documentId) {
132
- throw new Error('Missing documentId for update');
151
+ throw new Error("Missing documentId for update");
133
152
  }
134
153
 
135
- const response = await fetch('/api/blog', {
136
- method: 'PUT',
154
+ const response = await fetch("/api/blog", {
155
+ method: "PUT",
137
156
  headers: {
138
- 'Content-Type': 'application/json',
157
+ "Content-Type": "application/json",
139
158
  Authorization: `Bearer ${token}`,
140
159
  },
141
160
  body: JSON.stringify({
@@ -151,36 +170,36 @@ export default function BlogPostPage({ params }: BlogPostPageProps) {
151
170
 
152
171
  if (!response.ok) {
153
172
  const errorData = await response.json();
154
- throw new Error(errorData.error || 'Failed to update post');
173
+ throw new Error(errorData.error || "Failed to update post");
155
174
  }
156
175
 
157
- const fetchResponse = await fetch('/api/blog', {
176
+ const fetchResponse = await fetch("/api/blog", {
158
177
  headers: {
159
- 'Content-Type': 'application/json',
178
+ "Content-Type": "application/json",
160
179
  Authorization: `Bearer ${token}`,
161
180
  },
162
- cache: 'no-store',
181
+ cache: "no-store",
163
182
  });
164
183
  if (!fetchResponse.ok) {
165
184
  const errorData = await fetchResponse.json();
166
- throw new Error(errorData.error || 'Failed to fetch blog posts');
185
+ throw new Error(errorData.error || "Failed to fetch blog posts");
167
186
  }
168
187
  const { data } = await fetchResponse.json();
169
188
  const blogPosts = data.blogPosts.map((p: BlogPost) => ({
170
189
  ...p,
171
- title: p.title.length > 50 ? p.title.substring(0, 50) + '...' : p.title,
172
- description: p.description.length > 1500 ? p.description.substring(0, 1500) + '...' : p.description,
190
+ title: p.title.length > 50 ? p.title.substring(0, 50) + "..." : p.title,
191
+ description: p.description.length > 1500 ? p.description.substring(0, 1500) + "..." : p.description,
173
192
  publishedAt: new Date(p.publishedAt).toISOString(),
174
193
  }));
175
194
  const updatedPost = blogPosts.find((p: BlogPost) => p.documentId === post.documentId);
176
195
  if (!updatedPost) {
177
- throw new Error('Updated post not found');
196
+ throw new Error("Updated post not found");
178
197
  }
179
198
  setPost(updatedPost);
180
199
  setPosts(blogPosts);
181
200
  setIsEditing(false);
182
201
  } catch (err) {
183
- setError(err instanceof Error ? err.message : 'An unknown error occurred');
202
+ setError(err instanceof Error ? err.message : "An unknown error occurred");
184
203
  }
185
204
  };
186
205
 
@@ -195,7 +214,7 @@ export default function BlogPostPage({ params }: BlogPostPageProps) {
195
214
  }
196
215
 
197
216
  if (!post) {
198
- router.push('/404');
217
+ router.push("/404");
199
218
  return null;
200
219
  }
201
220
 
@@ -204,11 +223,11 @@ export default function BlogPostPage({ params }: BlogPostPageProps) {
204
223
  <div className="max-w-7xl mx-auto">
205
224
  {isAdmin && (
206
225
  <div className="mb-4 flex gap-4">
207
- <BackButton onClick={() => router.push('/blog')}>
226
+ <BackButton onClick={() => router.push("/blog")}>
208
227
  <ArrowLeft size={16} /> Blog Dashboard
209
228
  </BackButton>
210
229
  <EditToggleButton onClick={() => setIsEditing(!isEditing)}>
211
- {isEditing ? 'Cancel' : 'Edit Post'}
230
+ {isEditing ? "Cancel" : "Edit Post"}
212
231
  </EditToggleButton>
213
232
  </div>
214
233
  )}
@@ -259,16 +278,16 @@ export default function BlogPostPage({ params }: BlogPostPageProps) {
259
278
  <div className="flex items-center gap-2 text-sm text-gray-400 mb-4">
260
279
  <Calendar className="w-5 h-5 text-blue-400" />
261
280
  <span className="text-blue-400/90">
262
- By {post.author} |{' '}
263
- {new Date(post.publishedAt).toLocaleDateString('en-US', {
264
- year: 'numeric',
265
- month: 'long',
266
- day: 'numeric',
281
+ By {post.author} |{" "}
282
+ {new Date(post.publishedAt).toLocaleDateString("en-US", {
283
+ year: "numeric",
284
+ month: "long",
285
+ day: "numeric",
267
286
  })}
268
287
  </span>
269
288
  </div>
270
289
  <div className="text-black/90 leading-relaxed break-words">
271
- {post.description.split('\n').map((paragraph, index) => (
290
+ {post.description.split("\n").map((paragraph, index) => (
272
291
  <p key={index} className="mb-4 last:mb-0">
273
292
  {paragraph}
274
293
  </p>
package/app/blog/page.tsx CHANGED
@@ -1,12 +1,10 @@
1
1
  "use client";
2
2
 
3
3
  import { useState, useEffect } from "react";
4
- import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/other/card";
5
- import { Button } from "@/components/other/button";
6
4
  import { AlertCircle } from "lucide-react";
7
- import { Alert, AlertDescription } from "@/components/other/alert";
8
5
  import { useAuth, useUser } from "@clerk/nextjs";
9
- import { useStrapiAuth } from "@/lib/auth-context";
6
+ import { useStrapiAuth } from "@/lib/auth/auth-context";
7
+ import { isAdminUser } from "@/lib/auth/auth-utils";
10
8
  import Spinner from "@/components/addOns/non-functional/spinner";
11
9
  import BlogDashboard from "@/components/addOns/functional/blogSections/BlogDashboard";
12
10
  import BlogFormPopUp from "@/components/addOns/functional/blogSections/BlogFormPopUp";
@@ -36,11 +34,10 @@ export default function BlogPage() {
36
34
  documentId: "",
37
35
  title: "",
38
36
  description: "",
39
- author: user?.firstName && user?.lastName ? `${user.firstName} ${user.lastName}` : "",
37
+ author: user?.username || "",
40
38
  publishedAt: "",
41
39
  });
42
-
43
- const isAdmin = isSignedIn && !!user?.businessAdminId;
40
+ const [isAdmin, setIsAdmin] = useState(false);
44
41
 
45
42
  const timeoutPromise = (promise: Promise<any>, timeout: number) => {
46
43
  return Promise.race([
@@ -65,13 +62,49 @@ export default function BlogPage() {
65
62
  }, [checkSession]);
66
63
 
67
64
  useEffect(() => {
68
- if (isModalOpen && !formData.id && user?.firstName && user?.lastName) {
65
+ if (isModalOpen && !formData.id && user?.username) {
69
66
  setFormData({
70
67
  ...formData,
71
- author: `${user.firstName} ${user.lastName}`,
68
+ author: user.username,
72
69
  });
73
70
  }
74
- }, [isModalOpen, formData.id, user?.firstName, user?.lastName]);
71
+ }, [isModalOpen, formData.id, user?.username]);
72
+
73
+ useEffect(() => {
74
+ let isMounted = true;
75
+ const checkAdmin = async (retries = 3, delay = 1000) => {
76
+ if (!isSignedIn || !user?.authId) {
77
+ if (isMounted) setIsAdmin(false);
78
+ return;
79
+ }
80
+ for (let attempt = 1; attempt <= retries; attempt++) {
81
+ try {
82
+ const adminStatus = await isAdminUser(isSignedIn, user);
83
+ if (isMounted) {
84
+ setIsAdmin(adminStatus);
85
+ setError(null);
86
+ }
87
+ return;
88
+ } catch (error) {
89
+ console.error("BlogPage: Admin check failed:", {
90
+ error: error instanceof Error ? error.message : "Unknown error",
91
+ attempt,
92
+ timestamp: new Date().toISOString(),
93
+ });
94
+ if (attempt < retries) {
95
+ await new Promise(resolve => setTimeout(resolve, delay));
96
+ } else if (isMounted) {
97
+ setIsAdmin(false);
98
+ setError("Failed to verify admin status");
99
+ }
100
+ }
101
+ }
102
+ };
103
+ checkAdmin();
104
+ return () => {
105
+ isMounted = false;
106
+ };
107
+ }, [isSignedIn, user]);
75
108
 
76
109
  const fetchData = async () => {
77
110
  if (!isSignedIn || !clerkUser?.id) {
package/app/favicon.ico CHANGED
Binary file
@@ -1,15 +1,14 @@
1
- // src/pages/gallery.tsx
2
1
  "use client";
3
2
 
4
3
  import { useState, useEffect, useCallback } from "react";
5
- import { ImageDescCarousel } from "@/components/addOns/functional/ImageDescCarousel";
4
+ import { ImageDescCarousel } from "@/components/addOns/functional/carousels/ImageDescCarousel";
6
5
  import { GallerySection } from "@/components/addOns/functional/galleries/GalleryComplex";
7
6
  import Spinner from "@/components/addOns/non-functional/spinner";
8
7
  import { motion } from "framer-motion";
9
- import { useStrapiAuth } from "@/lib/auth-context";
8
+ import { useStrapiAuth } from "@/lib/auth/auth-context";
10
9
  import { useAuth } from "@clerk/nextjs";
11
10
  import { GALLERY_PAGE } from "../../lib/constants/gallery";
12
- import { StrapiUser, UploadedImage, Category } from "@/components/types";
11
+ import { StrapiUser, UploadedImage, Category } from "@/lib/types";
13
12
 
14
13
  export default function GalleryPage() {
15
14
  const { user, authLoading, checkSession } = useStrapiAuth();
@@ -64,11 +63,26 @@ export default function GalleryPage() {
64
63
  }
65
64
  const { data: images, meta } = await imagesResponse.json();
66
65
 
66
+ const mappedImages: UploadedImage[] = images.map((item: any) => ({
67
+ id: Number(item.id) || 0,
68
+ documentId: item.documentId || "",
69
+ title: item.title || "",
70
+ description: item.description || "",
71
+ url: item.url || "",
72
+ createdAt: item.createdAt || new Date().toISOString(),
73
+ category: item.category || "none",
74
+ subCategory: item.subCategory || "",
75
+ favorite: item.favorite ?? false,
76
+ banner: item.banner ?? false,
77
+ startDate: item.startDate || undefined,
78
+ endDate: item.endDate || undefined,
79
+ }));
80
+
67
81
  if (meta?.pagination?.total > 100) {
68
82
  setError(GALLERY_PAGE.ERRORS.PAGINATION_WARNING);
69
83
  }
70
84
 
71
- setUploadedImages(images || []);
85
+ setUploadedImages(mappedImages || []);
72
86
  } catch (err) {
73
87
  console.error("GalleryPage: Fetch error", err);
74
88
  setError(err instanceof Error ? err.message : GALLERY_PAGE.ERRORS.FETCH_ERROR);
@@ -86,7 +100,9 @@ export default function GalleryPage() {
86
100
  file: File | null,
87
101
  title: string,
88
102
  description: string,
89
- category: Category
103
+ category: Category,
104
+ subCategory: string,
105
+ favorite: boolean
90
106
  ) => {
91
107
  e.preventDefault();
92
108
  if (!file) {
@@ -119,6 +135,11 @@ export default function GalleryPage() {
119
135
  formData.append("title", title || `Image ${new Date().toISOString()}`);
120
136
  formData.append("description", description || "");
121
137
  formData.append("category", category);
138
+ formData.append("subCategory", subCategory || "");
139
+ formData.append("favorite", String(favorite));
140
+ formData.append("banner", String(false));
141
+ formData.append("startDate", "");
142
+ formData.append("endDate", "");
122
143
 
123
144
  const response = await fetch("/api/gallery-data", {
124
145
  method: "POST",
package/app/layout.tsx CHANGED
@@ -1,13 +1,10 @@
1
+ // app/layout.tsx for devvistakit
1
2
  import './globals.css';
2
3
  import type { Metadata } from 'next';
3
4
  import { Inter } from 'next/font/google';
4
5
  import { ClerkProvider } from '@clerk/nextjs';
5
- import { ThemeProvider } from '@/components/theme-provider';
6
- import { AuthProvider } from '@/lib/auth-context';
7
- import Navbar from '@/components/navBars/navbar';
8
- import { Toaster } from '@/components/other/toaster';
9
- import Footer from '@/components/footers/footer';
10
- import IconBubble from '@/components/addOns/non-functional/IconBubble';
6
+ import GoogleAnalyticsTracking from '@/lib/google/google-analytics-tracking';
7
+ import ClientLayout from './ClientLayout';
11
8
 
12
9
  const inter = Inter({ subsets: ['latin'], preload: true });
13
10
 
@@ -17,8 +14,8 @@ export const metadata: Metadata = {
17
14
  description: 'Your kit To a new website',
18
15
  openGraph: {
19
16
  title: 'DevVista Kit',
20
- description: '',
21
- url: 'https://devvistatech.com/',
17
+ description: 'Your kit To a new website',
18
+ url: 'https://devvistakit.devvistatech.com/',
22
19
  images: [{ url: '/images/test.png' }],
23
20
  },
24
21
  };
@@ -26,86 +23,38 @@ export const metadata: Metadata = {
26
23
  export default function RootLayout({ children }: { children: React.ReactNode }) {
27
24
  const clerkKey = process.env.NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY;
28
25
 
29
- // If clerkKey is not defined, log a warning and render without ClerkProvider
26
+ const HtmlShell = (
27
+ <html lang="en" className="h-full" suppressHydrationWarning>
28
+ <head>
29
+ <GoogleAnalyticsTracking />
30
+ </head>
31
+ <body className={`${inter.className} h-full bg-background text-foreground antialiased`}>
32
+ <ClientLayout>{children}</ClientLayout>
33
+ </body>
34
+ </html>
35
+ );
36
+
30
37
  if (!clerkKey) {
31
38
  console.warn(
32
- 'NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY is not defined. Clerk authentication will be disabled. ' +
33
- 'Please set this environment variable in a .env.local file or your deployment environment to enable Clerk functionality.'
34
- );
35
- return (
36
- <html lang="en" className="h-full" suppressHydrationWarning>
37
- <head>
38
- {/* Conditionally load Google Analytics script if NEXT_PUBLIC_GA_MEASUREMENT_ID is defined */}
39
- {process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID && (
40
- <script async src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID}`}></script>
41
- )}
42
- <script
43
- dangerouslySetInnerHTML={{
44
- __html: `
45
- window.dataLayer = window.dataLayer || [];
46
- function gtag(){dataLayer.push(arguments);}
47
- gtag('js', new Date());
48
- ${process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID ? `gtag('config', '${process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID}');` : ''}
49
- `,
50
- }}
51
- />
52
- </head>
53
- <body className={`${inter.className} h-full bg-gray-900 text-gray-700 antialiased`}>
54
- <ThemeProvider
55
- attribute="class"
56
- defaultTheme="dark"
57
- enableSystem={false}
58
- disableTransitionOnChange
59
- >
60
- <AuthProvider>
61
- <Navbar />
62
- <main className="min-h-screen w-full">{children}</main>
63
- <Toaster />
64
- <Footer />
65
- <IconBubble />
66
- </AuthProvider>
67
- </ThemeProvider>
68
- </body>
69
- </html>
39
+ 'NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY is not defined. Clerk authentication will be disabled.'
70
40
  );
41
+ return HtmlShell;
71
42
  }
72
43
 
73
- // If clerkKey is defined, render with ClerkProvider
74
44
  return (
75
- <ClerkProvider publishableKey={clerkKey}>
76
- <html lang="en" className="h-full" suppressHydrationWarning>
77
- <head>
78
- {process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID && (
79
- <script async src={`https://www.googletagmanager.com/gtag/js?id=${process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID}`}></script>
80
- )}
81
- <script
82
- dangerouslySetInnerHTML={{
83
- __html: `
84
- window.dataLayer = window.dataLayer || [];
85
- function gtag(){dataLayer.push(arguments);}
86
- gtag('js', new Date());
87
- ${process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID ? `gtag('config', '${process.env.NEXT_PUBLIC_GA_MEASUREMENT_ID}');` : ''}
88
- `,
89
- }}
90
- />
91
- </head>
92
- <body className={`${inter.className} h-full bg-gray-900 text-gray-700 antialiased`}>
93
- <ThemeProvider
94
- attribute="class"
95
- defaultTheme="dark"
96
- enableSystem={false}
97
- disableTransitionOnChange
98
- >
99
- <AuthProvider>
100
- <Navbar />
101
- <main className="min-h-screen w-full">{children}</main>
102
- <Toaster />
103
- <Footer />
104
- <IconBubble />
105
- </AuthProvider>
106
- </ThemeProvider>
107
- </body>
108
- </html>
45
+ <ClerkProvider
46
+ publishableKey={clerkKey}
47
+ appearance={{
48
+ baseTheme: undefined,
49
+ variables: { colorPrimary: '#3498db' },
50
+ }}
51
+ allowedRedirectOrigins={[
52
+ 'https://devvistakit.devvistatech.com',
53
+ 'https://devvista-kit.netlify.app',
54
+ 'http://localhost:3000',
55
+ ]}
56
+ >
57
+ {HtmlShell}
109
58
  </ClerkProvider>
110
59
  );
111
60
  }