From 61c2966ff773f941de1779571db83ddf4d213be8 Mon Sep 17 00:00:00 2001 From: Olivier <1+olivier@noreply.raspot.in> Date: Tue, 18 Aug 2026 15:59:11 +0200 Subject: [PATCH] =?UTF-8?q?T=C3=A9l=C3=A9verser=20les=20fichiers=20vers=20?= =?UTF-8?q?"first-login-ui/src"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- first-login-ui/src/Main.qml | 176 +++++++++++++++++++++++++++++++++++- 1 file changed, 174 insertions(+), 2 deletions(-) diff --git a/first-login-ui/src/Main.qml b/first-login-ui/src/Main.qml index 627d41f..1bd850e 100644 --- a/first-login-ui/src/Main.qml +++ b/first-login-ui/src/Main.qml @@ -31,6 +31,64 @@ Kirigami.ApplicationWindow { } } + // Indicateur volontairement informatif : il ne bloque jamais + // la validation du mot de passe. La longueur est privilégiée, + // la diversité des caractères apporte un bonus visuel. + function passwordStrengthLevel(password) { + if (password.length === 0) + return 0 + + var classes = 0 + if (/[a-z]/.test(password)) classes += 1 + if (/[A-Z]/.test(password)) classes += 1 + if (/[0-9]/.test(password)) classes += 1 + if (/[^A-Za-z0-9]/.test(password)) classes += 1 + + var level = 1 + + if (password.length >= 16) + level = classes >= 2 ? 4 : 3 + else if (password.length >= 12) + level = classes >= 2 ? 3 : 2 + else if (password.length >= 8) + level = classes >= 3 ? 2 : 1 + + // Quelques motifs manifestement faibles : on réduit + // l'indication sans empêcher l'utilisateur de continuer. + var lower = password.toLowerCase() + if (lower === "password" || lower === "motdepasse" || + lower === "azerty" || lower === "qwerty" || + lower === "12345678" || /^(.)\1+$/.test(password)) { + level = 1 + } + + return level + } + + function passwordStrengthLabel(level) { + if (level === 1) return "Faible" + if (level === 2) return "Moyen" + if (level === 3) return "Bon" + if (level === 4) return "Très bon" + return "Non évalué" + } + + function passwordStrengthColor(level) { + if (level === 1) return "#dc2626" + if (level === 2) return "#d97706" + if (level === 3) return "#2563eb" + if (level === 4) return "#16a34a" + return "#cbd5e1" + } + + function passwordStrengthHint(level) { + if (level === 1) return "Privilégiez surtout la longueur : une phrase de passe de 12 caractères ou plus est un bon point de départ." + if (level === 2) return "Correct pour un usage courant, mais quelques caractères supplémentaires amélioreraient sensiblement la robustesse." + if (level === 3) return "Bonne robustesse apparente : la longueur et la diversité sont satisfaisantes." + if (level === 4) return "Très bonne robustesse apparente : mot de passe long et suffisamment diversifié." + return "Saisissez votre nouveau mot de passe pour obtenir une indication de robustesse." + } + Shortcut { sequence: "Ctrl+Q" onActivated: Qt.quit() @@ -85,13 +143,16 @@ Kirigami.ApplicationWindow { component SecretField: Controls.TextField { id: field + + property bool secretVisible: false + Layout.fillWidth: true implicitHeight: 50 - echoMode: TextInput.Password + echoMode: secretVisible ? TextInput.Normal : TextInput.Password passwordCharacter: "●" font.pixelSize: 15 leftPadding: 16 - rightPadding: 16 + rightPadding: 56 selectByMouse: true background: Rectangle { @@ -100,6 +161,39 @@ Kirigami.ApplicationWindow { border.width: field.activeFocus ? 2 : 1 border.color: field.activeFocus ? "#2563eb" : "#cbd5e1" } + + Controls.ToolButton { + id: visibilityButton + anchors.right: parent.right + anchors.rightMargin: 8 + anchors.verticalCenter: parent.verticalCenter + width: 38 + height: 38 + + hoverEnabled: true + focusPolicy: Qt.NoFocus + Accessible.name: field.secretVisible ? "Masquer la valeur" : "Afficher la valeur" + + onClicked: field.secretVisible = !field.secretVisible + + contentItem: Kirigami.Icon { + source: field.secretVisible ? "view-hidden" : "view-visible" + implicitWidth: 20 + implicitHeight: 20 + color: visibilityButton.hovered ? "#2563eb" : "#64748b" + } + + background: Rectangle { + radius: 8 + color: visibilityButton.pressed ? "#dbeafe" + : visibilityButton.hovered ? "#eff6ff" + : "transparent" + + Behavior on color { + ColorAnimation { duration: 100 } + } + } + } } component StepDot: Rectangle { @@ -575,6 +669,84 @@ Kirigami.ApplicationWindow { placeholderText: "Choisissez votre mot de passe personnel" } + // Indicateur de robustesse purement informatif. + // Il ne participe pas à la condition d'activation + // du bouton Continuer. + Rectangle { + id: strengthCard + Layout.fillWidth: true + implicitHeight: 112 + radius: 12 + color: "#f8fafc" + border.width: 1 + border.color: "#e2e8f0" + + property int strengthLevel: root.passwordStrengthLevel(newPassword.text) + + ColumnLayout { + anchors.fill: parent + anchors.margins: 14 + spacing: 8 + + RowLayout { + Layout.fillWidth: true + + Controls.Label { + text: "Robustesse du mot de passe" + color: "#334155" + font.pixelSize: 12 + font.weight: Font.DemiBold + } + + Item { Layout.fillWidth: true } + + Controls.Label { + text: root.passwordStrengthLabel(strengthCard.strengthLevel) + color: root.passwordStrengthColor(strengthCard.strengthLevel) + font.pixelSize: 12 + font.weight: Font.Bold + } + } + + RowLayout { + Layout.fillWidth: true + spacing: 6 + + Repeater { + model: 4 + + Rectangle { + required property int index + Layout.fillWidth: true + implicitHeight: 7 + radius: 4 + color: index < parent.parent.parent.strengthLevel + ? root.passwordStrengthColor(strengthCard.strengthLevel) + : "#e2e8f0" + + Behavior on color { + ColorAnimation { duration: 140 } + } + } + } + } + + Controls.Label { + Layout.fillWidth: true + text: root.passwordStrengthHint(strengthCard.strengthLevel) + wrapMode: Text.WordWrap + color: "#64748b" + font.pixelSize: 11 + } + } + } + + Controls.Label { + text: "Indication uniquement : un mot de passe faible n'est pas bloqué." + color: "#94a3b8" + font.pixelSize: 11 + } + Controls.Label { text: "Confirmation" color: "#334155"