pre-bootstrap 2
This commit is contained in:
parent
45c4070034
commit
c380ec5777
@ -10,6 +10,9 @@
|
|||||||
<orderEntry type="inheritedJdk" />
|
<orderEntry type="inheritedJdk" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
|
<component name="PackageRequirementsSettings">
|
||||||
|
<option name="modifyBaseFiles" value="true" />
|
||||||
|
</component>
|
||||||
<component name="TemplatesService">
|
<component name="TemplatesService">
|
||||||
<option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
|
<option name="TEMPLATE_CONFIGURATION" value="Jinja2" />
|
||||||
<option name="TEMPLATE_FOLDERS">
|
<option name="TEMPLATE_FOLDERS">
|
||||||
|
3
admin.py
3
admin.py
@ -1,9 +1,10 @@
|
|||||||
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
|
from models import User, SARCall, SARCategory, GPSTrack
|
||||||
|
|
||||||
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(GPSTrack, db.session))
|
||||||
|
8
app.py
8
app.py
@ -1,14 +1,13 @@
|
|||||||
from flask import Flask
|
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
|
||||||
|
|
||||||
|
|
||||||
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)
|
db = SQLAlchemy(app)
|
||||||
migrate = Migrate(app,db)
|
migrate = Migrate(app, db)
|
||||||
login_manager = LoginManager(app)
|
login_manager = LoginManager(app)
|
||||||
|
|
||||||
import models
|
import models
|
||||||
@ -17,9 +16,10 @@ import login
|
|||||||
import sar_calls
|
import sar_calls
|
||||||
import dashboard
|
import dashboard
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def hello_world(): # put application's code here
|
def hello_world(): # put application's code here
|
||||||
return 'Hello World!'
|
return redirect(url_for('dashboard'))
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
@ -5,7 +5,7 @@ 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)
|
db = SQLAlchemy(app)
|
||||||
from models import User, SARCall, SARCategory
|
from models import User, SARCall, SARCategory
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
db.create_all()
|
db.create_all()
|
||||||
|
@ -1,8 +1,9 @@
|
|||||||
from app import app
|
from app import app
|
||||||
from flask import render_template
|
from flask import render_template
|
||||||
from flask_login import login_required, current_user
|
from flask_login import login_required, current_user
|
||||||
|
|
||||||
|
|
||||||
@app.route('/dashboard')
|
@app.route('/dashboard')
|
||||||
@login_required
|
@login_required
|
||||||
def dashboard():
|
def dashboard():
|
||||||
return render_template('dashboard.html', name=current_user.full_name)
|
return render_template('dashboard.html', name=current_user.full_name)
|
||||||
|
|
||||||
|
8
login.py
8
login.py
@ -2,10 +2,13 @@ from flask import Flask, render_template, redirect, url_for, request, flash
|
|||||||
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
|
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
|
||||||
from app import app, db, login_manager
|
from app import app, db, login_manager
|
||||||
from models import User
|
from models import User
|
||||||
|
|
||||||
|
|
||||||
@login_manager.user_loader
|
@login_manager.user_loader
|
||||||
def load_user(user_id):
|
def load_user(user_id):
|
||||||
return User.query.get(int(user_id))
|
return User.query.get(int(user_id))
|
||||||
|
|
||||||
|
|
||||||
@app.route('/login', methods=['GET', 'POST'])
|
@app.route('/login', methods=['GET', 'POST'])
|
||||||
def login():
|
def login():
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
@ -17,6 +20,7 @@ def login():
|
|||||||
return redirect(url_for('dashboard'))
|
return redirect(url_for('dashboard'))
|
||||||
return render_template('login.html')
|
return render_template('login.html')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/register', methods=['GET', 'POST'])
|
@app.route('/register', methods=['GET', 'POST'])
|
||||||
def register():
|
def register():
|
||||||
if request.method == 'POST':
|
if request.method == 'POST':
|
||||||
@ -26,12 +30,14 @@ def register():
|
|||||||
full_name = request.form.get('full_name')
|
full_name = request.form.get('full_name')
|
||||||
phone_number = request.form.get('phone_number')
|
phone_number = request.form.get('phone_number')
|
||||||
|
|
||||||
new_user = User(username=username, password=password, email=email, full_name=full_name, phone_number=phone_number)
|
new_user = User(username=username, password=password, email=email, full_name=full_name,
|
||||||
|
phone_number=phone_number)
|
||||||
db.session.add(new_user)
|
db.session.add(new_user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
return redirect(url_for('login'))
|
return redirect(url_for('login'))
|
||||||
return render_template('register.html')
|
return render_template('register.html')
|
||||||
|
|
||||||
|
|
||||||
@app.route('/logout')
|
@app.route('/logout')
|
||||||
@login_required
|
@login_required
|
||||||
def logout():
|
def logout():
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
from flask_login import UserMixin
|
from flask_login import UserMixin
|
||||||
from app import db
|
from app import db
|
||||||
|
|
||||||
|
|
||||||
class User(UserMixin, db.Model):
|
class User(UserMixin, db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
username = db.Column(db.String(150), unique=True, nullable=False)
|
username = db.Column(db.String(150), unique=True, nullable=False)
|
||||||
@ -24,3 +25,11 @@ class SARCall(db.Model):
|
|||||||
class SARCategory(db.Model):
|
class SARCategory(db.Model):
|
||||||
id = db.Column(db.Integer, primary_key=True)
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
name = db.Column(db.String(150), unique=True, nullable=False)
|
name = db.Column(db.String(150), unique=True, nullable=False)
|
||||||
|
|
||||||
|
|
||||||
|
class GPSTrack(db.Model):
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
data = db.Column(db.Text, nullable=False)
|
||||||
|
color = db.Column(db.String(7)) # Stores the color as a HEX code like #FF5733
|
||||||
|
sar_call_id = db.Column(db.Integer, db.ForeignKey('sar_call.id'), nullable=False)
|
||||||
|
sar_call = db.relationship('SARCall', backref=db.backref('gps_tracks', lazy=True))
|
||||||
|
9
requirements.txt
Normal file
9
requirements.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
alembic==1.12.1
|
||||||
|
Flask==3.0.0
|
||||||
|
Flask_Admin==1.6.1
|
||||||
|
Flask_Login==0.6.2
|
||||||
|
Flask_Migrate==4.0.5
|
||||||
|
flask_sqlalchemy==3.1.1
|
||||||
|
python_dateutil==2.8.2
|
||||||
|
SQLAlchemy==2.0.22
|
||||||
|
Werkzeug==2.3.7
|
16
sar_calls.py
16
sar_calls.py
@ -2,7 +2,9 @@ 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
|
from models import SARCall, GPSTrack
|
||||||
|
|
||||||
|
|
||||||
@app.route('/create_sar', methods=['GET', 'POST'])
|
@app.route('/create_sar', methods=['GET', 'POST'])
|
||||||
@login_required
|
@login_required
|
||||||
def create_sar():
|
def create_sar():
|
||||||
@ -12,7 +14,12 @@ def create_sar():
|
|||||||
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 = request.form.get('gpx_data')
|
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):
|
||||||
|
track = GPSTrack(data=data, color=color, sar_call=new_sar_call)
|
||||||
|
db.session.add(track)
|
||||||
|
|
||||||
new_sar_call = SARCall(
|
new_sar_call = SARCall(
|
||||||
start_date=start_date,
|
start_date=start_date,
|
||||||
@ -21,7 +28,8 @@ def create_sar():
|
|||||||
latitude=latitude,
|
latitude=latitude,
|
||||||
longitude=longitude,
|
longitude=longitude,
|
||||||
search_manager_id=current_user.id,
|
search_manager_id=current_user.id,
|
||||||
gpx_data=gpx_data
|
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()
|
||||||
@ -53,6 +61,7 @@ def edit_sar(id):
|
|||||||
return redirect(url_for('list_sar'))
|
return redirect(url_for('list_sar'))
|
||||||
return render_template('edit_sar.html', sar_call=sar_call)
|
return render_template('edit_sar.html', sar_call=sar_call)
|
||||||
|
|
||||||
|
|
||||||
@app.route('/delete_sar/<int:id>')
|
@app.route('/delete_sar/<int:id>')
|
||||||
@login_required
|
@login_required
|
||||||
def delete_sar(id):
|
def delete_sar(id):
|
||||||
@ -61,4 +70,3 @@ def delete_sar(id):
|
|||||||
db.session.commit()
|
db.session.commit()
|
||||||
flash('SAR call deleted successfully!', 'success')
|
flash('SAR call deleted successfully!', 'success')
|
||||||
return redirect(url_for('list_sar'))
|
return redirect(url_for('list_sar'))
|
||||||
|
|
||||||
|
75
templates/base.html
Normal file
75
templates/base.html
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<!-- ... meta tags, styles, etc. ... -->
|
||||||
|
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<title>{% block title %}Default Title{% endblock %}</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<!-- Navbar/Header -->
|
||||||
|
<nav class="navbar navbar-expand-lg navbar-light bg-light">
|
||||||
|
<a class="navbar-brand" href="#">SAR App</a>
|
||||||
|
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav"
|
||||||
|
aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
|
||||||
|
<span class="navbar-toggler-icon"></span>
|
||||||
|
</button>
|
||||||
|
<div class="collapse navbar-collapse" id="navbarNav">
|
||||||
|
<ul class="navbar-nav mr-auto">
|
||||||
|
<!-- Nav items go here -->
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/list_sar">SAR records</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="/create_sar">New record</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<!-- Display user info and logout link if user is authenticated -->
|
||||||
|
<ul class="navbar-nav ml-auto">
|
||||||
|
{% if current_user.is_authenticated %}
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="#">Logged in as: {{ current_user.username }}</a>
|
||||||
|
</li>
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ url_for('logout') }}">Logout</a>
|
||||||
|
</li>
|
||||||
|
{% else %}
|
||||||
|
<!-- Display login link if user is not authenticated -->
|
||||||
|
<li class="nav-item">
|
||||||
|
<a class="nav-link" href="{{ url_for('login') }}">Login</a>
|
||||||
|
</li>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- Main Content -->
|
||||||
|
<div class="container">
|
||||||
|
|
||||||
|
{% with messages = get_flashed_messages(with_categories=true) %}
|
||||||
|
{% if messages %}
|
||||||
|
{% for category, message in messages %}
|
||||||
|
<div class="alert alert-{{ category }}">{{ message }}</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<!-- Default content can be placed here. It will be overridden by child templates. -->
|
||||||
|
{% endblock %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Footer -->
|
||||||
|
<footer>
|
||||||
|
<!-- ... footer content ... -->
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
<!-- Scripts -->
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.16.0/umd/popper.min.js"></script>
|
||||||
|
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.5.2/js/bootstrap.min.js"></script>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
@ -1,39 +1,60 @@
|
|||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html>
|
|
||||||
<head>
|
{% block title %}
|
||||||
|
Create SAR Record
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
<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>
|
||||||
<title>Create SAR Call</title>
|
|
||||||
</head>
|
<div class="container mt-5">
|
||||||
<body>
|
<h2>Create SAR Call</h2>
|
||||||
<h2>Create SAR Call</h2>
|
<form action="{{ url_for('create_sar') }}" method="post" enctype="multipart/form-data">
|
||||||
<form action="/create_sar" method="post">
|
<div class="form-group">
|
||||||
<div>
|
<label for="start_date">Start Date:</label>
|
||||||
<label for="start_date">Start Date:</label>
|
<input type="date" name="start_date" class="form-control" required>
|
||||||
<input type="date" name="start_date" required>
|
</div>
|
||||||
</div>
|
|
||||||
<div>
|
<div class="form-group">
|
||||||
<label for="finish_date">Finish Date:</label>
|
<label for="finish_date">Finish Date:</label>
|
||||||
<input type="date" name="finish_date" required>
|
<input type="date" name="finish_date" class="form-control" required>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
|
||||||
<label for="category">Category:</label>
|
<div class="form-group">
|
||||||
<input type="text" name="category" required>
|
<label for="category">Category:</label>
|
||||||
</div>
|
<select name="category" class="form-control">
|
||||||
<div>
|
{% for cat in categories %}
|
||||||
<label for="latitude">Latitude:</label>
|
<option value="{{ cat }}">{{ cat }}</option>
|
||||||
<input type="text" name="latitude" required>
|
{% endfor %}
|
||||||
</div>
|
</select>
|
||||||
<div>
|
</div>
|
||||||
<label for="longitude">Longitude:</label>
|
|
||||||
<input type="text" name="longitude" required>
|
<div class="form-group">
|
||||||
</div>
|
<label for="coordinates">Geographical Coordinates:</label>
|
||||||
<div>
|
<input type="text" name="coordinates" class="form-control" placeholder="e.g. 40.7128, -74.0060"
|
||||||
<label for="gpx_data">GPX Data:</label>
|
required>
|
||||||
<textarea name="gpx_data"></textarea>
|
</div>
|
||||||
</div>
|
|
||||||
<div>
|
<div class="form-group">
|
||||||
<input type="submit" value="Create">
|
<label for="description">Description:</label>
|
||||||
|
<textarea name="description" class="form-control"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="hidden_description">Hidden Description:</label>
|
||||||
|
<textarea name="hidden_description" class="form-control"></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- GPX Track Fields (You can expand upon this based on the previous discussions about multiple tracks) -->
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="gpx_file">Upload GPX Track:</label>
|
||||||
|
<input type="file" name="gpx_file">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary">Create</button>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
<div id="map" style="width: 600px; height: 400px;"></div>
|
<div id="map" style="width: 600px; height: 400px;"></div>
|
||||||
<script>
|
<script>
|
||||||
@ -54,6 +75,15 @@
|
|||||||
document.querySelector('input[name="longitude"]').value = e.latlng.lng;
|
document.querySelector('input[name="longitude"]').value = e.latlng.lng;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
{#var gpxTracks = {{ gpx_data | tojson }};#}
|
||||||
|
{#gpxTracks.forEach(function (track) {#}
|
||||||
|
{# var gpxLayer = new L.GPX(track.data, {async: true, polyline_options: {color: track.color}});#}
|
||||||
|
{# gpxLayer.on('loaded', function (e) {#}
|
||||||
|
{# map.fitBounds(e.target.getBounds());#}
|
||||||
|
{# });#}
|
||||||
|
{# 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"]');
|
||||||
@ -62,9 +92,26 @@
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</form>
|
<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>
|
||||||
|
|
||||||
|
|
||||||
<a href="/dashboard">Back to Dashboard</a>
|
|
||||||
</body>
|
{% endblock %}
|
||||||
</html>
|
|
@ -1,11 +1,12 @@
|
|||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html>
|
|
||||||
<head>
|
{% block title %}
|
||||||
<title>Dashboard</title>
|
Dashboard
|
||||||
</head>
|
{% endblock %}
|
||||||
<body>
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
<h2>Welcome, {{ name }}!</h2>
|
<h2>Welcome, {{ name }}!</h2>
|
||||||
<p>This is the dashboard. More features will be added soon!</p>
|
<p>This is the dashboard. More features will be added soon!</p>
|
||||||
<a href="/logout">Logout</a>
|
|
||||||
</body>
|
{% endblock %}
|
||||||
</html>
|
|
||||||
|
@ -1,39 +1,88 @@
|
|||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html lang="en">
|
|
||||||
<head>
|
{% block title %}
|
||||||
<meta charset="UTF-8">
|
Edit SAR record
|
||||||
<title>Title</title>
|
{% endblock %}
|
||||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
|
|
||||||
|
{% 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>
|
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
|
|
||||||
<div id="map" style="width: 600px; height: 400px;"></div>
|
<h2>Edit SAR Call</h2>
|
||||||
<script>
|
<div class="container mt-5">
|
||||||
var map = L.map('map').setView([51.505, -0.09], 13); // Default to London, adjust as needed
|
<h2>Edit SAR Call</h2>
|
||||||
|
<form action="{{ url_for('edit_sar', id=sar_call.id) }}" 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" value="{{ sar_call.start_date }}" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
<div class="form-group">
|
||||||
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
<label for="finish_date">Finish Date:</label>
|
||||||
}).addTo(map);
|
<input type="date" name="finish_date" class="form-control" value="{{ sar_call.finish_date }}" required>
|
||||||
|
</div>
|
||||||
|
|
||||||
var marker;
|
<div class="form-group">
|
||||||
|
<label for="category">Category:</label>
|
||||||
|
<select name="category" class="form-control">
|
||||||
|
{% for cat in categories %}
|
||||||
|
<option value="{{ cat }}"
|
||||||
|
{% if cat == sar_call.category %}selected{% endif %}>{{ cat }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
map.on('click', function(e) {
|
<div class="form-group">
|
||||||
if (marker) {
|
<label for="coordinates">Geographical Coordinates:</label>
|
||||||
map.removeLayer(marker);
|
<input type="text" name="coordinates" class="form-control" value="{{ sar_call.longitude }}" required>
|
||||||
}
|
<input type="text" name="coordinates" class="form-control" value="{{ sar_call.latitude }}" required>
|
||||||
marker = L.marker(e.latlng).addTo(map);
|
</div>
|
||||||
document.querySelector('input[name="latitude"]').value = e.latlng.lat;
|
|
||||||
document.querySelector('input[name="longitude"]').value = e.latlng.lng;
|
|
||||||
});
|
|
||||||
|
|
||||||
// If editing, set the marker to the existing coordinates
|
<div class="form-group">
|
||||||
var latInput = document.querySelector('input[name="latitude"]');
|
<label for="description">Description:</label>
|
||||||
var lngInput = document.querySelector('input[name="longitude"]');
|
<textarea name="description" class="form-control">{{ sar_call.description }}</textarea>
|
||||||
if (latInput.value && lngInput.value) {
|
</div>
|
||||||
marker = L.marker([latInput.value, lngInput.value]).addTo(map);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
</body>
|
<div class="form-group">
|
||||||
</html>
|
<label for="hidden_description">Hidden Description:</label>
|
||||||
|
<textarea name="hidden_description" class="form-control">{{ sar_call.hidden_description }}</textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- GPX Track Fields (You can expand upon this based on the previous discussion about multiple tracks) -->
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="gpx_file">Upload GPX Track:</label>
|
||||||
|
<input type="file" name="gpx_file">
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary">Update</button>
|
||||||
|
</form>
|
||||||
|
<div id="map" style="width: 600px; height: 400px;"></div>
|
||||||
|
<script>
|
||||||
|
var map = L.map('map').setView([51.505, -0.09], 13); // Default to London, adjust as needed
|
||||||
|
|
||||||
|
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
||||||
|
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
var marker;
|
||||||
|
|
||||||
|
map.on('click', function (e) {
|
||||||
|
if (marker) {
|
||||||
|
map.removeLayer(marker);
|
||||||
|
}
|
||||||
|
marker = L.marker(e.latlng).addTo(map);
|
||||||
|
document.querySelector('input[name="latitude"]').value = e.latlng.lat;
|
||||||
|
document.querySelector('input[name="longitude"]').value = e.latlng.lng;
|
||||||
|
});
|
||||||
|
|
||||||
|
// If editing, set the marker to the existing coordinates
|
||||||
|
var latInput = document.querySelector('input[name="latitude"]');
|
||||||
|
var lngInput = document.querySelector('input[name="longitude"]');
|
||||||
|
if (latInput.value && lngInput.value) {
|
||||||
|
marker = L.marker([latInput.value, lngInput.value]).addTo(map);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<{% endblock %}
|
||||||
|
@ -1,13 +1,16 @@
|
|||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html>
|
|
||||||
<head>
|
{% block title %}
|
||||||
<title>List of SAR Calls</title>
|
SAR Records
|
||||||
</head>
|
{% endblock %}
|
||||||
<body>
|
|
||||||
<h2>List of SAR Calls</h2>
|
{% block content %}
|
||||||
<table>
|
<div class="container mt-5">
|
||||||
<thead>
|
<h2>SAR Records</h2>
|
||||||
|
<table class="table table-bordered table-hover">
|
||||||
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
|
<th>Id</th>
|
||||||
<th>Start Date</th>
|
<th>Start Date</th>
|
||||||
<th>Finish Date</th>
|
<th>Finish Date</th>
|
||||||
<th>Category</th>
|
<th>Category</th>
|
||||||
@ -15,23 +18,28 @@
|
|||||||
<th>Longitude</th>
|
<th>Longitude</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>
|
||||||
<td>{{ sar.start_date }}</td>
|
<td>{{ sar.id }}</td>
|
||||||
<td>{{ sar.finish_date }}</td>
|
<td>{{ sar.start_date }}</td>
|
||||||
<td>{{ sar.category }}</td>
|
<td>{{ sar.finish_date }}</td>
|
||||||
<td>{{ sar.latitude }}</td>
|
<td>{{ sar.category }}</td>
|
||||||
<td>{{ sar.longitude }}</td>
|
<td>{{ sar.latitude }}</td>
|
||||||
<td>
|
<td>{{ sar.longitude }}</td>
|
||||||
<a href="{{ url_for('edit_sar', id=sar.id) }}">Edit</a> |
|
<td>
|
||||||
<a href="{{ url_for('delete_sar', id=sar.id) }}">Delete</a>
|
<a href="{{ url_for('edit_sar', id=sar.id) }}">
|
||||||
</td>
|
<button type="button" class="btn btn-info">Edit</button>
|
||||||
</tr>
|
</a> |
|
||||||
|
<a href="{{ url_for('delete_sar', id=sar.id) }}">
|
||||||
|
<button type="button" class="btn btn-danger">Delete</button>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
<a href="/dashboard">Back to Dashboard</a>
|
<a href="/dashboard">Back to Dashboard</a>
|
||||||
</body>
|
</div>
|
||||||
</html>
|
{% endblock %}
|
@ -1,9 +1,11 @@
|
|||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html>
|
|
||||||
<head>
|
{% block title %}
|
||||||
<title>Login</title>
|
Login
|
||||||
</head>
|
{% endblock %}
|
||||||
<body>
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
<h2>Login</h2>
|
<h2>Login</h2>
|
||||||
<form action="/login" method="post">
|
<form action="/login" method="post">
|
||||||
<div>
|
<div>
|
||||||
@ -21,5 +23,5 @@
|
|||||||
<div>
|
<div>
|
||||||
Don't have an account? <a href="/register">Register</a>
|
Don't have an account? <a href="/register">Register</a>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
|
||||||
</html>
|
{% endblock %}
|
||||||
|
@ -1,37 +1,41 @@
|
|||||||
<!DOCTYPE html>
|
{% extends "base.html" %}
|
||||||
<html>
|
|
||||||
<head>
|
{% block title %}
|
||||||
<title>Register</title>
|
Register
|
||||||
</head>
|
{% endblock %}
|
||||||
<body>
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
<h2>Register</h2>
|
<h2>Register</h2>
|
||||||
<form action="/register" method="post">
|
<div class="form-group">
|
||||||
<div>
|
<form action="/register" method="post">
|
||||||
<label for="username">Username:</label>
|
<div>
|
||||||
<input type="text" name="username" required>
|
<label for="username">Username:</label>
|
||||||
</div>
|
<input type="text" name="username" class="form-control" required>
|
||||||
<div>
|
</div>
|
||||||
<label for="password">Password:</label>
|
<div>
|
||||||
<input type="password" name="password" required>
|
<label for="password">Password:</label>
|
||||||
</div>
|
<input type="password" name="password" class="form-control" required>
|
||||||
<div>
|
</div>
|
||||||
<label for="email">Email:</label>
|
<div>
|
||||||
<input type="email" name="email" required>
|
<label for="email">Email:</label>
|
||||||
</div>
|
<input type="email" name="email" class="form-control" required>
|
||||||
<div>
|
</div>
|
||||||
<label for="full_name">Full Name:</label>
|
<div>
|
||||||
<input type="text" name="full_name" required>
|
<label for="full_name">Full Name:</label>
|
||||||
</div>
|
<input type="text" name="full_name" class="form-control" required>
|
||||||
<div>
|
</div>
|
||||||
<label for="phone_number">Phone Number:</label>
|
<div>
|
||||||
<input type="tel" name="phone_number">
|
<label for="phone_number">Phone Number:</label>
|
||||||
</div>
|
<input type="tel" name="phone_number" class="form-control">
|
||||||
<div>
|
</div>
|
||||||
<input type="submit" value="Register">
|
<div>
|
||||||
</div>
|
<input type="submit" value="Register" class="btn btn-primary">
|
||||||
</form>
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
Already have an account? <a href="/login">Login</a>
|
Already have an account? <a href="/login">Login</a>
|
||||||
</div>
|
</div>
|
||||||
</body>
|
|
||||||
</html>
|
{% endblock %}
|
||||||
|
Loading…
Reference in New Issue
Block a user