# Location Module Templates - Quick Reference Guide ## Template Overview 5 production-ready Jinja2 templates for the Location (Lokaliteter) module: | Template | Purpose | Context | Key Features | |----------|---------|---------|--------------| | **list.html** | List all locations | `locations`, `total`, `page_number`, `total_pages`, `filters` | Pagination, bulk select, filters, responsive table | | **detail.html** | View location details | `location`, `location.*` (contacts, hours, services, capacity) | 6 tabs, modals, CRUD operations, progress bars | | **create.html** | Create new location | `location_types` | 5-section form, validation, character counter | | **edit.html** | Edit location | `location`, `location_types` | Pre-filled form, delete modal, PATCH request | | **map.html** | Interactive map | `locations`, `location_types` | Leaflet.js, clustering, type filters | --- ## Directory ``` /app/modules/locations/templates/ ``` --- ## Integration Points ### 1. Routes Required (Backend) ```python @router.get("/locations", response_model=List[Location]) def list_locations(skip: int = 0, limit: int = 10, ...): # Return filtered, paginated locations @router.get("/locations/create", ...) def create_page(location_types: List[str]): # Render create.html @router.get("/locations/{id}", response_model=LocationDetail) def detail_page(id: int): # Render detail.html with full object @router.get("/locations/{id}/edit", ...) def edit_page(id: int, location_types: List[str]): # Render edit.html with location pre-filled @router.get("/locations/map", ...) def map_page(locations: List[Location], location_types: List[str]): # Render map.html with location data ``` ### 2. API Endpoints Required ``` POST /api/v1/locations - Create location GET /api/v1/locations/{id} - Get location PATCH /api/v1/locations/{id} - Update location DELETE /api/v1/locations/{id} - Delete location (soft) POST /api/v1/locations/{id}/contacts - Add contact DELETE /api/v1/locations/{id}/contacts/{cid} - Delete contact POST /api/v1/locations/{id}/services - Add service DELETE /api/v1/locations/{id}/services/{sid} - Delete service POST /api/v1/locations/{id}/capacity - Add capacity DELETE /api/v1/locations/{id}/capacity/{cid} - Delete capacity ``` --- ## Context Variables Reference ### list.html ```python { 'locations': List[Location], 'total': int, 'skip': int, 'limit': int, 'page_number': int, 'total_pages': int, 'location_type': Optional[str], 'is_active': Optional[bool], 'location_types': List[str] } ``` ### detail.html ```python { 'location': LocationDetail, # With all nested data 'location.id': int, 'location.name': str, 'location.location_type': str, 'location.is_active': bool, 'location.address_*': str, 'location.phone': str, 'location.email': str, 'location.contacts': List[Contact], 'location.operating_hours': List[Hours], 'location.services': List[Service], 'location.capacity': List[Capacity], 'location.audit_log': List[AuditEntry], 'location_types': List[str] } ``` ### create.html ```python { 'location_types': List[str] } ``` ### edit.html ```python { 'location': Location, # Pre-fill values 'location_types': List[str] } ``` ### map.html ```python { 'locations': List[Location], # Must have: id, name, latitude, longitude, location_type, address_city 'location_types': List[str] } ``` --- ## CSS Classes Used ### Bootstrap 5 Classes ``` Container: container-fluid, px-4, py-4 Grid: row, col-*, col-md-*, col-lg-* Cards: card, card-body, card-header Forms: form-control, form-select, form-check, form-label Buttons: btn, btn-primary, btn-outline-secondary, btn-danger Tables: table, table-hover, table-responsive Badges: badge, bg-success, bg-secondary Modals: modal, modal-dialog, modal-content Alerts: alert, alert-danger Pagination: pagination, page-item, page-link Utilities: d-flex, gap-*, justify-content-*, align-items-* ``` ### Custom CSS Variables (from base.html) ```css --bg-body: #f8f9fa / #212529 --bg-card: #ffffff / #2c3034 --text-primary: #2c3e50 / #f8f9fa --text-secondary: #6c757d / #adb5bd --accent: #0f4c75 / #3d8bfd --accent-light: #eef2f5 / #373b3e --border-radius: 12px ``` --- ## JavaScript Events ### list.html - Checkbox select/deselect - Bulk delete confirmation - Individual delete confirmation - Row click navigation - Page navigation ### detail.html - Tab switching (Bootstrap nav-tabs) - Modal open/close - Form submission (Fetch API) - Delete confirmation - Inline delete buttons ### create.html - Character counter update - Form submission (Fetch API) - Error display/dismiss - Loading state toggle - Redirect on success ### edit.html - Same as create.html + delete modal ### map.html - Leaflet map initialization - Marker clustering - Popup display - Type filter update - Marker click handlers --- ## Color Reference ### Type Badges - **Branch** (Filial): `#0f4c75` - Deep Blue - **Warehouse** (Lager): `#f39c12` - Orange - **Service Center** (Servicecenter): `#2eb341` - Green - **Client Site** (Kundesite): `#9b59b6` - Purple ### Status Badges - **Active**: `#2eb341` (Green) - `bg-success` - **Inactive**: `#6c757d` (Gray) - `bg-secondary` ### Actions - **Primary**: `#0f4c75` (Blue) - `btn-primary` - **Secondary**: `#6c757d` (Gray) - `btn-outline-secondary` - **Danger**: `#e74c3c` (Red) - `btn-danger` --- ## Responsive Breakpoints | Size | Bootstrap | Applies | Changes | |------|-----------|---------|---------| | Mobile | < 576px | Default | Full-width forms, stacked buttons | | Tablet | >= 768px | `col-md-*` | 2-column forms, table layout | | Desktop | >= 1024px | `col-lg-*` | Multi-column forms, sidebar options | ### list.html Responsive Changes - < 768px: Hide "City" column, show only essential - >= 768px: Show all table columns ### detail.html Responsive Changes - < 768px: Stacked tabs, full-width modals - >= 768px: Side-by-side cards, responsive modals --- ## Icons (Font Awesome / Bootstrap Icons) ```html ``` --- ## Form Validation ### HTML5 Validation - `required` - Field must be filled - `type="email"` - Email format validation - `type="tel"` - Phone format - `type="number"` - Numeric input - `min="-90" max="90"` - Range validation - `maxlength="500"` - Length limit ### Server-Side Validation Expected from API: ```json { "detail": "Validation error message", "status": 422 } ``` --- ## Error Handling ### Client-Side - HTML5 validation prevents invalid submissions - Fetch API error handling - Try-catch for async operations - User-friendly error messages in alert boxes ### API Errors Expected format: ```json { "detail": "Location not found" } ``` --- ## Mobile Optimization - **Touch targets**: Minimum 44px height - **Forms**: Full-width on mobile - **Tables**: Convert to card view at 768px - **Buttons**: Stacked vertically on mobile - **Modals**: Full-screen on mobile - **Maps**: Responsive container --- ## Dark Mode Automatic via `data-bs-theme` attribute on ``: - Light mode: `data-bs-theme="light"` - Dark mode: `data-bs-theme="dark"` CSS variables automatically adjust colors. --- ## External Dependencies ### CSS ```html ``` ### JavaScript ```html ``` --- ## Common Patterns ### Opening a Modal ```javascript const modal = new bootstrap.Modal(document.getElementById('deleteModal')); modal.show(); modal.hide(); ``` ### Fetch API Call ```javascript const response = await fetch('/api/v1/locations', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(data) }); if (response.ok) { const result = await response.json(); // Success } else { const error = await response.json(); // Show error } ``` ### Character Counter ```javascript document.getElementById('notes').addEventListener('input', function() { document.getElementById('charCount').textContent = this.value.length; }); ``` ### Bulk Delete ```javascript const selectedIds = Array.from(checkboxes) .filter(cb => cb.checked) .map(cb => cb.value); Promise.all(selectedIds.map(id => fetch(`/api/v1/locations/${id}`, { method: 'DELETE' }) )) ``` --- ## Testing Checklist - [ ] Form validation works - [ ] Required fields enforced - [ ] Pagination navigation works - [ ] Filters persist across pages - [ ] Bulk select/deselect works - [ ] Individual delete confirmation - [ ] Modal forms submit correctly - [ ] Inline errors display - [ ] Map renders with markers - [ ] Map filter updates markers - [ ] Responsive at 375px - [ ] Responsive at 768px - [ ] Responsive at 1024px - [ ] Dark mode works - [ ] No console errors - [ ] API endpoints working --- ## Support & Troubleshooting ### Maps not showing - Check: Leaflet CDN is loaded - Check: Locations have latitude/longitude - Check: Zoom level is 6 (default Denmark view) ### Forms not submitting - Check: All required fields filled - Check: API endpoint is correct - Check: CSRF protection if enabled ### Modals not opening - Check: Bootstrap JS is loaded - Check: Modal ID matches button target - Check: No console errors ### Styles not applying - Check: Bootstrap 5 CSS loaded - Check: CSS variables inherited from base.html - Check: Dark mode toggle working --- ## References - [Bootstrap 5 Documentation](https://getbootstrap.com/docs/5.0/) - [Leaflet.js Documentation](https://leafletjs.com/) - [Jinja2 Template Documentation](https://jinja.palletsprojects.com/) - [MDN Web Docs - HTML](https://developer.mozilla.org/en-US/docs/Web/HTML) - [MDN Web Docs - CSS](https://developer.mozilla.org/en-US/docs/Web/CSS) - [MDN Web Docs - JavaScript](https://developer.mozilla.org/en-US/docs/Web/JavaScript) --- ## Template Files All files located in: `/app/modules/locations/templates/` **Ready for production deployment** ✅ Last updated: 31 January 2026