import QtQuick import QtQuick.Layouts import ZShell.Config import qs.Components import qs.Modules.Dashboard.Weather import qs.Services import qs.Helpers Item { id: root readonly property var today: Weather.forecast && Weather.forecast.length > 0 ? Weather.forecast[0] : null implicitWidth: layout.implicitWidth > 800 ? layout.implicitWidth : 840 implicitHeight: layout.implicitHeight Component.onCompleted: Weather.reload() ColumnLayout { id: layout anchors.fill: parent spacing: Tokens.spacing.medium RowLayout { Layout.leftMargin: Tokens.padding.large Layout.rightMargin: Tokens.padding.large Layout.fillWidth: true Column { spacing: Tokens.spacing.extraSmall CustomText { text: Weather.city || qsTr("Loading...") font.pointSize: Tokens.font.size.large color: Colors.palette.m3onSurface } CustomText { text: new Date().toLocaleDateString(Qt.locale("en_US"), "dddd, MMMM d") font.pointSize: Tokens.font.size.small color: Colors.palette.m3onSurfaceVariant } } Item { Layout.fillWidth: true } Row { spacing: Tokens.spacing.largeIncreased WeatherStat { icon: "wb_twilight" label: qsTr("Sunrise") value: Weather.sunrise colour: Colors.palette.m3tertiary } WeatherStat { icon: "bedtime" label: qsTr("Sunset") value: Weather.sunset colour: Colors.palette.m3tertiary } } } CustomRect { Layout.fillWidth: true implicitHeight: bigInfoRow.implicitHeight + bigInfoRow.anchors.margins * 2 radius: Tokens.rounding.large color: Colors.tPalette.m3surfaceContainer Graphs { id: bigInfoRow anchors.fill: parent anchors.margins: Tokens.padding.medium } } RowLayout { Layout.fillWidth: true spacing: Tokens.spacing.medium DetailCard { icon: Weather.icon label: qsTr("Temperature") value: Weather.temp colour: Colors.palette.m3primary } DetailCard { icon: "thermostat" label: qsTr("Feels like", "apparent temperature") value: Weather.feelsLike colour: Colors.palette.m3primary } DetailCard { icon: "water_drop" label: qsTr("Humidity") value: Strings.percent(Weather.humidity) colour: Colors.palette.m3secondary } DetailCard { icon: "air" label: qsTr("Wind") value: Weather.windSpeed ? qsTr("%1 km/h").arg(Weather.windSpeed) : "--" colour: Colors.palette.m3tertiary } } CustomText { Layout.topMargin: Tokens.spacing.medium Layout.leftMargin: Tokens.padding.medium visible: forecastRepeater.count > 0 text: qsTr("7-day forecast") font.pointSize: Tokens.font.size.normal color: Colors.palette.m3onSurface } RowLayout { Layout.fillWidth: true spacing: Tokens.spacing.medium Repeater { id: forecastRepeater model: Weather.forecast CustomRect { id: forecastItem required property int index required property var modelData Layout.fillWidth: true implicitHeight: forecastItemColumn.implicitHeight + Tokens.padding.medium * 2 radius: Tokens.rounding.large color: Colors.tPalette.m3surfaceContainer ColumnLayout { id: forecastItemColumn anchors.centerIn: parent spacing: Tokens.spacing.small CustomText { Layout.alignment: Qt.AlignHCenter text: forecastItem.index === 0 ? qsTr("Today", "forecast column") : new Date(forecastItem.modelData.date).toLocaleDateString(Qt.locale("en_US"), "ddd") font.pointSize: Tokens.font.size.small color: Colors.palette.m3primary } CustomText { Layout.topMargin: -Tokens.spacing.extraSmall Layout.alignment: Qt.AlignHCenter text: new Date(forecastItem.modelData.date).toLocaleDateString(Qt.locale("en_US"), "MMM d") font.pointSize: Tokens.font.size.small opacity: 0.7 color: Colors.palette.m3onSurfaceVariant } MaterialIcon { Layout.alignment: Qt.AlignHCenter text: forecastItem.modelData.icon font.pointSize: Tokens.font.size.extraLarge color: Colors.palette.m3secondary } CustomText { Layout.alignment: Qt.AlignHCenter text: { const min = Weather.formatTemp(forecastItem.modelData.minTempC, true); const max = Weather.formatTemp(forecastItem.modelData.maxTempC, true); return qsTr("%1 / %2", "min/max temperature").arg(min).arg(max); } font.pointSize: Tokens.font.size.small color: Colors.palette.m3tertiary } } } } } } component DetailCard: CustomRect { id: detailRoot property string icon property string label property string value property color colour Layout.fillWidth: true Layout.preferredHeight: 60 radius: Tokens.rounding.medium color: Colors.tPalette.m3surfaceContainer Row { anchors.centerIn: parent spacing: Tokens.spacing.medium MaterialIcon { text: detailRoot.icon color: detailRoot.colour font.pointSize: Tokens.font.size.large anchors.verticalCenter: parent.verticalCenter } Column { anchors.verticalCenter: parent.verticalCenter spacing: 0 CustomText { text: detailRoot.label font.pointSize: Tokens.font.size.small opacity: 0.7 horizontalAlignment: Text.AlignLeft } CustomText { text: detailRoot.value font.pointSize: Tokens.font.size.small horizontalAlignment: Text.AlignLeft } } } } component WeatherStat: Row { id: weatherStat property string icon property string label property string value property color colour spacing: Tokens.spacing.small MaterialIcon { text: weatherStat.icon font.pointSize: Tokens.font.size.extraLarge color: weatherStat.colour } Column { CustomText { text: weatherStat.label font.pointSize: Tokens.font.size.small color: Colors.palette.m3onSurfaceVariant } CustomText { text: weatherStat.value font.pointSize: Tokens.font.size.small color: Colors.palette.m3onSurface } } } }