volto-aemet 1.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. package/.changelog.draft +3 -0
  2. package/.release-it.json +34 -0
  3. package/CHANGELOG.md +11 -0
  4. package/LICENSE +21 -0
  5. package/README.md +323 -0
  6. package/babel.config.js +17 -0
  7. package/locales/de/LC_MESSAGES/volto.po +83 -0
  8. package/locales/en/LC_MESSAGES/volto.po +83 -0
  9. package/locales/es/LC_MESSAGES/volto.po +90 -0
  10. package/locales/pt_BR/LC_MESSAGES/volto.po +88 -0
  11. package/locales/volto.pot +85 -0
  12. package/news/.gitkeep +0 -0
  13. package/package.json +47 -0
  14. package/public/.gitkeep +0 -0
  15. package/src/components/Aemet/Weather.js +289 -0
  16. package/src/components/Aemet/Weather.test.js +398 -0
  17. package/src/components/Aemet/WeatherCurrent.jsx +97 -0
  18. package/src/components/Aemet/WeatherCurrent.test.jsx +282 -0
  19. package/src/components/Aemet/index.js +9 -0
  20. package/src/components/Blocks/AemetWeatherCurrent/View.jsx +11 -0
  21. package/src/components/Blocks/AemetWeatherForecast/Edit.jsx +72 -0
  22. package/src/components/Blocks/AemetWeatherForecast/View.jsx +32 -0
  23. package/src/components/Blocks/AemetWeatherForecast/messages.js +36 -0
  24. package/src/components/Blocks/AemetWeatherForecast/schema.js +37 -0
  25. package/src/config/blocks.ts +70 -0
  26. package/src/config/settings.ts +16 -0
  27. package/src/icons/aemet-logo.svg +311 -0
  28. package/src/icons/weather/temps/11.png +0 -0
  29. package/src/icons/weather/temps/11n.png +0 -0
  30. package/src/icons/weather/temps/12.png +0 -0
  31. package/src/icons/weather/temps/12n.png +0 -0
  32. package/src/icons/weather/temps/13.png +0 -0
  33. package/src/icons/weather/temps/13n.png +0 -0
  34. package/src/icons/weather/temps/14.png +0 -0
  35. package/src/icons/weather/temps/14n.png +0 -0
  36. package/src/icons/weather/temps/15.png +0 -0
  37. package/src/icons/weather/temps/15n.png +0 -0
  38. package/src/icons/weather/temps/16.png +0 -0
  39. package/src/icons/weather/temps/16n.png +0 -0
  40. package/src/icons/weather/temps/17.png +0 -0
  41. package/src/icons/weather/temps/17n.png +0 -0
  42. package/src/icons/weather/temps/23.png +0 -0
  43. package/src/icons/weather/temps/23n.png +0 -0
  44. package/src/icons/weather/temps/24.png +0 -0
  45. package/src/icons/weather/temps/24n.png +0 -0
  46. package/src/icons/weather/temps/25.png +0 -0
  47. package/src/icons/weather/temps/25n.png +0 -0
  48. package/src/icons/weather/temps/26.png +0 -0
  49. package/src/icons/weather/temps/26n.png +0 -0
  50. package/src/icons/weather/temps/33.png +0 -0
  51. package/src/icons/weather/temps/33n.png +0 -0
  52. package/src/icons/weather/temps/34.png +0 -0
  53. package/src/icons/weather/temps/34n.png +0 -0
  54. package/src/icons/weather/temps/35.png +0 -0
  55. package/src/icons/weather/temps/35n.png +0 -0
  56. package/src/icons/weather/temps/36.png +0 -0
  57. package/src/icons/weather/temps/36n.png +0 -0
  58. package/src/icons/weather/temps/43.png +0 -0
  59. package/src/icons/weather/temps/43n.png +0 -0
  60. package/src/icons/weather/temps/44.png +0 -0
  61. package/src/icons/weather/temps/44n.png +0 -0
  62. package/src/icons/weather/temps/45.png +0 -0
  63. package/src/icons/weather/temps/45n.png +0 -0
  64. package/src/icons/weather/temps/46.png +0 -0
  65. package/src/icons/weather/temps/46n.png +0 -0
  66. package/src/icons/weather/temps/51.png +0 -0
  67. package/src/icons/weather/temps/51n.png +0 -0
  68. package/src/icons/weather/temps/52.png +0 -0
  69. package/src/icons/weather/temps/52n.png +0 -0
  70. package/src/icons/weather/temps/53.png +0 -0
  71. package/src/icons/weather/temps/54.png +0 -0
  72. package/src/icons/weather/temps/54n.png +0 -0
  73. package/src/icons/weather/temps/61.png +0 -0
  74. package/src/icons/weather/temps/61n.png +0 -0
  75. package/src/icons/weather/temps/62.png +0 -0
  76. package/src/icons/weather/temps/62n.png +0 -0
  77. package/src/icons/weather/temps/63.png +0 -0
  78. package/src/icons/weather/temps/63n.png +0 -0
  79. package/src/icons/weather/temps/64.png +0 -0
  80. package/src/icons/weather/temps/64n.png +0 -0
  81. package/src/icons/weather/temps/71.png +0 -0
  82. package/src/icons/weather/temps/71n.png +0 -0
  83. package/src/icons/weather/temps/72.png +0 -0
  84. package/src/icons/weather/temps/72n.png +0 -0
  85. package/src/icons/weather/temps/73.png +0 -0
  86. package/src/icons/weather/temps/74.png +0 -0
  87. package/src/index.ts +24 -0
  88. package/src/theme/components/_weather_current.scss +69 -0
  89. package/src/theme/main.scss +3 -0
  90. package/towncrier.toml +33 -0
  91. package/tsconfig.json +30 -0
@@ -0,0 +1,88 @@
1
+ msgid ""
2
+ msgstr ""
3
+ "Project-Id-Version: Plone\n"
4
+ "Report-Msgid-Bugs-To: \n"
5
+ "POT-Creation-Date: 2023-04-06T16:01:32.969Z\n"
6
+ "PO-Revision-Date: \n"
7
+ "Last-Translator: Plone i18n <plone-i18n@lists.sourceforge.net>\n"
8
+ "Language: \n"
9
+ "Language-Team: Plone i18n <plone-i18n@lists.sourceforge.net>\n"
10
+ "Content-Type: text/plain; charset=utf-8\n"
11
+ "Content-Transfer-Encoding: 8bit\n"
12
+ "Plural-Forms: nplurals=1; plural=0;\n"
13
+ "MIME-Version: 1.0\n"
14
+ "Language-Code: pt_BR\n"
15
+ "Language-Name: Português do Brasil\n"
16
+ "Preferred-Encodings: utf-8\n"
17
+ "Domain: volto\n"
18
+
19
+ #. Default: "AEMET Weather Forecast"
20
+ #: components/Blocks/AemetWeatherForecast/messages
21
+ msgid "AEMET Weather Forecast"
22
+ msgstr ""
23
+
24
+ #. Default: "Default"
25
+ #: components/Blocks/AemetWeatherForecast/messages
26
+ msgid "Default"
27
+ msgstr ""
28
+
29
+ #. Default: "Location ID"
30
+ #: components/Blocks/AemetWeatherForecast/messages
31
+ msgid "Location ID"
32
+ msgstr ""
33
+
34
+ #. Default: "Province"
35
+ #: components/Blocks/AemetWeatherForecast/messages
36
+ msgid "Province"
37
+ msgstr ""
38
+
39
+ #. Default: "The Location ID of the AEMET service, for example '41091' to Sevilla location ID."
40
+ #: components/Blocks/AemetWeatherForecast/messages
41
+ msgid "The Location ID of the AEMET service, for example '41091' to Sevilla location ID."
42
+ msgstr ""
43
+
44
+ #. Default: "The name of the province of the location (in lowercase) of the AEMET service."
45
+ #: components/Blocks/AemetWeatherForecast/messages
46
+ msgid "The name of the province of the location (in lowercase) of the AEMET service."
47
+ msgstr ""
48
+
49
+ #. Default: "Current day"
50
+ #: components/Aemet/Weather
51
+ msgid "current_day"
52
+ msgstr ""
53
+
54
+ #. Default: "Current location"
55
+ #: components/Aemet/Weather
56
+ msgid "current_location"
57
+ msgstr ""
58
+
59
+ #. Default: "Current temperature"
60
+ #: components/Aemet/Weather
61
+ msgid "current_temperature"
62
+ msgstr ""
63
+
64
+ #. Default: "Current time"
65
+ #: components/Aemet/Weather
66
+ msgid "current_time"
67
+ msgstr ""
68
+
69
+ #. Default: "Loading weather forecast..."
70
+ #: components/Aemet/Weather
71
+ #: components/Aemet/WeatherCurrent
72
+ msgid "loading_weather_forecast"
73
+ msgstr ""
74
+
75
+ #. Default: "Minimum-Maximum"
76
+ #: components/Aemet/Weather
77
+ msgid "minimum_maximum"
78
+ msgstr ""
79
+
80
+ #. Default: "Not available"
81
+ #: components/Aemet/Weather
82
+ msgid "not_available"
83
+ msgstr ""
84
+
85
+ #. Default: "Weather description"
86
+ #: components/Aemet/Weather
87
+ msgid "weather_description"
88
+ msgstr ""
@@ -0,0 +1,85 @@
1
+ msgid ""
2
+ msgstr ""
3
+ "Project-Id-Version: Plone\n"
4
+ "POT-Creation-Date: 2026-07-19T12:29:19.597Z\n"
5
+ "Last-Translator: Plone i18n <plone-i18n@lists.sourceforge.net>\n"
6
+ "Language-Team: Plone i18n <plone-i18n@lists.sourceforge.net>\n"
7
+ "Content-Type: text/plain; charset=utf-8\n"
8
+ "Content-Transfer-Encoding: 8bit\n"
9
+ "Plural-Forms: nplurals=1; plural=0;\n"
10
+ "MIME-Version: 1.0\n"
11
+ "Language-Code: en\n"
12
+ "Language-Name: English\n"
13
+ "Preferred-Encodings: utf-8\n"
14
+ "Domain: volto\n"
15
+
16
+ #. Default: "AEMET Weather Forecast"
17
+ #: components/Blocks/AemetWeatherForecast/messages
18
+ msgid "AEMET Weather Forecast"
19
+ msgstr ""
20
+
21
+ #. Default: "Default"
22
+ #: components/Blocks/AemetWeatherForecast/messages
23
+ msgid "Default"
24
+ msgstr ""
25
+
26
+ #. Default: "Location ID"
27
+ #: components/Blocks/AemetWeatherForecast/messages
28
+ msgid "Location ID"
29
+ msgstr ""
30
+
31
+ #. Default: "Province"
32
+ #: components/Blocks/AemetWeatherForecast/messages
33
+ msgid "Province"
34
+ msgstr ""
35
+
36
+ #. Default: "The Location ID of the AEMET service, for example '41091' to Sevilla location ID."
37
+ #: components/Blocks/AemetWeatherForecast/messages
38
+ msgid "The Location ID of the AEMET service, for example '41091' to Sevilla location ID."
39
+ msgstr ""
40
+
41
+ #. Default: "The name of the province of the location (in lowercase) of the AEMET service."
42
+ #: components/Blocks/AemetWeatherForecast/messages
43
+ msgid "The name of the province of the location (in lowercase) of the AEMET service."
44
+ msgstr ""
45
+
46
+ #. Default: "Current day"
47
+ #: components/Aemet/Weather
48
+ msgid "current_day"
49
+ msgstr ""
50
+
51
+ #. Default: "Current location"
52
+ #: components/Aemet/Weather
53
+ msgid "current_location"
54
+ msgstr ""
55
+
56
+ #. Default: "Current temperature"
57
+ #: components/Aemet/Weather
58
+ msgid "current_temperature"
59
+ msgstr ""
60
+
61
+ #. Default: "Current time"
62
+ #: components/Aemet/Weather
63
+ msgid "current_time"
64
+ msgstr ""
65
+
66
+ #. Default: "Loading weather forecast..."
67
+ #: components/Aemet/Weather
68
+ #: components/Aemet/WeatherCurrent
69
+ msgid "loading_weather_forecast"
70
+ msgstr ""
71
+
72
+ #. Default: "Minimum-Maximum"
73
+ #: components/Aemet/Weather
74
+ msgid "minimum_maximum"
75
+ msgstr ""
76
+
77
+ #. Default: "Not available"
78
+ #: components/Aemet/Weather
79
+ msgid "not_available"
80
+ msgstr ""
81
+
82
+ #. Default: "Weather description"
83
+ #: components/Aemet/Weather
84
+ msgid "weather_description"
85
+ msgstr ""
package/news/.gitkeep ADDED
File without changes
package/package.json ADDED
@@ -0,0 +1,47 @@
1
+ {
2
+ "name": "volto-aemet",
3
+ "version": "1.0.0-alpha.0",
4
+ "description": "A volto add-on that integrates AEMET service with Plone to report the weather forecast.",
5
+ "author": "Leonardo J. Caballero G.",
6
+ "homepage": "https://github.com/collective/volto-aemet#readme",
7
+ "main": "src/index.ts",
8
+ "license": "MIT",
9
+ "keywords": [
10
+ "volto-addon",
11
+ "volto",
12
+ "block",
13
+ "plone",
14
+ "react",
15
+ "aemet",
16
+ "weather",
17
+ "forecast",
18
+ "integration"
19
+ ],
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git@github.com:collective/volto-aemet.git"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "addons": [],
28
+ "dependencies": {
29
+ "@plone/components": "3.0.3"
30
+ },
31
+ "peerDependencies": {
32
+ "react": "18.2.0",
33
+ "react-dom": "18.2.0",
34
+ "react-intl": "^3.12.1"
35
+ },
36
+ "devDependencies": {
37
+ "@plone/scripts": "^3.6.1",
38
+ "release-it": "^19.0.2"
39
+ },
40
+ "scripts": {
41
+ "i18n": "rm -rf build/messages && NODE_ENV=production i18n --addon",
42
+ "dry-release": "release-it --dry-run",
43
+ "release": "release-it",
44
+ "release-major-alpha": "release-it major --preRelease=alpha",
45
+ "release-alpha": "release-it --preRelease=alpha"
46
+ }
47
+ }
File without changes
@@ -0,0 +1,289 @@
1
+ /**
2
+ * Weather shared logic — data fetching and icon mapping.
3
+ * @module components/Aemet/Weather
4
+ */
5
+
6
+ import { useState, useEffect } from 'react';
7
+ import { defineMessages, useIntl } from 'react-intl';
8
+ import moment from 'moment';
9
+ import config from '@plone/volto/registry';
10
+
11
+ import climate11Icon from 'volto-aemet/icons/weather/temps/11.png';
12
+ import climate11nIcon from 'volto-aemet/icons/weather/temps/11n.png';
13
+ import climate12Icon from 'volto-aemet/icons/weather/temps/12.png';
14
+ import climate12nIcon from 'volto-aemet/icons/weather/temps/12n.png';
15
+ import climate13Icon from 'volto-aemet/icons/weather/temps/13.png';
16
+ import climate13nIcon from 'volto-aemet/icons/weather/temps/13n.png';
17
+ import climate14Icon from 'volto-aemet/icons/weather/temps/14.png';
18
+ import climate14nIcon from 'volto-aemet/icons/weather/temps/14n.png';
19
+ import climate15Icon from 'volto-aemet/icons/weather/temps/15.png';
20
+ import climate15nIcon from 'volto-aemet/icons/weather/temps/15n.png';
21
+ import climate16Icon from 'volto-aemet/icons/weather/temps/16.png';
22
+ import climate16nIcon from 'volto-aemet/icons/weather/temps/16n.png';
23
+ import climate17Icon from 'volto-aemet/icons/weather/temps/17.png';
24
+ import climate17nIcon from 'volto-aemet/icons/weather/temps/17n.png';
25
+ import climate23Icon from 'volto-aemet/icons/weather/temps/23.png';
26
+ import climate23nIcon from 'volto-aemet/icons/weather/temps/23n.png';
27
+ import climate24Icon from 'volto-aemet/icons/weather/temps/24.png';
28
+ import climate24nIcon from 'volto-aemet/icons/weather/temps/24n.png';
29
+ import climate25Icon from 'volto-aemet/icons/weather/temps/25.png';
30
+ import climate25nIcon from 'volto-aemet/icons/weather/temps/25n.png';
31
+ import climate26Icon from 'volto-aemet/icons/weather/temps/26.png';
32
+ import climate26nIcon from 'volto-aemet/icons/weather/temps/26n.png';
33
+ import climate33Icon from 'volto-aemet/icons/weather/temps/33.png';
34
+ import climate33nIcon from 'volto-aemet/icons/weather/temps/33n.png';
35
+ import climate34Icon from 'volto-aemet/icons/weather/temps/34.png';
36
+ import climate34nIcon from 'volto-aemet/icons/weather/temps/34n.png';
37
+ import climate35Icon from 'volto-aemet/icons/weather/temps/35.png';
38
+ import climate35nIcon from 'volto-aemet/icons/weather/temps/35n.png';
39
+ import climate36Icon from 'volto-aemet/icons/weather/temps/36.png';
40
+ import climate36nIcon from 'volto-aemet/icons/weather/temps/36n.png';
41
+ import climate43Icon from 'volto-aemet/icons/weather/temps/43.png';
42
+ import climate43nIcon from 'volto-aemet/icons/weather/temps/43n.png';
43
+ import climate44Icon from 'volto-aemet/icons/weather/temps/44.png';
44
+ import climate44nIcon from 'volto-aemet/icons/weather/temps/44n.png';
45
+ import climate45Icon from 'volto-aemet/icons/weather/temps/45.png';
46
+ import climate45nIcon from 'volto-aemet/icons/weather/temps/45n.png';
47
+ import climate46Icon from 'volto-aemet/icons/weather/temps/46.png';
48
+ import climate46nIcon from 'volto-aemet/icons/weather/temps/46n.png';
49
+ import climate51Icon from 'volto-aemet/icons/weather/temps/51.png';
50
+ import climate51nIcon from 'volto-aemet/icons/weather/temps/51n.png';
51
+ import climate52Icon from 'volto-aemet/icons/weather/temps/52.png';
52
+ import climate52nIcon from 'volto-aemet/icons/weather/temps/52n.png';
53
+ import climate53Icon from 'volto-aemet/icons/weather/temps/53.png';
54
+ // import climate53nIcon from 'volto-aemet/icons/weather/temps/53n.png';
55
+ import climate54Icon from 'volto-aemet/icons/weather/temps/54.png';
56
+ import climate54nIcon from 'volto-aemet/icons/weather/temps/54n.png';
57
+ import climate61Icon from 'volto-aemet/icons/weather/temps/61.png';
58
+ import climate61nIcon from 'volto-aemet/icons/weather/temps/61n.png';
59
+ import climate62Icon from 'volto-aemet/icons/weather/temps/62.png';
60
+ import climate62nIcon from 'volto-aemet/icons/weather/temps/62n.png';
61
+ import climate63Icon from 'volto-aemet/icons/weather/temps/63.png';
62
+ import climate63nIcon from 'volto-aemet/icons/weather/temps/63n.png';
63
+ import climate64Icon from 'volto-aemet/icons/weather/temps/64.png';
64
+ import climate64nIcon from 'volto-aemet/icons/weather/temps/64n.png';
65
+ import climate71Icon from 'volto-aemet/icons/weather/temps/71.png';
66
+ import climate71nIcon from 'volto-aemet/icons/weather/temps/71n.png';
67
+ import climate72Icon from 'volto-aemet/icons/weather/temps/72.png';
68
+ import climate72nIcon from 'volto-aemet/icons/weather/temps/72n.png';
69
+ import climate73Icon from 'volto-aemet/icons/weather/temps/73.png';
70
+ // import climate73nIcon from 'volto-aemet/icons/weather/temps/73n.png';
71
+ import climate74Icon from 'volto-aemet/icons/weather/temps/74.png';
72
+ // import climate74nIcon from 'volto-aemet/icons/weather/temps/74n.png';
73
+
74
+ // ---------------------------------------------------------------------------
75
+ // i18n messages
76
+ // ---------------------------------------------------------------------------
77
+
78
+ export const weatherMessages = defineMessages({
79
+ minimumMaximum: {
80
+ id: 'minimum_maximum',
81
+ defaultMessage: 'Minimum-Maximum',
82
+ },
83
+ currentDay: {
84
+ id: 'current_day',
85
+ defaultMessage: 'Current day',
86
+ },
87
+ currentLocation: {
88
+ id: 'current_location',
89
+ defaultMessage: 'Current location',
90
+ },
91
+ currentTemperature: {
92
+ id: 'current_temperature',
93
+ defaultMessage: 'Current temperature',
94
+ },
95
+ currentTime: {
96
+ id: 'current_time',
97
+ defaultMessage: 'Current time',
98
+ },
99
+ loadingCurrent: {
100
+ id: 'loading_weather_forecast',
101
+ defaultMessage: 'Loading weather forecast...',
102
+ },
103
+ weatherNotAvailable: {
104
+ id: 'not_available',
105
+ defaultMessage: 'Not available',
106
+ },
107
+ weatherDescription: {
108
+ id: 'weather_description',
109
+ defaultMessage: 'Weather description',
110
+ },
111
+ });
112
+
113
+ // ---------------------------------------------------------------------------
114
+ // Icon mapping
115
+ // ---------------------------------------------------------------------------
116
+
117
+ const weatherIconMap = {
118
+ 11: climate11Icon,
119
+ '11n': climate11nIcon,
120
+ 12: climate12Icon,
121
+ '12n': climate12nIcon,
122
+ 13: climate13Icon,
123
+ '13n': climate13nIcon,
124
+ 14: climate14Icon,
125
+ '14n': climate14nIcon,
126
+ 15: climate15Icon,
127
+ '15n': climate15nIcon,
128
+ 16: climate16Icon,
129
+ '16n': climate16nIcon,
130
+ 17: climate17Icon,
131
+ '17n': climate17nIcon,
132
+ 23: climate23Icon,
133
+ '23n': climate23nIcon,
134
+ 24: climate24Icon,
135
+ '24n': climate24nIcon,
136
+ 25: climate25Icon,
137
+ '25n': climate25nIcon,
138
+ 26: climate26Icon,
139
+ '26n': climate26nIcon,
140
+ 33: climate33Icon,
141
+ '33n': climate33nIcon,
142
+ 34: climate34Icon,
143
+ '34n': climate34nIcon,
144
+ 35: climate35Icon,
145
+ '35n': climate35nIcon,
146
+ 36: climate36Icon,
147
+ '36n': climate36nIcon,
148
+ 43: climate43Icon,
149
+ '43n': climate43nIcon,
150
+ 44: climate44Icon,
151
+ '44n': climate44nIcon,
152
+ 45: climate45Icon,
153
+ '45n': climate45nIcon,
154
+ 46: climate46Icon,
155
+ '46n': climate46nIcon,
156
+ 51: climate51Icon,
157
+ '51n': climate51nIcon,
158
+ 52: climate52Icon,
159
+ '52n': climate52nIcon,
160
+ 53: climate53Icon,
161
+ '53n': climate53Icon, // Note: using same icon for day/night
162
+ 54: climate54Icon,
163
+ '54n': climate54nIcon,
164
+ 61: climate61Icon,
165
+ '61n': climate61nIcon,
166
+ 62: climate62Icon,
167
+ '62n': climate62nIcon,
168
+ 63: climate63Icon,
169
+ '63n': climate63nIcon,
170
+ 64: climate64Icon,
171
+ '64n': climate64nIcon,
172
+ 71: climate71Icon,
173
+ '71n': climate71nIcon,
174
+ 72: climate72Icon,
175
+ '72n': climate72nIcon,
176
+ 73: climate73Icon,
177
+ '73n': climate73Icon, // Note: using same icon for day/night
178
+ 74: climate74Icon,
179
+ '74n': climate74Icon, // Note: using same icon for day/night
180
+ };
181
+
182
+ /**
183
+ * Returns the weather icon image for a given AEMET sky-state value.
184
+ * The value can be numeric ("46") or include a night suffix ("46n").
185
+ */
186
+ export const getWeatherIcon = (skyStateValue) => {
187
+ const cleanValue = skyStateValue?.trim();
188
+
189
+ if (weatherIconMap[cleanValue]) {
190
+ return weatherIconMap[cleanValue];
191
+ }
192
+
193
+ // Fallback: strip the night suffix and try again
194
+ const numericValue = cleanValue?.replace(/n$/i, '');
195
+ if (weatherIconMap[numericValue]) {
196
+ return weatherIconMap[numericValue];
197
+ }
198
+
199
+ return climate11Icon;
200
+ };
201
+
202
+ // ---------------------------------------------------------------------------
203
+ // Data-fetching hook
204
+ // ---------------------------------------------------------------------------
205
+
206
+ /**
207
+ * Custom hook that fetches today's weather forecast from the AEMET backend
208
+ * endpoint and returns the transformed data.
209
+ *
210
+ * @returns {{ forecast: Array, isLoading: boolean }}
211
+ */
212
+ export const useWeatherData = () => {
213
+ const [forecast, setForecast] = useState([]);
214
+ const [isLoading, setIsLoading] = useState(true);
215
+ const apiPath = config.settings.apiPath;
216
+ const intl = useIntl();
217
+
218
+ useEffect(() => {
219
+ const fetchWeather = async () => {
220
+ try {
221
+ const apiUrl = `${apiPath}/++api++/@aemet-weather-forecast`;
222
+
223
+ const response = await fetch(apiUrl, {
224
+ headers: {
225
+ Accept: 'application/json',
226
+ 'Content-Type': 'application/json',
227
+ },
228
+ });
229
+
230
+ if (!response.ok) {
231
+ throw new Error(`HTTP error! status: ${response.status}`);
232
+ }
233
+
234
+ const data = await response.json();
235
+
236
+ if (data && data.forecast && data.forecast.length > 0) {
237
+ const todayForecast = data.forecast.find(
238
+ (day) => day.date === moment().format('YYYY-MM-DD'),
239
+ );
240
+
241
+ if (todayForecast) {
242
+ setForecast([
243
+ {
244
+ currentHour: todayForecast.currentHour,
245
+ date: todayForecast.date,
246
+ name: todayForecast.name,
247
+ provinceName: todayForecast.province,
248
+ skyStateDescription: todayForecast.skyState,
249
+ skyStateText: todayForecast.skyStateValue,
250
+ minTemp: todayForecast.tempMin,
251
+ maxTemp: todayForecast.tempMax,
252
+ timePeriod: todayForecast.timePeriod,
253
+ },
254
+ ]);
255
+ } else {
256
+ throw new Error('No forecast data available for today');
257
+ }
258
+ } else {
259
+ throw new Error('Invalid response format from backend');
260
+ }
261
+ } catch (error) {
262
+ // eslint-disable-next-line no-console
263
+ console.error('Error fetching weather data:', error.message);
264
+
265
+ setForecast([
266
+ {
267
+ currentHour: moment().date(),
268
+ date: moment().format('YYYY-MM-DD'),
269
+ name: 'Sevilla',
270
+ provinceName: 'Sevilla',
271
+ skyStateDescription: intl.formatMessage(
272
+ weatherMessages.weatherNotAvailable,
273
+ ),
274
+ skyStateText: '11',
275
+ minTemp: '--',
276
+ maxTemp: '--',
277
+ timePeriod: intl.formatMessage(weatherMessages.weatherNotAvailable),
278
+ },
279
+ ]);
280
+ } finally {
281
+ setIsLoading(false);
282
+ }
283
+ };
284
+
285
+ fetchWeather();
286
+ }, [intl, apiPath]);
287
+
288
+ return { forecast, isLoading };
289
+ };