feat: Implement comprehensive Contacts Management System with API and UI integration
- Added new `contacts` and `project_contacts` database tables for managing contacts. - Created API endpoints for CRUD operations on contacts and linking them to projects. - Developed UI components including `ContactForm` and `ProjectContactSelector`. - Integrated navigation and translations for Polish language support. - Documented usage, features, and future enhancements for the contacts system. feat: Introduce DOCX Template System for generating documents from templates - Enabled creation and management of DOCX templates with placeholders for project data. - Documented the process for creating templates, uploading, and generating documents. - Included detailed information on available variables and custom data fields. - Implemented troubleshooting guidelines for common issues related to template generation. feat: Add Radicale CardDAV Sync Integration for automatic contact synchronization - Implemented automatic syncing of contacts to a Radicale server on create/update/delete actions. - Documented setup instructions, including environment variable configuration and initial sync script. - Provided troubleshooting steps for common sync issues and error codes. feat: Develop Route Planning Feature with Optimization using OpenRouteService API - Integrated multi-point routing and automatic optimization for project locations. - Documented setup, usage, and technical implementation details for route planning. - Included performance considerations and troubleshooting for common routing issues. chore: Remove unnecessary files and scripts from the codebase - Deleted temporary, debug-related, and test-specific files that are not needed in production. - Reviewed and ensured core application code and essential documentation remain intact.
This commit is contained in:
87
docs/README.md
Normal file
87
docs/README.md
Normal file
@@ -0,0 +1,87 @@
|
||||
# Documentation Index
|
||||
|
||||
**eProjektant Wastpol** - Complete documentation directory
|
||||
|
||||
---
|
||||
|
||||
## 📚 Main Documentation
|
||||
|
||||
- **[Main README](../README.md)** - Project overview, installation, API reference, deployment
|
||||
- **[Roadmap](../ROADMAP.md)** - Development roadmap, feature priorities, timelines
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Feature Documentation
|
||||
|
||||
### Core Features
|
||||
- **[Contacts System](features/CONTACTS_SYSTEM.md)** - Contact management, CardDAV sync, project linking
|
||||
- **[DOCX Templates](features/DOCX_TEMPLATES.md)** - Document generation, available variables, examples
|
||||
- **[Radicale Sync](features/RADICALE_SYNC.md)** - CardDAV integration, automatic sync, troubleshooting
|
||||
- **[Route Planning](features/ROUTE_PLANNING.md)** - Route optimization, multi-point routing, ORS integration
|
||||
|
||||
### Map System
|
||||
- **[Map Layers](MAP_LAYERS.md)** - WMTS/WMS configuration, adding custom layers, Polish geoportal
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Deployment Documentation
|
||||
|
||||
- **[Advanced Deployment](deployment/ADVANCED_DEPLOYMENT.md)** - Detailed deployment strategies
|
||||
- **[Git-Based Deployment](deployment/GIT_DEPLOYMENT.md)** - Git repository deployment, CI/CD
|
||||
|
||||
---
|
||||
|
||||
## 📖 Quick Links by Topic
|
||||
|
||||
### Getting Started
|
||||
1. [Installation Guide](../README.md#-getting-started)
|
||||
2. [Environment Configuration](../README.md#configuration)
|
||||
3. [Creating Admin User](../README.md#getting-started)
|
||||
4. [Docker Setup](../README.md#-docker-commands)
|
||||
|
||||
### Development
|
||||
1. [Project Structure](../README.md#-project-structure)
|
||||
2. [Available Scripts](../README.md#-available-scripts)
|
||||
3. [Database Schema](../README.md#%EF%B8%8F-database-schema)
|
||||
4. [Testing](../README.md#-testing)
|
||||
|
||||
### Features
|
||||
1. [Authentication & Roles](../README.md#-security--authentication)
|
||||
2. [Project Management](../README.md#-project-management)
|
||||
3. [Task System](../README.md#-advanced-task-system)
|
||||
4. [Notifications](../README.md#-notification-system)
|
||||
5. [GIS/Mapping](MAP_LAYERS.md)
|
||||
6. [Document Generation](features/DOCX_TEMPLATES.md)
|
||||
7. [Contact Management](features/CONTACTS_SYSTEM.md)
|
||||
|
||||
### API
|
||||
1. [API Endpoints](../README.md#-api-endpoints)
|
||||
2. [Authentication Endpoints](../README.md#authentication)
|
||||
3. [Projects API](../README.md#projects)
|
||||
4. [Contacts API](../README.md#contacts)
|
||||
|
||||
### Deployment
|
||||
1. [Production Deployment](../README.md#-deployment)
|
||||
2. [Docker Deployment](deployment/ADVANCED_DEPLOYMENT.md)
|
||||
3. [Git-Based Deployment](deployment/GIT_DEPLOYMENT.md)
|
||||
4. [Environment Variables](../README.md#environment-variables)
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
- **Database Issues**: See [README - Troubleshooting](../README.md#-troubleshooting)
|
||||
- **Map Layers**: See [Map Layers Guide](MAP_LAYERS.md)
|
||||
- **CardDAV Sync**: See [Radicale Sync](features/RADICALE_SYNC.md)
|
||||
- **Route Planning**: See [Route Planning Guide](features/ROUTE_PLANNING.md)
|
||||
|
||||
---
|
||||
|
||||
## 📝 Contributing
|
||||
|
||||
See [ROADMAP.md](../ROADMAP.md) for development priorities and [README - Contributing](../README.md#-contributing) for guidelines.
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: January 16, 2026
|
||||
**Version**: 0.1.1
|
||||
174
docs/features/CONTACTS_SYSTEM.md
Normal file
174
docs/features/CONTACTS_SYSTEM.md
Normal file
@@ -0,0 +1,174 @@
|
||||
# Contacts Management System
|
||||
|
||||
## Overview
|
||||
|
||||
A comprehensive contacts management system has been implemented to replace the simple text field for project contacts. This system allows you to:
|
||||
|
||||
- **Create and manage a centralized contact database**
|
||||
- **Link multiple contacts to each project**
|
||||
- **Categorize contacts** (Project contacts, Contractors, Offices, Suppliers, etc.)
|
||||
- **Track contact details** (name, phone, email, company, position)
|
||||
- **Set primary contacts** for projects
|
||||
- **Search and filter** contacts easily
|
||||
|
||||
## What Was Implemented
|
||||
|
||||
### 1. Database Schema
|
||||
|
||||
**New Tables:**
|
||||
|
||||
- **`contacts`** - Stores all contact information
|
||||
- `contact_id` (Primary Key)
|
||||
- `name`, `phone`, `email`, `company`, `position`
|
||||
- `contact_type` (project/contractor/office/supplier/other)
|
||||
- `notes`, `is_active`
|
||||
- `created_at`, `updated_at`
|
||||
|
||||
- **`project_contacts`** - Junction table linking projects to contacts (many-to-many)
|
||||
- `project_id`, `contact_id` (Composite Primary Key)
|
||||
- `relationship_type`, `is_primary`
|
||||
- `added_at`, `added_by`
|
||||
|
||||
### 2. API Endpoints
|
||||
|
||||
- **`GET /api/contacts`** - List all contacts (with filters)
|
||||
- **`POST /api/contacts`** - Create new contact
|
||||
- **`GET /api/contacts/[id]`** - Get contact details
|
||||
- **`PUT /api/contacts/[id]`** - Update contact
|
||||
- **`DELETE /api/contacts/[id]`** - Delete contact (soft/hard)
|
||||
- **`GET /api/projects/[id]/contacts`** - Get project's contacts
|
||||
- **`POST /api/projects/[id]/contacts`** - Link contact to project
|
||||
- **`DELETE /api/projects/[id]/contacts`** - Unlink contact from project
|
||||
- **`PATCH /api/projects/[id]/contacts`** - Set primary contact
|
||||
|
||||
### 3. UI Components
|
||||
|
||||
- **`ContactForm`** - Create/edit contact form
|
||||
- **`/contacts` page** - Full contacts management interface with:
|
||||
- Statistics dashboard
|
||||
- Search and filtering
|
||||
- Contact cards with quick actions
|
||||
- CRUD operations
|
||||
- **`ProjectContactSelector`** - Multi-contact selector for projects
|
||||
- View linked contacts
|
||||
- Add/remove contacts
|
||||
- Set primary contact
|
||||
- Real-time search
|
||||
|
||||
### 4. Integration
|
||||
|
||||
- **Navigation** - "Kontakty" link added to main navigation
|
||||
- **ProjectForm** - Contact text field replaced with `ProjectContactSelector`
|
||||
- **Translations** - Polish translations added to i18n
|
||||
- **Query Functions** - Comprehensive database query functions in `src/lib/queries/contacts.js`
|
||||
|
||||
## How to Use
|
||||
|
||||
### Initial Setup
|
||||
|
||||
1. **Run the migration script** to create the new tables:
|
||||
```bash
|
||||
node migrate-contacts.mjs
|
||||
```
|
||||
|
||||
2. **Start your development server**:
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
3. **Visit** `http://localhost:3000/contacts` to start adding contacts
|
||||
|
||||
### Managing Contacts
|
||||
|
||||
1. **Create Contacts**:
|
||||
- Go to `/contacts`
|
||||
- Click "Dodaj kontakt"
|
||||
- Fill in contact details
|
||||
- Select contact type (Project/Contractor/Office/Supplier/Other)
|
||||
|
||||
2. **Link Contacts to Projects**:
|
||||
- Edit any project
|
||||
- In the "Kontakty do projektu" section
|
||||
- Click "+ Dodaj kontakt"
|
||||
- Search and add contacts
|
||||
- Set one as primary if needed
|
||||
|
||||
3. **View Contact Details**:
|
||||
- Contacts page shows all contacts with:
|
||||
- Contact information (phone, email, company)
|
||||
- Number of linked projects
|
||||
- Contact type badges
|
||||
- Edit or delete contacts as needed
|
||||
|
||||
### Contact Types
|
||||
|
||||
- **Kontakt projektowy (Project)** - Project-specific contacts
|
||||
- **Wykonawca (Contractor)** - Construction contractors
|
||||
- **Urząd (Office)** - Government offices, municipalities
|
||||
- **Dostawca (Supplier)** - Material suppliers, vendors
|
||||
- **Inny (Other)** - Any other type of contact
|
||||
|
||||
### Features
|
||||
|
||||
- **Search** - Search by name, phone, email, or company
|
||||
- **Filter** - Filter by contact type
|
||||
- **Statistics** - See breakdown of contacts by type
|
||||
- **Multiple Contacts per Project** - Link as many contacts as needed
|
||||
- **Primary Contact** - Mark one contact as primary for each project
|
||||
- **Bidirectional Links** - See which projects a contact is linked to
|
||||
- **Soft Delete** - Deleted contacts are marked inactive, not removed
|
||||
|
||||
## Database Migration Notes
|
||||
|
||||
- The **old `contact` text field** in the `projects` table is still present
|
||||
- It hasn't been removed for backward compatibility
|
||||
- You can manually migrate old contact data by:
|
||||
1. Creating contacts from the old text data
|
||||
2. Linking them to the appropriate projects
|
||||
3. The old field will remain for reference
|
||||
|
||||
## File Structure
|
||||
|
||||
```
|
||||
src/
|
||||
├── app/
|
||||
│ ├── api/
|
||||
│ │ ├── contacts/
|
||||
│ │ │ ├── route.js # List/Create contacts
|
||||
│ │ │ └── [id]/
|
||||
│ │ │ └── route.js # Get/Update/Delete contact
|
||||
│ │ └── projects/
|
||||
│ │ └── [id]/
|
||||
│ │ └── contacts/
|
||||
│ │ └── route.js # Link/unlink contacts to project
|
||||
│ └── contacts/
|
||||
│ └── page.js # Contacts management page
|
||||
├── components/
|
||||
│ ├── ContactForm.js # Contact form component
|
||||
│ └── ProjectContactSelector.js # Project contact selector
|
||||
└── lib/
|
||||
├── queries/
|
||||
│ └── contacts.js # Database query functions
|
||||
└── init-db.js # Database schema with new tables
|
||||
```
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Potential improvements you could add:
|
||||
|
||||
- Contact import/export (CSV, Excel)
|
||||
- Contact groups or tags
|
||||
- Contact activity history
|
||||
- Email integration
|
||||
- Contact notes and history
|
||||
- Duplicate contact detection
|
||||
- Contact merge functionality
|
||||
- Advanced relationship types
|
||||
- Contact sharing between projects
|
||||
- Contact reminders/follow-ups
|
||||
|
||||
## Support
|
||||
|
||||
The old contact text field remains in the database, so no existing data is lost. You can gradually migrate to the new system at your own pace.
|
||||
|
||||
Enjoy your new contacts management system! 🎉
|
||||
286
docs/features/DOCX_TEMPLATES.md
Normal file
286
docs/features/DOCX_TEMPLATES.md
Normal file
@@ -0,0 +1,286 @@
|
||||
# DOCX Template System
|
||||
|
||||
This system allows you to generate DOCX documents by filling templates with project data.
|
||||
|
||||
## How to Create Templates
|
||||
|
||||
1. **Create a DOCX Template**: Use Microsoft Word or any DOCX editor to create your template.
|
||||
|
||||
2. **Add Placeholders**: Use single curly braces `{variableName}` to mark where data should be inserted. Available variables:
|
||||
|
||||
### Available Variables (with duplicates for repeated use)
|
||||
|
||||
#### Project Information
|
||||
- `{project_name}`, `{project_name_1}`, `{project_name_2}`, `{project_name_3}` - Project name
|
||||
- `{project_number}`, `{project_number_1}`, `{project_number_2}` - Project number
|
||||
- `{address}`, `{address_1}`, `{address_2}` - Project address
|
||||
- `{city}`, `{city_1}`, `{city_2}` - City
|
||||
- `{plot}` - Plot number
|
||||
- `{district}` - District
|
||||
- `{unit}` - Unit
|
||||
- `{investment_number}` - Investment number
|
||||
- `{wp}` - WP number
|
||||
- `{coordinates}` - GPS coordinates
|
||||
- `{notes}` - Project notes
|
||||
|
||||
#### Processed/Transformed Fields
|
||||
- `{investment_number_short}` - Last part of investment number after last dash (e.g., "1234567" from "I-BC-DE-1234567")
|
||||
- `{project_number_short}` - Last part of project number after last dash
|
||||
- `{project_name_upper}` - Project name in uppercase
|
||||
- `{project_name_lower}` - Project name in lowercase
|
||||
- `{city_upper}` - City name in uppercase
|
||||
- `{customer_upper}` - Customer name in uppercase
|
||||
|
||||
#### Contract Information
|
||||
- `{contract_number}` - Contract number
|
||||
- `{customer_contract_number}` - Customer contract number
|
||||
- `{customer}`, `{customer_1}`, `{customer_2}` - Customer name
|
||||
- `{investor}` - Investor name
|
||||
|
||||
#### Dates
|
||||
- `{finish_date}` - Finish date (formatted)
|
||||
- `{completion_date}` - Completion date (formatted)
|
||||
- `{today_date}` - Today's date
|
||||
|
||||
#### Project Type & Status
|
||||
- `{project_type}` - Project type (design/construction/design+construction)
|
||||
- `{project_status}` - Project status
|
||||
|
||||
#### Financial
|
||||
- `{wartosc_zlecenia}`, `{wartosc_zlecenia_1}`, `{wartosc_zlecenia_2}` - Contract value
|
||||
|
||||
#### Standard Custom Fields (Pre-filled but Editable)
|
||||
- `{zk}` - ZK field
|
||||
- `{nr_zk}` - ZK number
|
||||
- `{kabel}` - Cable information
|
||||
- `{dlugosc}` - Length
|
||||
- `{data_wykonania}` - Execution date
|
||||
- `{st_nr}` - Station number
|
||||
- `{obw}` - Circuit
|
||||
- `{wp_short}` - Short WP reference
|
||||
- `{plomba}` - Seal/plomb information
|
||||
|
||||
## Example Template Content
|
||||
|
||||
```
|
||||
Project Report
|
||||
|
||||
Project Name: {project_name} ({project_name_upper})
|
||||
Project Number: {project_number} (Short: {project_number_short})
|
||||
Location: {city_upper}, {address}
|
||||
|
||||
Investment Details:
|
||||
Full Investment Number: {investment_number}
|
||||
Short Investment Number: {investment_number_short}
|
||||
|
||||
Contract Details:
|
||||
Contract Number: {contract_number}
|
||||
Customer: {customer} ({customer_upper})
|
||||
Value: {wartosc_zlecenia} PLN
|
||||
|
||||
Custom Information:
|
||||
Meeting Notes: {meeting_notes}
|
||||
Special Instructions: {special_instructions}
|
||||
Additional Comments: {additional_comments}
|
||||
|
||||
Technical Details:
|
||||
ZK: {zk}
|
||||
ZK Number: {nr_zk}
|
||||
Cable: {kabel}
|
||||
Length: {dlugosc}
|
||||
Execution Date: {data_wykonania}
|
||||
Station Number: {st_nr}
|
||||
Circuit: {obw}
|
||||
WP Short: {wp_short}
|
||||
Seal: {plomba}
|
||||
|
||||
Primary Contact:
|
||||
Name: {primary_contact}
|
||||
Phone: {primary_contact_phone}
|
||||
Email: {primary_contact_email}
|
||||
|
||||
Generated on: {today_date}
|
||||
```
|
||||
|
||||
## Uploading Templates
|
||||
|
||||
1. Go to the Templates page (`/templates`)
|
||||
2. Click "Add Template"
|
||||
3. Provide a name and description
|
||||
4. Upload your DOCX file
|
||||
5. The template will be available for generating documents
|
||||
|
||||
## Generating Documents
|
||||
|
||||
1. Open any project page
|
||||
2. In the sidebar, find the "Generate Document" section
|
||||
3. Select a template from the dropdown
|
||||
4. **Optional**: Click "Pokaż dodatkowe pola" to add custom data
|
||||
5. Fill in the standard fields (zk, nr_zk, kabel, etc.) and any additional custom fields
|
||||
6. Click "Generate Document"
|
||||
7. The filled document will be downloaded automatically with filename: `{template_name}_{project_name}_{timestamp}.docx`
|
||||
|
||||
## Custom Data Fields
|
||||
|
||||
During document generation, you can add custom data that will be merged with the project data:
|
||||
|
||||
### Standard Fields (Pre-filled but Fully Editable)
|
||||
These fields are pre-filled with common names but can be modified or removed:
|
||||
- `zk`, `nr_zk`, `kabel`, `dlugosc`, `data_wykonania`, `st_nr`, `obw`, `wp_short`, `plomba`
|
||||
|
||||
### Additional Custom Fields
|
||||
- **Custom fields** override project data if they have the same name
|
||||
- Use descriptive names like `meeting_notes`, `special_instructions`, `custom_date`
|
||||
- Custom fields are available in templates as `{custom_field_name}`
|
||||
- Empty custom fields are ignored
|
||||
- All fields can be removed if not needed
|
||||
|
||||
### Example Custom Fields:
|
||||
- `meeting_notes`: "Please bring project documentation"
|
||||
- `special_instructions`: "Use company letterhead"
|
||||
- `custom_date`: "2025-01-15"
|
||||
- `additional_comments`: "Follow up required"
|
||||
|
||||
## Template Syntax
|
||||
|
||||
The system uses `docxtemplater` library which supports:
|
||||
- Simple variable replacement: `{variable}`
|
||||
- Loops: `{#contacts}{name}{/contacts}`
|
||||
- Conditions: `{#primary_contact}Primary: {name}{/primary_contact}`
|
||||
- Formatting and styling from your DOCX template is preserved
|
||||
|
||||
## Data Processing & Transformations
|
||||
|
||||
The system automatically provides processed versions of common fields:
|
||||
|
||||
- **Short codes**: `{investment_number_short}` extracts the last segment after dashes (e.g., "1234567" from "I-BC-DE-1234567")
|
||||
- **Case transformations**: `{project_name_upper}`, `{city_upper}`, `{customer_upper}` for uppercase versions
|
||||
- **Duplicate fields**: Multiple versions of the same field for repeated use (`{project_name_1}`, `{project_name_2}`, etc.)
|
||||
|
||||
If you need additional transformations (like extracting different parts of codes, custom formatting, calculations, etc.), please let us know and we can add them to the system.
|
||||
|
||||
## Tips
|
||||
|
||||
- Test your templates with sample data first
|
||||
- Use descriptive variable names
|
||||
- Keep formatting simple for best results
|
||||
- Save templates with `.docx` extension only
|
||||
- Maximum file size: 10MB
|
||||
- **For repeated information**: If you need the same data to appear multiple times, create unique placeholders like `{project_name_header}` and `{project_name_footer}` and provide the same value for both
|
||||
|
||||
## Storage & Persistence
|
||||
|
||||
Templates are stored in two locations for persistence in Docker environments:
|
||||
|
||||
### Database Storage
|
||||
- **Location**: `data/database.sqlite` (table: `docx_templates`)
|
||||
- **Content**: Template metadata (name, description, file paths, timestamps)
|
||||
- **Persistence**: Handled by Docker volume mount `./data:/app/data`
|
||||
|
||||
### File Storage
|
||||
- **Location**: `templates/` (host) → `/app/templates/` (container)
|
||||
- **Content**: Actual DOCX template files
|
||||
- **Persistence**: Handled by Docker volume mount `./templates:/app/templates`
|
||||
- **Web Access**: Files are served via `/api/templates/download/{filename}`
|
||||
|
||||
### Docker Volume Mounts
|
||||
Both development and production Docker setups include volume mounts to ensure template persistence across container restarts:
|
||||
|
||||
```yaml
|
||||
volumes:
|
||||
- ./data:/app/data # Database
|
||||
- ./templates:/app/templates # Template files
|
||||
- ./uploads:/app/public/uploads # Other uploads
|
||||
- ./backups:/app/backups # Backup files
|
||||
```
|
||||
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Problem: "Duplicate tag" error during generation**
|
||||
- **Cause**: Using the same placeholder multiple times (e.g., `{project_name}` twice)
|
||||
- **Solution**: Use numbered variants like `{project_name}`, `{project_name_1}`, `{project_name_2}` OR add the same value to custom fields with different names
|
||||
|
||||
**Problem: Template not rendering correctly**
|
||||
- **Cause**: Invalid placeholder syntax
|
||||
- **Solution**: Ensure all placeholders use single curly braces `{variable}` (not double `{{}}`)
|
||||
- **Verify**: Check for typos in variable names
|
||||
|
||||
**Problem: Missing data in generated document**
|
||||
- **Cause**: Project missing required fields or custom data not provided
|
||||
- **Solution**: Fill in all required project information or provide custom data during generation
|
||||
- **Check**: Review project details before generating
|
||||
|
||||
**Problem: Formatting lost in generated document**
|
||||
- **Cause**: Complex Word formatting or incompatible styles
|
||||
- **Solution**:
|
||||
- Simplify template formatting
|
||||
- Avoid complex tables or text boxes
|
||||
- Use basic styles (bold, italic, underline work best)
|
||||
- Test with minimal formatting first
|
||||
|
||||
**Problem: Generated file not downloading**
|
||||
- **Cause**: Browser popup blocker or network issue
|
||||
- **Solution**:
|
||||
- Allow popups for this site
|
||||
- Check browser console for errors (F12)
|
||||
- Try different browser
|
||||
- Check file size < 10MB
|
||||
|
||||
**Problem: Template upload fails**
|
||||
- **Cause**: File too large or invalid format
|
||||
- **Solution**:
|
||||
- Ensure file is .docx format (not .doc)
|
||||
- File size must be under 10MB
|
||||
- Re-save file in Word to fix corruption
|
||||
- Check file isn't password-protected
|
||||
|
||||
**Problem: Custom fields not appearing**
|
||||
- **Cause**: Field name mismatch between template and custom data
|
||||
- **Solution**:
|
||||
- Ensure exact match (case-sensitive)
|
||||
- Example: `{meeting_notes}` in template requires `meeting_notes` in custom data
|
||||
- Check for spaces in field names
|
||||
|
||||
**Problem: Dates not formatted correctly**
|
||||
- **Cause**: Date format differences
|
||||
- **Solution**: Dates are auto-formatted as YYYY-MM-DD
|
||||
- **Tip**: Use `{today_date}` for current date
|
||||
|
||||
### Getting Help
|
||||
|
||||
If you encounter other issues:
|
||||
1. Check browser console (F12) for error messages
|
||||
2. Verify template file is valid .docx
|
||||
3. Test with simpler template first
|
||||
4. Contact system administrator with error details
|
||||
|
||||
---
|
||||
|
||||
## 📋 Quick Reference
|
||||
|
||||
### File Limits
|
||||
- Maximum template size: 10MB
|
||||
- Supported format: .docx only
|
||||
- Unlimited templates per project
|
||||
|
||||
### Available Endpoints
|
||||
- `GET /api/templates` - List all templates
|
||||
- `POST /api/templates` - Upload new template
|
||||
- `POST /api/templates/generate` - Generate document
|
||||
- `GET /api/templates/download/{filename}` - Download template
|
||||
|
||||
### Best Practices
|
||||
✅ Test templates with sample data first
|
||||
✅ Use descriptive placeholder names
|
||||
✅ Keep formatting simple
|
||||
✅ Use numbered variants for repeated data
|
||||
✅ Provide meaningful template descriptions
|
||||
❌ Don't use same placeholder twice
|
||||
❌ Don't use complex Word features (macros, forms)
|
||||
❌ Don't upload non-.docx files
|
||||
|
||||
---
|
||||
|
||||
**See Also**: [Main README](../../README.md#-document-generation) | [API Documentation](../../README.md#templates-docx)
|
||||
351
docs/features/RADICALE_SYNC.md
Normal file
351
docs/features/RADICALE_SYNC.md
Normal file
@@ -0,0 +1,351 @@
|
||||
# Radicale CardDAV Sync Integration
|
||||
|
||||
This application now automatically syncs contacts to a Radicale CardDAV server whenever contacts are created, updated, or deleted.
|
||||
|
||||
## Features
|
||||
|
||||
- ✅ **Automatic Sync** - Contacts are automatically synced when created or updated
|
||||
- ✅ **Automatic Deletion** - Contacts are removed from Radicale when soft/hard deleted
|
||||
- ✅ **Non-Blocking** - Sync happens asynchronously without slowing down the API
|
||||
- ✅ **Optional** - Sync is disabled by default, enable by configuring environment variables
|
||||
- ✅ **VCARD 3.0** - Generates standard VCARD format with full contact details
|
||||
|
||||
## Setup
|
||||
|
||||
### 1. Configure Environment Variables
|
||||
|
||||
Add these to your `.env.local` or production environment:
|
||||
|
||||
```bash
|
||||
RADICALE_URL=http://localhost:5232
|
||||
RADICALE_USERNAME=your_username
|
||||
RADICALE_PASSWORD=your_password
|
||||
```
|
||||
|
||||
**Note:** If these variables are not set, sync will be disabled and the app will work normally.
|
||||
|
||||
### 2. Radicale Server Setup
|
||||
|
||||
Make sure your Radicale server:
|
||||
- Is accessible from your application server
|
||||
- Has a user created with the credentials you configured
|
||||
- Has a contacts collection at: `{username}/contacts/`
|
||||
|
||||
### 3. One-Time Initial Sync
|
||||
|
||||
To sync all existing contacts to Radicale:
|
||||
|
||||
```bash
|
||||
node export-contacts-to-radicale.mjs
|
||||
```
|
||||
|
||||
This script will:
|
||||
- Prompt for Radicale URL, username, and password
|
||||
- Export all active contacts as VCARDs
|
||||
- Upload them to your Radicale server
|
||||
|
||||
## How It Works
|
||||
|
||||
### When Creating a Contact
|
||||
|
||||
```javascript
|
||||
// POST /api/contacts
|
||||
const contact = createContact(data);
|
||||
|
||||
// Sync to Radicale asynchronously (non-blocking)
|
||||
syncContactAsync(contact);
|
||||
|
||||
return NextResponse.json(contact);
|
||||
```
|
||||
|
||||
### When Updating a Contact
|
||||
|
||||
```javascript
|
||||
// PUT /api/contacts/[id]
|
||||
const contact = updateContact(contactId, data);
|
||||
|
||||
// Sync updated contact to Radicale
|
||||
syncContactAsync(contact);
|
||||
|
||||
return NextResponse.json(contact);
|
||||
```
|
||||
|
||||
### When Deleting a Contact
|
||||
|
||||
```javascript
|
||||
// DELETE /api/contacts/[id]
|
||||
deleteContact(contactId);
|
||||
|
||||
// Delete from Radicale asynchronously
|
||||
deleteContactAsync(contactId);
|
||||
|
||||
return NextResponse.json({ message: "Contact deleted" });
|
||||
```
|
||||
|
||||
## VCARD Format
|
||||
|
||||
Each contact is exported with the following fields:
|
||||
|
||||
- **UID**: `contact-{id}@panel-app`
|
||||
- **FN/N**: Full name and structured name
|
||||
- **ORG**: Company
|
||||
- **TITLE**: Position/Title
|
||||
- **TEL**: Phone numbers (multiple supported - first as WORK, others as CELL)
|
||||
- **EMAIL**: Email address
|
||||
- **NOTE**: Contact type + notes
|
||||
- **CATEGORIES**: Based on contact type (Projekty, Wykonawcy, Urzędy, etc.)
|
||||
- **REV**: Last modified timestamp
|
||||
|
||||
## VCARD Storage Path
|
||||
|
||||
VCARDs are stored at:
|
||||
```
|
||||
{RADICALE_URL}/{RADICALE_USERNAME}/contacts/contact-{id}.vcf
|
||||
```
|
||||
|
||||
Example:
|
||||
```
|
||||
http://localhost:5232/admin/contacts/contact-123.vcf
|
||||
```
|
||||
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Problem: Sync not working / contacts not appearing in Radicale**
|
||||
|
||||
**Check 1: Environment Variables**
|
||||
```bash
|
||||
# Verify variables are set
|
||||
echo $RADICALE_URL
|
||||
echo $RADICALE_USERNAME
|
||||
# Don't echo password for security
|
||||
```
|
||||
|
||||
**Check 2: Radicale Server Connectivity**
|
||||
```bash
|
||||
# Test server is reachable
|
||||
curl -I http://your-radicale-server:5232
|
||||
|
||||
# Test authentication
|
||||
curl -u username:password http://your-radicale-server:5232/username/contacts/
|
||||
```
|
||||
|
||||
**Check 3: Application Logs**
|
||||
Look for sync messages in your application console:
|
||||
```
|
||||
✅ Synced contact 123 to Radicale
|
||||
❌ Failed to sync contact 456 to Radicale: 401 - Unauthorized
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Problem: 401 Unauthorized errors**
|
||||
- **Cause**: Invalid credentials or user doesn't exist
|
||||
- **Solution**:
|
||||
- Verify `RADICALE_USERNAME` and `RADICALE_PASSWORD`
|
||||
- Ensure user exists in Radicale
|
||||
- Check Radicale authentication method (basic auth vs htpasswd)
|
||||
|
||||
---
|
||||
|
||||
**Problem: 404 Not Found errors**
|
||||
- **Cause**: Contacts collection doesn't exist
|
||||
- **Solution**:
|
||||
- Create collection in Radicale: `/{username}/contacts/`
|
||||
- Verify collection URL matches `RADICALE_URL`
|
||||
- Check Radicale collection rights and permissions
|
||||
|
||||
---
|
||||
|
||||
**Problem: Network timeout or connection refused**
|
||||
- **Cause**: Radicale server not accessible from app server
|
||||
- **Solution**:
|
||||
- Check firewall rules
|
||||
- Verify Radicale is running: `systemctl status radicale`
|
||||
- Test with curl from app server
|
||||
- If using Docker, ensure network connectivity
|
||||
|
||||
---
|
||||
|
||||
**Problem: Contacts created but not syncing**
|
||||
- **Cause**: Environment variables not loaded or sync disabled
|
||||
- **Solution**:
|
||||
- Restart application after setting env vars
|
||||
- Check `.env` or `.env.local` file exists
|
||||
- Verify Next.js loaded environment: check server startup logs
|
||||
- Test with manual export script: `node export-contacts-to-radicale.mjs`
|
||||
|
||||
---
|
||||
|
||||
**Problem: Duplicate contacts in Radicale**
|
||||
- **Cause**: Re-running export script or UID conflicts
|
||||
- **Solution**:
|
||||
- UIDs are unique: `contact-{id}@panel-app`
|
||||
- Existing contacts are overwritten on update
|
||||
- Delete duplicates manually in Radicale if needed
|
||||
|
||||
---
|
||||
|
||||
**Problem: VCARD format errors in Radicale**
|
||||
- **Cause**: Invalid characters or incomplete data
|
||||
- **Solution**:
|
||||
- Check contact has at least name field
|
||||
- Special characters in names are escaped
|
||||
- Phone/email fields are optional
|
||||
- Review contact data for completeness
|
||||
|
||||
---
|
||||
|
||||
### Monitoring Sync Status
|
||||
|
||||
**Enable Detailed Logging**
|
||||
Edit `src/lib/radicale-sync.js` to increase logging verbosity:
|
||||
```javascript
|
||||
// Add more console.log statements
|
||||
console.log('Syncing contact:', contact);
|
||||
console.log('VCARD:', vcard);
|
||||
console.log('Response:', await response.text());
|
||||
```
|
||||
|
||||
**Check Radicale Server Logs**
|
||||
```bash
|
||||
# Typical log location
|
||||
tail -f /var/log/radicale/radicale.log
|
||||
|
||||
# Or check systemd journal
|
||||
journalctl -u radicale -f
|
||||
```
|
||||
|
||||
**Manual Sync Test**
|
||||
Test individual contact sync:
|
||||
```bash
|
||||
# Use the export script for a single contact
|
||||
node export-contacts-to-radicale.mjs
|
||||
# Select specific contact when prompted
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Disable Sync Temporarily
|
||||
|
||||
Comment out environment variables to disable sync without removing configuration:
|
||||
```bash
|
||||
# .env.local
|
||||
# RADICALE_URL=http://localhost:5232
|
||||
# RADICALE_USERNAME=admin
|
||||
# RADICALE_PASSWORD=secret
|
||||
```
|
||||
|
||||
Application will function normally without sync enabled.
|
||||
|
||||
---
|
||||
|
||||
### Manual Sync Endpoint
|
||||
|
||||
For manual sync control, you can trigger sync via API:
|
||||
|
||||
```bash
|
||||
# Sync specific contact
|
||||
POST /api/contacts/{id}/sync
|
||||
|
||||
# Response
|
||||
{
|
||||
"success": true,
|
||||
"message": "Contact synced to Radicale"
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Error Codes Reference
|
||||
|
||||
| Code | Meaning | Solution |
|
||||
|------|---------|----------|
|
||||
| 401 | Unauthorized | Check credentials |
|
||||
| 403 | Forbidden | Verify user has write permissions |
|
||||
| 404 | Not Found | Create contacts collection |
|
||||
| 409 | Conflict | UID collision (rare) |
|
||||
| 500 | Server Error | Check Radicale server logs |
|
||||
| ECONNREFUSED | Connection Refused | Server not reachable |
|
||||
| ETIMEDOUT | Timeout | Network/firewall issue |
|
||||
|
||||
---
|
||||
|
||||
## 📋 Configuration Reference
|
||||
|
||||
### Required Environment Variables
|
||||
```bash
|
||||
RADICALE_URL=http://localhost:5232
|
||||
RADICALE_USERNAME=your_username
|
||||
RADICALE_PASSWORD=your_password
|
||||
```
|
||||
|
||||
### Default Settings
|
||||
- **Collection Path**: `/{username}/contacts/`
|
||||
- **VCARD Version**: 3.0
|
||||
- **UID Format**: `contact-{id}@panel-app`
|
||||
- **Sync Mode**: Asynchronous (non-blocking)
|
||||
- **Retry Logic**: None (fire-and-forget)
|
||||
|
||||
---
|
||||
|
||||
## 📂 Files Reference
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `src/lib/radicale-sync.js` | Core sync logic, VCARD generation |
|
||||
| `src/app/api/contacts/route.js` | Create sync trigger |
|
||||
| `src/app/api/contacts/[id]/route.js` | Update/delete sync triggers |
|
||||
| `export-contacts-to-radicale.mjs` | Bulk export utility |
|
||||
|
||||
---
|
||||
|
||||
## 🔒 Security Best Practices
|
||||
|
||||
✅ **Do's:**
|
||||
- Use HTTPS for production Radicale servers
|
||||
- Store credentials in environment variables (never in code)
|
||||
- Use strong, unique passwords
|
||||
- Limit Radicale user permissions to contacts collection only
|
||||
- Regularly rotate credentials
|
||||
- Use separate credentials per environment (dev/staging/prod)
|
||||
|
||||
❌ **Don'ts:**
|
||||
- Don't commit credentials to git
|
||||
- Don't use HTTP in production
|
||||
- Don't share credentials between environments
|
||||
- Don't log passwords or sensitive data
|
||||
- Don't grant unnecessary permissions
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Advanced Configuration
|
||||
|
||||
### Custom Collection Path
|
||||
Modify `src/lib/radicale-sync.js`:
|
||||
```javascript
|
||||
const baseUrl = `${process.env.RADICALE_URL}/${process.env.RADICALE_USERNAME}/my-custom-collection/`;
|
||||
```
|
||||
|
||||
### Batch Sync Operations
|
||||
For large-scale sync (future enhancement):
|
||||
```javascript
|
||||
// Collect contacts, then sync in batches
|
||||
const batchSize = 50;
|
||||
// Implement batch logic
|
||||
```
|
||||
|
||||
### Webhook Integration
|
||||
Future: Trigger webhooks on sync events:
|
||||
```javascript
|
||||
// POST to webhook URL on sync success/failure
|
||||
fetch(WEBHOOK_URL, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ event: 'contact_synced', contact_id: id })
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**See Also**: [Contacts System](CONTACTS_SYSTEM.md) | [Main README](../../README.md#-cardav-integration-radicale) | [API Documentation](../../README.md#contacts)
|
||||
518
docs/features/ROUTE_PLANNING.md
Normal file
518
docs/features/ROUTE_PLANNING.md
Normal file
@@ -0,0 +1,518 @@
|
||||
# Route Planning Feature with Optimization
|
||||
|
||||
This feature allows you to plan routes between multiple project locations using OpenRouteService API, with automatic optimization to find the fastest route regardless of point addition order.
|
||||
|
||||
## 🌟 Overview
|
||||
|
||||
The route planning system integrates with your project map to help optimize field visits. It supports:
|
||||
- Multi-point routing through project locations
|
||||
- Automatic route optimization for 3+ points
|
||||
- Visual route display on the map
|
||||
- Distance and time estimation
|
||||
- Hybrid optimization approach (API + permutation testing)
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Setup
|
||||
|
||||
1. **Get an API Key**:
|
||||
- Visit [OpenRouteService](https://openrouteservice.org/)
|
||||
- Sign up for a free account
|
||||
- Generate an API key
|
||||
|
||||
2. **Configure Environment**:
|
||||
- Copy `.env.example` to `.env.local`
|
||||
- Add your API key: `NEXT_PUBLIC_ORS_API_KEY=your_actual_api_key`
|
||||
|
||||
3. **Install Dependencies**:
|
||||
```bash
|
||||
npm install @mapbox/polyline
|
||||
```
|
||||
|
||||
4. **Restart Development Server**:
|
||||
```bash
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## How to Use
|
||||
|
||||
### Basic Routing (2 Points)
|
||||
1. **Select Route Tool**: Click the route icon in the tool panel (looks like a path)
|
||||
2. **Add Projects**: Click on project markers to add them to your route
|
||||
3. **Calculate Route**: Click "Calculate Route" to get directions
|
||||
4. **View Results**: See distance, duration, and route path on the map
|
||||
|
||||
### Optimized Routing (3+ Points)
|
||||
1. **Select Route Tool**: Click the route icon in the tool panel
|
||||
2. **Add Projects**: Click on project markers (order doesn't matter)
|
||||
3. **Find Optimal Route**: Click "Find Optimal Route" - system automatically finds fastest path
|
||||
4. **View Optimization Results**: See which route order was selected and performance stats
|
||||
|
||||
## Features
|
||||
|
||||
### Core Features
|
||||
- **Multi-point routing**: Plan routes through multiple project locations
|
||||
- **Visual route display**: Blue dashed line shows the calculated route
|
||||
- **Route markers**: Green start marker, red end marker
|
||||
- **Route information**: Distance and estimated travel time
|
||||
- **Interactive management**: Add/remove projects from route
|
||||
- **Map auto-fit**: Automatically adjusts map view to show entire route
|
||||
|
||||
### Optimization Features ✨
|
||||
- **Hybrid Optimization**: Uses ORS Optimization API first, falls back to permutation testing
|
||||
- **Smart Fallback**: Automatically switches to proven permutation method if ORS fails
|
||||
- **Order Detection**: Clearly shows when route order was actually optimized vs unchanged
|
||||
- **Large Point Support**: Can handle up to 50+ points with ORS API
|
||||
- **Performance Monitoring**: Detailed logging of optimization approach and results
|
||||
- **Real-time Progress**: Shows "Finding Optimal Route..." during calculation
|
||||
|
||||
## Technical Implementation
|
||||
|
||||
### Core Functions
|
||||
|
||||
#### `calculateRoute()`
|
||||
Main function that handles both basic and optimized routing with hybrid approach:
|
||||
|
||||
```javascript
|
||||
const calculateRoute = async () => {
|
||||
// For 2 points: direct calculation
|
||||
if (coordinates.length === 2) {
|
||||
const routeData = await calculateRouteForCoordinates(coordinates);
|
||||
setRouteData({...routeData, optimized: false});
|
||||
return;
|
||||
}
|
||||
|
||||
// For 3+ points: try ORS Optimization API first
|
||||
let optimizationRequest = {
|
||||
jobs: coordinates.map((coord, index) => ({
|
||||
id: index,
|
||||
location: coord,
|
||||
service: 0
|
||||
})),
|
||||
vehicles: [{
|
||||
id: 0,
|
||||
profile: 'driving-car',
|
||||
// No fixed start/end for true optimization
|
||||
capacity: [coordinates.length]
|
||||
}],
|
||||
options: { g: true }
|
||||
};
|
||||
|
||||
try {
|
||||
const optimizationResponse = await fetch('https://api.openrouteservice.org/optimization', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': process.env.NEXT_PUBLIC_ORS_API_KEY,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(optimizationRequest)
|
||||
});
|
||||
const optimizationData = await optimizationResponse.json();
|
||||
|
||||
// Extract optimized order from ORS response
|
||||
const optimizedCoordinates = extractOptimizedOrder(optimizationData, coordinates);
|
||||
|
||||
// Check if order actually changed
|
||||
const orderChanged = detectOrderChange(coordinates, optimizedCoordinates);
|
||||
|
||||
if (orderChanged) {
|
||||
// Use optimized order
|
||||
const routeData = await calculateRouteForCoordinates(optimizedCoordinates);
|
||||
setRouteData({...routeData, optimized: true, optimizationStats: {
|
||||
method: 'ORS_Optimization_API',
|
||||
totalJobs: coordinates.length,
|
||||
duration: optimizationData.routes[0].duration,
|
||||
distance: optimizationData.routes[0].distance
|
||||
}});
|
||||
} else {
|
||||
// Fallback to permutation testing
|
||||
console.log('ORS optimization did not change order, trying permutations...');
|
||||
const bestRoute = await findOptimalRouteByPermutations(coordinates);
|
||||
const routeData = await calculateRouteForCoordinates(bestRoute);
|
||||
setRouteData({...routeData, optimized: true, optimizationStats: {
|
||||
method: 'Permutation_Testing',
|
||||
totalJobs: coordinates.length,
|
||||
duration: routeData.summary.total_duration,
|
||||
distance: routeData.summary.total_distance
|
||||
}});
|
||||
}
|
||||
} catch (error) {
|
||||
// Complete fallback to permutations
|
||||
console.log('ORS optimization failed, using permutation fallback...');
|
||||
const bestRoute = await findOptimalRouteByPermutations(coordinates);
|
||||
const routeData = await calculateRouteForCoordinates(bestRoute);
|
||||
setRouteData({...routeData, optimized: true, optimizationStats: {
|
||||
method: 'Permutation_Testing',
|
||||
totalJobs: coordinates.length,
|
||||
duration: routeData.summary.total_duration,
|
||||
distance: routeData.summary.total_distance
|
||||
}});
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
#### `calculateRouteForCoordinates(coordinates)`
|
||||
Handles individual OpenRouteService Directions API calls:
|
||||
|
||||
```javascript
|
||||
const calculateRouteForCoordinates = async (coordinates) => {
|
||||
const requestBody = {
|
||||
coordinates: coordinates,
|
||||
format: 'geojson',
|
||||
instructions: true,
|
||||
geometry_simplify: false,
|
||||
continue_straight: false,
|
||||
roundabout_exits: true,
|
||||
attributes: ['avgspeed', 'percentage']
|
||||
};
|
||||
|
||||
const response = await fetch('https://api.openrouteservice.org/v2/directions/driving-car', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Authorization': process.env.NEXT_PUBLIC_ORS_API_KEY,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(requestBody)
|
||||
});
|
||||
|
||||
return await response.json();
|
||||
};
|
||||
```
|
||||
|
||||
### UI Components
|
||||
|
||||
#### Dynamic Button Text
|
||||
```javascript
|
||||
{routeProjects.length > 2 ? 'Find Optimal Route' : 'Calculate Route'}
|
||||
```
|
||||
|
||||
#### Optimization Status Display
|
||||
```javascript
|
||||
{routeData.optimized && (
|
||||
<div className="mb-2 p-2 bg-green-50 border border-green-200 rounded">
|
||||
<div className="flex items-center gap-1 font-medium">
|
||||
✅ Route Optimized
|
||||
</div>
|
||||
<div className="mt-1">
|
||||
Tested {routeData.optimizationStats.totalPermutations} routes
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
### Optimization Limits
|
||||
- **Maximum Points**: Limited to 50 points (ORS can handle 100+ in some cases)
|
||||
- **Algorithm**: Advanced TSP solver instead of brute-force permutations
|
||||
- **API Calls**: Only 2 API calls (1 optimization + 1 detailed route)
|
||||
- **Processing Time**: ~1-2 seconds for 50 points (much faster than permutation testing)
|
||||
|
||||
### Memory Usage
|
||||
- Each route response contains detailed geometry data
|
||||
- Large numbers of points can consume significant memory
|
||||
- Automatic cleanup of unused route data
|
||||
|
||||
## API Integration
|
||||
|
||||
### OpenRouteService Optimization API
|
||||
```javascript
|
||||
{
|
||||
jobs: [
|
||||
{ id: 0, location: [lng, lat], service: 0 },
|
||||
{ id: 1, location: [lng, lat], service: 0 }
|
||||
],
|
||||
vehicles: [{
|
||||
id: 0,
|
||||
profile: 'driving-car',
|
||||
start: [lng, lat],
|
||||
end: [lng, lat],
|
||||
capacity: [point_count]
|
||||
}],
|
||||
options: { g: true }
|
||||
}
|
||||
```
|
||||
|
||||
### Directions API Parameters
|
||||
```javascript
|
||||
{
|
||||
coordinates: [[lng, lat], [lng, lat], ...],
|
||||
format: 'geojson',
|
||||
instructions: true,
|
||||
geometry_simplify: false,
|
||||
continue_straight: false,
|
||||
roundabout_exits: true,
|
||||
attributes: ['avgspeed', 'percentage']
|
||||
}
|
||||
```
|
||||
|
||||
### Response Handling
|
||||
- **Optimization API**: `data.routes[0].steps[]` for optimized order
|
||||
- **Directions API**: `data.routes[0].summary` for route details
|
||||
- **Fallback Path**: `data.features[0].properties.segments[0]`
|
||||
- **Geometry**: Supports both encoded polylines and direct coordinates
|
||||
- **Error Handling**: Graceful fallback for failed calculations
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### "Failed to calculate route"
|
||||
- **Cause**: Invalid API key or network issues
|
||||
- **Solution**: Verify `NEXT_PUBLIC_ORS_API_KEY` in `.env.local`
|
||||
|
||||
#### "Too many points for optimization"
|
||||
- **Cause**: Selected more than 50 points
|
||||
- **Solution**: Reduce to 50 or fewer points, or use manual routing
|
||||
|
||||
#### Optimization taking too long
|
||||
- **Cause**: Large number of points or slow API responses
|
||||
- **Solution**: Reduce points or wait for completion (much faster than before)
|
||||
|
||||
#### Optimization API unavailable
|
||||
- **Cause**: ORS Optimization API temporarily unavailable
|
||||
- **Solution**: Falls back to direct routing without optimization
|
||||
|
||||
#### Route order not optimized
|
||||
- **Cause**: ORS Optimization API returned same order or failed
|
||||
- **Solution**: System automatically falls back to permutation testing for guaranteed optimization
|
||||
|
||||
#### Optimization shows "Order unchanged"
|
||||
- **Cause**: Points may already be in optimal order, or API returned original sequence
|
||||
- **Solution**: Check browser console for detailed optimization logs
|
||||
|
||||
#### Permutation fallback activated
|
||||
- **Cause**: ORS API unavailable or returned suboptimal results
|
||||
- **Solution**: This is normal behavior - permutation testing ensures optimization
|
||||
|
||||
---
|
||||
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### 1. "API Key Missing" Error
|
||||
**Symptom**: Route calculation fails with authentication error
|
||||
|
||||
**Solutions**:
|
||||
- Check `.env.local` file has `NEXT_PUBLIC_ORS_API_KEY=your_key`
|
||||
- Verify no extra spaces around the key
|
||||
- Ensure development server was restarted after adding the key
|
||||
- Confirm your OpenRouteService API key is active
|
||||
|
||||
```bash
|
||||
# Verify environment variable
|
||||
echo $env:NEXT_PUBLIC_ORS_API_KEY
|
||||
# Should output your API key
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 2. Route Not Displaying on Map
|
||||
**Symptom**: Calculation succeeds but no route visible
|
||||
|
||||
**Solutions**:
|
||||
- Check browser console for coordinate transformation errors
|
||||
- Verify all projects have valid coordinates in database
|
||||
- Confirm map is zoomed to appropriate level
|
||||
- Check if route layer is enabled in layer control
|
||||
|
||||
**Debug**:
|
||||
```javascript
|
||||
// Check route data in browser console
|
||||
console.log('Route GeoJSON:', routeData.geojson);
|
||||
console.log('Route bounds:', routeData.bounds);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 3. Optimization Takes Too Long
|
||||
**Symptom**: "Find Optimal Route" hangs or times out for many points
|
||||
|
||||
**Current Limits**:
|
||||
- 8+ points: May take 30+ seconds
|
||||
- 10+ points: Not recommended (factorial growth)
|
||||
|
||||
**Solutions**:
|
||||
- Split route into multiple segments
|
||||
- Use manual point selection for 8+ locations
|
||||
- Consider implementing A* or genetic algorithm for large routes
|
||||
|
||||
**Permutation Growth**:
|
||||
```
|
||||
3 points = 6 routes to test
|
||||
4 points = 24 routes
|
||||
5 points = 120 routes
|
||||
6 points = 720 routes
|
||||
7 points = 5,040 routes
|
||||
8 points = 40,320 routes
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 4. API Rate Limit Exceeded
|
||||
**Symptom**: Error 429 or "Too many requests"
|
||||
|
||||
**Solutions**:
|
||||
- OpenRouteService free tier: 40 requests/minute, 500/day
|
||||
- Wait 1 minute and try again
|
||||
- Consider upgrading to paid plan for higher limits
|
||||
- Implement request queuing with delays
|
||||
|
||||
```javascript
|
||||
// Add rate limiting check
|
||||
if (routeProjects.length > 5) {
|
||||
alert('Large route may hit rate limits. Consider breaking into segments.');
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 5. Incorrect Route Order
|
||||
**Symptom**: Optimization doesn't select expected fastest route
|
||||
|
||||
**Causes**:
|
||||
- Road network topology (one-way streets, traffic restrictions)
|
||||
- API routing preferences (avoid highways, ferries)
|
||||
- Distance vs time optimization trade-offs
|
||||
|
||||
**Verification**:
|
||||
```javascript
|
||||
// Check all tested routes in console
|
||||
routeData.optimizationStats.testedRoutes.forEach(route => {
|
||||
console.log(`Route ${route.order}: ${route.distance}m in ${route.duration}s`);
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
#### 6. Map Coordinate Transformation Errors
|
||||
**Symptom**: "Failed to transform coordinates" in console
|
||||
|
||||
**Solutions**:
|
||||
- Verify Proj4 definitions are loaded
|
||||
- Check project coordinates are in valid EPSG:2180 format
|
||||
- Confirm transformation libraries are properly initialized
|
||||
|
||||
```javascript
|
||||
// Test coordinate transformation
|
||||
import proj4 from 'proj4';
|
||||
const wgs84 = proj4('EPSG:2180', 'EPSG:4326', [x, y]);
|
||||
console.log('Transformed:', wgs84);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Performance Tips
|
||||
|
||||
1. **Batch Route Calculations**: Group nearby projects before calculating routes
|
||||
2. **Cache Routes**: Store frequently used routes in localStorage
|
||||
3. **Limit Points**: Use max 7 points for real-time optimization
|
||||
4. **Debounce Updates**: Wait for user to finish selecting points
|
||||
5. **Progressive Loading**: Calculate partial routes while building full path
|
||||
|
||||
---
|
||||
|
||||
### API Limitations
|
||||
|
||||
| Tier | Requests/Minute | Requests/Day | Cost |
|
||||
|------|----------------|--------------|------|
|
||||
| Free | 40 | 500 | $0 |
|
||||
| Starter | 300 | 10,000 | Contact ORS |
|
||||
| Business | Custom | Custom | Contact ORS |
|
||||
|
||||
**Best Practices**:
|
||||
- Avoid unnecessary recalculations
|
||||
- Implement client-side caching
|
||||
- Show loading states during API calls
|
||||
- Handle errors gracefully with user feedback
|
||||
|
||||
---
|
||||
|
||||
### Quick Reference
|
||||
|
||||
**Enable Route Planning**:
|
||||
```bash
|
||||
# 1. Get API key from openrouteservice.org
|
||||
# 2. Add to .env.local
|
||||
NEXT_PUBLIC_ORS_API_KEY=your_key_here
|
||||
# 3. Restart dev server
|
||||
npm run dev
|
||||
```
|
||||
|
||||
**Debug Mode**:
|
||||
```javascript
|
||||
// Enable in RoutePanel.js
|
||||
const DEBUG = true;
|
||||
// Logs all tested routes and optimization stats
|
||||
```
|
||||
|
||||
**Performance Monitoring**:
|
||||
```javascript
|
||||
console.time('Route Optimization');
|
||||
await optimizeRoute();
|
||||
console.timeEnd('Route Optimization');
|
||||
// Shows exact optimization duration
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Debug Information
|
||||
Check browser console for detailed logs:
|
||||
- Coordinate parsing details
|
||||
- API request/response structures
|
||||
- **Optimization approach used** (ORS API vs permutation fallback)
|
||||
- **Order change detection** (whether optimization actually improved the route)
|
||||
- Performance timing information
|
||||
- **Original vs optimized coordinate sequences**
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## 📁 File Structure
|
||||
|
||||
```
|
||||
src/app/projects/map/page.js # Main map page with routing logic
|
||||
src/components/ui/LeafletMap.js # Map component with route rendering
|
||||
src/components/ui/mapLayers.js # Map layer configurations
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📦 Dependencies
|
||||
|
||||
- `@mapbox/polyline`: For decoding route geometry
|
||||
- `leaflet`: Map rendering library
|
||||
- `react-leaflet`: React integration for Leaflet
|
||||
- `proj4`: Coordinate system transformations
|
||||
- OpenRouteService API key (free tier available)
|
||||
|
||||
---
|
||||
|
||||
## 🚀 Future Enhancements
|
||||
|
||||
- **Advanced Vehicle Constraints**: Multiple vehicles, capacity limits, time windows
|
||||
- **Route Preferences**: Allow users to prioritize distance vs time vs fuel efficiency
|
||||
- **Real-time Traffic**: Integration with live traffic data
|
||||
- **Route History**: Save and compare previously optimized routes
|
||||
- **Mobile Optimization**: Optimize routes considering current location
|
||||
- **Multi-stop Services**: Add service times at each location
|
||||
- **Advanced Optimization**: Implement A* or genetic algorithms for 8+ points
|
||||
- **Multi-Day Routes**: Break long routes into segments with overnight stops
|
||||
- **Export Options**: Export routes to GPS devices or Google Maps
|
||||
- **Cost Estimation**: Calculate fuel costs and travel expenses
|
||||
|
||||
---
|
||||
|
||||
## 📚 Additional Resources
|
||||
|
||||
- [OpenRouteService API Documentation](https://openrouteservice.org/dev/#/api-docs)
|
||||
- [Directions API Reference](https://openrouteservice.org/dev/#/api-docs/v2/directions)
|
||||
- [Polyline Encoding](https://developers.google.com/maps/documentation/utilities/polylinealgorithm)
|
||||
- [Leaflet Routing Integration](https://www.liedman.net/leaflet-routing-machine/)
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: January 2025
|
||||
**Maintainer**: Panel Development Team
|
||||
Reference in New Issue
Block a user