Menu 1 4 5 ok + bugs
This commit is contained in:
+36
-43
@@ -1,4 +1,7 @@
|
||||
from flask import Blueprint, render_template, request, redirect, url_for, session, flash
|
||||
import time
|
||||
import requests
|
||||
import mysql.connector
|
||||
from werkzeug.security import check_password_hash, generate_password_hash
|
||||
from sqlalchemy import text
|
||||
from app import db
|
||||
@@ -141,6 +144,7 @@ def gestion_utilisateurs():
|
||||
|
||||
return render_template('gestion_utilisateurs.html', users=users)
|
||||
|
||||
|
||||
# Route pour la gestion des actions
|
||||
@main.route('/gestion-actions', methods=['GET', 'POST'])
|
||||
def gestion_actions():
|
||||
@@ -149,67 +153,52 @@ def gestion_actions():
|
||||
|
||||
if request.method == 'POST':
|
||||
action_type = request.form.get('action')
|
||||
# On récupère aussi l'id caché s'il s'agit d'une modification
|
||||
action_id = request.form.get('id')
|
||||
isin = request.form.get('isin')
|
||||
isin = request.form.get('isin') or None
|
||||
ticker = request.form.get('ticker')
|
||||
yahoo_code = request.form.get('yahoo_code')
|
||||
company_name = request.form.get('company_name')
|
||||
exchange = request.form.get('exchange')
|
||||
pays = request.form.get('pays')
|
||||
currency = request.form.get('currency')
|
||||
|
||||
if action_type == 'creer':
|
||||
if isin:
|
||||
try:
|
||||
# Vérifier si l'ISIN existe déjà
|
||||
check_query = text("SELECT COUNT(*) FROM actions WHERE isin = :isin")
|
||||
exists = db.session.execute(check_query, {"isin": isin}).scalar()
|
||||
|
||||
if exists > 0:
|
||||
flash("L'action existe déjà, pas de création.", "warning")
|
||||
else:
|
||||
# L'id s'incrémente tout seul, on ne l'inclus pas dans l'INSERT
|
||||
query = text("""
|
||||
INSERT INTO actions (isin, ticker, yahoo_code, company_name, updated_at)
|
||||
VALUES (:isin, :ticker, :yahoo_code, :company_name, NOW())
|
||||
""")
|
||||
db.session.execute(query, {
|
||||
"isin": isin,
|
||||
"ticker": ticker,
|
||||
"yahoo_code": yahoo_code,
|
||||
"company_name": company_name
|
||||
})
|
||||
db.session.commit()
|
||||
flash("Action créée", "success")
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
flash("Erreur lors de la création de l'action.", "danger")
|
||||
else:
|
||||
flash("L'ISIN est obligatoire.", "danger")
|
||||
|
||||
try:
|
||||
query = text("""
|
||||
INSERT INTO actions (isin, ticker, company_name, exchange, pays, currency, updated_at)
|
||||
VALUES (:isin, :ticker, :company_name, :exchange, :pays, :currency, NOW())
|
||||
""")
|
||||
db.session.execute(query, {
|
||||
"isin": isin, "ticker": ticker, "company_name": company_name,
|
||||
"exchange": exchange, "pays": pays, "currency": currency
|
||||
})
|
||||
db.session.commit()
|
||||
flash("Action créée avec succès", "success")
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
flash("Erreur lors de la création de l'action.", "danger")
|
||||
|
||||
elif action_type == 'modifier' and action_id:
|
||||
try:
|
||||
# On met à jour en se basant sur l'id technique unique
|
||||
query = text("""
|
||||
UPDATE actions
|
||||
SET isin = :isin, ticker = :ticker, yahoo_code = :yahoo_code, company_name = :company_name, updated_at = NOW()
|
||||
SET isin = :isin, ticker = :ticker, company_name = :company_name, exchange = :exchange,
|
||||
pays = :pays, currency = :currency, updated_at = NOW()
|
||||
WHERE id = :id
|
||||
""")
|
||||
db.session.execute(query, {
|
||||
"id": action_id,
|
||||
"isin": isin,
|
||||
"ticker": ticker,
|
||||
"yahoo_code": yahoo_code,
|
||||
"company_name": company_name
|
||||
"id": action_id, "isin": isin, "ticker": ticker, "company_name": company_name,
|
||||
"exchange": exchange, "pays": pays, "currency": currency
|
||||
})
|
||||
db.session.commit()
|
||||
flash("Modification effectuée", "success")
|
||||
except Exception as e:
|
||||
db.session.rollback()
|
||||
flash("Erreur lors de la modification (Vérifiez si l'ISIN n'est pas déjà utilisé par une autre ligne).", "danger")
|
||||
flash("Erreur lors de la modification.", "danger")
|
||||
|
||||
return redirect(url_for('main.gestion_actions'))
|
||||
|
||||
# On sélectionne l'id en plus dans la requête
|
||||
actions_query = text("SELECT id, isin, ticker, yahoo_code, company_name, updated_at FROM actions ORDER BY company_name ASC")
|
||||
# Récupération avec la nouvelle structure
|
||||
actions_query = text("SELECT id, isin, ticker, company_name, exchange, pays, currency, updated_at FROM actions ORDER BY company_name ASC")
|
||||
result = db.session.execute(actions_query)
|
||||
|
||||
actions_list = []
|
||||
@@ -218,8 +207,10 @@ def gestion_actions():
|
||||
'id': row.id,
|
||||
'isin': row.isin if row.isin else '',
|
||||
'ticker': row.ticker if row.ticker else '',
|
||||
'yahoo_code': row.yahoo_code if row.yahoo_code else '',
|
||||
'company_name': row.company_name if row.company_name else 'Sans nom',
|
||||
'exchange': row.exchange if row.exchange else '',
|
||||
'pays': row.pays if row.pays else '',
|
||||
'currency': row.currency if row.currency else '',
|
||||
'updated_at': row.updated_at.strftime('%Y-%m-%d %H:%M:%S') if row.updated_at else ''
|
||||
})
|
||||
|
||||
@@ -302,4 +293,6 @@ def gestion_societes():
|
||||
'Mail': row.Mail if row.Mail else ''
|
||||
})
|
||||
|
||||
return render_template('gestion_societes.html', societes=societes)
|
||||
return render_template('gestion_societes.html', societes=societes)
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
endblock %} {% block header_title %}Gestion actions{% endblock %} {% block
|
||||
content %}
|
||||
<div class="flex flex-col items-center justify-center py-6 relative">
|
||||
<!-- Système de Toasts (Notifications flottantes centrées) -->
|
||||
<!-- Système de Toasts -->
|
||||
<div
|
||||
id="toast-container"
|
||||
class="fixed top-6 left-1/2 transform -translate-x-1/2 z-50 space-y-3 w-full max-w-md px-4 pointer-events-none"
|
||||
@@ -45,11 +45,11 @@ content %}
|
||||
<div class="grid grid-cols-1 md:grid-cols-12 gap-8 py-4">
|
||||
<!-- COLONNE GAUCHE : Recherche et Liste -->
|
||||
<div
|
||||
class="md:col-span-4 bg-gray-950 border border-gray-800 rounded-xl p-4 flex flex-col h-[560px]"
|
||||
class="md:col-span-4 bg-gray-950 border border-gray-800 rounded-xl p-4 flex flex-col h-[580px]"
|
||||
>
|
||||
<div class="mb-3">
|
||||
<label class="block text-xs font-medium text-gray-400 mb-1"
|
||||
>Rechercher (ISIN, Ticker, Nom)</label
|
||||
>Rechercher (ISIN, Ticker, Nom, Pays)</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
@@ -66,32 +66,32 @@ content %}
|
||||
Actions enregistrées
|
||||
</h3>
|
||||
|
||||
<!-- Liste avec délégation d'événements (Nom et Ticker uniquement) -->
|
||||
<div
|
||||
class="overflow-y-auto flex-1 space-y-1 pr-1"
|
||||
id="actions-list-container"
|
||||
>
|
||||
{% for a in actions %}
|
||||
<div
|
||||
onclick="selectionnerAction('{{ a.id }}', '{{ a.isin }}', '{{ a.ticker }}', '{{ a.yahoo_code }}', '{{ a.company_name | e }}', '{{ a.updated_at }}')"
|
||||
class="action-item cursor-pointer px-3 py-2.5 rounded-lg {% if loop.first %}bg-blue-600 text-white{% else %}hover:bg-gray-800 text-gray-300{% endif %} font-medium text-sm transition flex flex-col justify-between"
|
||||
class="action-item cursor-pointer px-3 py-2.5 rounded-lg {% if loop.first %}bg-blue-600 text-white{% else %}hover:bg-gray-800 text-gray-300{% endif %} font-medium text-sm transition flex justify-between items-center"
|
||||
data-id="{{ a.id }}"
|
||||
data-isin="{{ a.isin }}"
|
||||
data-ticker="{{ a.ticker | lower }}"
|
||||
data-company="{{ a.company_name | lower }}"
|
||||
data-search="{{ a.isin | lower }} {{ a.ticker | lower }} {{ a.company_name | lower }}"
|
||||
data-ticker="{{ a.ticker }}"
|
||||
data-company="{{ a.company_name }}"
|
||||
data-exchange="{{ a.exchange }}"
|
||||
data-pays="{{ a.pays }}"
|
||||
data-currency="{{ a.currency }}"
|
||||
data-updated="{{ a.updated_at }}"
|
||||
data-search="{{ a.isin | lower }} {{ a.ticker | lower }} {{ a.company_name | lower }} {{ a.exchange | lower }} {{ a.pays | lower }}"
|
||||
>
|
||||
<div class="flex justify-between items-center w-full">
|
||||
<span class="truncate font-semibold pr-2"
|
||||
>{{ a.company_name }}</span
|
||||
>
|
||||
<span
|
||||
class="text-xs {% if loop.first %}bg-blue-800{% else %}bg-gray-800 text-gray-400{% endif %} px-2 py-0.5 rounded whitespace-nowrap"
|
||||
>{{ a.ticker or 'N/A' }}</span
|
||||
>
|
||||
</div>
|
||||
<span class="text-xs opacity-70 font-mono mt-0.5"
|
||||
>{{ a.isin }}</span
|
||||
<span class="truncate font-semibold pr-2"
|
||||
>{{ a.company_name }}</span
|
||||
>
|
||||
<span
|
||||
class="ticker-badge text-xs {% if loop.first %}bg-blue-800 text-white{% else %}bg-gray-800 text-gray-400{% endif %} px-2 py-0.5 rounded whitespace-nowrap font-mono"
|
||||
>
|
||||
{{ a.ticker or 'N/A' }}
|
||||
</span>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
@@ -107,7 +107,6 @@ content %}
|
||||
action=""
|
||||
class="space-y-4 flex-1 flex flex-col justify-between"
|
||||
>
|
||||
<!-- ID technique caché pour les modifications -->
|
||||
<input type="hidden" id="field-id" name="id" />
|
||||
|
||||
<div class="space-y-4">
|
||||
@@ -118,7 +117,6 @@ content %}
|
||||
</h3>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<!-- ISIN (Modifiable) -->
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-400 mb-1"
|
||||
>ISIN</label
|
||||
@@ -127,12 +125,9 @@ content %}
|
||||
type="text"
|
||||
id="field-isin"
|
||||
name="isin"
|
||||
required
|
||||
class="w-full bg-gray-900 border border-gray-800 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Ticker -->
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-400 mb-1"
|
||||
>Ticker</label
|
||||
@@ -147,38 +142,56 @@ content %}
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<!-- Yahoo Code -->
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-400 mb-1"
|
||||
>Yahoo Code</label
|
||||
>Nom de la société (company_name)</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="field-yahoo_code"
|
||||
name="yahoo_code"
|
||||
id="field-company"
|
||||
name="company_name"
|
||||
class="w-full bg-gray-900 border border-gray-800 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Company Name -->
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-400 mb-1"
|
||||
>Company Name</label
|
||||
>Exchange</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="field-company_name"
|
||||
name="company_name"
|
||||
id="field-exchange"
|
||||
name="exchange"
|
||||
class="w-full bg-gray-900 border border-gray-800 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<!-- Updated At -->
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-400 mb-1"
|
||||
>Dernière mise à jour (Lecture seule)</label
|
||||
>Pays</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="field-pays"
|
||||
name="pays"
|
||||
class="w-full bg-gray-900 border border-gray-800 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-400 mb-1"
|
||||
>Devise (currency)</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
id="field-currency"
|
||||
name="currency"
|
||||
class="w-full bg-gray-900 border border-gray-800 rounded-lg px-3 py-2 text-white text-sm focus:outline-none focus:border-blue-500"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-medium text-gray-400 mb-1"
|
||||
>Mise à jour</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
@@ -191,7 +204,6 @@ content %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Bas : Boutons -->
|
||||
<div
|
||||
class="flex items-center justify-end space-x-3 pt-6 border-t border-gray-800 mt-6"
|
||||
>
|
||||
@@ -250,68 +262,69 @@ content %}
|
||||
}, 4000);
|
||||
});
|
||||
|
||||
const container = document.getElementById("actions-list-container");
|
||||
if (container) {
|
||||
container.addEventListener("click", (e) => {
|
||||
const item = e.target.closest(".action-item");
|
||||
if (item) {
|
||||
selectionnerActionItem(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const premiereAction = document.querySelector(".action-item");
|
||||
if (premiereAction) {
|
||||
premiereAction.click();
|
||||
selectionnerActionItem(premiereAction);
|
||||
}
|
||||
});
|
||||
|
||||
function selectionnerAction(
|
||||
id,
|
||||
isin,
|
||||
ticker,
|
||||
yahoo_code,
|
||||
company_name,
|
||||
updated_at,
|
||||
) {
|
||||
document.getElementById("field-id").value = id;
|
||||
document.getElementById("field-isin").value = isin;
|
||||
document.getElementById("field-ticker").value = ticker;
|
||||
document.getElementById("field-yahoo_code").value = yahoo_code;
|
||||
document.getElementById("field-company_name").value = company_name;
|
||||
document.getElementById("field-updated_at").value = updated_at;
|
||||
function selectionnerActionItem(item) {
|
||||
document.getElementById("field-id").value = item.dataset.id;
|
||||
document.getElementById("field-isin").value = item.dataset.isin;
|
||||
document.getElementById("field-ticker").value = item.dataset.ticker;
|
||||
document.getElementById("field-company").value = item.dataset.company;
|
||||
document.getElementById("field-exchange").value = item.dataset.exchange;
|
||||
document.getElementById("field-pays").value = item.dataset.pays;
|
||||
document.getElementById("field-currency").value = item.dataset.currency;
|
||||
document.getElementById("field-updated_at").value = item.dataset.updated;
|
||||
|
||||
document.querySelectorAll(".action-item").forEach((el) => {
|
||||
el.classList.remove("bg-blue-600", "text-white");
|
||||
el.classList.add("hover:bg-gray-800", "text-gray-300");
|
||||
const badge = el.querySelector("span:nth-child(1) span:last-child");
|
||||
const badge = el.querySelector(".ticker-badge");
|
||||
if (badge) {
|
||||
badge.classList.remove("bg-blue-800");
|
||||
badge.classList.remove("bg-blue-800", "text-white");
|
||||
badge.classList.add("bg-gray-800", "text-gray-400");
|
||||
}
|
||||
});
|
||||
|
||||
const selectedEl = document.querySelector(`[data-id="${id}"]`);
|
||||
if (selectedEl) {
|
||||
selectedEl.classList.remove("hover:bg-gray-800", "text-gray-300");
|
||||
selectedEl.classList.add("bg-blue-600", "text-white");
|
||||
const badge = selectedEl.querySelector(
|
||||
"span:nth-child(1) span:last-child",
|
||||
);
|
||||
if (badge) {
|
||||
badge.classList.remove("bg-gray-800", "text-gray-400");
|
||||
badge.classList.add("bg-blue-800");
|
||||
}
|
||||
selectedEl.scrollIntoView({ block: "nearest" });
|
||||
item.classList.remove("hover:bg-gray-800", "text-gray-300");
|
||||
item.classList.add("bg-blue-600", "text-white");
|
||||
const badge = item.querySelector(".ticker-badge");
|
||||
if (badge) {
|
||||
badge.classList.remove("bg-gray-800", "text-gray-400");
|
||||
badge.classList.add("bg-blue-800", "text-white");
|
||||
}
|
||||
item.scrollIntoView({ block: "nearest" });
|
||||
}
|
||||
|
||||
function preparerCreation() {
|
||||
document.getElementById("field-id").value = "";
|
||||
document.getElementById("field-isin").value = "";
|
||||
document.getElementById("field-ticker").value = "";
|
||||
document.getElementById("field-yahoo_code").value = "";
|
||||
document.getElementById("field-company_name").value = "";
|
||||
document.getElementById("field-company").value = "";
|
||||
document.getElementById("field-exchange").value = "";
|
||||
document.getElementById("field-pays").value = "";
|
||||
document.getElementById("field-currency").value = "";
|
||||
document.getElementById("field-updated_at").value =
|
||||
"Automatique à la création";
|
||||
|
||||
// Retirer la sélection visuelle active de la liste
|
||||
document.querySelectorAll(".action-item").forEach((el) => {
|
||||
el.classList.remove("bg-blue-600", "text-white");
|
||||
el.classList.add("hover:bg-gray-800", "text-gray-300");
|
||||
const badge = el.querySelector("span:nth-child(1) span:last-child");
|
||||
const badge = el.querySelector(".ticker-badge");
|
||||
if (badge) {
|
||||
badge.classList.remove("bg-blue-800");
|
||||
badge.classList.remove("bg-blue-800", "text-white");
|
||||
badge.classList.add("bg-gray-800", "text-gray-400");
|
||||
}
|
||||
});
|
||||
@@ -338,7 +351,7 @@ content %}
|
||||
});
|
||||
|
||||
if (premierCorrespondant && terme !== "") {
|
||||
premierCorrespondant.click();
|
||||
selectionnerActionItem(premierCorrespondant);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -347,7 +360,7 @@ content %}
|
||||
filtrerActions();
|
||||
const premiereAction = document.querySelector(".action-item");
|
||||
if (premiereAction) {
|
||||
premiereAction.click();
|
||||
selectionnerActionItem(premiereAction);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -12,7 +12,6 @@ header_title %}Tableau de bord{% endblock %} {% block content %}
|
||||
</p>
|
||||
|
||||
<!-- Grille principale : 2 colonnes pour les boutons -->
|
||||
<!-- Grille des 8 gros boutons -->
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-5 mb-8">
|
||||
<!-- btn 1 -->
|
||||
<a
|
||||
@@ -86,4 +85,5 @@ header_title %}Tableau de bord{% endblock %} {% block content %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
||||
Reference in New Issue
Block a user