Skip to main content

Locations

Locations represent sender (Absender) and receiver (Empfänger) addresses for trips. Each location can have GPS coordinates and multiple contacts.

Location Fields

FieldTypeDescription
idUUIDLocation ID
namestringDisplay name
streetstring?Street and number
zipstring?Postal code
citystring?City
countrystring?Country
latitudefloat?GPS latitude
longitudefloat?GPS longitude
notesstring?Free-text notes
is_activeboolInactive locations hidden in selects

Contact Fields

FieldTypeDescription
idUUIDContact ID
location_idUUIDParent location
namestringContact name
phonestring?Phone number
emailstring?Email address
rolestring?Role label (e.g. "Disposant")

REST Endpoints

MethodPathPermissionDescription
GET/v1/locationslocations:readList locations (paginated)
GET/v1/locations/{id}locations:readGet location with contacts
POST/v1/locationslocations:writeCreate location (with contacts)
PATCH/v1/locations/{id}locations:writeUpdate location
DELETE/v1/locations/{id}locations:deleteDelete location (cascades contacts)
POST/v1/locations/{id}/contactslocations:writeAdd contact
PATCH/v1/locations/{id}/contacts/{cid}locations:writeUpdate contact
DELETE/v1/locations/{id}/contacts/{cid}locations:writeRemove contact

GraphQL

  • locations(page, limit)PaginatedLocations
  • location(id)GqlLocation (includes contacts: [GqlLocationContact])
  • createLocation(input)GqlLocation
  • updateLocation(id, input)GqlLocation
  • deleteLocation(id)Boolean
  • addLocationContact(locationId, input)GqlLocationContact
  • updateLocationContact(id, input)GqlLocationContact
  • deleteLocationContact(id)Boolean

Create Location with Contacts

POST /v1/locations
{
"name": "Kieswerk Mannheim",
"street": "Hafenstraße 12",
"zip": "68159",
"city": "Mannheim",
"country": "DE",
"latitude": 49.4875,
"longitude": 8.4660,
"contacts": [
{
"name": "Hans Müller",
"phone": "+49 621 123456",
"email": "h.mueller@kieswerk.de",
"role": "Disposant"
}
]
}

Geocoding

On the location create/edit UI, clicking "Locate" triggers address-to-coordinate resolution via the Photon API:

GET https://photon.komoot.io/api/?q={street} {zip} {city} {country}&limit=5&lang=de

Multiple results are shown as suggestions. Reverse geocoding (drag marker → fill address) uses:

GET https://photon.komoot.io/reverse?lat={lat}&lon={lng}&lang=de

MapLocationPicker Component

The MapLocationPicker component from @elcto/ui combines a MapLibre GL JS map with a draggable marker and an optional geocoding search bar:

import { MapLocationPicker } from '@elcto/ui';

<MapLocationPicker
latitude={lat}
longitude={lng}
onPositionChange={({ lat, lng }) => setCoords({ lat, lng })}
tiles="carto"
geocodingEnabled={true}
/>

Dragging the marker calls onPositionChange and updates the latitude/longitude form fields in real time. Manually editing the fields also moves the marker.

Deletion Behaviour

Deleting a location cascades to all its contacts (ON DELETE CASCADE). Trips referencing the deleted location have their origin_id or destination_id set to NULL.

RBAC Permissions

PermissionDescription
locations:readView locations
locations:writeCreate and update locations and contacts
locations:deleteDelete locations

Country Selection

The country field uses a CountrySelect component from @elcto/ui that stores ISO alpha-2 country codes (e.g. "DE", "US"). Features:

  • Searchable dropdown with flag emojis
  • Country names translated based on locale (en/de)
  • Value stored as ISO code in database

Map Location Picker

The create and edit pages include an interactive map for setting coordinates:

  • FloatingInput with search icon for geocoding (auto-search after 600ms, or Enter for immediate)
  • Draggable marker for fine-tuning position
  • Coordinates sync bidirectionally between inputs and map
  • Uses Photon (Komoot) API for geocoding