forked from orson/bachemap
fixed crosshair and part of the dashboard displays
This commit is contained in:
parent
c99a897d92
commit
ee49adc084
6
app.py
6
app.py
@ -275,7 +275,8 @@ def create_app(config=Config):
|
||||
qr_update = mongo.db.users.update_one({'_id': ObjectId(current_user.id)}, {'$set': {'referral_code': invite_code}})
|
||||
print(invite_code)
|
||||
if not current_user.is_admin:
|
||||
pins = list(mongo.db.pins.find({"added_by": current_user.id}))
|
||||
pins = list(mongo.db.pins.find({"added_by": current_user.id}).sort("time", -1))
|
||||
print(pins)
|
||||
return render_template('dashboard.html', pins=pins, invite_code=invite_code)
|
||||
if current_user.is_admin:
|
||||
users = list(mongo.db.users.find())
|
||||
@ -362,7 +363,7 @@ def create_app(config=Config):
|
||||
if len(username) >= 2:
|
||||
cleaned_username = username[0] + "***" + username[-1]
|
||||
else:
|
||||
cleaned_username = username
|
||||
cleaned_username = username+"***"+random.choice(string.ascii_letters, k=1)
|
||||
cleaned_leaders.append({"username": cleaned_username, "count": count})
|
||||
except Exception as e:
|
||||
print(f"Error processing user_id {user_id}: {e}")
|
||||
@ -385,6 +386,7 @@ def create_app(config=Config):
|
||||
|
||||
# Calculate percentage (handle case where total_users might be 0)
|
||||
active_percentage = round((active_users / total_users) * 100, 1) if total_users > 0 else 0
|
||||
|
||||
return render_template('leaderboard.html', leaders=cleaned_leaders, percentage = active_percentage)
|
||||
|
||||
|
||||
|
||||
@ -398,7 +398,9 @@ section#pinner-modal::-webkit-scrollbar-thumb {
|
||||
/* Animation removed as it was causing visibility issues */
|
||||
}
|
||||
|
||||
|
||||
#dash-container {
|
||||
max-width: 75vw;
|
||||
}
|
||||
|
||||
/* Notifications */
|
||||
.flashes {
|
||||
@ -422,6 +424,13 @@ ul.flashes li {
|
||||
font-size: 1.2rem;
|
||||
color: black;
|
||||
}
|
||||
|
||||
/*/html/body/main/div[1]/div[1]/div[2]/svg/g/path */
|
||||
|
||||
path {
|
||||
cursor: crosshair !important;
|
||||
}
|
||||
|
||||
/* Animations */
|
||||
@keyframes fadeOut {
|
||||
0% { opacity: 1; transform: translateY(0); }
|
||||
@ -474,7 +483,9 @@ ul.flashes li {
|
||||
nav ul {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
#dash-container {
|
||||
max-width: 95vw;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 812px) {
|
||||
|
||||
@ -28,7 +28,14 @@
|
||||
|
||||
</form>
|
||||
<script>
|
||||
submitButton = document.getElementById('submit');
|
||||
if (submitButton) {
|
||||
submitButton.addEventListener('click', function() {
|
||||
// Disable the button after submission
|
||||
submitButton.disabled = true;
|
||||
});
|
||||
}
|
||||
document.getElementById('submit').onclick=function(e){
|
||||
document.getElementById('progress').hidden=''
|
||||
document.getElementById('progress').hidden='false'
|
||||
}
|
||||
</script>
|
||||
|
||||
@ -17,14 +17,15 @@
|
||||
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
|
||||
crossorigin=""></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
|
||||
<link rel="icon" href="{{ url_for('static', filename='images/favico.ico') }}" type="image/x-icon">
|
||||
<link rel="shortcut icon" href="{{ url_for('static', filename='images/favico.ico') }}" type="image/x-icon">
|
||||
<!-- <script src="https://app.unpkg.com/leaflet.markercluster@1.4.1/files/dist/leaflet.markercluster.js" type="text/javascript"></script> -->
|
||||
<script type="application/importmap" src="https://app.unpkg.com/leaflet.markercluster@1.4.1/files/dist/leaflet.markercluster-src.js.map"></script>
|
||||
<link href="https://app.unpkg.com/leaflet.markercluster@1.4.1/files/dist/MarkerCluster.css">
|
||||
<link href="https://app.unpkg.com/leaflet.markercluster@1.4.1/files/dist/MarkerCluster.Default.css">
|
||||
<link rel="icon" href="{{ url_for('static', filename='images/favico.ico') }}" type="image/x-icon">
|
||||
<link rel="shortcut icon" href="{{ url_for('static', filename='images/favico.ico') }}" type="image/x-icon">
|
||||
<!-- <script src="https://app.unpkg.com/leaflet.markercluster@1.4.1/files/dist/leaflet.markercluster.js" type="text/javascript"></script> -->
|
||||
<script type="application/importmap" src="https://app.unpkg.com/leaflet.markercluster@1.4.1/files/dist/leaflet.markercluster-src.js.map"></script>
|
||||
<link href="https://app.unpkg.com/leaflet.markercluster@1.4.1/files/dist/MarkerCluster.css">
|
||||
<link href="https://app.unpkg.com/leaflet.markercluster@1.4.1/files/dist/MarkerCluster.Default.css">
|
||||
<script src="{{ url_for('static', filename='leaflet.markercluster.js')}}"></script>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='styles.css') }}">
|
||||
|
||||
<!-- <script src="{{ url_for('static', filename='src/MarkerClusterGroup.js')}}"></script> -->
|
||||
|
||||
<!-- <script type="importmap" src="{{ url_for('static', filename='leaflet.markercluster.js.map')}}"></script>
|
||||
@ -106,7 +107,7 @@
|
||||
</section>
|
||||
{% else %}
|
||||
<div class="flashes">
|
||||
<h3>Seguramente quieres hacer <a href="{{ url_for('thelogin') }}">login</a></h3>
|
||||
<h3>Seguramente quieres <a href="{{ url_for('thelogin') }}">iniciar sesión</a></h3>
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
@ -132,7 +133,9 @@
|
||||
pinner_button_top.addEventListener('click', toggleSlide);
|
||||
}
|
||||
const cancel_add = document.getElementById("cancel-add");
|
||||
cancel_add.addEventListener('click', toggleSlide);
|
||||
if (cancel_add) {
|
||||
cancel_add.addEventListener('click', toggleSlide);
|
||||
}
|
||||
observer.observe(document.body, { childList: true, subtree: true });
|
||||
</script>
|
||||
<script>
|
||||
|
||||
@ -28,7 +28,6 @@
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
#file-fallback {
|
||||
display: none;
|
||||
position: fixed;
|
||||
@ -41,6 +40,16 @@
|
||||
border-radius: 10px;
|
||||
color: black;
|
||||
}
|
||||
#location-status {
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
color: white;
|
||||
padding: 5px 10px;
|
||||
border-radius: 5px;
|
||||
z-index: 100;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
@ -50,21 +59,21 @@
|
||||
<canvas id="canvas"></canvas>
|
||||
</div>
|
||||
|
||||
<div id="location-status">Esperando ubicación...</div>
|
||||
|
||||
<div style="position: absolute; bottom: 0; width: 100%; display: flex; flex-direction: column; padding: 10px; box-sizing: border-box;">
|
||||
<button id="capture-btn" class="control-button" style="margin: 10px; right:20%"><i class="fa fas fa-camera"></i></button>
|
||||
<div style="display: flex; justify-content: space-between; width: 100%; box-sizing: border-box;">
|
||||
<button id="retake-btn" class="control-button" style="display: none; margin: 10px 5px 10px 10px; flex: 1; left:20%; background-color: rgba(231, 9, 9, 0.671);color:white; left:20%"><i class="fas fa-redo"></i></button>
|
||||
<button id="send-btn" class="control-button" style="display: none; margin: 10px 10px 10px 5px; flex: 1; background-color: rgba(49, 182, 28, 0.6);color:white; right: 20%;"><i class="fas fa-paper-plane"></i></button>
|
||||
<button id="send-btn" class="control-button" style="display: none; margin: 10px 10px 10px 5px; flex: 1; background-color: rgba(49, 182, 28, 0.6);color:white; right: 20%;" disabled><i class="fas fa-paper-plane"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<form id="camera-form" action="{{ url_for('camera') }}" method="POST" enctype="multipart/form-data" style="display:none;">
|
||||
<input type="hidden" name="latitude" id="latitude" value="">
|
||||
<input type="hidden" name="longitude" id="longitude" value="">
|
||||
<input type="hidden" name="has_image" id="has_image" value="false">
|
||||
|
||||
<!-- Fallback file input for unsupported browsers -->
|
||||
<div id="file-fallback">
|
||||
<label for="photo">Toma o selecciona una foto:</label>
|
||||
<input type="file" name="photo" id="photo" accept="image/*" capture="environment" required>
|
||||
@ -82,6 +91,11 @@
|
||||
const form = document.getElementById('camera-form');
|
||||
const fallbackInput = document.getElementById('file-fallback');
|
||||
const hasImageInput = document.getElementById('has_image');
|
||||
const locationStatus = document.getElementById('location-status');
|
||||
|
||||
// Location tracking variables
|
||||
let hasLocation = false;
|
||||
let watchId = null;
|
||||
|
||||
// Set up camera stream
|
||||
async function setupCamera() {
|
||||
@ -109,6 +123,17 @@
|
||||
fallbackInput.style.display = 'block';
|
||||
}
|
||||
|
||||
// Check if we can enable the send button
|
||||
function updateSendButtonState() {
|
||||
if (hasImageInput.value === 'true' && hasLocation) {
|
||||
sendBtn.disabled = false;
|
||||
sendBtn.style.opacity = '1';
|
||||
} else {
|
||||
sendBtn.disabled = true;
|
||||
sendBtn.style.opacity = '0.5';
|
||||
}
|
||||
}
|
||||
|
||||
// Capture photo from video feed
|
||||
captureBtn.addEventListener('click', function() {
|
||||
const context = canvas.getContext('2d');
|
||||
@ -128,6 +153,9 @@
|
||||
|
||||
// Mark that we have an image
|
||||
hasImageInput.value = 'true';
|
||||
|
||||
// Check if we can enable send button
|
||||
updateSendButtonState();
|
||||
});
|
||||
|
||||
// Retake photo
|
||||
@ -143,7 +171,7 @@
|
||||
|
||||
// Send photo
|
||||
sendBtn.addEventListener('click', function() {
|
||||
if (hasImageInput.value === 'true') {
|
||||
if (hasImageInput.value === 'true' && hasLocation) {
|
||||
// Convert canvas to blob and append to FormData
|
||||
canvas.toBlob(function(blob) {
|
||||
const formData = new FormData(form);
|
||||
@ -169,26 +197,62 @@
|
||||
}
|
||||
}).catch(error => console.error('Error:', error));
|
||||
}, 'image/jpeg', 0.9);
|
||||
} else if (!hasLocation) {
|
||||
alert('Esperando ubicación GPS. Por favor espera.');
|
||||
}
|
||||
});
|
||||
|
||||
// Request Geolocation Permission and Set Form Fields
|
||||
if ('geolocation' in navigator) {
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
locationStatus.textContent = 'Obteniendo ubicación...';
|
||||
locationStatus.style.backgroundColor = 'rgba(255, 165, 0, 0.7)'; // Orange
|
||||
|
||||
// Start watching position for more accurate results
|
||||
watchId = navigator.geolocation.watchPosition(
|
||||
(position) => {
|
||||
document.getElementById('latitude').value = position.coords.latitude;
|
||||
document.getElementById('longitude').value = position.coords.longitude;
|
||||
hasLocation = true;
|
||||
locationStatus.textContent = 'Ubicación: ✓';
|
||||
locationStatus.style.backgroundColor = 'rgba(0, 128, 0, 0.7)'; // Green
|
||||
updateSendButtonState();
|
||||
|
||||
// Once we get a good reading, we can stop watching
|
||||
if (position.coords.accuracy < 100) { // If accuracy is under 100 meters
|
||||
navigator.geolocation.clearWatch(watchId);
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
console.error('Error getting location:', error);
|
||||
locationStatus.textContent = 'Error de ubicación';
|
||||
locationStatus.style.backgroundColor = 'rgba(255, 0, 0, 0.7)'; // Red
|
||||
|
||||
// Try getting location once more with different options
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
(position) => {
|
||||
document.getElementById('latitude').value = position.coords.latitude;
|
||||
document.getElementById('longitude').value = position.coords.longitude;
|
||||
hasLocation = true;
|
||||
locationStatus.textContent = 'Ubicación: ✓';
|
||||
locationStatus.style.backgroundColor = 'rgba(0, 128, 0, 0.7)'; // Green
|
||||
updateSendButtonState();
|
||||
},
|
||||
(error) => {
|
||||
console.error('Final error getting location:', error);
|
||||
locationStatus.textContent = 'Error de ubicación';
|
||||
},
|
||||
{ maximumAge: 60000, timeout: 10000 }
|
||||
);
|
||||
},
|
||||
{
|
||||
enableHighAccuracy: true,
|
||||
timeout: 5000
|
||||
timeout: 10000
|
||||
}
|
||||
);
|
||||
} else {
|
||||
console.error('Geolocation not supported in this browser.');
|
||||
locationStatus.textContent = 'GPS no soportado';
|
||||
locationStatus.style.backgroundColor = 'rgba(255, 0, 0, 0.7)'; // Red
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
@ -7,11 +7,11 @@
|
||||
overflow-y: scroll;
|
||||
}
|
||||
</style>
|
||||
<div class="container-fluid" style="max-width: 75vw; background-color: rgba(189, 216, 3, 0.9); padding: 3rem; padding-top:10rem;display:block">
|
||||
<div class="container-fluid" id="dash-container" style="background-color: rgba(189, 216, 3, 0.9); padding: 3rem; padding-top:10rem;display:block">
|
||||
<h3>Hola <span style="color: darkgreen;">{{current_user.username}}</span></h3>
|
||||
<p>Aquí puedes ver los pines que has agregado y consultar tu enlace/QR de invitación. Cada vez que recargas esta página, tu enlace de invitacón cambia y el anterior se vuelve inválido.</p>
|
||||
<div class="grid" style="padding-bottom: 2rem;">
|
||||
<div id="qrgen" style="padding-left: 5rem;">
|
||||
<div id="qrgen" style="display:flex;justify-content: center;">
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
var qrcode = new QRCode(document.getElementById("qrgen"), {
|
||||
@ -23,21 +23,56 @@
|
||||
correctLevel : QRCode.CorrectLevel.L
|
||||
});
|
||||
</script>
|
||||
<div>
|
||||
<h2>Tu link es:</h2>
|
||||
<p><a href="https://baches.qro.mx/registrame/{{invite_code}}">https://baches.qro.mx/registrame/{{invite_code}}</a></p>
|
||||
<div style="display: grid; justify-content: center;">
|
||||
<input type="text" id="invite_code" value="{{invite_code}}" style="" hidden="true">
|
||||
<button id="copy-button" onclick="copyToClipboard()" style="background-color: rgb(255, 213, 0); color: rgb(0, 0, 0); border: none; padding: 12px 20px; border-radius: 25px; cursor: pointer; margin-top: 15px; display: flex; align-items: center; justify-content: center; font-size:1.2rem;font-weight: bold; box-shadow: 0 2px 5px rgba(0,0,0,0.2); transition: all 0.3s ease;">
|
||||
<span style="font-size: 2rem; margin-right: 10px;">📋</span> Copia tu link de invitación
|
||||
</button>
|
||||
<script>
|
||||
function copyToClipboard() {
|
||||
var copyText = document.getElementById("invite_code");
|
||||
copyText.select();
|
||||
// Modern clipboard API
|
||||
navigator.clipboard.writeText("https://baches.qro.mx/registrame/" + copyText.value).then(() => {
|
||||
console.log('Link copied successfully');
|
||||
}).catch(err => {
|
||||
// Fallback for browsers that don't support clipboard API
|
||||
document.execCommand("copy");
|
||||
console.log('Using fallback clipboard method');
|
||||
});
|
||||
|
||||
var button = document.getElementById("copy-button");
|
||||
|
||||
// Change to copied state
|
||||
button.style.backgroundColor = "green";
|
||||
button.style.color = "white";
|
||||
button.style.transition = "all 0.5s ease";
|
||||
button.innerHTML = "<span style=\"font-size: 2rem; margin-right: 10px;\">✓</span> ¡Copiado!";
|
||||
|
||||
// Revert back after 2 seconds
|
||||
setTimeout(function() {
|
||||
button.style.backgroundColor = "rgb(255, 213, 0)";
|
||||
button.style.color = "rgb(0, 0, 0)";
|
||||
button.style.transition = "all 0.5s ease";
|
||||
button.innerHTML = "<span style=\"font-size: 2rem; margin-right: 10px;\">📋</span> Copia tu link de invitación";
|
||||
}, 2000);
|
||||
}
|
||||
</script>
|
||||
<p>¡Cada vez que recargas esta página, tu código de invitación cambia para prevenir malos usos! </p>
|
||||
<p>Asegúrate de que a quien invites use su invitación de inmediato 😈</p>
|
||||
</div>
|
||||
</div>
|
||||
{% if pins %}
|
||||
{% for pin in pins %}
|
||||
|
||||
<div class="pin-card" style="margin-bottom: 2rem; background-color: #f8f9fa; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); overflow: hidden; display: flex; flex-wrap: wrap;">
|
||||
<div class="pin-card" style="{% if pin.reviewed and pin.reviewed == false %} background-color: rgb(255, 170, 0) {% endif %};margin-bottom: 2rem; background-color: #f8f9fa; border-radius: 8px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); overflow: hidden; display: flex; flex-wrap: wrap;">
|
||||
<div class="pin-image" style="flex: 0 0 250px; padding: 15px; display: flex; align-items: center; justify-content: center;">
|
||||
<img style="max-width: 100%; max-height: 200px; object-fit: cover; border-radius: 4px;" src="{{ pin.photo }}" alt="Foto de mejora urbana">
|
||||
</div>
|
||||
<div class="pin-data" style="flex: 1; padding: 15px;">
|
||||
<h4 style="margin-top: 0; color: #333;">{{ pin.typeofpin }}</h4>
|
||||
<p><strong>Agregado el:</strong> {{ pin.time }}</p>
|
||||
<p><strong>Agregado:</strong> {{ pin.time }}</p>
|
||||
{% if pin.reviewed and pin.reviewed == False %}<p><strong>Estado:</strong> Pendiente de revisión</p>{% endif %}
|
||||
<p><strong>Modificado:</strong> {% if pin.last_mod %} {{pin.last_mod}} {% endif %}</p>
|
||||
<p><strong>Descripción:</strong> {{ pin.description }}</p>
|
||||
<p><strong>Dirección:</strong> {{ pin.address }}</p>
|
||||
<p><small>Coordenadas: {{ pin.lat }}, {{ pin.lng }}</small></p>
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
{% extends 'base.html' %}
|
||||
|
||||
{% block content %}
|
||||
<div id="map" style="height: 100%; position: static;"></div>
|
||||
<div id="map" style="height: 100%; position: static; cursor:crosshair"></div>
|
||||
<script>
|
||||
// document.addEventListener('DOMContentLoaded', function() {
|
||||
var map = new L.map('map', {center: [20.57, -100.38], zoom:16, zoomControl: false });
|
||||
@ -38,7 +38,7 @@
|
||||
// Improved geolocation using direct browser API
|
||||
function getLocation() {
|
||||
if (navigator.geolocation) {
|
||||
console.warn("Geolocalización soportada en este navegador.", navigator.geolocation);
|
||||
console.log("Geolocalización soportada en este navegador.", navigator.geolocation);
|
||||
navigator.geolocation.getCurrentPosition(
|
||||
onLocationFound,
|
||||
onLocationError,
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
{% extends 'base.html' %}
|
||||
{% block content %}
|
||||
<div class="container" style="background-color: rgba(255,255,255,0.85); backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(15px); padding:2rem; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
|
||||
<div class="container" style="background-color: rgba(255,255,255,0.85); backdrop-filter: blur(5px); -webkit-backdrop-filter: blur(15px); padding:2rem; margin-top: 5rem; box-shadow: 0 4px 6px rgba(0,0,0,0.1);">
|
||||
<!-- Header -->
|
||||
<div class="header-cta">
|
||||
<h2 style="text-align: center; color: #172f01; margin-bottom: 1rem;">¡Hazte mapista y pon tu granito de fotoevidencia!</h2>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user