A minimalist GIS-style web application that helps non-technical users discover nearby healthcare and community-based healing resources. Built for Cultivating Self's hackathon MVP.
- Interactive Map: Explore healing resources across Northern California with an intuitive map interface
- List View + Pagination: Browse resources in a fast list view with pagination
- Five Pillar Filtering: Filter resources by Cultivating Self's five pillars:
- Ecological Medicine
- Cultural Medicine
- Food & Farming as Medicine
- Ecorestoration
- Spirituality as Medicine
- Intent-Based Search: Search resources by intent tags (e.g., "stress", "chronic pain", "community")
- Resource Details: Click on map markers to view detailed information about each resource
- Appointments: Schedule appointments and view upcoming/past appointments
- Elemental Mastery (Gamification): Log completed visits to earn XP for Fire/Water/Air/Earth and track progress over time (for motivation/exploration only — not medical advice)
- Suggest Resources: Community members can suggest new resources for review
- Mobile Responsive: Fully responsive design that works on all devices
- Frontend: React 19 with Vite
- Map Library: Mapbox GL JS
- Styling: CSS with CSS Variables (no framework dependencies)
- Data Storage: JSON files + localStorage (suggestions, preferences, and Elemental Mastery state)
- Node.js 18+ and npm
- A Mapbox account (free tier available)
- Clone the repository:
git clone <repository-url>
cd ecomap- Install dependencies:
npm install-
Set up your Mapbox API key:
- Sign up for a free account at mapbox.com
- Get your default public token from your account page
- Create a
.envfile in the project root and add:
MAPBOX_TOKEN=your_actual_token_here -
Start the development server:
npm run dev- Open your browser to
http://localhost:5173
/- Main map/list experience/suggestions- Review suggested resources/profile/mastery- Elemental Mastery dashboard
The app uses localStorage for lightweight client-side persistence:
userGameState_v1- Elemental Mastery progress + visit historylistViewMode- Map/List view preference
ecomap/
├── src/
│ ├── components/ # React components
│ │ ├── MapContainer.jsx # Main map component
│ │ ├── MapMarker.jsx # Map marker component
│ │ ├── ControlsPanel.jsx # Search and filters container
│ │ ├── SearchBar.jsx # Search input
│ │ ├── PillarFilters.jsx # Pillar filter buttons
│ │ ├── ResourceDrawer.jsx # Resource details panel
│ │ └── SuggestResourceModal.jsx # Suggest resource form
│ ├── hooks/
│ │ └── useResources.js # Resource data and filtering logic
│ │ └── useGameState.js # Elemental Mastery state hook
│ ├── lib/
│ │ ├── gameState.js # Elemental Mastery localStorage model
│ │ ├── awardXP.js # Elemental Mastery XP + cooldown logic
│ │ └── leveling.js # Elemental Mastery level curve
│ ├── data/
│ │ └── resources.json # Seed resource data
│ ├── styles/
│ │ ├── variables.css # CSS variables (design system)
│ │ └── components.css # Component styles
│ ├── App.jsx # Main app component
│ ├── App.css # App styles
│ ├── main.jsx # Entry point
│ └── index.css # Global styles
├── public/ # Static assets
├── .env.example # Environment variables template
└── package.json
npm run dev- Start development servernpm run build- Build for productionnpm run preview- Preview production buildnpm run lint- Run ESLint
Resources are stored in JSON format with the following schema:
{
"id": "unique-id",
"name": "Resource Name",
"description": "Plain-language description",
"pillars": ["ecological_medicine", "cultural_medicine", ...],
"intent_tags": ["stress", "chronic pain", "community"],
"location": {
"type": "Point",
"coordinates": [longitude, latitude]
},
"address": {
"street": "123 Main St",
"city": "City Name",
"state": "CA",
"zip": "12345"
},
"contact": {
"phone": "(555) 123-4567",
"email": "contact@example.com",
"website": "https://example.com"
},
"hours": "Mon-Fri 9am-5pm",
"status": "active" // or "pending" for suggested resources
}- How it works: Use “Complete Visit” to log a completed visit. XP is awarded to the service’s element(s), split evenly across multiple elements.
- Cooldown: You can’t log the same service repeatedly within a short cooldown window (anti-spam guardrail).
- Privacy: Optional notes in the UI are not stored.
- Disclaimer: For motivation and exploration only — not medical advice.
The app uses a minimalist, earthy color palette:
- Primary Background: Off-white (#FAF9F6)
- Earth Tones: Clay and brown tones
- Soft Greens: Sage and muted greens
- Typography: System fonts for performance
- Spacing: 4px base unit scale
See src/styles/variables.css for the complete design system.
- Push your code to GitHub
- Import your repository in Vercel
- Add environment variable
MAPBOX_TOKENin Vercel dashboard - Deploy
- Push your code to GitHub
- Connect repository in Netlify
- Build command:
npm run build - Publish directory:
dist - Add environment variable
MAPBOX_TOKENin Netlify dashboard
Make sure to set MAPBOX_TOKEN in your deployment platform's environment variables.
- Migrate to PostGIS database for production
- Add FastAPI backend for CRUD operations
- Implement proper address geocoding
- Add resource verification workflow
- Build admin interface
- Add user accounts and saved resources
Built for Cultivating Self hackathon MVP.
For issues or questions, please contact the development team.