refactor: enhance TimerPage layout and functionality; update theme and tokens for improved design consistency

- Refactored TimerPage component to improve layout and user experience.
- Updated theme configuration to utilize design tokens for better maintainability.
- Introduced new tokens for colors and typography to align with design principles.
- Simplified state management and improved timer functionality.
- Enhanced accessibility and responsiveness of UI elements.
This commit is contained in:
timka123 committed 2026-10-18 23:57:36 +03:00
1 parent 17a0a3c4c9
commit 77b26757bf
84 files changed
+4501 -2682

No files matched your search

+190 -141
View File
@@ -1,8 +1,9 @@
// The day, summarised — the same payload the Telegram message is built from.
// The day in numbers — the same payload the Telegram message is built from.
import QtQuick
import QtQuick.Controls.Material
import QtQuick.Controls
import QtQuick.Layouts
import "."
import "components"
ScrollView {
@@ -14,166 +15,214 @@ ScrollView {
readonly property var metrics: payload ? payload.metrics : null
ColumnLayout {
width: page.availableWidth
spacing: 16
width: page.availableWidth - 56
x: 28
y: 26
spacing: 22
PageHeader {
eyebrow: controller.report.sent_at ? "sent to Telegram" : "not sent yet"
title: page.payload ? "Report · " + page.payload.date : "Report"
caption: page.payload ? page.payload.verdict : ""
GhostButton { text: "Reload"; onClicked: controller.loadReport() }
PrimaryButton { text: "Send to Telegram"; onClicked: controller.sendReport() }
}
Panel {
padding: 0
RowLayout {
Layout.fillWidth: true
spacing: 0
Repeater {
model: [
{ label: "Pomodoros",
value: page.metrics ? page.metrics.pomodoros + "/" + page.metrics.goal : "—",
caption: page.metrics && page.metrics.goal_met ? "goal met" : "goal not met yet",
accent: true },
{ label: "Focus",
value: page.metrics ? page.metrics.focus_minutes + "m" : "—",
caption: page.metrics ? page.metrics.break_minutes + "m resting" : "" },
{ label: "Score",
value: page.payload ? "" + Math.round(page.payload.score) : "—",
caption: "out of 100" },
{ label: "Streak",
value: page.payload ? page.payload.streak.current + "d" : "—",
caption: page.payload ? "best " + page.payload.streak.best : "" }
]
delegate: RowLayout {
required property int index
required property var modelData
Layout.fillWidth: true
spacing: 0
Rectangle {
visible: index > 0
width: 1
Layout.preferredHeight: 84
color: Theme.rule
}
Stat {
Layout.fillWidth: true
Layout.margins: 20
label: modelData.label
value: modelData.value
caption: modelData.caption
size: 38
accent: modelData.accent ? Theme.work : Theme.ink
}
}
}
}
}
ColumnLayout {
Layout.fillWidth: true
Layout.margins: 20
spacing: 16
RowLayout {
Layout.fillWidth: true
Label {
text: page.payload ? "Report for " + page.payload.date : "Report"
font.pixelSize: 20
font.weight: Font.Medium
Layout.fillWidth: true
}
Button { text: "⟳"; flat: true; onClicked: controller.loadReport() }
PrimaryButton { text: "Send to Telegram"; onClicked: controller.sendReport() }
}
GridLayout {
Layout.fillWidth: true
columns: page.width > 760 ? 4 : 2
columnSpacing: 12
rowSpacing: 12
StatTile {
label: "Pomodoros"
value: page.metrics ? page.metrics.pomodoros + "/" + page.metrics.goal : "—"
caption: page.metrics && page.metrics.goal_met ? "goal met 🎯" : "keep going"
accent: window.workColor
}
StatTile {
label: "Focus"
value: page.metrics ? page.metrics.focus_minutes + "m" : "—"
caption: page.metrics ? page.metrics.break_minutes + "m resting" : ""
}
StatTile {
label: "Score"
value: page.payload ? Math.round(page.payload.score) : "—"
caption: "out of 100"
}
StatTile {
label: "Streak"
value: page.payload ? page.payload.streak.current + "d" : "—"
caption: page.payload ? "best " + page.payload.streak.best : ""
}
}
Card {
title: "Last 30 days"
subtitle: "Goal line is the dashed row; darker bars cleared it."
spacing: 10
SectionTitle { text: "Last 30 days"; trailing: "goal line dashed" }
Panel {
BarRow {
Layout.fillWidth: true
days: controller.stats.range ? controller.stats.range : []
goal: controller.goal
}
}
}
Card {
visible: page.payload && page.payload.achievements.length > 0
title: "Achievements"
Flow {
Layout.fillWidth: true
spacing: 8
Repeater {
model: page.payload ? page.payload.achievements : []
delegate: Pane {
required property var modelData
Material.elevation: 0
Material.roundedScale: Material.SmallScale
padding: 10
background: Rectangle {
radius: 10
color: Material.accent
opacity: 0.14
ColumnLayout {
Layout.fillWidth: true
visible: page.payload && page.payload.achievements.length > 0
spacing: 10
SectionTitle { text: "Achievements" }
Flow {
Layout.fillWidth: true
spacing: 8
Repeater {
model: page.payload ? page.payload.achievements : []
delegate: Rectangle {
required property var modelData
implicitWidth: badge.implicitWidth + 24
implicitHeight: badge.implicitHeight + 18
radius: 10
color: Qt.alpha(Theme.ember, 0.08)
border.width: 1
border.color: Qt.alpha(Theme.ember, 0.22)
ColumnLayout {
id: badge
anchors.centerIn: parent
spacing: 1
Label {
text: modelData.emoji + " " + modelData.title
color: Theme.ink
font.pixelSize: 13
font.weight: Font.DemiBold
font.family: Theme.sansFamily
}
ColumnLayout {
spacing: 1
Label {
text: modelData.emoji + " " + modelData.title
font.weight: Font.DemiBold
font.pixelSize: 13
}
Label {
text: modelData.detail
visible: modelData.detail !== ""
font.pixelSize: 11
opacity: 0.75
}
Label {
visible: modelData.detail !== ""
text: modelData.detail
color: Theme.muted
font.pixelSize: 11
font.family: Theme.sansFamily
}
}
}
}
}
}
Card {
visible: page.payload && page.payload.fun_facts.length > 0
title: "The funny part"
Repeater {
model: page.payload ? page.payload.fun_facts : []
delegate: Label {
required property var modelData
text: modelData
wrapMode: Text.WordWrap
Layout.fillWidth: true
font.pixelSize: 13
}
}
}
Card {
visible: page.metrics && page.metrics.tasks_completed.length > 0
title: "Closed today"
Repeater {
model: page.metrics ? page.metrics.tasks_completed : []
delegate: RowLayout {
required property var modelData
Layout.fillWidth: true
Label { text: "✅"; font.pixelSize: 12 }
Label { text: modelData.title; Layout.fillWidth: true; elide: Text.ElideRight }
Label { text: modelData.source; font.pixelSize: 11; opacity: 0.6 }
}
}
}
Card {
title: "All time"
RowLayout {
ColumnLayout {
Layout.fillWidth: true
visible: page.payload && page.payload.fun_facts.length > 0
spacing: 8
SectionTitle { text: "The funny part" }
Repeater {
model: page.payload ? page.payload.fun_facts : []
delegate: Label {
required property var modelData
text: modelData
color: Theme.ink
font.pixelSize: 13
font.family: Theme.sansFamily
wrapMode: Text.WordWrap
Layout.fillWidth: true
spacing: 12
StatTile {
label: "Pomodoros"
value: controller.stats.lifetime ? controller.stats.lifetime.pomodoros : "—"
}
StatTile {
label: "Hours focused"
value: controller.stats.lifetime ? controller.stats.lifetime.focus_hours : "—"
}
StatTile {
label: "Tasks closed"
value: controller.stats.lifetime ? controller.stats.lifetime.tasks_completed : "—"
}
StatTile {
label: "Blocked"
value: controller.stats.lifetime ? controller.stats.lifetime.blocked_attempts : "—"
}
}
}
Label {
visible: page.payload && page.payload.verdict !== ""
text: page.payload ? page.payload.verdict : ""
font.italic: true
font.pixelSize: 14
opacity: 0.85
wrapMode: Text.WordWrap
Layout.fillWidth: true
}
}
ColumnLayout {
Layout.fillWidth: true
visible: page.metrics && page.metrics.tasks_completed.length > 0
spacing: 6
SectionTitle { text: "Closed today" }
Repeater {
model: page.metrics ? page.metrics.tasks_completed : []
delegate: RowLayout {
required property var modelData
Layout.fillWidth: true
spacing: 12
Label {
text: modelData.title
color: Theme.ink
font.pixelSize: 13
font.family: Theme.sansFamily
elide: Text.ElideRight
Layout.fillWidth: true
}
Label {
text: modelData.source
color: Theme.faint
font.pixelSize: 11
font.family: Theme.sansFamily
}
}
}
}
ColumnLayout {
Layout.fillWidth: true
spacing: 10
SectionTitle { text: "All time" }
Panel {
padding: 0
RowLayout {
Layout.fillWidth: true
spacing: 0
Repeater {
model: [
{ label: "Pomodoros",
value: controller.stats.lifetime ? "" + controller.stats.lifetime.pomodoros : "—" },
{ label: "Hours focused",
value: controller.stats.lifetime ? "" + controller.stats.lifetime.focus_hours : "—" },
{ label: "Tasks closed",
value: controller.stats.lifetime ? "" + controller.stats.lifetime.tasks_completed : "—" },
{ label: "Blocked",
value: controller.stats.lifetime ? "" + controller.stats.lifetime.blocked_attempts : "—" }
]
delegate: RowLayout {
required property int index
required property var modelData
Layout.fillWidth: true
spacing: 0
Rectangle {
visible: index > 0
width: 1
Layout.preferredHeight: 64
color: Theme.rule
}
Stat {
Layout.fillWidth: true
Layout.margins: 18
label: modelData.label
value: modelData.value
}
}
}
}
}
}
Item { Layout.preferredHeight: 10 }
}
Component.onCompleted: controller.loadReport()