From 8e0bbd58d97b44697feb306b9ef1ac1b861e1474 Mon Sep 17 00:00:00 2001 From: Guillermo Pages Date: Fri, 7 Nov 2025 08:12:06 +0100 Subject: [PATCH] feat(landing+auth): add marketing landing page and admin auth guards Per Chief Cole's guidance (Chief_Cole-20251107080515), enhanced manager portal with showcase landing and auth protection. Changes: 1. Marketing Landing Page: - Hero section with gradient backgrounds and value props - Feature tiles showcasing club management, scheduling, booking insights - CTA buttons linking to login - Marketing copy focused on venue admin benefits - Responsive design matching consumer app style 2. Auth Guards for /admin Routes: - Created AdminAuthGuard component using useSwissOIDAuth hook - Redirects unauthenticated users to login immediately - Shows loading state during auth check - Preserves locale in redirect flow - Wrapped all /admin/clubs pages with guard 3. Protected Routes: - /admin/clubs - guarded - /admin/clubs/[club_id] - guarded - / - public landing (no guard needed) Result: Unauthenticated users see marketing showcase at root, and are redirected to login if they try to access /admin routes directly. Refs: docs/owners/Frontend_Faye-needs-to-read-from-Chief_Cole-20251107080515.md --- package-lock.json | 4 +- .../[locale]/admin/clubs/[club_id]/page.tsx | 7 +- src/app/[locale]/admin/clubs/page.tsx | 7 +- src/app/[locale]/page.tsx | 172 ++++++++++++++---- src/components/AdminAuthGuard.tsx | 56 ++++++ 5 files changed, 208 insertions(+), 38 deletions(-) create mode 100644 src/components/AdminAuthGuard.tsx diff --git a/package-lock.json b/package-lock.json index b715a9d..f77294b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "playchoo", + "name": "playchoo-manager", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "playchoo", + "name": "playchoo-manager", "version": "0.0.0", "dependencies": { "@dnd-kit/core": "^6.3.1", diff --git a/src/app/[locale]/admin/clubs/[club_id]/page.tsx b/src/app/[locale]/admin/clubs/[club_id]/page.tsx index 41c5935..81c086b 100644 --- a/src/app/[locale]/admin/clubs/[club_id]/page.tsx +++ b/src/app/[locale]/admin/clubs/[club_id]/page.tsx @@ -1,4 +1,5 @@ import AdminClubDetailComponent from './AdminClubDetail'; +import AdminAuthGuard from '@/src/components/AdminAuthGuard'; export default async function AdminClubDetailPage({ params @@ -8,5 +9,9 @@ export default async function AdminClubDetailPage({ const { club_id } = await params; const clubId = parseInt(club_id, 10); - return ; + return ( + + + + ); } diff --git a/src/app/[locale]/admin/clubs/page.tsx b/src/app/[locale]/admin/clubs/page.tsx index 84c7229..5710c09 100644 --- a/src/app/[locale]/admin/clubs/page.tsx +++ b/src/app/[locale]/admin/clubs/page.tsx @@ -1,5 +1,10 @@ import AdminClubsList from './AdminClubsList'; +import AdminAuthGuard from '@/src/components/AdminAuthGuard'; export default async function AdminClubsPage() { - return ; + return ( + + + + ); } diff --git a/src/app/[locale]/page.tsx b/src/app/[locale]/page.tsx index 95df82d..e9c6350 100644 --- a/src/app/[locale]/page.tsx +++ b/src/app/[locale]/page.tsx @@ -1,5 +1,5 @@ import Link from 'next/link'; -import { Building, Users, Calendar } from 'lucide-react'; +import { Building, Users, Calendar, ArrowRight, Zap, BarChart3, Clock, Shield, CheckCircle2 } from 'lucide-react'; import { Locale } from '@/i18n-config'; import { getTranslate } from './dictionaries'; @@ -7,64 +7,168 @@ export default async function AdminHome({ params }: { params: Promise<{ locale: const { locale } = await params; const {t} = await getTranslate(locale); + // Auth backend URL for login CTA + const loginUrl = process.env.NEXT_PUBLIC_AUTH_BACKEND_URL + ? `${process.env.NEXT_PUBLIC_AUTH_BACKEND_URL}/login` + : '/login'; + return (
-
+ {/* Hero Section */} +
+ {/* Background decorations */}
-
-
+
+

- {t('Venue Management')} + {t('Manage Your Venue.')}
- {t('Admin Portal')} + {t('Effortlessly.')}

-

- {t('Manage your clubs, courts, schedules, and bookings all in one place.')} +

+ {t('Streamline court scheduling, track bookings, and optimize operations—all from one powerful dashboard.')} +

+ + {/* CTA Buttons */} +
+ + {t('Get Started')} + + + + {t('Learn More')} + +
+ + {/* Value Props */} +
+
+ + {t('Real-time updates')} +
+
+ + {t('Analytics')} +
+
+ + {t('Save hours')} +
+
+ + {t('Secure')} +
+
+
+
+
+ + {/* Features Section */} +
+
+
+

+ {t('Everything you need to run your venue')} +

+

+ {t('From court management to booking insights, all the tools you need in one place.')}

-
- -
-
- -
-

{t('Clubs')}

-

{t('View and manage your venue locations')}

+
+
+
+
- +

{t('Club Management')}

+

+ {t('Manage multiple venues, courts, and facilities from a single dashboard. View real-time availability and status.')} +

+
    +
  • + + {t('Multi-venue support')} +
  • +
  • + + {t('Court configuration')} +
  • +
+
-
-
-
- -
-

{t('Schedules')}

-

{t('Coming soon')}

+
+
+
+

{t('Schedule Control')}

+

+ {t('Create flexible schedules, manage slot availability, and automate recurring bookings with ease.')} +

+
    +
  • + + {t('Automated scheduling')} +
  • +
  • + + {t('Recurring patterns')} +
  • +
-
-
-
- -
-

{t('Bookings')}

-

{t('Coming soon')}

+
+
+
+

{t('Booking Insights')}

+

+ {t('Track reservations, analyze usage patterns, and optimize your venue operations with detailed analytics.')} +

+
    +
  • + + {t('Usage analytics')} +
  • +
  • + + {t('Performance metrics')} +
  • +
+ + {/* CTA Section */} +
+
+

+ {t('Ready to streamline your venue operations?')} +

+

+ {t('Join venue managers who trust Playchoo to run their facilities efficiently.')} +

+ + {t('Access Your Dashboard')} + + +
+
); } diff --git a/src/components/AdminAuthGuard.tsx b/src/components/AdminAuthGuard.tsx new file mode 100644 index 0000000..871c89e --- /dev/null +++ b/src/components/AdminAuthGuard.tsx @@ -0,0 +1,56 @@ +'use client'; + +import { useEffect, ReactNode } from 'react'; +import { useRouter } from 'next/navigation'; +import { useSwissOIDAuth } from 'swissoid-front'; +import { Loader2 } from 'lucide-react'; +import useTranslation from '@/src/hooks/useTranslation'; + +interface AdminAuthGuardProps { + children: ReactNode; +} + +/** + * Auth guard for /admin routes + * Redirects unauthenticated users to login while preserving locale + */ +export default function AdminAuthGuard({ children }: AdminAuthGuardProps) { + const { user, loading } = useSwissOIDAuth(); + const router = useRouter(); + const { locale, t } = useTranslation(); + + useEffect(() => { + if (!loading && !user) { + // Build login URL with locale-aware return path + const loginUrl = process.env.NEXT_PUBLIC_AUTH_BACKEND_URL + ? `${process.env.NEXT_PUBLIC_AUTH_BACKEND_URL}/login` + : '/login'; + + // Redirect to login + window.location.href = loginUrl; + } + }, [user, loading, locale, router]); + + // Show loading state while checking auth + if (loading) { + return ( +
+ +

{t('Checking authentication...')}

+
+ ); + } + + // Show loading state while redirecting + if (!user) { + return ( +
+ +

{t('Redirecting to login...')}

+
+ ); + } + + // User is authenticated, render children + return <>{children}; +}