diff --git a/app/auth/page.tsx b/app/auth/page.tsx
index 3fe7048f..dfe81e0f 100644
--- a/app/auth/page.tsx
+++ b/app/auth/page.tsx
@@ -26,6 +26,13 @@ function AuthContent() {
const authError = searchParams.get('error');
const authSuccess = searchParams.get('success');
+ // Redirect if user is already logged in
+ useEffect(() => {
+ if (user) {
+ router.replace(redirectTo);
+ }
+ }, [user, router, redirectTo]);
+
// Check if Supabase is configured
const supabaseUrl = process.env.NEXT_PUBLIC_SUPABASE_URL;
const supabaseAnonKey = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY;
@@ -69,7 +76,7 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=your_key`}
);
}
- if (loading || user) {
+ if (loading) {
return (
diff --git a/app/layout.tsx b/app/layout.tsx
index 9a1df371..58937ff9 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -12,7 +12,6 @@ import { Analytics } from '@vercel/analytics/react';
import './globals.css';
import { CookieConsentBanner } from '@/components/ui/cookie-consent-banner';
import { QueryProvider } from '@/lib/query-client';
-import { AnalyticsScripts } from '@/components/analytics-scripts';
export const viewport: Viewport = {
themeColor: '#0D0D10',
@@ -22,34 +21,62 @@ export const viewport: Viewport = {
};
export const metadata: Metadata = {
- title: 'ZapDev - Build Amazing Apps with AI',
+ metadataBase: new URL('https://zapdev.link'),
+ title: {
+ default: 'ZapDev: AI-Powered Development Platform to Build and Deploy Web Apps',
+ template: '%s | ZapDev',
+ },
description:
- 'The most powerful AI-driven development platform. Generate, preview, and deploy beautiful applications in seconds.',
+ 'ZapDev is an innovative AI-driven platform that empowers you to generate, preview, and deploy stunning web applications in seconds. Turn your ideas into reality with our cutting-edge AI assistant and WebContainer technology.',
generator: 'ZapDev',
applicationName: 'ZapDev',
- keywords: ['AI development', 'web apps', 'code generation', 'WebContainer', 'AI assistant'],
- authors: [{ name: 'ZapDev Team' }],
- metadataBase: new URL('https://zapdev.link'),
+ keywords: [
+ 'AI development',
+ 'web apps',
+ 'code generation',
+ 'WebContainer',
+ 'AI assistant',
+ 'AI developer tools',
+ 'no-code',
+ 'low-code',
+ 'application builder',
+ 'deploy web apps',
+ 'AI website builder',
+ ],
+ authors: [{ name: 'ZapDev Team', url: 'https://zapdev.link' }],
+ creator: 'ZapDev',
+ publisher: 'ZapDev',
openGraph: {
- type: 'website',
- locale: 'en_US',
- title: 'ZapDev - Build Amazing Apps with AI',
+ title: 'ZapDev: AI-Powered Development Platform',
description:
- 'The most powerful AI-driven development platform. Generate, preview, and deploy beautiful applications in seconds.',
+ 'Generate, preview, and deploy stunning web applications in seconds with our AI-driven development platform.',
+ url: 'https://zapdev.link',
siteName: 'ZapDev',
+ locale: 'en_US',
+ type: 'website',
},
twitter: {
card: 'summary_large_image',
- title: 'ZapDev - Build Amazing Apps with AI',
+ title: 'ZapDev: AI-Powered Development Platform',
description:
- 'The most powerful AI-driven development platform. Generate, preview, and deploy beautiful applications in seconds.',
+ 'Generate, preview, and deploy stunning web applications in seconds with our AI-driven development platform.',
+ },
+ robots: {
+ index: true,
+ follow: true,
+ googleBot: {
+ index: true,
+ follow: true,
+ 'max-video-preview': -1,
+ 'max-image-preview': 'large',
+ 'max-snippet': -1,
+ },
},
icons: {
+ icon: '/favicon.svg',
shortcut: '/favicon.svg',
},
- formatDetection: {
- telephone: false,
- },
+ manifest: '/site.webmanifest',
};
const inter = Inter({
@@ -60,8 +87,6 @@ const inter = Inter({
fallback: ['system-ui', 'arial'],
});
-
-
export default function RootLayout({
children,
}: Readonly<{
@@ -71,6 +96,18 @@ export default function RootLayout({
+
-
diff --git a/app/page.tsx b/app/page.tsx
index 8822831c..766b7db3 100644
--- a/app/page.tsx
+++ b/app/page.tsx
@@ -8,6 +8,7 @@ import Hero from '@/components/hero';
import FinalCTA from '@/components/final-cta';
import Pricing from '@/components/pricing';
import { useSupabase } from '@/components/SupabaseProvider';
+import { AuthButtons } from '@/components/auth-buttons';
import { CrossBrowserButton } from '@/components/ui/cross-browser-button';
const FeaturesShowcase = dynamic(() => import('@/components/features-showcase'), {
@@ -55,7 +56,6 @@ export default function Home() {
const [showFloatingCTA, setShowFloatingCTA] = useState(false);
const [mounted, setMounted] = useState(false);
const { scrollY } = useScroll();
- const { user, loading } = useSupabase();
// Ensure component is mounted before complex interactions
useEffect(() => {
@@ -89,111 +89,6 @@ export default function Home() {
return () => unsubscribe();
}, [scrollY, showFloatingCTA, mounted]);
- // Simple auth buttons that don't cause re-renders
- const AuthButtons = () => {
- if (loading) {
- return (
-
- );
- }
-
- if (user) {
- return (
-
-
- Chat
-
-
- Subscribe
-
-
- );
- }
-
- return (
-
-
- Sign In
-
-
- Sign Up
-
-
- );
- };
-
- // Simplified floating CTA
- const FloatingCTA = () => {
- const buttonText = user ? 'Go to Chat' : 'Sign In';
- const targetRoute = user ? '/chat' : '/auth';
-
- return (
-
router.push(targetRoute)}
- className="cross-browser-button gradient-button-primary flex items-center gap-2 rounded-full px-6 py-3 font-medium text-white shadow-lg shadow-purple-900/20"
- motionProps={{
- whileHover: { scale: 1.05 },
- whileTap: { scale: 0.95 }
- }}
- >
- {buttonText}
-
-
- );
- };
-
// Don't render complex interactions until mounted
if (!mounted) {
return (
@@ -204,74 +99,103 @@ export default function Home() {
}
return (
-
- {/* Auth buttons */}
-
+
-
-
+
+ {/* Debug: Simple static buttons to confirm they show */}
+
+
+
+
+ {/* Uncomment this after we confirm the basic buttons work */}
+ {/* */}
+
+
- {/* Main CTA button */}
+ {/* Floating CTA */}
- {/* Floating CTA for subscribing */}
- {showFloatingCTA && (
-
-
- {user ? 'Subscribe' : 'Sign Up'}
-
-
-
- )}
-
- {/* Main content */}
-
+
-
-
+
+
);
}
+
+// Simplified floating CTA
+const FloatingCTA = () => {
+ const router = useRouter();
+ const { user } = useSupabase();
+ const buttonText = user ? 'Go to Chat' : 'Sign In';
+ const targetRoute = user ? '/chat' : '/auth';
+
+ return (
+
router.push(targetRoute)}
+ className="cross-browser-button gradient-button-primary flex items-center gap-2 rounded-full px-6 py-3 font-medium text-white shadow-lg shadow-purple-900/20"
+ motionProps={{
+ whileHover: { scale: 1.05 },
+ whileTap: { scale: 0.95 },
+ }}
+ >
+ {buttonText}
+
+
+ );
+};
diff --git a/app/sitemap.ts b/app/sitemap.ts
new file mode 100644
index 00000000..6f4f54b0
--- /dev/null
+++ b/app/sitemap.ts
@@ -0,0 +1,15 @@
+import { MetadataRoute } from 'next';
+
+export default function sitemap(): MetadataRoute.Sitemap {
+ const baseUrl = 'https://zapdev.link';
+
+ // Update this list with your actual pages
+ const staticPages = ['/', '/auth', '/pricing', '/chat'];
+
+ return staticPages.map((path) => ({
+ url: `${baseUrl}${path}`,
+ lastModified: new Date(),
+ changeFrequency: 'weekly',
+ priority: path === '/' ? 1 : 0.8,
+ }));
+}
\ No newline at end of file
diff --git a/components/auth-buttons.tsx b/components/auth-buttons.tsx
index bcd398d5..0988cee4 100644
--- a/components/auth-buttons.tsx
+++ b/components/auth-buttons.tsx
@@ -1,86 +1,92 @@
'use client';
+import { useRouter } from 'next/navigation';
+import { motion } from 'framer-motion';
import { useSupabase } from '@/components/SupabaseProvider';
-import { Button } from '@/components/ui/button';
import { CrossBrowserButton } from '@/components/ui/cross-browser-button';
-import { useState, useEffect } from 'react';
-import { AUTH_COOKIES, hasAuthCookies } from '@/lib/auth-constants';
-import { errorLogger, ErrorCategory } from '@/lib/error-logger';
export function AuthButtons() {
- const { user, loading, signOut } = useSupabase();
- const [isLoading, setIsLoading] = useState(false);
- const [cookieAuth, setCookieAuth] = useState
(null);
+ const router = useRouter();
+ const { user, loading } = useSupabase();
- // Quick cookie check for instant feedback
- useEffect(() => {
- if (typeof window !== 'undefined') {
- setCookieAuth(hasAuthCookies(document.cookie));
- }
- }, []);
+ const goToPricingPage = () => {
+ router.push('/pricing');
+ };
- const handleSignOut = async () => {
- setIsLoading(true);
- try {
- await signOut();
- setCookieAuth(false); // Update cookie state immediately
- } catch (error) {
- errorLogger.error(ErrorCategory.GENERAL, 'Sign out error:', error);
- } finally {
- setIsLoading(false);
- }
+ const goToAuth = () => {
+ router.push('/auth');
};
- // Use user data if available, otherwise fall back to cookie check
- const isAuthenticated = !loading && user !== undefined ? !!user : cookieAuth;
+ const goToChat = () => {
+ router.push('/chat');
+ };
- // Show loading placeholder only if we have no information at all
- if (loading && cookieAuth === null) {
+ if (loading) {
return (
-
-
-
+
);
}
- if (isAuthenticated) {
+ if (user) {
return (
-
-
{user?.email || 'Authenticated'}
+
+
+ Chat
+
- {isLoading ? 'Signing out...' : 'Sign Out'}
+ Subscribe
);
}
return (
-
+
(window.location.href = '/auth')}
- className="cross-browser-button text-zinc-300 hover:bg-zinc-800 hover:text-white"
+ onClick={goToAuth}
+ className="cross-browser-button gradient-button-primary rounded-lg px-4 py-2 text-sm font-medium"
motionProps={{
- whileHover: { scale: 1.02 },
- whileTap: { scale: 0.98 }
+ initial: { opacity: 0, y: -10 },
+ animate: { opacity: 1, y: 0 },
+ transition: { duration: 0.3, delay: 0.1 },
+ whileHover: { scale: 1.05, y: -2 },
+ whileTap: { scale: 0.95 },
}}
>
- Login
+ Sign In
(window.location.href = '/auth')}
- className="cross-browser-button gradient-button-primary"
+ onClick={goToAuth}
+ className="cross-browser-button gradient-button-secondary rounded-lg px-4 py-2 text-sm font-medium"
motionProps={{
- whileHover: { scale: 1.02 },
- whileTap: { scale: 0.98 }
+ initial: { opacity: 0, y: -10 },
+ animate: { opacity: 1, y: 0 },
+ transition: { duration: 0.3, delay: 0.2 },
+ whileHover: { scale: 1.05, y: -2 },
+ whileTap: { scale: 0.95 },
}}
>
Sign Up
diff --git a/middleware.ts b/middleware.ts
index 2f7f7d72..d381fd02 100644
--- a/middleware.ts
+++ b/middleware.ts
@@ -83,28 +83,18 @@ export async function middleware(request: NextRequest) {
} = await supabase.auth.getUser()
const { pathname } = request.nextUrl
- const isAuthPage = pathname === '/auth'
- // If the user is on the auth page, allow it to be rendered
- if (isAuthPage) {
- // But if they are logged in, redirect them away from the auth page
- if (user) {
- const nextUrl = request.nextUrl.searchParams.get('next')
- const redirectTo = nextUrl && nextUrl.startsWith('/') ? nextUrl : '/chat'
- const url = new URL(redirectTo, request.url)
- url.searchParams.delete('next')
- return NextResponse.redirect(url)
- }
- return response
- }
-
- // If user is not logged in and tries to access chat, redirect to auth page
- // preserving the originally requested path
+ // Protect the /chat route
if (!user && pathname.startsWith('/chat')) {
const url = new URL('/auth', request.url)
- url.searchParams.set('next', pathname)
+ url.searchParams.set('redirectTo', pathname)
return NextResponse.redirect(url)
}
+
+ // If a logged-in user tries to access the /auth page, redirect them to /chat
+ if (user && pathname === '/auth') {
+ return NextResponse.redirect(new URL('/chat', request.url))
+ }
return response
}
diff --git a/next.config.mjs b/next.config.mjs
index 53b94412..0a4eda69 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -90,39 +90,23 @@ const nextConfig = {
async headers() {
const isDev = process.env.NODE_ENV === 'development';
- // More permissive CSP for development, strict for production
- const csp = isDev
- ? [
- "default-src 'self' 'unsafe-inline' 'unsafe-eval'",
- "script-src 'self' 'unsafe-inline' 'unsafe-eval' *",
- "connect-src 'self' * ws: wss:",
- "img-src 'self' data: blob: *",
- "style-src 'self' 'unsafe-inline' *",
- "frame-src 'self' *",
- "font-src 'self' data: *",
- "worker-src 'self' blob:",
- "child-src 'self' blob:",
- ].join('; ')
- : [
- "default-src 'self'",
- "script-src 'self' 'unsafe-eval' 'unsafe-inline' https://funky-humpback-59.clerk.accounts.dev https://js.clerk.com https://clerk.accounts.dev https://cdn.jsdelivr.net https://plausible.io https://cloud.umami.is https://cdn.databuddy.cc",
- "connect-src 'self' http://localhost:* https://api.github.com https://funky-humpback-59.clerk.accounts.dev https://cdn.jsdelivr.net wss://original-meerkat-657.convex.cloud https://fonts.googleapis.com https://fonts.gstatic.com https://*.supabase.co https://*.supabase.com wss://*.supabase.co wss://*.supabase.com https://plausible.io https://cloud.umami.is https://cdn.databuddy.cc",
- "img-src 'self' data: blob:",
- "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
- "frame-src 'self' https://clerk.accounts.dev",
- "font-src 'self' data: https://cdn.jsdelivr.net https://fonts.gstatic.com",
- "worker-src 'self' blob:",
- "child-src 'self' blob:",
- ].join('; ');
-
return [
- // Only apply CSP in production or when explicitly enabled
+ // Disable CSP in development to prevent blocking issues
...(isDev ? [] : [{
source: '/:path*',
headers: [
{
key: 'Content-Security-Policy',
- value: csp,
+ value: [
+ "default-src 'self'",
+ "script-src 'self' 'unsafe-eval' 'unsafe-inline' https://cdn.jsdelivr.net https://plausible.io https://cloud.umami.is https://cdn.databuddy.cc",
+ "connect-src 'self' https://*.supabase.co https://*.supabase.com wss://*.supabase.co wss://*.supabase.com https://plausible.io https://cloud.umami.is https://cdn.databuddy.cc",
+ "img-src 'self' data: blob:",
+ "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
+ "font-src 'self' data: https://fonts.gstatic.com",
+ "worker-src 'self' blob:",
+ "child-src 'self' blob:",
+ ].join('; '),
},
],
}]),
diff --git a/public/robots.txt b/public/robots.txt
new file mode 100644
index 00000000..b0629da5
--- /dev/null
+++ b/public/robots.txt
@@ -0,0 +1,9 @@
+# Allow all user agents to crawl the site
+User-agent: *
+Allow: /
+
+# Disallow crawling of specific routes that don't provide value to search engines
+Disallow: /auth/callback
+
+# Point to the sitemap
+Sitemap: https://zapdev.link/sitemap.xml
\ No newline at end of file
diff --git a/public/site.webmanifest b/public/site.webmanifest
new file mode 100644
index 00000000..b9eaa750
--- /dev/null
+++ b/public/site.webmanifest
@@ -0,0 +1,16 @@
+{
+ "name": "ZapDev",
+ "short_name": "ZapDev",
+ "description": "AI-Powered Development Platform",
+ "start_url": "/",
+ "display": "standalone",
+ "background_color": "#0D0D10",
+ "theme_color": "#6C52A0",
+ "icons": [
+ {
+ "src": "/favicon.svg",
+ "sizes": "any",
+ "type": "image/svg+xml"
+ }
+ ]
+}
\ No newline at end of file