# GGO Med Healthcare Website > Next.js 16+ healthcare platform for GGO Med (ggomed.co.uk) - Medical services, conditions information, and patient resources ## Project Overview GGO Med is a modern healthcare website built with Next.js 16 (App Router), React 19, and Sanity CMS v4. The site provides comprehensive information about medical conditions, services, and treatments with a focus on men's health and urology. **Live Site**: https://ggomed.co.uk **CMS**: Sanity Studio at `/studio` **Repository**: https://github.com/GGO-Med/ggomed.co.uk ## Technology Stack ### Frontend - **Framework**: Next.js 16.0.4 (App Router, React 19) - **Styling**: Tailwind CSS 4, shadcn/ui components - **Fonts**: Plus Jakarta Sans (primary) - **State**: React hooks, @tanstack/react-query - **Icons**: lucide-react ### CMS & Content - **CMS**: Sanity Studio v4 (embedded at `/studio`) - **Content**: Portable Text rendering with custom blocks - **Schemas**: TypeScript-based Sanity schemas - **SEO**: Schema.org medical markup, structured data ### Infrastructure - **Deployment**: Vercel - **Caching**: ISR (Incremental Static Regeneration) with on-demand revalidation - **Redirects**: Managed via Sanity CMS (proxy.ts middleware) - **Testing**: Vitest (unit), Playwright (e2e) ### Special Features - HTML/Docx to Portable Text parser service - Mega menu navigation with hover interactions - Medical Schema.org markup generation - Newsletter integration (Beehiiv) - Contact form with rate limiting ## Architecture ### Directory Structure ``` app/ ├── (site)/ # Public-facing pages (grouped route) │ ├── [slug]/ # Dynamic pages (conditions, services) │ ├── category/[category]/ # Category hub pages │ └── blog/[slug]/ # Blog posts ├── api/ # API routes │ ├── contact/ # Contact form submission │ ├── normalize-parse/ # HTML parser service │ ├── sanity/ # Sanity webhooks (ISR revalidation) │ └── newsletter/ # Newsletter subscription ├── studio/ # Sanity Studio (embedded CMS) └── tools/ # Internal tools components/ ├── ui/ # shadcn/ui primitives ├── content-renderer.tsx # Portable Text renderer ├── navbar.tsx # Main navigation (mega menu) ├── footer.tsx # Footer with legacy colors └── [feature].tsx # Feature-specific components lib/ ├── sanity.client.ts # Sanity fetch client ├── sanity.queries.ts # GROQ queries ├── sanity-transformer.ts # Data transformers ├── schema/ # Schema.org generators │ └── medical-transformer.ts └── normalize/ # HTML parser utilities sanity/ ├── schemas/ # Sanity content schemas │ ├── categoryHubPage.ts │ ├── dedicatedPage.ts │ ├── blogPost.ts │ └── index.ts └── lib/ # Sanity utilities parser-service/ # Standalone parser service packages/parser-core/ # Shared parser logic ``` ### Key Patterns **Content Types**: - **Category Hub Pages**: Main topic pages (e.g., "Erectile Dysfunction") - **Dedicated Pages**: Sub-topic pages (e.g., "Haematuria") - **Blog Posts**: News, articles, and educational content **Navigation**: - Mega menu with two columns (hardcoded data, not yet in CMS) - Hover state management via `onPointerEnter`/`onPointerLeave` - Mobile accordion navigation **ISR & Caching**: - On-demand revalidation via `/api/sanity/revalidate` webhook - Sanity webhook triggers cache updates on publish - Tag-based revalidation for targeted cache invalidation ## Design System (CRITICAL) ### Sacred Palette Rules **Must follow `.github/agents/sacred-palette.agent.md`** **STRICT RULES**: 1. **Colors**: Use ONLY existing tokens from `app/globals.css` - Brand colors: `bg-brand-*`, `text-brand-*` - System colors: `bg-background`, `text-foreground`, `border-border` - NO new hex/OKLCH colors - NO arbitrary Tailwind colors (e.g., `text-blue-500`) 2. **Legacy Exception**: Navbar/footer use `blue-*`/`gray-*` classes - Preserve existing usage, DO NOT expand to new components 3. **Dark Mode**: DISABLED - Do not add new `dark:` classes - Do not re-enable dark mode 4. **Typography**: Plus Jakarta Sans only (already configured) 5. **Spacing/Radii/Shadows**: Use existing scales, no one-off values **Verification Steps**: 1. Check `app/globals.css` for available tokens 2. Review `.github/agents/sacred-palette.agent.md` before styling 3. Reuse patterns from `components/ui/*` ## Parser Service **Location**: `parser-service/`, `packages/parser-core/` **Purpose**: Convert HTML/Markdown/Docx to Portable Text and Questionnaire schemas **Key Features**: - HTML to Portable Text conversion - Docx to Portable Text conversion - HTML to Questionnaire schema (for medical assessments) - Image resolution and embedding - FAQ reference resolution - Nested list handling (max 3 levels) **Testing**: ```bash pnpm test html-to-portable-text pnpm test html-to-questionnaire pnpm test normalize-parse-validation ``` **Coverage Target**: 70% minimum for `packages/parser-core` **Edge Cases**: - Empty/malformed HTML must not crash (emit warnings) - Nested lists >3 levels: warn and clamp to 3 levels - Output must match `@portabletext/types` Block[] structure **Documentation**: - `docs/parser-readme.md` - `docs/html-parser-guidance.md` ## Navigation System **Primary File**: `components/navbar.tsx` **Architecture**: - Mega menu with two columns - State: `useState` with hover via `onPointerEnter`/`onPointerLeave` - Data: Hardcoded arrays (`conditionsItems`, `servicesItems`) - Future: Migrate to Sanity schema (not yet implemented) **Category Structure**: ```typescript { category: string, items: Array<{ href: string; text: string }> } ``` **IMPORTANT**: Preserve hover behavior and state reset on pointer leave ## Development Workflow ### Setup ```bash # Install dependencies pnpm install # Run development server pnpm dev # Access app: http://localhost:3000 # Access Sanity Studio: http://localhost:3000/studio ``` ### Testing ```bash pnpm lint # ESLint only (no Prettier) pnpm test # All Vitest tests pnpm test:watch # Watch mode pnpm test:api # API route tests pnpm test:e2e # Playwright e2e tests ``` ### Build ```bash pnpm build # Production build ``` ### Before Committing ```bash pnpm lint # Must pass pnpm test # Must pass pnpm build # Must succeed ``` ## Common Pitfalls 1. **Design Tokens**: Never add new colors; use existing tokens only 2. **Parser**: No crashes on malformed HTML; emit warnings instead 3. **Navigation**: Verify all new routes exist before adding to menus 4. **ISR**: Ensure Sanity webhook hits `/api/sanity/revalidate` correctly 5. **TypeScript**: Avoid `any`; use `// @ts-expect-error` with reason if unavoidable ## Key Files ### Critical Files (Handle with Care) - `proxy.ts` - Redirect middleware (affects global routing) - `app/api/sanity/revalidate/route.ts` - ISR webhook - `components/navbar.tsx` - Main navigation - `lib/sanity.queries.ts` - GROQ queries - `lib/schema/medical-transformer.ts` - Schema.org markup ### Configuration Files - `next.config.ts` - Next.js configuration - `sanity.config.ts` - Sanity Studio configuration - `tailwind.config.ts` - Tailwind CSS configuration (if exists) - `app/globals.css` - Global styles and design tokens ### Documentation - `README.md` - Getting started - `CLIENT_DOCUMENTATION.md` - Content management guide - `docs/handover/03-DEVELOPER-MANUAL.md` - Developer manual - `docs/parser-readme.md` - Parser service documentation - `.github/agents/sacred-palette.agent.md` - Design system rules ## Environment Variables ```bash # Sanity CMS NEXT_PUBLIC_SANITY_PROJECT_ID="..." NEXT_PUBLIC_SANITY_DATASET="production" SANITY_API_TOKEN="sk_..." # ISR Revalidation REVALIDATE_SECRET="..." # Newsletter (Beehiiv) BEEHIIV_PUBLICATION_ID="pub_..." BEEHIIV_API_KEY="..." # Optional: LLM for parser OPENAI_API_KEY="sk-..." ``` ## Project Conventions ### Code Style - TypeScript strict mode enabled - ESLint for linting (no Prettier in CI) - Use existing component patterns from `components/ui/` - JSDoc only for complex exported functions ### Content Structure - Medical content follows NHS/PIF standards - Schema.org markup for medical entities - Portable Text for rich content - Sanity field descriptions required ### Testing Strategy - Unit tests: Vitest for business logic - API tests: Vitest for API routes - E2E tests: Playwright for critical user flows - Coverage target: 70% for parser-core ### Git Workflow - Feature branches from main - PR required for merge - CI checks: lint, test, build - Vercel preview deployments ## Medical Content Standards **Tone**: Professional, empathetic, evidence-based **Audience**: Patients seeking medical information **Compliance**: UK medical advertising standards **Accreditation**: Patient Information Forum (PIF) certified **Content Guidelines**: - Clear, accessible language (avoid jargon) - Evidence-based information with citations - Empathetic tone for sensitive topics - Clear calls-to-action for appointments ## Troubleshooting ### Common Issues **Build Failures**: - Clear `.next` cache: `rm -rf .next` - Reinstall dependencies: `rm -rf node_modules && pnpm install` - Check Node version: `node --version` (requires 20+) **Sanity Issues**: - Check project ID in environment variables - Verify API token has read/write permissions - Clear Sanity cache in Studio settings **ISR Not Working**: - Verify webhook secret matches `REVALIDATE_SECRET` - Check Vercel function logs for revalidation endpoint - Manually trigger: `pnpm revalidate:http` **Parser Errors**: - Check HTML structure for malformed tags - Review parser test suite for examples - Check console for warning messages ## Support & Handover **Current Status**: In handover to @gollandi **Previous Developer**: Contact via GitHub issues **Documentation**: See `docs/handover/` for comprehensive guides **For Questions**: 1. Check `docs/handover/03-DEVELOPER-MANUAL.md` 2. Review `CLIENT_DOCUMENTATION.md` for CMS workflows 3. Search existing GitHub issues 4. Create new issue with [Question] tag --- **Last Updated**: 2026-01-07 **Document Version**: 1.0 **Maintainer**: @gollandi