mirror of
https://github.com/Freika/dawarich.git
synced 2026-01-11 09:41:40 -05:00
Implement rendering the route when the dates if the trip are changed
This commit is contained in:
parent
2cfc485f12
commit
e8842a9476
8 changed files with 189 additions and 47 deletions
File diff suppressed because one or more lines are too long
|
|
@ -3,9 +3,10 @@
|
||||||
class TripsController < ApplicationController
|
class TripsController < ApplicationController
|
||||||
before_action :authenticate_user!
|
before_action :authenticate_user!
|
||||||
before_action :set_trip, only: %i[show edit update destroy]
|
before_action :set_trip, only: %i[show edit update destroy]
|
||||||
|
before_action :set_coordinates, only: %i[show edit]
|
||||||
|
|
||||||
def index
|
def index
|
||||||
@trips = current_user.trips
|
@trips = current_user.trips.order(created_at: :desc).page(params[:page])
|
||||||
end
|
end
|
||||||
|
|
||||||
def show
|
def show
|
||||||
|
|
@ -54,6 +55,14 @@ class TripsController < ApplicationController
|
||||||
@trip = current_user.trips.find(params[:id])
|
@trip = current_user.trips.find(params[:id])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
def set_coordinates
|
||||||
|
@coordinates = @trip.points.pluck(
|
||||||
|
:latitude, :longitude, :battery, :altitude, :timestamp, :velocity, :id,
|
||||||
|
:country
|
||||||
|
).map { [_1.to_f, _2.to_f, _3.to_s, _4.to_s, _5.to_s, _6.to_s, _7.to_s, _8.to_s] }
|
||||||
|
end
|
||||||
|
|
||||||
def trip_params
|
def trip_params
|
||||||
params.require(:trip).permit(:name, :started_at, :ended_at, :notes, :field_notes)
|
params.require(:trip).permit(:name, :started_at, :ended_at, :notes, :field_notes)
|
||||||
end
|
end
|
||||||
|
|
|
||||||
69
app/javascript/controllers/datetime_controller.js
Normal file
69
app/javascript/controllers/datetime_controller.js
Normal file
|
|
@ -0,0 +1,69 @@
|
||||||
|
import { Controller } from "@hotwired/stimulus"
|
||||||
|
|
||||||
|
export default class extends Controller {
|
||||||
|
static targets = ["startedAt", "endedAt", "apiKey"]
|
||||||
|
static values = { tripsId: String }
|
||||||
|
|
||||||
|
connect() {
|
||||||
|
console.log("Datetime controller connected")
|
||||||
|
this.debounceTimer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async updateCoordinates(event) {
|
||||||
|
// Clear any existing timeout
|
||||||
|
if (this.debounceTimer) {
|
||||||
|
clearTimeout(this.debounceTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set new timeout
|
||||||
|
this.debounceTimer = setTimeout(async () => {
|
||||||
|
const startedAt = this.startedAtTarget.value
|
||||||
|
const endedAt = this.endedAtTarget.value
|
||||||
|
const apiKey = this.apiKeyTarget.value
|
||||||
|
|
||||||
|
if (startedAt && endedAt) {
|
||||||
|
try {
|
||||||
|
const params = new URLSearchParams({
|
||||||
|
start_at: startedAt,
|
||||||
|
end_at: endedAt,
|
||||||
|
api_key: apiKey,
|
||||||
|
slim: true
|
||||||
|
})
|
||||||
|
let allPoints = [];
|
||||||
|
let currentPage = 1;
|
||||||
|
const perPage = 1000;
|
||||||
|
|
||||||
|
do {
|
||||||
|
const paginatedParams = `${params}&page=${currentPage}&per_page=${perPage}`;
|
||||||
|
const response = await fetch(`/api/v1/points?${paginatedParams}`);
|
||||||
|
const data = await response.json();
|
||||||
|
|
||||||
|
allPoints = [...allPoints, ...data];
|
||||||
|
|
||||||
|
const totalPages = parseInt(response.headers.get('X-Total-Pages'));
|
||||||
|
currentPage++;
|
||||||
|
|
||||||
|
if (!totalPages || currentPage > totalPages) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
const event = new CustomEvent('coordinates-updated', {
|
||||||
|
detail: { coordinates: allPoints },
|
||||||
|
bubbles: true,
|
||||||
|
composed: true
|
||||||
|
})
|
||||||
|
|
||||||
|
const tripsElement = document.querySelector('[data-controller="trips"]')
|
||||||
|
if (tripsElement) {
|
||||||
|
tripsElement.dispatchEvent(event)
|
||||||
|
} else {
|
||||||
|
console.error('Trips controller element not found')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,24 +13,43 @@ import { esriWorldGrayCanvasMapLayer } from "../maps/layers"
|
||||||
import { fetchAndDisplayPhotos } from '../maps/helpers';
|
import { fetchAndDisplayPhotos } from '../maps/helpers';
|
||||||
|
|
||||||
export default class extends Controller {
|
export default class extends Controller {
|
||||||
static targets = ["container"]
|
static targets = ["container", "startedAt", "endedAt"]
|
||||||
|
static values = { }
|
||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
this.coordinates = JSON.parse(this.element.dataset.coordinates)
|
console.log("Trips controller connected")
|
||||||
this.apiKey = this.element.dataset.api_key
|
this.coordinates = JSON.parse(this.containerTarget.dataset.coordinates)
|
||||||
this.userSettings = JSON.parse(this.element.dataset.user_settings)
|
this.apiKey = this.containerTarget.dataset.api_key
|
||||||
this.timezone = this.element.dataset.timezone
|
this.userSettings = JSON.parse(this.containerTarget.dataset.user_settings)
|
||||||
this.distanceUnit = this.element.dataset.distance_unit
|
this.timezone = this.containerTarget.dataset.timezone
|
||||||
|
this.distanceUnit = this.containerTarget.dataset.distance_unit
|
||||||
|
|
||||||
|
// Initialize map and layers
|
||||||
|
this.initializeMap()
|
||||||
|
|
||||||
|
// Add event listener for coordinates updates
|
||||||
|
this.element.addEventListener('coordinates-updated', (event) => {
|
||||||
|
console.log("Coordinates updated:", event.detail.coordinates)
|
||||||
|
this.updateMapWithCoordinates(event.detail.coordinates)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move map initialization to separate method
|
||||||
|
initializeMap() {
|
||||||
// Initialize layer groups
|
// Initialize layer groups
|
||||||
this.markersLayer = L.layerGroup()
|
this.markersLayer = L.layerGroup()
|
||||||
this.polylinesLayer = L.layerGroup()
|
this.polylinesLayer = L.layerGroup()
|
||||||
this.photoMarkers = L.layerGroup()
|
this.photoMarkers = L.layerGroup()
|
||||||
|
|
||||||
const center = [this.coordinates[0][0], this.coordinates[0][1]]
|
// Set default center and zoom for world view
|
||||||
|
const hasValidCoordinates = this.coordinates && Array.isArray(this.coordinates) && this.coordinates.length > 0
|
||||||
|
const center = hasValidCoordinates
|
||||||
|
? [this.coordinates[0][0], this.coordinates[0][1]]
|
||||||
|
: [20, 0] // Roughly centers the world map
|
||||||
|
const zoom = hasValidCoordinates ? 14 : 2
|
||||||
|
|
||||||
// Initialize map
|
// Initialize map
|
||||||
this.map = L.map(this.containerTarget).setView(center, 14)
|
this.map = L.map(this.containerTarget).setView(center, zoom)
|
||||||
|
|
||||||
// Add base map layer
|
// Add base map layer
|
||||||
osmMapLayer(this.map, "OpenStreetMap")
|
osmMapLayer(this.map, "OpenStreetMap")
|
||||||
|
|
@ -111,7 +130,7 @@ export default class extends Controller {
|
||||||
marker.bindPopup(popupContent)
|
marker.bindPopup(popupContent)
|
||||||
|
|
||||||
// Add to markers layer instead of directly to map
|
// Add to markers layer instead of directly to map
|
||||||
this.markersLayer.addTo(this.map)
|
|
||||||
marker.addTo(this.markersLayer)
|
marker.addTo(this.markersLayer)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -135,19 +154,25 @@ export default class extends Controller {
|
||||||
this.map.fitBounds(bounds, { padding: [50, 50] })
|
this.map.fitBounds(bounds, { padding: [50, 50] })
|
||||||
}
|
}
|
||||||
|
|
||||||
baseMaps() {
|
// Add this new method to update coordinates and refresh the map
|
||||||
let selectedLayerName = this.userSettings.preferred_map_layer || "OpenStreetMap";
|
updateMapWithCoordinates(newCoordinates) {
|
||||||
|
// Transform the coordinates to match the expected format
|
||||||
|
this.coordinates = newCoordinates.map(point => [
|
||||||
|
parseFloat(point.latitude),
|
||||||
|
parseFloat(point.longitude),
|
||||||
|
(point.timestamp).toString()
|
||||||
|
])
|
||||||
|
|
||||||
return {
|
// Clear existing layers
|
||||||
OpenStreetMap: osmMapLayer(this.map, selectedLayerName),
|
this.markersLayer.clearLayers()
|
||||||
"OpenStreetMap.HOT": osmHotMapLayer(this.map, selectedLayerName),
|
this.polylinesLayer.clearLayers()
|
||||||
OPNV: OPNVMapLayer(this.map, selectedLayerName),
|
this.photoMarkers.clearLayers()
|
||||||
openTopo: openTopoMapLayer(this.map, selectedLayerName),
|
|
||||||
cyclOsm: cyclOsmMapLayer(this.map, selectedLayerName),
|
// Add new markers and route if coordinates exist
|
||||||
esriWorldStreet: esriWorldStreetMapLayer(this.map, selectedLayerName),
|
if (this.coordinates?.length > 0) {
|
||||||
esriWorldTopo: esriWorldTopoMapLayer(this.map, selectedLayerName),
|
this.addMarkers()
|
||||||
esriWorldImagery: esriWorldImageryMapLayer(this.map, selectedLayerName),
|
this.addPolyline()
|
||||||
esriWorldGrayCanvas: esriWorldGrayCanvasMapLayer(this.map, selectedLayerName)
|
this.fitMapToBounds()
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,7 +50,7 @@
|
||||||
data-user_settings=<%= current_user.settings.to_json %>
|
data-user_settings=<%= current_user.settings.to_json %>
|
||||||
data-coordinates="<%= @coordinates %>"
|
data-coordinates="<%= @coordinates %>"
|
||||||
data-timezone="<%= Rails.configuration.time_zone %>">
|
data-timezone="<%= Rails.configuration.time_zone %>">
|
||||||
<div data-maps-target="container" class="h-[25rem] w-full min-h-screen">
|
<div data-maps-target="container" class="h-[25rem] rounded-lg w-full min-h-screen">
|
||||||
<div id="fog" class="fog"></div>
|
<div id="fog" class="fog"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -11,29 +11,63 @@
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
||||||
<div class="flex flex-col lg:flex-row gap-4 mt-4">
|
<div class="flex flex-col lg:flex-row gap-4 my-4" data-controller="trips">
|
||||||
<div class="form-control w-full lg:w-1/2">
|
<div class="w-full lg:w-1/2">
|
||||||
<%= form.label :name %>
|
<div
|
||||||
<%= form.text_field :name, class: 'input input-bordered' %>
|
id='map trips-container'
|
||||||
</div>
|
class="w-full h-full rounded-lg"
|
||||||
<div class="flex flex-col lg:flex-row lg:w-1/2 gap-4">
|
data-trips-target="container"
|
||||||
<div class="form-control w-full lg:w-1/2">
|
data-distance_unit="<%= DISTANCE_UNIT %>"
|
||||||
<%= form.label :started_at %>
|
data-api_key="<%= current_user.api_key %>"
|
||||||
<%= form.datetime_field :started_at, include_seconds: false, class: 'input input-bordered', value: trip.started_at %>
|
data-user_settings="<%= current_user.settings.to_json %>"
|
||||||
</div>
|
data-coordinates="<%= [].to_json %>"
|
||||||
<div class="form-control w-full lg:w-1/2">
|
data-timezone="<%= Rails.configuration.time_zone %>">
|
||||||
<%= form.label :ended_at %>
|
|
||||||
<%= form.datetime_field :ended_at, include_seconds: false, class: 'input input-bordered', value: trip.ended_at %>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-control w-full mt-4 mb-4">
|
<div
|
||||||
<%= form.label :field_notes %>
|
class="w-full lg:w-1/2 space-y-4"
|
||||||
<%= form.rich_text_area :field_notes %>
|
data-controller="datetime">
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="inline mb-4">
|
<div class="form-control">
|
||||||
<%= form.submit class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
|
<%= form.label :name %>
|
||||||
|
<%= form.text_field :name, class: 'input input-bordered w-full' %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-col sm:flex-row gap-4">
|
||||||
|
<div class="form-control w-full">
|
||||||
|
<input type="hidden" data-datetime-target="apiKey" value="<%= current_user.api_key %>">
|
||||||
|
<%= form.label :started_at %>
|
||||||
|
<%= form.datetime_field :started_at,
|
||||||
|
include_seconds: false,
|
||||||
|
class: 'input input-bordered w-full',
|
||||||
|
value: trip.started_at,
|
||||||
|
data: {
|
||||||
|
datetime_target: "startedAt",
|
||||||
|
action: "change->datetime#updateCoordinates"
|
||||||
|
} %>
|
||||||
|
</div>
|
||||||
|
<div class="form-control w-full">
|
||||||
|
<%= form.label :ended_at %>
|
||||||
|
<%= form.datetime_field :ended_at,
|
||||||
|
include_seconds: false,
|
||||||
|
class: 'input input-bordered w-full',
|
||||||
|
value: trip.ended_at,
|
||||||
|
data: {
|
||||||
|
datetime_target: "endedAt",
|
||||||
|
action: "change->datetime#updateCoordinates"
|
||||||
|
} %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-control">
|
||||||
|
<%= form.label :field_notes %>
|
||||||
|
<%= form.rich_text_area :field_notes %>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<%= form.submit class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,11 @@
|
||||||
|
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<div id="trips" class="min-w-full">
|
<div id="trips" class="min-w-full">
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<h1 class="text-2xl font-bold">Trips</h1>
|
||||||
|
<%= link_to 'New trip', new_trip_path, class: 'btn btn-primary' %>
|
||||||
|
</div>
|
||||||
|
|
||||||
<% if @trips.empty? %>
|
<% if @trips.empty? %>
|
||||||
<div class="hero min-h-80 bg-base-200">
|
<div class="hero min-h-80 bg-base-200">
|
||||||
<div class="hero-content text-center">
|
<div class="hero-content text-center">
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
<div class="w-full">
|
<div class="w-full">
|
||||||
<div
|
<div
|
||||||
id='map'
|
id='map'
|
||||||
class="w-full h-full"
|
class="w-full h-full rounded-lg"
|
||||||
data-controller="trips"
|
data-controller="trips"
|
||||||
data-trips-target="container"
|
data-trips-target="container"
|
||||||
data-distance_unit="<%= DISTANCE_UNIT %>"
|
data-distance_unit="<%= DISTANCE_UNIT %>"
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue