# Map Editor Plugin Transformation - Implementation Summary ## Overview Successfully transformed the map-editor plugin to use Kirby subpages for markers instead of YAML storage. Markers are now fully-featured Kirby pages with extensible block content. ## Changes Implemented ### 1. Backend Infrastructure #### New Files Created **`/public/site/plugins/map-editor/api/routes.php`** - Implements 5 API endpoints: - `GET /api/map-editor/pages/:pageId/markers` - List all markers for a map page - `POST /api/map-editor/pages/:pageId/markers` - Create new marker - `PATCH /api/map-editor/pages/:pageId/markers/:markerId` - Update marker position (multi mode) - `DELETE /api/map-editor/pages/:pageId/markers/:markerId` - Delete marker - `PATCH /api/map-editor/pages/:pageId/position` - Update position (single mode) - All endpoints include: - Authentication checks - CSRF token verification - Permission checks (read/create/update/delete) - Proper HTTP status codes - Error handling **`/public/site/blueprints/pages/marker.yml`** - Two-tab structure: - **Content tab**: Title + extensible blocks field (heading, text, image, list, quote) - **Position tab**: - Left column: latitude/longitude number fields - Right column: map-editor in single mode for visual positioning **`/public/site/plugins/map-editor/src/composables/useMarkersApi.js`** - Replaces old YAML-based useMarkers.js - Provides reactive API interface: - `fetchMarkers()` - Load markers from API - `createMarker(position)` - Create new marker - `updateMarkerPosition(markerId, position)` - Update position - `deleteMarker(markerId)` - Delete marker - Includes loading states, error handling, CSRF management #### Modified Files **`/public/site/plugins/map-editor/index.php`** - Registered API routes - Added new field props: `mode`, `latitude`, `longitude` **`/public/site/blueprints/pages/map.yml`** - Added markers section to sidebar: - Type: pages - Template: marker - Sorted by num (Kirby's built-in ordering) **`/public/site/plugins/map-editor/src/composables/useMapData.js`** - Removed all marker-related logic - `saveMapData()` now only saves: background, center, zoom - Removed markers parameter from function signatures ### 2. Frontend Refactoring **`/public/site/plugins/map-editor/src/components/field/MapEditor.vue`** Major refactor with two distinct modes: #### Multi Mode (default) - Displays MarkerList sidebar - Loads markers via API on mount - Create marker: `handleAddMarker()` → API call - Delete marker: `deleteMarker()` → API call with confirmation - Edit marker: `editMarker()` → Redirects to Kirby Panel - Drag marker: `handleMarkerMoved()` → API call to update position - Automatically fetches markers from subpages #### Single Mode (for marker blueprint) - Hides MarkerList sidebar - Creates single marker from `latitude`/`longitude` props - Displays marker at current page coordinates - Drag marker: Updates page via `/api/map-editor/pages/:pageId/position` endpoint - Watches latitude/longitude props to update map when fields change - Smaller height (400px vs 600px) ### 3. Removed Files - `/public/site/plugins/map-editor/src/components/map/MarkerEditor.vue` - Modal editor no longer needed (Panel handles editing) - `/public/site/plugins/map-editor/src/composables/useMarkers.js` - Replaced by useMarkersApi.js ## Data Flow ### Multi Mode (Map Page) 1. User opens map page in Panel 2. MapEditor fetches markers via `GET /api/map-editor/pages/:pageId/markers` 3. Markers displayed on map + in sidebar 4. User actions: - Click "Add" or click map → `POST /api/.../markers` → New subpage created - Drag marker → `PATCH /api/.../markers/:markerId` → Position updated - Click "Edit" → Redirect to Panel marker page - Click "Delete" → `DELETE /api/.../markers/:markerId` → Subpage deleted 5. Changes to center/zoom → Saved to mapdata YAML ### Single Mode (Marker Page) 1. User opens marker page in Panel 2. MapEditor receives latitude/longitude from blueprint query (`{{ page.latitude }}`) 3. Creates visual marker at those coordinates 4. User drags marker → `PATCH /api/map-editor/pages/:pageId/position` → Updates latitude/longitude fields 5. No markers section or CRUD buttons shown ## API Response Format ### GET /api/map-editor/pages/:pageId/markers ```json { "status": "success", "data": { "markers": [ { "id": "map/carte/marker-1234567890", "slug": "marker-1234567890", "title": "Marqueur 1", "position": {"lat": 43.8, "lon": 4.3}, "num": 1, "panelUrl": "/panel/pages/map+carte+marker-1234567890" } ] } } ``` ### POST /api/map-editor/pages/:pageId/markers Request: ```json {"position": {"lat": 43.8, "lon": 4.3}} ``` Response: Same format as GET, but with single marker ### Error Response ```json { "status": "error", "message": "Error description", "code": 400 } ``` ## Security - All API endpoints require authentication - CSRF protection via `X-CSRF` header - Permission checks (isReadable, can('create'), can('update'), can('delete')) - Input validation (coordinate ranges, required fields) - Proper error handling with try/catch ## Marker Ordering - Uses Kirby's native `num` field for ordering - Listed subpages sorted by `num asc` - Panel drag-and-drop automatically manages num - No custom ordering logic needed ## Migration Notes - Only one map page exists with fake content → No migration needed - Old YAML markers structure no longer used - mapdata field now only stores: background, center, zoom ## Testing Checklist - [x] API routes created and registered - [x] Blueprint structure correct - [x] MapEditor.vue refactored for API - [x] Single mode implemented - [x] Old files removed (MarkerEditor.vue, useMarkers.js) - [ ] Test marker creation from map - [ ] Test marker deletion with confirmation - [ ] Test marker drag updates position - [ ] Test edit button redirects to Panel - [ ] Test single mode in marker page - [ ] Test single mode drag updates coordinates - [ ] Test GeocodeSearch in both modes - [ ] Test with 50 markers (performance) - [ ] Verify CSRF protection works - [ ] Verify permissions are enforced ## Known Considerations 1. **Panel Refresh**: In single mode, when dragging a marker, the API updates the latitude/longitude fields, but the Panel doesn't automatically refresh. Users may need to reload to see updated number values. 2. **Page ID Extraction**: The code extracts page ID from `props.name` with a regex. This works for standard Kirby field names but may need adjustment if field naming changes. 3. **Error Handling**: API errors are logged to console. Consider adding user-visible error messages in the UI. 4. **Loading States**: Loading states are available in the component but not visually displayed. Consider adding a loading spinner. ## Next Steps (Future Improvements) 1. Add visual loading indicators during API calls 2. Add user-visible error messages (toasts/alerts) 3. Implement real-time Panel field sync in single mode 4. Add marker icon customization 5. Add marker search/filter in MarkerList 6. Consider pagination for maps with many markers 7. Add bulk operations (delete multiple, reorder) 8. Add marker clustering on map for better performance ## File Structure After Implementation ``` /public/site/plugins/map-editor/ ├── api/ │ └── routes.php (NEW) ├── src/ │ ├── components/ │ │ ├── field/ │ │ │ └── MapEditor.vue (MODIFIED - major refactor) │ │ └── map/ │ │ ├── MapPreview.vue │ │ ├── MarkerList.vue │ │ └── MarkerEditor.vue (DELETED) │ └── composables/ │ ├── useMarkersApi.js (NEW) │ ├── useMapData.js (MODIFIED) │ └── useMarkers.js (DELETED) └── index.php (MODIFIED) /public/site/blueprints/pages/ ├── marker.yml (NEW) └── map.yml (MODIFIED) ``` ## Summary The transformation successfully achieves the goal of making markers first-class Kirby content with extensible fields. The implementation: - ✅ Maintains backward compatibility for map data (center, zoom, background) - ✅ Provides clean API-based architecture - ✅ Supports both multi-marker (map page) and single-marker (marker page) modes - ✅ Leverages Kirby's built-in Panel for content editing - ✅ Includes proper security and error handling - ✅ Uses Kirby's native ordering system - ✅ Removes obsolete YAML-based marker storage The plugin is now ready for testing and refinement based on real-world usage.