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": "[email protected]",
"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