2026-07-22 16:37:53 +02:00
|
|
|
<!doctype html>
|
|
|
|
|
<html lang="fr" class="dark">
|
|
|
|
|
<head>
|
|
|
|
|
<meta charset="UTF-8" />
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
|
|
|
<title>{% block title %}Bolsa App{% endblock %}</title>
|
|
|
|
|
|
|
|
|
|
<!-- Favicon -->
|
|
|
|
|
<link
|
|
|
|
|
rel="icon"
|
|
|
|
|
type="image/png"
|
|
|
|
|
href="{{ url_for('static', filename='images/favicon.png') }}"
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
<!-- Tailwind CSS CDN -->
|
|
|
|
|
<script src="https://cdn.jsdelivr.net/npm/@tailwindcss/browser@4"></script>
|
|
|
|
|
</head>
|
|
|
|
|
<body
|
|
|
|
|
class="bg-gray-950 text-gray-100 min-h-screen flex flex-col justify-between"
|
|
|
|
|
>
|
|
|
|
|
<!-- Header Global -->
|
|
|
|
|
<header
|
|
|
|
|
class="bg-gray-900 border-b border-gray-800 px-6 py-4 flex items-center justify-between"
|
|
|
|
|
>
|
|
|
|
|
<!-- Gauche : Logo (taille augmentée d'environ 1.5x, ex: h-10 -> h-16) -->
|
|
|
|
|
<div class="flex items-center">
|
|
|
|
|
<a
|
|
|
|
|
href="{{ url_for('main.menu') if session.get('user_id') else url_for('main.index') }}"
|
|
|
|
|
>
|
|
|
|
|
<img
|
|
|
|
|
src="{{ url_for('static', filename='images/GFBlancLogo.png') }}"
|
|
|
|
|
alt="Logo Giraud Finance"
|
|
|
|
|
class="h-16 w-auto"
|
|
|
|
|
/>
|
|
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Centre : Titre (agrandi avec text-4xl et police plus grasse) -->
|
|
|
|
|
<div class="absolute left-1/2 transform -translate-x-1/2">
|
|
|
|
|
<h1 class="text-4xl font-extrabold tracking-wider text-white">Bolsa</h1>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Droite : Horloges Paris & New-York + Lien de déconnexion -->
|
|
|
|
|
<div class="text-xs font-mono text-right space-y-1">
|
|
|
|
|
<div id="clock-paris" class="flex items-center justify-end space-x-2">
|
|
|
|
|
<span>Paris : <span class="time">--:--:--</span></span>
|
|
|
|
|
<span
|
|
|
|
|
id="status-paris"
|
|
|
|
|
class="inline-block w-2.5 h-2.5 rounded-full bg-red-500"
|
|
|
|
|
title="Fermée"
|
|
|
|
|
></span>
|
|
|
|
|
</div>
|
|
|
|
|
<div id="clock-ny" class="flex items-center justify-end space-x-2">
|
|
|
|
|
<span>New-York : <span class="time">--:--:--</span></span>
|
|
|
|
|
<span
|
|
|
|
|
id="status-ny"
|
|
|
|
|
class="inline-block w-2.5 h-2.5 rounded-full bg-red-500"
|
|
|
|
|
title="Fermée"
|
|
|
|
|
></span>
|
|
|
|
|
</div>
|
|
|
|
|
{% if session.get('user_id') %}
|
|
|
|
|
<div class="pt-1.5 flex justify-end">
|
|
|
|
|
<a
|
|
|
|
|
href="{{ url_for('main.logout') }}"
|
|
|
|
|
class="no-underline px-2.5 py-1 bg-red-950/60 hover:bg-red-900/80 border border-red-900/60 text-red-300 rounded text-[11px] font-sans transition duration-200"
|
|
|
|
|
>
|
|
|
|
|
Se déconnecter
|
|
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
{% endif %}
|
|
|
|
|
</div>
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
<!-- Container dynamique -->
|
2026-07-26 21:04:01 +02:00
|
|
|
<main class="flex-grow w-full px-2 py-4">
|
2026-07-22 16:37:53 +02:00
|
|
|
{% block content %}{% endblock %}
|
|
|
|
|
</main>
|
|
|
|
|
|
|
|
|
|
<!-- Footer Global -->
|
|
|
|
|
<footer
|
|
|
|
|
class="bg-gray-900 border-t border-gray-800 px-6 py-4 flex items-center justify-between text-xs text-gray-400"
|
|
|
|
|
>
|
|
|
|
|
<!-- Espace vide à gauche pour équilibrer le flexbox -->
|
|
|
|
|
<div class="w-1/3"></div>
|
|
|
|
|
|
|
|
|
|
<!-- Centre : Copyright -->
|
|
|
|
|
<div class="w-1/3 text-center">
|
|
|
|
|
<p>copyright © Jean-François GIRAUD - Giraud Finance - 2026</p>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Droite : Nom et IP de l'utilisateur si connecté -->
|
|
|
|
|
<div class="w-1/3 text-right font-mono">
|
|
|
|
|
{% if session.get('user_id') %}
|
|
|
|
|
<div class="text-gray-200 font-semibold">
|
|
|
|
|
{{ session.get('user_name') }}
|
|
|
|
|
</div>
|
|
|
|
|
<div id="external-ip" class="text-gray-400 text-[11px]">
|
|
|
|
|
{{ session.get('user_ip', 'IP inconnue') }}
|
|
|
|
|
</div>
|
|
|
|
|
{% endif %}
|
|
|
|
|
</div>
|
|
|
|
|
</footer>
|
|
|
|
|
|
|
|
|
|
<!-- Script pour récupérer l'IP externe en JS -->
|
|
|
|
|
{% if session.get('user_id') %}
|
|
|
|
|
<script>
|
|
|
|
|
fetch("https://api.ipify.org?format=json")
|
|
|
|
|
.then((response) => response.json())
|
|
|
|
|
.then((data) => {
|
|
|
|
|
document.getElementById("external-ip").textContent = data.ip;
|
|
|
|
|
})
|
|
|
|
|
.catch((error) => {
|
|
|
|
|
document.getElementById("external-ip").textContent =
|
|
|
|
|
"IP non disponible";
|
|
|
|
|
});
|
|
|
|
|
</script>
|
|
|
|
|
{% endif %}
|
|
|
|
|
|
|
|
|
|
<!-- Script pour l'heure et l'état des bourses -->
|
|
|
|
|
<script>
|
|
|
|
|
function updateClocks() {
|
|
|
|
|
const now = new Date();
|
|
|
|
|
|
|
|
|
|
// Options pour Paris (Europe/Paris)
|
|
|
|
|
const optionsParis = {
|
|
|
|
|
timeZone: "Europe/Paris",
|
|
|
|
|
hour: "2-digit",
|
|
|
|
|
minute: "2-digit",
|
|
|
|
|
second: "2-digit",
|
|
|
|
|
hour12: false,
|
|
|
|
|
};
|
|
|
|
|
const timeStringParis = new Intl.DateTimeFormat(
|
|
|
|
|
"fr-FR",
|
|
|
|
|
optionsParis,
|
|
|
|
|
).format(now);
|
|
|
|
|
document.querySelector("#clock-paris .time").textContent =
|
|
|
|
|
timeStringParis;
|
|
|
|
|
|
|
|
|
|
// Options pour New York (America/New_York)
|
|
|
|
|
const optionsNY = {
|
|
|
|
|
timeZone: "America/New_York",
|
|
|
|
|
hour: "2-digit",
|
|
|
|
|
minute: "2-digit",
|
|
|
|
|
second: "2-digit",
|
|
|
|
|
hour12: false,
|
|
|
|
|
};
|
|
|
|
|
const timeStringNY = new Intl.DateTimeFormat("fr-FR", optionsNY).format(
|
|
|
|
|
now,
|
|
|
|
|
);
|
|
|
|
|
document.querySelector("#clock-ny .time").textContent = timeStringNY;
|
|
|
|
|
|
|
|
|
|
// Vérification ouverture bourses (Jours ouvrés Lundi-Vendredi)
|
|
|
|
|
const dayParis = new Intl.DateTimeFormat("fr-FR", {
|
|
|
|
|
timeZone: "Europe/Paris",
|
|
|
|
|
weekday: "short",
|
|
|
|
|
}).format(now);
|
|
|
|
|
const hourNumParis = parseFloat(
|
|
|
|
|
new Intl.DateTimeFormat("fr-FR", {
|
|
|
|
|
timeZone: "Europe/Paris",
|
|
|
|
|
hour: "numeric",
|
|
|
|
|
minute: "numeric",
|
|
|
|
|
hour12: false,
|
|
|
|
|
})
|
|
|
|
|
.format(now)
|
|
|
|
|
.replace(":", "."),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
const dayNY = new Intl.DateTimeFormat("fr-FR", {
|
|
|
|
|
timeZone: "America/New_York",
|
|
|
|
|
weekday: "short",
|
|
|
|
|
}).format(now);
|
|
|
|
|
const hourNumNY = parseFloat(
|
|
|
|
|
new Intl.DateTimeFormat("fr-FR", {
|
|
|
|
|
timeZone: "America/New_York",
|
|
|
|
|
hour: "numeric",
|
|
|
|
|
minute: "numeric",
|
|
|
|
|
hour12: false,
|
|
|
|
|
})
|
|
|
|
|
.format(now)
|
|
|
|
|
.replace(":", "."),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Paris ouvert du Lundi au Vendredi de 09:00 à 17:30
|
|
|
|
|
const isParisOpen =
|
|
|
|
|
!["sam.", "dim.", "sam", "dim"].includes(dayParis.toLowerCase()) &&
|
|
|
|
|
hourNumParis >= 9.0 &&
|
|
|
|
|
hourNumParis <= 17.3;
|
|
|
|
|
const dotParis = document.getElementById("status-paris");
|
|
|
|
|
dotParis.className = `inline-block w-2.5 h-2.5 rounded-full ${isParisOpen ? "bg-green-500 shadow-[0_0_8px_rgba(34,197,94,0.6)]" : "bg-red-500"}`;
|
|
|
|
|
|
|
|
|
|
// New York ouvert du Lundi au Vendredi de 09:30 à 16:00
|
|
|
|
|
const isNYOpen =
|
|
|
|
|
!["sam.", "dim.", "sam", "dim"].includes(dayNY.toLowerCase()) &&
|
|
|
|
|
hourNumNY >= 9.3 &&
|
|
|
|
|
hourNumNY <= 16.0;
|
|
|
|
|
const dotNY = document.getElementById("status-ny");
|
|
|
|
|
dotNY.className = `inline-block w-2.5 h-2.5 rounded-full ${isNYOpen ? "bg-green-500 shadow-[0_0_8px_rgba(34,197,94,0.6)]" : "bg-red-500"}`;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setInterval(updateClocks, 1000);
|
|
|
|
|
updateClocks();
|
|
|
|
|
</script>
|
|
|
|
|
</body>
|
|
|
|
|
</html>
|