Files
colibre/src/pages/compte/admin.py
T
2026-07-13 23:24:27 +02:00

219 lines
7.3 KiB
Python

import dash_bootstrap_components as dbc
from dash import Input, Output, State, callback, dcc, html, register_page
from flask_login import current_user
from src.pages._compte_shell import account_guard, account_shell
register_page(
__name__,
path="/compte/admin",
title="Mon compte | colibre",
name="Mon compte",
description="Gestion de votre compte colibre.",
)
ERROR_MESSAGES = {
"invalid_current_password": "Le mot de passe actuel est incorrect.",
"no_password_set": "Votre compte n'a pas de mot de passe (connexion via LinkedIn). Utilisez la réinitialisation de mot de passe pour en définir un.",
"password_too_short": "Le nouveau mot de passe doit faire au moins 8 caractères.",
"password_mismatch": "Les nouveaux mots de passe ne correspondent pas.",
"invalid_email": "L'adresse email n'est pas valide.",
"email_taken": "Cette adresse email est déjà utilisée.",
"email_send_failed": "L'envoi de l'email de confirmation a échoué.",
"invalid_token": "Le lien de confirmation est invalide ou expiré.",
}
SUCCESS_MESSAGES = {
"password_changed": "Mot de passe mis à jour.",
"email_pending": "Un email de confirmation a été envoyé à la nouvelle adresse.",
"email_changed": "Votre adresse email a été mise à jour.",
}
def _csrf(index: str):
return dcc.Input(
type="hidden",
id={"type": "csrf-input", "index": index},
name="csrf_token",
)
def _email_section():
return html.Div(
[
html.H4("Adresse email", className="mt-2"),
html.P([html.Strong("Email actuel : "), current_user.email]),
html.Form(
method="POST",
action="/auth/change-email",
children=[
_csrf("change-email"),
dbc.Label("Nouvelle adresse email"),
dbc.Input(
type="email", name="email", required=True, className="mb-3"
),
dbc.Button(
"Mettre à jour l'email", type="submit", color="secondary"
),
],
),
]
)
def _password_section():
return html.Div(
[
html.H4("Mot de passe", className="mt-4"),
html.Form(
method="POST",
action="/auth/change-password",
children=[
_csrf("change-password"),
dbc.Label("Mot de passe actuel"),
dbc.Input(
type="password",
name="current_password",
required=True,
className="mb-3",
),
dbc.Label("Nouveau mot de passe (8 caractères minimum)"),
dbc.Input(
type="password",
name="password",
required=True,
minLength=8,
className="mb-3",
),
dbc.Label("Confirmer le nouveau mot de passe"),
dbc.Input(
type="password",
name="password_confirm",
required=True,
minLength=8,
className="mb-3",
),
dbc.Button(
"Changer le mot de passe", type="submit", color="secondary"
),
],
),
]
)
def _danger_section():
return html.Div(
[
html.H4(
"Zone danger",
className="mt-3",
style={"color": "#d9230f"},
),
html.P("La suppression de votre compte est définitive."),
dbc.Button(
"Supprimer mon compte",
id="delete-open",
color="danger",
outline=True,
),
dbc.Modal(
id="delete-modal",
is_open=False,
children=[
dbc.ModalHeader(dbc.ModalTitle("Supprimer le compte")),
html.Form(
method="POST",
action="/auth/delete-account",
children=[
dbc.ModalBody(
[
_csrf("delete-account"),
html.P(
"Cette action est définitive. "
"Saisissez votre mot de passe pour confirmer."
),
dbc.Input(
type="password",
name="current_password",
required=True,
placeholder="Mot de passe",
),
]
),
dbc.ModalFooter(
[
dbc.Button(
"Annuler", id="delete-cancel", color="secondary"
),
dbc.Button(
"Supprimer définitivement",
type="submit",
color="danger",
),
]
),
],
),
],
),
],
className="rounded p-3 mt-4",
style={"border": "1px solid #d9230f"},
)
def _logout_section():
return html.Form(
method="POST",
action="/auth/logout",
className="mt-4",
children=[
_csrf("logout"),
dbc.Button("Déconnexion", type="submit", color="secondary", outline=True),
],
)
def layout(
error=None, password_changed=None, email_pending=None, email_changed=None, **_
):
guard = account_guard("/compte/admin", require_subscription=False)
if guard is not None:
return guard
alerts = []
if error in ERROR_MESSAGES:
alerts.append(dbc.Alert(ERROR_MESSAGES[error], color="danger"))
for flag, value in (
("password_changed", password_changed),
("email_pending", email_pending),
("email_changed", email_changed),
):
if value == "1":
alerts.append(dbc.Alert(SUCCESS_MESSAGES[flag], color="success"))
contenu = html.Div(
[
html.H2("Compte"),
*alerts,
_email_section(),
html.Hr(className="mt-4"),
_password_section(),
html.Hr(className="mt-4"),
_danger_section(),
]
)
return account_shell("admin", contenu)
@callback(
Output("delete-modal", "is_open"),
Input("delete-open", "n_clicks"),
Input("delete-cancel", "n_clicks"),
State("delete-modal", "is_open"),
prevent_initial_call=True,
)
def _toggle_delete_modal(_open, _cancel, is_open):
return not is_open