sustitude flet by flask
Browse files- app/main.py +36 -3
- app/templates/prestamos.html +34 -4
app/main.py
CHANGED
|
@@ -11,6 +11,7 @@ import datetime
|
|
| 11 |
from flask import Flask, render_template, request, jsonify, redirect, url_for, flash
|
| 12 |
from flask_socketio import SocketIO, emit
|
| 13 |
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
|
|
|
|
| 14 |
import mysql.connector
|
| 15 |
import gitlab
|
| 16 |
import telebot
|
|
@@ -207,12 +208,11 @@ def login():
|
|
| 207 |
return render_template('login.html')
|
| 208 |
|
| 209 |
cursor = conn.cursor(dictionary=True)
|
| 210 |
-
|
| 211 |
-
cursor.execute("SELECT id, username FROM users WHERE username = %s AND password = %s", (username, password))
|
| 212 |
user_data = cursor.fetchone()
|
| 213 |
conn.close()
|
| 214 |
|
| 215 |
-
if user_data:
|
| 216 |
user = User(str(user_data['id']), user_data['username'])
|
| 217 |
login_user(user)
|
| 218 |
flash(f"¡Bienvenido, {username}!", "green")
|
|
@@ -281,6 +281,39 @@ def api_prestamo():
|
|
| 281 |
loan_mgr.add_loan(nuevo)
|
| 282 |
return jsonify({"status": "success", "id": loan_id})
|
| 283 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 284 |
@app.route('/repos')
|
| 285 |
@login_required
|
| 286 |
def repos():
|
|
|
|
| 11 |
from flask import Flask, render_template, request, jsonify, redirect, url_for, flash
|
| 12 |
from flask_socketio import SocketIO, emit
|
| 13 |
from flask_login import LoginManager, UserMixin, login_user, login_required, logout_user, current_user
|
| 14 |
+
from werkzeug.security import generate_password_hash, check_password_hash
|
| 15 |
import mysql.connector
|
| 16 |
import gitlab
|
| 17 |
import telebot
|
|
|
|
| 208 |
return render_template('login.html')
|
| 209 |
|
| 210 |
cursor = conn.cursor(dictionary=True)
|
| 211 |
+
cursor.execute("SELECT id, username, password FROM users WHERE username = %s", (username,))
|
|
|
|
| 212 |
user_data = cursor.fetchone()
|
| 213 |
conn.close()
|
| 214 |
|
| 215 |
+
if user_data and check_password_hash(user_data['password'], password):
|
| 216 |
user = User(str(user_data['id']), user_data['username'])
|
| 217 |
login_user(user)
|
| 218 |
flash(f"¡Bienvenido, {username}!", "green")
|
|
|
|
| 281 |
loan_mgr.add_loan(nuevo)
|
| 282 |
return jsonify({"status": "success", "id": loan_id})
|
| 283 |
|
| 284 |
+
@app.route('/api/entregar/<loan_id>', methods=['POST'])
|
| 285 |
+
def api_entregar(loan_id):
|
| 286 |
+
loan = None
|
| 287 |
+
for l in loan_mgr.get_all():
|
| 288 |
+
if l['id'] == loan_id:
|
| 289 |
+
loan = l
|
| 290 |
+
break
|
| 291 |
+
|
| 292 |
+
if not loan:
|
| 293 |
+
return jsonify({"status": "error", "message": "Préstamo no encontrado"}), 404
|
| 294 |
+
|
| 295 |
+
if loan['status_loan'] != "ACCEPTED":
|
| 296 |
+
return jsonify({"status": "error", "message": "Solo se pueden entregar préstamos aceptados"}), 400
|
| 297 |
+
|
| 298 |
+
ahora = datetime.datetime.now().strftime("%H:%M")
|
| 299 |
+
loan_mgr.update_status(loan_id, "DELIVERED")
|
| 300 |
+
|
| 301 |
+
mensaje_tg = (
|
| 302 |
+
"📦 *ENTREGA DE MATERIALES*\n"
|
| 303 |
+
f"👤 *Solicitante:* {escape_md(loan['Solicitante'])}\n"
|
| 304 |
+
f"⏰ *Hora de entrega:* {ahora}\n"
|
| 305 |
+
f"🛠 *Items:* {escape_md(loan['item'])}"
|
| 306 |
+
)
|
| 307 |
+
|
| 308 |
+
if bot and TG_CHAT_ID:
|
| 309 |
+
try:
|
| 310 |
+
bot.send_message(TG_CHAT_ID, mensaje_tg, parse_mode="Markdown")
|
| 311 |
+
except Exception as e:
|
| 312 |
+
print(f"Error Telegram Entregar: {e}")
|
| 313 |
+
|
| 314 |
+
socketio.emit('notification', {"text": f"Entrega confirmada para {loan['Solicitante']}", "color": "blue"})
|
| 315 |
+
return jsonify({"status": "success"})
|
| 316 |
+
|
| 317 |
@app.route('/repos')
|
| 318 |
@login_required
|
| 319 |
def repos():
|
app/templates/prestamos.html
CHANGED
|
@@ -84,6 +84,8 @@
|
|
| 84 |
<div style="text-align: right;">
|
| 85 |
{% if loan.status_loan == 'ACCEPTED' %}
|
| 86 |
<i class="fas fa-check-circle" style="color: #10b981; font-size: 1.5rem;"></i>
|
|
|
|
|
|
|
| 87 |
{% elif loan.status_loan == 'DECLINED' %}
|
| 88 |
<i class="fas fa-times-circle" style="color: #ef4444; font-size: 1.5rem;"></i>
|
| 89 |
{% else %}
|
|
@@ -183,12 +185,18 @@
|
|
| 183 |
const modal = document.getElementById('modal-content');
|
| 184 |
const overlay = document.getElementById('modal-overlay');
|
| 185 |
|
| 186 |
-
const
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 187 |
|
| 188 |
modal.innerHTML = `
|
| 189 |
<h2 style="font-family:'Outfit'; text-align:center; margin-bottom:1.5rem;">RECIBO DE PRÉSTAMO</h2>
|
| 190 |
-
<div style="background:${
|
| 191 |
-
ESTADO: ${
|
| 192 |
</div>
|
| 193 |
<p><strong>SOLICITANTE:</strong> ${loan.Solicitante}</p>
|
| 194 |
<p><strong>SALIDA:</strong> ${loan.hora}</p>
|
|
@@ -196,12 +204,34 @@
|
|
| 196 |
<hr style="opacity:0.1; margin:1rem 0;">
|
| 197 |
<p><strong>HERRAMIENTAS:</strong></p>
|
| 198 |
<p style="white-space:pre-wrap; font-size:0.9rem;">${loan.item}</p>
|
| 199 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
`;
|
| 201 |
modal.style.display = 'block';
|
| 202 |
overlay.style.display = 'block';
|
| 203 |
}
|
| 204 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 205 |
function closeModal() {
|
| 206 |
document.getElementById('modal-content').style.display = 'none';
|
| 207 |
document.getElementById('modal-overlay').style.display = 'none';
|
|
|
|
| 84 |
<div style="text-align: right;">
|
| 85 |
{% if loan.status_loan == 'ACCEPTED' %}
|
| 86 |
<i class="fas fa-check-circle" style="color: #10b981; font-size: 1.5rem;"></i>
|
| 87 |
+
{% elif loan.status_loan == 'DELIVERED' %}
|
| 88 |
+
<i class="fas fa-box" style="color: #3b82f6; font-size: 1.5rem;"></i>
|
| 89 |
{% elif loan.status_loan == 'DECLINED' %}
|
| 90 |
<i class="fas fa-times-circle" style="color: #ef4444; font-size: 1.5rem;"></i>
|
| 91 |
{% else %}
|
|
|
|
| 185 |
const modal = document.getElementById('modal-content');
|
| 186 |
const overlay = document.getElementById('modal-overlay');
|
| 187 |
|
| 188 |
+
const statusConfig = {
|
| 189 |
+
'ACCEPTED': { color: '#10b981', label: 'ACEPTADO', canDeliver: true },
|
| 190 |
+
'DELIVERED': { color: '#3b82f6', label: 'ENTREGADO', canDeliver: false },
|
| 191 |
+
'DECLINED': { color: '#ef4444', label: 'DECLINADO', canDeliver: false },
|
| 192 |
+
'PENDING': { color: '#f59e0b', label: 'PENDIENTE', canDeliver: false }
|
| 193 |
+
};
|
| 194 |
+
const config = statusConfig[loan.status_loan] || statusConfig['PENDING'];
|
| 195 |
|
| 196 |
modal.innerHTML = `
|
| 197 |
<h2 style="font-family:'Outfit'; text-align:center; margin-bottom:1.5rem;">RECIBO DE PRÉSTAMO</h2>
|
| 198 |
+
<div style="background:${config.color}; color:white; padding:0.5rem; border-radius:8px; text-align:center; font-weight:bold; margin-bottom:1.5rem;">
|
| 199 |
+
ESTADO: ${config.label}
|
| 200 |
</div>
|
| 201 |
<p><strong>SOLICITANTE:</strong> ${loan.Solicitante}</p>
|
| 202 |
<p><strong>SALIDA:</strong> ${loan.hora}</p>
|
|
|
|
| 204 |
<hr style="opacity:0.1; margin:1rem 0;">
|
| 205 |
<p><strong>HERRAMIENTAS:</strong></p>
|
| 206 |
<p style="white-space:pre-wrap; font-size:0.9rem;">${loan.item}</p>
|
| 207 |
+
|
| 208 |
+
<div style="display:flex; gap:1rem; margin-top:1.5rem;">
|
| 209 |
+
${config.canDeliver ? `
|
| 210 |
+
<button class="btn" style="flex:1; background:#3b82f6; color:white;" onclick="entregarPrestamo('${loan.id}')">
|
| 211 |
+
<i class="fas fa-box"></i> ENTREGAR
|
| 212 |
+
</button>
|
| 213 |
+
` : ''}
|
| 214 |
+
<button class="btn btn-primary" style="flex:1;" onclick="closeModal()">Cerrar</button>
|
| 215 |
+
</div>
|
| 216 |
`;
|
| 217 |
modal.style.display = 'block';
|
| 218 |
overlay.style.display = 'block';
|
| 219 |
}
|
| 220 |
|
| 221 |
+
async function entregarPrestamo(loanId) {
|
| 222 |
+
if (!confirm('¿Confirmar la entrega de materiales?')) return;
|
| 223 |
+
|
| 224 |
+
const response = await fetch(`/api/entregar/${loanId}`, {
|
| 225 |
+
method: 'POST'
|
| 226 |
+
});
|
| 227 |
+
|
| 228 |
+
if (response.ok) {
|
| 229 |
+
location.reload();
|
| 230 |
+
} else {
|
| 231 |
+
alert('Error al procesar la entrega');
|
| 232 |
+
}
|
| 233 |
+
}
|
| 234 |
+
|
| 235 |
function closeModal() {
|
| 236 |
document.getElementById('modal-content').style.display = 'none';
|
| 237 |
document.getElementById('modal-overlay').style.display = 'none';
|