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