Téléverser les fichiers vers "first-login-ui/src"
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
qt_add_executable(nixos-first-login-ui
|
||||
main.cpp
|
||||
)
|
||||
|
||||
qt_add_qml_module(nixos-first-login-ui
|
||||
URI org.raspot.nixosfirstlogin
|
||||
VERSION 1.0
|
||||
QML_FILES
|
||||
Main.qml
|
||||
)
|
||||
|
||||
target_link_libraries(nixos-first-login-ui
|
||||
PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Gui
|
||||
Qt6::Qml
|
||||
Qt6::Quick
|
||||
Qt6::QuickControls2
|
||||
)
|
||||
|
||||
install(
|
||||
TARGETS nixos-first-login-ui
|
||||
RUNTIME DESTINATION bin
|
||||
)
|
||||
@@ -0,0 +1,864 @@
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls as Controls
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
Kirigami.ApplicationWindow {
|
||||
id: root
|
||||
|
||||
title: "Configuration initiale du poste"
|
||||
visibility: Window.FullScreen
|
||||
color: "#eef2f7"
|
||||
|
||||
property int currentStep: 0
|
||||
property var steps: [
|
||||
{ "title": "Bienvenue", "subtitle": "Présentation" },
|
||||
{ "title": "Mot de passe", "subtitle": "Accès de secours" },
|
||||
{ "title": "YubiKey", "subtitle": "PIN personnel" },
|
||||
{ "title": "Terminé", "subtitle": "Poste prêt" }
|
||||
]
|
||||
|
||||
function goNext() {
|
||||
if (currentStep < steps.length - 1) {
|
||||
currentStep += 1
|
||||
}
|
||||
}
|
||||
|
||||
function goBack() {
|
||||
if (currentStep > 0) {
|
||||
currentStep -= 1
|
||||
}
|
||||
}
|
||||
|
||||
Shortcut {
|
||||
sequence: "Ctrl+Q"
|
||||
onActivated: Qt.quit()
|
||||
}
|
||||
|
||||
component PrimaryButton: Controls.Button {
|
||||
id: control
|
||||
implicitHeight: 48
|
||||
implicitWidth: 170
|
||||
leftPadding: 24
|
||||
rightPadding: 24
|
||||
|
||||
contentItem: Controls.Label {
|
||||
text: control.text
|
||||
font.pixelSize: 15
|
||||
font.weight: Font.DemiBold
|
||||
color: "white"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
radius: 12
|
||||
color: control.down ? "#1e40af" : control.hovered ? "#2563eb" : "#1d4ed8"
|
||||
Behavior on color { ColorAnimation { duration: 120 } }
|
||||
}
|
||||
}
|
||||
|
||||
component SecondaryButton: Controls.Button {
|
||||
id: control
|
||||
implicitHeight: 48
|
||||
implicitWidth: 140
|
||||
leftPadding: 22
|
||||
rightPadding: 22
|
||||
|
||||
contentItem: Controls.Label {
|
||||
text: control.text
|
||||
font.pixelSize: 15
|
||||
font.weight: Font.Medium
|
||||
color: "#334155"
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
radius: 12
|
||||
color: control.down ? "#dbe3ee" : control.hovered ? "#edf2f7" : "transparent"
|
||||
border.width: 1
|
||||
border.color: "#cbd5e1"
|
||||
}
|
||||
}
|
||||
|
||||
component SecretField: Controls.TextField {
|
||||
id: field
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 50
|
||||
echoMode: TextInput.Password
|
||||
passwordCharacter: "●"
|
||||
font.pixelSize: 15
|
||||
leftPadding: 16
|
||||
rightPadding: 16
|
||||
selectByMouse: true
|
||||
|
||||
background: Rectangle {
|
||||
radius: 10
|
||||
color: field.activeFocus ? "#ffffff" : "#f8fafc"
|
||||
border.width: field.activeFocus ? 2 : 1
|
||||
border.color: field.activeFocus ? "#2563eb" : "#cbd5e1"
|
||||
}
|
||||
}
|
||||
|
||||
component StepDot: Rectangle {
|
||||
required property int stepIndex
|
||||
|
||||
width: 34
|
||||
height: 34
|
||||
radius: 17
|
||||
color: stepIndex < root.currentStep ? "#22c55e"
|
||||
: stepIndex === root.currentStep ? "#ffffff"
|
||||
: "#17345f"
|
||||
border.width: stepIndex === root.currentStep ? 2 : 0
|
||||
border.color: "#93c5fd"
|
||||
|
||||
Controls.Label {
|
||||
anchors.centerIn: parent
|
||||
text: parent.stepIndex < root.currentStep ? "✓" : (parent.stepIndex + 1)
|
||||
color: parent.stepIndex === root.currentStep ? "#0f2b50" : "white"
|
||||
font.pixelSize: 14
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
// -----------------------------------------------------
|
||||
// Colonne gauche : identité + progression
|
||||
// -----------------------------------------------------
|
||||
Rectangle {
|
||||
Layout.preferredWidth: Math.max(330, root.width * 0.29)
|
||||
Layout.fillHeight: true
|
||||
|
||||
gradient: Gradient {
|
||||
GradientStop { position: 0.0; color: "#081a33" }
|
||||
GradientStop { position: 0.55; color: "#0b2a50" }
|
||||
GradientStop { position: 1.0; color: "#123d70" }
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 44
|
||||
spacing: 0
|
||||
|
||||
RowLayout {
|
||||
spacing: 14
|
||||
|
||||
Rectangle {
|
||||
width: 54
|
||||
height: 54
|
||||
radius: 16
|
||||
color: "#1d4ed8"
|
||||
|
||||
Controls.Label {
|
||||
anchors.centerIn: parent
|
||||
text: "N"
|
||||
color: "white"
|
||||
font.pixelSize: 25
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 2
|
||||
|
||||
Controls.Label {
|
||||
text: "NixOS Workstations"
|
||||
color: "white"
|
||||
font.pixelSize: 19
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
text: "Configuration du poste"
|
||||
color: "#a8c1e1"
|
||||
font.pixelSize: 13
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.preferredHeight: 70 }
|
||||
|
||||
Controls.Label {
|
||||
text: "VOTRE CONFIGURATION"
|
||||
color: "#7fa4d1"
|
||||
font.pixelSize: 11
|
||||
font.weight: Font.Bold
|
||||
font.letterSpacing: 1.5
|
||||
}
|
||||
|
||||
Item { Layout.preferredHeight: 26 }
|
||||
|
||||
Repeater {
|
||||
model: root.steps
|
||||
|
||||
delegate: Item {
|
||||
required property int index
|
||||
required property var modelData
|
||||
|
||||
Layout.fillWidth: true
|
||||
Layout.preferredHeight: 78
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 16
|
||||
|
||||
ColumnLayout {
|
||||
Layout.alignment: Qt.AlignTop
|
||||
spacing: 0
|
||||
|
||||
StepDot { stepIndex: index }
|
||||
|
||||
Rectangle {
|
||||
visible: index < root.steps.length - 1
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredWidth: 2
|
||||
Layout.preferredHeight: 36
|
||||
color: index < root.currentStep ? "#22c55e" : "#28517e"
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop
|
||||
spacing: 3
|
||||
|
||||
Controls.Label {
|
||||
text: modelData.title
|
||||
color: index === root.currentStep ? "white" : "#c1d3e8"
|
||||
font.pixelSize: 15
|
||||
font.weight: index === root.currentStep ? Font.DemiBold : Font.Medium
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
text: modelData.subtitle
|
||||
color: "#7699c2"
|
||||
font.pixelSize: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 86
|
||||
radius: 14
|
||||
color: "#102f55"
|
||||
border.width: 1
|
||||
border.color: "#214b78"
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 16
|
||||
spacing: 12
|
||||
|
||||
Controls.Label {
|
||||
text: "🛡"
|
||||
font.pixelSize: 23
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 3
|
||||
|
||||
Controls.Label {
|
||||
text: "Configuration locale"
|
||||
color: "white"
|
||||
font.pixelSize: 13
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
Layout.fillWidth: true
|
||||
text: "Les secrets définitifs ne seront pas enregistrés dans Git."
|
||||
wrapMode: Text.WordWrap
|
||||
color: "#91add0"
|
||||
font.pixelSize: 11
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.preferredHeight: 20 }
|
||||
|
||||
Controls.Label {
|
||||
text: "Ctrl+Q : quitter l'aperçu"
|
||||
color: "#6289b6"
|
||||
font.pixelSize: 11
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -----------------------------------------------------
|
||||
// Partie droite
|
||||
// -----------------------------------------------------
|
||||
Rectangle {
|
||||
id: contentArea
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
color: "#eef2f7"
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: Math.max(48, contentArea.width * 0.07)
|
||||
anchors.rightMargin: Math.max(48, contentArea.width * 0.07)
|
||||
anchors.topMargin: 34
|
||||
anchors.bottomMargin: 34
|
||||
spacing: 20
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
Rectangle {
|
||||
implicitWidth: previewLabel.implicitWidth + 28
|
||||
implicitHeight: 32
|
||||
radius: 16
|
||||
color: "#fff7ed"
|
||||
border.width: 1
|
||||
border.color: "#fed7aa"
|
||||
|
||||
Controls.Label {
|
||||
id: previewLabel
|
||||
anchors.centerIn: parent
|
||||
text: "MODE APERÇU — aucune modification système"
|
||||
color: "#c2410c"
|
||||
font.pixelSize: 11
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
SecondaryButton {
|
||||
text: "Quitter l'aperçu"
|
||||
onClicked: Qt.quit()
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
radius: 24
|
||||
color: "white"
|
||||
border.width: 1
|
||||
border.color: "#dce3ec"
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Math.max(34, Math.min(64, parent.width * 0.055))
|
||||
spacing: 24
|
||||
|
||||
// Petit indicateur de progression supérieur
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 10
|
||||
|
||||
Repeater {
|
||||
model: root.steps.length
|
||||
|
||||
Rectangle {
|
||||
required property int index
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 5
|
||||
radius: 3
|
||||
color: index <= root.currentStep ? "#2563eb" : "#e2e8f0"
|
||||
|
||||
Behavior on color { ColorAnimation { duration: 180 } }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StackLayout {
|
||||
id: wizard
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
currentIndex: root.currentStep
|
||||
|
||||
// ---------------------------------
|
||||
// 0 - Bienvenue
|
||||
// ---------------------------------
|
||||
Item {
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 20
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredWidth: 86
|
||||
Layout.preferredHeight: 86
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
radius: 24
|
||||
color: "#eff6ff"
|
||||
border.width: 1
|
||||
border.color: "#bfdbfe"
|
||||
|
||||
Controls.Label {
|
||||
anchors.centerIn: parent
|
||||
text: "✦"
|
||||
color: "#2563eb"
|
||||
font.pixelSize: 42
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
Layout.fillWidth: true
|
||||
text: "Bienvenue sur votre nouveau poste"
|
||||
color: "#0f172a"
|
||||
font.pixelSize: 32
|
||||
font.weight: Font.Bold
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
Layout.maximumWidth: 720
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: "Quelques étapes rapides vont personnaliser vos moyens d'authentification et finaliser la préparation de votre environnement de travail."
|
||||
wrapMode: Text.WordWrap
|
||||
color: "#64748b"
|
||||
font.pixelSize: 16
|
||||
lineHeight: 1.25
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Item { Layout.preferredHeight: 12 }
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: 14
|
||||
|
||||
Repeater {
|
||||
model: [
|
||||
{ "icon": "🔑", "title": "Mot de passe", "text": "Définir votre accès local de secours" },
|
||||
{ "icon": "🔐", "title": "YubiKey", "text": "Personnaliser votre PIN FIDO2" }
|
||||
]
|
||||
|
||||
Rectangle {
|
||||
required property var modelData
|
||||
Layout.preferredWidth: 290
|
||||
Layout.preferredHeight: 128
|
||||
radius: 16
|
||||
color: "#f8fafc"
|
||||
border.width: 1
|
||||
border.color: "#e2e8f0"
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 18
|
||||
spacing: 14
|
||||
|
||||
Controls.Label {
|
||||
text: modelData.icon
|
||||
font.pixelSize: 28
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 5
|
||||
|
||||
Controls.Label {
|
||||
text: modelData.title
|
||||
color: "#0f172a"
|
||||
font.pixelSize: 15
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
Layout.fillWidth: true
|
||||
text: modelData.text
|
||||
wrapMode: Text.WordWrap
|
||||
color: "#64748b"
|
||||
font.pixelSize: 12
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
PrimaryButton {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: "Commencer"
|
||||
onClicked: root.goNext()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------
|
||||
// 1 - Mot de passe
|
||||
// ---------------------------------
|
||||
Item {
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 18
|
||||
|
||||
Controls.Label {
|
||||
text: "Mot de passe local"
|
||||
color: "#0f172a"
|
||||
font.pixelSize: 29
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
Layout.maximumWidth: 720
|
||||
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 {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 70
|
||||
radius: 12
|
||||
color: "#eff6ff"
|
||||
border.width: 1
|
||||
border.color: "#bfdbfe"
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 16
|
||||
spacing: 12
|
||||
|
||||
Controls.Label {
|
||||
text: "ℹ"
|
||||
color: "#2563eb"
|
||||
font.pixelSize: 22
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
Layout.fillWidth: true
|
||||
text: "Aperçu uniquement : les champs ci-dessous ne modifient actuellement aucun mot de passe."
|
||||
wrapMode: Text.WordWrap
|
||||
color: "#1e40af"
|
||||
font.pixelSize: 13
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.preferredHeight: 4 }
|
||||
|
||||
Controls.Label {
|
||||
text: "Mot de passe temporaire actuel"
|
||||
color: "#334155"
|
||||
font.pixelSize: 13
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
|
||||
SecretField {
|
||||
id: currentPassword
|
||||
placeholderText: "Saisissez le mot de passe temporaire"
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
text: "Nouveau mot de passe"
|
||||
color: "#334155"
|
||||
font.pixelSize: 13
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
|
||||
SecretField {
|
||||
id: newPassword
|
||||
placeholderText: "Choisissez votre mot de passe personnel"
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
text: "Confirmation"
|
||||
color: "#334155"
|
||||
font.pixelSize: 13
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
|
||||
SecretField {
|
||||
id: confirmPassword
|
||||
placeholderText: "Confirmez votre nouveau mot de passe"
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
visible: confirmPassword.text.length > 0 && newPassword.text !== confirmPassword.text
|
||||
text: "Les deux nouveaux mots de passe ne correspondent pas."
|
||||
color: "#dc2626"
|
||||
font.pixelSize: 12
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
SecondaryButton {
|
||||
text: "Retour"
|
||||
onClicked: root.goBack()
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
PrimaryButton {
|
||||
text: "Continuer"
|
||||
enabled: newPassword.text.length > 0 && newPassword.text === confirmPassword.text
|
||||
opacity: enabled ? 1.0 : 0.45
|
||||
onClicked: root.goNext()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------
|
||||
// 2 - YubiKey
|
||||
// ---------------------------------
|
||||
Item {
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 18
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 20
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredWidth: 80
|
||||
Layout.preferredHeight: 80
|
||||
radius: 22
|
||||
color: "#f0fdf4"
|
||||
border.width: 1
|
||||
border.color: "#bbf7d0"
|
||||
|
||||
Controls.Label {
|
||||
anchors.centerIn: parent
|
||||
text: "🔐"
|
||||
font.pixelSize: 34
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 5
|
||||
|
||||
Controls.Label {
|
||||
text: "PIN de votre YubiKey"
|
||||
color: "#0f172a"
|
||||
font.pixelSize: 29
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
Layout.fillWidth: true
|
||||
text: "Remplacez le PIN temporaire de préparation par un PIN personnel que vous serez seul à connaître."
|
||||
wrapMode: Text.WordWrap
|
||||
color: "#64748b"
|
||||
font.pixelSize: 15
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 78
|
||||
radius: 12
|
||||
color: "#fffbeb"
|
||||
border.width: 1
|
||||
border.color: "#fde68a"
|
||||
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 16
|
||||
spacing: 12
|
||||
|
||||
Controls.Label {
|
||||
text: "⌨"
|
||||
color: "#b45309"
|
||||
font.pixelSize: 23
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
Layout.fillWidth: true
|
||||
text: "Vérifiez Verr Num avant d'utiliser le pavé numérique. En cas de doute, utilisez les chiffres situés au-dessus des lettres."
|
||||
wrapMode: Text.WordWrap
|
||||
color: "#92400e"
|
||||
font.pixelSize: 13
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
text: "PIN temporaire actuel"
|
||||
color: "#334155"
|
||||
font.pixelSize: 13
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
|
||||
SecretField {
|
||||
id: currentPin
|
||||
placeholderText: "PIN temporaire"
|
||||
inputMethodHints: Qt.ImhDigitsOnly
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
text: "Nouveau PIN"
|
||||
color: "#334155"
|
||||
font.pixelSize: 13
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
|
||||
SecretField {
|
||||
id: newPin
|
||||
placeholderText: "Nouveau PIN personnel"
|
||||
inputMethodHints: Qt.ImhDigitsOnly
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
text: "Confirmation du nouveau PIN"
|
||||
color: "#334155"
|
||||
font.pixelSize: 13
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
|
||||
SecretField {
|
||||
id: confirmPin
|
||||
placeholderText: "Confirmez le nouveau PIN"
|
||||
inputMethodHints: Qt.ImhDigitsOnly
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
visible: confirmPin.text.length > 0 && newPin.text !== confirmPin.text
|
||||
text: "Les deux nouveaux PIN ne correspondent pas."
|
||||
color: "#dc2626"
|
||||
font.pixelSize: 12
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
|
||||
SecondaryButton {
|
||||
text: "Retour"
|
||||
onClicked: root.goBack()
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true }
|
||||
|
||||
PrimaryButton {
|
||||
text: "Finaliser"
|
||||
enabled: newPin.text.length >= 4 && newPin.text === confirmPin.text
|
||||
opacity: enabled ? 1.0 : 0.45
|
||||
onClicked: root.goNext()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------------------------
|
||||
// 3 - Terminé
|
||||
// ---------------------------------
|
||||
Item {
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 18
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredWidth: 96
|
||||
Layout.preferredHeight: 96
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
radius: 48
|
||||
color: "#dcfce7"
|
||||
border.width: 1
|
||||
border.color: "#86efac"
|
||||
|
||||
Controls.Label {
|
||||
anchors.centerIn: parent
|
||||
text: "✓"
|
||||
color: "#16a34a"
|
||||
font.pixelSize: 50
|
||||
font.weight: Font.Bold
|
||||
}
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
Layout.fillWidth: true
|
||||
text: "Votre poste est prêt"
|
||||
color: "#0f172a"
|
||||
font.pixelSize: 32
|
||||
font.weight: Font.Bold
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Controls.Label {
|
||||
Layout.maximumWidth: 680
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: "Dans la version finale, cet écran confirmera que votre mot de passe local et le PIN de votre YubiKey ont été personnalisés avec succès."
|
||||
wrapMode: Text.WordWrap
|
||||
color: "#64748b"
|
||||
font.pixelSize: 15
|
||||
lineHeight: 1.25
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Item { Layout.preferredHeight: 12 }
|
||||
|
||||
Rectangle {
|
||||
Layout.preferredWidth: 520
|
||||
Layout.preferredHeight: 112
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
radius: 16
|
||||
color: "#f8fafc"
|
||||
border.width: 1
|
||||
border.color: "#e2e8f0"
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
anchors.margins: 18
|
||||
spacing: 10
|
||||
|
||||
RowLayout {
|
||||
Controls.Label { text: "✓"; color: "#16a34a"; font.pixelSize: 17; font.weight: Font.Bold }
|
||||
Controls.Label { text: "Mot de passe personnel configuré"; color: "#334155"; font.pixelSize: 14 }
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Controls.Label { text: "✓"; color: "#16a34a"; font.pixelSize: 17; font.weight: Font.Bold }
|
||||
Controls.Label { text: "PIN personnel YubiKey configuré"; color: "#334155"; font.pixelSize: 14 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { Layout.fillHeight: true }
|
||||
|
||||
RowLayout {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
spacing: 12
|
||||
|
||||
SecondaryButton {
|
||||
text: "Revoir"
|
||||
onClicked: root.currentStep = 0
|
||||
}
|
||||
|
||||
PrimaryButton {
|
||||
text: "Fermer l'aperçu"
|
||||
onClicked: Qt.quit()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#include <QGuiApplication>
|
||||
#include <QIcon>
|
||||
#include <QQmlApplicationEngine>
|
||||
#include <QQuickStyle>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QGuiApplication app(argc, argv);
|
||||
|
||||
QGuiApplication::setOrganizationName(QStringLiteral("Raspot"));
|
||||
QGuiApplication::setOrganizationDomain(QStringLiteral("raspot.in"));
|
||||
QGuiApplication::setApplicationName(QStringLiteral("NixOS First Login"));
|
||||
QGuiApplication::setDesktopFileName(QStringLiteral("org.raspot.nixosfirstlogin"));
|
||||
|
||||
QIcon::setThemeName(QStringLiteral("breeze"));
|
||||
|
||||
// L'UI est volontairement dessinée par l'application afin d'avoir
|
||||
// un rendu stable et homogène, tout en restant une application Qt/QML.
|
||||
if (qEnvironmentVariableIsEmpty("QT_QUICK_CONTROLS_STYLE")) {
|
||||
QQuickStyle::setStyle(QStringLiteral("Basic"));
|
||||
}
|
||||
|
||||
QQmlApplicationEngine engine;
|
||||
engine.loadFromModule(QStringLiteral("org.raspot.nixosfirstlogin"),
|
||||
QStringLiteral("Main"));
|
||||
|
||||
if (engine.rootObjects().isEmpty()) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
Reference in New Issue
Block a user