Show visited cities on map page

This commit is contained in:
Eugene Burmakin 2024-12-11 22:00:33 +01:00
parent cab70839b9
commit e7c393a776
5 changed files with 125 additions and 3 deletions

View file

@ -0,0 +1,30 @@
# frozen_string_literal: true
class Api::V1::Countries::VisitedCitiesController < ApiController
before_action :validate_params
def index
start_at = DateTime.parse(params[:start_at]).to_i
end_at = DateTime.parse(params[:end_at]).to_i
points = current_api_user
.tracked_points
.where(timestamp: start_at..end_at)
render json: { data: CountriesAndCities.new(points).call }
end
private
def validate_params
missing_params = %i[start_at end_at].select { |param| params[param].blank? }
if missing_params.any?
render json: {
error: "Missing required parameters: #{missing_params.join(', ')}"
}, status: :bad_request and return
end
params.permit(:start_at, :end_at)
end
end

View file

@ -1,2 +0,0 @@
module Api::V1::Points::TrackedMonthsHelper
end

View file

@ -958,8 +958,9 @@ export default class extends Controller {
} else {
this.map.addControl(this.rightPanel);
localStorage.setItem('mapPanelOpen', 'true');
// Fetch visited cities when panel is opened
this.fetchAndDisplayVisitedCities();
}
return;
}
@ -1008,6 +1009,7 @@ export default class extends Controller {
`).join('')}
</div>
</div>
</div>
`;
@ -1141,6 +1143,24 @@ export default class extends Controller {
L.DomEvent.disableClickPropagation(div);
// Add container for visited cities
div.innerHTML += `
<div id="visited-cities-container" class="mt-4">
<h3 class="text-lg font-bold mb-2">Visited cities</h3>
<div id="visited-cities-list" class="space-y-2"
style="max-height: 300px; overflow-y: auto; padding-right: 10px;">
<p class="text-gray-500">Loading visited places...</p>
</div>
</div>
`;
// Prevent map zoom when scrolling the cities list
const citiesList = div.querySelector('#visited-cities-list');
L.DomEvent.disableScrollPropagation(citiesList);
// Fetch visited cities when panel is first created
this.fetchAndDisplayVisitedCities();
return div;
};
@ -1187,5 +1207,71 @@ export default class extends Controller {
const endDate = `${year}-12-31T23:59`;
return `map?end_at=${encodeURIComponent(endDate)}&start_at=${encodeURIComponent(startDate)}`;
}
async fetchAndDisplayVisitedCities() {
const urlParams = new URLSearchParams(window.location.search);
const startAt = urlParams.get('start_at') || new Date().toISOString();
const endAt = urlParams.get('end_at') || new Date().toISOString();
try {
const response = await fetch(`/api/v1/countries/visited_cities?api_key=${this.apiKey}&start_at=${startAt}&end_at=${endAt}`, {
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
});
if (!response.ok) {
throw new Error('Network response was not ok');
}
const data = await response.json();
this.displayVisitedCities(data.data);
} catch (error) {
console.error('Error fetching visited cities:', error);
const container = document.getElementById('visited-cities-list');
if (container) {
container.innerHTML = '<p class="text-red-500">Error loading visited places</p>';
}
}
}
displayVisitedCities(citiesData) {
const container = document.getElementById('visited-cities-list');
if (!container) return;
if (!citiesData || citiesData.length === 0) {
container.innerHTML = '<p class="text-gray-500">No places visited during this period</p>';
return;
}
const html = citiesData.map(country => `
<div class="mb-4">
<h4 class="font-bold text-md">${country.country}</h4>
<ul class="ml-4 space-y-1">
${country.cities.map(city => `
<li class="text-sm">
${city.city}
<span class="text-gray-500">
(${new Date(city.timestamp * 1000).toLocaleDateString()})
</span>
</li>
`).join('')}
</ul>
</div>
`).join('');
container.innerHTML = html;
}
formatDuration(seconds) {
const days = Math.floor(seconds / (24 * 60 * 60));
const hours = Math.floor((seconds % (24 * 60 * 60)) / (60 * 60));
if (days > 0) {
return `${days}d ${hours}h`;
}
return `${hours}h`;
}
}

View file

@ -78,6 +78,7 @@ Rails.application.routes.draw do
namespace :countries do
resources :borders, only: :index
resources :visited_cities, only: :index
end
namespace :points do

View file

@ -0,0 +1,7 @@
require 'rails_helper'
RSpec.describe "Api::V1::Countries::VisitedCities", type: :request do
describe "GET /index" do
pending "add some examples (or delete) #{__FILE__}"
end
end