Add notes to places and manage place tags

This commit is contained in:
Eugene Burmakin 2025-11-18 22:16:34 +01:00
parent c711bed383
commit 2ab24201c1
7 changed files with 78 additions and 11 deletions

View file

@ -21,6 +21,10 @@ OIDC_REDIRECT_URI=https://your-dawarich-url.com/users/auth/openid_connect/callba
- Support for KML file uploads. #350 - Support for KML file uploads. #350
- Added a commented line in the `docker-compose.yml` file to use an alternative PostGIS image for ARM architecture. - Added a commented line in the `docker-compose.yml` file to use an alternative PostGIS image for ARM architecture.
- User can now create a place directly from the map and add tags and notes to it. If reverse geocoding is enabled, list of nearby places will be shown as suggestions.
- User can create and manage tags for places.
- [ ] Tags can be added when creating or editing a place.
- User can enable or disable places layers on the map to show/hide all or just some of their places based on tags.
## Fixed ## Fixed

View file

@ -75,7 +75,7 @@ module Api
end end
def place_params def place_params
params.require(:place).permit(:name, :latitude, :longitude, :source) params.require(:place).permit(:name, :latitude, :longitude, :source, :note)
end end
def tag_ids def tag_ids
@ -102,6 +102,7 @@ module Api
latitude: place.latitude, latitude: place.latitude,
longitude: place.longitude, longitude: place.longitude,
source: place.source, source: place.source,
note: place.note,
icon: place.tags.first&.icon, icon: place.tags.first&.icon,
color: place.tags.first&.color, color: place.tags.first&.color,
visits_count: place.visits.count, visits_count: place.visits.count,

View file

@ -143,6 +143,7 @@ export class PlacesManager {
<div class="place-popup" style="min-width: 200px;"> <div class="place-popup" style="min-width: 200px;">
<h3 class="font-bold text-lg mb-2">${place.name}</h3> <h3 class="font-bold text-lg mb-2">${place.name}</h3>
${tags ? `<div class="mb-2">${tags}</div>` : ''} ${tags ? `<div class="mb-2">${tags}</div>` : ''}
${place.note ? `<p class="text-sm text-gray-600 mb-2 italic">${this.escapeHtml(place.note)}</p>` : ''}
${place.visits_count ? `<p class="text-sm">Visits: ${place.visits_count}</p>` : ''} ${place.visits_count ? `<p class="text-sm">Visits: ${place.visits_count}</p>` : ''}
<div class="mt-2 flex gap-2"> <div class="mt-2 flex gap-2">
<button class="btn btn-xs btn-error" data-place-id="${place.id}" data-action="delete-place"> <button class="btn btn-xs btn-error" data-place-id="${place.id}" data-action="delete-place">
@ -153,6 +154,12 @@ export class PlacesManager {
`; `;
} }
escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
setupMapClickHandler() { setupMapClickHandler() {
this.map.on('click', (e) => { this.map.on('click', (e) => {
if (this.creationMode) { if (this.creationMode) {

View file

@ -47,12 +47,46 @@ export class PrivacyZoneManager {
filterPoints(points) { filterPoints(points) {
if (!this.zones || this.zones.length === 0) return points; if (!this.zones || this.zones.length === 0) return points;
return points.filter(point => { // Filter points and ensure polylines break at privacy zone boundaries
// Point format: [lat, lng, ...] // We need to manipulate timestamps to force polyline breaks
const filteredPoints = [];
let lastWasPrivate = false;
let privacyZoneEncountered = false;
for (let i = 0; i < points.length; i++) {
const point = points[i];
const lat = point[0]; const lat = point[0];
const lng = point[1]; const lng = point[1];
return !this.isPointInPrivacyZone(lat, lng); const isPrivate = this.isPointInPrivacyZone(lat, lng);
});
if (!isPrivate) {
// Point is not in privacy zone, include it
const newPoint = [...point]; // Clone the point array
// If we just exited a privacy zone, force a polyline break by adding
// a large time gap that exceeds minutes_between_routes threshold
if (privacyZoneEncountered && filteredPoints.length > 0) {
// Add 2 hours (120 minutes) to timestamp to force a break
// This is larger than default minutes_between_routes (30 min)
const lastPoint = filteredPoints[filteredPoints.length - 1];
if (newPoint[4]) { // If timestamp exists (index 4)
newPoint[4] = lastPoint[4] + (120 * 60); // Add 120 minutes in seconds
}
privacyZoneEncountered = false;
}
filteredPoints.push(newPoint);
lastWasPrivate = false;
} else {
// Point is in privacy zone - skip it
if (!lastWasPrivate) {
privacyZoneEncountered = true;
}
lastWasPrivate = true;
}
}
return filteredPoints;
} }
filterTracks(tracks) { filterTracks(tracks) {

View file

@ -25,15 +25,30 @@
<label class="label"> <label class="label">
<span class="label-text font-semibold">Place Name *</span> <span class="label-text font-semibold">Place Name *</span>
</label> </label>
<input <input
type="text" type="text"
name="name" name="name"
placeholder="Enter place name..." placeholder="Enter place name..."
class="input input-bordered w-full" class="input input-bordered w-full"
data-place-creation-target="nameInput" data-place-creation-target="nameInput"
required> required>
</div> </div>
<div class="form-control">
<label class="label">
<span class="label-text font-semibold">Note</span>
</label>
<textarea
name="note"
placeholder="Add a personal note about this place..."
class="textarea textarea-bordered w-full bg-base-100"
rows="3"
data-place-creation-target="noteInput"></textarea>
<label class="label">
<span class="label-text-alt">Optional - Add any notes or details about this place</span>
</label>
</div>
<div class="form-control"> <div class="form-control">
<label class="label"> <label class="label">
<span class="label-text font-semibold">Tags</span> <span class="label-text font-semibold">Tags</span>

View file

@ -0,0 +1,5 @@
class AddNoteToPlaces < ActiveRecord::Migration[8.0]
def change
add_column :places, :note, :text
end
end

3
db/schema.rb generated
View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[8.0].define(version: 2025_11_18_204141) do ActiveRecord::Schema[8.0].define(version: 2025_11_18_210506) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "pg_catalog.plpgsql" enable_extension "pg_catalog.plpgsql"
enable_extension "postgis" enable_extension "postgis"
@ -181,6 +181,7 @@ ActiveRecord::Schema[8.0].define(version: 2025_11_18_204141) do
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
t.geography "lonlat", limit: {srid: 4326, type: "st_point", geographic: true} t.geography "lonlat", limit: {srid: 4326, type: "st_point", geographic: true}
t.bigint "user_id" t.bigint "user_id"
t.text "note"
t.index "(((geodata -> 'properties'::text) ->> 'osm_id'::text))", name: "index_places_on_geodata_osm_id" t.index "(((geodata -> 'properties'::text) ->> 'osm_id'::text))", name: "index_places_on_geodata_osm_id"
t.index ["lonlat"], name: "index_places_on_lonlat", using: :gist t.index ["lonlat"], name: "index_places_on_lonlat", using: :gist
t.index ["user_id"], name: "index_places_on_user_id" t.index ["user_id"], name: "index_places_on_user_id"