@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,437 @@
1
+ "use client";
2
+
3
+ import { useState, useEffect, useRef } from "react";
4
+ import { motion } from "framer-motion";
5
+ import { Card } from "@/components/other/card";
6
+ import { UploadButton, CloseButton, DeleteButton, CancelButton, TrashIconButton, ToggleButton } from "@/components/other/button";
7
+ import { Upload, FileText, X } from "lucide-react";
8
+ import { useAuth } from "@clerk/nextjs";
9
+ import Spinner from "@/components/addOns/non-functional/spinner";
10
+ import { FILE_UPLOADER } from "./constants/fileUploader";
11
+ import { UploadedFile } from "@/lib/types";
12
+ import { isAdminUser } from "@/lib/auth/auth-utils";
13
+
14
+ interface FileUploaderProps {
15
+ user: any;
16
+ isSignedIn: boolean | undefined;
17
+ }
18
+
19
+ export function FileUploader({ user, isSignedIn }: FileUploaderProps) {
20
+ const { getToken } = useAuth();
21
+ const [uploadedFiles, setUploadedFiles] = useState<UploadedFile[]>([]);
22
+ const [pdfUrl, setPdfUrl] = useState<string>("");
23
+ const [isLoading, setIsLoading] = useState(true);
24
+ const [error, setError] = useState<string | null>(null);
25
+ const [isAdmin, setIsAdmin] = useState(false);
26
+ const [uploading, setUploading] = useState(false);
27
+ const [showAllFiles, setShowAllFiles] = useState(false);
28
+ const [isConfirmDeleteOpen, setIsConfirmDeleteOpen] = useState(false);
29
+ const [fileToDelete, setFileToDelete] = useState<string | null>(null);
30
+ const hasFetched = useRef(false);
31
+
32
+ const baseUrl = process.env.BASE_URL || "";
33
+
34
+ // Fetch files
35
+ useEffect(() => {
36
+ if (hasFetched.current) return;
37
+ hasFetched.current = true;
38
+
39
+ const fetchFiles = async () => {
40
+ setIsLoading(true);
41
+ try {
42
+ const response = await fetch(`/api/files?t=${Date.now()}`, {
43
+ headers: { "Content-Type": "application/json" },
44
+ cache: "no-store",
45
+ });
46
+ if (response.ok) {
47
+ const result = await response.json();
48
+ if (result.meta?.pagination?.total > 100) {
49
+ setError(FILE_UPLOADER.ERRORS.PAGINATION_WARNING);
50
+ }
51
+ setUploadedFiles(
52
+ result.data.map((file: any) => ({
53
+ id: file.id,
54
+ documentId: file.documentId,
55
+ name: file.name || "Untitled",
56
+ url: file.url,
57
+ createdAt: file.createdAt,
58
+ })) || []
59
+ );
60
+ } else {
61
+ throw new Error(FILE_UPLOADER.ERRORS.FETCH_FILES_FAILED);
62
+ }
63
+ } catch (err) {
64
+ setError(err instanceof Error ? err.message : FILE_UPLOADER.ERRORS.FETCH_ERROR);
65
+ } finally {
66
+ setIsLoading(false);
67
+ }
68
+ };
69
+ fetchFiles();
70
+ }, []);
71
+
72
+ useEffect(() => {
73
+ let isMounted = true;
74
+ const checkAdmin = async () => {
75
+ if (!isSignedIn || !user?.authId) {
76
+ if (isMounted) setIsAdmin(false);
77
+ return;
78
+ }
79
+ const adminStatus = await isAdminUser(isSignedIn, user);
80
+ if (isMounted) {
81
+ setIsAdmin(adminStatus);
82
+ }
83
+ };
84
+ checkAdmin();
85
+ return () => {
86
+ isMounted = false;
87
+ };
88
+ }, [isSignedIn, user]);
89
+
90
+ useEffect(() => {
91
+ const event = new CustomEvent("modalStateChange", {
92
+ detail: { isOpen: isConfirmDeleteOpen },
93
+ });
94
+ window.dispatchEvent(event);
95
+ }, [isConfirmDeleteOpen]);
96
+
97
+ useEffect(() => {
98
+ if (isConfirmDeleteOpen) {
99
+ const scrollY = window.scrollY;
100
+ document.body.style.position = "fixed";
101
+ document.body.style.top = `-${scrollY}px`;
102
+ document.body.style.width = "100%";
103
+ document.body.classList.add("overflow-hidden");
104
+
105
+ return () => {
106
+ const scrollYRestored = parseInt(document.body.style.top || "0", 10) * -1;
107
+ document.body.style.position = "";
108
+ document.body.style.top = "";
109
+ document.body.style.width = "";
110
+ document.body.classList.remove("overflow-hidden");
111
+ window.scrollTo(0, scrollYRestored);
112
+ };
113
+ }
114
+ }, [isConfirmDeleteOpen]);
115
+
116
+ useEffect(() => {
117
+ const handleEsc = (e: KeyboardEvent) => {
118
+ if (e.key === "Escape" && isConfirmDeleteOpen) {
119
+ handleCancelDelete();
120
+ }
121
+ };
122
+ window.addEventListener("keydown", handleEsc);
123
+ return () => window.removeEventListener("keydown", handleEsc);
124
+ }, [isConfirmDeleteOpen]);
125
+
126
+ const openConfirmDelete = (documentId: string) => {
127
+ setFileToDelete(documentId);
128
+ setIsConfirmDeleteOpen(true);
129
+ };
130
+
131
+ const handleCancelDelete = () => {
132
+ setIsConfirmDeleteOpen(false);
133
+ setFileToDelete(null);
134
+ };
135
+
136
+ const handleFileUpload = async (e: React.ChangeEvent<HTMLInputElement>) => {
137
+ if (!isAdmin) {
138
+ setError(FILE_UPLOADER.ERRORS.UNAUTHORIZED_UPLOAD);
139
+ return;
140
+ }
141
+ const file = e.target.files?.[0];
142
+ if (!file) {
143
+ setError(FILE_UPLOADER.ERRORS.NO_FILE_SELECTED);
144
+ return;
145
+ }
146
+ if (file.type !== "application/pdf") {
147
+ setError(FILE_UPLOADER.ERRORS.INVALID_FILE_TYPE);
148
+ return;
149
+ }
150
+ setError(null);
151
+ setUploading(true);
152
+ try {
153
+ const token = await getToken();
154
+ if (!token) throw new Error(FILE_UPLOADER.ERRORS.NO_AUTH_TOKEN);
155
+ const formData = new FormData();
156
+ formData.append("files", file);
157
+
158
+ const response = await fetch("/api/files", {
159
+ method: "POST",
160
+ headers: {
161
+ Authorization: `Bearer ${token}`,
162
+ },
163
+ body: formData,
164
+ });
165
+
166
+ if (!response.ok) {
167
+ const errorData = await response.json();
168
+ throw new Error(errorData.error || FILE_UPLOADER.ERRORS.UPLOAD_FILE_FAILED.replace("${response.status}", response.status.toString()));
169
+ }
170
+ const result = await response.json();
171
+ setUploadedFiles(
172
+ result.data.map((file: any) => ({
173
+ id: file.id,
174
+ documentId: file.documentId,
175
+ name: file.name || "Unnamed File",
176
+ url: file.url,
177
+ createdAt: file.createdAt,
178
+ })) || []
179
+ );
180
+ setPdfUrl(result.newFileUrl || "");
181
+ } catch (err) {
182
+ setError(err instanceof Error ? err.message : FILE_UPLOADER.ERRORS.UPLOAD_FILE_ERROR);
183
+ } finally {
184
+ setUploading(false);
185
+ }
186
+ };
187
+
188
+ const handleDeleteFile = async (documentId: string) => {
189
+ if (!isAdmin) {
190
+ setError(FILE_UPLOADER.ERRORS.UNAUTHORIZED_DELETE);
191
+ return;
192
+ }
193
+ setError(null);
194
+ setUploading(true);
195
+ try {
196
+ const token = await getToken();
197
+ if (!token) throw new Error(FILE_UPLOADER.ERRORS.NO_AUTH_TOKEN);
198
+ const response = await fetch("/api/files", {
199
+ method: "DELETE",
200
+ headers: {
201
+ "Content-Type": "application/json",
202
+ Authorization: `Bearer ${token}`,
203
+ },
204
+ body: JSON.stringify({ documentId }),
205
+ });
206
+ if (!response.ok) {
207
+ const errorData = await response.json();
208
+ throw new Error(errorData.error || FILE_UPLOADER.ERRORS.DELETE_FILE_FAILED.replace("${response.status}", response.status.toString()));
209
+ }
210
+ const result = await response.json();
211
+ setUploadedFiles(
212
+ result.data.map((file: any) => ({
213
+ id: file.id,
214
+ documentId: file.documentId,
215
+ name: file.name || "Unnamed File",
216
+ url: file.url,
217
+ createdAt: file.createdAt,
218
+ })) || []
219
+ );
220
+ if (pdfUrl === uploadedFiles.find((f) => f.documentId === documentId)?.url) {
221
+ setPdfUrl("");
222
+ }
223
+ } catch (err) {
224
+ setError(err instanceof Error ? err.message : FILE_UPLOADER.ERRORS.DELETE_FILE_ERROR);
225
+ } finally {
226
+ setUploading(false);
227
+ }
228
+ };
229
+
230
+ const handleConfirmDelete = async () => {
231
+ if (fileToDelete !== null) {
232
+ await handleDeleteFile(fileToDelete);
233
+ setIsConfirmDeleteOpen(false);
234
+ setFileToDelete(null);
235
+ }
236
+ };
237
+
238
+ const truncateText = (text: string, maxLength: number = 30): string => {
239
+ if (text.length <= maxLength) return text;
240
+ return text.slice(0, maxLength - 3) + "...";
241
+ };
242
+
243
+ const modalVariants = {
244
+ hidden: { opacity: 0, y: "100vh" },
245
+ visible: { opacity: 1, y: 0, transition: { duration: 0.5, ease: [0.16, 1, 0.3, 1] } },
246
+ exit: { opacity: 0, y: "100vh", transition: { duration: 0.3, ease: "easeIn" } },
247
+ };
248
+
249
+ const sectionVariants = {
250
+ hidden: { opacity: 0, y: 20 },
251
+ visible: { opacity: 1, y: 0, transition: { duration: 0.5, ease: "easeOut" } },
252
+ };
253
+
254
+ if (isLoading) return <Spinner />;
255
+
256
+ return (
257
+ <div className="w-full bg-[#1E2A44] px-4 py-12 sm:px-8 sm:py-16 border-none outline-none z-0">
258
+ <style jsx>{`
259
+ :root {
260
+ --jubilee: #F47C7C;
261
+ --exuberant-blue: #FF69B4;
262
+ --modern-purple: #D946EF;
263
+ }
264
+ .modal-glassmorphism {
265
+ background: rgba(255, 255, 255, 0.08);
266
+ backdrop-filter: blur(10px);
267
+ -webkit-backdrop-filter: blur(10px);
268
+ border: 2px solid transparent;
269
+ border-image: linear-gradient(
270
+ 45deg,
271
+ var(--exuberant-blue),
272
+ var(--jubilee),
273
+ var(--modern-purple)
274
+ ) 1;
275
+ box-shadow: 0 16px 48px rgba(0, 0, 0, 0.1);
276
+ border-radius: 2rem;
277
+ max-height: 90vh;
278
+ overflow: hidden;
279
+ }
280
+ @supports not (backdrop-filter: blur(10px)) {
281
+ .modal-glassmorphism {
282
+ background: rgba(255, 255, 255, 0.2);
283
+ }
284
+ }
285
+ @media (max-width: 640px) {
286
+ .modal-glassmorphism {
287
+ padding: 1rem;
288
+ max-height: 85vh;
289
+ }
290
+ }
291
+ .file-item {
292
+ z-index: 30;
293
+ pointer-events: auto;
294
+ cursor: pointer;
295
+ }
296
+ `}</style>
297
+ <motion.section
298
+ variants={sectionVariants}
299
+ initial="hidden"
300
+ animate="visible"
301
+ className="w-full max-w-7xl mx-auto p-4 sm:p-8 relative z-10"
302
+ >
303
+ <Card className="bg-[#1E2A44] text-white flex flex-col items-center space-y-6 rounded-none border-none outline-none">
304
+ <h3 className="text-2xl sm:text-3xl md:text-4xl font-bold tracking-tight text-white text-center">
305
+ {FILE_UPLOADER.UI.TITLE}
306
+ </h3>
307
+ <div className="space-y-4 w-full">
308
+ {error && (
309
+ <p className="text-red-400 text-base sm:text-lg md:text-xl text-center">{error}</p>
310
+ )}
311
+ {isAdmin ? (
312
+ <div className="flex flex-col sm:flex-row items-center justify-center space-y-4 sm:space-y-0 sm:space-x-4">
313
+ <input
314
+ type="file"
315
+ accept="application/pdf"
316
+ onChange={handleFileUpload}
317
+ disabled={uploading}
318
+ className="hidden"
319
+ id="file-upload"
320
+ />
321
+ <label htmlFor="file-upload" className="cursor-pointer">
322
+ <UploadButton disabled={uploading} asChild>
323
+ <span className="flex items-center justify-center">
324
+ <Upload className="mr-2 h-4 w-4 sm:h-5 sm:w-5" />
325
+ {FILE_UPLOADER.BUTTONS.UPLOAD_BUTTON}
326
+ </span>
327
+ </UploadButton>
328
+ </label>
329
+ {uploading && (
330
+ <span className="text-white font-bold text-base sm:text-lg">Uploading...</span>
331
+ )}
332
+ </div>
333
+ ) : (
334
+ <p className="text-gray-400 text-sm font-medium text-center">
335
+ {isSignedIn ? "You need admin privileges to upload files." : "Please sign in to upload files."}
336
+ </p>
337
+ )}
338
+ {uploadedFiles.length > 0 ? (
339
+ <>
340
+ {uploadedFiles.slice(0, showAllFiles ? undefined : 8).map((file) => {
341
+ const token = localStorage.getItem("jwt") || "";
342
+ const fullUrl = file.url.startsWith("http")
343
+ ? `${file.url}${token ? (file.url.includes("?") ? "&" : "?") + `token=${token}` : ""}`
344
+ : `${baseUrl}${file.url}${token ? `?token=${token}` : ""}`;
345
+ return (
346
+ <div
347
+ key={file.documentId}
348
+ className="file-item flex flex-col sm:flex-row items-center justify-between p-4 sm:p-6 rounded-lg bg-[#2A3A66]/50 hover:bg-[#2A3A66]/70 hover:shadow-lg hover:border-[#FF69B4]/50 transition-all cursor-pointer"
349
+ onClick={() => {
350
+ setPdfUrl(fullUrl);
351
+ window.open(fullUrl, "_blank", "noopener,noreferrer");
352
+ }}
353
+ role="button"
354
+ tabIndex={0}
355
+ onKeyDown={(e) => {
356
+ if (e.key === "Enter" || e.key === " ") {
357
+ setPdfUrl(fullUrl);
358
+ window.open(fullUrl, "_blank", "noopener,noreferrer");
359
+ }
360
+ }}
361
+ aria-label={`Open PDF: ${truncateText(file.name)}`}
362
+ >
363
+ <div className="text-white font-bold text-base sm:text-lg md:text-xl flex items-center w-full sm:w-auto min-w-0 sm:pb-0 pb-6">
364
+ <FileText className="mr-2 h-4 w-4 sm:h-5 sm:w-5 text-[#FF69B4] flex-shrink-0" />
365
+ <span className="truncate">{truncateText(file.name)}</span>
366
+ </div>
367
+ {isAdmin && (
368
+ <TrashIconButton
369
+ onClick={(e) => {
370
+ e.stopPropagation();
371
+ openConfirmDelete(file.documentId);
372
+ }}
373
+ aria-label={`Delete file ${truncateText(file.name)}`}
374
+ variant="trash-icon"
375
+ className="relative"
376
+ >
377
+ <X />
378
+ </TrashIconButton>
379
+ )}
380
+ </div>
381
+ );
382
+ })}
383
+ {uploadedFiles.length > 8 && (
384
+ <div className="text-center mt-4">
385
+ <ToggleButton onClick={() => setShowAllFiles(!showAllFiles)}>
386
+ {showAllFiles ? FILE_UPLOADER.BUTTONS.SHOW_LESS_BUTTON : FILE_UPLOADER.BUTTONS.SHOW_MORE_BUTTON}
387
+ </ToggleButton>
388
+ </div>
389
+ )}
390
+ </>
391
+ ) : (
392
+ <p className="text-gray-300 font-bold text-base sm:text-lg md:text-xl text-center">
393
+ {FILE_UPLOADER.UI.NO_FILES_TEXT}
394
+ </p>
395
+ )}
396
+ </div>
397
+ </Card>
398
+ </motion.section>
399
+ {isConfirmDeleteOpen && isAdmin && (
400
+ <motion.div
401
+ variants={modalVariants}
402
+ initial="hidden"
403
+ animate="visible"
404
+ exit="exit"
405
+ className="fixed inset-0 bg-black/90 flex items-center justify-center z-[10000] p-4 isolate modal-glassmorphism"
406
+ onClick={handleCancelDelete}
407
+ role="dialog"
408
+ aria-modal="true"
409
+ aria-labelledby="modal-title"
410
+ >
411
+ <div
412
+ className="relative max-w-5xl w-full mx-4 p-6 bg-gray-800/50 border border-gray-700/50 rounded-lg shadow-lg"
413
+ onClick={(e) => e.stopPropagation()}
414
+ >
415
+ <CloseButton onClick={handleCancelDelete}>
416
+ <X className="h-6 w-6 sm:h-8 sm:w-8" />
417
+ </CloseButton>
418
+ <h3 id="modal-title" className="text-xl font-bold text-white mb-4">
419
+ {FILE_UPLOADER.UI.CONFIRM_DELETE_TITLE}
420
+ </h3>
421
+ <p className="text-gray-300 text-sm mb-4">
422
+ {FILE_UPLOADER.UI.CONFIRM_DELETE_MESSAGE}
423
+ </p>
424
+ <div className="flex space-x-3">
425
+ <DeleteButton onClick={handleConfirmDelete} disabled={uploading}>
426
+ {uploading ? FILE_UPLOADER.BUTTONS.DELETING_BUTTON : FILE_UPLOADER.BUTTONS.DELETE_BUTTON}
427
+ </DeleteButton>
428
+ <CancelButton onClick={handleCancelDelete}>
429
+ {FILE_UPLOADER.BUTTONS.CANCEL_BUTTON}
430
+ </CancelButton>
431
+ </div>
432
+ </div>
433
+ </motion.div>
434
+ )}
435
+ </div>
436
+ );
437
+ }
@@ -0,0 +1,45 @@
1
+ export const FILE_UPLOADER = {
2
+ UI: {
3
+ TITLE: "Fitness Forum",
4
+ NO_FILES_TEXT: "No files uploaded yet.",
5
+ UPLOAD_LABEL: "Upload PDF",
6
+ SELECTED_FILE_TEXT: "Selected file: ${fileName}",
7
+ CONFIRM_DELETE_TITLE: "Confirm Deletion",
8
+ CONFIRM_DELETE_MESSAGE: "Are you sure you want to delete this file? This action cannot be undone.",
9
+ EDIT_MODAL_HEADING: "Edit File",
10
+ NAME_LABEL: "File Name",
11
+ NAME_PLACEHOLDER: "Enter the file name",
12
+ DESCRIPTION_LABEL: "File Description (optional)",
13
+ DESCRIPTION_PLACEHOLDER: "Enter a description for the file",
14
+ FILE_LABEL: "Replace PDF (optional)",
15
+ },
16
+ ERRORS: {
17
+ FETCH_FILES_FAILED: "Failed to fetch files",
18
+ FETCH_ERROR: "An error occurred while fetching files",
19
+ UNAUTHORIZED_UPLOAD: "You are not authorized to upload files",
20
+ UNAUTHORIZED_DELETE: "You are not authorized to delete files",
21
+ UNAUTHORIZED_UPDATE: "You are not authorized to update files",
22
+ NO_AUTH_TOKEN: "Authentication token not found",
23
+ NO_FILE_SELECTED: "No file selected",
24
+ INVALID_FILE_TYPE: "Only PDF files are allowed",
25
+ UPLOAD_FILE_FAILED: "Failed to upload file: ${response.status}",
26
+ UPLOAD_FILE_ERROR: "An error occurred while uploading the file",
27
+ DELETE_FILE_FAILED: "Failed to delete file: ${response.status}",
28
+ DELETE_FILE_ERROR: "An error occurred while deleting the file",
29
+ UPDATE_FILE_FAILED: "Failed to update file: ${response.status}",
30
+ UPDATE_FILE_ERROR: "An error occurred while updating the file",
31
+ PAGINATION_WARNING: "Too many files to display, please contact support",
32
+ REQUIRED_FIELDS: "File name is required.",
33
+ },
34
+ BUTTONS: {
35
+ UPLOAD_BUTTON: "Upload PDF",
36
+ DELETING_BUTTON: "Deleting...",
37
+ DELETE_BUTTON: "Delete",
38
+ CANCEL_BUTTON: "Cancel",
39
+ SHOW_MORE_BUTTON: "Show More",
40
+ SHOW_LESS_BUTTON: "Show Less",
41
+ EDIT_BUTTON: "Edit",
42
+ UPDATE_BUTTON: "Update",
43
+ SAVING_BUTTON: "Saving...",
44
+ },
45
+ };