Téléverser les fichiers vers "first-login-ui/src"

This commit is contained in:
2026-08-18 16:15:08 +02:00
parent 10ea1dbad7
commit c50a27bea5
+278 -95
View File
@@ -19,18 +19,53 @@ Kirigami.ApplicationWindow {
{ "title": "Terminé", "subtitle": "Poste prêt" } { "title": "Terminé", "subtitle": "Poste prêt" }
] ]
property bool passwordStepValid:
currentPassword.text.trim().length > 0 &&
newPassword.text.length > 0 &&
confirmPassword.text.length > 0 &&
newPassword.text === confirmPassword.text
property bool pinStepValid:
currentPin.text.length >= 4 &&
newPin.text.length >= 4 &&
confirmPin.text.length >= 4 &&
newPin.text === confirmPin.text
property bool workflowComplete: passwordStepValid && pinStepValid
property bool closeAttempted: false
function goNext() { function goNext() {
if (currentStep < steps.length - 1) { // Défense en profondeur : même si un bouton était activé par erreur,
// on ne franchit jamais une étape dont les champs requis sont incomplets.
if (currentStep === 1 && !passwordStepValid)
return
if (currentStep === 2 && !pinStepValid)
return
if (currentStep < steps.length - 1)
currentStep += 1 currentStep += 1
}
} }
function goBack() { function goBack() {
if (currentStep > 0) { if (currentStep > 0)
currentStep -= 1 currentStep -= 1
}
onClosing: function(close) {
if (!workflowComplete) {
close.accepted = false
closeAttempted = true
closeWarningTimer.restart()
} }
} }
Timer {
id: closeWarningTimer
interval: 3500
repeat: false
onTriggered: root.closeAttempted = false
}
// Indicateur volontairement informatif : il ne bloque jamais // Indicateur volontairement informatif : il ne bloque jamais
// la validation du mot de passe. La longueur est privilégiée, // la validation du mot de passe. La longueur est privilégiée,
// la diversité des caractères apporte un bonus visuel. // la diversité des caractères apporte un bonus visuel.
@@ -89,11 +124,6 @@ Kirigami.ApplicationWindow {
return "Saisissez votre nouveau mot de passe pour obtenir une indication de robustesse." return "Saisissez votre nouveau mot de passe pour obtenir une indication de robustesse."
} }
Shortcut {
sequence: "Ctrl+Q"
onActivated: Qt.quit()
}
component PrimaryButton: Controls.Button { component PrimaryButton: Controls.Button {
id: control id: control
implicitHeight: 48 implicitHeight: 48
@@ -196,6 +226,29 @@ Kirigami.ApplicationWindow {
} }
} }
component FieldLabel: RowLayout {
id: fieldLabel
property string iconName: "dialog-password"
property string labelText: ""
spacing: 8
Kirigami.Icon {
source: fieldLabel.iconName
implicitWidth: 17
implicitHeight: 17
color: "#64748b"
}
Controls.Label {
text: fieldLabel.labelText
color: "#334155"
font.pixelSize: 13
font.weight: Font.DemiBold
}
}
component StepDot: Rectangle { component StepDot: Rectangle {
required property int stepIndex required property int stepIndex
@@ -301,37 +354,49 @@ Kirigami.ApplicationWindow {
anchors.fill: parent anchors.fill: parent
spacing: 16 spacing: 16
ColumnLayout { // Colonne de largeur fixe : tous les marqueurs et tous
Layout.alignment: Qt.AlignTop // les titres commencent exactement au même endroit.
spacing: 0 Item {
Layout.preferredWidth: 42
Layout.fillHeight: true
StepDot { stepIndex: index } StepDot {
anchors.top: parent.top
anchors.horizontalCenter: parent.horizontalCenter
stepIndex: index
}
Rectangle { Rectangle {
visible: index < root.steps.length - 1 visible: index < root.steps.length - 1
Layout.alignment: Qt.AlignHCenter anchors.top: parent.top
Layout.preferredWidth: 2 anchors.topMargin: 34
Layout.preferredHeight: 36 anchors.bottom: parent.bottom
anchors.horizontalCenter: parent.horizontalCenter
width: 2
color: index < root.currentStep ? "#22c55e" : "#28517e" color: index < root.currentStep ? "#22c55e" : "#28517e"
} }
} }
ColumnLayout { ColumnLayout {
Layout.fillWidth: true Layout.fillWidth: true
Layout.alignment: Qt.AlignTop Layout.alignment: Qt.AlignVCenter
spacing: 3 spacing: 3
Controls.Label { Controls.Label {
Layout.fillWidth: true
text: modelData.title text: modelData.title
color: index === root.currentStep ? "white" : "#c1d3e8" color: index === root.currentStep ? "white" : "#c1d3e8"
font.pixelSize: 15 font.pixelSize: 15
font.weight: index === root.currentStep ? Font.DemiBold : Font.Medium font.weight: index === root.currentStep ? Font.DemiBold : Font.Medium
horizontalAlignment: Text.AlignLeft
} }
Controls.Label { Controls.Label {
Layout.fillWidth: true
text: modelData.subtitle text: modelData.subtitle
color: "#7699c2" color: "#7699c2"
font.pixelSize: 12 font.pixelSize: 12
horizontalAlignment: Text.AlignLeft
} }
} }
} }
@@ -382,11 +447,22 @@ Kirigami.ApplicationWindow {
Item { Layout.preferredHeight: 20 } Item { Layout.preferredHeight: 20 }
Controls.Label { RowLayout {
text: "Ctrl+Q : quitter l'aperçu"
color: "#6289b6"
font.pixelSize: 11
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
spacing: 6
Kirigami.Icon {
source: "lock"
implicitWidth: 14
implicitHeight: 14
color: "#6289b6"
}
Controls.Label {
text: "Assistant obligatoire jusqu'à la fin"
color: "#6289b6"
font.pixelSize: 11
}
} }
} }
} }
@@ -431,9 +507,37 @@ Kirigami.ApplicationWindow {
Item { Layout.fillWidth: true } Item { Layout.fillWidth: true }
SecondaryButton { Rectangle {
text: "Quitter l'aperçu" implicitWidth: lockStatusRow.implicitWidth + 24
onClicked: Qt.quit() implicitHeight: 34
radius: 17
color: root.closeAttempted ? "#fef2f2" : "#f8fafc"
border.width: 1
border.color: root.closeAttempted ? "#fecaca" : "#cbd5e1"
RowLayout {
id: lockStatusRow
anchors.centerIn: parent
spacing: 7
Kirigami.Icon {
source: root.workflowComplete ? "lock-open" : "lock"
implicitWidth: 16
implicitHeight: 16
color: root.closeAttempted ? "#dc2626" : "#64748b"
}
Controls.Label {
text: root.closeAttempted
? "Terminez la configuration avant de quitter"
: root.workflowComplete
? "Configuration complète"
: "Configuration en cours"
color: root.closeAttempted ? "#b91c1c" : "#475569"
font.pixelSize: 11
font.weight: Font.DemiBold
}
}
} }
} }
@@ -597,20 +701,47 @@ Kirigami.ApplicationWindow {
anchors.fill: parent anchors.fill: parent
spacing: 18 spacing: 18
Controls.Label { RowLayout {
text: "Mot de passe local" Layout.fillWidth: true
color: "#0f172a" spacing: 20
font.pixelSize: 29
font.weight: Font.Bold
}
Controls.Label { Rectangle {
Layout.maximumWidth: 720 Layout.preferredWidth: 80
text: "Ce mot de passe reste disponible comme solution de secours si votre YubiKey est momentanément indisponible." Layout.preferredHeight: 80
wrapMode: Text.WordWrap radius: 22
color: "#64748b" color: "#eff6ff"
font.pixelSize: 15 border.width: 1
lineHeight: 1.2 border.color: "#bfdbfe"
Kirigami.Icon {
anchors.centerIn: parent
source: "dialog-password"
implicitWidth: 38
implicitHeight: 38
color: "#2563eb"
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: 5
Controls.Label {
text: "Mot de passe local"
color: "#0f172a"
font.pixelSize: 29
font.weight: Font.Bold
}
Controls.Label {
Layout.fillWidth: true
text: "Ce mot de passe reste disponible comme solution de secours si votre YubiKey est momentanément indisponible."
wrapMode: Text.WordWrap
color: "#64748b"
font.pixelSize: 15
lineHeight: 1.2
}
}
} }
Rectangle { Rectangle {
@@ -626,11 +757,11 @@ Kirigami.ApplicationWindow {
anchors.margins: 16 anchors.margins: 16
spacing: 12 spacing: 12
Controls.Label { Kirigami.Icon {
text: "" source: "emblem-information"
implicitWidth: 22
implicitHeight: 22
color: "#2563eb" color: "#2563eb"
font.pixelSize: 22
font.weight: Font.Bold
} }
Controls.Label { Controls.Label {
@@ -645,11 +776,9 @@ Kirigami.ApplicationWindow {
Item { Layout.preferredHeight: 4 } Item { Layout.preferredHeight: 4 }
Controls.Label { FieldLabel {
text: "Mot de passe temporaire actuel" iconName: "dialog-password"
color: "#334155" labelText: "Mot de passe temporaire actuel"
font.pixelSize: 13
font.weight: Font.DemiBold
} }
SecretField { SecretField {
@@ -657,11 +786,9 @@ Kirigami.ApplicationWindow {
placeholderText: "Saisissez le mot de passe temporaire" placeholderText: "Saisissez le mot de passe temporaire"
} }
Controls.Label { FieldLabel {
text: "Nouveau mot de passe" iconName: "document-edit"
color: "#334155" labelText: "Nouveau mot de passe"
font.pixelSize: 13
font.weight: Font.DemiBold
} }
SecretField { SecretField {
@@ -690,6 +817,14 @@ Kirigami.ApplicationWindow {
RowLayout { RowLayout {
Layout.fillWidth: true Layout.fillWidth: true
spacing: 8
Kirigami.Icon {
source: "security-high"
implicitWidth: 17
implicitHeight: 17
color: root.passwordStrengthColor(strengthCard.strengthLevel)
}
Controls.Label { Controls.Label {
text: "Robustesse du mot de passe" text: "Robustesse du mot de passe"
@@ -747,11 +882,9 @@ Kirigami.ApplicationWindow {
font.pixelSize: 11 font.pixelSize: 11
} }
Controls.Label { FieldLabel {
text: "Confirmation" iconName: "dialog-ok-apply"
color: "#334155" labelText: "Confirmation"
font.pixelSize: 13
font.weight: Font.DemiBold
} }
SecretField { SecretField {
@@ -780,7 +913,7 @@ Kirigami.ApplicationWindow {
PrimaryButton { PrimaryButton {
text: "Continuer" text: "Continuer"
enabled: newPassword.text.length > 0 && newPassword.text === confirmPassword.text enabled: root.passwordStepValid
opacity: enabled ? 1.0 : 0.45 opacity: enabled ? 1.0 : 0.45
onClicked: root.goNext() onClicked: root.goNext()
} }
@@ -865,11 +998,9 @@ Kirigami.ApplicationWindow {
} }
} }
Controls.Label { FieldLabel {
text: "PIN temporaire actuel" iconName: "dialog-password"
color: "#334155" labelText: "PIN temporaire actuel"
font.pixelSize: 13
font.weight: Font.DemiBold
} }
SecretField { SecretField {
@@ -878,11 +1009,9 @@ Kirigami.ApplicationWindow {
inputMethodHints: Qt.ImhDigitsOnly inputMethodHints: Qt.ImhDigitsOnly
} }
Controls.Label { FieldLabel {
text: "Nouveau PIN" iconName: "security-high"
color: "#334155" labelText: "Nouveau PIN"
font.pixelSize: 13
font.weight: Font.DemiBold
} }
SecretField { SecretField {
@@ -891,11 +1020,9 @@ Kirigami.ApplicationWindow {
inputMethodHints: Qt.ImhDigitsOnly inputMethodHints: Qt.ImhDigitsOnly
} }
Controls.Label { FieldLabel {
text: "Confirmation du nouveau PIN" iconName: "dialog-ok-apply"
color: "#334155" labelText: "Confirmation du nouveau PIN"
font.pixelSize: 13
font.weight: Font.DemiBold
} }
SecretField { SecretField {
@@ -925,7 +1052,7 @@ Kirigami.ApplicationWindow {
PrimaryButton { PrimaryButton {
text: "Finaliser" text: "Finaliser"
enabled: newPin.text.length >= 4 && newPin.text === confirmPin.text enabled: root.pinStepValid
opacity: enabled ? 1.0 : 0.45 opacity: enabled ? 1.0 : 0.45
onClicked: root.goNext() onClicked: root.goNext()
} }
@@ -952,12 +1079,12 @@ Kirigami.ApplicationWindow {
border.width: 1 border.width: 1
border.color: "#86efac" border.color: "#86efac"
Controls.Label { Kirigami.Icon {
anchors.centerIn: parent anchors.centerIn: parent
text: "✓" source: "dialog-ok-apply"
implicitWidth: 52
implicitHeight: 52
color: "#16a34a" color: "#16a34a"
font.pixelSize: 50
font.weight: Font.Bold
} }
} }
@@ -983,28 +1110,82 @@ Kirigami.ApplicationWindow {
Item { Layout.preferredHeight: 12 } Item { Layout.preferredHeight: 12 }
Rectangle { RowLayout {
Layout.preferredWidth: 520
Layout.preferredHeight: 112
Layout.alignment: Qt.AlignHCenter Layout.alignment: Qt.AlignHCenter
radius: 16 spacing: 12
color: "#f8fafc"
border.width: 1
border.color: "#e2e8f0"
ColumnLayout { Repeater {
anchors.fill: parent model: [
anchors.margins: 18 { "icon": "dialog-password", "title": "Mot de passe", "text": "Accès local renseigné" },
spacing: 10 { "icon": "security-high", "title": "YubiKey", "text": "PIN personnel renseigné" },
{ "icon": "computer-laptop", "title": "Poste", "text": "Configuration finalisée" }
]
RowLayout { Rectangle {
Controls.Label { text: "✓"; color: "#16a34a"; font.pixelSize: 17; font.weight: Font.Bold } required property var modelData
Controls.Label { text: "Mot de passe personnel configuré"; color: "#334155"; font.pixelSize: 14 } Layout.preferredWidth: 205
} Layout.preferredHeight: 126
radius: 16
color: "#f8fafc"
border.width: 1
border.color: "#e2e8f0"
RowLayout { ColumnLayout {
Controls.Label { text: "✓"; color: "#16a34a"; font.pixelSize: 17; font.weight: Font.Bold } anchors.fill: parent
Controls.Label { text: "PIN personnel YubiKey configuré"; color: "#334155"; font.pixelSize: 14 } anchors.margins: 16
spacing: 7
RowLayout {
Layout.fillWidth: true
Rectangle {
Layout.preferredWidth: 38
Layout.preferredHeight: 38
radius: 11
color: "#eff6ff"
Kirigami.Icon {
anchors.centerIn: parent
source: modelData.icon
implicitWidth: 21
implicitHeight: 21
color: "#2563eb"
}
}
Item { Layout.fillWidth: true }
Rectangle {
Layout.preferredWidth: 25
Layout.preferredHeight: 25
radius: 13
color: "#dcfce7"
Kirigami.Icon {
anchors.centerIn: parent
source: "dialog-ok-apply"
implicitWidth: 15
implicitHeight: 15
color: "#16a34a"
}
}
}
Controls.Label {
text: modelData.title
color: "#0f172a"
font.pixelSize: 14
font.weight: Font.DemiBold
}
Controls.Label {
Layout.fillWidth: true
text: modelData.text
color: "#64748b"
font.pixelSize: 11
wrapMode: Text.WordWrap
}
}
} }
} }
} }
@@ -1021,7 +1202,9 @@ Kirigami.ApplicationWindow {
} }
PrimaryButton { PrimaryButton {
text: "Fermer l'aperçu" text: "Terminer"
enabled: root.workflowComplete
opacity: enabled ? 1.0 : 0.45
onClicked: Qt.quit() onClicked: Qt.quit()
} }
} }