# Contact Management & Map Integration Comprehensive contact information management system with interactive map support using Leaflet.js (open source). ## Features ### Contact Management - **Contact Categories**: Organize contacts by department (Management, Coaches, Office, etc.) - **Contact Persons**: Store detailed information for each contact - Name, Position, Email, Phone - Optional photo/image - Description - Category assignment - Display order customization - Active/inactive status ### Location & Map - **Address Information**: Full address with city, ZIP, country - **GPS Coordinates**: Latitude/Longitude for precise location - **Interactive Map**: Leaflet.js-based map display - Custom markers - Configurable zoom level - Multiple map styles (default, dark, satellite) - Can be shown on homepage - **Contact Details**: Phone and email for the main location ## Database Schema ### Tables Created 1. **contact_categories** - id, name, description, display_order, is_active - Unique constraint on name 2. **contacts** - id, category_id (FK), name, position, email, phone - image_url, description, display_order, is_active - Foreign key to contact_categories 3. **settings** (extended) - contact_address, contact_city, contact_zip, contact_country - contact_phone, contact_email - location_latitude, location_longitude - map_zoom_level, map_style - show_map_on_homepage ## API Endpoints ### Public Endpoints (No Auth Required) #### Get All Contacts (Grouped by Category) ``` GET /api/v1/contacts ``` **Response:** ```json { "categories": { "Management": [ { "id": 1, "name": "John Doe", "position": "President", "email": "john@club.com", "phone": "+420 123 456 789", "image_url": "/uploads/john.jpg", "description": "Club president since 2020", "display_order": 0, "is_active": true, "category": {...} } ], "Coaches": [...] }, "uncategorized": [...] } ``` #### Get Contact Categories ``` GET /api/v1/contacts/categories ``` **Response:** ```json [ { "id": 1, "name": "Management", "description": "Club management and board", "display_order": 0, "is_active": true } ] ``` ### Admin Endpoints (Require Auth + Admin Role) #### Contact Categories Management **List Categories** ``` GET /api/v1/admin/contacts/categories ``` **Create Category** ``` POST /api/v1/admin/contacts/categories Content-Type: application/json { "name": "Management", "description": "Club management team", "display_order": 0, "is_active": true } ``` **Update Category** ``` PUT /api/v1/admin/contacts/categories/:id Content-Type: application/json { "name": "Updated Name", "description": "New description", "display_order": 5, "is_active": false } ``` **Delete Category** ``` DELETE /api/v1/admin/contacts/categories/:id ``` *Note: Cannot delete if contacts are assigned to it* #### Contacts Management **List All Contacts** ``` GET /api/v1/admin/contacts ``` **Create Contact** ``` POST /api/v1/admin/contacts Content-Type: application/json { "category_id": 1, "name": "John Doe", "position": "President", "email": "john@club.com", "phone": "+420 123 456 789", "image_url": "/uploads/john.jpg", "description": "Club president since 2020", "display_order": 0, "is_active": true } ``` **Update Contact** ``` PUT /api/v1/admin/contacts/:id Content-Type: application/json { "name": "Updated Name", "position": "Vice President", "email": "new@email.com", "phone": "+420 987 654 321", "category_id": 2, "is_active": true } ``` *Note: All fields are optional. Set `category_id: null` to remove category assignment* **Delete Contact** ``` DELETE /api/v1/admin/contacts/:id ``` ### Settings API (Location/Map Configuration) **Update Settings (Admin Only)** ``` PUT /api/v1/admin/settings Content-Type: application/json { "contact_address": "Main Street 123", "contact_city": "Prague", "contact_zip": "110 00", "contact_country": "Czech Republic", "contact_phone": "+420 123 456 789", "contact_email": "info@club.com", "location_latitude": 50.0755, "location_longitude": 14.4378, "map_zoom_level": 15, "map_style": "default", "show_map_on_homepage": true } ``` **Get Public Settings** ``` GET /api/v1/settings ``` Returns all public settings including contact/map information. ## Map Integration ### Using Leaflet.js The frontend should use **Leaflet.js** (open source alternative to Google Maps): ```html
``` ### Map Styles The `map_style` field supports: - **"default"**: Standard OpenStreetMap - **"dark"**: Dark theme map - **"satellite"**: Satellite imagery (requires API key for some providers) - **Custom URL**: Full tile URL template Example tile URLs: ```javascript // Default OpenStreetMap https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png // CartoDB Dark Matter https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png // Stadia Maps (requires API key) https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png?api_key=YOUR_API_KEY ``` ## Migration Run the migration to create tables: ```bash # Apply migration make migrate-up # Or manually migrate -path database/migrations -database "postgres://..." up ``` The migration file: `000006_create_contact_tables.up.sql` ## Frontend Implementation Guide ### Contact Page Structure ```jsx // Fetch contacts const response = await fetch('/api/v1/contacts'); const data = await response.json(); // Display grouped by category {Object.entries(data.categories).map(([categoryName, contacts]) => (

{categoryName}

{contacts.map(contact => (
{contact.image_url && {contact.name}}

{contact.name}

{contact.position}

{contact.email} {contact.phone}

{contact.description}

))}
))} ``` ### Map Component ```jsx import { useEffect, useRef } from 'react'; import L from 'leaflet'; function ContactMap({ latitude, longitude, zoom, address, clubName }) { const mapRef = useRef(null); const mapInstanceRef = useRef(null); useEffect(() => { if (!mapRef.current || mapInstanceRef.current) return; // Initialize map const map = L.map(mapRef.current).setView([latitude, longitude], zoom || 15); mapInstanceRef.current = map; // Add tile layer L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap', maxZoom: 19 }).addTo(map); // Add marker const marker = L.marker([latitude, longitude]).addTo(map); marker.bindPopup(`${clubName}
${address}`); // Cleanup return () => { if (mapInstanceRef.current) { mapInstanceRef.current.remove(); mapInstanceRef.current = null; } }; }, [latitude, longitude, zoom, address, clubName]); return
; } ``` ## Admin Panel Integration ### Settings Form Fields Add to your admin settings form: ```jsx // Contact Information Section

Contact Information

// Map Configuration Section

Map Configuration

``` ### Contact Management UI Create admin pages for: 1. **Contact Categories List** - View, create, edit, delete categories 2. **Contacts List** - View all contacts with category filter 3. **Contact Form** - Create/edit contact with category dropdown 4. **Drag-and-drop ordering** - Reorder contacts within categories ## Best Practices 1. **Images**: Upload contact photos to `/uploads/` and use relative paths 2. **Display Order**: Use increments of 10 (0, 10, 20...) to allow easy reordering 3. **Categories**: Create logical groups (Management, Coaches, Medical Staff, Office) 4. **Map Markers**: Use club colors for custom marker icons 5. **Privacy**: Only display active contacts on public pages 6. **Mobile**: Ensure map is responsive and touch-friendly 7. **Performance**: Use lazy loading for contact images ## Security Notes - Admin endpoints are protected by JWT authentication + admin role check - Public endpoints expose only active contacts and categories - Email addresses are visible (consider adding spam protection or obfuscation) - Phone numbers are displayed as-is (consider formatting based on locale) ## Future Enhancements - [ ] Contact form directly on contact page - [ ] vCard download for contacts - [ ] Office hours/availability scheduling - [ ] Multiple locations/maps support - [ ] Department-specific contact forms - [ ] Social media links per contact - [ ] Search/filter functionality - [ ] Map clustering for multiple locations