First working prototype with map search and some dynamic UI
This commit is contained in:
parent
c380ec5777
commit
64d00c8293
@ -7,10 +7,11 @@
|
|||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="jdk" jdkName="Python 3.9 (SARBase)" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
<component name="PackageRequirementsSettings">
|
<component name="PackageRequirementsSettings">
|
||||||
|
<option name="removeUnused" value="true" />
|
||||||
<option name="modifyBaseFiles" value="true" />
|
<option name="modifyBaseFiles" value="true" />
|
||||||
</component>
|
</component>
|
||||||
<component name="TemplatesService">
|
<component name="TemplatesService">
|
||||||
|
@ -3,5 +3,5 @@
|
|||||||
<component name="Black">
|
<component name="Black">
|
||||||
<option name="sdkName" value="Python 3.11 (SARBase)" />
|
<option name="sdkName" value="Python 3.11 (SARBase)" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11 (SARBase)" project-jdk-type="Python SDK" />
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9 (SARBase)" project-jdk-type="Python SDK" />
|
||||||
</project>
|
</project>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
4
admin.py
4
admin.py
@ -1,10 +1,12 @@
|
|||||||
from flask_admin import Admin
|
from flask_admin import Admin
|
||||||
from flask_admin.contrib.sqla import ModelView
|
from flask_admin.contrib.sqla import ModelView
|
||||||
from app import app, db
|
from app import app, db
|
||||||
from models import User, SARCall, SARCategory, GPSTrack
|
from models import User, SARCall, SARCategory, GPSTrack, SARStatus, SARResult
|
||||||
|
|
||||||
admin = Admin(app, name='SAR Admin', template_mode='bootstrap3')
|
admin = Admin(app, name='SAR Admin', template_mode='bootstrap3')
|
||||||
admin.add_view(ModelView(User, db.session))
|
admin.add_view(ModelView(User, db.session))
|
||||||
admin.add_view(ModelView(SARCall, db.session))
|
admin.add_view(ModelView(SARCall, db.session))
|
||||||
admin.add_view(ModelView(SARCategory, db.session))
|
admin.add_view(ModelView(SARCategory, db.session))
|
||||||
|
admin.add_view(ModelView(SARStatus, db.session))
|
||||||
|
admin.add_view(ModelView(SARResult, db.session))
|
||||||
admin.add_view(ModelView(GPSTrack, db.session))
|
admin.add_view(ModelView(GPSTrack, db.session))
|
||||||
|
17
app.py
17
app.py
@ -2,11 +2,24 @@ from flask import Flask, redirect, url_for
|
|||||||
from flask_sqlalchemy import SQLAlchemy
|
from flask_sqlalchemy import SQLAlchemy
|
||||||
from flask_migrate import Migrate
|
from flask_migrate import Migrate
|
||||||
from flask_login import LoginManager
|
from flask_login import LoginManager
|
||||||
|
from sqlalchemy import MetaData
|
||||||
|
|
||||||
|
convention = {
|
||||||
|
"ix": 'ix_%(column_0_label)s',
|
||||||
|
"uq": "uq_%(table_name)s_%(column_0_name)s",
|
||||||
|
"ck": "ck_%(table_name)s_%(constraint_name)s",
|
||||||
|
"fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
|
||||||
|
"pk": "pk_%(table_name)s"
|
||||||
|
}
|
||||||
|
|
||||||
|
metadata = MetaData(naming_convention=convention)
|
||||||
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
app.config['SECRET_KEY'] = 'secret_key'
|
app.config['SECRET_KEY'] = 'secret_key'
|
||||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite3'
|
#app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite3'
|
||||||
db = SQLAlchemy(app)
|
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+mysqlconnector://sarbaseuser:password@localhost/sarbaseapp'
|
||||||
|
db = SQLAlchemy(app, metadata=metadata)
|
||||||
migrate = Migrate(app, db)
|
migrate = Migrate(app, db)
|
||||||
login_manager = LoginManager(app)
|
login_manager = LoginManager(app)
|
||||||
|
|
||||||
|
11
create_db.py
11
create_db.py
@ -1,11 +0,0 @@
|
|||||||
from flask import Flask, render_template, redirect, url_for, request, flash
|
|
||||||
from flask_sqlalchemy import SQLAlchemy
|
|
||||||
|
|
||||||
app = Flask(__name__)
|
|
||||||
app.config['SECRET_KEY'] = 'secret_key'
|
|
||||||
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///db.sqlite3'
|
|
||||||
db = SQLAlchemy(app)
|
|
||||||
from models import User, SARCall, SARCategory
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
db.create_all()
|
|
18
models.py
18
models.py
@ -14,18 +14,30 @@ class User(UserMixin, db.Model):
|
|||||||
class SARCall(db.Model):
|
class SARCall(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
start_date = db.Column(db.DateTime, nullable=False)
|
start_date = db.Column(db.DateTime, nullable=False)
|
||||||
finish_date = db.Column(db.DateTime, nullable=False)
|
finish_date = db.Column(db.DateTime, nullable=True)
|
||||||
category = db.Column(db.String(150), nullable=False)
|
category = db.Column(db.Integer, db.ForeignKey('sar_category.id'), nullable=False)
|
||||||
|
status = db.Column(db.Integer, db.ForeignKey('sar_status.id'), nullable=False)
|
||||||
|
result = db.Column(db.Integer, db.ForeignKey('sar_result.id'), nullable=True)
|
||||||
latitude = db.Column(db.Float, nullable=False)
|
latitude = db.Column(db.Float, nullable=False)
|
||||||
longitude = db.Column(db.Float, nullable=False)
|
longitude = db.Column(db.Float, nullable=False)
|
||||||
|
title = db.Column(db.String(150), nullable=False)
|
||||||
|
description = db.Column(db.Text, nullable=True)
|
||||||
|
description_hidden = db.Column(db.Text, nullable=True)
|
||||||
search_manager_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
search_manager_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
|
||||||
gpx_data = db.Column(db.Text, nullable=True) # This will store GPX data as a text
|
gpx_data = db.Column(db.Text, nullable=True) # This will store GPX data as a text
|
||||||
|
|
||||||
|
|
||||||
class SARCategory(db.Model):
|
class SARCategory(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True, unique=True, nullable=False)
|
||||||
name = db.Column(db.String(150), unique=True, nullable=False)
|
name = db.Column(db.String(150), unique=True, nullable=False)
|
||||||
|
|
||||||
|
class SARResult(db.Model):
|
||||||
|
id = db.Column(db.Integer, primary_key=True, unique=True, nullable=False)
|
||||||
|
name = db.Column(db.String(150), unique=True, nullable=False)
|
||||||
|
|
||||||
|
class SARStatus(db.Model):
|
||||||
|
id = db.Column(db.Integer, primary_key=True, unique=True, nullable=False)
|
||||||
|
name = db.Column(db.String(150), unique=True, nullable=False)
|
||||||
|
|
||||||
class GPSTrack(db.Model):
|
class GPSTrack(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
alembic==1.12.1
|
alembic==1.12.1
|
||||||
Flask==3.0.0
|
Flask==3.0.0
|
||||||
Flask_Admin==1.6.1
|
Flask_Admin==1.6.1
|
||||||
Flask_Login==0.6.2
|
Flask_Login==0.6.3
|
||||||
Flask_Migrate==4.0.5
|
Flask_Migrate==4.0.5
|
||||||
flask_sqlalchemy==3.1.1
|
flask_sqlalchemy==3.1.1
|
||||||
python_dateutil==2.8.2
|
python_dateutil==2.8.2
|
||||||
SQLAlchemy==2.0.22
|
SQLAlchemy==2.0.22
|
||||||
Werkzeug==2.3.7
|
Werkzeug==3.0.1
|
||||||
|
mysql_connector_python==8.2.0
|
||||||
|
72
sar_calls.py
72
sar_calls.py
@ -2,71 +2,105 @@ from app import app, db
|
|||||||
from flask import request, redirect, flash, render_template, url_for
|
from flask import request, redirect, flash, render_template, url_for
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
from dateutil import parser
|
from dateutil import parser
|
||||||
from models import SARCall, GPSTrack
|
from models import SARCall, GPSTrack, SARCategory, SARStatus, User
|
||||||
|
|
||||||
|
|
||||||
@app.route('/create_sar', methods=['GET', 'POST'])
|
@app.route('/create_sar', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def create_sar():
|
def create_sar():
|
||||||
|
categories = SARCategory.query.all()
|
||||||
|
statuses = SARStatus.query.order_by('id').all()
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
start_date = parser.parse(request.form.get('start_date'))
|
start_date = parser.parse(request.form.get('start_date'))
|
||||||
finish_date = parser.parse(request.form.get('finish_date'))
|
|
||||||
category = request.form.get('category')
|
category = request.form.get('category')
|
||||||
latitude = request.form.get('latitude')
|
latitude = request.form.get('latitude')
|
||||||
longitude = request.form.get('longitude')
|
longitude = request.form.get('longitude')
|
||||||
gpx_data_list = request.form.getlist('gpx_data[]')
|
status = request.form.get('status')
|
||||||
gpx_color_list = request.form.getlist('gpx_color[]')
|
title = request.form.get('title')
|
||||||
|
# gpx_data_list = request.form.getlist('gpx_data[]')
|
||||||
|
# gpx_color_list = request.form.getlist('gpx_color[]')
|
||||||
|
|
||||||
for data, color in zip(gpx_data_list, gpx_color_list):
|
# for data, color in zip(gpx_data_list, gpx_color_list):
|
||||||
track = GPSTrack(data=data, color=color, sar_call=new_sar_call)
|
# track = GPSTrack(data=data, color=color, sar_call=new_sar_call)
|
||||||
db.session.add(track)
|
# db.session.add(track)
|
||||||
|
|
||||||
new_sar_call = SARCall(
|
new_sar_call = SARCall(
|
||||||
start_date=start_date,
|
start_date=start_date,
|
||||||
finish_date=finish_date,
|
|
||||||
category=category,
|
category=category,
|
||||||
latitude=latitude,
|
latitude=latitude,
|
||||||
longitude=longitude,
|
longitude=longitude,
|
||||||
search_manager_id=current_user.id,
|
search_manager_id=current_user.id,
|
||||||
gpx_data=gpx_data_list,
|
status=status,
|
||||||
gpx_color_list=gpx_color_list,
|
title=title,
|
||||||
|
description=request.form.get('description'),
|
||||||
|
description_hidden=request.form.get('description_hidden'),
|
||||||
|
# gpx_data=gpx_data_list,
|
||||||
|
# gpx_color_list=gpx_color_list,
|
||||||
)
|
)
|
||||||
db.session.add(new_sar_call)
|
db.session.add(new_sar_call)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash('SAR call created successfully!', 'success')
|
flash('SAR call created successfully!', 'success')
|
||||||
return redirect(url_for('dashboard'))
|
return redirect(url_for('dashboard'))
|
||||||
return render_template('create_sar.html')
|
|
||||||
|
return render_template('create_sar.html', categories=categories, statuses=statuses)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/list_sar')
|
@app.route('/list_sar')
|
||||||
@login_required
|
@login_required
|
||||||
def list_sar():
|
def list_sar():
|
||||||
sar_calls = SARCall.query.all()
|
sar_calls = SARCall.query.join(User, SARCall.search_manager_id == User.id).join(SARCategory, SARCall.category == SARCategory.id).add_columns(SARCategory, User, SARCall).all()
|
||||||
return render_template('list_sar.html', sar_calls=sar_calls)
|
return render_template('list_sar.html', sar_calls=sar_calls)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/edit_sar/<int:id>', methods=['GET', 'POST'])
|
@app.route('/edit_sar/<int:id>', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def edit_sar(id):
|
def edit_sar(id):
|
||||||
sar_call = SARCall.query.get(id)
|
sar_call = SARCall.query.get_or_404(id)
|
||||||
|
categories = SARCategory.query.all()
|
||||||
|
statuses = SARStatus.query.order_by('id').all()
|
||||||
|
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
sar_call.start_date = request.form.get('start_date')
|
sar_call.start_date = parser.parse(request.form.get('start_date'))
|
||||||
sar_call.finish_date = request.form.get('finish_date')
|
if request.form.get('finish_date'):
|
||||||
|
sar_call.finish_date = parser.parse(request.form.get('finish_date'))
|
||||||
sar_call.category = request.form.get('category')
|
sar_call.category = request.form.get('category')
|
||||||
sar_call.latitude = request.form.get('latitude')
|
sar_call.latitude = request.form.get('latitude')
|
||||||
sar_call.longitude = request.form.get('longitude')
|
sar_call.longitude = request.form.get('longitude')
|
||||||
sar_call.gpx_data = request.form.get('gpx_data')
|
sar_call.status = request.form.get('status')
|
||||||
|
sar_call.result = request.form.get('result')
|
||||||
|
sar_call.title = request.form.get('title')
|
||||||
|
sar_call.description = request.form.get('description')
|
||||||
|
sar_call.description_hidden = request.form.get('description_hidden')
|
||||||
|
# sar_call.gpx_data = request.form.get('gpx_data')
|
||||||
|
|
||||||
|
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash('SAR call updated successfully!', 'success')
|
flash('SAR call updated successfully!', 'success')
|
||||||
return redirect(url_for('list_sar'))
|
return redirect(url_for('list_sar'))
|
||||||
return render_template('edit_sar.html', sar_call=sar_call)
|
|
||||||
|
if sar_call.start_date:
|
||||||
|
sar_call.start_date = sar_call.start_date.strftime('%Y-%m-%d')
|
||||||
|
if sar_call.finish_date:
|
||||||
|
sar_call.finish_date = sar_call.finish_date.strftime('%Y-%m-%d')
|
||||||
|
|
||||||
|
return render_template('edit_sar.html', sar_call=sar_call,categories=categories, statuses=statuses)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@app.route('/sar_details/<int:id>')
|
||||||
|
def sar_details(id):
|
||||||
|
sar = SARCall.query.get_or_404(id) # Fetch the SARCall record or return 404
|
||||||
|
return render_template('sar_details.html', sar=sar)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@app.route('/delete_sar/<int:id>')
|
@app.route('/delete_sar/<int:id>')
|
||||||
@login_required
|
@login_required
|
||||||
def delete_sar(id):
|
def delete_sar(id):
|
||||||
sar_call = SARCall.query.get(id)
|
sar_call = SARCall.query.get_or_404(id)
|
||||||
db.session.delete(sar_call)
|
db.session.delete(sar_call)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash('SAR call deleted successfully!', 'success')
|
flash('SAR call record deleted successfully!', 'success')
|
||||||
return redirect(url_for('list_sar'))
|
return redirect(url_for('list_sar'))
|
||||||
|
|
||||||
|
@ -8,50 +8,73 @@
|
|||||||
|
|
||||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"/>
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"/>
|
||||||
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
|
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
|
||||||
|
<link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css"/>
|
||||||
|
<script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
|
||||||
|
|
||||||
|
|
||||||
<div class="container mt-5">
|
<div class="container mt-5">
|
||||||
<h2>Create SAR Call</h2>
|
<h2>Create SAR Call</h2>
|
||||||
|
|
||||||
|
|
||||||
<form action="{{ url_for('create_sar') }}" method="post" enctype="multipart/form-data">
|
<form action="{{ url_for('create_sar') }}" method="post" enctype="multipart/form-data">
|
||||||
<div class="form-group">
|
|
||||||
<label for="start_date">Start Date:</label>
|
|
||||||
<input type="date" name="start_date" class="form-control" required>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="finish_date">Finish Date:</label>
|
<label for="status">Status:</label>
|
||||||
<input type="date" name="finish_date" class="form-control" required>
|
<select name="status" id="status" class="form-control">
|
||||||
|
{% for status in statuses %}
|
||||||
|
<option value="{{ status.id }}">
|
||||||
|
{{ status.name }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="start_date">Start Date:</label>
|
||||||
|
<div>
|
||||||
|
<button type="button" id="today_button" class="btn btn-secondary">Today</button>
|
||||||
|
</div>
|
||||||
|
<input type="date" name="start_date" id="start_date" class="form-control" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="category">Category:</label>
|
<label for="category">Category:</label>
|
||||||
<select name="category" class="form-control">
|
<select name="category" id="category" class="form-control">
|
||||||
{% for cat in categories %}
|
{% for category in categories %}
|
||||||
<option value="{{ cat }}">{{ cat }}</option>
|
<option value="{{ category.id }}">{{ category.name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="coordinates">Geographical Coordinates:</label>
|
<label for="coordinates">Initial Planning Point (IPP) Coordinates:</label>
|
||||||
<input type="text" name="coordinates" class="form-control" placeholder="e.g. 40.7128, -74.0060"
|
<input type="text" name="longitude" class="form-control" placeholder="e.g. 40.7128"
|
||||||
|
required>
|
||||||
|
<input type="text" name="latitude" class="form-control" placeholder="e.g. -74.0060"
|
||||||
required>
|
required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="title">Title:</label>
|
||||||
|
<input type="text" name="title" class="form-control"
|
||||||
|
placeholder="e.g. M65, London center, missing since 12.12.2022" required>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="description">Description:</label>
|
<label for="description">Description:</label>
|
||||||
<textarea name="description" class="form-control"></textarea>
|
<textarea name="description" class="form-control"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="hidden_description">Hidden Description:</label>
|
<label for="description_hidden">Hidden Description:</label>
|
||||||
<textarea name="hidden_description" class="form-control"></textarea>
|
<textarea name="description_hidden" class="form-control"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- GPX Track Fields (You can expand upon this based on the previous discussions about multiple tracks) -->
|
{# <!-- GPX Track Fields (You can expand upon this based on the previous discussions about multiple tracks) -->#}
|
||||||
<div class="form-group">
|
{# <div class="form-group">#}
|
||||||
<label for="gpx_file">Upload GPX Track:</label>
|
{# <label for="gpx_file">Upload GPX Track:</label>#}
|
||||||
<input type="file" name="gpx_file">
|
{# <input type="file" name="gpx_file">#}
|
||||||
</div>
|
{# </div>#}
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary">Create</button>
|
<button type="submit" class="btn btn-primary">Create</button>
|
||||||
</form>
|
</form>
|
||||||
@ -66,6 +89,24 @@
|
|||||||
|
|
||||||
var marker;
|
var marker;
|
||||||
|
|
||||||
|
// Add the geocoder
|
||||||
|
L.Control.geocoder({
|
||||||
|
defaultMarkGeocode: true
|
||||||
|
})
|
||||||
|
.on('markgeocode', function (e) {
|
||||||
|
if (marker) {
|
||||||
|
map.removeLayer(marker);
|
||||||
|
}
|
||||||
|
var latlng = e.geocode.center;
|
||||||
|
L.marker(latlng).addTo(map);
|
||||||
|
map.setView(latlng, map.getZoom());
|
||||||
|
// Update your form fields with the selected location
|
||||||
|
document.querySelector('input[name="latitude"]').value = latlng.lat;
|
||||||
|
document.querySelector('input[name="longitude"]').value = latlng.lng;
|
||||||
|
})
|
||||||
|
.addTo(map);
|
||||||
|
|
||||||
|
|
||||||
map.on('click', function (e) {
|
map.on('click', function (e) {
|
||||||
if (marker) {
|
if (marker) {
|
||||||
map.removeLayer(marker);
|
map.removeLayer(marker);
|
||||||
@ -82,36 +123,48 @@
|
|||||||
{# map.fitBounds(e.target.getBounds());#}
|
{# map.fitBounds(e.target.getBounds());#}
|
||||||
{# });#}
|
{# });#}
|
||||||
{# map.addLayer(gpxLayer);#}
|
{# map.addLayer(gpxLayer);#}
|
||||||
{#});#}
|
{#})
|
||||||
|
;
|
||||||
|
#}
|
||||||
|
|
||||||
// If editing, set the marker to the existing coordinates
|
// If editing, set the marker to the existing coordinates
|
||||||
var latInput = document.querySelector('input[name="latitude"]');
|
var latInput = document.querySelector('input[name="latitude"]');
|
||||||
var lngInput = document.querySelector('input[name="longitude"]');
|
var lngInput = document.querySelector('input[name="longitude"]');
|
||||||
if (latInput.value && lngInput.value) {
|
if (latInput.value && lngInput.value) {
|
||||||
marker = L.marker([latInput.value, lngInput.value]).addTo(map);
|
marker = L.marker([latInput.value, lngInput.value]).addTo(map);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
{##}
|
||||||
|
{# <script>#}
|
||||||
|
{# function addTrack() {#}
|
||||||
|
{# let trackDiv = document.createElement('div');#}
|
||||||
|
{# trackDiv.className = 'gpx_track';#}
|
||||||
|
{# trackDiv.innerHTML = `#}
|
||||||
|
{# <label>GPX Data:</label>#}
|
||||||
|
{# <textarea name="gpx_data[]"></textarea>#}
|
||||||
|
{# <label>Color:</label>#}
|
||||||
|
{# <input type="color" name="gpx_color[]" value="#ff0000">#}
|
||||||
|
{# <button type="button" onclick="removeTrack(this)">Remove</button>#}
|
||||||
|
{# `;#}
|
||||||
|
{# document.getElementById('gpx_tracks').appendChild(trackDiv);#}
|
||||||
|
{# }#}
|
||||||
|
{##}
|
||||||
|
{# function removeTrack(button) {#}
|
||||||
|
{# let trackDiv = button.parentElement;#}
|
||||||
|
{# trackDiv.remove();#}
|
||||||
|
{# }#}
|
||||||
|
{# </script>#}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
function addTrack() {
|
document.getElementById('today_button').addEventListener('click', function () {
|
||||||
let trackDiv = document.createElement('div');
|
var today = new Date();
|
||||||
trackDiv.className = 'gpx_track';
|
var dd = String(today.getDate()).padStart(2, '0');
|
||||||
trackDiv.innerHTML = `
|
var mm = String(today.getMonth() + 1).padStart(2, '0'); // January is 0!
|
||||||
<label>GPX Data:</label>
|
var yyyy = today.getFullYear();
|
||||||
<textarea name="gpx_data[]"></textarea>
|
today = yyyy + '-' + mm + '-' + dd;
|
||||||
<label>Color:</label>
|
document.getElementById('start_date').value = today;
|
||||||
<input type="color" name="gpx_color[]" value="#ff0000">
|
});
|
||||||
<button type="button" onclick="removeTrack(this)">Remove</button>
|
|
||||||
`;
|
|
||||||
document.getElementById('gpx_tracks').appendChild(trackDiv);
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeTrack(button) {
|
|
||||||
let trackDiv = button.parentElement;
|
|
||||||
trackDiv.remove();
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -14,46 +14,66 @@
|
|||||||
<h2>Edit SAR Call</h2>
|
<h2>Edit SAR Call</h2>
|
||||||
<form action="{{ url_for('edit_sar', id=sar_call.id) }}" method="post" enctype="multipart/form-data">
|
<form action="{{ url_for('edit_sar', id=sar_call.id) }}" method="post" enctype="multipart/form-data">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
<div>
|
||||||
|
<label for="status">Status:</label>
|
||||||
|
<select name="status" id="status" class="form-control">
|
||||||
|
{% for status in statuses %}
|
||||||
|
<option value="{{ status.id }}" {% if status.id == sar_call.status %}selected{% endif %}>
|
||||||
|
{{ status.name }}
|
||||||
|
</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<label for="start_date">Start Date:</label>
|
<label for="start_date">Start Date:</label>
|
||||||
<input type="date" name="start_date" class="form-control" value="{{ sar_call.start_date }}" required>
|
<input type="date" name="start_date" class="form-control" value="{{ sar_call.start_date }}" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="finish_date">Finish Date:</label>
|
<label for="finish_date">Finish Date:</label>
|
||||||
<input type="date" name="finish_date" class="form-control" value="{{ sar_call.finish_date }}" required>
|
<div>
|
||||||
|
<button type="button" id="today_button" class="btn btn-secondary">Today</button>
|
||||||
|
</div>
|
||||||
|
<input type="date" name="finish_date" id="finish_date" class="form-control" value="{{ sar_call.finish_date }}" >
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="category">Category:</label>
|
<label for="category">Category:</label>
|
||||||
<select name="category" class="form-control">
|
<select name="category" id="category" class="form-control">
|
||||||
{% for cat in categories %}
|
{% for category in categories %}
|
||||||
<option value="{{ cat }}"
|
<option value="{{ category.id }}" {% if category.id == sar_call.category %}selected{% endif %}>
|
||||||
{% if cat == sar_call.category %}selected{% endif %}>{{ cat }}</option>
|
{{ category.name }}
|
||||||
|
</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="coordinates">Geographical Coordinates:</label>
|
<label for="coordinates">Geographical Coordinates:</label>
|
||||||
<input type="text" name="coordinates" class="form-control" value="{{ sar_call.longitude }}" required>
|
<input type="text" name="longitude" class="form-control" value="{{ sar_call.longitude }}" required>
|
||||||
<input type="text" name="coordinates" class="form-control" value="{{ sar_call.latitude }}" required>
|
<input type="text" name="latitude" class="form-control" value="{{ sar_call.latitude }}" required>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="title">Title:</label>
|
||||||
|
<input type="text" name="title" class="form-control" value="{{ sar_call.title }}" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="description">Description:</label>
|
<label for="description">Description:</label>
|
||||||
<textarea name="description" class="form-control">{{ sar_call.description }}</textarea>
|
<textarea name="description" class="form-control">{{ sar_call.description }}</textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="hidden_description">Hidden Description:</label>
|
<label for="description_hidden">Hidden Description:</label>
|
||||||
<textarea name="hidden_description" class="form-control">{{ sar_call.hidden_description }}</textarea>
|
<textarea name="description_hidden" class="form-control">{{ sar_call.description_hidden }}</textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- GPX Track Fields (You can expand upon this based on the previous discussion about multiple tracks) -->
|
{# <!-- GPX Track Fields (You can expand upon this based on the previous discussion about multiple tracks) -->#}
|
||||||
<div class="form-group">
|
{# <div class="form-group">#}
|
||||||
<label for="gpx_file">Upload GPX Track:</label>
|
{# <label for="gpx_file">Upload GPX Track:</label>#}
|
||||||
<input type="file" name="gpx_file">
|
{# <input type="file" name="gpx_file">#}
|
||||||
</div>
|
{# </div>#}
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary">Update</button>
|
<button type="submit" class="btn btn-primary">Update</button>
|
||||||
</form>
|
</form>
|
||||||
@ -83,6 +103,17 @@
|
|||||||
marker = L.marker([latInput.value, lngInput.value]).addTo(map);
|
marker = L.marker([latInput.value, lngInput.value]).addTo(map);
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
<script>
|
||||||
|
document.getElementById('today_button').addEventListener('click', function() {
|
||||||
|
var today = new Date();
|
||||||
|
var dd = String(today.getDate()).padStart(2, '0');
|
||||||
|
var mm = String(today.getMonth() + 1).padStart(2, '0'); // January is 0!
|
||||||
|
var yyyy = today.getFullYear();
|
||||||
|
today = yyyy + '-' + mm + '-' + dd;
|
||||||
|
document.getElementById('finish_date').value = today;
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<{% endblock %}
|
<{% endblock %}
|
||||||
|
@ -11,28 +11,32 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Id</th>
|
<th>Id</th>
|
||||||
|
<th>Title</th>
|
||||||
<th>Start Date</th>
|
<th>Start Date</th>
|
||||||
<th>Finish Date</th>
|
<th>Finish Date</th>
|
||||||
<th>Category</th>
|
<th>Category</th>
|
||||||
<th>Latitude</th>
|
<th>Latitude</th>
|
||||||
<th>Longitude</th>
|
<th>Longitude</th>
|
||||||
|
<th>Created by</th>
|
||||||
<th>Actions</th>
|
<th>Actions</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for sar in sar_calls %}
|
{% for sar in sar_calls %}
|
||||||
<tr>
|
<tr class="clickable-row" data-href="{{ url_for('sar_details', id=sar.SARCall.id) }}">
|
||||||
<td>{{ sar.id }}</td>
|
<td>{{ sar.SARCall.id }}</td>
|
||||||
<td>{{ sar.start_date }}</td>
|
<td>{{ sar.SARCall.title }}</td>
|
||||||
<td>{{ sar.finish_date }}</td>
|
<td>{{ sar.SARCall.start_date }}</td>
|
||||||
<td>{{ sar.category }}</td>
|
<td>{{ sar.SARCall.finish_date }}</td>
|
||||||
<td>{{ sar.latitude }}</td>
|
<td>{{ sar.SARCategory.name }}</td>
|
||||||
<td>{{ sar.longitude }}</td>
|
<td>{{ sar.SARCall.latitude }}</td>
|
||||||
|
<td>{{ sar.SARCall.longitude }}</td>
|
||||||
|
<td>{{ sar.User.full_name }}</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ url_for('edit_sar', id=sar.id) }}">
|
<a href="{{ url_for('edit_sar', id=sar.SARCall.id) }}">
|
||||||
<button type="button" class="btn btn-info">Edit</button>
|
<button type="button" class="btn btn-info">Edit</button>
|
||||||
</a> |
|
</a> |
|
||||||
<a href="{{ url_for('delete_sar', id=sar.id) }}">
|
<a href="{{ url_for('delete_sar', id=sar.SARCall.id) }}">
|
||||||
<button type="button" class="btn btn-danger">Delete</button>
|
<button type="button" class="btn btn-danger">Delete</button>
|
||||||
</a>
|
</a>
|
||||||
</td>
|
</td>
|
||||||
@ -42,4 +46,15 @@
|
|||||||
</table>
|
</table>
|
||||||
<a href="/dashboard">Back to Dashboard</a>
|
<a href="/dashboard">Back to Dashboard</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
document.querySelectorAll('.clickable-row').forEach(row => {
|
||||||
|
row.addEventListener('click', () => {
|
||||||
|
window.location.href = row.dataset.href;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
44
templates/sar_details.html
Normal file
44
templates/sar_details.html
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
{% 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" />
|
||||||
|
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
|
||||||
|
<style>
|
||||||
|
#map { height: 400px; }
|
||||||
|
.button { margin: 5px; }
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div class="container mt-4">
|
||||||
|
<h1 class="mb-4">SAR Job Details</h1>
|
||||||
|
<div class="card mb-4">
|
||||||
|
<div class="card-body">
|
||||||
|
<p><strong>ID:</strong> {{ sar.id }}</p>
|
||||||
|
<p><strong>Start Date:</strong> {{ sar.start_date }}</p>
|
||||||
|
<p><strong>Manager:</strong> {{ sar.manager }}</p>
|
||||||
|
<!-- Other SAR details -->
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<div id="map" class="mb-4"></div>
|
||||||
|
<div>
|
||||||
|
<a href="{{ url_for('edit_sar', id=sar.id) }}" class="btn btn-primary">Edit</a>
|
||||||
|
<a href="{{ url_for('delete_sar', id=sar.id) }}" class="btn btn-danger">Delete</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script>
|
||||||
|
var map = L.map('map').setView([{{ sar.latitude }}, {{ sar.longitude }}], 13);
|
||||||
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
|
maxZoom: 19,
|
||||||
|
attribution: '© OpenStreetMap contributors'
|
||||||
|
}).addTo(map);
|
||||||
|
L.marker([{{ sar.latitude }}, {{ sar.longitude }}]).addTo(map);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
{% endblock %}
|
Loading…
Reference in New Issue
Block a user