@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.
- package/README.md +40 -0
- package/app/ClientLayout.tsx +66 -0
- package/app/about/page.tsx +11 -248
- package/app/adRequest/page.tsx +101 -25
- package/app/admin-profile/page.tsx +123 -0
- package/app/analytics/page.tsx +41 -5
- package/app/api/about/route.ts +2 -18
- package/app/api/adRequest/route.ts +7 -27
- package/app/api/analytics/[reportType]/route.ts +1 -64
- package/app/api/bio/route.ts +1 -17
- package/app/api/blog/route.ts +1 -19
- package/app/api/contacts/route.ts +1 -46
- package/app/api/files/route.ts +1 -15
- package/app/api/gallery-data/route.ts +53 -61
- package/app/api/schedule/route.ts +5 -21
- package/app/api/signup/route.ts +129 -0
- package/app/api/sync-user/route.ts +268 -94
- package/app/api/verify-admin/route.ts +46 -0
- package/app/blog/[id]/page.tsx +71 -52
- package/app/blog/page.tsx +43 -10
- package/app/favicon.ico +0 -0
- package/app/gallery/page.tsx +27 -6
- package/app/layout.tsx +31 -82
- package/app/page.tsx +20 -311
- package/app/products/constants/product.ts +27 -0
- package/app/products/page.tsx +296 -0
- package/app/products/productOne/page.tsx +266 -0
- package/app/products/productTwo/page.tsx +272 -0
- package/app/schedule/page.tsx +78 -40
- package/bin/init.js +0 -12
- package/components/addOns/functional/ClassList.tsx +21 -17
- package/components/addOns/functional/ProductList.tsx +1027 -0
- package/components/addOns/functional/aboutSections/AboutSection.tsx +107 -70
- package/components/addOns/functional/aboutSections/constants/aboutSection.ts +9 -4
- package/components/addOns/functional/banner/Banner.tsx +150 -0
- package/components/addOns/functional/banner/BannerDashboard.tsx +283 -0
- package/components/addOns/functional/bioSections/BioEditor.tsx +471 -0
- package/components/addOns/functional/bioSections/constants/bioEditor.ts +36 -0
- package/components/addOns/functional/blogSections/BlogDashboard.tsx +1 -1
- package/components/addOns/functional/blogSections/BlogFormPopUp.tsx +2 -1
- package/components/addOns/functional/{ImageDescCarousel.tsx → carousels/ImageDescCarousel.tsx} +166 -57
- package/components/addOns/functional/carousels/ProductDescCarousel.tsx +1129 -0
- package/components/addOns/functional/{ScheduleCarousel.tsx → carousels/ScheduleCarousel.tsx} +110 -50
- package/components/addOns/functional/carousels/constants.ts/productDescCarousel.ts +197 -0
- package/components/addOns/functional/carousels/constants.ts/scheduleCarousel.ts +20 -0
- package/components/addOns/functional/contactsDashboard/ContactsDashboard.tsx +1 -1
- package/components/addOns/functional/fileUploaders/FileUploader.tsx +437 -0
- package/components/addOns/functional/fileUploaders/constants/fileUploader.ts +45 -0
- package/components/addOns/functional/galleries/GalleryComplex.tsx +468 -267
- package/components/addOns/functional/galleries/GallerySimple.tsx +78 -50
- package/components/addOns/functional/galleries/ThreeSetGallery.tsx +260 -0
- package/components/addOns/functional/schedules/ScheduleGridOne.tsx +22 -8
- package/components/addOns/functional/schedules/ScheduleGridTwo.tsx +12 -7
- package/components/addOns/functional/schedules/ScheduleGridTwoBasic.tsx +12 -7
- package/components/addOns/non-functional/SampleCarousel.tsx +3 -3
- package/components/addOns/non-functional/ThreeSetGallery.tsx +3 -3
- package/components/addOns/non-functional/featureSections/FeaturesSection.tsx +74 -0
- package/components/addOns/non-functional/featureSections/constants/featuresSection.ts +30 -0
- package/components/addOns/non-functional/{Heros/HeroSection.tsx → heros/HomeHero.tsx} +17 -15
- package/components/addOns/non-functional/heros/ProductHero.tsx +111 -0
- package/components/addOns/non-functional/heros/constants/hero.ts +62 -0
- package/components/addOns/non-functional/imageCarousels/ProductSlider.tsx +6 -6
- package/components/addOns/non-functional/imageCarousels/ProgramCarousel.tsx +10 -10
- package/components/footers/footer.tsx +161 -198
- package/components/other/admin-menu.tsx +1 -1
- package/lib/auth/auth-context.tsx +225 -0
- package/lib/auth/auth-utils.tsx +30 -0
- package/lib/constants/adRequest.ts +199 -56
- package/lib/constants/admin-profile.ts +12 -0
- package/lib/constants/page.ts +15 -15
- package/lib/google/google-analytics-tracking.tsx +44 -0
- package/lib/types.ts +235 -0
- package/lib/utils/compressImage.tsx +32 -0
- package/middleware.ts +9 -5
- package/next.config.js +1 -1
- package/package.json +3 -2
- package/public/images/test.png +0 -0
- package/components/addOns/functional/BioEditor.tsx +0 -447
- package/components/addOns/functional/FileUploader.tsx +0 -295
- package/components/addOns/non-functional/FeaturesSection.tsx +0 -63
- package/components/types.ts +0 -50
- package/lib/auth-context.tsx +0 -131
- package/lib/verify-user.ts +0 -118
- /package/lib/{google-analytics.tsx → google/google-analytics.tsx} +0 -0
package/components/addOns/functional/{ImageDescCarousel.tsx → carousels/ImageDescCarousel.tsx}
RENAMED
|
@@ -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 {
|
|
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-
|
|
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:
|
|
159
|
-
title:
|
|
160
|
-
description:
|
|
161
|
-
category:
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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({
|
|
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({
|
|
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", {
|
|
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({
|
|
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",
|
|
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={
|
|
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">
|