# 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
<i class="bi bi-plus-lg"></i>           <!-- Plus -->
<i class="bi bi-pencil"></i>            <!-- Edit -->
<i class="bi bi-trash"></i>             <!-- Delete -->
<i class="bi bi-eye"></i>               <!-- View -->
<i class="bi bi-arrow-left"></i>        <!-- Back -->
<i class="bi bi-map-marker-alt"></i>    <!-- Location -->
<i class="bi bi-phone"></i>             <!-- Phone -->
<i class="bi bi-envelope"></i>          <!-- Email -->
<i class="bi bi-clock"></i>             <!-- Time -->
<i class="bi bi-chevron-left"></i>      <!-- Prev -->
<i class="bi bi-chevron-right"></i>     <!-- Next -->
<i class="bi bi-funnel"></i>            <!-- Filter -->
<i class="bi bi-pin-map"></i>           <!-- Location Pin -->
<i class="bi bi-check-lg"></i>          <!-- Check -->
<i class="bi bi-hourglass-split"></i>   <!-- Loading -->
```

---

## 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 `<html>`:
- Light mode: `data-bs-theme="light"`
- Dark mode: `data-bs-theme="dark"`

CSS variables automatically adjust colors.

---

## External Dependencies

### CSS
```html
<!-- Bootstrap 5 -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">

<!-- Bootstrap Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">

<!-- Leaflet (map.html only) -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.min.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet.markercluster@1.5.1/dist/MarkerCluster.css" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet.markercluster@1.5.1/dist/MarkerCluster.Default.css" />
```

### JavaScript
```html
<!-- Bootstrap 5 -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>

<!-- Leaflet (map.html only) -->
<script src="https://cdn.jsdelivr.net/npm/leaflet@1.9.4/dist/leaflet.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/leaflet.markercluster@1.5.1/dist/leaflet.markercluster.js"></script>
```

---

## 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
