# NocoDB Map Viewer A containerized web application that visualizes geographic data from NocoDB on an interactive map using Leaflet.js. ## Features - πŸ—ΊοΈ Interactive map visualization with OpenStreetMap - πŸ” **Unified search system with docs and address search** (Ctrl+K to activate) - πŸ“ Real-time geolocation support - βž• Add new locations directly from the map - πŸ”„ Auto-refresh every 30 seconds - πŸ“± Responsive design for mobile devices - πŸ”’ Secure API proxy to protect credentials - πŸ‘€ User authentication with login system - βš™οΈ Admin panel for system configuration - 🎯 Configurable map start location - πŸ“‹ Walk Sheet generator for door-to-door canvassing - πŸ”— QR code integration for digital resources - πŸ“… Volunteer shift management system with calendar and grid views - βœ‹ User shift signup and cancellation with color-coded calendar - πŸ‘₯ Admin shift creation and management - πŸ‘¨β€πŸ’Ό User management panel for admin users (create, delete users) - πŸ” Role-based access control (Admin vs User permissions) - πŸ“§ Email notifications and password recovery via SMTP - πŸ“Š CSV data import with batch geocoding and visual progress tracking - 🐳 Docker containerization for easy deployment - πŸ†“ 100% open source (no proprietary dependencies) ## Quick Start ### Prerequisites - Docker and Docker Compose - NocoDB instance with API access - NocoDB API token ### Installation 1. **Get NocoDB API Token** 1. Login to your NocoDB instance 2. Click user icon β†’ **Account Settings** β†’ **API Tokens** 3. Create new token with read/write permissions 4. Copy the token for the next step 2. **Configure Environment** Edit the `.env` file with your NocoDB API and API Url: ```env # NocoDB API Configuration NOCODB_API_URL=https://db.cmlite.org/api/v1 NOCODB_API_TOKEN=your-api-token-here # These will be populated after running build-nocodb.sh NOCODB_VIEW_URL= NOCODB_LOGIN_SHEET= NOCODB_SETTINGS_SHEET= NOCODB_SHIFTS_SHEET= NOCODB_SHIFT_SIGNUPS_SHEET= # Domain Configuration DOMAIN=cmlite.org # MkDocs Integration MKDOCS_URL=https://cmlite.org MKDOCS_SEARCH_URL=https://cmlite.org MKDOCS_SITE_SERVER_PORT=4002 # Server Configuration PORT=3000 NODE_ENV=production # Session Secret (Generate with: openssl rand -hex 32) SESSION_SECRET=your-secure-random-string # Map Defaults (Edmonton, Alberta, Canada) DEFAULT_LAT=53.5461 DEFAULT_LNG=-113.4938 DEFAULT_ZOOM=11 # Optional: Map Boundaries (prevents users from adding points outside area) # BOUND_NORTH=53.7 # BOUND_SOUTH=53.4 # BOUND_EAST=-113.3 # BOUND_WEST=-113.7 # Cloudflare Settings TRUST_PROXY=true COOKIE_DOMAIN=.cmlite.org # Allowed Origins ALLOWED_ORIGINS=https://map.cmlite.org,http://localhost:3000 # Email Configuration (Optional - for password recovery) SMTP_HOST=smtp.gmail.com SMTP_PORT=587 SMTP_SECURE=false SMTP_USER=your-email@gmail.com SMTP_PASS=your-app-password EMAIL_FROM_NAME=CMlite Support EMAIL_FROM_ADDRESS=noreply@cmlite.org APP_NAME=CMlite Map ``` 3. **Auto-Create Database Structure** Run the build script to create required tables: ```bash chmod +x build-nocodb.sh ./build-nocodb.sh ``` This creates five tables: - **Locations** - Main map data with geo-location, contact info, support levels - **Login** - User authentication (email, name, admin flag) - **Settings** - Admin configuration and QR codes - **Shifts** - Shift scheduling and management - **Shift Signups** - User shift registrations 4. **Get Table URLs** After the script completes: 1. Login to your NocoDB instance at https://db.cmlite.org 2. Navigate to your project ("Map Viewer Project - TIMESTAMP") 3. Copy the view URLs for each table from your browser address bar 4. URLs should look like: `https://db.cmlite.org/dashboard/#/nc/project-id/table-id` 5. **Update Environment with URLs** Edit your `.env` file and add the table URLs: ```env NOCODB_VIEW_URL=https://db.cmlite.org/dashboard/#/nc/pnsalzrup2zqvz8/m6g7bkzv7s1w2ur NOCODB_LOGIN_SHEET=https://db.cmlite.org/dashboard/#/nc/pnsalzrup2zqvz8/mizyc64e4r7ppzh NOCODB_SETTINGS_SHEET=https://db.cmlite.org/dashboard/#/nc/pnsalzrup2zqvz8/mix06f2mlep7gqb NOCODB_SHIFTS_SHEET=https://db.cmlite.org/dashboard/#/nc/pnsalzrup2zqvz8/mkx0tex0iquus1u NOCODB_SHIFT_SIGNUPS_SHEET=https://db.cmlite.org/dashboard/#/nc/pnsalzrup2zqvz8/mi8jg1tn26mu8fj ``` 6. **Build and Deploy** Build the Docker image and start the application: ```bash # Build the Docker image docker-compose build # Start the application docker-compose up -d ``` 7. **Verify Installation** - Check container status: `docker-compose ps` - View logs: `docker-compose logs -f map-viewer` - Access the application at: http://localhost:3000 - Access shift management at: http://localhost:3000/shifts.html - Access admin panel at: http://localhost:3000/admin.html (admin users only) ## Database Schema The build script automatically creates the following table structure: ### Main Locations Table - `ID` (ID): Auto-incrementing primary key - `Geo-Location` (GeoData): Geographic coordinate data - `latitude` (Decimal): Precision 8, Scale 8 - `longitude` (Decimal): Precision 8, Scale 8 - `First Name` (Single Line Text): Person's first name - `Last Name` (Single Line Text): Person's last name - `Email` (Email): Email address - `Phone` (PhoneNumber): Phone number with validation - `Unit Number` (Single Line Text): Unit or apartment number - `Support Level` (Single Select): Options: "1" (Green), "2" (Yellow), "3" (Orange), "4" (Red) - `Address` (Single Line Text): Street address - `Sign` (Checkbox): Has campaign sign - `Sign Size` (Single Select): Options: "Regular" (Blue), "Large" (Green), "Unsure" (Orange) - `Notes` (Long Text): Additional details and comments - `created_by_user` (Single Line Text): Creator email - `last_updated_by_user` (Single Line Text): Last updater email ### Login Table - `ID` (ID): Auto-incrementing primary key - `Email` (Email): User email address (required) - `Password` (Single Line Text): User password (required) - `Name` (Single Line Text): User display name - `Admin` (Checkbox): Admin privileges - `Created At` (DateTime): Account creation timestamp - `Last Login` (DateTime): Last login timestamp ### Settings Table - `ID` (ID): Auto-incrementing primary key - `created_at` (DateTime): Record creation timestamp - `created_by` (Single Line Text): Creator identifier - `Geo-Location` (Single Line Text): Format "latitude;longitude" - `latitude` (Decimal): Precision 8, Scale 8 - `longitude` (Decimal): Precision 8, Scale 8 - `zoom` (Number): Map zoom level - `Walk Sheet Title` (Single Line Text): Title for walk sheets - `Walk Sheet Subtitle` (Single Line Text): Subtitle for walk sheets - `Walk Sheet Footer` (Long Text): Footer text for walk sheets - `QR Code 1 URL` (URL): First QR code link - `QR Code 1 Label` (Single Line Text): First QR code label - `QR Code 2 URL` (URL): Second QR code link - `QR Code 2 Label` (Single Line Text): Second QR code label - `QR Code 3 URL` (URL): Third QR code link - `QR Code 3 Label` (Single Line Text): Third QR code label ### Shifts Table - `ID` (ID): Auto-incrementing primary key - `Title` (Single Line Text): Shift title (required) - `Description` (Long Text): Detailed shift description - `Date` (Date): Shift date (required) - `Start Time` (Time): Shift start time (required) - `End Time` (Time): Shift end time (required) - `Location` (Single Line Text): Shift location - `Max Volunteers` (Number): Maximum volunteer capacity (required) - `Current Volunteers` (Number): Current volunteer count - `Status` (Single Select): Options: "Open" (Green), "Full" (Orange), "Cancelled" (Red) - `Created By` (Single Line Text): Creator identifier - `Created At` (DateTime): Creation timestamp - `Updated At` (DateTime): Last update timestamp ### Shift Signups Table - `ID` (ID): Auto-incrementing primary key - `Shift ID` (Number): Reference to shifts table (required) - `Shift Title` (Single Line Text): Copy of shift title for reference - `User Email` (Email): User's email address (required) - `User Name` (Single Line Text): User's display name - `Signup Date` (DateTime): When user signed up - `Status` (Single Select): Options: "Confirmed" (Green), "Cancelled" (Red) ## API Endpoints ### Public Endpoints - `GET /api/locations` - Fetch all locations (requires auth) - `POST /api/locations` - Create new location (requires auth) - `GET /api/locations/:id` - Get single location (requires auth) - `PUT /api/locations/:id` - Update location (requires auth) - `DELETE /api/locations/:id` - Delete location (requires auth) - `GET /api/config/start-location` - Get map start location - `GET /health` - Health check ### Shifts Endpoints (requires authentication) - `GET /api/shifts` - Get all available shifts - `GET /api/shifts/my-signups` - Get current user's shift signups - `POST /api/shifts/:shiftId/signup` - Sign up for a shift - `POST /api/shifts/:shiftId/cancel` - Cancel shift signup ### Shifts Admin Endpoints (requires admin privileges) - `GET /api/shifts/admin` - Get all shifts including cancelled ones - `POST /api/shifts/admin` - Create new shift - `PUT /api/shifts/admin/:id` - Update existing shift - `DELETE /api/shifts/admin/:id` - Delete shift ### Authentication Endpoints - `POST /api/auth/login` - User login - `GET /api/auth/check` - Check authentication status - `POST /api/auth/logout` - User logout ### Admin Endpoints (requires admin privileges) - `GET /api/admin/start-location` - Get start location with source info - `POST /api/admin/start-location` - Update map start location - `GET /api/admin/walk-sheet-config` - Get walk sheet configuration - `POST /api/admin/walk-sheet-config` - Save walk sheet configuration ### Geocoding Endpoints (requires authentication) - `GET /api/geocode/reverse?lat=&lng=` - Reverse geocode coordinates to address - `GET /api/geocode/forward?address=
` - Forward geocode address to coordinates - `GET /api/geocode/search?query=&limit=` - Search for multiple address matches - `GET /api/geocode/cache/stats` - Get geocoding cache statistics (admin only) All geocoding endpoints include rate limiting (30 requests per 15 minutes per IP) and support Cloudflare IP detection for accurate rate limiting. ## Shifts Management The application includes a comprehensive volunteer shift management system accessible at `/shifts.html`. ### User Features - **Dual View Options**: Toggle between grid view and calendar view for shift display - **Calendar View**: Interactive monthly calendar showing shifts with color-coded indicators: - Green: Shifts you've signed up for - Blue: Available shifts you can join - Gray: Full shifts (no spots available) - **View Available Shifts**: See all upcoming shifts with date, time, and capacity information - **Sign Up for Shifts**: One-click signup for available shifts (works in both views) - **My Shifts Dashboard**: View all your current shift signups at the top of the page - **Cancel Signups**: Cancel your shift signups when needed - **Date Filtering**: Filter shifts by specific dates (applies to both views) - **Real-time Updates**: Shift availability updates dynamically - **Interactive Calendar**: Click on calendar shifts for detailed popup with signup options - **Calendar Navigation**: Navigate between months to view future shifts ### Admin Features Administrators have additional capabilities for managing shifts: - **Create New Shifts**: Add new volunteer shifts with date, time, location, and capacity - **Edit Existing Shifts**: Modify shift details, times, or capacity - **Cancel Shifts**: Mark shifts as cancelled (they remain in system but hidden from users) - **View All Signups**: See who has signed up for each shift - **Manage Capacity**: Set maximum number of volunteers per shift ### Shift Status System - **Open** (Green): Shift is available and accepting signups - **Full** (Orange): Shift has reached maximum capacity - **Cancelled** (Red): Shift has been cancelled by admin The system automatically updates shift status based on current signups vs. maximum capacity. ## Unified Search System The application features a powerful unified search system accessible via the search bar in the header or by pressing `Ctrl+K` anywhere in the application. ### Search Modes The search system operates in two modes: 1. **Documentation Search**: Search through integrated MkDocs documentation 2. **Address Search**: Search for addresses and geographic locations ### Features - **Mode Toggle**: Switch between docs and address search with dedicated buttons - **Keyboard Shortcuts**: - `Ctrl+K` or `Cmd+K`: Focus search input from anywhere - `Escape`: Close search results - Arrow keys: Navigate through search results - `Enter`: Select highlighted result - **Real-time Results**: Search results update as you type (minimum 2 characters) - **Smart Caching**: Results are cached for improved performance - **QR Code Generation**: Generate QR codes for documentation links - **Visual Feedback**: Loading states, result counts, and error handling ### Documentation Search When connected to a MkDocs documentation site: - **Full-text Search**: Search through all documentation content - **Snippet Preview**: See relevant excerpts with search terms highlighted - **Direct Navigation**: Click results to open documentation pages - **Path Display**: Shows the document path and section - **QR Code Support**: Generate QR codes for sharing documentation links ### Address Search For geographic location search: - **Geocoding Integration**: Powered by Nominatim/OpenStreetMap - **Multiple Results**: Returns up to 5 address matches - **Map Integration**: Click results to view location on map - **Temporary Markers**: Visual markers for search results - **Quick Actions**: Add locations directly from search results - **Coordinate Display**: Shows precise latitude/longitude coordinates ### Configuration The unified search system integrates with MkDocs documentation when configured: ```env MKDOCS_URL=https://your-docs-site.com MKDOCS_SEARCH_URL=https://your-docs-site.com MKDOCS_SITE_SERVER_PORT=4002 ``` ### Rate Limiting Address search is rate-limited to prevent abuse: - 30 requests per 15-minute window per IP - Cloudflare IP detection for accurate limiting - Graceful error handling for rate limit exceeded ## Admin Panel ## Unified Search System The application features a powerful unified search system accessible via the search bar in the header. ### Search Modes The search system operates in three modes: 1. **Documentation Search**: Search through integrated MkDocs documentation 2. **Address Search**: Search for addresses and geographic locations 3. **Database Search**: Search through loaded location records on the map ### Features - **Mode Toggle**: Switch between docs, address, and database search with dedicated buttons - **Keyboard Shortcuts**: - `Ctrl+K` or `Cmd+K`: Focus search input from anywhere - `Ctrl+Shift+D`: Switch to docs mode - `Ctrl+Shift+M`: Switch to map mode - `Ctrl+Shift+B`: Switch to database mode - `Escape`: Close search results - Arrow keys: Navigate through search results - `Enter`: Select highlighted result ### Database Search For searching through loaded location data: - **Full-text Search**: Search through names, addresses, emails, phone numbers, and notes - **Smart Matching**: Finds partial matches across multiple fields - **Result Preview**: See relevant details with search terms highlighted - **Map Integration**: Click results to pan to location and open marker popup - **Marker Highlighting**: Temporarily highlights selected markers on the map - **Fast Performance**: Searches through already-loaded data for instant results ## Admin Panel Users with admin privileges can access the admin panel at `/admin.html` to configure system settings. ### Features #### Dashboard Analytics - **Campaign Overview**: Real-time statistics and metrics - **Support Distribution**: Visual breakdown of support levels (1-4) - **Sign Tracking**: Monitor lawn sign requests - **User Analytics**: Track user growth and daily entries - **Performance Score**: Overall campaign performance metric #### Start Location Configuration - **Interactive Map**: Visual interface for selecting coordinates - **Real-time Preview**: See changes immediately on the admin map - **Validation**: Built-in coordinate and zoom level validation #### Walk Sheet Generator - **Printable Forms**: Generate 8.5x11 walk sheets for door-to-door canvassing - **QR Code Integration**: Add up to 3 QR codes with custom URLs and labels - **Form Field Matching**: Automatically matches fields from the main location form - **Live Preview**: See changes as you type - **Print Optimization**: Proper formatting for printing or PDF export - **Persistent Storage**: All QR codes and settings saved to NocoDB #### Shift Management - **Create Shifts**: Set up volunteer shifts with dates, times, and capacity - **Manage Volunteers**: View signups and manage shift participants - **Real-time Updates**: See shift status changes immediately #### User Management - **Create Users**: Add new user accounts with email and password - **Role Assignment**: Assign admin or user privileges - **User List**: View all registered users with their details and creation dates - **Delete Users**: Remove user accounts (with confirmation prompts) - **Security**: Password validation and admin-only access #### Convert Data - **CSV Upload**: Upload CSV files containing addresses for bulk import - **Drag & Drop Interface**: Easy file upload with visual feedback - **Real-time Geocoding**: Addresses are geocoded in real-time with progress tracking - **Visual Progress**: Live progress bar and status updates during processing - **Map Preview**: Interactive map showing geocoded locations before saving - **Results Table**: Detailed table with success/error status for each address - **Batch Save**: Save all successfully geocoded locations to the database - **Field Mapping**: Automatically maps common CSV fields (First Name, Last Name, Email, Phone, etc.) - **Error Handling**: Clear error messages for failed geocoding attempts - **File Validation**: CSV format validation and file size limits (10MB max) ### Access Control - Admin access is controlled via the `Admin` checkbox in the Login table - Only authenticated users with admin privileges can access `/admin.html` - Admin status is checked on every request to admin endpoints - User management functions are restricted to admin users only ### Start Location Priority The system uses a cascading fallback system for map start location: 1. **Database**: Admin-configured location stored in Settings table (highest priority) 2. **Environment**: Default values from .env file (medium priority) 3. **Hardcoded**: Edmonton, Canada coordinates (lowest priority) ## Configuration All configuration is done via environment variables: | Variable | Description | Default | |----------|-------------|---------| | `NOCODB_API_URL` | NocoDB API base URL | Required | | `NOCODB_API_TOKEN` | API authentication token | Required | | `NOCODB_VIEW_URL` | Full NocoDB view URL for locations table | Required | | `NOCODB_LOGIN_SHEET` | Login table URL for authentication | Required | | `NOCODB_SETTINGS_SHEET` | Settings table URL for admin config | Required | | `NOCODB_SHIFTS_SHEET` | Shifts table URL for shift management | Required | | `NOCODB_SHIFT_SIGNUPS_SHEET` | Shift signups table URL for user registrations | Required | | `DOMAIN` | Primary domain for the application | Required | | `MKDOCS_URL` | MkDocs documentation site URL | Optional | | `MKDOCS_SEARCH_URL` | MkDocs search endpoint URL | Optional | | `MKDOCS_SITE_SERVER_PORT` | Port for MkDocs integration | 4002 | | `PORT` | Server port | 3000 | | `NODE_ENV` | Environment mode | production | | `SESSION_SECRET` | Session encryption secret (generate with openssl rand -hex 32) | Required | | `DEFAULT_LAT` | Default map latitude | 53.5461 | | `DEFAULT_LNG` | Default map longitude | -113.4938 | | `DEFAULT_ZOOM` | Default map zoom level | 11 | | `BOUND_NORTH` | Northern boundary for map points (optional) | None | | `BOUND_SOUTH` | Southern boundary for map points (optional) | None | | `BOUND_EAST` | Eastern boundary for map points (optional) | None | | `BOUND_WEST` | Western boundary for map points (optional) | None | | `TRUST_PROXY` | Trust proxy headers (for Cloudflare) | true | | `COOKIE_DOMAIN` | Cookie domain setting | .cmlite.org | | `ALLOWED_ORIGINS` | CORS allowed origins (comma-separated) | Multiple URLs | | `SMTP_HOST` | SMTP server hostname (optional) | smtp.gmail.com | | `SMTP_PORT` | SMTP server port (optional) | 587 | | `SMTP_SECURE` | Use SSL for SMTP (optional) | false | | `SMTP_USER` | SMTP username (optional) | your-email@gmail.com | | `SMTP_PASS` | SMTP password (optional) | your-app-password | | `EMAIL_FROM_NAME` | Email sender name (optional) | CMlite Support | | `EMAIL_FROM_ADDRESS` | Email sender address (optional) | noreply@cmlite.org | | `APP_NAME` | Application name for emails (optional) | CMlite Map | ## Maintenance Commands ### Update Application ```bash docker-compose down git pull origin main docker-compose build docker-compose up -d ``` ### Development Mode ```bash cd app npm install npm run dev ``` ### Health Check ```bash curl http://localhost:3000/health ``` ## Development To run in development mode: 1. Install dependencies: ```bash cd app npm install ``` 2. Start with hot reload: ```bash npm run dev ``` ## Security Considerations - API tokens are kept server-side only - CORS is configured for security - Rate limiting prevents abuse - Input validation on all endpoints - Helmet.js for security headers ## Troubleshooting ### Locations not showing - Verify table has `Geo-Location`, `latitude`, and `longitude` columns - Check that coordinates are valid numbers - Ensure API token has read permissions ### Cannot add locations - Verify API token has write permissions - Check browser console for errors - Ensure coordinates are within valid ranges ### Connection errors - Verify NocoDB instance is accessible - Check API URL format - Confirm network connectivity ### Build Script Issues - Ensure NocoDB instance is accessible - Verify API token has admin permissions - Check that the NocoDB database is clean (delete all bases before running) ## License MIT License - See LICENSE file for details ## Support For issues or questions: 1. Check the troubleshooting section 2. Review NocoDB documentation 3. Open an issue on GitHub ## Known Bugs - First load of page often fails, need to debug - Want UI for dots to have an edit button that then brings up the form