diff --git a/Doc/Guide utilisateur.odt b/Doc/Guide utilisateur.odt
new file mode 100644
index 0000000..64ceb5a
Binary files /dev/null and b/Doc/Guide utilisateur.odt differ
diff --git a/Doc/Guide utilisateur.pdf b/Doc/Guide utilisateur.pdf
new file mode 100644
index 0000000..f9fd240
Binary files /dev/null and b/Doc/Guide utilisateur.pdf differ
diff --git a/frontend/app/routes.py b/frontend/app/routes.py
index 3699c96..266de87 100644
--- a/frontend/app/routes.py
+++ b/frontend/app/routes.py
@@ -46,6 +46,9 @@ YOUTUBE_JOBS_DIR = Path(tempfile.gettempdir()) / "pysonnerie-youtube-jobs"
_YOUTUBE_JOBS_LOCK = threading.Lock()
_YOUTUBE_JOBS: dict[str, dict[str, object]] = {}
+AUTH_HTTP_ERRORS = ("Erreur HTTP 401", "Erreur HTTP 403")
+BACKEND_UNAVAILABLE_ERRORS = ("Backend inaccessible", "Echec du controle de sante")
+
def _backend_client() -> BackendClient | None:
backend_url = session.get("backend_url")
@@ -330,10 +333,17 @@ def login() -> str | Response:
try:
client.health()
client.list_triggers()
- except BackendApiError:
+ except BackendApiError as exc:
+ error_text = str(exc)
+ if any(code in error_text for code in AUTH_HTTP_ERRORS):
+ login_error = "Identifiant ou mot de passe incorrect."
+ elif any(code in error_text for code in BACKEND_UNAVAILABLE_ERRORS):
+ login_error = "Impossible de se connecter au service pour le moment. Vérifiez que le backend est demarré puis réessayez."
+ else:
+ login_error = "Connexion impossible pour le moment. Réessayez dans quelques instants."
return render_template(
"login.html",
- login_error="Impossible de se connecter au service pour le moment. Vérifiez que le backend est demarré puis réessayez.",
+ login_error=login_error,
attempted_username=username,
)
diff --git a/frontend/app/templates/base.html b/frontend/app/templates/base.html
index 79ce6dd..a932d21 100644
--- a/frontend/app/templates/base.html
+++ b/frontend/app/templates/base.html
@@ -26,7 +26,7 @@ html,
body {
margin: 0;
padding: 0;
- min-height: 100%;
+ height: 100%;
}
body {
@@ -37,6 +37,15 @@ body {
radial-gradient(circle at 100% 100%, #d5efe6 0%, transparent 40%),
var(--bg);
overflow-x: hidden;
+ min-height: 100dvh;
+ display: flex;
+ flex-direction: column;
+}
+
+.app-content {
+ flex: 1 0 auto;
+ min-height: 0;
+ display: flow-root;
}
h1,
@@ -142,7 +151,7 @@ h3 {
}
.auth-wrap {
- min-height: 100vh;
+ min-height: 100%;
display: grid;
place-items: center;
padding: 1rem;
@@ -528,6 +537,17 @@ td {
background: #fff;
}
+.app-footer {
+ flex-shrink: 0;
+ margin: 0;
+ padding: 0.75rem 1.2rem 1rem;
+ padding-top: 0.75rem;
+ border-top: 1px solid var(--line);
+ color: var(--muted);
+ font-size: 0.85rem;
+ text-align: center;
+}
+
@media (max-width: 1080px) {
.panel:nth-child(1),
.panel:nth-child(2),
@@ -572,7 +592,9 @@ td {
{% endif %}
{% endwith %}
- {% block content %}{% endblock %}
+ {% block content %}{% endblock %}
+
+