@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,4 +1,3 @@
1
- // src/components/addOns/functional/ImageDescCarousel.tsx
2
1
  "use client";
3
2
 
4
3
  import { useState, useEffect, useRef } from "react";
@@ -19,25 +18,8 @@ import Image from "next/image";
19
18
  import { Upload, ChevronRight, X, ChevronLeft } from "lucide-react";
20
19
  import { motion, useScroll, useTransform } from "framer-motion";
21
20
  import { useAuth, useUser } from "@clerk/nextjs";
22
- import { StrapiUser, UploadedImage, Category } from "@/components/types";
23
-
24
- interface ImageDescCarouselProps {
25
- user: StrapiUser | null;
26
- uploadedImages: UploadedImage[];
27
- setUploadedImages: (images: UploadedImage[]) => void;
28
- error: string | null;
29
- setError: (error: string | null) => void;
30
- isLoading: boolean;
31
- setIsLoading: (isLoading: boolean) => void;
32
- handleImageUpload: (
33
- e: React.FormEvent<HTMLFormElement>,
34
- file: File | null,
35
- title: string,
36
- description: string,
37
- category: Category
38
- ) => Promise<void>;
39
- handleDeleteImage: (documentId: string) => Promise<void>;
40
- }
21
+ import { isAdminUser } from "@/lib/auth/auth-utils";
22
+ import { StrapiUser, UploadedImage, Category, ImageDescCarouselProps, UploadFormState, EditFormState } from "@/lib/types";
41
23
 
42
24
  const Slideshow = ({
43
25
  images,
@@ -98,7 +80,7 @@ const Slideshow = ({
98
80
  quality={85}
99
81
  />
100
82
  {isAdmin && (
101
- <div className="absolute top-2 right-2 flex space-x-2 opacity-0 hover:opacity-100 transition-opacity duration-500">
83
+ <div className="absolute top-2 right-2 flex space-x-2 opacity-100">
102
84
  <EditIconButton
103
85
  onClick={(e) => {
104
86
  e.stopPropagation();
@@ -147,6 +129,7 @@ export function ImageDescCarousel({
147
129
  }: ImageDescCarouselProps) {
148
130
  const { isSignedIn } = useUser();
149
131
  const { getToken } = useAuth();
132
+ const [isAdmin, setIsAdmin] = useState(false);
150
133
  const [activeTab, setActiveTab] = useState<Category>("indoor");
151
134
  const [currentSlide, setCurrentSlide] = useState(0);
152
135
  const [isUploadModalOpen, setIsUploadModalOpen] = useState(false);
@@ -154,20 +137,30 @@ export function ImageDescCarousel({
154
137
  const [isConfirmDeleteOpen, setIsConfirmDeleteOpen] = useState(false);
155
138
  const [documentIdToDelete, setDocumentIdToDelete] = useState<string | null>(null);
156
139
  const [selectedImage, setSelectedImage] = useState<UploadedImage | null>(null);
157
- const [uploadForm, setUploadForm] = useState<{
158
- file: File | null;
159
- title: string;
160
- description: string;
161
- category: Category;
162
- }>({ file: null, title: "", description: "", category: "indoor" });
163
- const [editForm, setEditForm] = useState<{
164
- id: number;
165
- documentId: string;
166
- title: string;
167
- description: string;
168
- category: Category;
169
- file: File | null;
170
- }>({ id: 0, documentId: "", title: "", description: "", category: "indoor", file: null });
140
+ const [uploadForm, setUploadForm] = useState<UploadFormState>({
141
+ file: null,
142
+ title: "",
143
+ description: "",
144
+ category: "indoor",
145
+ subCategory: "",
146
+ favorite: false,
147
+ banner: false,
148
+ startDate: "",
149
+ endDate: "",
150
+ });
151
+ const [editForm, setEditForm] = useState<EditFormState>({
152
+ id: 0,
153
+ documentId: "",
154
+ file: null,
155
+ title: "",
156
+ description: "",
157
+ category: "indoor",
158
+ subCategory: "",
159
+ favorite: false,
160
+ banner: false,
161
+ startDate: "",
162
+ endDate: "",
163
+ });
171
164
  const [isSubmitting, setIsSubmitting] = useState(false);
172
165
 
173
166
  const containerRef = useRef<HTMLDivElement>(null);
@@ -177,7 +170,23 @@ export function ImageDescCarousel({
177
170
  });
178
171
  const parallaxY = useTransform(scrollYProgress, [0, 1], [0, -50]);
179
172
 
180
- const isAdmin = isSignedIn && !!user?.businessAdminId || false;
173
+ useEffect(() => {
174
+ let isMounted = true;
175
+ const checkAdmin = async () => {
176
+ if (!isSignedIn || !user?.authId) {
177
+ if (isMounted) setIsAdmin(false);
178
+ return;
179
+ }
180
+ const adminStatus = await isAdminUser(isSignedIn, user);
181
+ if (isMounted) {
182
+ setIsAdmin(adminStatus);
183
+ }
184
+ };
185
+ checkAdmin();
186
+ return () => {
187
+ isMounted = false;
188
+ };
189
+ }, [isSignedIn, user]);
181
190
 
182
191
  useEffect(() => {
183
192
  const isAnyModalOpen = isUploadModalOpen || isEditModalOpen || isConfirmDeleteOpen || !!selectedImage;
@@ -215,22 +224,49 @@ export function ImageDescCarousel({
215
224
  setEditForm({
216
225
  id: image.id,
217
226
  documentId: image.documentId,
227
+ file: null,
218
228
  title: image.title?.trim() && !image.title.match(/Image \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z/) ? image.title : "",
219
229
  description: image.description || "",
220
230
  category: image.category && image.category !== "none" ? image.category : "indoor",
221
- file: null,
231
+ subCategory: image.subCategory || "",
232
+ favorite: image.favorite || false,
233
+ banner: image.banner || false,
234
+ startDate: image.startDate || "",
235
+ endDate: image.endDate || "",
222
236
  });
223
237
  setIsEditModalOpen(true);
224
238
  };
225
239
 
226
240
  const handleCloseEditModal = () => {
227
241
  setIsEditModalOpen(false);
228
- setEditForm({ id: 0, documentId: "", title: "", description: "", category: "indoor", file: null });
242
+ setEditForm({
243
+ id: 0,
244
+ documentId: "",
245
+ file: null,
246
+ title: "",
247
+ description: "",
248
+ category: "indoor",
249
+ subCategory: "",
250
+ favorite: false,
251
+ banner: false,
252
+ startDate: "",
253
+ endDate: "",
254
+ });
229
255
  };
230
256
 
231
257
  const handleCloseUploadModal = () => {
232
258
  setIsUploadModalOpen(false);
233
- setUploadForm({ file: null, title: "", description: "", category: "indoor" });
259
+ setUploadForm({
260
+ file: null,
261
+ title: "",
262
+ description: "",
263
+ category: "indoor",
264
+ subCategory: "",
265
+ favorite: false,
266
+ banner: false,
267
+ startDate: "",
268
+ endDate: "",
269
+ });
234
270
  };
235
271
 
236
272
  const openConfirmDelete = (documentId: string) => {
@@ -270,7 +306,11 @@ export function ImageDescCarousel({
270
306
  if (!isAdmin) {
271
307
  console.error("ImageDescCarousel: Unauthorized edit attempt", {
272
308
  isSignedIn,
309
+ authId: user?.authId,
273
310
  businessAdminId: user?.businessAdminId,
311
+ userRole: user?.userRole,
312
+ businessOwner: user?.businessOwner ?? null,
313
+ timestamp: new Date().toISOString(),
274
314
  });
275
315
  setError("Unauthorized: Only admins can edit images");
276
316
  return;
@@ -281,7 +321,9 @@ export function ImageDescCarousel({
281
321
  setIsSubmitting(true);
282
322
  const token = await getToken();
283
323
  if (!token) {
284
- console.error("ImageDescCarousel: No authentication token available");
324
+ console.error("ImageDescCarousel: No authentication token available", {
325
+ timestamp: new Date().toISOString(),
326
+ });
285
327
  setError("Authentication error: Please log in again");
286
328
  return;
287
329
  }
@@ -291,6 +333,11 @@ export function ImageDescCarousel({
291
333
  formData.append("title", editForm.title || `Image ${new Date().toISOString()}`);
292
334
  formData.append("description", editForm.description || "");
293
335
  formData.append("category", editForm.category);
336
+ formData.append("subCategory", editForm.subCategory || "");
337
+ formData.append("favorite", String(editForm.favorite));
338
+ formData.append("banner", String(editForm.banner));
339
+ formData.append("startDate", editForm.startDate || "");
340
+ formData.append("endDate", editForm.endDate || "");
294
341
  if (editForm.file) {
295
342
  formData.append("file", editForm.file);
296
343
  }
@@ -305,7 +352,11 @@ export function ImageDescCarousel({
305
352
 
306
353
  if (!response.ok) {
307
354
  const errorData = await response.json();
308
- console.error("ImageDescCarousel: Edit failed", { status: response.status, errorData });
355
+ console.error("ImageDescCarousel: Edit failed", {
356
+ status: response.status,
357
+ errorData,
358
+ timestamp: new Date().toISOString(),
359
+ });
309
360
  if (response.status === 401) {
310
361
  setError("Authentication error: Please log in again");
311
362
  return;
@@ -317,9 +368,24 @@ export function ImageDescCarousel({
317
368
  setUploadedImages(data || []);
318
369
  setError(null);
319
370
  setIsEditModalOpen(false);
320
- setEditForm({ id: 0, documentId: "", title: "", description: "", category: "indoor", file: null });
371
+ setEditForm({
372
+ id: 0,
373
+ documentId: "",
374
+ file: null,
375
+ title: "",
376
+ description: "",
377
+ category: "indoor",
378
+ subCategory: "",
379
+ favorite: false,
380
+ banner: false,
381
+ startDate: "",
382
+ endDate: "",
383
+ });
321
384
  } catch (err) {
322
- console.error("ImageDescCarousel: Edit Error", err);
385
+ console.error("ImageDescCarousel: Edit Error", {
386
+ error: err instanceof Error ? err.message : "Unknown error",
387
+ timestamp: new Date().toISOString(),
388
+ });
323
389
  setError(err instanceof Error ? err.message : "Failed to edit image");
324
390
  } finally {
325
391
  setIsLoading(false);
@@ -327,6 +393,61 @@ export function ImageDescCarousel({
327
393
  }
328
394
  };
329
395
 
396
+ const handleUploadSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
397
+ e.preventDefault();
398
+ if (!isAdmin) {
399
+ console.error("ImageDescCarousel: Unauthorized upload attempt", {
400
+ isSignedIn,
401
+ authId: user?.authId,
402
+ businessAdminId: user?.businessAdminId,
403
+ userRole: user?.userRole,
404
+ businessOwner: user?.businessOwner ?? null,
405
+ timestamp: new Date().toISOString(),
406
+ });
407
+ setError("Unauthorized: Only admins can upload images");
408
+ return;
409
+ }
410
+ if (!uploadForm.file || uploadForm.file.size === 0) {
411
+ setError("Please select a valid image file");
412
+ return;
413
+ }
414
+ setIsLoading(true);
415
+ setIsSubmitting(true);
416
+ try {
417
+ await handleImageUpload(
418
+ e,
419
+ uploadForm.file,
420
+ uploadForm.title,
421
+ uploadForm.description,
422
+ uploadForm.category,
423
+ uploadForm.subCategory || "",
424
+ uploadForm.favorite
425
+ );
426
+ setError(null);
427
+ setIsUploadModalOpen(false);
428
+ setUploadForm({
429
+ file: null,
430
+ title: "",
431
+ description: "",
432
+ category: "indoor",
433
+ subCategory: "",
434
+ favorite: false,
435
+ banner: false,
436
+ startDate: "",
437
+ endDate: "",
438
+ });
439
+ } catch (err) {
440
+ console.error("ImageDescCarousel: Upload Error", {
441
+ error: err instanceof Error ? err.message : "Unknown error",
442
+ timestamp: new Date().toISOString(),
443
+ });
444
+ setError(err instanceof Error ? err.message : "Failed to upload image");
445
+ } finally {
446
+ setIsLoading(false);
447
+ setIsSubmitting(false);
448
+ }
449
+ };
450
+
330
451
  const sectionVariants = {
331
452
  hidden: { opacity: 0 },
332
453
  visible: {
@@ -446,17 +567,11 @@ export function ImageDescCarousel({
446
567
  </CloseButton>
447
568
  <h3 className="text-xl font-bold text-white mb-4">Upload Image</h3>
448
569
  <form
449
- onSubmit={(e) => {
450
- setIsSubmitting(true);
451
- handleImageUpload(e, uploadForm.file, uploadForm.title, uploadForm.description, uploadForm.category).then(() => {
452
- handleCloseUploadModal();
453
- setIsSubmitting(false);
454
- });
455
- }}
570
+ onSubmit={handleUploadSubmit}
456
571
  className="space-y-4"
457
572
  >
458
573
  <div>
459
- <label htmlFor="image-upload" className="block text-sm font-medium text-gray-300 mb-1">
574
+ <label htmlFor="image-upload" className="block text-sm font-medium text-gray-300 mb-1 max-[320px]:text-xs">
460
575
  Choose Image
461
576
  </label>
462
577
  <input
@@ -516,9 +631,6 @@ export function ImageDescCarousel({
516
631
  <option value="indoor">Indoor</option>
517
632
  <option value="outdoor">Outdoor</option>
518
633
  <option value="commercial">Commercial</option>
519
- {/* Add project-specific categories, e.g.:
520
- <option value="landscape-boulders">Landscape Boulders</option>
521
- */}
522
634
  </select>
523
635
  </div>
524
636
  <div className="flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-3">
@@ -618,9 +730,6 @@ export function ImageDescCarousel({
618
730
  <option value="indoor">Indoor</option>
619
731
  <option value="outdoor">Outdoor</option>
620
732
  <option value="commercial">Commercial</option>
621
- {/* Add project-specific categories, e.g.:
622
- <option value="landscape-boulders">Landscape Boulders</option>
623
- */}
624
733
  </select>
625
734
  </div>
626
735
  <div className="flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-3">