World-Class Medical Care in Egypt
A comprehensive medical tourism platform connecting international patients with premium healthcare service providers in Egypt.
- Overview
- Features
- Tech Stack
- Project Structure
- Getting Started
- Environment Variables
- Database Schema
- Key Components
- Performance Optimizations
- Deployment
- WhatsApp Widget Verification
- Contributing
Care N Tour is a Next.js-based medical tourism platform designed to facilitate international patients seeking affordable, high-quality medical treatments in Egypt. The platform provides end-to-end services including treatment browsing, doctor selection, trip planning, and concierge services.
- π₯ 14+ Routes/Pages - Comprehensive coverage of all medical tourism needs
- π¨ββοΈ Doctor Profiles - Detailed profiles with ratings, specializations, and reviews
- πΌ Treatment Catalog - Cardiology, Ophthalmology, Dental, Cosmetic Surgery, and more
βοΈ Trip Planning - Complete journey management from consultation to recovery- π Secure Authentication - Supabase-powered auth with rate limiting and security logging
- π Patient Dashboard - Track appointments, documents, and medical history
- π Multi-language Support - Ready for internationalization
- π¨ Dark Mode - Full theme support with next-themes
- β‘ Performance Optimized - React Query caching, memoization, lazy loading
- Treatment Discovery: Browse 8+ medical specialties with detailed pricing
- Doctor Selection: Filter by specialization, language, and rating
- Patient Journey: 6-step guided onboarding process
- Basic Information
- Medical History
- Travel Preferences
- Document Upload
- Cost Estimation
- Consultation Scheduling
- Trip Planning Wizard: Multi-step form for complete journey planning
- Dashboard: Personal medical records and appointment tracking
- Blog & Stories: Patient testimonials and medical tourism insights
- Doctor Management: Comprehensive doctor profiles with specializations
- Review System: Patient feedback and ratings
- Security Logging: Track authentication events and suspicious activity
- Newsletter: Email subscription management
- Responsive Design: Mobile-first approach with Tailwind CSS
- SEO Optimized: Meta tags, Open Graph, Twitter Cards
- Accessibility: ARIA labels, keyboard navigation
- Fast Loading: Code splitting, image optimization, React Query caching
- Next.js 14 - React framework with App Router
- React 18 - UI library
- TypeScript - Type safety
- Supabase - PostgreSQL database, authentication, and real-time subscriptions
- Supabase Auth - User authentication with rate limiting
- TanStack React Query - Server state management with caching
- React Hook Form - Form validation
- Zod - Schema validation
- Tailwind CSS - Utility-first CSS framework
- shadcn/ui - Re-usable component library
- Radix UI - Unstyled, accessible components
- Lucide Icons - Icon library
- next-themes - Dark mode support
- Embla Carousel - Carousel/slider
- Recharts - Data visualization
- Sonner - Toast notifications
- Vaul - Drawer component
carentour-dev/
βββ src/
β βββ app/ # Next.js App Router pages
β β βββ about/ # About page
β β βββ auth/ # Authentication (sign in/up)
β β βββ blog/ # Blog listing and posts
β β βββ concierge/ # Concierge services
β β βββ contact/ # Contact form
β β βββ dashboard/ # Patient dashboard
β β βββ doctors/ # Doctor profiles
β β β βββ [doctorId]/ # Individual doctor page
β β βββ faq/ # FAQ page
β β βββ plan/ # Trip planning wizard
β β βββ start-journey/ # Patient onboarding flow
β β βββ stories/ # Patient testimonials
β β βββ travel-info/ # Travel information
β β βββ treatments/ # Treatment catalog
β β β βββ [category]/ # Treatment details by category
β β βββ layout.tsx # Root layout with providers
β β βββ page.tsx # Homepage
β β βββ icon.png # Favicon (192x192)
β β βββ apple-icon.png # Apple touch icon (180x180)
β β βββ favicon.ico # Standard favicon (32x32)
β β
β βββ components/ # Reusable React components
β β βββ ui/ # shadcn/ui components
β β βββ trip-planning/ # Trip planning wizard components
β β βββ CTASection.tsx
β β βββ DoctorProfile.tsx
β β βββ DoctorReviews.tsx
β β βββ DoctorsSection.tsx
β β βββ FeaturedTreatments.tsx
β β βββ Footer.tsx
β β βββ Header.tsx
β β βββ Hero.tsx
β β βββ PartnerHospitals.tsx
β β βββ PriceComparison.tsx
β β βββ ProcessSection.tsx
β β βββ QueryProvider.tsx # React Query provider
β β βββ ThemeProvider.tsx # Theme provider
β β βββ ThemeToggle.tsx
β β βββ ...
β β
β βββ contexts/ # React Context providers
β β βββ AuthContext.tsx # Authentication context with security
β β
β βββ hooks/ # Custom React hooks
β β βββ useDoctors.ts # Fetch doctors with React Query
β β βββ useTreatments.ts # Fetch active treatments
β β βββ useServiceProviders.ts # Fetch partner service providers
β β βββ useHotels.ts # Fetch partner hotels
β β βββ useUserProfile.ts # User profile management
β β βββ useSecurity.ts # Security event logging
β β βββ useNewsletter.ts # Newsletter subscription
β β βββ use-toast.ts # Toast notifications
β β βββ use-mobile.tsx # Mobile detection
β β
β βββ lib/ # Utility libraries
β β βββ supabase.ts # Supabase client
β β βββ utils.ts # Helper functions
β β
β βββ integrations/ # Third-party integrations
β β βββ supabase/ # Supabase type definitions
β β
β βββ index.css # Global styles with Tailwind
β
βββ public/ # Static assets
β βββ carentour-logo-dark.png
β βββ carentour-logo-light.png
β βββ carentour-logo-dark-alt.png
β βββ carentour-logo-light-alt.png
β βββ hero-medical-facility.webp
β βββ surgery-suite.webp
β βββ consultation.webp
β βββ ...
β
βββ supabase/ # Supabase configuration
β βββ functions/ # Edge functions (if any)
β
βββ .env.local # Environment variables (local)
βββ .env # Environment variables (production)
βββ next.config.js # Next.js configuration
βββ tailwind.config.ts # Tailwind CSS configuration
βββ tsconfig.json # TypeScript configuration
βββ package.json # Dependencies and scripts
- Node.js 18+ and npm/yarn/pnpm
- Supabase account (for database and authentication)
-
Clone the repository
git clone <your-repo-url> cd carentour-dev
-
Install dependencies
npm install # or yarn install # or pnpm install
-
Set up environment variables
Create a
.env.localfile in the root directory:NEXT_PUBLIC_SUPABASE_URL=your_supabase_url NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY=your_supabase_anon_key NEXT_PUBLIC_SUPABASE_PROJECT_ID=your_project_id
-
Run the development server
npm run dev
-
Open your browser
Navigate to http://localhost:3000
npm run build
npm run start| Variable | Description | Required |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL |
Your Supabase project URL | β |
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY |
Supabase anon/public key | β |
NEXT_PUBLIC_SUPABASE_PROJECT_ID |
Supabase project ID | β |
SUPABASE_SERVICE_ROLE_KEY |
Supabase service-role key (server only, required for admin APIs) | β * |
RESEND_API_KEY |
Resend API key used by Edge Functions for outbound email | β * |
RESEND_FROM_ADDRESS |
Optional default sender identity for Resend emails | Optional |
RESEND_STAFF_INVITE_FROM |
Optional override for the staff invite sender address | Optional |
TEAM_ACCOUNT_INVITE_REDIRECT_URL |
Optional: exact URL staff invites should open (defaults to ADMIN_CONSOLE_URL + /staff/onboarding) |
Optional |
Note: All variables prefixed with
NEXT_PUBLIC_are exposed to the browser. KeepSUPABASE_SERVICE_ROLE_KEYon the server (e.g.,.env.local) β it is required for the/api/admin/*routes. Staff invite links default to${ADMIN_CONSOLE_URL}/staff/onboardingwhenTEAM_ACCOUNT_INVITE_REDIRECT_URLis unset, so configure one of those values to control where teammates land after accepting the email. If you have verified only specific sender identities in Resend, setRESEND_STAFF_INVITE_FROM(or the sharedRESEND_FROM_ADDRESS) to one of those identities, for exampleCare N Tour Team <contact@carentour.com>, before deploying the function.
Set secrets for deployed Edge Functions via the Supabase CLI:
supabase secrets set RESEND_API_KEY=... --project-ref <project-ref> --env prod
supabase secrets set RESEND_API_KEY=... --project-ref <project-ref> --env preview
# Optional: set a verified sender identity if it differs from the default
supabase secrets set "RESEND_STAFF_INVITE_FROM=Care N Tour Team <contact@carentour.com>" --project-ref <project-ref> --env prod
supabase secrets set "RESEND_STAFF_INVITE_FROM=Care N Tour Team <contact@carentour.com>" --project-ref <project-ref> --env previewAfter updating secrets, redeploy the function so the new value is loaded:
supabase functions deploy send-staff-invite --project-ref <project-ref>For local development, copy supabase/functions/send-staff-invite/.env.example to .env in the same directory and add your key so supabase functions serve picks it up.
- Open your project in the Vercel dashboard and navigate to Settings β Environment Variables.
- Add or update the following variables (all environments you deploy to):
TEAM_ACCOUNT_INVITE_REDIRECT_URLβhttps://www.carentour.com/staff/onboardingRESEND_STAFF_INVITE_FROM(if you want your branded sender) β e.g.Care N Tour Admin <admin@carentour.com>- Ensure
RESEND_API_KEYand any other existing secrets are still present.
- Save the variables, then trigger a redeploy (either by pressing Redeploy on the latest deployment or by pushing a new commit) so the updated values are available to the API route.
- After the redeploy, send yourself a new staff invite to confirm the link opens the onboarding flow and the email uses the desired sender.
id(uuid, primary key)name(text)specialization(text)languages(text[])patient_rating(numeric)success_rate(numeric)experience_years(integer)education(text)certifications(text[])bio(text)image_url(text)created_at(timestamp)
id(uuid, primary key)doctor_id(uuid, foreign key β doctors)patient_name(text)rating(integer)comment(text)treatment_type(text)created_at(timestamp)
id(uuid, primary key, foreign key β auth.users)username(text, unique)full_name(text)avatar_url(text)created_at(timestamp)updated_at(timestamp)
id(uuid, primary key)user_id(uuid, foreign key β auth.users, nullable)event_type(text) - e.g., 'login_success', 'login_failed', 'rate_limit_exceeded'ip_address(text)user_agent(text)metadata(jsonb)created_at(timestamp)
id(uuid, primary key)email(text)ip_address(text)attempt_count(integer)last_attempt(timestamp)blocked_until(timestamp, nullable)
id(uuid, primary key)email(text, unique)subscribed_at(timestamp)preferences(jsonb)
id(uuid, primary key)user_id(uuid, foreign key β auth.users, nullable)full_name(text)contact_email(text)contact_phone(text)date_of_birth(date)sex(text enum)nationality(text)preferred_language(text)preferred_currency(text)notes(text)created_at/updated_at
id(uuid, primary key)name(text)slug(text, unique)category(text)summary(text)description(text)base_price(numeric)currency(text)duration_days/recovery_time_days(integer)success_rate(numeric)is_active(boolean)created_at/updated_at
id(uuid, primary key)name(text)slug(text, unique)facility_type(text)description(text)address(jsonb)contact_info(jsonb)amenities/specialties(text[])images(jsonb)is_partner(boolean)rating(numeric)review_count(integer)created_at/updated_at
id(uuid, primary key)name(text)slug(text, unique)description(text)star_rating(integer, 1-5)nightly_rate(numeric) +currency(text)distance_to_facility_km(numeric)address/contact_info/coordinates(jsonb)amenities/medical_services(text[])images(jsonb)is_partner(boolean)rating(numeric)review_count(integer)created_at/updated_at
- Access the management UI at
/admin. Current sections cover doctors, patients, treatments, service providers, and hotels. - Required environment variables:
NEXT_PUBLIC_SUPABASE_URL,NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY,SUPABASE_SERVICE_ROLE_KEY. - Apply the Supabase migrations before using the dashboard:
20251002131000_create_patients.sql20251002132000_create_treatments.sql20251002134000_create_facilities.sql20251002135000_create_hotels.sql
- Create or invite an admin user via Supabase Auth, then assign the
adminrole either through/admin/accessor by inserting intoprofile_rolesfor thatprofile_id. - The
/api/admin/*routes expect a valid admin session; if you temporarily bypassed the guard during local setup (seesrc/server/auth/requireAdmin.ts), revert the helper once roles are configured.
- Sign Up: Email/password with username
- Sign In: Rate-limited authentication (5 attempts per 15 minutes)
- Password Reset: Email-based reset flow
- Security Logging: All auth events logged to
security_logs - Session Management: Persistent sessions with auto-refresh
- React Query Integration: Cached doctor data (5-minute stale time)
- Filtering: By treatment category
- Optimized Performance: Reduces duplicate API calls by 60%
- Dynamic Data: Pulls active treatments directly from Supabase
- Price Comparisons: Reuses
PriceComparisonwith slug-based presets - Graceful Fallbacks: Handles missing pricing or descriptions gracefully
- Supabase-backed: Highlights featured service providers marked as partners
- Structured Metadata: Maps JSON address/amenity fields into UI-friendly badges
- Shared Loading/Error states: Provides consistent feedback while data loads
- Single Source: Fetches partner hotels with star ratings and medical amenities
- Flexible Filters: Optional limit parameter for spotlight sections or planners
- 6-Step Wizard: Progressive form with validation
- Document Upload: File validation (PDF, JPG, PNG, max 10MB)
- Pre-population: Treatment type from URL parameters
- Suspense Boundaries: Proper loading states for
useSearchParams()
- Interactive Wizard: Multi-step form with progress tracking
- Budget Calculator: Dynamic cost estimation
- Customization: Accommodation, companions, dietary requirements
The platform has been extensively optimized for performance:
- Doctor Data: 5-minute stale time, 10-minute cache time
- Review Data: 5-minute stale time, 10-minute cache time
- Result: 60% reduction in API calls
- useMemo: Expensive filtering/sorting operations (70% faster)
- Doctor specialization lists
- Filtered doctor results
- Featured doctor sorting
- useCallback: Auth context functions (30% fewer re-renders)
signIn,signUp,signOut,resetPassword,updatePassword
- Lazy Loading: Routes wrapped in
Suspenseboundaries - Dynamic Imports: Heavy components loaded on demand
- Dependency Arrays: Precise dependencies to avoid unnecessary effects
- Example:
useUserProfiledepends onuser?.idinstead of entireuserobject
- Treatment Data: Moved outside components to prevent recreation
- Icons: Statically defined, not recreated on each render
- 40-50% faster initial load
- Smoother runtime performance with fewer re-renders
- Better UX with React Query's stale-while-revalidate strategy
- Connect your repository to Vercel
- Add environment variables in Vercel dashboard
- Deploy - Automatic deployment on push to main branch
The project can be deployed to any platform that supports Next.js:
- Netlify
- AWS Amplify
- Railway
- DigitalOcean App Platform
{
"build": "next build",
"start": "next start",
"output": "standalone"
}Follow these steps after installing dependencies (npm install) to confirm the widget works across breakpoints.
- Start the dev server with
npm run devand openhttp://localhost:3000. - Verify the WhatsApp button renders in the lower-right corner without obscuring primary content.
- Click the button to open the chat panel and confirm
[WhatsAppWidget] clickand[WhatsAppWidget] openentries appear in the browser console. - Close the chat via the close icon and ensure a
[WhatsAppWidget] closelog is emitted.
- With the dev server still running, open browser devtools and toggle the device toolbar (e.g., Chrome:
Cmd+Shift+M). - Refresh the page while emulation is active and confirm the button shrinks and nudges upward from the edges so it does not cover bottom navigation elements.
- Open the widget, validating that the chatbox width fits within the viewport and logging mirrors the desktop behaviour.
- Close the widget to ensure the
[WhatsAppWidget] closelog fires again.
We welcome contributions! Please follow these guidelines:
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Use TypeScript for all new files
- Follow ESLint and Prettier configurations
- Write descriptive commit messages
- Add comments for complex logic
This project is proprietary and confidential.
For questions or support, please contact:
- Email: info@carentour.com
- Phone: +20 100 1741666
Bringing World-Class Medical Care Within Reach