@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,123 @@
1
+ "use client";
2
+
3
+ import { useState, useEffect } from "react";
4
+ import { useUser } from "@clerk/nextjs";
5
+ import { useStrapiAuth } from "@/lib/auth/auth-context";
6
+ import { isAdminUser } from "@/lib/auth/auth-utils";
7
+ import Spinner from "@/components/addOns/non-functional/spinner";
8
+ import { ADMIN_PROFILE_PAGE } from "@/lib/constants/admin-profile";
9
+ import { BannerDashboard } from "@/components/addOns/functional/banner/BannerDashboard";
10
+ import Banner from "@/components/addOns/functional/banner/Banner";
11
+
12
+ export default function AdminProfile() {
13
+ const { user, authLoading, checkSession } = useStrapiAuth();
14
+ const { isSignedIn } = useUser();
15
+ const [isAdmin, setIsAdmin] = useState(false);
16
+
17
+ useEffect(() => {
18
+ window.scrollTo(0, 0);
19
+ }, []);
20
+
21
+ useEffect(() => {
22
+ const handleLogin = () => checkSession();
23
+ const handleLogout = () => checkSession();
24
+ window.addEventListener("user-login", handleLogin);
25
+ window.addEventListener("user-logout", handleLogout);
26
+ return () => {
27
+ window.removeEventListener("user-login", handleLogin);
28
+ window.removeEventListener("user-logout", handleLogout);
29
+ };
30
+ }, [checkSession]);
31
+
32
+ useEffect(() => {
33
+ let isMounted = true;
34
+ const checkAdmin = async (retries = 3, delay = 1000) => {
35
+ if (!isSignedIn || !user?.authId) {
36
+ if (isMounted) setIsAdmin(false);
37
+ return;
38
+ }
39
+ for (let attempt = 1; attempt <= retries; attempt++) {
40
+ try {
41
+ const adminStatus = await isAdminUser(isSignedIn, user);
42
+ if (isMounted) {
43
+ setIsAdmin(adminStatus);
44
+ }
45
+ return;
46
+ } catch (error) {
47
+ console.error("AdminProfile: Admin check failed:", {
48
+ error: error instanceof Error ? error.message : "Unknown error",
49
+ attempt,
50
+ timestamp: new Date().toISOString(),
51
+ });
52
+ if (attempt < retries) {
53
+ await new Promise(resolve => setTimeout(resolve, delay));
54
+ } else if (isMounted) {
55
+ setIsAdmin(false);
56
+ }
57
+ }
58
+ }
59
+ };
60
+ checkAdmin();
61
+ return () => {
62
+ isMounted = false;
63
+ };
64
+ }, [isSignedIn, user]);
65
+
66
+ if (authLoading || isSignedIn === undefined) {
67
+ return (
68
+ <div className="min-h-screen bg-gray-900 text-gray-200 py-32 px-4 sm:px-6 lg:px-8 pt-48 lg:pt-56">
69
+ <div className="max-w-7xl mx-auto text-center">
70
+ <Spinner />
71
+ </div>
72
+ </div>
73
+ );
74
+ }
75
+
76
+ if (!isSignedIn || !user) {
77
+ return (
78
+ <div className="min-h-screen bg-gray-900 text-gray-200 flex items-center justify-center">
79
+ <div className="text-center">
80
+ <h1 className="text-3xl font-bold text-gray-400">{ADMIN_PROFILE_PAGE.UI.NOT_AUTHENTICATED_HEADING}</h1>
81
+ <p className="mt-4 text-gray-500">{ADMIN_PROFILE_PAGE.UI.NOT_AUTHENTICATED_MESSAGE}</p>
82
+ </div>
83
+ </div>
84
+ );
85
+ }
86
+
87
+ if (!isAdmin) {
88
+ return (
89
+ <div className="min-h-screen bg-gray-900 text-gray-200 flex items-center justify-center">
90
+ <div className="text-center">
91
+ <h1 className="text-3xl font-bold text-gray-400">{ADMIN_PROFILE_PAGE.UI.UNAUTHORIZED_HEADING}</h1>
92
+ <p className="mt-4 text-gray-500">{ADMIN_PROFILE_PAGE.UI.UNAUTHORIZED_MESSAGE}</p>
93
+ </div>
94
+ </div>
95
+ );
96
+ }
97
+
98
+ return (
99
+ <div className="min-h-screen bg-gray-900 text-gray-200 py-32 px-4 sm:px-6 lg:px-8 pt-48 lg:pt-56">
100
+ <Banner />
101
+ <div className="max-w-7xl mx-auto">
102
+ <header className="mb-12 relative">
103
+ <div className="absolute inset-0 bg-gradient-to-r from-blue-500/10 to-gray-800/20 rounded-full blur-3xl"></div>
104
+ <h1 className="text-4xl sm:text-5xl font-extrabold tracking-tight bg-clip-text text-transparent bg-gradient-to-r from-blue-400 to-gray-300 p-2">
105
+ {ADMIN_PROFILE_PAGE.UI.DASHBOARD_HEADING}
106
+ </h1>
107
+ <p className="mt-2 text-sm sm:text-base text-gray-400">
108
+ {ADMIN_PROFILE_PAGE.UI.DASHBOARD_SUBHEADING}
109
+ </p>
110
+ </header>
111
+
112
+ <div className="text-center">
113
+ <p className="text-lg text-gray-300">{ADMIN_PROFILE_PAGE.UI.WELCOME_MESSAGE}</p>
114
+ <p className="mt-4 text-gray-400">{ADMIN_PROFILE_PAGE.UI.CONTENT_MESSAGE}</p>
115
+ </div>
116
+
117
+ <div className="mt-8">
118
+ <BannerDashboard />
119
+ </div>
120
+ </div>
121
+ </div>
122
+ );
123
+ }
@@ -1,7 +1,7 @@
1
- 'use client';
1
+ "use client";
2
2
 
3
3
  import { useState, useEffect, useMemo } from 'react';
4
- import { useStrapiAuth } from '@/lib/auth-context';
4
+ import { useStrapiAuth } from '@/lib/auth/auth-context';
5
5
  import { useAuth } from '@clerk/nextjs';
6
6
  import {
7
7
  AnalyticsData,
@@ -12,11 +12,12 @@ import {
12
12
  getUserActivityData,
13
13
  RealtimeData,
14
14
  UserActivityData,
15
- } from '@/lib/google-analytics';
15
+ } from '@/lib/google/google-analytics';
16
+ import { isAdminUser } from '@/lib/auth/auth-utils';
16
17
  import { NewUsersChart } from '@/components/addOns/functional/NewUserAnalytics';
17
18
  import ContactsDashboard from '@/components/addOns/functional/contactsDashboard/ContactsDashboard';
18
19
  import Spinner from '@/components/addOns/non-functional/spinner';
19
- import { ExternalLinkButton, RetryButton } from '@/components/other/button';
20
+ import { ExternalLinkButton, RetryButton } from '@/components/other/button';
20
21
 
21
22
  interface StrapiUser {
22
23
  id: number;
@@ -60,10 +61,45 @@ export default function AnalyticsPage() {
60
61
  const [error, setError] = useState<string | null>(null);
61
62
  const [isLoading, setIsLoading] = useState(true);
62
63
  const [hasSynced, setHasSynced] = useState(false);
64
+ const [isAdmin, setIsAdmin] = useState(false);
63
65
 
64
66
  const memoizedUser = useMemo(() => user, [user?.authId, user?.businessAdminId, user?.userRole]);
65
67
 
66
- const isAdmin = isSignedIn && !!memoizedUser?.businessAdminId && memoizedUser?.userRole === 'admin';
68
+ useEffect(() => {
69
+ let isMounted = true;
70
+ const checkAdmin = async (retries = 3, delay = 1000) => {
71
+ if (!isSignedIn || !user?.authId) {
72
+ if (isMounted) setIsAdmin(false);
73
+ return;
74
+ }
75
+ for (let attempt = 1; attempt <= retries; attempt++) {
76
+ try {
77
+ const adminStatus = await isAdminUser(isSignedIn, user);
78
+ if (isMounted) {
79
+ setIsAdmin(adminStatus);
80
+ setError(null);
81
+ }
82
+ return;
83
+ } catch (error) {
84
+ console.error("AnalyticsPage: Admin check failed:", {
85
+ error: error instanceof Error ? error.message : "Unknown error",
86
+ attempt,
87
+ timestamp: new Date().toISOString(),
88
+ });
89
+ if (attempt < retries) {
90
+ await new Promise(resolve => setTimeout(resolve, delay));
91
+ } else if (isMounted) {
92
+ setIsAdmin(false);
93
+ setError("Failed to verify admin status");
94
+ }
95
+ }
96
+ }
97
+ };
98
+ checkAdmin();
99
+ return () => {
100
+ isMounted = false;
101
+ };
102
+ }, [isSignedIn, user]);
67
103
 
68
104
  const fetchAnalyticsData = async () => {
69
105
  if (!isAdmin) {
@@ -1,24 +1,8 @@
1
1
  import { NextRequest, NextResponse } from "next/server";
2
2
  import { getAuth } from "@clerk/nextjs/server";
3
+ import { StrapiUser, AboutContent } from "@/lib/types";
3
4
 
4
- export const revalidate = 0;
5
-
6
- interface StrapiUser {
7
- id: number;
8
- username: string;
9
- email: string;
10
- businessAdminId?: string;
11
- }
12
-
13
- interface AboutContent {
14
- id: number;
15
- documentId: string;
16
- title: string;
17
- description: string;
18
- about: boolean;
19
- image?: { url: string; id: number };
20
- createdAt: string;
21
- }
5
+ export const revalidate = 0;
22
6
 
23
7
  const CONTENT_API_URL =
24
8
  process.env.STRAPI_CONTENT_API_URL_ABOUT || process.env.STRAPI_CONTENT_API_URL || "";
@@ -1,27 +1,7 @@
1
1
  import { NextRequest, NextResponse } from "next/server";
2
2
  import { getAuth } from "@clerk/nextjs/server";
3
3
  import nodemailer from "nodemailer";
4
-
5
- interface StrapiUser {
6
- id: number;
7
- username: string;
8
- email: string;
9
- businessAdminId?: string;
10
- businessTitle?: string;
11
- }
12
-
13
- interface AdRequest {
14
- id: number;
15
- documentId: string;
16
- platform: string;
17
- adType: string;
18
- description: string;
19
- timestamp: string;
20
- userId: string;
21
- credits: number;
22
- lastResetDate: string;
23
- nextResetDate: string;
24
- }
4
+ import { StrapiUser, AdRequest } from "@/lib/types";
25
5
 
26
6
  const AD_REQUEST_API_URL = process.env.STRAPI_AD_REQUEST_API_URL || "";
27
7
  const STRAPI_USER_LIST_API_URL = process.env.STRAPI_USER_LIST_API_URL || "";
@@ -315,12 +295,12 @@ export async function POST(request: NextRequest) {
315
295
  padding: 20px;
316
296
  text-align: center;
317
297
  }
318
- .header .business-name {
319
- color: white;
320
- font-size: 28px;
321
- font-weight: bold;
322
- margin-bottom: 10px;
323
- }
298
+ .header .business-name {
299
+ color: white;
300
+ font-size: 28px;
301
+ font-weight: bold;
302
+ margin-bottom: 10px;
303
+ }
324
304
  .header h1 {
325
305
  color: #ffffff;
326
306
  font-size: 24px;
@@ -1,70 +1,7 @@
1
1
  import { NextRequest, NextResponse } from "next/server";
2
2
  import { BetaAnalyticsDataClient } from "@google-analytics/data";
3
3
  import { getAuth } from "@clerk/nextjs/server";
4
-
5
- interface StrapiUser {
6
- id: number;
7
- username: string;
8
- email: string;
9
- authId: string;
10
- authProvider: string;
11
- businessAdminId?: string;
12
- userRole?: string;
13
- firstName?: string;
14
- lastName?: string;
15
- businessId?: string[] | null;
16
- dateJoined?: string;
17
- businessOwner?: boolean;
18
- userStatus?: string;
19
- timezone?: string | null;
20
- language?: string | null;
21
- isVerified?: boolean;
22
- businessTitle?: string | null;
23
- userTitle?: string | null;
24
- number?: string | null;
25
- address?: {
26
- zip: string;
27
- city: string;
28
- state: string;
29
- street: string;
30
- country: string;
31
- } | null;
32
- websiteUrl?: string | null;
33
- primaryBusinessColor?: string | null;
34
- secondaryBusinessColor?: string | null;
35
- logoImage?: string | null;
36
- }
37
-
38
- interface AnalyticsData {
39
- date: string;
40
- country: string;
41
- deviceCategory: string;
42
- activeUsers: number;
43
- totalUsers: number;
44
- newUsers: number;
45
- pageViews: number;
46
- engagedSessions: number;
47
- averageSessionDuration: number;
48
- averageEngagementTimePerActiveUser: number;
49
- bounceRate: number;
50
- }
51
-
52
- interface ChannelGroupData {
53
- date: string;
54
- channelGroup: string;
55
- newUsers: number;
56
- activeUsers: number;
57
- }
58
-
59
- interface RealtimeData {
60
- activeUsersInLast30Minutes: number;
61
- }
62
-
63
- interface UserActivityData {
64
- date: string;
65
- country: string;
66
- activeUsers: number;
67
- }
4
+ import { StrapiUser, AnalyticsData, ChannelGroupData, RealtimeData, UserActivityData } from "@/lib/types";
68
5
 
69
6
  const PROPERTY_ID = `properties/${process.env.GOOGLE_ANALYTICS_PROPERTY_ID}`;
70
7
  const STRAPI_USER_LIST_API_URL = process.env.STRAPI_USER_LIST_API_URL || "";
@@ -1,25 +1,9 @@
1
1
  import { NextRequest, NextResponse } from "next/server";
2
2
  import { getAuth } from "@clerk/nextjs/server";
3
+ import { StrapiUser, BioContent } from "@/lib/types";
3
4
 
4
5
  export const revalidate = 0; // Disable Next.js ISR
5
6
 
6
- interface StrapiUser {
7
- id: number;
8
- username: string;
9
- email: string;
10
- businessAdminId?: string;
11
- }
12
-
13
- interface BioContent {
14
- id: number;
15
- documentId: string;
16
- title: string;
17
- description: string;
18
- bio: boolean;
19
- image?: { url: string; id: number };
20
- createdAt: string;
21
- }
22
-
23
7
  const CONTENT_API_URL = process.env.STRAPI_CONTENT_API_URL || "";
24
8
  const UPLOAD_API_URL = process.env.STRAPI_API_URL + "/api/upload" || "";
25
9
  const BASE_URL = process.env.STRAPI_API_URL || "";
@@ -1,23 +1,6 @@
1
1
  import { NextRequest, NextResponse } from "next/server";
2
2
  import { getAuth } from "@clerk/nextjs/server";
3
-
4
- interface StrapiUser {
5
- id: number;
6
- username: string;
7
- email: string;
8
- businessAdminId?: string;
9
- firstName?: string;
10
- lastName?: string;
11
- }
12
-
13
- interface BlogPost {
14
- id: number;
15
- documentId: string;
16
- title: string;
17
- description: string;
18
- author: string;
19
- publishedAt: string;
20
- }
3
+ import { StrapiUser, BlogPost } from "@/lib/types";
21
4
 
22
5
  const BLOG_API_URL = process.env.STRAPI_BLOG_API_URL || "";
23
6
  const STRAPI_USER_LIST_API_URL = process.env.STRAPI_USER_LIST_API_URL || "";
@@ -188,7 +171,6 @@ export async function POST(request: NextRequest) {
188
171
  }
189
172
  }
190
173
 
191
- // api/blog.ts
192
174
  export async function PUT(request: NextRequest) {
193
175
  const { userId } = getAuth(request);
194
176
  if (!userId) {
@@ -1,51 +1,6 @@
1
1
  import { NextRequest, NextResponse } from "next/server";
2
2
  import { getAuth } from "@clerk/nextjs/server";
3
-
4
- interface StrapiUser {
5
- id: number;
6
- documentId?: string;
7
- authId: string;
8
- authProvider: string;
9
- email: string;
10
- username: string;
11
- businessAdminId?: string;
12
- userRole?: string;
13
- firstName?: string;
14
- lastName?: string;
15
- businessId?: string[] | null;
16
- dateJoined?: string;
17
- businessOwner?: boolean;
18
- userStatus?: string;
19
- timezone?: string | null;
20
- language?: string | null;
21
- isVerified?: boolean;
22
- businessTitle?: string | null;
23
- userTitle?: string | null;
24
- number?: string | null;
25
- address?: {
26
- zip: string;
27
- city: string;
28
- state: string;
29
- street: string;
30
- country: string;
31
- } | null;
32
- websiteUrl?: string | null;
33
- primaryBusinessColor?: string | null;
34
- secondaryBusinessColor?: string | null;
35
- logoImage?: string | null;
36
- }
37
-
38
- interface StrapiContact {
39
- id: number;
40
- documentId: string;
41
- name: string;
42
- lastName: string;
43
- email: string;
44
- phone: string;
45
- subject: string;
46
- message: string;
47
- submissionDate: string;
48
- }
3
+ import { StrapiUser, StrapiContact } from "@/lib/types";
49
4
 
50
5
  async function verifyUser(request: NextRequest): Promise<StrapiUser | null> {
51
6
  const { userId } = getAuth(request);
@@ -1,23 +1,9 @@
1
1
  import { NextRequest, NextResponse } from "next/server";
2
2
  import { getAuth } from "@clerk/nextjs/server";
3
+ import { StrapiUser, UploadedFile } from "@/lib/types";
3
4
 
4
5
  export const revalidate = 0; // Disable Next.js ISR
5
6
 
6
- interface StrapiUser {
7
- id: number;
8
- username: string;
9
- email: string;
10
- businessAdminId?: string;
11
- }
12
-
13
- interface UploadedFile {
14
- id: number;
15
- documentId: string;
16
- name: string;
17
- url: string;
18
- createdAt: string;
19
- }
20
-
21
7
  const DOC_API_URL = process.env.STRAPI_DOC_API_URL || "";
22
8
  const BASE_URL = process.env.STRAPI_API_URL || "";
23
9
  const UPLOAD_API_URL = `${BASE_URL}/api/upload`;