Imlement visits deletion API

This commit is contained in:
Eugene Burmakin
2025-08-21 20:41:53 +02:00
parent 6e773b6b51
commit 550d20c555
8 changed files with 151 additions and 27 deletions
-1
View File
@@ -1,6 +1,5 @@
// Configure your import map in config/importmap.rb. Read more: https://github.com/rails/importmap-rails
import "@rails/ujs"
import "@rails/actioncable"
import "controllers"
import "@hotwired/turbo-rails"
@@ -72,22 +72,21 @@ export default class extends Controller {
// Create the Add Visit control
const AddVisitControl = L.Control.extend({
onAdd: (map) => {
const button = L.DomUtil.create('button', 'add-visit-button');
button.innerHTML = '📍 Add a Visit';
button.title = 'Click to add a visit to the map';
const button = L.DomUtil.create('button', 'leaflet-control-button add-visit-button');
button.innerHTML = '';
button.title = 'Add a visit';
// Style the button
button.style.backgroundColor = 'white';
button.style.padding = '8px 12px';
button.style.border = '2px solid #ccc';
button.style.borderRadius = '4px';
// Style the button to match other map controls
button.style.width = '48px';
button.style.height = '48px';
button.style.border = 'none';
button.style.cursor = 'pointer';
button.style.boxShadow = '0 1px 4px rgba(0,0,0,0.3)';
button.style.fontSize = '14px';
button.style.fontWeight = 'bold';
button.style.marginBottom = '5px';
button.style.display = 'block';
button.style.width = '100%';
button.style.backgroundColor = 'white';
button.style.borderRadius = '4px';
button.style.padding = '0';
button.style.lineHeight = '48px';
button.style.fontSize = '18px';
button.style.textAlign = 'center';
button.style.transition = 'all 0.2s ease';
@@ -135,10 +134,9 @@ export default class extends Controller {
this.isAddingVisit = true;
// Update button style to show active state
button.style.backgroundColor = '#007bff';
button.style.backgroundColor = '#dc3545';
button.style.color = 'white';
button.style.borderColor = '#0056b3';
button.innerHTML = '✕ Cancel';
button.innerHTML = '';
// Change cursor to crosshair
this.map.getContainer().style.cursor = 'crosshair';
@@ -155,8 +153,7 @@ export default class extends Controller {
// Reset button style
button.style.backgroundColor = 'white';
button.style.color = 'black';
button.style.borderColor = '#ccc';
button.innerHTML = '📍 Add Visit';
button.innerHTML = '';
// Reset cursor
this.map.getContainer().style.cursor = '';
@@ -370,7 +367,7 @@ export default class extends Controller {
if (stimulusController && stimulusController.visitsManager) {
console.log('Found maps controller with visits manager');
// Clear existing visits and fetch fresh data
if (stimulusController.visitsManager.visitCircles) {
stimulusController.visitsManager.visitCircles.clearLayers();
@@ -386,7 +383,7 @@ export default class extends Controller {
}
} else {
console.log('Could not find maps controller or visits manager');
// Fallback: Try to dispatch a custom event
const refreshEvent = new CustomEvent('visits:refresh', { bubbles: true });
mapsController.dispatchEvent(refreshEvent);
@@ -414,7 +411,7 @@ export default class extends Controller {
if (confirmedVisitsLayer && !map.hasLayer(confirmedVisitsLayer)) {
console.log('Adding confirmed visits layer to map');
map.addLayer(confirmedVisitsLayer);
// Update the layer control checkbox to reflect the layer is now active
this.updateLayerControlCheckbox('Confirmed Visits', true);
}
@@ -442,7 +439,7 @@ export default class extends Controller {
if (label && label.textContent.trim() === layerName) {
console.log(`Updating ${layerName} checkbox to ${isEnabled}`);
input.checked = isEnabled;
// Trigger change event to ensure proper state management
input.dispatchEvent(new Event('change', { bubbles: true }));
}
+56 -1
View File
@@ -1357,7 +1357,7 @@ export class VisitsManager {
<label class="label">
<span class="label-text text-sm font-medium">Location</span>
</label>
<select class="select select-bordered select-sm w-full bg-base-200 text-base-content" name="place">
<select class="select select-bordered select-sm text-xs w-full bg-base-200 text-base-content" name="place">
${possiblePlaces.length > 0 ? possiblePlaces.map(place => `
<option value="${place.id}" ${place.id === visit.place.id ? 'selected' : ''}>
${place.name}
@@ -1391,6 +1391,14 @@ export class VisitsManager {
</button>
` : ''}
</div>
<div class="mt-2">
<button type="button" class="btn btn-sm btn-outline btn-error w-full delete-visit" data-id="${visit.id}">
<svg class="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
</svg>
Delete Visit
</button>
</div>
</form>
</div>
`;
@@ -1507,9 +1515,11 @@ export class VisitsManager {
// Add event listeners for confirm and decline buttons
const confirmBtn = form.querySelector('.confirm-visit');
const declineBtn = form.querySelector('.decline-visit');
const deleteBtn = form.querySelector('.delete-visit');
confirmBtn?.addEventListener('click', (event) => this.handleStatusChange(event, visit.id, 'confirmed'));
declineBtn?.addEventListener('click', (event) => this.handleStatusChange(event, visit.id, 'declined'));
deleteBtn?.addEventListener('click', (event) => this.handleDeleteVisit(event, visit.id));
}
}
@@ -1551,6 +1561,51 @@ export class VisitsManager {
}
}
/**
* Handles deletion of a visit with confirmation
* @param {Event} event - The click event
* @param {string} visitId - The visit ID to delete
*/
async handleDeleteVisit(event, visitId) {
event.preventDefault();
event.stopPropagation();
// Show confirmation dialog
const confirmDelete = confirm('Are you sure you want to delete this visit? This action cannot be undone.');
if (!confirmDelete) {
return;
}
try {
const response = await fetch(`/api/v1/visits/${visitId}`, {
method: 'DELETE',
headers: {
'Authorization': `Bearer ${this.apiKey}`,
}
});
if (response.ok) {
// Close the popup
if (this.currentPopup) {
this.map.closePopup(this.currentPopup);
this.currentPopup = null;
}
// Refresh the visits list
this.fetchAndDisplayVisits();
showFlashMessage('notice', 'Visit deleted successfully');
} else {
const errorData = await response.json();
const errorMessage = errorData.error || 'Failed to delete visit';
showFlashMessage('error', errorMessage);
}
} catch (error) {
console.error('Error deleting visit:', error);
showFlashMessage('error', 'Failed to delete visit');
}
}
/**
* Truncates text to a specified length and adds ellipsis if needed
* @param {string} text - The text to truncate