@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
@@ -0,0 +1,283 @@
1
+ "use client";
2
+
3
+ import { useState, useEffect } from "react";
4
+ import { useAuth } from "@clerk/nextjs";
5
+ import { UploadedImage } from "@/lib/types";
6
+ import { GALLERY_COMPLEX } from "../galleries/constants/galleryComplex";
7
+ import Spinner from "@/components/addOns/non-functional/spinner";
8
+ import { SubmitButton } from "@/components/other/button";
9
+
10
+ interface BannerDashboardProps {
11
+ onUpdate?: (updatedImages: UploadedImage[]) => void;
12
+ }
13
+
14
+ export function BannerDashboard({ onUpdate }: BannerDashboardProps) {
15
+ const { getToken } = useAuth();
16
+ const [bannerData, setBannerData] = useState<UploadedImage | null>(null);
17
+ const [isLoading, setIsLoading] = useState(false);
18
+ const [error, setError] = useState<string | null>(null);
19
+ const [form, setForm] = useState({
20
+ title: "",
21
+ description: "",
22
+ startDate: "",
23
+ endDate: "",
24
+ });
25
+
26
+ useEffect(() => {
27
+ const fetchBanner = async () => {
28
+ setIsLoading(true);
29
+ try {
30
+ const response = await fetch(`/api/gallery-data?t=${Date.now()}`, {
31
+ headers: { "Content-Type": "application/json" },
32
+ cache: "no-store",
33
+ });
34
+ if (response.ok) {
35
+ const result = await response.json();
36
+ const images = Array.isArray(result.data)
37
+ ? result.data.map((item: any) => ({
38
+ id: Number(item.id) || 0,
39
+ documentId: item.documentId || "",
40
+ title: item.title || "Untitled",
41
+ description: item.description || "",
42
+ url: item.image?.url
43
+ ? item.image.url.startsWith("http")
44
+ ? item.image.url
45
+ : `${process.env.STRAPI_API_URL}${item.image.url}`
46
+ : "",
47
+ createdAt: item.createdAt || new Date().toISOString(),
48
+ category: item.category || "none",
49
+ subCategory: item.subCategory || "",
50
+ favorite: item.favorite || false,
51
+ banner: item.banner || false,
52
+ startDate: item.startDate || undefined,
53
+ endDate: item.endDate || undefined,
54
+ }))
55
+ : [];
56
+ const banner = images.find((image: UploadedImage) => image.banner);
57
+ setBannerData((prev) =>
58
+ JSON.stringify(prev) !== JSON.stringify(banner) ? banner : prev
59
+ );
60
+ if (banner) {
61
+ setForm({
62
+ title: banner.title || "",
63
+ description: banner.description || "",
64
+ startDate: banner.startDate
65
+ ? new Date(banner.startDate).toISOString().slice(0, 10)
66
+ : "",
67
+ endDate: banner.endDate
68
+ ? new Date(banner.endDate).toISOString().slice(0, 10)
69
+ : "",
70
+ });
71
+ }
72
+ } else {
73
+ setError("Failed to fetch banner data");
74
+ }
75
+ } catch (err) {
76
+ setError("Error fetching banner data");
77
+ console.error("BannerDashboard: Fetch Error", err);
78
+ } finally {
79
+ setIsLoading(false);
80
+ }
81
+ };
82
+ fetchBanner();
83
+ }, []);
84
+
85
+ const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
86
+ e.preventDefault();
87
+ if (!bannerData) {
88
+ setError("No banner image selected");
89
+ return;
90
+ }
91
+ if (!form.startDate || !form.endDate) {
92
+ setError("Both start date and end date are required");
93
+ return;
94
+ }
95
+ if (new Date(form.startDate) > new Date(form.endDate)) {
96
+ setError("End date must be after start date");
97
+ return;
98
+ }
99
+ setIsLoading(true);
100
+ try {
101
+ const token = await getToken();
102
+ if (!token) {
103
+ setError(GALLERY_COMPLEX.ERRORS.AUTHENTICATION_ERROR);
104
+ console.error("BannerDashboard: No authentication token available", {
105
+ timestamp: new Date().toISOString(),
106
+ });
107
+ return;
108
+ }
109
+
110
+ const formData = new FormData();
111
+ formData.append("documentId", bannerData.documentId);
112
+ formData.append("title", form.title);
113
+ formData.append("description", form.description);
114
+ formData.append("banner", String(bannerData.banner));
115
+ formData.append("startDate", new Date(form.startDate).toISOString());
116
+ formData.append("endDate", new Date(form.endDate).toISOString());
117
+
118
+ console.log("Submitting FormData:", Object.fromEntries(formData));
119
+
120
+ const response = await fetch("/api/gallery-data", {
121
+ method: "PUT",
122
+ headers: {
123
+ Authorization: `Bearer ${token}`,
124
+ },
125
+ body: formData,
126
+ });
127
+
128
+ if (!response.ok) {
129
+ const errorData = await response.json();
130
+ console.error("BannerDashboard: Update failed", {
131
+ status: response.status,
132
+ errorData,
133
+ timestamp: new Date().toISOString(),
134
+ });
135
+ if (response.status === 401) {
136
+ setError(GALLERY_COMPLEX.ERRORS.AUTHENTICATION_ERROR);
137
+ return;
138
+ }
139
+ throw new Error(
140
+ errorData.error ||
141
+ GALLERY_COMPLEX.ERRORS.EDIT_FAILED_STATUS.replace(
142
+ "${response.status}",
143
+ response.status.toString()
144
+ )
145
+ );
146
+ }
147
+
148
+ const { data } = await response.json();
149
+ if (onUpdate) {
150
+ onUpdate(data || []);
151
+ }
152
+ setBannerData((prev) =>
153
+ prev
154
+ ? {
155
+ ...prev,
156
+ title: form.title,
157
+ description: form.description,
158
+ startDate: form.startDate
159
+ ? new Date(form.startDate).toISOString()
160
+ : undefined,
161
+ endDate: form.endDate
162
+ ? new Date(form.endDate).toISOString()
163
+ : undefined,
164
+ banner: prev.banner,
165
+ }
166
+ : prev
167
+ );
168
+ setError(null);
169
+ } catch (err) {
170
+ console.error("BannerDashboard: Update Error", {
171
+ error: err instanceof Error ? err.message : "Unknown error",
172
+ timestamp: new Date().toISOString(),
173
+ });
174
+ setError(
175
+ err instanceof Error ? err.message : GALLERY_COMPLEX.ERRORS.EDIT_FAILED
176
+ );
177
+ } finally {
178
+ setIsLoading(false);
179
+ }
180
+ };
181
+
182
+ if (isLoading) {
183
+ return (
184
+ <div className="p-4 sm:p-6 bg-gray-800 rounded-lg min-h-[200px] flex items-center justify-center">
185
+ <Spinner />
186
+ </div>
187
+ );
188
+ }
189
+
190
+ return (
191
+ <div className="p-4 sm:p-6 bg-gray-800 rounded-lg shadow-lg max-w-2xl mx-auto">
192
+ <h3 className="text-lg sm:text-xl font-bold text-white mb-4">Manage Banner</h3>
193
+ {error && <p className="text-red-500 text-sm mb-4">{error}</p>}
194
+ {bannerData ? (
195
+ <form onSubmit={handleSubmit} className="flex flex-col space-y-4">
196
+ <div>
197
+ <label
198
+ htmlFor="title"
199
+ className="block text-sm font-medium text-gray-300 mb-1"
200
+ >
201
+ Banner Title
202
+ </label>
203
+ <input
204
+ id="title"
205
+ type="text"
206
+ value={form.title}
207
+ onChange={(e) => setForm({ ...form, title: e.target.value })}
208
+ className="p-2 bg-gray-700 text-white rounded-lg w-full focus:ring-2 focus:ring-blue-500 focus:outline-none text-sm sm:text-base"
209
+ disabled={isLoading}
210
+ placeholder="Enter banner title"
211
+ />
212
+ </div>
213
+ <div>
214
+ <label
215
+ htmlFor="description"
216
+ className="block text-sm font-medium text-gray-300 mb-1"
217
+ >
218
+ Banner Description
219
+ </label>
220
+ <textarea
221
+ id="description"
222
+ value={form.description}
223
+ onChange={(e) => setForm({ ...form, description: e.target.value })}
224
+ className="p-2 bg-gray-700 text-white rounded-lg w-full focus:ring-2 focus:ring-blue-500 focus:outline-none text-sm sm:text-base"
225
+ disabled={isLoading}
226
+ placeholder="Enter banner description"
227
+ rows={4}
228
+ />
229
+ </div>
230
+ <div>
231
+ <label
232
+ htmlFor="start-date"
233
+ className="block text-sm font-medium text-gray-300 mb-1"
234
+ >
235
+ Start Date
236
+ </label>
237
+ <input
238
+ id="start-date"
239
+ type="date"
240
+ value={form.startDate}
241
+ onChange={(e) => setForm({ ...form, startDate: e.target.value })}
242
+ className="p-2 bg-gray-700 text-white rounded-lg w-full focus:ring-2 focus:ring-blue-500 focus:outline-none text-sm sm:text-base"
243
+ disabled={isLoading}
244
+ required
245
+ />
246
+ </div>
247
+ <div>
248
+ <label
249
+ htmlFor="end-date"
250
+ className="block text-sm font-medium text-gray-300 mb-1"
251
+ >
252
+ End Date
253
+ </label>
254
+ <input
255
+ id="end-date"
256
+ type="date"
257
+ value={form.endDate}
258
+ onChange={(e) => setForm({ ...form, endDate: e.target.value })}
259
+ className="p-2 bg-gray-700 text-white rounded-lg w-full focus:ring-2 focus:ring-blue-500 focus:outline-none text-sm sm:text-base"
260
+ disabled={isLoading}
261
+ required
262
+ />
263
+ </div>
264
+ <div className="flex justify-end">
265
+ <SubmitButton
266
+ type="submit"
267
+ disabled={isLoading}
268
+ className="px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:outline-none disabled:bg-blue-400 text-sm sm:text-base"
269
+ >
270
+ {isLoading
271
+ ? GALLERY_COMPLEX.BUTTONS.SAVING_BUTTON
272
+ : GALLERY_COMPLEX.BUTTONS.SAVE_BUTTON}
273
+ </SubmitButton>
274
+ </div>
275
+ </form>
276
+ ) : (
277
+ <p className="text-gray-400 text-sm sm:text-base">
278
+ No banner image found. Please upload an image with banner: true in the gallery.
279
+ </p>
280
+ )}
281
+ </div>
282
+ );
283
+ }