diff --git a/qml/pages/CalendarPage.qml b/qml/pages/CalendarPage.qml
new file mode 100644
index 0000000..e1b8d26
--- /dev/null
+++ b/qml/pages/CalendarPage.qml
@@ -0,0 +1,96 @@
+import QtQuick 2.0
+import Sailfish.Silica 1.0
+import "../js/tides.js" as Tides
+
+// Calendario maree: i prossimi 30 giorni con le ore di alta e bassa marea.
+Page {
+ id: page
+
+ property var port
+
+ SilicaListView {
+ id: list
+ anchors.fill: parent
+ model: ListModel { id: dayModel }
+
+ header: PageHeader {
+ title: page.port ? page.port[0] : ""
+ description: qsTr("Next 30 days — high and low tides")
+ }
+
+ delegate: Item {
+ width: parent.width
+ height: model.isDay ? Theme.itemSizeSmall : Theme.itemSizeSmall * 0.8
+
+ // riga del giorno
+ Rectangle {
+ visible: model.isDay
+ anchors.fill: parent
+ color: Theme.rgba(Theme.highlightBackgroundColor, 0.2)
+ Label {
+ text: model.day
+ anchors.left: parent.left
+ anchors.leftMargin: Theme.horizontalPageMargin
+ anchors.verticalCenter: parent.verticalCenter
+ color: Theme.highlightColor
+ font.pixelSize: Theme.fontSizeSmall
+ }
+ }
+
+ // riga evento (alta/bassa)
+ Label {
+ visible: !model.isDay
+ text: model.kind === "high" ? "▲" : "▼"
+ color: model.kind === "high" ? Theme.highlightColor : Theme.errorColor
+ font.pixelSize: Theme.fontSizeSmall
+ anchors.left: parent.left
+ anchors.leftMargin: Theme.horizontalPageMargin
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ Label {
+ visible: !model.isDay
+ text: model.time
+ color: Theme.primaryColor
+ anchors.left: parent.left
+ anchors.leftMargin: Theme.horizontalPageMargin + 36
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ Label {
+ visible: !model.isDay
+ text: model.height
+ color: Theme.secondaryColor
+ font.pixelSize: Theme.fontSizeSmall
+ anchors.right: parent.right
+ anchors.rightMargin: Theme.horizontalPageMargin
+ anchors.verticalCenter: parent.verticalCenter
+ }
+ }
+
+ VerticalScrollDecorator {}
+ }
+
+ Component.onCompleted: build()
+
+ function build() {
+ dayModel.clear()
+ if (!port) {
+ console.log("[tide] calendar: port undefined")
+ return
+ }
+ console.log("[tide] calendar build:", port[0])
+ var now = new Date()
+ for (var g = 0; g < 30; g++) {
+ var d = new Date(now.getFullYear(), now.getMonth(), now.getDate() + g)
+ var ex = Tides.extrema(port, d, 1)
+ dayModel.append({ isDay: true, day: Tides.fmtDate(d.getTime()),
+ time: "", kind: "", height: "" })
+ for (var i = 0; i < ex.length; i++) {
+ dayModel.append({ isDay: false, day: "",
+ time: Tides.fmtHM(ex[i].t),
+ kind: ex[i].kind,
+ height: Tides.fmtH(ex[i].h, appSettings.unit) })
+ }
+ }
+ console.log("[tide] calendar giorni:", 30, "righe:", dayModel.count)
+ }
+}
diff --git a/qml/pages/DetailPage.qml b/qml/pages/DetailPage.qml
index 0cf9511..9206f61 100644
--- a/qml/pages/DetailPage.qml
+++ b/qml/pages/DetailPage.qml
@@ -49,6 +49,12 @@ Page {
{ port: port, portIndex: Tides.PORTS.indexOf(port), custom: false })
}
+ function openCalendar() {
+ console.log("[tide] openCalendar:", page.port ? page.port[0] : "undefined")
+ pageStack.push(Qt.resolvedUrl("CalendarPage.qml"),
+ { port: page.port })
+ }
+
function refresh() {
if (!port) {
console.log("[tide] refresh: port UNDEFINED")
@@ -378,6 +384,11 @@ Page {
pageStack.replace(Qt.resolvedUrl("MainPage.qml"))
}
}
+ MenuItem {
+ text: qsTr("30-day calendar")
+ visible: page.port !== undefined
+ onClicked: page.openCalendar()
+ }
MenuItem {
text: qsTr("Settings")
onClicked: pageStack.push(Qt.resolvedUrl("SettingsPage.qml"))
diff --git a/translations/harbour-tide.ts b/translations/harbour-tide.ts
index 5d9a793..4585c9d 100644
--- a/translations/harbour-tide.ts
+++ b/translations/harbour-tide.ts
@@ -22,6 +22,7 @@
DetailPage
+ 30-day calendar
lat %1°
📍 Nearest port: %1 — %2 · ±%3 m
24 h
@@ -69,4 +70,8 @@
CoverPage
No port selected
+
+ CalendarPage
+ Next 30 days — high and low tides
+
diff --git a/translations/harbour-tide_it.ts b/translations/harbour-tide_it.ts
index 142d44c..dfc4bb3 100644
--- a/translations/harbour-tide_it.ts
+++ b/translations/harbour-tide_it.ts
@@ -22,6 +22,7 @@
DetailPage
+ 30-day calendarCalendario 30 giorni
lat %1°lat %1°
📍 Nearest port: %1 — %2 · ±%3 m📍 Porto più vicino: %1 — %2 · ±%3 m
24 h24 h
@@ -69,4 +70,8 @@
CoverPage
No port selectedNessun porto selezionato
+
+ CalendarPage
+ Next 30 days — high and low tidesProssimi 30 giorni — alte e basse maree
+