A fully responsive marketing landing page for Workplace Network, a faith-based mentoring and networking platform for Christian career professionals.
- Next.js 14 (App Router, TypeScript)
- Tailwind CSS with custom design tokens
- Supabase (Postgres + RLS) for data storage
- Resend for transactional confirmation emails
- Zod for shared client/server form validation
- Vercel for deployment
git clone <repo-url>
cd workplace-network
npm installcp .env.example .env.localOpen .env.local and fill in:
| Variable | Where to find it |
|---|---|
NEXT_PUBLIC_SUPABASE_URL |
Supabase → Project Settings → API |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Supabase → Project Settings → API |
SUPABASE_SERVICE_ROLE_KEY |
Supabase → Project Settings → API (secret) |
RESEND_API_KEY |
resend.com → API Keys |
RESEND_FROM_EMAIL |
A verified sender domain in Resend |
⚠️ SUPABASE_SERVICE_ROLE_KEYbypasses RLS — never expose it in the browser. It is only used in server-side API routes.
In the Supabase SQL Editor (or via Supabase CLI), run:
-- Paste the contents of:
supabase/migrations/001_initial_schema.sqlThis creates all tables, indexes, RLS policies, and optional seed data.
npx supabase login
npx supabase link --project-ref your-project-ref
npx supabase db pushnpm run dev- Push to GitHub
- Import the repo at vercel.com/new
- Add all environment variables from
.env.examplein the Vercel dashboard - Deploy — Vercel auto-detects Next.js
src/
├── app/
│ ├── layout.tsx # Root layout + fonts + metadata
│ ├── page.tsx # Home page (server component, fetches all data)
│ ├── globals.css # Tailwind + custom CSS
│ └── api/
│ └── register/
│ └── route.ts # Registration API (POST) with rate limiting
├── components/
│ ├── Navbar.tsx # Sticky nav + mobile hamburger
│ ├── Footer.tsx # Footer with social links
│ ├── Countdown.tsx # Live countdown timer (client component)
│ ├── RegistrationForm.tsx # Self-contained form with all states
│ └── sections/
│ ├── Hero.tsx
│ ├── AboutUs.tsx
│ ├── MeetOurMentors.tsx
│ ├── FeaturesBanner.tsx
│ ├── CommunityGroups.tsx
│ ├── WhyWorkplaceNetwork.tsx
│ ├── FeaturedEvent.tsx # ← owns countdown + form
│ ├── CuratedEvents.tsx # ← separate, no form/countdown
│ ├── TestimonialCarousel.tsx
│ ├── MemberStories.tsx
│ ├── JoinCTA.tsx
│ ├── FAQ.tsx
│ └── LatestArticles.tsx
├── lib/
│ ├── supabase.ts # Public + admin Supabase clients
│ ├── schemas.ts # Zod validation schemas (shared client/server)
│ └── fallback-data.ts # Static fallbacks used during build/preview
└── types/
└── database.ts # Supabase table types
supabase/
└── migrations/
└── 001_initial_schema.sql
- User fills out the form in the Featured Event section
- Client-side Zod validation runs first
POST /api/registeris called with rate limiting per IP- Server checks for duplicate email (application-level + Postgres unique constraint)
- Row is inserted into
registrationstable using the service role key - Resend sends a confirmation email (non-blocking — won't fail the response)
- Form switches to a success state inline
Update the is_featured flag in Supabase Studio:
-- Unfeature the old event
update events set is_featured = false where is_featured = true;
-- Feature the new event
update events set is_featured = true where id = 'your-new-event-id';No code change or redeployment needed.
When the countdown hits zero, the registration form automatically disables itself via the onExpire callback — no admin action needed.
- Content sections use ISR (
revalidate = 300) — rebuilt every 5 minutes - Heavy client components (countdown, form, carousel, accordion) are code-split
- All images use lazy loading except the hero's first image
- Horizontal carousels use CSS scroll-snap for smooth mobile UX
- Supabase writes go through a server Route Handler — never client-side
Replace placeholder Unsplash URLs with your own assets:
- Upload images to Supabase Storage (create a public bucket called
assets) - Update
photo_url/flyer_urlcolumns in the relevant tables via Supabase Studio - The page will pick them up on the next ISR revalidation (or
npm run build)
For local development, you can also put images in /public/images/ and reference them as /images/filename.jpg.
| What | Where |
|---|---|
| Colours / design tokens | tailwind.config.ts |
| FAQ questions | src/lib/fallback-data.ts → FAQ_ITEMS |
| Reasons to join | src/components/sections/FeaturedEvent.tsx → REASONS_TO_JOIN |
| Testimonial quotes | src/components/sections/TestimonialCarousel.tsx |
| Nav links | src/components/Navbar.tsx → NAV_LINKS |
| Hero images | src/components/sections/Hero.tsx → HERO_IMAGES |