1026 lines
41 KiB
Markdown
1026 lines
41 KiB
Markdown
# NocoDB Map V- 🔒 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
|
||
- 📅 Calendar integration (Google, Outlook, Apple) for shift export
|
||
- 👥 Admin shift creation and management with volunteer assignment
|
||
- 📧 **Automated shift email notifications** - Send shift details to volunteers with visual progress tracking
|
||
- 👨💼 User management panel for admin users (create, delete users)
|
||
- 📧 **Admin broadcast emailing** - Rich HTML email composer with live preview and delivery tracking
|
||
- 📧 **Listmonk Integration** - Real-time sync with self-hosted newsletter platform for advanced email marketing
|
||
- 🔐 Role-based access control (Admin vs User permissions)Automated shift email notifications** - Send shift details to volunteers with visual progress tracking
|
||
- <20>👨💼 User management panel for admin users (create, delete users)
|
||
- <20> **Admin broadcast emailing** - Rich HTML email composer with live preview and delivery trackingntainerized 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
|
||
- <20> Calendar integration (Google, Outlook, Apple) for shift export
|
||
- <20>👥 Admin shift creation and management with volunteer assignment
|
||
- <20> **Automated shift email notifications** - Send shift details to volunteers
|
||
- <20>👨💼 User management panel for admin users (create, delete users)
|
||
- <20> **Admin broadcast emailing** - Rich HTML email composer with live preview
|
||
- <20>🔐 Role-based access control (Admin vs User permissions)
|
||
- ⏰ Temporary user accounts with automatic expiration
|
||
- 📧 Email notifications and password recovery via SMTP
|
||
- 📊 CSV data import with batch geocoding, visual progress tracking, and downloadable error reports
|
||
- ✂️ **Cut feature for geographic overlays** - Admin-drawn polygons for map regions
|
||
- 🗺️ Interactive polygon drawing with click-to-add-points system
|
||
- 🎨 Customizable cut properties (color, opacity, category, visibility)
|
||
- 🐳 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 (Required for full functionality)
|
||
# Used for password recovery, shift notifications, and admin broadcast emails
|
||
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
|
||
|
||
# Listmonk Integration (Optional - enhances email marketing capabilities)
|
||
LISTMONK_URL=http://localhost:9000
|
||
LISTMONK_USERNAME=admin
|
||
LISTMONK_PASSWORD=your-listmonk-password
|
||
LISTMONK_ENABLED=true
|
||
LISTMONK_SYNC_ON_STARTUP=true
|
||
LISTMONK_AUTO_CREATE_LISTS=true
|
||
```
|
||
|
||
3. **Auto-Create Database Structure**
|
||
|
||
Run the build script to create required tables:
|
||
```bash
|
||
chmod +x build-nocodb.sh
|
||
./build-nocodb.sh
|
||
```
|
||
|
||
This creates six 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
|
||
- **Cuts** - Geographic polygon overlays for map regions
|
||
|
||
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
|
||
NOCODB_CUTS_SHEET=https://db.cmlite.org/dashboard/#/nc/pnsalzrup2zqvz8/mxxxxxxxxxxxxxx
|
||
```
|
||
|
||
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
|
||
- `UserType` (Single Select): Options: "user" (Blue), "temp" (Orange) - User role type
|
||
- `ExpiresAt` (DateTime): Expiration date for temporary users
|
||
- `ExpireDays` (Number): Number of days until temp user expires
|
||
- `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)
|
||
|
||
### Cuts Table
|
||
- `ID` (ID): Auto-incrementing primary key
|
||
- `name` (Single Line Text): Cut name/title (required)
|
||
- `description` (Long Text): Detailed description of the cut
|
||
- `geojson` (Long Text): GeoJSON polygon data (required)
|
||
- `bounds_north` (Decimal): Northern boundary latitude (Precision 8, Scale 8)
|
||
- `bounds_south` (Decimal): Southern boundary latitude (Precision 8, Scale 8)
|
||
- `bounds_east` (Decimal): Eastern boundary longitude (Precision 8, Scale 8)
|
||
- `bounds_west` (Decimal): Western boundary longitude (Precision 8, Scale 8)
|
||
- `color` (Single Line Text): Hex color code (default: "#007bff")
|
||
- `opacity` (Decimal): Fill opacity 0-1 (Precision 3, Scale 2, default: 0.3)
|
||
- `category` (Single Line Text): Category/tag for organization
|
||
- `is_public` (Checkbox): Whether cut is visible to non-admin users (default: true)
|
||
- `created_by` (Single Line Text): Creator email
|
||
- `created_at` (DateTime): Creation timestamp
|
||
- `updated_at` (DateTime): Last update timestamp
|
||
|
||
## Email Features
|
||
|
||
The system includes comprehensive email functionality powered by SMTP configuration:
|
||
|
||
### 🔧 Admin Email Broadcasting
|
||
- **Rich HTML email composer** with live preview and formatting toolbar
|
||
- **Mass email sending** to all registered users
|
||
- **Professional email templates** with consistent branding
|
||
- **Real-time recipient counting** and success/failure tracking
|
||
- **Visual progress tracking** with animated progress bars and individual email status
|
||
- **Detailed delivery reports** showing successful sends and failure reasons
|
||
- Support for rich text content including headers, lists, links, and formatting
|
||
|
||
### 📅 Shift Email Notifications
|
||
- **Automated shift detail emails** sent to all volunteers
|
||
- **Professional HTML templates** with complete shift information
|
||
- **Shift status tracking** (open, full, cancelled) in emails
|
||
- **Volunteer management** - add/remove users from shifts
|
||
- **Calendar integration** - Google, Outlook, and Apple calendar export
|
||
- **Visual progress indicators** with real-time sending status per volunteer
|
||
- **Email status reporting** with success/failure details and error messages
|
||
|
||
### 👤 User Account Emails
|
||
- **Login credential delivery** for new users
|
||
- **Password recovery** with secure email notifications
|
||
- **Temporary user accounts** with automatic expiration tracking
|
||
- **Professional HTML and plain text** email templates
|
||
|
||
### 📧 Email Template System
|
||
- **Responsive HTML templates** with professional design
|
||
- **Variable substitution** for personalized content
|
||
- **Multi-format support** (HTML and plain text)
|
||
- **Consistent branding** across all email communications
|
||
|
||
## Listmonk Integration
|
||
|
||
The application includes real-time integration with [Listmonk](https://listmonk.app/), a self-hosted newsletter and mailing list manager, enabling advanced email marketing capabilities and subscriber management.
|
||
|
||
### 🔄 Real-Time Synchronization
|
||
|
||
The system automatically synchronizes data between the Map application and Listmonk:
|
||
|
||
- **Automatic List Creation**: Creates and maintains email lists for different user segments
|
||
- **Real-Time Updates**: New locations and users are instantly synced to Listmonk
|
||
- **Bi-Directional Sync**: Unsubscribes in Listmonk update the Map database
|
||
- **Error Handling**: Visual status indicators and detailed logging for sync failures
|
||
|
||
### 📋 Automated List Management
|
||
|
||
The integration automatically manages the following Listmonk subscriber lists:
|
||
|
||
- **All Locations**: Complete list of all mapped locations with contact information
|
||
- **All Users**: List of all registered Map application users
|
||
- **Support Level Lists**: Separate lists for each support level (1-4) for targeted campaigns
|
||
- **Sign Status Lists**: Lists based on campaign sign status (Has Sign, No Sign)
|
||
- **Combined Lists**: Smart combinations like "Support Level 4 with Signs" for precision targeting
|
||
|
||
### ⚙️ Configuration
|
||
|
||
Add the following environment variables to your `.env` file:
|
||
|
||
```env
|
||
# Listmonk Integration (Optional - enhances email marketing capabilities)
|
||
LISTMONK_API_URL=http://172.20.0.9:9000/api
|
||
LISTMONK_USERNAME=admin
|
||
LISTMONK_PASSWORD=your-listmonk-password
|
||
LISTMONK_SYNC_ENABLED=true
|
||
|
||
# Listmonk Sync Settings
|
||
LISTMONK_INITIAL_SYNC=true # Set to true only for first run to sync existing data
|
||
```
|
||
|
||
### 🔍 Finding the Correct Listmonk API URL
|
||
|
||
The `LISTMONK_API_URL` must point to the internal Docker network address where Listmonk is running. Here are several methods to find the correct URL:
|
||
|
||
#### Method 1: Using Docker Inspect (Recommended)
|
||
|
||
If Listmonk is running in a Docker container, find its internal IP address:
|
||
|
||
```bash
|
||
# List all running containers
|
||
docker ps
|
||
|
||
# Find the Listmonk container name/ID, then inspect it
|
||
docker inspect <listmonk-container-name> | grep IPAddress
|
||
|
||
# Example output:
|
||
# "IPAddress": "172.20.0.9"
|
||
```
|
||
|
||
Your `LISTMONK_API_URL` would then be: `http://172.20.0.9:9000/api`
|
||
|
||
#### Method 2: Using Docker Network Inspection
|
||
|
||
If both containers are on the same Docker network:
|
||
|
||
```bash
|
||
# List Docker networks
|
||
docker network ls
|
||
|
||
# Inspect the network (usually named after your docker-compose project)
|
||
docker network inspect <network-name>
|
||
|
||
# Look for the Listmonk container in the "Containers" section
|
||
# Note the "IPv4Address" value
|
||
```
|
||
|
||
#### Method 3: Using Container Names (Docker Compose)
|
||
|
||
If using Docker Compose with both services in the same `docker-compose.yml`:
|
||
|
||
```yaml
|
||
# In your docker-compose.yml
|
||
services:
|
||
listmonk:
|
||
image: listmonk/listmonk:latest
|
||
container_name: listmonk
|
||
# ... other config
|
||
|
||
map-app:
|
||
# ... your map application config
|
||
```
|
||
|
||
You can use the service name: `LISTMONK_API_URL=http://listmonk:9000/api`
|
||
|
||
#### Method 4: Check Docker Compose Logs
|
||
|
||
```bash
|
||
# View Listmonk container logs to see what IP it's binding to
|
||
docker-compose logs listmonk
|
||
|
||
# Look for lines like:
|
||
# listmonk_1 | INFO[0000] listmonk started. IP: 0.0.0.0:9000
|
||
```
|
||
|
||
#### Method 5: Testing Connectivity
|
||
|
||
Test if your URL is correct by running a test from within your Map application container:
|
||
|
||
```bash
|
||
# Access your map application container
|
||
docker exec -it <map-container-name> /bin/bash
|
||
|
||
# Test connectivity to Listmonk
|
||
curl http://172.20.0.9:9000/api/health
|
||
# Should return: {"data":true}
|
||
|
||
# Or test with your credentials
|
||
curl -u "admin:your-password" http://172.20.0.9:9000/api/lists
|
||
```
|
||
|
||
#### Common IP Ranges
|
||
|
||
Docker typically assigns containers to these network ranges:
|
||
- `172.17.0.x` - Default bridge network
|
||
- `172.18.0.x` - Custom bridge networks
|
||
- `172.20.0.x` - Docker Compose networks
|
||
- `192.168.x.x` - Some custom networks
|
||
|
||
#### Troubleshooting Connection Issues
|
||
|
||
If you're having connection problems:
|
||
|
||
1. **Verify Listmonk is running**: `docker ps | grep listmonk`
|
||
2. **Check port exposure**: Ensure Listmonk exposes port 9000
|
||
3. **Network connectivity**: Both containers must be on the same Docker network
|
||
4. **Firewall rules**: Ensure no firewalls block inter-container communication
|
||
5. **DNS resolution**: Try IP address instead of container name if DNS fails
|
||
|
||
#### Example Working Configuration
|
||
|
||
```env
|
||
# Working example from a real deployment
|
||
LISTMONK_API_URL=http://172.20.0.9:9000/api
|
||
LISTMONK_USERNAME=API
|
||
LISTMONK_PASSWORD=s0f6qSuWTGMX8AWbz8f4EQxGFSZCZxAC
|
||
LISTMONK_SYNC_ENABLED=true
|
||
LISTMONK_INITIAL_SYNC=true
|
||
```
|
||
|
||
> **Note**: Never use `localhost` or `127.0.0.1` for the Listmonk URL when running in Docker containers, as this refers to the container's own loopback interface, not the Listmonk container.
|
||
|
||
### 📊 Admin Features
|
||
|
||
Administrators have access to comprehensive Listmonk management tools:
|
||
|
||
#### Real-Time Status Monitoring
|
||
- **Connection Status**: Live indicator showing Listmonk connectivity
|
||
- **Sync Statistics**: Real-time counts of subscribers, lists, and sync operations
|
||
- **Error Notifications**: Popup alerts and terminal logs for sync failures
|
||
- **Last Sync Timestamps**: Track when data was last synchronized
|
||
|
||
#### Bulk Operations
|
||
- **Full Resync**: Manually trigger complete data synchronization
|
||
- **Selective Sync**: Sync specific data types (locations, users, lists)
|
||
- **Progress Tracking**: Visual progress bars for bulk operations
|
||
- **Detailed Reports**: Success/failure counts with error details
|
||
|
||
#### List Management
|
||
- **Auto-List Creation**: Automatically create and maintain subscriber lists
|
||
- **Custom Segmentation**: Create lists based on support levels, sign status, and combinations
|
||
- **Subscriber Counts**: Real-time subscriber counts for each list
|
||
- **List Status Monitoring**: Track list health and sync status
|
||
|
||
### 🔌 API Integration Points
|
||
|
||
The Listmonk integration adds the following API endpoints:
|
||
|
||
#### Admin Listmonk Endpoints (requires admin privileges)
|
||
- `GET /api/listmonk/status` - Get connection status and sync statistics
|
||
- `POST /api/listmonk/sync/full` - Trigger full synchronization
|
||
- `POST /api/listmonk/sync/locations` - Sync locations only
|
||
- `POST /api/listmonk/sync/users` - Sync users only
|
||
- `POST /api/listmonk/test-connection` - Test Listmonk connectivity
|
||
- `POST /api/listmonk/reinitialize` - Recreate all lists and resync data
|
||
- `GET /api/listmonk/lists` - Get all Listmonk lists with subscriber counts
|
||
|
||
### 🚀 Getting Started with Listmonk
|
||
|
||
1. **Set up Listmonk**: Deploy Listmonk using Docker Compose (see parent project documentation)
|
||
2. **Configure Integration**: Add Listmonk credentials to your `.env` file
|
||
3. **Initialize Lists**: Lists are automatically created on first startup
|
||
4. **Monitor Status**: Check the admin panel for sync status and connection health
|
||
5. **Customize Campaigns**: Use Listmonk's web interface to create targeted email campaigns
|
||
|
||
### 🔍 Status Indicators
|
||
|
||
The system provides visual feedback on integration status:
|
||
|
||
- **🟢 Connected**: Listmonk is accessible and syncing properly
|
||
- **🟡 Warning**: Sync delays or minor issues detected
|
||
- **🔴 Error**: Connection failed or sync errors occurred
|
||
- **⚪ Disabled**: Listmonk integration is disabled
|
||
|
||
### 📈 Benefits
|
||
|
||
- **Enhanced Email Marketing**: Leverage Listmonk's advanced campaign features
|
||
- **Automated Segmentation**: Automatic subscriber lists based on Map data
|
||
- **Improved Deliverability**: Professional email marketing infrastructure
|
||
- **Real-Time Updates**: Always-current subscriber information
|
||
- **Detailed Analytics**: Track email engagement and campaign performance
|
||
- **Professional Templates**: Rich HTML email templates and campaign builder
|
||
|
||
### 🔧 Troubleshooting
|
||
|
||
Common issues and solutions:
|
||
|
||
- **Connection Failed**: Verify Listmonk URL and credentials in `.env`
|
||
- **Sync Errors**: Check terminal logs for detailed error messages
|
||
- **Missing Lists**: Enable `LISTMONK_AUTO_CREATE_LISTS=true` and restart
|
||
- **Slow Sync**: Monitor network connectivity and Listmonk performance
|
||
- **Duplicate Subscribers**: The system automatically handles duplicates using email addresses
|
||
|
||
## 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
|
||
- `POST /api/shifts/admin/:shiftId/add-user` - Add user to shift
|
||
- `DELETE /api/shifts/admin/:shiftId/remove-user/:userId` - Remove user from shift
|
||
- `POST /api/shifts/admin/:shiftId/email-details` - Email shift details to all volunteers
|
||
|
||
### 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
|
||
|
||
### User Management Endpoints (requires admin privileges)
|
||
|
||
- `GET /api/users` - Get all users
|
||
- `POST /api/users` - Create new user
|
||
- `DELETE /api/users/:id` - Delete user
|
||
- `POST /api/users/:id/send-login-details` - Send login details to user
|
||
- `POST /api/users/email-all` - Send broadcast email to all users
|
||
|
||
### Cuts Endpoints
|
||
|
||
#### Public Cuts Endpoints (requires authentication)
|
||
- `GET /api/cuts/public` - Get all public cuts visible to users
|
||
|
||
#### Admin Cuts Endpoints (requires admin privileges)
|
||
- `GET /api/cuts` - Get all cuts (including private ones)
|
||
- `POST /api/cuts` - Create new cut
|
||
- `GET /api/cuts/:id` - Get single cut by ID
|
||
- `PUT /api/cuts/:id` - Update existing cut
|
||
- `DELETE /api/cuts/:id` - Delete cut
|
||
|
||
### Geocoding Endpoints (requires authentication)
|
||
|
||
- `GET /api/geocode/reverse?lat=<lat>&lng=<lng>` - Reverse geocode coordinates to address
|
||
- `GET /api/geocode/forward?address=<address>` - Forward geocode address to coordinates
|
||
- `GET /api/geocode/search?query=<query>&limit=<number>` - 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
|
||
- **Email Notifications**: Send shift details to all volunteers via email
|
||
|
||
### 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.
|
||
|
||
## Cut Feature - Geographic Overlays
|
||
|
||
The Cut feature allows administrators to create and manage polygon overlays on the map, useful for defining geographic regions, neighborhoods, or operational areas.
|
||
|
||
### Admin Cut Creation
|
||
|
||
Administrators can create cuts through the admin panel at `/admin.html`:
|
||
|
||
- **Interactive Drawing**: Click points on the map to define polygon boundaries
|
||
- **Real-time Preview**: See the polygon shape as you draw
|
||
- **Point Management**: Add points by clicking, finish by clicking the first point or using the complete button
|
||
- **Visual Feedback**: Clear indicators for drawing mode and vertex points
|
||
|
||
### Cut Properties
|
||
|
||
Each cut supports the following properties:
|
||
|
||
- **Name**: Required title for the cut (e.g., "Downtown District", "Canvassing Area A")
|
||
- **Description**: Optional detailed description of the cut's purpose
|
||
- **Color**: Hex color code for the polygon border and fill (default: "#007bff")
|
||
- **Opacity**: Fill transparency from 0.0 (transparent) to 1.0 (opaque) (default: 0.3)
|
||
- **Category**: Optional categorization tag for organization
|
||
- **Visibility**: Public (visible to all users) or Private (admin-only)
|
||
|
||
### Cut Management
|
||
|
||
- **View All Cuts**: List all existing cuts with their properties
|
||
- **Edit Cuts**: Modify any cut property after creation
|
||
- **Delete Cuts**: Remove cuts with confirmation prompts
|
||
- **Import/Export**: JSON format for backup and migration
|
||
- **Real-time Updates**: Changes appear immediately on all connected maps
|
||
|
||
### Public Cut Display
|
||
|
||
Public cuts are automatically displayed on the main map for all authenticated users:
|
||
|
||
- **Polygon Overlays**: Cuts appear as colored polygon overlays
|
||
- **Non-Interactive**: Users can see cuts but cannot modify them
|
||
- **Responsive**: Cuts adapt to different screen sizes and zoom levels
|
||
- **Performance Optimized**: Efficient rendering for multiple cuts
|
||
|
||
### Use Cases
|
||
|
||
- **Canvassing Districts**: Define geographic areas for volunteer assignments
|
||
- **Neighborhood Boundaries**: Mark community or administrative boundaries
|
||
- **Event Areas**: Highlight locations for rallies, meetings, or activities
|
||
- **Restricted Zones**: Mark areas requiring special attention or restrictions
|
||
- **Progress Tracking**: Visual representation of completed campaign areas
|
||
|
||
## 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
|
||
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
|
||
- `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
|
||
|
||
### 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
|
||
|
||
### 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
|
||
|
||
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
|
||
|
||
#### Cut Management
|
||
|
||
- **Interactive Drawing**: Click-to-add-points polygon drawing system on the map
|
||
- **Cut Properties**: Configure name, description, color, opacity, and category
|
||
- **Visibility Control**: Set cuts as public (visible to all users) or private (admin-only)
|
||
- **Real-time Preview**: See cut polygons rendered on the map during creation
|
||
- **Cut Management**: View, edit, and delete existing cuts with full CRUD operations
|
||
- **Import/Export**: JSON import/export functionality for cut data backup and migration
|
||
- **Map Integration**: Cuts display as colored polygon overlays on both admin and public maps
|
||
- **Responsive Design**: Touch-friendly interface for mobile and tablet devices
|
||
|
||
#### 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 (required for email features) | smtp.gmail.com |
|
||
| `SMTP_PORT` | SMTP server port (required for email features) | 587 |
|
||
| `SMTP_SECURE` | Use SSL for SMTP (required for email features) | false |
|
||
| `SMTP_USER` | SMTP username (required for email features) | your-email@gmail.com |
|
||
| `SMTP_PASS` | SMTP password (required for email features) | your-app-password |
|
||
| `EMAIL_FROM_NAME` | Sender name for outgoing emails | CMlite Support |
|
||
| `EMAIL_FROM_ADDRESS` | Sender email address for outgoing emails | noreply@cmlite.org |
|
||
| `APP_NAME` | Application name used in emails and branding | CMlite Map |
|
||
| `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
|
||
```
|
||
|
||
## Usage Guide
|
||
|
||
### Email Features
|
||
|
||
#### Admin Broadcasting
|
||
1. Access the admin panel at `/admin.html`
|
||
2. Navigate to the "User Management" section
|
||
3. Click "Email All Users" to open the email composer
|
||
4. Use the rich text editor to format your message
|
||
5. Preview your email before sending
|
||
6. Send to all registered users with delivery tracking
|
||
|
||
#### Shift Email Notifications
|
||
1. In the admin panel, go to "Shift Management"
|
||
2. Click "Manage Volunteers" for any shift
|
||
3. Add or remove volunteers as needed
|
||
4. Click "Email Shift Details" to notify all volunteers
|
||
5. Professional emails include shift details, location, and calendar links
|
||
|
||
#### User Management
|
||
1. Create new users (regular or temporary with expiration)
|
||
2. Send login credentials automatically via email
|
||
3. Password recovery through secure email notifications
|
||
4. Temporary users receive expiration warnings
|
||
|
||
#### Calendar Integration
|
||
- Users can export shifts to Google Calendar, Outlook, or Apple Calendar
|
||
- Calendar invites include all shift details and location information
|
||
- One-click calendar integration for better volunteer organization
|
||
|
||
## 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)
|
||
|
||
### Email Issues
|
||
|
||
**Emails not sending:**
|
||
- Verify SMTP configuration in `.env` file
|
||
- Check SMTP credentials are correct
|
||
- Test SMTP connection with your email provider
|
||
- Review application logs for email errors
|
||
|
||
**Gmail SMTP setup:**
|
||
1. Enable 2-factor authentication on your Google account
|
||
2. Generate an App Password (not your regular password)
|
||
3. Use `smtp.gmail.com` as SMTP_HOST with port 587
|
||
4. Set SMTP_SECURE to `false` for STARTTLS
|
||
|
||
**Email templates not rendering:**
|
||
- Check that all template files exist in `app/templates/email/`
|
||
- Verify template variable names match controller implementations
|
||
- Review logs for template rendering errors
|
||
|
||
**Shift emails not working:**
|
||
- Ensure shift signups table is properly configured
|
||
- Verify users have valid email addresses
|
||
- Check that email templates include all required variables
|
||
|
||
## 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 |