Files
kadence/desktop/kadence_desktop/qml/TasksPage.qml
T
timka123 77b26757bf 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.
2026-10-18 23:57:36 +03:00

235 lines
9.1 KiB
QML

// Everything you owe, grouped by deadline.
//
// There is no checkbox: a task is closed in Jira, Todoist or Google Tasks, and
// the next refresh notices. Two places to tick something off is two places to
// be wrong.
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import "."
import "components"
ScrollView {
id: page
contentWidth: availableWidth
clip: true
function bucketTasks(bucket) {
var out = []
for (var i = 0; i < controller.tasks.length; i++) {
var task = controller.tasks[i]
if (!task.is_done && task.bucket === bucket) out.push(task)
}
return out
}
function bucketColor(bucket) {
if (bucket === "overdue") return Theme.clay
if (bucket === "today") return Theme.amber
if (bucket === "active") return Theme.pine
if (bucket === "upcoming") return Theme.faint
return Theme.rule
}
ColumnLayout {
width: page.availableWidth - 56
x: 28
y: 26
spacing: 22
PageHeader {
eyebrow: "From Jira, Todoist, Google Tasks and your own list"
title: "Tasks"
caption: "Close them where they live — the next refresh credits them to today."
GhostButton { text: "Add"; onClicked: addDialog.open() }
PrimaryButton { text: "Refresh sources"; onClicked: controller.refreshSources() }
}
Repeater {
model: [
{ key: "overdue", label: "Overdue" },
{ key: "today", label: "Due today" },
{ key: "active", label: "In progress" },
{ key: "upcoming", label: "Coming up" },
{ key: "someday", label: "No deadline" }
]
delegate: ColumnLayout {
required property var modelData
property var items: page.bucketTasks(modelData.key)
visible: items.length > 0
Layout.fillWidth: true
spacing: 2
SectionTitle { text: modelData.label; trailing: "" + parent.items.length }
Repeater {
model: parent.items
delegate: Item {
required property var modelData
Layout.fillWidth: true
implicitHeight: row.implicitHeight + 20
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: 1
color: Theme.rule
}
RowLayout {
id: row
anchors.fill: parent
anchors.topMargin: 10
anchors.bottomMargin: 10
spacing: 14
Rectangle {
width: 2
Layout.fillHeight: true
radius: 1
color: page.bucketColor(modelData.bucket)
}
ColumnLayout {
Layout.fillWidth: true
spacing: 3
Label {
text: modelData.title
color: Theme.ink
font.pixelSize: 14
font.weight: modelData.pinned ? Font.DemiBold : Font.Normal
font.family: Theme.sansFamily
elide: Text.ElideRight
Layout.fillWidth: true
}
RowLayout {
spacing: 12
Label {
text: modelData.source_label
color: Theme.faint; font.pixelSize: 11
font.family: Theme.sansFamily
}
Label {
visible: !!modelData.project && modelData.project !== modelData.source_label
text: modelData.project || ""
color: Theme.faint; font.pixelSize: 11
font.family: Theme.sansFamily
}
Label {
visible: !!modelData.status
text: modelData.status || ""
color: Theme.faint; font.pixelSize: 11
font.family: Theme.sansFamily
}
Label {
visible: modelData.days_overdue > 0
text: modelData.days_overdue + "d overdue"
color: Theme.clay; font.pixelSize: 11
font.weight: Font.DemiBold
font.family: Theme.sansFamily
}
Label {
visible: !!modelData.due_date && modelData.days_overdue === 0
text: "due " + (modelData.due_date || "")
color: Theme.faint; font.pixelSize: 11
font.family: Theme.sansFamily
}
}
}
GhostButton {
text: modelData.pinned ? "Unpin" : "Pin"
implicitHeight: 28
onClicked: controller.setTaskPinned(modelData.id, !modelData.pinned)
}
GhostButton {
visible: !!modelData.url
text: "Open"
implicitHeight: 28
onClicked: controller.openUrl(modelData.url)
}
GhostButton {
text: "Focus"
implicitHeight: 28
onClicked: {
controller.attachTask(modelData.id)
controller.startWork()
window.page = 0
}
}
}
}
}
}
}
Panel {
visible: controller.tasks.length === 0
ColumnLayout {
spacing: 8
Label {
text: "Nothing on the list"
color: Theme.ink
font.pixelSize: 17
font.family: Theme.displayFamily
}
Label {
text: "Connect Jira, Todoist or Google Tasks in Settings, or add something yourself."
color: Theme.muted
font.pixelSize: 12
font.family: Theme.sansFamily
Layout.fillWidth: true
wrapMode: Text.WordWrap
}
GhostButton { text: "Open settings"; onClicked: window.page = 4 }
}
}
Item { Layout.preferredHeight: 10 }
}
Dialog {
id: addDialog
anchors.centerIn: Overlay.overlay
width: 380
modal: true
title: "New task"
standardButtons: Dialog.Cancel
ColumnLayout {
anchors.fill: parent
spacing: 12
TextField {
id: titleField
Layout.fillWidth: true
placeholderText: "What needs doing?"
}
TextField {
id: dueField
Layout.fillWidth: true
placeholderText: "Due date — YYYY-MM-DD (optional)"
}
Label {
text: "Local tasks live only here, so they are the one kind you can delete."
color: Theme.faint
font.pixelSize: 11
font.family: Theme.sansFamily
Layout.fillWidth: true
wrapMode: Text.WordWrap
}
PrimaryButton {
text: "Add"
enabled: titleField.text.trim() !== ""
Layout.alignment: Qt.AlignRight
onClicked: {
controller.createTask(titleField.text.trim(), dueField.text.trim())
titleField.text = ""; dueField.text = ""
addDialog.close()
}
}
}
}
}