@devvistatech/devvista-kit 0.0.10 → 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/CHANGELOG.md +12 -12
- package/LICENSE +6 -6
- package/README.md +55 -15
- package/app/ClientLayout.tsx +66 -0
- package/app/about/page.tsx +61 -298
- package/app/adRequest/page.tsx +625 -549
- package/app/admin-profile/page.tsx +123 -0
- package/app/analytics/page.tsx +382 -346
- package/app/api/about/route.ts +290 -306
- package/app/api/adRequest/route.ts +547 -567
- package/app/api/analytics/[reportType]/route.ts +274 -337
- package/app/api/bio/route.ts +297 -313
- package/app/api/blog/route.ts +288 -306
- package/app/api/chat/route.ts +14 -14
- package/app/api/contact/route.ts +409 -409
- package/app/api/contacts/route.ts +179 -224
- package/app/api/files/route.ts +415 -429
- package/app/api/gallery-data/route.ts +727 -735
- package/app/api/schedule/route.ts +439 -455
- package/app/api/signup/route.ts +129 -0
- package/app/api/sync-user/route.ts +306 -132
- package/app/api/trial-request/route.ts +297 -297
- package/app/api/verify-admin/route.ts +46 -0
- package/app/blog/[id]/page.tsx +307 -288
- package/app/blog/page.tsx +249 -216
- package/app/contact/page.tsx +284 -284
- package/app/faq/page.tsx +191 -191
- package/app/favicon.ico +0 -0
- package/app/gallery/page.tsx +336 -315
- package/app/globals.css +58 -58
- package/app/layout.tsx +59 -110
- package/app/not-found.tsx +20 -20
- package/app/page.tsx +47 -338
- 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 +698 -660
- package/bin/init.js +207 -219
- package/components/addOns/functional/CalendlyWidget.tsx +107 -107
- package/components/addOns/functional/ClassList.tsx +149 -145
- package/components/addOns/functional/ClassPopup.tsx +398 -398
- package/components/addOns/functional/ContactForm.tsx +284 -284
- package/components/addOns/functional/NewUserAnalytics.tsx +100 -100
- package/components/addOns/functional/ProductList.tsx +1027 -0
- package/components/addOns/functional/aboutSections/AboutSection.tsx +581 -544
- package/components/addOns/functional/aboutSections/constants/aboutSection.ts +70 -65
- 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 +184 -184
- package/components/addOns/functional/blogSections/BlogFormPopUp.tsx +555 -554
- package/components/addOns/functional/blogSections/BlogList.tsx +148 -148
- package/components/addOns/functional/blogSections/BlogSidebar.tsx +58 -58
- package/components/addOns/functional/blogSections/constants/blogDashboard.ts +28 -28
- package/components/addOns/functional/blogSections/constants/blogFormPopUp.ts +97 -97
- package/components/addOns/functional/blogSections/constants/blogList.ts +22 -22
- package/components/addOns/functional/blogSections/constants/blogSidebar.ts +15 -15
- package/components/addOns/functional/{ImageDescCarousel.tsx → carousels/ImageDescCarousel.tsx} +839 -730
- package/components/addOns/functional/carousels/ProductDescCarousel.tsx +1129 -0
- package/components/addOns/functional/{ScheduleCarousel.tsx → carousels/ScheduleCarousel.tsx} +231 -171
- 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 +366 -366
- package/components/addOns/functional/contactsDashboard/constants/contactsDashboard.ts +70 -70
- 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 +1037 -836
- package/components/addOns/functional/galleries/GallerySimple.tsx +537 -509
- package/components/addOns/functional/galleries/ThreeSetGallery.tsx +260 -0
- package/components/addOns/functional/galleries/constants/galleryComplex.ts +106 -106
- package/components/addOns/functional/galleries/constants/gallerySimple.ts +76 -76
- package/components/addOns/functional/schedules/ScheduleGridOne.tsx +276 -262
- package/components/addOns/functional/schedules/ScheduleGridTwo.tsx +299 -294
- package/components/addOns/functional/schedules/ScheduleGridTwoBasic.tsx +293 -288
- package/components/addOns/functional/schedules/SchedulerForm.tsx +428 -428
- package/components/addOns/functional/schedules/constants/ScheduleGridTwo.ts +40 -40
- package/components/addOns/functional/schedules/constants/ScheduleGridTwoBasic.ts +40 -40
- package/components/addOns/functional/schedules/constants/SchedulerForm.ts +65 -65
- package/components/addOns/functional/schedules/constants/scheduleGridOne.ts +54 -54
- package/components/addOns/non-functional/AnnouncementBanner.tsx +46 -46
- package/components/addOns/non-functional/IconBubble.tsx +49 -49
- package/components/addOns/non-functional/SampleCarousel.tsx +204 -204
- package/components/addOns/non-functional/Testimonials.tsx +334 -334
- package/components/addOns/non-functional/ThreeSetGallery.tsx +63 -63
- package/components/addOns/non-functional/aboutSections/AboutSection.tsx +62 -62
- package/components/addOns/non-functional/aboutSections/constants/aboutSection.ts +24 -24
- 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} +144 -142
- 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 +117 -117
- package/components/addOns/non-functional/imageCarousels/ProgramCarousel.tsx +232 -232
- package/components/addOns/non-functional/imageCarousels/constants/programCarousel.ts +39 -39
- package/components/addOns/non-functional/imageCarousels/constants/programSlider.ts +36 -36
- package/components/addOns/non-functional/spinner.tsx +21 -21
- package/components/footers/footer.tsx +416 -453
- package/components/navBars/navbar.tsx +310 -310
- package/components/other/accordion.tsx +58 -58
- package/components/other/admin-menu.tsx +68 -68
- package/components/other/alert-dialog.tsx +141 -141
- package/components/other/alert.tsx +59 -59
- package/components/other/aspect-ratio.tsx +7 -7
- package/components/other/avatar.tsx +50 -50
- package/components/other/badge.tsx +36 -36
- package/components/other/breadcrumb.tsx +115 -115
- package/components/other/button.tsx +738 -738
- package/components/other/calendar.tsx +66 -66
- package/components/other/card.tsx +86 -86
- package/components/other/carousel.tsx +274 -274
- package/components/other/chart.tsx +363 -363
- package/components/other/checkbox.tsx +30 -30
- package/components/other/collapsible.tsx +11 -11
- package/components/other/command.tsx +155 -155
- package/components/other/context-menu.tsx +200 -200
- package/components/other/dialog.tsx +122 -122
- package/components/other/drawer.tsx +118 -118
- package/components/other/dropdown-menu.tsx +200 -200
- package/components/other/form.tsx +179 -179
- package/components/other/hover-card.tsx +29 -29
- package/components/other/input-otp.tsx +71 -71
- package/components/other/input.tsx +25 -25
- package/components/other/label.tsx +26 -26
- package/components/other/menubar.tsx +236 -236
- package/components/other/mobile-icon.tsx +21 -21
- package/components/other/navigation-menu.tsx +128 -128
- package/components/other/pagination.tsx +117 -117
- package/components/other/popover.tsx +31 -31
- package/components/other/progress.tsx +28 -28
- package/components/other/radio-group.tsx +44 -44
- package/components/other/resizable.tsx +45 -45
- package/components/other/scroll-area.tsx +48 -48
- package/components/other/select.tsx +160 -160
- package/components/other/separator.tsx +31 -31
- package/components/other/sheet.tsx +140 -140
- package/components/other/skeleton.tsx +15 -15
- package/components/other/slider.tsx +28 -28
- package/components/other/social-icons.tsx +39 -39
- package/components/other/sonner.tsx +31 -31
- package/components/other/switch.tsx +29 -29
- package/components/other/table.tsx +117 -117
- package/components/other/tabs.tsx +55 -55
- package/components/other/textarea.tsx +24 -24
- package/components/other/toast.tsx +122 -122
- package/components/other/toaster.tsx +35 -35
- package/components/other/toggle-group.tsx +61 -61
- package/components/other/toggle.tsx +45 -45
- package/components/other/tooltip.tsx +30 -30
- package/components/theme-provider.tsx +8 -8
- package/hooks/use-toast.ts +188 -188
- package/lib/auth/auth-context.tsx +225 -0
- package/lib/auth/auth-utils.tsx +30 -0
- package/lib/constants/about.ts +34 -34
- package/lib/constants/adRequest.ts +256 -113
- package/lib/constants/admin-profile.ts +12 -0
- package/lib/constants/contact.ts +40 -40
- package/lib/constants/faq.ts +34 -34
- package/lib/constants/gallery.ts +42 -42
- package/lib/constants/page.ts +69 -69
- package/lib/constants/schedule.ts +71 -71
- package/lib/google/google-analytics-tracking.tsx +44 -0
- package/lib/{google-analytics.tsx → google/google-analytics.tsx} +97 -97
- package/lib/types.ts +235 -0
- package/lib/utils/compressImage.tsx +32 -0
- package/middleware.ts +46 -42
- package/netlify.toml +5 -5
- package/next.config.js +10 -10
- package/package.json +117 -116
- package/public/images/test.png +0 -0
- package/tailwind.config.ts +89 -89
- package/tsconfig.json +23 -23
- 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/dist/.next/types/app/api/about/route.js +0 -52
- package/dist/.next/types/app/api/blog/route.js +0 -52
- package/dist/.next/types/app/api/files/route.js +0 -52
- package/dist/.next/types/app/api/schedule/route.js +0 -52
- package/dist/.next/types/app/api/sync-user/route.js +0 -52
- package/dist/.next/types/app/layout.js +0 -22
- package/dist/.next/types/app/page.js +0 -22
- package/dist/app/about/page.jsx +0 -258
- package/dist/app/adRequest/page.jsx +0 -531
- package/dist/app/analytics/page.jsx +0 -298
- package/dist/app/api/about/route.js +0 -285
- package/dist/app/api/adRequest/route.js +0 -440
- package/dist/app/api/analytics/[reportType]/route.js +0 -357
- package/dist/app/api/bio/route.js +0 -293
- package/dist/app/api/blog/route.js +0 -366
- package/dist/app/api/chat/route.js +0 -58
- package/dist/app/api/contact/route.js +0 -163
- package/dist/app/api/contacts/route.js +0 -234
- package/dist/app/api/files/route.js +0 -444
- package/dist/app/api/gallery-data/route.js +0 -719
- package/dist/app/api/schedule/route.js +0 -461
- package/dist/app/api/sync-user/route.js +0 -186
- package/dist/app/api/trial-request/route.js +0 -165
- package/dist/app/blog/[id]/page.jsx +0 -312
- package/dist/app/blog/page.jsx +0 -210
- package/dist/app/constants/about.js +0 -32
- package/dist/app/constants/adRequest.js +0 -113
- package/dist/app/constants/contact.js +0 -40
- package/dist/app/constants/faq.js +0 -36
- package/dist/app/constants/gallery.js +0 -42
- package/dist/app/constants/page.js +0 -69
- package/dist/app/constants/schedule.js +0 -71
- package/dist/app/contact/page.jsx +0 -119
- package/dist/app/faq/page.jsx +0 -97
- package/dist/app/gallery/page.jsx +0 -281
- package/dist/app/layout.jsx +0 -45
- package/dist/app/not-found.jsx +0 -14
- package/dist/app/page.jsx +0 -324
- package/dist/app/schedule/page.jsx +0 -500
- package/dist/components/addOns/functional/BioEditor.jsx +0 -187
- package/dist/components/addOns/functional/CalendlyWidget.jsx +0 -61
- package/dist/components/addOns/functional/ClassList.jsx +0 -158
- package/dist/components/addOns/functional/ClassPopup.jsx +0 -300
- package/dist/components/addOns/functional/ContactForm.jsx +0 -219
- package/dist/components/addOns/functional/FileUploader.jsx +0 -222
- package/dist/components/addOns/functional/ImageDescCarousel.jsx +0 -491
- package/dist/components/addOns/functional/NewUserAnalytics.jsx +0 -71
- package/dist/components/addOns/functional/ScheduleCarousel.jsx +0 -68
- package/dist/components/addOns/functional/aboutSections/AboutSection.jsx +0 -372
- package/dist/components/addOns/functional/aboutSections/constants/aboutSection.js +0 -65
- package/dist/components/addOns/functional/blogSections/BlogDashboard.jsx +0 -111
- package/dist/components/addOns/functional/blogSections/BlogFormPopUp.jsx +0 -465
- package/dist/components/addOns/functional/blogSections/BlogList.jsx +0 -170
- package/dist/components/addOns/functional/blogSections/BlogSidebar.jsx +0 -35
- package/dist/components/addOns/functional/blogSections/constants/blogDashboard.js +0 -28
- package/dist/components/addOns/functional/blogSections/constants/blogFormPopUp.js +0 -97
- package/dist/components/addOns/functional/blogSections/constants/blogList.js +0 -22
- package/dist/components/addOns/functional/blogSections/constants/blogSidebar.js +0 -15
- package/dist/components/addOns/functional/contactsDashboard/ContactsDashboard.jsx +0 -355
- package/dist/components/addOns/functional/contactsDashboard/constants/contactsDashboard.js +0 -70
- package/dist/components/addOns/functional/galleries/GalleryComplex.jsx +0 -605
- package/dist/components/addOns/functional/galleries/GallerySimple.jsx +0 -363
- package/dist/components/addOns/functional/galleries/constants/galleryComplex.js +0 -106
- package/dist/components/addOns/functional/galleries/constants/gallerySimple.js +0 -76
- package/dist/components/addOns/functional/schedules/ScheduleGridOne.jsx +0 -167
- package/dist/components/addOns/functional/schedules/ScheduleGridTwo.jsx +0 -100
- package/dist/components/addOns/functional/schedules/ScheduleGridTwoBasic.jsx +0 -97
- package/dist/components/addOns/functional/schedules/SchedulerForm.jsx +0 -188
- package/dist/components/addOns/functional/schedules/constants/ScheduleGridTwo.js +0 -40
- package/dist/components/addOns/functional/schedules/constants/ScheduleGridTwoBasic.js +0 -40
- package/dist/components/addOns/functional/schedules/constants/SchedulerForm.js +0 -65
- package/dist/components/addOns/functional/schedules/constants/scheduleGridOne.js +0 -54
- package/dist/components/addOns/non-functional/AnnouncementBanner.jsx +0 -24
- package/dist/components/addOns/non-functional/FeaturesSection.jsx +0 -38
- package/dist/components/addOns/non-functional/HeroSection.jsx +0 -71
- package/dist/components/addOns/non-functional/Heros/HeroSection.jsx +0 -71
- package/dist/components/addOns/non-functional/IconBubble.jsx +0 -36
- package/dist/components/addOns/non-functional/SampleCarousel.jsx +0 -114
- package/dist/components/addOns/non-functional/Testimonials.jsx +0 -177
- package/dist/components/addOns/non-functional/ThreeSetGallery.jsx +0 -40
- package/dist/components/addOns/non-functional/aboutSections/AboutSection.jsx +0 -35
- package/dist/components/addOns/non-functional/aboutSections/constants/aboutSection.js +0 -24
- package/dist/components/addOns/non-functional/imageCarousels/ProductSlider.jsx +0 -80
- package/dist/components/addOns/non-functional/imageCarousels/ProgramCarousel.jsx +0 -155
- package/dist/components/addOns/non-functional/imageCarousels/constants/programCarousel.js +0 -39
- package/dist/components/addOns/non-functional/imageCarousels/constants/programSlider.js +0 -36
- package/dist/components/addOns/non-functional/spinner.jsx +0 -13
- package/dist/components/footers/footer.jsx +0 -217
- package/dist/components/navBars/navbar.jsx +0 -159
- package/dist/components/other/accordion.jsx +0 -40
- package/dist/components/other/admin-menu.jsx +0 -34
- package/dist/components/other/alert-dialog.jsx +0 -64
- package/dist/components/other/alert.jsx +0 -41
- package/dist/components/other/aspect-ratio.jsx +0 -4
- package/dist/components/other/avatar.jsx +0 -31
- package/dist/components/other/badge.jsx +0 -32
- package/dist/components/other/breadcrumb.jsx +0 -57
- package/dist/components/other/button.jsx +0 -322
- package/dist/components/other/calendar.jsx +0 -43
- package/dist/components/other/card.jsx +0 -44
- package/dist/components/other/carousel.jsx +0 -140
- package/dist/components/other/chart.jsx +0 -182
- package/dist/components/other/checkbox.jsx +0 -26
- package/dist/components/other/collapsible.jsx +0 -6
- package/dist/components/other/command.jsx +0 -68
- package/dist/components/other/context-menu.jsx +0 -88
- package/dist/components/other/dialog.jsx +0 -60
- package/dist/components/other/drawer.jsx +0 -60
- package/dist/components/other/dropdown-menu.jsx +0 -90
- package/dist/components/other/form.jsx +0 -89
- package/dist/components/other/hover-card.jsx +0 -23
- package/dist/components/other/input-otp.jsx +0 -46
- package/dist/components/other/input.jsx +0 -19
- package/dist/components/other/label.jsx +0 -23
- package/dist/components/other/login-popup.jsx +0 -1
- package/dist/components/other/menubar.jsx +0 -96
- package/dist/components/other/mobile-icon.jsx +0 -11
- package/dist/components/other/navigation-menu.jsx +0 -62
- package/dist/components/other/pagination.jsx +0 -63
- package/dist/components/other/popover.jsx +0 -25
- package/dist/components/other/progress.jsx +0 -23
- package/dist/components/other/radio-group.jsx +0 -31
- package/dist/components/other/resizable.jsx +0 -29
- package/dist/components/other/scroll-area.jsx +0 -36
- package/dist/components/other/select.jsx +0 -83
- package/dist/components/other/separator.jsx +0 -21
- package/dist/components/other/sheet.jsx +0 -74
- package/dist/components/other/signup-popup.jsx +0 -1
- package/dist/components/other/skeleton.jsx +0 -17
- package/dist/components/other/slider.jsx +0 -26
- package/dist/components/other/social-icons.jsx +0 -15
- package/dist/components/other/sonner.jsx +0 -27
- package/dist/components/other/switch.jsx +0 -23
- package/dist/components/other/table.jsx +0 -56
- package/dist/components/other/tabs.jsx +0 -32
- package/dist/components/other/textarea.jsx +0 -19
- package/dist/components/other/toast.jsx +0 -58
- package/dist/components/other/toaster.jsx +0 -31
- package/dist/components/other/toggle-group.jsx +0 -41
- package/dist/components/other/toggle.jsx +0 -39
- package/dist/components/other/tooltip.jsx +0 -24
- package/dist/components/theme-provider.jsx +0 -18
- package/dist/components/types.js +0 -1
- package/dist/hooks/use-toast.js +0 -135
- package/dist/lib/auth-context.jsx +0 -144
- package/dist/lib/constants/about.js +0 -32
- package/dist/lib/constants/adRequest.js +0 -113
- package/dist/lib/constants/contact.js +0 -40
- package/dist/lib/constants/faq.js +0 -36
- package/dist/lib/constants/gallery.js +0 -42
- package/dist/lib/constants/page.js +0 -69
- package/dist/lib/constants/schedule.js +0 -71
- package/dist/lib/google-analytics.jsx +0 -148
- package/dist/lib/utils.js +0 -9
- package/dist/lib/verify-user.js +0 -142
- package/dist/middleware.js +0 -37
- package/dist/tailwind.config.js +0 -86
- package/dist/tsconfig.tsbuildinfo +0 -1
- package/lib/auth-context.tsx +0 -131
- package/lib/verify-user.ts +0 -118
|
@@ -1,71 +1,71 @@
|
|
|
1
|
-
// components/addOns/functional/contactsDashboard/constants/ContactsDashboard.ts
|
|
2
|
-
// Constants for hardcoded text in ContactsDashboard.tsx, organized by purpose
|
|
3
|
-
|
|
4
|
-
export const CONTACTS_DASHBOARD = {
|
|
5
|
-
// UI Text for ContactsDashboard
|
|
6
|
-
UI: {
|
|
7
|
-
// Section heading
|
|
8
|
-
SECTION_HEADING: "Contact Submissions",
|
|
9
|
-
// Subheading for chart section
|
|
10
|
-
CHART_SUBHEADING: "Leads Overview",
|
|
11
|
-
// Label for year filter select
|
|
12
|
-
YEAR_FILTER_LABEL: "Filter by Year:",
|
|
13
|
-
// Option in year filter select
|
|
14
|
-
ALL_YEARS_OPTION: "All Years",
|
|
15
|
-
// Chart title
|
|
16
|
-
CHART_TITLE: "New Leads by Month (Unique Emails)",
|
|
17
|
-
// Chart y-axis title
|
|
18
|
-
Y_AXIS_TITLE: "Number of Leads",
|
|
19
|
-
// Chart x-axis title
|
|
20
|
-
X_AXIS_TITLE: "Month",
|
|
21
|
-
// Chart month labels
|
|
22
|
-
MONTH_LABELS: [
|
|
23
|
-
"Jan",
|
|
24
|
-
"Feb",
|
|
25
|
-
"Mar",
|
|
26
|
-
"Apr",
|
|
27
|
-
"May",
|
|
28
|
-
"Jun",
|
|
29
|
-
"Jul",
|
|
30
|
-
"Aug",
|
|
31
|
-
"Sep",
|
|
32
|
-
"Oct",
|
|
33
|
-
"Nov",
|
|
34
|
-
"Dec",
|
|
35
|
-
],
|
|
36
|
-
// Text when no contacts exist
|
|
37
|
-
NO_CONTACTS_MESSAGE: "No valid contact submissions found.",
|
|
38
|
-
// Label for email field
|
|
39
|
-
EMAIL_LABEL: "Email:",
|
|
40
|
-
// Label for phone field
|
|
41
|
-
PHONE_LABEL: "Phone:",
|
|
42
|
-
// Label for subject field
|
|
43
|
-
SUBJECT_LABEL: "Subject:",
|
|
44
|
-
// Label for message field
|
|
45
|
-
MESSAGE_LABEL: "Message:",
|
|
46
|
-
// Label for submission date field
|
|
47
|
-
SUBMITTED_LABEL: "Submitted:",
|
|
48
|
-
},
|
|
49
|
-
// Button Text for ContactsDashboard
|
|
50
|
-
BUTTONS: {
|
|
51
|
-
// Retry button text
|
|
52
|
-
RETRY_BUTTON: "Retry",
|
|
53
|
-
// Show More button text
|
|
54
|
-
SHOW_MORE_BUTTON: "Show More",
|
|
55
|
-
// Close All button text
|
|
56
|
-
CLOSE_ALL_BUTTON: "Close All",
|
|
57
|
-
},
|
|
58
|
-
// Error Messages for ContactsDashboard
|
|
59
|
-
ERRORS: {
|
|
60
|
-
// Error when token is missing
|
|
61
|
-
NO_TOKEN: "No JWT token found from Clerk",
|
|
62
|
-
// Error when fetch fails with status code
|
|
63
|
-
FETCH_FAILED: "Failed to fetch contacts: ${response.status}",
|
|
64
|
-
// Generic error for fetch
|
|
65
|
-
UNKNOWN_ERROR: "An unknown error occurred",
|
|
66
|
-
// Error when not signed in
|
|
67
|
-
UNAUTHORIZED: "Unauthorized: Please log in to view the contacts dashboard",
|
|
68
|
-
// Error when all contacts are invalid
|
|
69
|
-
NO_VALID_CONTACTS: "No valid contact submissions found. All entries have incomplete data.",
|
|
70
|
-
},
|
|
1
|
+
// components/addOns/functional/contactsDashboard/constants/ContactsDashboard.ts
|
|
2
|
+
// Constants for hardcoded text in ContactsDashboard.tsx, organized by purpose
|
|
3
|
+
|
|
4
|
+
export const CONTACTS_DASHBOARD = {
|
|
5
|
+
// UI Text for ContactsDashboard
|
|
6
|
+
UI: {
|
|
7
|
+
// Section heading
|
|
8
|
+
SECTION_HEADING: "Contact Submissions",
|
|
9
|
+
// Subheading for chart section
|
|
10
|
+
CHART_SUBHEADING: "Leads Overview",
|
|
11
|
+
// Label for year filter select
|
|
12
|
+
YEAR_FILTER_LABEL: "Filter by Year:",
|
|
13
|
+
// Option in year filter select
|
|
14
|
+
ALL_YEARS_OPTION: "All Years",
|
|
15
|
+
// Chart title
|
|
16
|
+
CHART_TITLE: "New Leads by Month (Unique Emails)",
|
|
17
|
+
// Chart y-axis title
|
|
18
|
+
Y_AXIS_TITLE: "Number of Leads",
|
|
19
|
+
// Chart x-axis title
|
|
20
|
+
X_AXIS_TITLE: "Month",
|
|
21
|
+
// Chart month labels
|
|
22
|
+
MONTH_LABELS: [
|
|
23
|
+
"Jan",
|
|
24
|
+
"Feb",
|
|
25
|
+
"Mar",
|
|
26
|
+
"Apr",
|
|
27
|
+
"May",
|
|
28
|
+
"Jun",
|
|
29
|
+
"Jul",
|
|
30
|
+
"Aug",
|
|
31
|
+
"Sep",
|
|
32
|
+
"Oct",
|
|
33
|
+
"Nov",
|
|
34
|
+
"Dec",
|
|
35
|
+
],
|
|
36
|
+
// Text when no contacts exist
|
|
37
|
+
NO_CONTACTS_MESSAGE: "No valid contact submissions found.",
|
|
38
|
+
// Label for email field
|
|
39
|
+
EMAIL_LABEL: "Email:",
|
|
40
|
+
// Label for phone field
|
|
41
|
+
PHONE_LABEL: "Phone:",
|
|
42
|
+
// Label for subject field
|
|
43
|
+
SUBJECT_LABEL: "Subject:",
|
|
44
|
+
// Label for message field
|
|
45
|
+
MESSAGE_LABEL: "Message:",
|
|
46
|
+
// Label for submission date field
|
|
47
|
+
SUBMITTED_LABEL: "Submitted:",
|
|
48
|
+
},
|
|
49
|
+
// Button Text for ContactsDashboard
|
|
50
|
+
BUTTONS: {
|
|
51
|
+
// Retry button text
|
|
52
|
+
RETRY_BUTTON: "Retry",
|
|
53
|
+
// Show More button text
|
|
54
|
+
SHOW_MORE_BUTTON: "Show More",
|
|
55
|
+
// Close All button text
|
|
56
|
+
CLOSE_ALL_BUTTON: "Close All",
|
|
57
|
+
},
|
|
58
|
+
// Error Messages for ContactsDashboard
|
|
59
|
+
ERRORS: {
|
|
60
|
+
// Error when token is missing
|
|
61
|
+
NO_TOKEN: "No JWT token found from Clerk",
|
|
62
|
+
// Error when fetch fails with status code
|
|
63
|
+
FETCH_FAILED: "Failed to fetch contacts: ${response.status}",
|
|
64
|
+
// Generic error for fetch
|
|
65
|
+
UNKNOWN_ERROR: "An unknown error occurred",
|
|
66
|
+
// Error when not signed in
|
|
67
|
+
UNAUTHORIZED: "Unauthorized: Please log in to view the contacts dashboard",
|
|
68
|
+
// Error when all contacts are invalid
|
|
69
|
+
NO_VALID_CONTACTS: "No valid contact submissions found. All entries have incomplete data.",
|
|
70
|
+
},
|
|
71
71
|
};
|
|
@@ -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
|
+
};
|