7.2 KiB · text History 35c3541
import QtQuick
import QtQuick.Controls as QQC2
import QtQuick.Layouts
import org.kde.kirigami as Kirigami
import org.kde.kcmutils as KCM

import "Grafana.js" as G

// Which panels' values sit in the panel next to the logo. Any panel can be
// pinned; it shows its first series, reduced the way the panel reduces it.
KCM.SimpleKCM {
    id: configPinned

    property string cfg_pinned: "[]"
    property string cfg_dashboards: "[]"
    property string cfg_instanceUrl: ""
    property string cfg_accessToken: ""
    property alias cfg_pinnedLabels: labelsCheckBox.checked

    readonly property var pinned: parse(cfg_pinned)
    readonly property var dashboardsChosen: parse(cfg_dashboards)

    property string browsing: ""
    property var panels: []
    property bool isLoading: false
    property string loadError: ""

    function parse(text) {
        try {
            var v = JSON.parse(text || "[]")
            return Array.isArray(v) ? v : []
        } catch (e) {
            return []
        }
    }

    GrafanaAPI {
        id: api
        baseUrl: configPinned.cfg_instanceUrl
        accessToken: configPinned.cfg_accessToken
    }

    function browse(uid) {
        browsing = uid
        panels = []
        if (!uid) return
        isLoading = true
        loadError = ""
        api.dashboard(uid, function(ok, data) {
            isLoading = false
            if (!ok) { loadError = data; return }
            panels = G.allPanels(data.dashboard).filter(function(p) { return p.type !== "text" && !p.repeat })
                .map(function(p) { return { id: p.id, title: p.title || i18n("(untitled)"), type: p.type } })
        })
    }

    function save(list) { cfg_pinned = JSON.stringify(list) }
    function isPinned(uid, id) { return pinned.some(function(p) { return p.uid === uid && p.panelId === id }) }
    function titleOf(uid) {
        for (var i = 0; i < dashboardsChosen.length; i++) if (dashboardsChosen[i].uid === uid) return dashboardsChosen[i].title
        return uid
    }
    function pin(p) {
        if (isPinned(browsing, p.id)) return
        save(pinned.concat([{ uid: browsing, panelId: p.id, title: p.title, dashboardTitle: titleOf(browsing) }]))
    }
    function removeAt(i) { var l = pinned.slice(); l.splice(i, 1); save(l) }
    function move(i, delta) {
        var t = i + delta
        if (t < 0 || t >= pinned.length) return
        var l = pinned.slice()
        var item = l.splice(i, 1)[0]
        l.splice(t, 0, item)
        save(l)
    }
    function setLabel(i, label) {
        var l = pinned.slice()
        l[i] = Object.assign({}, l[i], { label: label })
        if (!label) delete l[i].label
        save(l)
    }

    Component.onCompleted: if (dashboardsChosen.length) browse(dashboardsChosen[0].uid)

    ColumnLayout {
        spacing: Kirigami.Units.largeSpacing

        Kirigami.Heading {
            level: 3
            text: i18n("Pinned to the panel")
        }

        QQC2.Label {
            visible: pinned.length === 0
            Layout.fillWidth: true
            wrapMode: Text.WordWrap
            opacity: 0.7
            text: i18n("Nothing pinned: the panel shows just the logo. Pin a stat (or any panel) below to keep its value in view.")
        }

        Repeater {
            model: pinned

            delegate: RowLayout {
                required property var modelData
                required property int index
                Layout.fillWidth: true
                spacing: Kirigami.Units.smallSpacing

                Kirigami.Icon {
                    source: "window-pin"
                    Layout.preferredWidth: Kirigami.Units.iconSizes.small
                    Layout.preferredHeight: Kirigami.Units.iconSizes.small
                }

                QQC2.Label {
                    Layout.fillWidth: true
                    elide: Text.ElideRight
                    text: modelData.title + "  ·  " + (modelData.dashboardTitle || modelData.uid)
                }

                QQC2.TextField {
                    Layout.preferredWidth: Kirigami.Units.gridUnit * 7
                    placeholderText: i18n("Label")
                    text: modelData.label || ""
                    onEditingFinished: if (text !== (modelData.label || "")) setLabel(index, text)
                    QQC2.ToolTip.text: i18n("Shown above the value instead of the panel title")
                    QQC2.ToolTip.visible: hovered
                }

                QQC2.ToolButton { icon.name: "go-up"; enabled: index > 0; onClicked: move(index, -1) }
                QQC2.ToolButton { icon.name: "go-down"; enabled: index < pinned.length - 1; onClicked: move(index, 1) }
                QQC2.ToolButton { icon.name: "list-remove"; onClicked: removeAt(index) }
            }
        }

        QQC2.CheckBox {
            id: labelsCheckBox
            text: i18n("Show titles above the values in a horizontal panel")
        }

        Kirigami.Heading {
            level: 3
            text: i18n("Panels")
        }

        QQC2.Label {
            visible: dashboardsChosen.length === 0
            Layout.fillWidth: true
            wrapMode: Text.WordWrap
            opacity: 0.7
            text: i18n("Add dashboards on the General page first.")
        }

        RowLayout {
            visible: dashboardsChosen.length > 0
            Layout.fillWidth: true

            QQC2.ComboBox {
                id: dashPicker
                Layout.fillWidth: true
                model: dashboardsChosen
                textRole: "title"
                onActivated: function(i) { browse(dashboardsChosen[i].uid) }
            }

            QQC2.BusyIndicator {
                visible: isLoading
                Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
                Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium
            }
        }

        QQC2.Label {
            visible: loadError !== ""
            Layout.fillWidth: true
            wrapMode: Text.WordWrap
            color: Kirigami.Theme.negativeTextColor
            text: loadError
        }

        Repeater {
            model: panels

            delegate: RowLayout {
                required property var modelData
                Layout.fillWidth: true

                Kirigami.Icon {
                    source: modelData.type === "stat" ? "format-number-percent"
                          : modelData.type === "timeseries" ? "office-chart-line"
                          : modelData.type === "table" ? "table" : "office-chart-bar"
                    Layout.preferredWidth: Kirigami.Units.iconSizes.small
                    Layout.preferredHeight: Kirigami.Units.iconSizes.small
                }

                QQC2.Label {
                    Layout.fillWidth: true
                    elide: Text.ElideRight
                    text: modelData.title + "  <font color=\"" + Kirigami.Theme.disabledTextColor + "\">" + modelData.type + "</font>"
                    textFormat: Text.StyledText
                }

                QQC2.Button {
                    text: isPinned(browsing, modelData.id) ? i18n("Pinned") : i18n("Pin")
                    icon.name: isPinned(browsing, modelData.id) ? "checkmark" : "window-pin"
                    enabled: !isPinned(browsing, modelData.id)
                    onClicked: pin(modelData)
                }
            }
        }
    }
}