10 KiB
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
-
contact_categories
- id, name, description, display_order, is_active
- Unique constraint on name
-
contacts
- id, category_id (FK), name, position, email, phone
- image_url, description, display_order, is_active
- Foreign key to contact_categories
-
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:
{
"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:
[
{
"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):
<!-- Include Leaflet CSS and JS -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<!-- Map container -->
<div id="map" style="height: 400px;"></div>
<script>
// Initialize map
const map = L.map('map').setView([50.0755, 14.4378], 15);
// Add tile layer (OpenStreetMap)
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors',
maxZoom: 19
}).addTo(map);
// Add custom marker
const customIcon = L.icon({
iconUrl: '/dist/img/marker-icon.png',
iconSize: [32, 32],
iconAnchor: [16, 32],
popupAnchor: [0, -32]
});
const marker = L.marker([50.0755, 14.4378], { icon: customIcon })
.addTo(map)
.bindPopup('<b>FC Example</b><br>Main Street 123<br>Prague');
</script>
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:
// 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:
# 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
// 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]) => (
<div key={categoryName}>
<h2>{categoryName}</h2>
{contacts.map(contact => (
<div key={contact.id} className="contact-card">
{contact.image_url && <img src={contact.image_url} alt={contact.name} />}
<h3>{contact.name}</h3>
<p>{contact.position}</p>
<a href={`mailto:${contact.email}`}>{contact.email}</a>
<a href={`tel:${contact.phone}`}>{contact.phone}</a>
<p>{contact.description}</p>
</div>
))}
</div>
))}
Map Component
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(`<b>${clubName}</b><br>${address}`);
// Cleanup
return () => {
if (mapInstanceRef.current) {
mapInstanceRef.current.remove();
mapInstanceRef.current = null;
}
};
}, [latitude, longitude, zoom, address, clubName]);
return <div ref={mapRef} style={{ height: '400px', width: '100%' }} />;
}
Admin Panel Integration
Settings Form Fields
Add to your admin settings form:
// Contact Information Section
<section>
<h3>Contact Information</h3>
<input name="contact_address" placeholder="Address" />
<input name="contact_city" placeholder="City" />
<input name="contact_zip" placeholder="ZIP Code" />
<input name="contact_country" placeholder="Country" />
<input name="contact_phone" placeholder="Phone" />
<input name="contact_email" type="email" placeholder="Email" />
</section>
// Map Configuration Section
<section>
<h3>Map Configuration</h3>
<input name="location_latitude" type="number" step="any" placeholder="Latitude" />
<input name="location_longitude" type="number" step="any" placeholder="Longitude" />
<input name="map_zoom_level" type="number" min="1" max="20" placeholder="Zoom Level (1-20)" />
<select name="map_style">
<option value="default">Default</option>
<option value="dark">Dark Theme</option>
<option value="satellite">Satellite</option>
</select>
<label>
<input name="show_map_on_homepage" type="checkbox" />
Show map on homepage
</label>
</section>
Contact Management UI
Create admin pages for:
- Contact Categories List - View, create, edit, delete categories
- Contacts List - View all contacts with category filter
- Contact Form - Create/edit contact with category dropdown
- Drag-and-drop ordering - Reorder contacts within categories
Best Practices
- Images: Upload contact photos to
/uploads/and use relative paths - Display Order: Use increments of 10 (0, 10, 20...) to allow easy reordering
- Categories: Create logical groups (Management, Coaches, Medical Staff, Office)
- Map Markers: Use club colors for custom marker icons
- Privacy: Only display active contacts on public pages
- Mobile: Ensure map is responsive and touch-friendly
- 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