From c4f72ded99b00a664ac2a034ac6e35b56f5fcfb5 Mon Sep 17 00:00:00 2001 From: johndoe6345789 Date: Thu, 19 Mar 2026 09:26:42 +0000 Subject: [PATCH] =?UTF-8?q?feat(qt6):=20MD3=20rework=20all=20views=20?= =?UTF-8?q?=E2=80=94=20Dashboard,=20Profile,=20Admin,=20SuperGod,=20Commen?= =?UTF-8?q?ts,=20Settings?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix CCard content nesting (no anchors.fill inside CCard) - chipColor/badgeColor string→Theme color fixes - anchors-in-layout warnings resolved - Tonal surfaces, proper MD3 spacing - CButton replaces hand-rolled Rectangle buttons - All 6 views preserved with full functionality Co-Authored-By: Claude Opus 4.6 (1M context) --- frontends/qt6/AdminView.qml | 53 ++-- frontends/qt6/CommentsView.qml | 210 ++++++------ frontends/qt6/DashboardView.qml | 196 +++++++----- frontends/qt6/ProfileView.qml | 410 ++++++++++++++---------- frontends/qt6/SettingsView.qml | 547 +++++++++++++++----------------- frontends/qt6/SuperGodPanel.qml | 504 ++++++++++++++--------------- 6 files changed, 986 insertions(+), 934 deletions(-) diff --git a/frontends/qt6/AdminView.qml b/frontends/qt6/AdminView.qml index 7aca27a84..7a7a65059 100644 --- a/frontends/qt6/AdminView.qml +++ b/frontends/qt6/AdminView.qml @@ -6,7 +6,7 @@ import "qmllib/dbal" Rectangle { id: root - color: "transparent" + color: Theme.background // ── DBAL connection ────────────────────────────────────────── DBALProvider { id: dbal } @@ -239,7 +239,6 @@ Rectangle { function deleteRecord(idx) { var data = records[selectedEntity].slice(); - var filtered = getFilteredRecords(); var actualRec = getPagedRecords()[idx]; for (var i = 0; i < data.length; i++) { if (data[i].id === actualRec.id) { @@ -326,10 +325,9 @@ Rectangle { // ── Stats bar ────────────────────────────────────────────── Rectangle { Layout.fillWidth: true - Layout.preferredHeight: 80 - color: Theme.paper - border.color: Theme.border - border.width: 1 + Layout.preferredHeight: 88 + color: Theme.surface + radius: 0 RowLayout { anchors.fill: parent @@ -349,8 +347,7 @@ Rectangle { Layout.fillHeight: true RowLayout { - anchors.fill: parent - anchors.margins: 12 + Layout.fillWidth: true spacing: 8 Rectangle { @@ -382,9 +379,7 @@ Rectangle { Rectangle { Layout.preferredWidth: 220 Layout.fillHeight: true - color: Theme.paper - border.color: Theme.border - border.width: 1 + color: Theme.surface ColumnLayout { anchors.fill: parent @@ -418,7 +413,7 @@ Rectangle { Rectangle { Layout.fillWidth: true Layout.fillHeight: true - color: "transparent" + color: Theme.background ColumnLayout { anchors.fill: parent @@ -466,6 +461,7 @@ Rectangle { delegate: CChip { text: modelData checked: activeFilter === modelData + chipColor: activeFilter === modelData ? Theme.primary : Theme.surface onClicked: { activeFilter = modelData; currentPage = 0; } } } @@ -487,15 +483,15 @@ Rectangle { Layout.fillHeight: true ColumnLayout { - anchors.fill: parent - anchors.margins: 1 + Layout.fillWidth: true spacing: 0 // ── Column headers ──────────────────── Rectangle { Layout.fillWidth: true height: 44 - color: Theme.surface + color: Theme.surfaceVariant + radius: 0 RowLayout { anchors.fill: parent @@ -557,8 +553,9 @@ Rectangle { color: { if (selectedRow === rowIndex) return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12); if (selectedRows[rowIndex]) return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.06); - return rowIndex % 2 === 0 ? "transparent" : Qt.rgba(Theme.surface.r, Theme.surface.g, Theme.surface.b, 0.4); + return rowIndex % 2 === 0 ? "transparent" : Theme.surfaceVariant; } + radius: 0 MouseArea { anchors.fill: parent @@ -638,11 +635,24 @@ Rectangle { visible: totalFiltered() === 0 Layout.preferredHeight: visible ? 120 : 0 - CText { + ColumnLayout { anchors.centerIn: parent - variant: "body1" - text: "No records found" - color: Theme.textSecondary + spacing: 8 + + CText { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + variant: "h4" + text: "No records found" + color: Theme.textSecondary + } + CText { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + variant: "caption" + text: "Try adjusting your search or filter criteria." + color: Theme.textMuted + } } } @@ -652,7 +662,8 @@ Rectangle { Rectangle { Layout.fillWidth: true height: 48 - color: Theme.surface + color: Theme.surfaceVariant + radius: 0 RowLayout { anchors.fill: parent diff --git a/frontends/qt6/CommentsView.qml b/frontends/qt6/CommentsView.qml index 51ef4fe3d..36e9f8613 100644 --- a/frontends/qt6/CommentsView.qml +++ b/frontends/qt6/CommentsView.qml @@ -168,6 +168,7 @@ Rectangle { CBadge { text: commentsModel.count + " comments" + badgeColor: Theme.info } Item { Layout.fillWidth: true } @@ -186,32 +187,26 @@ Rectangle { CCard { Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - anchors.margins: 20 - spacing: 12 + CText { variant: "subtitle1"; text: "Post a Comment" } - CText { variant: "subtitle1"; text: "Post a Comment" } + CTextField { + Layout.fillWidth: true + label: "Your comment" + placeholderText: "Write your thoughts..." + text: newCommentText + onTextChanged: newCommentText = text + } - CTextField { - Layout.fillWidth: true - label: "Your comment" - placeholderText: "Write your thoughts..." - text: newCommentText - onTextChanged: newCommentText = text - } - - FlexRow { - Layout.fillWidth: true - spacing: 8 - Item { Layout.fillWidth: true } - CButton { - text: "Post Comment" - variant: "primary" - size: "sm" - enabled: newCommentText.trim().length > 0 - onClicked: addComment() - } + FlexRow { + Layout.fillWidth: true + spacing: 8 + Item { Layout.fillWidth: true } + CButton { + text: "Post Comment" + variant: "primary" + size: "sm" + enabled: newCommentText.trim().length > 0 + onClicked: addComment() } } } @@ -223,88 +218,83 @@ Rectangle { delegate: CCard { Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - anchors.margins: 20 - spacing: 10 + // Comment header: avatar, user, time + FlexRow { + Layout.fillWidth: true + spacing: 12 - // Comment header: avatar, user, time - FlexRow { + CAvatar { + initials: model.initials + } + + ColumnLayout { Layout.fillWidth: true - spacing: 12 - - CAvatar { - initials: model.initials - } - - ColumnLayout { - Layout.fillWidth: true - spacing: 2 - - FlexRow { - spacing: 8 - CText { - variant: "subtitle1" - text: model.username - } - CChip { - text: model.username === appWindow.currentUser ? "You" : "" - visible: model.username === appWindow.currentUser - } - } + spacing: 2 + FlexRow { + spacing: 8 CText { - variant: "caption" - text: model.timestamp + variant: "subtitle1" + text: model.username } - } - } - - // Comment body - CText { - Layout.fillWidth: true - variant: "body1" - text: model.body - wrapMode: Text.WordWrap - } - - CDivider { Layout.fillWidth: true } - - // Actions row - FlexRow { - Layout.fillWidth: true - spacing: 8 - - CButton { - text: model.liked ? "Liked (" + model.likes + ")" : "Like (" + model.likes + ")" - variant: model.liked ? "primary" : "ghost" - size: "sm" - onClicked: { - var newLikes; - if (model.liked) { - newLikes = model.likes - 1; - commentsModel.setProperty(index, "likes", newLikes) - commentsModel.setProperty(index, "liked", false) - } else { - newLikes = model.likes + 1; - commentsModel.setProperty(index, "likes", newLikes) - commentsModel.setProperty(index, "liked", true) - } - likeCommentOnDBAL(model.commentId, newLikes) + CChip { + text: model.username === appWindow.currentUser ? "You" : "" + chipColor: Theme.primary + visible: model.username === appWindow.currentUser } } - Item { Layout.fillWidth: true } + CText { + variant: "caption" + text: model.timestamp + } + } + } - CButton { - text: "Delete" - variant: "danger" - size: "sm" - visible: canDelete(model.username) - onClicked: { - deleteCommentOnDBAL(model.commentId) - commentsModel.remove(index) + // Comment body + CText { + Layout.fillWidth: true + variant: "body1" + text: model.body + wrapMode: Text.WordWrap + } + + CDivider { Layout.fillWidth: true } + + // Actions row + FlexRow { + Layout.fillWidth: true + spacing: 8 + + CButton { + text: model.liked ? "Liked (" + model.likes + ")" : "Like (" + model.likes + ")" + variant: model.liked ? "primary" : "ghost" + size: "sm" + onClicked: { + var newLikes; + if (model.liked) { + newLikes = model.likes - 1; + commentsModel.setProperty(index, "likes", newLikes) + commentsModel.setProperty(index, "liked", false) + } else { + newLikes = model.likes + 1; + commentsModel.setProperty(index, "likes", newLikes) + commentsModel.setProperty(index, "liked", true) } + likeCommentOnDBAL(model.commentId, newLikes) + } + } + + Item { Layout.fillWidth: true } + + CButton { + text: "Delete" + variant: "danger" + size: "sm" + visible: canDelete(model.username) + onClicked: { + deleteCommentOnDBAL(model.commentId) + commentsModel.remove(index) } } } @@ -316,22 +306,22 @@ Rectangle { Layout.fillWidth: true visible: commentsModel.count === 0 - ColumnLayout { - anchors.fill: parent - anchors.margins: 40 - spacing: 12 + Item { Layout.preferredHeight: 24 } - CText { - Layout.alignment: Qt.AlignHCenter - variant: "h4" - text: "No comments yet" - } - CText { - Layout.alignment: Qt.AlignHCenter - variant: "body2" - text: "Be the first to start the discussion!" - } + CText { + Layout.fillWidth: true + variant: "h4" + text: "No comments yet" + horizontalAlignment: Text.AlignHCenter } + CText { + Layout.fillWidth: true + variant: "body2" + text: "Be the first to start the discussion!" + horizontalAlignment: Text.AlignHCenter + } + + Item { Layout.preferredHeight: 24 } } // Bottom spacer diff --git a/frontends/qt6/DashboardView.qml b/frontends/qt6/DashboardView.qml index 9ed7b16ce..f13e90ce6 100644 --- a/frontends/qt6/DashboardView.qml +++ b/frontends/qt6/DashboardView.qml @@ -6,7 +6,7 @@ import "qmllib/dbal" Rectangle { id: dashRoot - color: "transparent" + color: Theme.background // ── DBAL connection ────────────────────────────────────────── DBALProvider { id: dbal } @@ -14,6 +14,13 @@ Rectangle { property var healthData: ({}) property bool dbalOnline: dbal.connected + // ── MD3 palette ────────────────────────────────────────────── + readonly property bool isDark: Theme.mode === "dark" + readonly property color surfaceContainer: isDark ? Qt.rgba(1, 1, 1, 0.05) : Qt.rgba(0.31, 0.31, 0.44, 0.06) + readonly property color surfaceContainerHigh: isDark ? Qt.rgba(1, 1, 1, 0.08) : Qt.rgba(0.31, 0.31, 0.44, 0.10) + readonly property color onSurface: Theme.text + readonly property color onSurfaceVariant: Theme.textSecondary + function refreshDBAL() { dbal.ping(function(success, error) { if (success) { @@ -28,43 +35,55 @@ Rectangle { ScrollView { anchors.fill: parent - anchors.margins: 24 clip: true + contentWidth: availableWidth ColumnLayout { width: parent.width - spacing: 20 + spacing: 0 - // Welcome header + Item { Layout.preferredHeight: 24 } + + // ── Welcome header ─────────────────────────────────── CCard { Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - anchors.margins: 24 - spacing: 12 + Layout.leftMargin: 24 + Layout.rightMargin: 24 + variant: "filled" - CText { - variant: "h3" - text: "Welcome back, " + appWindow.currentUser - } - CText { - variant: "body1" - text: "Level " + appWindow.currentLevel + " \u00b7 " + appWindow.currentRole + " access" - } + CText { + Layout.fillWidth: true + variant: "h3" + text: "Welcome back, " + appWindow.currentUser + } - CButton { - text: dbal.loading ? "Refreshing..." : "Refresh" - variant: "ghost" - size: "sm" - enabled: !dbal.loading - onClicked: refreshDBAL() - } + Item { Layout.preferredHeight: 4 } + + CText { + Layout.fillWidth: true + variant: "body1" + text: "Level " + appWindow.currentLevel + " \u00b7 " + appWindow.currentRole + " access" + color: onSurfaceVariant + } + + Item { Layout.preferredHeight: 12 } + + CButton { + text: dbal.loading ? "Refreshing..." : "Refresh" + variant: "ghost" + size: "sm" + enabled: !dbal.loading + onClicked: refreshDBAL() } } - // Stats row + Item { Layout.preferredHeight: 16 } + + // ── Stats row ──────────────────────────────────────── FlexRow { Layout.fillWidth: true + Layout.leftMargin: 24 + Layout.rightMargin: 24 spacing: 16 Repeater { @@ -76,67 +95,100 @@ Rectangle { ] delegate: CCard { Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - anchors.margins: 16 - spacing: 8 - CText { variant: "caption"; text: modelData.title } - CText { variant: "h3"; text: modelData.value } - CStatusBadge { status: modelData.status; text: modelData.status === "success" ? "Online" : "Active" } - } - } - } - } + variant: "outlined" - // Recent activity - CCard { - Layout.fillWidth: true - title: "Recent Activity" - - ColumnLayout { - anchors.fill: parent - anchors.margins: 16 - spacing: 8 - - CText { variant: "h4"; text: "Recent Activity" } - CDivider { Layout.fillWidth: true } - - Repeater { - model: [ - { action: "Package installed", detail: "material_ui v2.1.0", time: "2 min ago" }, - { action: "User logged in", detail: "admin", time: "5 min ago" }, - { action: "Workflow executed", detail: "on_user_created", time: "12 min ago" }, - { action: "Schema updated", detail: "forum entity", time: "1 hr ago" }, - { action: "Seed data loaded", detail: "5 namespaces", time: "2 hr ago" } - ] - delegate: CListItem { + CText { Layout.fillWidth: true - title: modelData.action - subtitle: modelData.detail + " \u00b7 " + modelData.time + variant: "caption" + text: modelData.title + color: onSurfaceVariant + } + + Item { Layout.preferredHeight: 4 } + + CText { + Layout.fillWidth: true + variant: "h3" + text: modelData.value + } + + Item { Layout.preferredHeight: 8 } + + CStatusBadge { + status: modelData.status + text: modelData.status === "success" ? "Online" : "Active" } } } } - // Quick actions + Item { Layout.preferredHeight: 16 } + + // ── Recent activity ────────────────────────────────── CCard { Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - anchors.margins: 16 - spacing: 12 - CText { variant: "h4"; text: "Quick Actions" } - FlexRow { + Layout.leftMargin: 24 + Layout.rightMargin: 24 + variant: "filled" + + CText { + Layout.fillWidth: true + variant: "h4" + text: "Recent Activity" + } + + Item { Layout.preferredHeight: 8 } + + CDivider { Layout.fillWidth: true } + + Item { Layout.preferredHeight: 8 } + + Repeater { + model: [ + { action: "Package installed", detail: "material_ui v2.1.0", time: "2 min ago" }, + { action: "User logged in", detail: "admin", time: "5 min ago" }, + { action: "Workflow executed", detail: "on_user_created", time: "12 min ago" }, + { action: "Schema updated", detail: "forum entity", time: "1 hr ago" }, + { action: "Seed data loaded", detail: "5 namespaces", time: "2 hr ago" } + ] + delegate: CListItem { Layout.fillWidth: true - spacing: 10 - CButton { text: "Forum"; variant: "default"; onClicked: appWindow.currentView = "forum" } - CButton { text: "Gallery"; variant: "default"; onClicked: appWindow.currentView = "gallery" } - CButton { text: "Guestbook"; variant: "default"; onClicked: appWindow.currentView = "guestbook" } - CButton { text: "Blog"; variant: "default"; onClicked: appWindow.currentView = "blog" } - CButton { text: "Profile"; variant: "ghost"; onClicked: appWindow.currentView = "profile" } + title: modelData.action + subtitle: modelData.detail + " \u00b7 " + modelData.time } } } + + Item { Layout.preferredHeight: 16 } + + // ── Quick actions ──────────────────────────────────── + CCard { + Layout.fillWidth: true + Layout.leftMargin: 24 + Layout.rightMargin: 24 + variant: "filled" + + CText { + Layout.fillWidth: true + variant: "h4" + text: "Quick Actions" + } + + Item { Layout.preferredHeight: 12 } + + FlexRow { + Layout.fillWidth: true + spacing: 10 + CButton { text: "Forum"; variant: "default"; onClicked: appWindow.currentView = "forum" } + CButton { text: "Gallery"; variant: "default"; onClicked: appWindow.currentView = "gallery" } + CButton { text: "Guestbook"; variant: "default"; onClicked: appWindow.currentView = "guestbook" } + CButton { text: "Blog"; variant: "default"; onClicked: appWindow.currentView = "blog" } + CButton { text: "Profile"; variant: "ghost"; onClicked: appWindow.currentView = "profile" } + } + } + + // Bottom spacer + Item { Layout.preferredHeight: 24 } } } } diff --git a/frontends/qt6/ProfileView.qml b/frontends/qt6/ProfileView.qml index d14e09e8d..a1bc7df91 100644 --- a/frontends/qt6/ProfileView.qml +++ b/frontends/qt6/ProfileView.qml @@ -5,12 +5,20 @@ import QmlComponents 1.0 import "qmllib/dbal" Rectangle { - color: "transparent" + id: profileRoot + color: Theme.background - // ── DBAL connection ── + // ── DBAL connection ────────────────────────────────────────── DBALProvider { id: dbal } - // ── Mock fallback data ── + // ── MD3 palette ────────────────────────────────────────────── + readonly property bool isDark: Theme.mode === "dark" + readonly property color surfaceContainer: isDark ? Qt.rgba(1, 1, 1, 0.05) : Qt.rgba(0.31, 0.31, 0.44, 0.06) + readonly property color surfaceContainerHigh: isDark ? Qt.rgba(1, 1, 1, 0.08) : Qt.rgba(0.31, 0.31, 0.44, 0.10) + readonly property color onSurface: Theme.text + readonly property color onSurfaceVariant: Theme.textSecondary + + // ── Mock fallback data ─────────────────────────────────────── property string mockBio: "MetaBuilder enthusiast and open-source contributor." property string mockEmail: "demo@metabuilder.io" @@ -23,7 +31,7 @@ Rectangle { property bool saving: false property string saveStatus: "" - // ── DBAL data loading ── + // ── DBAL data loading ──────────────────────────────────────── function loadProfile() { if (!appWindow.currentUser) return; dbal.read("user", appWindow.currentUser, function(result, error) { @@ -91,241 +99,303 @@ Rectangle { ScrollView { anchors.fill: parent - anchors.margins: 24 clip: true + contentWidth: availableWidth ColumnLayout { width: parent.width - spacing: 20 + spacing: 0 - // Profile card + Item { Layout.preferredHeight: 24 } + + // ── Profile header card ────────────────────────────── CCard { Layout.fillWidth: true + Layout.leftMargin: 24 + Layout.rightMargin: 24 + variant: "filled" - ColumnLayout { - anchors.fill: parent - anchors.margins: 24 + FlexRow { + Layout.fillWidth: true spacing: 16 - FlexRow { + CAvatar { + initials: userInitials() + } + + ColumnLayout { Layout.fillWidth: true - spacing: 16 + spacing: 6 - CAvatar { - initials: userInitials() - } - - ColumnLayout { + CText { Layout.fillWidth: true - spacing: 6 + variant: "h3" + text: appWindow.currentUser + } - CText { - variant: "h3" - text: appWindow.currentUser - } - CText { - variant: "body2" - text: userEmail - color: Theme.text - opacity: 0.7 - } + CText { + Layout.fillWidth: true + variant: "body2" + text: userEmail + color: onSurfaceVariant + } - FlexRow { - spacing: 8 - CBadge { text: appWindow.currentRole } - CBadge { text: "Level 2" } - } + FlexRow { + spacing: 8 + CBadge { text: appWindow.currentRole; badgeColor: Theme.primary } + CBadge { text: "Level 2"; badgeColor: Theme.info } + } - CText { - variant: "caption" - text: "Member since January 15, 2026" - } + CText { + Layout.fillWidth: true + variant: "caption" + text: "Member since January 15, 2026" + color: onSurfaceVariant } } } } - // Activity summary + Item { Layout.preferredHeight: 16 } + + // ── Activity summary ───────────────────────────────── CCard { Layout.fillWidth: true + Layout.leftMargin: 24 + Layout.rightMargin: 24 + variant: "filled" - ColumnLayout { - anchors.fill: parent - anchors.margins: 20 - spacing: 12 + CText { + Layout.fillWidth: true + variant: "h4" + text: "Activity Summary" + } - CText { variant: "h4"; text: "Activity Summary" } - CDivider { Layout.fillWidth: true } + Item { Layout.preferredHeight: 8 } - FlexRow { - Layout.fillWidth: true - spacing: 16 + CDivider { Layout.fillWidth: true } - Repeater { - model: [ - { label: "Posts", value: "42" }, - { label: "Comments", value: "128" }, - { label: "Last Login", value: "Today, 09:15" } - ] - delegate: CCard { + Item { Layout.preferredHeight: 12 } + + FlexRow { + Layout.fillWidth: true + spacing: 16 + + Repeater { + model: [ + { label: "Posts", value: "42" }, + { label: "Comments", value: "128" }, + { label: "Last Login", value: "Today, 09:15" } + ] + delegate: CCard { + Layout.fillWidth: true + variant: "outlined" + + CText { Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - anchors.margins: 16 - spacing: 6 - CText { variant: "caption"; text: modelData.label } - CText { variant: "h4"; text: modelData.value } - } + variant: "caption" + text: modelData.label + color: onSurfaceVariant + } + + Item { Layout.preferredHeight: 4 } + + CText { + Layout.fillWidth: true + variant: "h4" + text: modelData.value } } } } } - // Edit profile section + Item { Layout.preferredHeight: 16 } + + // ── Edit profile ───────────────────────────────────── CCard { Layout.fillWidth: true + Layout.leftMargin: 24 + Layout.rightMargin: 24 + variant: "filled" - ColumnLayout { - anchors.fill: parent - anchors.margins: 20 - spacing: 14 + CText { + Layout.fillWidth: true + variant: "h4" + text: "Edit Profile" + } - CText { variant: "h4"; text: "Edit Profile" } - CDivider { Layout.fillWidth: true } + Item { Layout.preferredHeight: 8 } - CTextField { - Layout.fillWidth: true - label: "Display Name" - placeholderText: "Enter display name" - text: userDisplayName - onTextChanged: userDisplayName = text - } + CDivider { Layout.fillWidth: true } - CTextField { - Layout.fillWidth: true - label: "Email" - placeholderText: "Enter email address" - text: userEmail - onTextChanged: userEmail = text - } + Item { Layout.preferredHeight: 14 } - CTextField { - Layout.fillWidth: true - label: "Bio" - placeholderText: "Tell us about yourself..." - text: userBio - onTextChanged: userBio = text - } + CTextField { + Layout.fillWidth: true + label: "Display Name" + placeholderText: "Enter display name" + text: userDisplayName + onTextChanged: userDisplayName = text + } + + Item { Layout.preferredHeight: 14 } + + CTextField { + Layout.fillWidth: true + label: "Email" + placeholderText: "Enter email address" + text: userEmail + onTextChanged: userEmail = text + } + + Item { Layout.preferredHeight: 14 } + + CTextField { + Layout.fillWidth: true + label: "Bio" + placeholderText: "Tell us about yourself..." + text: userBio + onTextChanged: userBio = text } } - // Change password section + Item { Layout.preferredHeight: 16 } + + // ── Change password ─────────────────────────────────── CCard { Layout.fillWidth: true + Layout.leftMargin: 24 + Layout.rightMargin: 24 + variant: "filled" - ColumnLayout { - anchors.fill: parent - anchors.margins: 20 - spacing: 14 + CText { + Layout.fillWidth: true + variant: "h4" + text: "Change Password" + } - CText { variant: "h4"; text: "Change Password" } - CDivider { Layout.fillWidth: true } + Item { Layout.preferredHeight: 8 } - CTextField { - Layout.fillWidth: true - label: "Current Password" - placeholderText: "Enter current password" - echoMode: TextInput.Password - text: currentPassword - onTextChanged: currentPassword = text - } + CDivider { Layout.fillWidth: true } - CTextField { - Layout.fillWidth: true - label: "New Password" - placeholderText: "Enter new password" - echoMode: TextInput.Password - text: newPassword - onTextChanged: newPassword = text - } + Item { Layout.preferredHeight: 14 } - CTextField { - Layout.fillWidth: true - label: "Confirm New Password" - placeholderText: "Re-enter new password" - echoMode: TextInput.Password - text: confirmPassword - onTextChanged: confirmPassword = text - } + CTextField { + Layout.fillWidth: true + label: "Current Password" + placeholderText: "Enter current password" + echoMode: TextInput.Password + text: currentPassword + onTextChanged: currentPassword = text + } - CAlert { - Layout.fillWidth: true - severity: "info" - text: "Passwords must be at least 8 characters with uppercase, lowercase, and a number." - visible: newPassword.length > 0 - } + Item { Layout.preferredHeight: 14 } - CAlert { - Layout.fillWidth: true - severity: "error" - text: "Passwords do not match." - visible: confirmPassword.length > 0 && newPassword !== confirmPassword - } + CTextField { + Layout.fillWidth: true + label: "New Password" + placeholderText: "Enter new password" + echoMode: TextInput.Password + text: newPassword + onTextChanged: newPassword = text + } + + Item { Layout.preferredHeight: 14 } + + CTextField { + Layout.fillWidth: true + label: "Confirm New Password" + placeholderText: "Re-enter new password" + echoMode: TextInput.Password + text: confirmPassword + onTextChanged: confirmPassword = text + } + + Item { Layout.preferredHeight: 14 } + + CAlert { + Layout.fillWidth: true + severity: "info" + text: "Passwords must be at least 8 characters with uppercase, lowercase, and a number." + visible: newPassword.length > 0 + } + + CAlert { + Layout.fillWidth: true + severity: "error" + text: "Passwords do not match." + visible: confirmPassword.length > 0 && newPassword !== confirmPassword } } - // Connected accounts + Item { Layout.preferredHeight: 16 } + + // ── Connected accounts ──────────────────────────────── CCard { Layout.fillWidth: true + Layout.leftMargin: 24 + Layout.rightMargin: 24 + variant: "filled" - ColumnLayout { - anchors.fill: parent - anchors.margins: 20 - spacing: 12 + CText { + Layout.fillWidth: true + variant: "h4" + text: "Connected Accounts" + } - CText { variant: "h4"; text: "Connected Accounts" } - CDivider { Layout.fillWidth: true } + Item { Layout.preferredHeight: 8 } - CListItem { - Layout.fillWidth: true - title: "GitHub" - subtitle: "Linked as @" + appWindow.currentUser - leadingIcon: "github" - } + CDivider { Layout.fillWidth: true } - FlexRow { - Layout.fillWidth: true - Layout.leftMargin: 12 - spacing: 8 - CStatusBadge { status: "success"; text: "Connected" } - Item { Layout.fillWidth: true } - CButton { text: "Unlink"; variant: "ghost"; size: "sm" } - } + Item { Layout.preferredHeight: 8 } - CDivider { Layout.fillWidth: true } + CListItem { + Layout.fillWidth: true + title: "GitHub" + subtitle: "Linked as @" + appWindow.currentUser + leadingIcon: "github" + } - CListItem { - Layout.fillWidth: true - title: "Discord" - subtitle: "Not linked" - leadingIcon: "discord" - } + FlexRow { + Layout.fillWidth: true + Layout.leftMargin: 12 + spacing: 8 + CStatusBadge { status: "success"; text: "Connected" } + Item { Layout.fillWidth: true } + CButton { text: "Unlink"; variant: "ghost"; size: "sm" } + } - FlexRow { - Layout.fillWidth: true - Layout.leftMargin: 12 - spacing: 8 - CStatusBadge { status: "warning"; text: "Not Connected" } - Item { Layout.fillWidth: true } - CButton { text: "Link Account"; variant: "primary"; size: "sm" } - } + Item { Layout.preferredHeight: 8 } + + CDivider { Layout.fillWidth: true } + + Item { Layout.preferredHeight: 8 } + + CListItem { + Layout.fillWidth: true + title: "Discord" + subtitle: "Not linked" + leadingIcon: "discord" + } + + FlexRow { + Layout.fillWidth: true + Layout.leftMargin: 12 + spacing: 8 + CStatusBadge { status: "warning"; text: "Not Connected" } + Item { Layout.fillWidth: true } + CButton { text: "Link Account"; variant: "primary"; size: "sm" } } } - // Save button + Item { Layout.preferredHeight: 16 } + + // ── Save button ────────────────────────────────────── FlexRow { Layout.fillWidth: true + Layout.leftMargin: 24 + Layout.rightMargin: 24 spacing: 12 Item { Layout.fillWidth: true } CButton { @@ -337,7 +407,7 @@ Rectangle { } // Bottom spacer - Item { Layout.preferredHeight: 20 } + Item { Layout.preferredHeight: 24 } } } } diff --git a/frontends/qt6/SettingsView.qml b/frontends/qt6/SettingsView.qml index 13d5cc193..ef99a04b1 100644 --- a/frontends/qt6/SettingsView.qml +++ b/frontends/qt6/SettingsView.qml @@ -180,85 +180,70 @@ Rectangle { CCard { Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - anchors.margins: 20 + CText { variant: "h4"; text: "Profile" } + CDivider { Layout.fillWidth: true } + + FlexRow { + Layout.fillWidth: true + Layout.topMargin: 4 spacing: 16 - CText { variant: "h4"; text: "Profile" } - CDivider { Layout.fillWidth: true } - - FlexRow { - Layout.fillWidth: true - spacing: 16 - - // Avatar - Rectangle { - width: 64 - height: 64 - radius: 32 - color: Theme.primary - Layout.alignment: Qt.AlignTop - - CText { - anchors.centerIn: parent - text: userInitials() - variant: "h4" - color: "#ffffff" - font.bold: true - } - } - - ColumnLayout { - Layout.fillWidth: true - spacing: 4 - - CText { - variant: "subtitle1" - text: appWindow.currentUser - font.bold: true - } - CText { - variant: "body2" - text: appWindow.currentRole + " \u00b7 Level " + appWindow.currentLevel - opacity: 0.7 - } - } + // Avatar + CAvatar { + size: "lg" + initials: userInitials() } - CTextField { + ColumnLayout { Layout.fillWidth: true - label: "Display Name" - placeholderText: "Enter display name" - text: displayName - onTextChanged: displayName = text + spacing: 4 + + CText { + variant: "subtitle1" + text: appWindow.currentUser + font.bold: true + } + CText { + variant: "body2" + text: appWindow.currentRole + " \u00b7 Level " + appWindow.currentLevel + opacity: 0.7 + } + } + } + + CTextField { + Layout.fillWidth: true + Layout.topMargin: 8 + label: "Display Name" + placeholderText: "Enter display name" + text: displayName + onTextChanged: displayName = text + } + + CTextField { + Layout.fillWidth: true + label: "Email" + placeholderText: "Enter email address" + text: userEmail + onTextChanged: userEmail = text + } + + FlexRow { + Layout.fillWidth: true + spacing: 12 + + Item { Layout.fillWidth: true } + + CAlert { + visible: profileSaved + severity: "success" + text: "Profile saved successfully" } - CTextField { - Layout.fillWidth: true - label: "Email" - placeholderText: "Enter email address" - text: userEmail - onTextChanged: userEmail = text - } - - FlexRow { - Layout.fillWidth: true - spacing: 12 - - Item { Layout.fillWidth: true } - - CAlert { - visible: profileSaved - severity: "success" - text: "Profile saved successfully" - } - - CButton { - text: "Save Profile" - variant: "primary" - onClicked: saveProfile() - } + CButton { + text: "Save Profile" + variant: "primary" + onClicked: saveProfile() } } } @@ -267,67 +252,62 @@ Rectangle { CCard { Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - anchors.margins: 20 - spacing: 16 + CText { variant: "h4"; text: "Appearance" } + CDivider { Layout.fillWidth: true } - CText { variant: "h4"; text: "Appearance" } - CDivider { Layout.fillWidth: true } + // Theme selector + CText { + variant: "subtitle2" + text: "Theme" + Layout.topMargin: 4 + } - // Theme selector - CText { - variant: "subtitle2" - text: "Theme" - } + Flow { + Layout.fillWidth: true + spacing: 8 - Flow { - Layout.fillWidth: true - spacing: 8 - - Repeater { - model: availableThemes - delegate: CButton { - text: modelData.label - variant: selectedTheme === modelData.id ? "primary" : "default" - size: "sm" - onClicked: { - selectedTheme = modelData.id - appWindow.currentTheme = modelData.id - if (typeof Theme.setTheme === "function") { - Theme.setTheme(modelData.id) - } - savePreferences() + Repeater { + model: availableThemes + delegate: CButton { + text: modelData.label + variant: selectedTheme === modelData.id ? "primary" : "default" + size: "sm" + onClicked: { + selectedTheme = modelData.id + appWindow.currentTheme = modelData.id + if (typeof Theme.setTheme === "function") { + Theme.setTheme(modelData.id) } + savePreferences() } } } + } - // Font size selector - CText { - variant: "subtitle2" - text: "Font Size" - Layout.topMargin: 8 - } + // Font size selector + CText { + variant: "subtitle2" + text: "Font Size" + Layout.topMargin: 8 + } - FlexRow { - Layout.fillWidth: true - spacing: 8 + FlexRow { + Layout.fillWidth: true + spacing: 8 - Repeater { - model: [ - { id: "small", label: "Small" }, - { id: "medium", label: "Medium" }, - { id: "large", label: "Large" } - ] - delegate: CButton { - text: modelData.label - variant: fontSize === modelData.id ? "primary" : "default" - size: "sm" - onClicked: { - fontSize = modelData.id - savePreferences() - } + Repeater { + model: [ + { id: "small", label: "Small" }, + { id: "medium", label: "Medium" }, + { id: "large", label: "Large" } + ] + delegate: CButton { + text: modelData.label + variant: fontSize === modelData.id ? "primary" : "default" + size: "sm" + onClicked: { + fontSize = modelData.id + savePreferences() } } } @@ -338,78 +318,73 @@ Rectangle { CCard { Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - anchors.margins: 20 - spacing: 16 + CText { variant: "h4"; text: "Notifications" } + CDivider { Layout.fillWidth: true } - CText { variant: "h4"; text: "Notifications" } - CDivider { Layout.fillWidth: true } + // Email notifications toggle + FlexRow { + Layout.fillWidth: true + Layout.topMargin: 4 + spacing: 12 - // Email notifications toggle - FlexRow { + ColumnLayout { Layout.fillWidth: true - spacing: 12 - - ColumnLayout { - Layout.fillWidth: true - spacing: 2 - CText { variant: "subtitle2"; text: "Email Notifications" } - CText { variant: "caption"; text: "Receive notification summaries via email"; opacity: 0.6 } - } - - Switch { - checked: emailNotifications - onCheckedChanged: { - emailNotifications = checked - savePreferences() - } - } + spacing: 2 + CText { variant: "subtitle2"; text: "Email Notifications" } + CText { variant: "caption"; text: "Receive notification summaries via email"; opacity: 0.6 } } - CDivider { Layout.fillWidth: true } - - // Desktop notifications toggle - FlexRow { - Layout.fillWidth: true - spacing: 12 - - ColumnLayout { - Layout.fillWidth: true - spacing: 2 - CText { variant: "subtitle2"; text: "Desktop Notifications" } - CText { variant: "caption"; text: "Show desktop push notifications for alerts"; opacity: 0.6 } - } - - Switch { - checked: desktopNotifications - onCheckedChanged: { - desktopNotifications = checked - savePreferences() - } + CSwitch { + checked: emailNotifications + onToggled: function(value) { + emailNotifications = value + savePreferences() } } + } - CDivider { Layout.fillWidth: true } + CDivider { Layout.fillWidth: true } - // Sound alerts toggle - FlexRow { + // Desktop notifications toggle + FlexRow { + Layout.fillWidth: true + spacing: 12 + + ColumnLayout { Layout.fillWidth: true - spacing: 12 + spacing: 2 + CText { variant: "subtitle2"; text: "Desktop Notifications" } + CText { variant: "caption"; text: "Show desktop push notifications for alerts"; opacity: 0.6 } + } - ColumnLayout { - Layout.fillWidth: true - spacing: 2 - CText { variant: "subtitle2"; text: "Sound Alerts" } - CText { variant: "caption"; text: "Play a sound when new notifications arrive"; opacity: 0.6 } + CSwitch { + checked: desktopNotifications + onToggled: function(value) { + desktopNotifications = value + savePreferences() } + } + } - Switch { - checked: soundAlerts - onCheckedChanged: { - soundAlerts = checked - savePreferences() - } + CDivider { Layout.fillWidth: true } + + // Sound alerts toggle + FlexRow { + Layout.fillWidth: true + spacing: 12 + + ColumnLayout { + Layout.fillWidth: true + spacing: 2 + CText { variant: "subtitle2"; text: "Sound Alerts" } + CText { variant: "caption"; text: "Play a sound when new notifications arrive"; opacity: 0.6 } + } + + CSwitch { + checked: soundAlerts + onToggled: function(value) { + soundAlerts = value + savePreferences() } } } @@ -419,81 +394,75 @@ Rectangle { CCard { Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - anchors.margins: 20 - spacing: 16 + CText { variant: "h4"; text: "Connection" } + CDivider { Layout.fillWidth: true } - CText { variant: "h4"; text: "Connection" } - CDivider { Layout.fillWidth: true } + // DBAL URL + CText { variant: "subtitle2"; text: "DBAL Server"; Layout.topMargin: 4 } - // DBAL URL - CText { variant: "subtitle2"; text: "DBAL Server" } + FlexRow { + Layout.fillWidth: true + spacing: 12 - FlexRow { + CTextField { Layout.fillWidth: true - spacing: 12 - - CTextField { - Layout.fillWidth: true - label: "DBAL URL" - placeholderText: "http://localhost:8080" - text: dbalUrl - onTextChanged: dbalUrl = text - } - - ColumnLayout { - spacing: 4 - Layout.alignment: Qt.AlignBottom - - CButton { - text: dbalConnectionStatus === "testing" ? "Testing..." : "Test Connection" - variant: "default" - size: "sm" - enabled: dbalConnectionStatus !== "testing" - onClicked: testDBALConnection() - } - - CStatusBadge { - status: connectionStatusColor(dbalConnectionStatus) - text: connectionStatusLabel(dbalConnectionStatus) - } - } + label: "DBAL URL" + placeholderText: "http://localhost:8080" + text: dbalUrl + onTextChanged: dbalUrl = text } - CDivider { Layout.fillWidth: true } + ColumnLayout { + spacing: 4 + Layout.alignment: Qt.AlignBottom - // Media Service URL - CText { variant: "subtitle2"; text: "Media Service" } - - FlexRow { - Layout.fillWidth: true - spacing: 12 - - CTextField { - Layout.fillWidth: true - label: "Media Service URL" - placeholderText: "http://localhost:9090" - text: mediaServiceUrl - onTextChanged: mediaServiceUrl = text + CButton { + text: dbalConnectionStatus === "testing" ? "Testing..." : "Test Connection" + variant: "default" + size: "sm" + enabled: dbalConnectionStatus !== "testing" + onClicked: testDBALConnection() } - ColumnLayout { - spacing: 4 - Layout.alignment: Qt.AlignBottom + CStatusBadge { + status: connectionStatusColor(dbalConnectionStatus) + text: connectionStatusLabel(dbalConnectionStatus) + } + } + } - CButton { - text: mediaConnectionStatus === "testing" ? "Testing..." : "Test Connection" - variant: "default" - size: "sm" - enabled: mediaConnectionStatus !== "testing" - onClicked: testMediaConnection() - } + CDivider { Layout.fillWidth: true } - CStatusBadge { - status: connectionStatusColor(mediaConnectionStatus) - text: connectionStatusLabel(mediaConnectionStatus) - } + // Media Service URL + CText { variant: "subtitle2"; text: "Media Service" } + + FlexRow { + Layout.fillWidth: true + spacing: 12 + + CTextField { + Layout.fillWidth: true + label: "Media Service URL" + placeholderText: "http://localhost:9090" + text: mediaServiceUrl + onTextChanged: mediaServiceUrl = text + } + + ColumnLayout { + spacing: 4 + Layout.alignment: Qt.AlignBottom + + CButton { + text: mediaConnectionStatus === "testing" ? "Testing..." : "Test Connection" + variant: "default" + size: "sm" + enabled: mediaConnectionStatus !== "testing" + onClicked: testMediaConnection() + } + + CStatusBadge { + status: connectionStatusColor(mediaConnectionStatus) + text: connectionStatusLabel(mediaConnectionStatus) } } } @@ -503,62 +472,56 @@ Rectangle { CCard { Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - anchors.margins: 20 - spacing: 12 + CText { variant: "h4"; text: "About" } + CDivider { Layout.fillWidth: true } - CText { variant: "h4"; text: "About" } - CDivider { Layout.fillWidth: true } + Repeater { + model: [ + { label: "Version", value: "2.1.0" }, + { label: "Build Date", value: "2026-03-19" }, + { label: "Qt Version", value: "6.8.x" }, + { label: "Platform", value: Qt.platform.os }, + { label: "DBAL Schema", value: "v1 REST API" } + ] - Repeater { - model: [ - { label: "Version", value: "2.1.0" }, - { label: "Build Date", value: "2026-03-19" }, - { label: "Qt Version", value: "6.8.x" }, - { label: "Platform", value: Qt.platform.os }, - { label: "DBAL Schema", value: "v1 REST API" } - ] - - delegate: FlexRow { - Layout.fillWidth: true - spacing: 12 - - CText { - variant: "body2" - text: modelData.label - opacity: 0.6 - Layout.preferredWidth: 120 - } - - CText { - variant: "body1" - text: modelData.value - } - } - } - - CDivider { Layout.fillWidth: true } - - FlexRow { + delegate: FlexRow { Layout.fillWidth: true spacing: 12 - CButton { - text: "View Documentation" - variant: "default" - size: "sm" - onClicked: Qt.openUrlExternally("https://github.com/nicholasgriffintn/metabuilder") + CText { + variant: "body2" + text: modelData.label + opacity: 0.6 + Layout.preferredWidth: 120 } - CButton { - text: "Report Issue" - variant: "ghost" - size: "sm" - onClicked: Qt.openUrlExternally("https://github.com/nicholasgriffintn/metabuilder/issues") + CText { + variant: "body1" + text: modelData.value } } } + + CDivider { Layout.fillWidth: true } + + FlexRow { + Layout.fillWidth: true + spacing: 12 + + CButton { + text: "View Documentation" + variant: "default" + size: "sm" + onClicked: Qt.openUrlExternally("https://github.com/nicholasgriffintn/metabuilder") + } + + CButton { + text: "Report Issue" + variant: "ghost" + size: "sm" + onClicked: Qt.openUrlExternally("https://github.com/nicholasgriffintn/metabuilder/issues") + } + } } // ── Data status footer ────────────────────────────── diff --git a/frontends/qt6/SuperGodPanel.qml b/frontends/qt6/SuperGodPanel.qml index 814cb201c..595d674c1 100644 --- a/frontends/qt6/SuperGodPanel.qml +++ b/frontends/qt6/SuperGodPanel.qml @@ -6,7 +6,7 @@ import "qmllib/dbal" Rectangle { id: superGodPanel - color: "transparent" + color: Theme.background // ── DBAL connection ── DBALProvider { id: dbal } @@ -150,53 +150,47 @@ Rectangle { CCard { Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - anchors.margins: 20 + FlexRow { + Layout.fillWidth: true spacing: 12 - FlexRow { - Layout.fillWidth: true - spacing: 12 + CText { variant: "h2"; text: "Super God Panel" } + CBadge { text: "Level 5"; badgeColor: Theme.primary } + CStatusBadge { status: "success"; text: "Platform Control" } - CText { variant: "h2"; text: "Super God Panel" } - CBadge { text: "Level 5" } - CStatusBadge { status: "success"; text: "Platform Control" } + Item { Layout.fillWidth: true } - Item { Layout.fillWidth: true } - - CButton { - text: "Level 4" - variant: "ghost" - size: "sm" - onClicked: appWindow.currentView = "god" - } - CButton { - text: "Level 3" - variant: "ghost" - size: "sm" - onClicked: appWindow.currentView = "admin" - } - CButton { - text: "Level 2" - variant: "ghost" - size: "sm" - onClicked: appWindow.currentView = "dashboard" - } + CButton { + text: "Level 4" + variant: "ghost" + size: "sm" + onClicked: appWindow.currentView = "god" } - - CDivider { Layout.fillWidth: true } - - FlexRow { - Layout.fillWidth: true - spacing: 8 - - CChip { text: tenants.length + " Tenants" } - CChip { text: godUsers.length + " God Users" } - CChip { text: pendingTransfers.length + " Pending Transfers" } - CChip { text: "14 DB Backends" } - CChip { text: "4 Daemons" } + CButton { + text: "Level 3" + variant: "ghost" + size: "sm" + onClicked: appWindow.currentView = "admin" } + CButton { + text: "Level 2" + variant: "ghost" + size: "sm" + onClicked: appWindow.currentView = "dashboard" + } + } + + CDivider { Layout.fillWidth: true } + + FlexRow { + Layout.fillWidth: true + spacing: 8 + + CChip { text: tenants.length + " Tenants"; chipColor: Theme.primary } + CChip { text: godUsers.length + " God Users"; chipColor: Theme.primary } + CChip { text: pendingTransfers.length + " Pending Transfers"; chipColor: Theme.warning } + CChip { text: "14 DB Backends"; chipColor: Theme.info } + CChip { text: "4 Daemons"; chipColor: Theme.success } } } @@ -259,51 +253,45 @@ Rectangle { delegate: CCard { Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - anchors.margins: 16 + FlexRow { + Layout.fillWidth: true spacing: 10 - FlexRow { - Layout.fillWidth: true - spacing: 10 - - CText { variant: "h4"; text: modelData.name } - CStatusBadge { - status: modelData.status === "active" ? "success" : "warning" - text: modelData.status - } - Item { Layout.fillWidth: true } - CBadge { text: "Tenant" } + CText { variant: "h4"; text: modelData.name } + CStatusBadge { + status: modelData.status === "active" ? "success" : "warning" + text: modelData.status } + Item { Layout.fillWidth: true } + CBadge { text: "Tenant"; badgeColor: Theme.primary } + } - CDivider { Layout.fillWidth: true } + CDivider { Layout.fillWidth: true } - RowLayout { - Layout.fillWidth: true - spacing: 24 + RowLayout { + Layout.fillWidth: true + spacing: 24 - ColumnLayout { - spacing: 4 - CText { variant: "caption"; text: "Owner"; color: Theme.textSecondary } - CText { variant: "body1"; text: modelData.owner } - } - ColumnLayout { - spacing: 4 - CText { variant: "caption"; text: "Homepage"; color: Theme.textSecondary } - CText { variant: "body1"; text: modelData.homepage } - } - ColumnLayout { - spacing: 4 - CText { variant: "caption"; text: "Created"; color: Theme.textSecondary } - CText { variant: "body1"; text: modelData.created } - } - Item { Layout.fillWidth: true } - CButton { - text: "Configure" - variant: "ghost" - size: "sm" - } + ColumnLayout { + spacing: 4 + CText { variant: "caption"; text: "Owner"; color: Theme.textSecondary } + CText { variant: "body1"; text: modelData.owner } + } + ColumnLayout { + spacing: 4 + CText { variant: "caption"; text: "Homepage"; color: Theme.textSecondary } + CText { variant: "body1"; text: modelData.homepage } + } + ColumnLayout { + spacing: 4 + CText { variant: "caption"; text: "Created"; color: Theme.textSecondary } + CText { variant: "body1"; text: modelData.created } + } + Item { Layout.fillWidth: true } + CButton { + text: "Configure" + variant: "ghost" + size: "sm" } } } @@ -344,8 +332,7 @@ Rectangle { Layout.fillWidth: true RowLayout { - anchors.fill: parent - anchors.margins: 16 + Layout.fillWidth: true spacing: 16 CAvatar { initials: modelData.initials } @@ -357,8 +344,8 @@ Rectangle { FlexRow { spacing: 8 CText { variant: "subtitle1"; text: modelData.username } - CBadge { text: "L" + modelData.level } - CBadge { text: modelData.role } + CBadge { text: "L" + modelData.level; badgeColor: Theme.primary } + CBadge { text: modelData.role; badgeColor: Theme.secondary } } FlexRow { @@ -425,77 +412,71 @@ Rectangle { Layout.fillWidth: true visible: showTransferForm - ColumnLayout { - anchors.fill: parent - anchors.margins: 20 - spacing: 12 + CText { variant: "h4"; text: "New Transfer Request" } + CDivider { Layout.fillWidth: true } - CText { variant: "h4"; text: "New Transfer Request" } - CDivider { Layout.fillWidth: true } - - RowLayout { - Layout.fillWidth: true - spacing: 16 - - CTextField { - Layout.fillWidth: true - label: "From User" - placeholderText: "e.g. admin" - text: transferFrom - onTextChanged: transferFrom = text - } - CTextField { - Layout.fillWidth: true - label: "To User" - placeholderText: "e.g. devops" - text: transferTo - onTextChanged: transferTo = text - } - } + RowLayout { + Layout.fillWidth: true + spacing: 16 CTextField { Layout.fillWidth: true - label: "Reason" - placeholderText: "Describe the reason for this transfer" - text: transferReason - onTextChanged: transferReason = text + label: "From User" + placeholderText: "e.g. admin" + text: transferFrom + onTextChanged: transferFrom = text } - CTextField { Layout.fillWidth: true - label: "Expiry Date" - placeholderText: "YYYY-MM-DD" - text: transferExpiry - onTextChanged: transferExpiry = text + label: "To User" + placeholderText: "e.g. devops" + text: transferTo + onTextChanged: transferTo = text } + } - FlexRow { - Layout.fillWidth: true - spacing: 8 + CTextField { + Layout.fillWidth: true + label: "Reason" + placeholderText: "Describe the reason for this transfer" + text: transferReason + onTextChanged: transferReason = text + } - Item { Layout.fillWidth: true } - CButton { - text: "Submit Transfer" - variant: "primary" - size: "sm" - onClicked: { - if (transferFrom && transferTo && transferReason) { - var newTransfer = { - from: transferFrom, - to: transferTo, - reason: transferReason, - expiry: transferExpiry || "No expiry", - status: "pending" - }; - var updated = pendingTransfers.slice(); - updated.push(newTransfer); - pendingTransfers = updated; - transferFrom = ""; - transferTo = ""; - transferReason = ""; - transferExpiry = ""; - showTransferForm = false; - } + CTextField { + Layout.fillWidth: true + label: "Expiry Date" + placeholderText: "YYYY-MM-DD" + text: transferExpiry + onTextChanged: transferExpiry = text + } + + FlexRow { + Layout.fillWidth: true + spacing: 8 + + Item { Layout.fillWidth: true } + CButton { + text: "Submit Transfer" + variant: "primary" + size: "sm" + onClicked: { + if (transferFrom && transferTo && transferReason) { + var newTransfer = { + from: transferFrom, + to: transferTo, + reason: transferReason, + expiry: transferExpiry || "No expiry", + status: "pending" + }; + var updated = pendingTransfers.slice(); + updated.push(newTransfer); + pendingTransfers = updated; + transferFrom = ""; + transferTo = ""; + transferReason = ""; + transferExpiry = ""; + showTransferForm = false; } } } @@ -513,72 +494,66 @@ Rectangle { delegate: CCard { Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - anchors.margins: 16 + FlexRow { + Layout.fillWidth: true spacing: 10 - FlexRow { - Layout.fillWidth: true - spacing: 10 + CText { variant: "subtitle1"; text: modelData.from + " -> " + modelData.to } + CStatusBadge { status: "warning"; text: "Pending" } + Item { Layout.fillWidth: true } + CText { variant: "caption"; text: "Expires: " + modelData.expiry; color: Theme.textSecondary } + } - CText { variant: "subtitle1"; text: modelData.from + " → " + modelData.to } - CStatusBadge { status: "warning"; text: "Pending" } - Item { Layout.fillWidth: true } - CText { variant: "caption"; text: "Expires: " + modelData.expiry; color: Theme.textSecondary } - } + CText { + variant: "body2" + text: modelData.reason + wrapMode: Text.Wrap + Layout.fillWidth: true + } - CText { - variant: "body2" - text: modelData.reason - wrapMode: Text.Wrap - Layout.fillWidth: true - } + FlexRow { + Layout.fillWidth: true + spacing: 8 - FlexRow { - Layout.fillWidth: true - spacing: 8 - - Item { Layout.fillWidth: true } - CButton { - text: "Approve" - variant: "primary" - size: "sm" - onClicked: { - var entry = { - from: pendingTransfers[index].from, - to: pendingTransfers[index].to, - reason: pendingTransfers[index].reason, - date: "2026-03-18 " + Qt.formatTime(new Date(), "hh:mm"), - status: "approved" - }; - var hist = transferHistory.slice(); - hist.unshift(entry); - transferHistory = hist; - var pend = pendingTransfers.slice(); - pend.splice(index, 1); - pendingTransfers = pend; - } + Item { Layout.fillWidth: true } + CButton { + text: "Approve" + variant: "primary" + size: "sm" + onClicked: { + var entry = { + from: pendingTransfers[index].from, + to: pendingTransfers[index].to, + reason: pendingTransfers[index].reason, + date: "2026-03-18 " + Qt.formatTime(new Date(), "hh:mm"), + status: "approved" + }; + var hist = transferHistory.slice(); + hist.unshift(entry); + transferHistory = hist; + var pend = pendingTransfers.slice(); + pend.splice(index, 1); + pendingTransfers = pend; } - CButton { - text: "Deny" - variant: "danger" - size: "sm" - onClicked: { - var entry = { - from: pendingTransfers[index].from, - to: pendingTransfers[index].to, - reason: pendingTransfers[index].reason, - date: "2026-03-18 " + Qt.formatTime(new Date(), "hh:mm"), - status: "denied" - }; - var hist = transferHistory.slice(); - hist.unshift(entry); - transferHistory = hist; - var pend = pendingTransfers.slice(); - pend.splice(index, 1); - pendingTransfers = pend; - } + } + CButton { + text: "Deny" + variant: "danger" + size: "sm" + onClicked: { + var entry = { + from: pendingTransfers[index].from, + to: pendingTransfers[index].to, + reason: pendingTransfers[index].reason, + date: "2026-03-18 " + Qt.formatTime(new Date(), "hh:mm"), + status: "denied" + }; + var hist = transferHistory.slice(); + hist.unshift(entry); + transferHistory = hist; + var pend = pendingTransfers.slice(); + pend.splice(index, 1); + pendingTransfers = pend; } } } @@ -604,8 +579,7 @@ Rectangle { variant: "outlined" RowLayout { - anchors.fill: parent - anchors.margins: 14 + Layout.fillWidth: true spacing: 16 ColumnLayout { @@ -614,7 +588,7 @@ Rectangle { FlexRow { spacing: 8 - CText { variant: "subtitle1"; text: modelData.from + " → " + modelData.to } + CText { variant: "subtitle1"; text: modelData.from + " -> " + modelData.to } CStatusBadge { status: modelData.status === "approved" ? "success" : "error" text: modelData.status @@ -662,26 +636,20 @@ Rectangle { delegate: CCard { Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - anchors.margins: 16 + FlexRow { + Layout.fillWidth: true spacing: 10 - FlexRow { - Layout.fillWidth: true - spacing: 10 - - CText { variant: "subtitle1"; text: modelData.name } - CStatusBadge { - status: modelData.status === "healthy" ? "success" : "warning" - text: modelData.status - } - Item { Layout.fillWidth: true } - CBadge { text: ":" + modelData.port } + CText { variant: "subtitle1"; text: modelData.name } + CStatusBadge { + status: modelData.status === "healthy" ? "success" : "warning" + text: modelData.status } - - CText { variant: "caption"; text: "Uptime: " + modelData.uptime; color: Theme.textSecondary } + Item { Layout.fillWidth: true } + CBadge { text: ":" + modelData.port; badgeColor: Theme.info } } + + CText { variant: "caption"; text: "Uptime: " + modelData.uptime; color: Theme.textSecondary } } } } @@ -708,26 +676,30 @@ Rectangle { delegate: CCard { Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - anchors.margins: 16 - spacing: 8 + CText { + variant: "caption" + text: modelData.label + color: Theme.textSecondary + Layout.fillWidth: true + } - CText { variant: "caption"; text: modelData.label; color: Theme.textSecondary } - CText { variant: "h3"; text: modelData.value + "%" } + CText { + variant: "h3" + text: modelData.value + "%" + Layout.fillWidth: true + } + + Rectangle { + Layout.fillWidth: true + height: 6 + radius: 3 + color: Theme.surfaceVariant Rectangle { - Layout.fillWidth: true - height: 6 + width: parent.width * (modelData.value / 100) + height: parent.height radius: 3 - color: Theme.border - - Rectangle { - width: parent.width * (modelData.value / 100) - height: parent.height - radius: 3 - color: modelData.value > 80 ? Theme.error : modelData.value > 60 ? Theme.warning : Theme.primary - } + color: modelData.value > 80 ? Theme.error : modelData.value > 60 ? Theme.warning : Theme.primary } } } @@ -777,35 +749,29 @@ Rectangle { CCard { Layout.fillWidth: true - ColumnLayout { - anchors.fill: parent - anchors.margins: 16 - spacing: 8 + RowLayout { + Layout.fillWidth: true + spacing: 24 - RowLayout { - Layout.fillWidth: true - spacing: 24 - - ColumnLayout { - spacing: 4 - CText { variant: "caption"; text: "Version"; color: Theme.textSecondary } - CText { variant: "body1"; text: "2.5.0-rc1" } - } - ColumnLayout { - spacing: 4 - CText { variant: "caption"; text: "Build Date"; color: Theme.textSecondary } - CText { variant: "body1"; text: "2026-03-15" } - } - ColumnLayout { - spacing: 4 - CText { variant: "caption"; text: "Node Count"; color: Theme.textSecondary } - CText { variant: "body1"; text: "4 nodes" } - } - ColumnLayout { - spacing: 4 - CText { variant: "caption"; text: "Platform"; color: Theme.textSecondary } - CText { variant: "body1"; text: "MetaBuilder Universal" } - } + ColumnLayout { + spacing: 4 + CText { variant: "caption"; text: "Version"; color: Theme.textSecondary } + CText { variant: "body1"; text: "2.5.0-rc1" } + } + ColumnLayout { + spacing: 4 + CText { variant: "caption"; text: "Build Date"; color: Theme.textSecondary } + CText { variant: "body1"; text: "2026-03-15" } + } + ColumnLayout { + spacing: 4 + CText { variant: "caption"; text: "Node Count"; color: Theme.textSecondary } + CText { variant: "body1"; text: "4 nodes" } + } + ColumnLayout { + spacing: 4 + CText { variant: "caption"; text: "Platform"; color: Theme.textSecondary } + CText { variant: "body1"; text: "MetaBuilder Universal" } } } }