SARBase/templates/sar_details.html

552 lines
22 KiB
HTML

{% extends "base.html" %}
{% block title %}
SAR job details
{% endblock %}
{% block content %}
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"/>
<!-- Leaflet.draw CSS -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.css">
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<!-- Leaflet.draw JS -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
<!-- Include leaflet-gpx plugin -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet-gpx/1.7.0/gpx.min.js"></script>
<script src='https://unpkg.com/@turf/turf@6/turf.min.js'></script>
<script src="/static/togpx.js"></script>
<style>
#map {
height: 400px;
}
.button {
margin: 5px;
}
.comment-text {
white-space: pre-line;
}
</style>
<div class="container mt-4">
<h2 class="mb-4">#{{ sar.SARCall.id }} : {{ sar.SARCall.title }}</h2>
<div class="card mb-4">
<div class="card-body">
<table>
<tr>
<td>
<div class="card-mb5">
<div class="card-body">
<div>
<p>Created at: {{ sar.SARCall.created }}</p>
<p>Last updated: {{ sar.SARCall.updated }}</p>
</div>
<p><strong>Coordination
manager:</strong> {{ sar.SARCall.coordination_officer.full_name }}</p>
<p><strong>Search manager:</strong> {{ sar.SARCall.search_officer.full_name }}</p>
<p>Status: {{ sar.SARStatus.name }}</p>
{% if is_logged_in %}
<p>Category: {{ sar.SARCategory.name }}</p>
{% endif %}
<p><strong>Start Date:</strong> {{ sar.SARCall.start_date }}</p>
<p><strong>Finish Date:</strong> {{ sar.SARCall.finish_date }}</p>
<p>Result: {{ sar.SARResult.name }}</p>
</div>
</div>
</td>
<td>
<div class="card-mb5">
<div class="card-body">
<p>IPP longitude: {{ sar.SARCall.longitude }} </p>
<p>IPP latitude: {{ sar.SARCall.latitude }}</p>
<p>Longitude found: {{ sar.SARCall.longitude_found }}</p>
<p>Latitude found: {{ sar.SARCall.latitude_found }}</p>
<p>Description: {{ sar.SARCall.description }}</p>
{% if is_logged_in %}
<p>Description private: {{ sar.SARCall.description_hidden }}</p>
{% endif %}
</div>
</div>
</td>
<!-- Other SAR details -->
</tr>
</table>
</div>
</div>
<div>
{% if is_logged_in %}
<a href="{{ url_for('edit_sar', id=sar.SARCall.id) }}" class="btn btn-primary">Edit</a>
<a href="{{ url_for('delete_sar', id=sar.SARCall.id) }}" class="btn btn-danger">Delete</a>
{% endif %}
</div>
<div id="map" class="mb-4"></div>
<!-- Display Comments -->
{% for comment in sar.SARCall.comments %}
<div class="comment">
<div class="card mb-4">
<div class="card-body">
<strong>{{ comment.user.full_name }}</strong>: at {{ comment.created }} ,
updated {{ comment.updated }}
<div class="card mb-5">
<div class="card-body">
<div class="comment-text"><p id="comment-text-{{ comment.id }}">{{ comment.text }}</p>
</div>
</div>
</div>
{% for gpx_track in comments_with_gpx %}
{% if gpx_track.comment_id == comment.id %}
<li>
<a href="{{ url_for('get_gpx', gpx_id=gpx_track.id) }}">{{ gpx_track.name }}</a>: {{ gpx_track.comment }}
</li>
{% endif %}
{% endfor %}
{% if is_logged_in %}
{% if current_user.id == comment.user_id or current_user.id == 1 or current_user.id == sar.SARCall.user_id %}
<button class="edit-comment-btn" data-comment-id="{{ comment.id }}"
data-comment-text="{{ comment.text }}">Edit
</button>
<!-- Button to Open GPX Upload Modal -->
<button type="button" class="gpx-upload-button" data-toggle="modal"
data-target="#uploadGPXModal"
data-comment-id="{{ comment.id }}">
Upload GPX File
</button>
<button type="button" class="delete-comment-btn">
<a href="{{ url_for('delete_comment', id=comment.id) }}">Delete</a>
</button>
{% endif %}
{% endif %}
</div>
</div>
</div>
{% endfor %}
<!-- Edit Comment Modal -->
<div class="modal fade" id="editCommentModal" tabindex="-1" aria-labelledby="editCommentModalLabel"
aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="editCommentModalLabel">Edit Comment</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<div class="modal-body">
<form id="editCommentForm">
<div class="form-group">
<label for="commentText" class="col-form-label">Comment:</label>
<textarea class="form-control" id="commentText"></textarea>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="saveComment">Save changes</button>
</div>
</div>
</div>
</div>
<!-- Add Comment Form -->
{% if is_logged_in %}
<form action="{{ url_for('add_comment', sar_call_id=sar.SARCall.id) }}" method="post"
enctype="multipart/form-data">
<div class="form-group">
<label for="text">Comment:</label>
<textarea name="text" class="form-control"></textarea>
</div>
<div class="form-group">
</div>
<button type="submit" class="btn btn-primary">Add Comment</button>
</form>
{% endif %}
</div>
<!-- GPX File Upload Modal -->
<div class="modal fade" id="uploadGPXModal" tabindex="-1" aria-labelledby="uploadGPXModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="uploadGPXModalLabel">Upload GPX File</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form id="uploadGPXForm" method="post" enctype="multipart/form-data">
<div class="modal-body">
<div class="form-group">
<label for="gpxFileName">Description: </label>
<input type="text" class="form-control" id="gpxFileName" name="gpxFileName" required>
</div>
<div class="form-group">
<label for="gpxFile">GPX File</label>
<input type="file" class="form-control" id="gpxFile" name="gpxFile" required>
</div>
</div>
<input type="hidden" id="commentIdForGPX" name="commentId">
<input type="hidden" id="sarIdForGPX" name="sarId" value="{{ sar.SARCall.id }}">
<div class="modal-footer">
<button type="submit" class="btn btn-primary">Upload</button>
</div>
</form>
</div>
</div>
</div>
{# <!-- Button to trigger the modal -->#}
{# <button id="save-track-button" type="button" class="btn btn-primary" data-toggle="modal"#}
{# data-target="#saveTrackModal">#}
{# Save Track#}
{# </button>#}
<!-- The Modal -->
<div class="modal fade" id="saveTrackModal" tabindex="-1" role="dialog" aria-labelledby="saveTrackModalLabel"
aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="saveTrackModalLabel">Save Track</h5>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">&times;</span>
</button>
</div>
<form id="uploadTRKForm" method="post" enctype="multipart/form-data">
<div class="modal-body">
<!-- Form inputs for track name and comment -->
<div class="form-group">
<label for="track-name">Track Name:</label>
<input type="text" class="form-control" id="track-name">
</div>
<div class="form-group">
<label for="track-comment">Comment:</label>
<textarea class="form-control" id="track-comment"></textarea>
</div>
</div>
<input type="hidden" id="sarIdForTRK" name="sarId" value="{{ sar.SARCall.id }}">
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-primary" id="confirm-save-track">Save</button>
</div>
</form>
</div>
</div>
</div>
<script>
// Load GPX file and add to map
function loadAndDisplayGPX(gpxId) {
fetch('/get_gpx/' + gpxId)
.then(response => response.text())
.then(gpxData => {
new L.GPX(gpxData, {
async: true,
marker_options: {
startIconUrl: '/static/pin-icon-start.png',
endIconUrl: '/static/pin-icon-end.png',
shadowUrl: '/static/pin-shadow.png',
wptIconUrls: {
'': '/static/pin-icon-wpt.png'
}
}
}).on('loaded', function (e) {
map.fitBounds(e.target.getBounds());
}).addTo(map);
})
.catch(error => console.error('Error loading GPX file:', error));
}
var gpxData = {{ gpx_ids | tojson }};
var map = L.map('map').setView([{{ sar.SARCall.latitude }}, {{ sar.SARCall.longitude }}], 13);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 19,
attribution: '© OpenStreetMap contributors'
}).addTo(map);
L.marker([{{ sar.SARCall.latitude }}, {{ sar.SARCall.longitude }}]).addTo(map);
gpxData.forEach(function (id) {
loadAndDisplayGPX(id);
});
// Initialize drawing capabilities
var drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
var drawControl = new L.Control.Draw({
edit: {
featureGroup: drawnItems,
},
draw: {
polygon: true,
polyline: true,
rectangle: false,
circle: true,
marker: true,
},
});
map.addControl(drawControl);
map.on('draw:created', function (e) {
var layer = e.layer;
// Add the drawn layer to the map and drawnItems feature group
map.addLayer(layer);
drawnItems.addLayer(layer);
// Get the GeoJSON representation of the drawn shape
var geoJSON = layer.toGeoJSON();
// Send the GeoJSON data to your server to save as a comment
// You can use AJAX or any method you prefer for this part
});
// Create a custom control for the "Save Track" button
var saveTrackControl = L.Control.extend({
options: {
position: 'bottomleft', // You can adjust the position as needed
},
onAdd: function (map) {
// Create a container div for the button
var container = L.DomUtil.create('div', 'leaflet-bar leaflet-control');
// Create the "Save Track" button and add it to the container
var button = L.DomUtil.create('a', 'leaflet-control-button', container);
button.innerHTML = '&#128190;' //floppy disk icon :)
// Add a click event handler to the button
L.DomEvent.on(button, 'click', function () {
// Open your modal dialog for saving the track here
$('#saveTrackModal').modal('show');
});
return container;
},
});
// Add the custom control to your map
new saveTrackControl().addTo(map);
</script>
<script>
var commentId; // Declare this outside of the .edit-comment-btn click handler
var commentText; // Declare this outside of the .edit-comment-btn click handler
$(document).ready(function () {
$('.edit-comment-btn').on('click', function () {
// Get the comment data
commentId = $(this).data('comment-id');
commentText = $(this).data('comment-text');
// Set the comment data in the modal
$('#commentText').val(commentText);
$('#editCommentModal').modal('show');
// Save changes
$('#saveComment').on('click', function () {
$.ajax({
url: '/edit_comment/' + commentId,
method: 'POST',
data: {comment: $('#commentText').val()},
success: function (response) {
// Update the comment display on the page
$('#comment-text-' + commentId).text($('#commentText').val());
$('button.edit-comment-btn[data-comment-id="' + commentId + '"]').data('comment-text', $('#commentText').val());
$('#editCommentModal').modal('hide');
// Update the comment display on the page as needed
},
error: function () {
// Handle error
alert("Error updating comment");
}
});
});
});
});
</script>
<script>
var commentId;
$('.gpx-upload-button').on('click', function () {
var commentId = $(this).data('comment-id');
$('#commentIdForGPX').val(commentId);
});
$(document).ready(function () {
{#commentId = $(this).data('comment-id');#}
{#console.log(commentId);#}
// Handle form submission
$('#uploadGPXForm').on('submit', function (e) {
e.preventDefault();
var formData = new FormData(this);
$.ajax({
url: '/upload_gpx', // Update with the correct route
type: 'POST',
data: formData,
contentType: false,
processData: false,
success: function (response) {
// Handle success - Close modal, Update the map with new GPX data
$('#uploadGPXModal').modal('hide');
},
error: function () {
// Handle error
alert("Error uploading GPX file");
}
});
});
});
$('#uploadGPXModal').on('hie', function (e) {
// Refresh the parent page (assuming you want to refresh the entire page)
location.reload(); // This will refresh the current page
});
</script>
<script>
function getTrackData() {
var drItems = {
"type": "FeatureCollection",
"features": []
};
// Assuming you are using a Leaflet.Draw FeatureGroup for drawn items
// Later in your code, when you want to iterate over the features
drawnItems.eachLayer(function (layer) {
feature = layer.toGeoJSON();
if (layer instanceof L.Circle) {
feature.properties.radius = layer.getRadius();
drItems.features.push(feature);
}
else if(layer instanceof L.CircleMarker)
{
feature.properties.radius = layer.getRadius();
drItems.features.push(feature);
}
else
{
drItems.features.push(feature);
}
});
// Check if there are any features (tracks) drawn
if (drItems.features.length === 0) {
return null; // No tracks drawn
}
// Convert GeoJSON to GPX format (you can use a library like togeojson or turf.js)
var gpxData = geoJSONToGPX(drItems);
return gpxData;
}
// Function to convert GeoJSON to GPX
function geoJSONToGPX(geoJSONData) {
// Convert each point to a circle polygon
let convertedFeatures = geoJSONData.features.map(feature => {
console.log(feature);
if (feature.geometry.type === 'Point' && feature.properties.radius) {
// Convert radius from meters to kilometers for turf.circle
let radiusInKm = feature.properties.radius / 1000;
// Create a circular polygon
let circle = turf.circle(feature.geometry.coordinates, radiusInKm);
// Optional: transfer other properties to the new feature
circle.properties = {...feature.properties};
return circle;
}
return feature;
});
// Create a new GeoJSON FeatureCollection
let convertedGeoJSON = {
"type": "FeatureCollection",
"features": convertedFeatures
};
console.log(convertedGeoJSON);
var gpx = togpx(convertedGeoJSON);
return gpx;
}
// JavaScript to handle the confirmation and capture data
document.getElementById('confirm-save-track').addEventListener('click', function () {
var trackName = document.getElementById('track-name').value;
var trackComment = document.getElementById('track-comment').value;
var sarID = document.getElementById('sarIdForGPX').value;
// Get the track data
var trackData = getTrackData();
// Send trackName, trackComment, and trackData to the server
$.ajax({
url: '/save_track',
method: 'POST',
data: {
track_data: trackData,
track_name: trackName,
track_comment: trackComment,
sar_call_id: sarID,
},
success: function (response) {
// Handle success response
alert(response.message);
},
error: function (error) {
// Handle error response
alert('An error occurred while saving the track.');
}
});
// Close the modal
$('#saveTrackModal').modal('hide');
});
$('#saveTrackModal').on('hidden.bs.modal', function (e) {
// Refresh the parent page (assuming you want to refresh the entire page)
location.reload(); // This will refresh the current page
});
</script>
{% endblock %}