@vixoniccom/aqi 0.0.3-dev.4 → 0.0.3
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.
- package/.env +1 -0
- package/.github/workflows/main.yml +132 -0
- package/.github/workflows/sonarqube.yml +5 -5
- package/CHANGELOG.md +11 -0
- package/build/main.js +1 -1
- package/build.zip +0 -0
- package/configuration/appeareanceGroup/AppeareanceInputs.ts +10 -2
- package/configuration.json +19 -0
- package/package.json +3 -3
- package/sonar-project.properties +2 -1
- package/src/App.tsx +5 -11
- package/src/components/Card.tsx +3 -1
- package/src/parameters.d.ts +1 -1
- package/src/services/index.ts +6 -21
- package/.github/workflows/npm-publish.yml +0 -105
package/build.zip
CHANGED
|
Binary file
|
|
@@ -1,9 +1,17 @@
|
|
|
1
|
-
import { Label, NumberInput, SelectAssetKna, TextFormat, TextInput } from '@vixoniccom/modules'
|
|
1
|
+
import { Label, NumberInput, SelectAssetKna, SelectInput, TextFormat, TextInput } from '@vixoniccom/modules'
|
|
2
2
|
|
|
3
3
|
export const appeareanceInputs = [
|
|
4
4
|
new Label({ label: 'General' }),
|
|
5
5
|
new SelectAssetKna({ id: 'backgroundImage', label: 'Fondo', required: false, extensions: ['jpg', 'png'] }),
|
|
6
|
-
|
|
6
|
+
new Label({ label: 'Posición' }),
|
|
7
|
+
new SelectInput({
|
|
8
|
+
id: 'positionCard',
|
|
9
|
+
label: 'Tipo de posición',
|
|
10
|
+
items: [
|
|
11
|
+
{ label: 'Horizontal', value: 'horizontal' },
|
|
12
|
+
{ label: 'Vertical', value: 'vertical' },
|
|
13
|
+
],
|
|
14
|
+
}),
|
|
7
15
|
new Label({ label: 'Diseño de la tarjeta' }),
|
|
8
16
|
new NumberInput({
|
|
9
17
|
id: 'topSeparationCard',
|
package/configuration.json
CHANGED
|
@@ -20,6 +20,25 @@
|
|
|
20
20
|
],
|
|
21
21
|
"multiple": false
|
|
22
22
|
},
|
|
23
|
+
{
|
|
24
|
+
"type": "label",
|
|
25
|
+
"label": "Posición"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"id": "positionCard",
|
|
29
|
+
"label": "Tipo de posición",
|
|
30
|
+
"type": "select-input",
|
|
31
|
+
"items": [
|
|
32
|
+
{
|
|
33
|
+
"value": "horizontal",
|
|
34
|
+
"label": "Horizontal"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"value": "vertical",
|
|
38
|
+
"label": "Vertical"
|
|
39
|
+
}
|
|
40
|
+
]
|
|
41
|
+
},
|
|
23
42
|
{
|
|
24
43
|
"type": "label",
|
|
25
44
|
"label": "Diseño de la tarjeta"
|
package/package.json
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
"author": {
|
|
9
9
|
"name": "Daniel Alvayay"
|
|
10
10
|
},
|
|
11
|
-
"version": "0.0.3
|
|
11
|
+
"version": "0.0.3",
|
|
12
12
|
"scripts": {
|
|
13
13
|
"prepublish": "vixonic-module-packager --mode=build",
|
|
14
14
|
"watch": "vixonic-module-packager --mode=watch",
|
|
@@ -28,8 +28,8 @@
|
|
|
28
28
|
"devDependencies": {
|
|
29
29
|
"@types/react": "^18.3.23",
|
|
30
30
|
"@types/react-dom": "^18.3.7",
|
|
31
|
-
"@vixoniccom/module-packager": "^2.13.0
|
|
32
|
-
"@vixoniccom/modules": "^2.
|
|
31
|
+
"@vixoniccom/module-packager": "^2.13.0",
|
|
32
|
+
"@vixoniccom/modules": "^2.25.0-dev.6",
|
|
33
33
|
"standard-version": "^9.5.0"
|
|
34
34
|
}
|
|
35
35
|
}
|
package/sonar-project.properties
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
sonar.projectKey=Vixonic_store-air-quality-index_de6b8e55-8f87-4d53-b9d5-7e514a2702d9
|
|
1
|
+
sonar.projectKey=Vixonic_store-air-quality-index_de6b8e55-8f87-4d53-b9d5-7e514a2702d9
|
|
2
|
+
sonar.exclusions=.github/**/*
|
package/src/App.tsx
CHANGED
|
@@ -14,26 +14,19 @@ export const App: React.FunctionComponent<Props> = ({ data, start }) => {
|
|
|
14
14
|
const { parameters, downloadsPath } = data
|
|
15
15
|
const backgroundImageState = parameters.backgroundImage ? `url('${downloadsPath}/${parameters.backgroundImage.filename}')` : ''
|
|
16
16
|
const [formattedData, setFormattedData] = useState<AqiData>()
|
|
17
|
-
const cityInput = parameters?.cityInput || "
|
|
17
|
+
const cityInput = parameters?.cityInput || "Santiago"
|
|
18
18
|
const msj0 = parameters?.msj0 || 'No hay datos para mostrar'
|
|
19
19
|
const updateTime = 600000
|
|
20
20
|
|
|
21
21
|
useEffect(() => {
|
|
22
|
-
|
|
23
|
-
if (!start) {
|
|
24
|
-
console.warn('[App] start=false → skipping fetch')
|
|
25
|
-
return
|
|
26
|
-
}
|
|
22
|
+
if (!start) return
|
|
27
23
|
|
|
28
24
|
const fetchData = async () => {
|
|
29
|
-
console.log('[App] fetchData: starting fetch for city:', cityInput)
|
|
30
25
|
try {
|
|
31
26
|
const data = await getData(cityInput, updateTime, msj0)
|
|
32
|
-
console.log('[App] fetchData: received data:', data)
|
|
33
|
-
console.log('[App] fetchData: has aqi?', data?.aqi !== undefined, '| aqi value:', data?.aqi)
|
|
34
27
|
setFormattedData(data)
|
|
35
28
|
} catch (error) {
|
|
36
|
-
console.error('
|
|
29
|
+
console.error('Error fetching data:', error)
|
|
37
30
|
setFormattedData(undefined)
|
|
38
31
|
}
|
|
39
32
|
}
|
|
@@ -74,7 +67,8 @@ export const App: React.FunctionComponent<Props> = ({ data, start }) => {
|
|
|
74
67
|
cardGap: parameters?.cardGap,
|
|
75
68
|
aqiFormat: parameters?.aqiFormat,
|
|
76
69
|
stationFormat: parameters?.stationFormat,
|
|
77
|
-
qualityFormat: parameters?.qualityFormat
|
|
70
|
+
qualityFormat: parameters?.qualityFormat,
|
|
71
|
+
positionCard: parameters?.positionCard
|
|
78
72
|
}} />
|
|
79
73
|
</div>
|
|
80
74
|
) : (
|
package/src/components/Card.tsx
CHANGED
|
@@ -13,6 +13,7 @@ interface Props {
|
|
|
13
13
|
aqiFormat?: TextFormat.Value;
|
|
14
14
|
stationFormat?: TextFormat.Value;
|
|
15
15
|
qualityFormat?: TextFormat.Value;
|
|
16
|
+
positionCard?: string;
|
|
16
17
|
};
|
|
17
18
|
}
|
|
18
19
|
|
|
@@ -25,7 +26,8 @@ export const Card: React.FunctionComponent<Props> = ({ data, format }) => {
|
|
|
25
26
|
display: 'flex',
|
|
26
27
|
width: `${format.cardWidth || 300}px`,
|
|
27
28
|
height: `${format.cardHeight || 200}px`,
|
|
28
|
-
flexDirection: 'column'
|
|
29
|
+
flexDirection: `${format.positionCard === 'vertical' ? 'column' : 'row'}`,
|
|
30
|
+
alignItems: `${format.positionCard === 'horizontal' ? 'center' : ''}`,
|
|
29
31
|
backgroundColor: color,
|
|
30
32
|
borderRadius: "10px"
|
|
31
33
|
}}>
|
package/src/parameters.d.ts
CHANGED
|
@@ -5,8 +5,8 @@ declare type VixonicData = {
|
|
|
5
5
|
}
|
|
6
6
|
|
|
7
7
|
declare type VixonicParameters = Partial<{
|
|
8
|
-
services: {id: string }
|
|
9
8
|
backgroundImage: { id?: string; filename?: string; extension?: string }
|
|
9
|
+
positionCard: 'horizontal' | 'vertical'
|
|
10
10
|
topSeparationCard: number
|
|
11
11
|
leftSeparationCard: number
|
|
12
12
|
cardWidth: number
|
package/src/services/index.ts
CHANGED
|
@@ -3,9 +3,8 @@ import localforage from 'localforage'
|
|
|
3
3
|
import { AirQuality, AqiData, StorageData } from '../types'
|
|
4
4
|
import { API_RESPONSE_STATUS } from '../utils'
|
|
5
5
|
|
|
6
|
-
if (!process.env.AQI_TOKEN) console.warn('
|
|
6
|
+
if (!process.env.AQI_TOKEN) console.warn('Env variable not found. Skipping API call.')
|
|
7
7
|
const TOKEN = process.env.AQI_TOKEN
|
|
8
|
-
console.log('[AQI] Token loaded:', TOKEN ? `${TOKEN.slice(0, 4)}...` : 'MISSING')
|
|
9
8
|
|
|
10
9
|
const refetchData = (referenceDate: Date, updateTime: number): boolean => {
|
|
11
10
|
const now = new Date()
|
|
@@ -16,34 +15,26 @@ const refetchData = (referenceDate: Date, updateTime: number): boolean => {
|
|
|
16
15
|
|
|
17
16
|
if (sameYear && sameMonth && sameDay) {
|
|
18
17
|
const differenceMilliseconds = Math.abs(now.getTime() - referenceDate.getTime())
|
|
19
|
-
|
|
20
|
-
console.log(`[AQI] refetchData: same day, elapsed=${differenceMilliseconds}ms, updateTime=${updateTime}ms, shouldRefetch=${shouldRefetch}`)
|
|
21
|
-
return shouldRefetch
|
|
18
|
+
return differenceMilliseconds >= updateTime
|
|
22
19
|
}
|
|
23
|
-
console.log('[AQI] refetchData: different day → forcing refetch')
|
|
24
20
|
return true
|
|
25
21
|
}
|
|
26
22
|
|
|
27
23
|
const requestData = async (cityInput: string): Promise<AirQuality | null> => {
|
|
28
24
|
const URL = `https://api.waqi.info/feed/${cityInput}/?token=${TOKEN}`
|
|
29
|
-
console.log('[AQI] requestData: calling API for city:', cityInput)
|
|
30
25
|
try {
|
|
31
26
|
const response = await axios.get<AirQuality>(URL)
|
|
32
|
-
console.log('[AQI] requestData: HTTP status:', response?.status)
|
|
33
|
-
console.log('[AQI] requestData: response data:', JSON.stringify(response?.data, null, 2))
|
|
34
27
|
if (response?.status === 200) {
|
|
35
28
|
return response.data
|
|
36
29
|
}
|
|
37
|
-
console.warn('[AQI] requestData: unexpected HTTP status, returning null')
|
|
38
30
|
return null
|
|
39
31
|
} catch (error) {
|
|
40
|
-
console.error('
|
|
32
|
+
console.error('Error fetching AQI data:', error)
|
|
41
33
|
return null
|
|
42
34
|
}
|
|
43
35
|
}
|
|
44
36
|
|
|
45
37
|
const processData = (response: AirQuality | null, cityInput: string, defaultMsg: string): AqiData => {
|
|
46
|
-
console.log('[AQI] processData: raw response status:', response?.status)
|
|
47
38
|
let data: AqiData = { city: cityInput, station: defaultMsg }
|
|
48
39
|
if (response?.status === API_RESPONSE_STATUS.OK) {
|
|
49
40
|
data = {
|
|
@@ -51,29 +42,23 @@ const processData = (response: AirQuality | null, cityInput: string, defaultMsg:
|
|
|
51
42
|
city: cityInput,
|
|
52
43
|
station: response.data.city.name
|
|
53
44
|
}
|
|
54
|
-
console.log('[AQI] processData: API OK → processed data:', data)
|
|
55
|
-
} else {
|
|
56
|
-
console.warn('[AQI] processData: API status is not OK → using default message. Response:', response)
|
|
57
45
|
}
|
|
58
46
|
return data
|
|
59
47
|
}
|
|
60
48
|
|
|
61
49
|
export const getData = async (cityInput: string, updateTime: number, defaultMsg: string): Promise<AqiData> => {
|
|
62
|
-
console.log('[AQI] getData called with city:', cityInput, '| updateTime:', updateTime, '| defaultMsg:', defaultMsg)
|
|
63
50
|
const storageData: StorageData | null = await localforage.getItem('aqi')
|
|
64
|
-
console.log('[AQI] getData: cached data:', storageData ? { city: storageData.item?.city, date: storageData.date, aqi: storageData.item?.aqi } : 'null')
|
|
65
51
|
|
|
66
52
|
let data
|
|
67
53
|
if (!storageData?.item || storageData?.item.city !== cityInput || !storageData?.date || refetchData(new Date(storageData.date), updateTime)) {
|
|
68
|
-
console.log('[AQI] getData: cache MISS or stale → fetching from API')
|
|
69
54
|
data = await requestData(cityInput)
|
|
70
55
|
const processedData = processData(data, cityInput, defaultMsg)
|
|
71
|
-
|
|
72
|
-
|
|
56
|
+
if (processedData.aqi !== undefined) {
|
|
57
|
+
await localforage.setItem('aqi', { item: processedData, date: new Date() })
|
|
58
|
+
}
|
|
73
59
|
return processedData
|
|
74
60
|
} else {
|
|
75
61
|
data = storageData.item
|
|
76
|
-
console.log('[AQI] getData: cache HIT → returning cached data:', data)
|
|
77
62
|
return data
|
|
78
63
|
}
|
|
79
64
|
}
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
name: Publish to NPM
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
branches:
|
|
6
|
-
- master
|
|
7
|
-
- main
|
|
8
|
-
pull_request:
|
|
9
|
-
branches:
|
|
10
|
-
- master
|
|
11
|
-
- main
|
|
12
|
-
types: [closed]
|
|
13
|
-
|
|
14
|
-
jobs:
|
|
15
|
-
publish:
|
|
16
|
-
# Solo ejecutar si es un push a main/master o un merge a main/master
|
|
17
|
-
if: github.event_name == 'push' || (github.event.pull_request.merged == true && (github.event.pull_request.base.ref == 'master' || github.event.pull_request.base.ref == 'main'))
|
|
18
|
-
runs-on: ubuntu-latest
|
|
19
|
-
|
|
20
|
-
steps:
|
|
21
|
-
- name: Checkout code
|
|
22
|
-
uses: actions/checkout@v4
|
|
23
|
-
with:
|
|
24
|
-
fetch-depth: 0
|
|
25
|
-
|
|
26
|
-
- name: Setup Node.js 20
|
|
27
|
-
uses: actions/setup-node@v4
|
|
28
|
-
with:
|
|
29
|
-
node-version: '20'
|
|
30
|
-
registry-url: 'https://registry.npmjs.org'
|
|
31
|
-
scope: '@vixoniccom'
|
|
32
|
-
|
|
33
|
-
- name: Configure npm authentication
|
|
34
|
-
run: |
|
|
35
|
-
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
|
|
36
|
-
echo "@vixoniccom:registry=https://registry.npmjs.org/" >> ~/.npmrc
|
|
37
|
-
echo "registry=https://registry.npmjs.org/" >> ~/.npmrc
|
|
38
|
-
|
|
39
|
-
- name: Cache node modules
|
|
40
|
-
uses: actions/cache@v3
|
|
41
|
-
with:
|
|
42
|
-
path: ~/.npm
|
|
43
|
-
key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
|
|
44
|
-
restore-keys: |
|
|
45
|
-
${{ runner.os }}-node-
|
|
46
|
-
|
|
47
|
-
- name: Install dependencies
|
|
48
|
-
run: npm ci
|
|
49
|
-
env:
|
|
50
|
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
51
|
-
|
|
52
|
-
- name: Build package
|
|
53
|
-
run: npm run prepublish
|
|
54
|
-
env:
|
|
55
|
-
AQI_TOKEN: ${{ secrets.AQI_TOKEN }}
|
|
56
|
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
57
|
-
|
|
58
|
-
- name: Verify build.zip exists
|
|
59
|
-
run: |
|
|
60
|
-
if [ ! -f "build.zip" ]; then
|
|
61
|
-
echo "❌ Error: build.zip not found after build process"
|
|
62
|
-
exit 1
|
|
63
|
-
fi
|
|
64
|
-
echo "✅ build.zip found successfully"
|
|
65
|
-
ls -la build.zip
|
|
66
|
-
|
|
67
|
-
- name: Check if version already exists on npm
|
|
68
|
-
id: check-version
|
|
69
|
-
run: |
|
|
70
|
-
PACKAGE_NAME=$(node -p "require('./package.json').name")
|
|
71
|
-
PACKAGE_VERSION=$(node -p "require('./package.json').version")
|
|
72
|
-
|
|
73
|
-
echo "Checking if $PACKAGE_NAME@$PACKAGE_VERSION exists on npm..."
|
|
74
|
-
|
|
75
|
-
if npm view "$PACKAGE_NAME@$PACKAGE_VERSION" version 2>/dev/null; then
|
|
76
|
-
echo "version-exists=true" >> $GITHUB_OUTPUT
|
|
77
|
-
echo "⚠️ Version $PACKAGE_VERSION already exists on npm"
|
|
78
|
-
else
|
|
79
|
-
echo "version-exists=false" >> $GITHUB_OUTPUT
|
|
80
|
-
echo "✅ Version $PACKAGE_VERSION does not exist on npm - ready to publish"
|
|
81
|
-
fi
|
|
82
|
-
|
|
83
|
-
- name: Publish to npm
|
|
84
|
-
if: steps.check-version.outputs.version-exists == 'false'
|
|
85
|
-
run: |
|
|
86
|
-
echo "Publishing to npm..."
|
|
87
|
-
npm publish --access public
|
|
88
|
-
env:
|
|
89
|
-
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
90
|
-
AQI_TOKEN: ${{ secrets.AQI_TOKEN }}
|
|
91
|
-
|
|
92
|
-
- name: Publication success
|
|
93
|
-
if: steps.check-version.outputs.version-exists == 'false'
|
|
94
|
-
run: |
|
|
95
|
-
PACKAGE_NAME=$(node -p "require('./package.json').name")
|
|
96
|
-
PACKAGE_VERSION=$(node -p "require('./package.json').version")
|
|
97
|
-
echo "🎉 Successfully published $PACKAGE_NAME@$PACKAGE_VERSION to npm!"
|
|
98
|
-
echo "📦 Package includes build.zip with the compiled application"
|
|
99
|
-
|
|
100
|
-
- name: Skip publication
|
|
101
|
-
if: steps.check-version.outputs.version-exists == 'true'
|
|
102
|
-
run: |
|
|
103
|
-
PACKAGE_VERSION=$(node -p "require('./package.json').version")
|
|
104
|
-
echo "⏭️ Skipping publication - version $PACKAGE_VERSION already exists on npm"
|
|
105
|
-
echo "💡 To publish a new version, update the version in package.json or run 'npm run release'"
|