diff --git a/.github/workflows/index-validation.yaml b/.github/workflows/index-validation.yaml index cc9c650..01e27b3 100644 --- a/.github/workflows/index-validation.yaml +++ b/.github/workflows/index-validation.yaml @@ -20,7 +20,7 @@ jobs: - name: Setup Node uses: actions/setup-node@v4 with: - node-version: 16 + node-version: 22 cache: npm - name: Install dependencies diff --git a/.github/workflows/pages-deploy.yaml b/.github/workflows/pages-deploy.yaml index 26344e4..382eeeb 100644 --- a/.github/workflows/pages-deploy.yaml +++ b/.github/workflows/pages-deploy.yaml @@ -36,7 +36,7 @@ jobs: - name: Setup Node uses: actions/setup-node@v4 with: - node-version: "18" + node-version: 22 cache: npm - name: Setup Pages uses: actions/configure-pages@v4 diff --git a/.vscode/tasks.json b/.vscode/tasks.json index cd36c6d..a705916 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -20,13 +20,19 @@ "type": "npm", "script": "validate-index", "group": "build", - "label": "Validate index.json" + "label": "Validate index files.", }, { "type": "npm", "script": "generate-index-json", "group": "build", "label": "Generate index.json" + }, + { + "type": "npm", + "script": "generate-schemas", + "group": "build", + "label": "Generate json schemas from schema.ts" } ] } diff --git a/index/Conexiotechnologies.json b/index/Conexiotechnologies.json index 917841f..770b33f 100644 --- a/index/Conexiotechnologies.json +++ b/index/Conexiotechnologies.json @@ -4,11 +4,14 @@ "apps": [ { "name": "conexio-firmware-sdk", + "repo": "https://github.com/Conexiotechnologies/conexio-firmware-sdk", + "docsUrl": "https://github.com/Conexiotechnologies/conexio-firmware-sdk/blob/main/README.md", "title": "Conexio Stratus Firmware SDK", "description": "NCS example applications for [Conexio Stratus devices](https://docs.conexiotech.com/)", "kind": "sample", "tags": ["lte"], "apps": "samples/*", + "avatar": "https://avatars.githubusercontent.com/u/79224914?s=48&v=4", "releases": [ { "date": "2024-09-16T02:29:37Z", diff --git a/index/Lora-net.json b/index/Lora-net.json index 01e5e75..a24b4d7 100644 --- a/index/Lora-net.json +++ b/index/Lora-net.json @@ -5,10 +5,13 @@ { "description": "Software Development Kit featuring demonstrations, drivers and middleware enabling development of connectivity and geolocation applications", "name": "SWSD006", + "repo": "https://github.com/Lora-net/SWSD006", + "docsUrl": "https://github.com/Lora-net/SWSD006/blob/v2.6.1/README.md", "title": "Sidewalk & LoRaWAN Mullti-stack SDK for LR11xx (SWSD006)", "kind": "sample", "license": "Apache License 2.0", "apps": "samples/*", + "avatar": "https://avatars.githubusercontent.com/u/5756403?s=48&v=4", "releases": [ { "date": "2024-08-08T14:30:49Z", diff --git a/index/SmartThingsCommunity.json b/index/SmartThingsCommunity.json index ad776c9..809b408 100755 --- a/index/SmartThingsCommunity.json +++ b/index/SmartThingsCommunity.json @@ -4,6 +4,8 @@ "apps": [ { "name": "SmartThingsFindPartnershipProgram", + "repo": "https://github.com/SmartThingsCommunity/SmartThingsFindPartnershipProgram", + "avatar": "https://avatars.githubusercontent.com/u/7317431?s=48&v=4", "title": "SmartThings Find Device SDK", "description": "SmartThings Find Device SDK is the core device library that makes BLE device to work with SmartThings Find Service. It would supply ready-to-use port code for nRF chipsets. You may simply build your own device firmware with given application example.", "kind": "sample", diff --git a/index/blecon.json b/index/blecon.json index 0ec8728..0a290b2 100644 --- a/index/blecon.json +++ b/index/blecon.json @@ -4,10 +4,13 @@ "apps": [ { "name": "blecon-device-sdk", + "repo": "https://github.com/blecon/blecon-device-sdk", + "docsUrl": "https://github.com/blecon/blecon-device-sdk/blob/main/README.md", "title": "Blecon Device SDK", "description": "Enable Blecon Bluetooth to Cloud IoT Connectivity on your device using the Blecon Device SDK. This SDK includes the Blecon modem libraries and samples.", "kind": "sample", "license": "Apache License 2.0", + "avatar": "https://avatars.githubusercontent.com/u/66618302?s=48&v=4", "apps": "examples/zephyr/*", "releases": [ { diff --git a/index/circuitdojo.json b/index/circuitdojo.json index a5c3338..6f8a683 100644 --- a/index/circuitdojo.json +++ b/index/circuitdojo.json @@ -4,11 +4,14 @@ "apps": [ { "name": "nrf9160-feather-examples-and-drivers", + "repo": "https://github.com/circuitdojo/nrf9160-feather-examples-and-drivers", + "docsUrl": "https://github.com/circuitdojo/nrf9160-feather-examples-and-drivers/blob/v2.4.x/README.md", "title": "nRF9160 Feather Examples and Drivers", "description": "Zephyr examples and drivers for the [nRF9160 Feather](https://docs.circuitdojo.com/nrf9160-getting-started.html)", "kind": "sample", "tags": ["lte"], "apps": "samples/*", + "avatar": "https://avatars.githubusercontent.com/u/34612814?s=48&v=4", "releases": [ { "date": "2023-12-20T20:37:28Z", diff --git a/index/golioth.json b/index/golioth.json index 36f0889..738cd77 100644 --- a/index/golioth.json +++ b/index/golioth.json @@ -1,13 +1,17 @@ { "name": "golioth", "description": "Golioth Cloud platform, which includes a device SDK, example code, and Reference Designs", + "avatar": "https://avatars.githubusercontent.com/u/58146410?s=48&v=4", "apps": [ { "name": "reference-design-template", + "repo": "https://github.com/golioth/reference-design-template", + "docsUrl": "https://github.com/golioth/reference-design-template/blob/main/README.md", "title": "The Golioth Reference Design template", "description": "This starter project is a standalone application that includes databases (both stateful and time-based), Over-the-air update capabilities, settings service, remote procedure calls (RPCs)", "kind": "template", "tags": ["dfu","lte"], + "avatar": "https://avatars.githubusercontent.com/u/58146410?s=48&v=4", "releases": [ { "date": "2024-02-21T21:05:15Z", @@ -37,6 +41,8 @@ }, { "name": "reference-design-can-asset-tracker", + "repo": "https://github.com/golioth/reference-design-can-asset-tracker", + "docsUrl": "https://github.com/golioth/reference-design-can-asset-tracker/blob/main/README.md", "title": "CAN / OBD-II Asset Tracker Reference Design", "description": "This Reference Design requires external hardware on top of an nRF9160-DK (see [https://projects.golioth.io/reference-designs/can-asset-tracker/guide-nrf9160-dk/](https://projects.golioth.io/reference-designs/can-asset-tracker/guide-nrf9160-dk/), but includes all code required to pass messages received on a CAN bus back to the Cloud, as well as regular GPS readings. Over-the-air updates are built in.", "kind": "template", @@ -53,6 +59,8 @@ }, { "name": "reference-design-air-quality", + "repo": "https://github.com/golioth/reference-design-air-quality", + "docsUrl": "https://github.com/golioth/reference-design-air-quality/blob/main/README.md", "title": "Air Quality Monitor Reference Design", "description": "This Reference Design requires external hardware on top of an nRF9160-DK (see [https://projects.golioth.io/reference-designs/air-quality-monitor/guide-nrf9160-dk](https://projects.golioth.io/reference-designs/air-quality-monitor/guide-nrf9160-dk/), but includes all code required to monitor readings from connected air quality sensors and pass back to the Cloud. Includes remote procedure call to clean sensor, and Over-the-air updates are built in.", "kind": "template", @@ -70,6 +78,8 @@ { "name": "reference-design-coldchain", "title": "Cold Chain Asset Tracker Reference Design", + "repo": "https://github.com/golioth/reference-design-coldchain", + "docsUrl": "https://github.com/golioth/reference-design-coldchain/blob/main/README.md", "description": "This Reference Design requires external hardware on top of an nRF9160-DK (see [https://projects.golioth.io/reference-designs/cold-chain-tracker/](https://projects.golioth.io/reference-designs/cold-chain-tracker/), but includes all code required to monitor weather sensor (temperature) readings and pass back to the Cloud, as well as regular GPS readings. Configurable online/offline updates rates using settings subsystem. Over-the-air updates are built in.", "kind": "template", "license": "Apache 2.0", @@ -85,6 +95,8 @@ }, { "name": "reference-design-modbus-vibration-monitor", + "repo": "https://github.com/golioth/reference-design-modbus-vibration-monitor", + "docsUrl": "https://github.com/golioth/reference-design-modbus-vibration-monitor/blob/main/README.md", "title": "Modbus Vibration Monitor Reference Design", "description": "This Reference Design requires external hardware on top of an nRF9160-DK (see [https://projects.golioth.io/reference-designs/modbus-vibration-monitor/guide-nrf9160-dk](https://projects.golioth.io/reference-designs/modbus-vibration-monitor/guide-nrf9160-dk), but includes all code required to communicate over RS-485 to a Modbus server (sensor) and pass back to the Cloud. Over-the-air updates are built in.", "kind": "template", diff --git a/index/hello-nrfcloud.json b/index/hello-nrfcloud.json index 904042f..c35a25e 100644 --- a/index/hello-nrfcloud.json +++ b/index/hello-nrfcloud.json @@ -4,10 +4,12 @@ "apps": [ { "title": "Thingy:91 X: Hello nRF Cloud firmware", + "repo": "https://github.com/hello-nrfcloud/firmware", "name": "firmware", "description": "This firmware is used with hello.nrfcloud.com", "kind": "sample", "tags": ["dfu", "lte"], + "avatar": "https://avatars.githubusercontent.com/u/137044581?s=48&v=4", "releases": [ { "date": "2024-08-11T14:37:00Z", diff --git a/index/nrfconnect.json b/index/nrfconnect.json index 2ea146b..cb6e578 100644 --- a/index/nrfconnect.json +++ b/index/nrfconnect.json @@ -1,163 +1,176 @@ { - "name": "nRF Connect", - "description": "Official nRF Connect SDK applications", - "apps": [ + "name": "nRF Connect", + "description": "Official nRF Connect SDK applications", + "avatar": "https://avatars.githubusercontent.com/u/40860733?s=200&v=4", + "apps": [ + { + "name": "sdk-sidewalk", + "title": "Amazon Sidewalk Add-on", + "repo": "https://github.com/nrfconnect/sdk-sidewalk", + "description": "Amazon Sidewalk is a shared network designed to provide a stable and reliable connection at your home and outside of it. Is is based on two variants, one using Bluetooth® LE (more suited for home applications) and the other one using sub-GHz with the Semtech radio transceiver (for applications requiring longer range).", + "kind": "template", + "tags": ["sidewalk", "connectivity"], + "apps": "samples/*", + "license": "Other", + "releases": [ { - "name": "sdk-sidewalk", - "title": "Amazon Sidewalk Add-on", - "description": "Amazon Sidewalk is a shared network designed to provide a stable and reliable connection at your home and outside of it. Is is based on two variants, one using Bluetooth® LE (more suited for home applications) and the other one using sub-GHz with the Semtech radio transceiver (for applications requiring longer range).", - "kind": "template", - "tags": ["sidewalk", "connectivity"], - "apps": "samples/*", - "license": "Other", - "releases": [ - { - "name": "Amazon Sidewalk add-on v0.1.0", - "tag": "v0.1.0-add-on", - "date": "2025-03-21T10:00:00Z", - "sdk": "10dd3ddbf2560aba2da99a2dc2536b177a1498d7" - } - ], - "defaultBranch": "main", - "docsUrl": "https://docs.nordicsemi.com/bundle/sidewalk_latest/page/index.html" + "name": "Amazon Sidewalk add-on v0.1.0", + "tag": "v0.1.0-add-on", + "date": "2025-03-21T10:00:00Z", + "sdk": "10dd3ddbf2560aba2da99a2dc2536b177a1498d7" + } + ], + "defaultBranch": "main", + "docsUrl": "https://docs.nordicsemi.com/bundle/sidewalk_latest/page/index.html" + }, + { + "name": "ncs-zigbee-r22", + "title": "Zigbee R22", + "repo": "https://github.com/nrfconnect/ncs-zigbee-r22", + "description": "The Zigbee R22 add-on for the nRF Connect SDK provides support for developing Zigbee R22 applications based on the third-party precompiled ZBOSS R22 stack.", + "kind": "template", + "docsUrl": "https://docs.nordicsemi.com/bundle?cluster=true&exclude_metadata_filter.field=display-type&exclude_metadata_filter.value=inline&labelkey=addon-zigbee-r22&rpp=10&sort.field=last_uploaded&sort.value=desc", + "tags": [ + "zigbee", + "connectivity" + ], + "apps": "samples/*", + "avatar": "https://avatars.githubusercontent.com/u/40860733?s=200&v=4", + "defaultBranch": "v1.0.0", + "releases": [ + { + "name": "Zigbee R22 add-on v1.0.0", + "tag": "v1.0.0", + "date": "2025-03-10T13:03:00Z", + "sdk": "v2.8.0" + } + ] + }, + { + "name": "ncs-zigbee", + "title": "Zigbee R23", + "repo": "https://github.com/nrfconnect/ncs-zigbee", + "description": "The Zigbee R23 add-on for the nRF Connect SDK provides support for developing Zigbee R23 applications based on the third-party precompiled ZBOSS R23 stack.", + "kind": "template", + "tags": [ + "zigbee", + "connectivity" + ], + "apps": "samples/*", + "avatar": "https://avatars.githubusercontent.com/u/40860733?s=200&v=4", + "releases": [ + { + "name": "Zigbee add-on v0.1.0", + "tag": "v0.1.0", + "date": "2024-11-15T12:47:22Z", + "sdk": "v2.8.0" + }, + { + "name": "Zigbee add-on v0.2.0", + "tag": "v0.2.0", + "date": "2024-12-03T10:44:00Z", + "sdk": "v2.8.0" + }, + { + "name": "Zigbee add-on v0.3.0", + "tag": "v0.3.0", + "date": "2025-01-13T12:20:00Z", + "sdk": "v2.9.0" + }, + { + "name": "Zigbee add-on v0.3.1", + "tag": "v0.3.1", + "date": "2025-01-14T03:13:00Z", + "sdk": "v2.9.0" + }, + { + "name": "Zigbee add-on v0.4.0", + "tag": "v0.4.0", + "date": "2025-03-31T10:40:00Z", + "sdk": "v2.9.0" + } + ], + "defaultBranch": "v0.4.0", + "docsUrl": "https://docs.nordicsemi.com/bundle/addon-zigbee-r23-latest/page/index.html" + }, + { + "name": "ncs-example-application", + "repo": "https://github.com/nrfconnect/ncs-example-application", + "title": "nRF Connect SDK example application", + "description": "Official reference implementation for various out-of-tree concepts, like boards, drivers and a CI setup.", + "kind": "template", + "tags": [], + "apps": "app", + "avatar": "https://avatars.githubusercontent.com/u/40860733?s=200&v=4", + "docsUrl": "https://github.com/nrfconnect/ncs-example-application/blob/main/README.md", + "releases": [ + { + "date": "2024-12-17T16:52:00Z", + "name": "ncs-example-application v2.9.0", + "tag": "v2.9.0", + "sdk": "v2.9.0" + }, + { + "date": "2024-11-14T13:52:00Z", + "name": "ncs-example-application v2.8.0", + "tag": "v2.8.0", + "sdk": "v2.8.0" + }, + { + "date": "2024-07-04T19:19:00Z", + "name": "ncs-example-application v2.7.0", + "tag": "v2.7.0", + "sdk": "v2.7.0" + }, + { + "date": "2024-09-17T10:54:00Z", + "name": "ncs-example-application v2.6.2", + "tag": "v2.6.2", + "sdk": "v2.6.2" + }, + { + "date": "2024-04-25T13:53:00Z", + "name": "ncs-example-application v2.6.1", + "tag": "v2.6.1", + "sdk": "v2.6.1" + }, + { + "date": "2024-03-13T14:30:49Z", + "name": "ncs-example-application v2.6.0", + "tag": "v2.6.0", + "sdk": "v2.6.0" + }, + { + "date": "2024-02-01T09:50:12Z", + "name": "ncs-example-application v2.5.2", + "tag": "v2.5.2", + "sdk": "v2.5.2" + }, + { + "date": "2023-12-18T20:37:28Z", + "name": "ncs-example-application v2.5.1", + "tag": "v2.5.1", + "sdk": "v2.5.1" }, { - "name": "ncs-zigbee", - "title": "Zigbee R23", - "description": "The Zigbee R23 add-on for the nRF Connect SDK provides support for developing Zigbee R23 applications based on the third-party precompiled ZBOSS R23 stack.", - "kind": "template", - "tags": ["zigbee", "connectivity"], - "apps": "samples/*", - "license": "Other", - "releases": [ - { - "name": "Zigbee add-on v0.1.0", - "tag": "v0.1.0", - "date": "2024-11-15T12:47:22Z", - "sdk": "v2.8.0" - }, - { - "name": "Zigbee add-on v0.2.0", - "tag": "v0.2.0", - "date": "2024-12-03T10:44:00Z", - "sdk": "v2.8.0" - }, - { - "name": "Zigbee add-on v0.3.0", - "tag": "v0.3.0", - "date": "2025-01-13T12:20:00Z", - "sdk": "v2.9.0" - }, - { - "name": "Zigbee add-on v0.3.1", - "tag": "v0.3.1", - "date": "2025-01-14T03:13:00Z", - "sdk": "v2.9.0" - }, - { - "name": "Zigbee add-on v0.4.0", - "tag": "v0.4.0", - "date": "2025-03-31T10:40:00Z", - "sdk": "v2.9.0" - } - ], - "defaultBranch": "v0.4.0", - "docsUrl": "https://docs.nordicsemi.com/bundle/addon-zigbee-r23-latest/page/index.html" + "date": "2023-10-26T13:55:02Z", + "name": "ncs-example-application v2.5.0", + "tag": "v2.5.0", + "sdk": "v2.5.0" }, { - "name": "ncs-zigbee-r22", - "title": "Zigbee R22", - "description": "The Zigbee R22 add-on for the nRF Connect SDK provides support for developing Zigbee R22 applications based on the third-party precompiled ZBOSS R22 stack.", - "kind": "template", - "docsUrl": " https://docs.nordicsemi.com/bundle?cluster=true&exclude_metadata_filter.field=display-type&exclude_metadata_filter.value=inline&labelkey=addon-zigbee-r22&rpp=10&sort.field=last_uploaded&sort.value=desc", - "tags": ["zigbee", "connectivity"], - "apps": "samples/*", - "license": "Other", - "defaultBranch": "v1.0.0", - "releases": [ - { - "name": "Zigbee R22 add-on v1.0.0", - "tag": "v1.0.0", - "date": "2025-03-10T13:03:00Z", - "sdk": "v2.8.0" - } - ] + "date": "2023-06-01T13:25:38Z", + "name": "ncs-example-application v2.4.0", + "tag": "v2.4.0", + "sdk": "v2.4.0" }, { - "name": "ncs-example-application", - "title": "nRF Connect SDK example application", - "description": "Official reference implementation for various out-of-tree concepts, like boards, drivers and a CI setup.", - "kind": "template", - "tags": [], - "apps": "app", - "releases": [ - { - "date": "2024-12-17T16:52:00Z", - "name": "ncs-example-application v2.9.0", - "tag": "v2.9.0", - "sdk": "v2.9.0" - }, - { - "date": "2024-11-14T13:52:00Z", - "name": "ncs-example-application v2.8.0", - "tag": "v2.8.0", - "sdk": "v2.8.0" - }, - { - "date": "2024-07-04T19:19:00Z", - "name": "ncs-example-application v2.7.0", - "tag": "v2.7.0", - "sdk": "v2.7.0" - }, - { - "date": "2024-09-17T10:54:00Z", - "name": "ncs-example-application v2.6.2", - "tag": "v2.6.2", - "sdk": "v2.6.2" - }, - { - "date": "2024-04-25T13:53:00Z", - "name": "ncs-example-application v2.6.1", - "tag": "v2.6.1", - "sdk": "v2.6.1" - }, - { - "date": "2024-03-13T14:30:49Z", - "name": "ncs-example-application v2.6.0", - "tag": "v2.6.0", - "sdk": "v2.6.0" - }, - { - "date": "2024-02-01T09:50:12Z", - "name": "ncs-example-application v2.5.2", - "tag": "v2.5.2", - "sdk": "v2.5.2" - }, - { - "date": "2023-12-18T20:37:28Z", - "name": "ncs-example-application v2.5.1", - "tag": "v2.5.1", - "sdk": "v2.5.1" - }, - { - "date": "2023-10-26T13:55:02Z", - "name": "ncs-example-application v2.5.0", - "tag": "v2.5.0", - "sdk": "v2.5.0" - }, - { - "date": "2023-06-01T13:25:38Z", - "name": "ncs-example-application v2.4.0", - "tag": "v2.4.0", - "sdk": "v2.4.0" - }, - { - "date": "2023-03-02T10:45:20Z", - "name": "ncs-example-application v2.3.0", - "tag": "v2.3.0", - "sdk": "v2.3.0" - } - ] + "date": "2023-03-02T10:45:20Z", + "name": "ncs-example-application v2.3.0", + "tag": "v2.3.0", + "sdk": "v2.3.0" } - ] + ] + } + ] } diff --git a/index/onomondo.json b/index/onomondo.json index 76cec0c..5e1f822 100644 --- a/index/onomondo.json +++ b/index/onomondo.json @@ -4,10 +4,13 @@ "apps": [ { "name": "nrf-softsim", + "repo": "https://github.com/onomondo/nrf-softsim", + "docsUrl": "https://github.com/onomondo/nrf-softsim/blob/master/README.md", "description": "Manifest repo for integrating SoftSIM and nrf-sdk", "kind": "sample", "tags": ["lte"], "apps": "samples/*", + "avatar": "https://avatars.githubusercontent.com/u/13015261?s=48&v=4", "releases": [ { "date": "2024-02-20T12:00:50Z", diff --git a/package-lock.json b/package-lock.json index 329cbf1..ab0d25d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "fuse.js": "^6.6.2", "json-schema-to-ts": "^2.9.2", "lodash": "^4.17.21", - "next": "^15.2.2", + "next": "^15.2.4", "node-fetch": "^3.3.2", "octokit": "^4.1.2", "react": "^18.2.0", @@ -1373,15 +1373,15 @@ "license": "MIT" }, "node_modules/@next/env": { - "version": "15.2.2", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.2.tgz", - "integrity": "sha512-yWgopCfA9XDR8ZH3taB5nRKtKJ1Q5fYsTOuYkzIIoS8TJ0UAUKAGF73JnGszbjk2ufAQDj6mDdgsJAFx5CLtYQ==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.4.tgz", + "integrity": "sha512-+SFtMgoiYP3WoSswuNmxJOCwi06TdWE733D+WPjpXIe4LXGULwEaofiiAy6kbS0+XjM5xF5n3lKuBwN2SnqD9g==", "license": "MIT" }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.2.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.2.2.tgz", - "integrity": "sha512-HNBRnz+bkZ+KfyOExpUxTMR0Ow8nkkcE6IlsdEa9W/rI7gefud19+Sn1xYKwB9pdCdxIP1lPru/ZfjfA+iT8pw==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.2.4.tgz", + "integrity": "sha512-1AnMfs655ipJEDC/FHkSr0r3lXBgpqKo4K1kiwfUf3iE68rDFXZ1TtHdMvf7D0hMItgDZ7Vuq3JgNMbt/+3bYw==", "cpu": [ "arm64" ], @@ -1395,9 +1395,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.2.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.2.2.tgz", - "integrity": "sha512-mJOUwp7al63tDpLpEFpKwwg5jwvtL1lhRW2fI1Aog0nYCPAhxbJsaZKdoVyPZCy8MYf/iQVNDuk/+i29iLCzIA==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.2.4.tgz", + "integrity": "sha512-3qK2zb5EwCwxnO2HeO+TRqCubeI/NgCe+kL5dTJlPldV/uwCnUgC7VbEzgmxbfrkbjehL4H9BPztWOEtsoMwew==", "cpu": [ "x64" ], @@ -1411,9 +1411,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.2.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.2.2.tgz", - "integrity": "sha512-5ZZ0Zwy3SgMr7MfWtRE7cQWVssfOvxYfD9O7XHM7KM4nrf5EOeqwq67ZXDgo86LVmffgsu5tPO57EeFKRnrfSQ==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.2.4.tgz", + "integrity": "sha512-HFN6GKUcrTWvem8AZN7tT95zPb0GUGv9v0d0iyuTb303vbXkkbHDp/DxufB04jNVD+IN9yHy7y/6Mqq0h0YVaQ==", "cpu": [ "arm64" ], @@ -1427,9 +1427,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.2.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.2.2.tgz", - "integrity": "sha512-cgKWBuFMLlJ4TWcFHl1KOaVVUAF8vy4qEvX5KsNd0Yj5mhu989QFCq1WjuaEbv/tO1ZpsQI6h/0YR8bLwEi+nA==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.2.4.tgz", + "integrity": "sha512-Oioa0SORWLwi35/kVB8aCk5Uq+5/ZIumMK1kJV+jSdazFm2NzPDztsefzdmzzpx5oGCJ6FkUC7vkaUseNTStNA==", "cpu": [ "arm64" ], @@ -1443,9 +1443,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.2.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.2.2.tgz", - "integrity": "sha512-c3kWSOSsVL8rcNBBfOq1+/j2PKs2nsMwJUV4icUxRgGBwUOfppeh7YhN5s79enBQFU+8xRgVatFkhHU1QW7yUA==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.2.4.tgz", + "integrity": "sha512-yb5WTRaHdkgOqFOZiu6rHV1fAEK0flVpaIN2HB6kxHVSy/dIajWbThS7qON3W9/SNOH2JWkVCyulgGYekMePuw==", "cpu": [ "x64" ], @@ -1459,9 +1459,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.2.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.2.2.tgz", - "integrity": "sha512-PXTW9PLTxdNlVYgPJ0equojcq1kNu5NtwcNjRjHAB+/sdoKZ+X8FBu70fdJFadkxFIGekQTyRvPMFF+SOJaQjw==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.2.4.tgz", + "integrity": "sha512-Dcdv/ix6srhkM25fgXiyOieFUkz+fOYkHlydWCtB0xMST6X9XYI3yPDKBZt1xuhOytONsIFJFB08xXYsxUwJLw==", "cpu": [ "x64" ], @@ -1475,9 +1475,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.2.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.2.2.tgz", - "integrity": "sha512-nG644Es5llSGEcTaXhnGWR/aThM/hIaz0jx4MDg4gWC8GfTCp8eDBWZ77CVuv2ha/uL9Ce+nPTfYkSLG67/sHg==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.2.4.tgz", + "integrity": "sha512-dW0i7eukvDxtIhCYkMrZNQfNicPDExt2jPb9AZPpL7cfyUo7QSNl1DjsHjmmKp6qNAqUESyT8YFl/Aw91cNJJg==", "cpu": [ "arm64" ], @@ -1491,9 +1491,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.2.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.2.2.tgz", - "integrity": "sha512-52nWy65S/R6/kejz3jpvHAjZDPKIbEQu4x9jDBzmB9jJfuOy5rspjKu4u77+fI4M/WzLXrrQd57hlFGzz1ubcQ==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.2.4.tgz", + "integrity": "sha512-SbnWkJmkS7Xl3kre8SdMF6F/XDh1DTFEhp0jRTj/uB8iPKoU2bb2NDfcu+iifv1+mxQEd1g2vvSxcZbXSKyWiQ==", "cpu": [ "x64" ], @@ -4078,12 +4078,12 @@ } }, "node_modules/next": { - "version": "15.2.2", - "resolved": "https://registry.npmjs.org/next/-/next-15.2.2.tgz", - "integrity": "sha512-dgp8Kcx5XZRjMw2KNwBtUzhngRaURPioxoNIVl5BOyJbhi9CUgEtKDO7fx5wh8Z8vOVX1nYZ9meawJoRrlASYA==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/next/-/next-15.2.4.tgz", + "integrity": "sha512-VwL+LAaPSxEkd3lU2xWbgEOtrM8oedmyhBqaVNmgKB+GvZlCy9rgaEc+y2on0wv+l0oSFqLtYD6dcC1eAedUaQ==", "license": "MIT", "dependencies": { - "@next/env": "15.2.2", + "@next/env": "15.2.4", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", @@ -4098,14 +4098,14 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.2.2", - "@next/swc-darwin-x64": "15.2.2", - "@next/swc-linux-arm64-gnu": "15.2.2", - "@next/swc-linux-arm64-musl": "15.2.2", - "@next/swc-linux-x64-gnu": "15.2.2", - "@next/swc-linux-x64-musl": "15.2.2", - "@next/swc-win32-arm64-msvc": "15.2.2", - "@next/swc-win32-x64-msvc": "15.2.2", + "@next/swc-darwin-arm64": "15.2.4", + "@next/swc-darwin-x64": "15.2.4", + "@next/swc-linux-arm64-gnu": "15.2.4", + "@next/swc-linux-arm64-musl": "15.2.4", + "@next/swc-linux-x64-gnu": "15.2.4", + "@next/swc-linux-x64-musl": "15.2.4", + "@next/swc-win32-arm64-msvc": "15.2.4", + "@next/swc-win32-x64-msvc": "15.2.4", "sharp": "^0.33.5" }, "peerDependencies": { diff --git a/package.json b/package.json index 420077c..7d0865e 100644 --- a/package.json +++ b/package.json @@ -33,7 +33,7 @@ "fuse.js": "^6.6.2", "json-schema-to-ts": "^2.9.2", "lodash": "^4.17.21", - "next": "^15.2.2", + "next": "^15.2.4", "node-fetch": "^3.3.2", "octokit": "^4.1.2", "react": "^18.2.0", diff --git a/resources/output_schema.json b/resources/output_schema.json index 0500886..c50c20f 100644 --- a/resources/output_schema.json +++ b/resources/output_schema.json @@ -31,35 +31,24 @@ "External" ] }, - "location": { - "type": "string" - }, "avatar": { "type": "string", "format": "uri" }, - "urls": { + "contact": { "type": "object", "properties": { - "support": { + "devzoneUsername": { "type": "string", - "format": "uri" + "description": "Nordic Semiconductor devzone`s account registered as responsible for providing the support for the Add-ons." }, "email": { "type": "string", - "format": "uri" - }, - "blog": { - "type": "string", - "format": "uri" - }, - "twitter": { - "type": "string", - "format": "uri" + "description": "The email that the Nordic Semiconductor DevZone account is registered with." } }, "required": [ - "support" + "devzoneUsername" ], "additionalProperties": false } @@ -69,8 +58,7 @@ "name", "description", "type", - "kind", - "urls" + "kind" ], "additionalProperties": false } @@ -98,9 +86,6 @@ "repo": { "type": "string" }, - "isTemplate": { - "type": "boolean" - }, "owner": { "type": "string", "description": "The ID of the owner organization." @@ -175,15 +160,6 @@ }, "minItems": 1 }, - "watchers": { - "type": "integer" - }, - "stars": { - "type": "integer" - }, - "forks": { - "type": "integer" - }, "defaultBranch": { "type": "string" }, @@ -209,23 +185,24 @@ "required": [ "detailsUrl" ] + }, + "avatar": { + "type": "string", + "description": "An image displayed next to an Add-on" } }, "required": [ "id", "name", + "repo", "description", - "isTemplate", "owner", "kind", "tags", "releases", - "watchers", - "stars", - "forks", "defaultBranch", "lastUpdate", - "repo" + "docsUrl" ], "additionalProperties": false } diff --git a/resources/schema.json b/resources/schema.json index df7ea26..d323d7e 100644 --- a/resources/schema.json +++ b/resources/schema.json @@ -10,6 +10,26 @@ "type": "string", "description": "A short sentence describing the organization." }, + "avatar": { + "type": "string", + "description": "An url of an avatar displayed next to an Add-on." + }, + "contact": { + "type": "object", + "properties": { + "devzoneUsername": { + "type": "string", + "description": "Nordic Semiconductor devzone`s account registered as responsible for providing the support for the Add-ons." + }, + "email": { + "type": "string", + "description": "The email that the Nordic Semiconductor DevZone account is registered with." + } + }, + "required": [ + "devzoneUsername" + ] + }, "apps": { "type": "array", "items": { @@ -19,6 +39,10 @@ "type": "string", "description": "The name of the application repo. Should be the repo-name in the GitHub URL: https://github.com/org/repo-name." }, + "repo": { + "type": "string", + "description": "The Url of an Add-on repository" + }, "title": { "type": "string", "description": "Human readable name of the repo to be shown in the UI. Defaults to the name property." @@ -32,6 +56,10 @@ "default": "west.yml", "description": "Alternative filename for the west manifest. Defaults to west.yml." }, + "avatar": { + "type": "string", + "description": "An url of an avatar displayed next to an Add-on." + }, "kind": { "description": "The type of the app repo.", "oneOf": [ @@ -138,9 +166,12 @@ "additionalProperties": false, "required": [ "name", + "repo", "kind", "tags", - "releases" + "releases", + "description", + "docsUrl" ] }, "description": "A list of applications contributed by the organization." diff --git a/scripts/generate-index-json.ts b/scripts/generate-index-json.ts index 54acf07..abe2e92 100644 --- a/scripts/generate-index-json.ts +++ b/scripts/generate-index-json.ts @@ -8,10 +8,7 @@ import fs from 'fs/promises'; import path from 'path'; -import { Octokit } from '@octokit/rest'; -import fetch from 'node-fetch'; import colours from 'ansi-colors'; - import type { OrgIndex, AppIndex, @@ -19,9 +16,7 @@ import type { Organization, Application, } from '../site/src/schema'; -import { appIndexSchema } from '../site/src/schema'; import { ParsedOrgFile, readOrgIndexFiles } from './orgFiles'; -import { execSync } from 'child_process'; const nordicOrgs: string[] = ['nrfconnect', 'nordic', 'nordicplayground', 'hello-nrfcloud']; const partnerOrgs: string[] = ['golioth', 'blecon']; @@ -30,22 +25,6 @@ function notUndefined(value: T | undefined): value is T { return value !== undefined; } -function initialiseGitHubApi() { - const authToken = - process.env.GITHUB_TOKEN ?? execSync('gh auth token', { encoding: 'utf-8' }).trim(); - - if (!authToken) { - throw new Error( - 'No auth token was provided, so you may encounter rate limit issues when calling the GitHub API.\n' + - 'Provide a token by setting the "GITHUB_TOKEN" environment variable.\n', - ); - } - - return new Octokit({ request: { fetch }, auth: authToken }); -} - -const octokit = initialiseGitHubApi(); - async function generateIndex(orgIndices: ParsedOrgFile[]): Promise { const appIndex: AppIndex = { orgs: {}, apps: [] }; @@ -55,18 +34,7 @@ async function generateIndex(orgIndices: ParsedOrgFile[]): Promise { appIndex.apps.push(...apps); } - appIndex.apps = appIndex.apps.sort((a, b) => { - const [updatedA, updatedB] = [ - new Date(a.lastUpdate), - new Date(b.lastUpdate) - ]; - - if (updatedA === updatedB) { - return a.name < b.name ? -1 : 1; - } - - return updatedA > updatedB ? -1 : 1; - }); + appIndex.apps = appIndex.apps.sort((a, b) => a.name < b.name ? -1 : 1); return appIndex; } @@ -76,8 +44,6 @@ async function fetchOrgData({ orgIndex, }: ParsedOrgFile): Promise<{ org: Organization; apps: Application[] }> { try { - const userData = await octokit.users.getByUsername({ username: orgId }); - let kind: Organization['kind']; if (nordicOrgs.includes(orgId)) { kind = 'Nordic Semiconductor'; @@ -92,15 +58,9 @@ async function fetchOrgData({ name: orgIndex.name, description: orgIndex.description, kind, - type: userData.data.type as (typeof validOrgTypes)[number], - urls: { - support: userData.data.html_url, - email: userData.data.email ?? undefined, - blog: userData.data.blog ?? undefined, - twitter: userData.data.twitter_username ?? undefined, - }, - avatar: userData.data.avatar_url, - location: userData.data.location ?? undefined, + type: 'Organization' as (typeof validOrgTypes)[number], + contact: orgIndex.contact, + avatar: orgIndex.avatar, }; const apps = await Promise.all(orgIndex.apps.map((app) => fetchRepoData(orgId, app))); @@ -113,60 +73,47 @@ async function fetchOrgData({ } } -async function getReadmeUrl(orgId: string, app: OrgIndex['apps'][number]): Promise { - let readmeUrl: string | undefined; - - try { - const { data } = await octokit.repos.getReadme({ - owner: orgId, - repo: app.name, - }); - - readmeUrl = data.html_url ?? undefined; - } catch { - readmeUrl = undefined; - } - - return readmeUrl; -} - async function fetchRepoData( orgId: string, app: OrgIndex['apps'][number], -): Promise { +): Promise { try { - const repoUrl = `https://github.com/${orgId}/${app.name}`; - - const { data: repoData } = await octokit.repos.get({ - owner: orgId, - repo: app.name, - }); - - let docsUrl = app.docsUrl ?? await getReadmeUrl(orgId, app); + try { + app.releases = app.releases.sort((a, b) => { + const [updatedA, updatedB] = [ + new Date(a.date), + new Date(b.date) + ]; + + if (updatedA === updatedB) { + return a.name.localeCompare(b.name); + } + + return updatedA > updatedB ? -1 : 1; + }); + } catch { + console.log(`failed to parse ${app.name}`) + } console.log(colours.green(`Fetched data for ${orgId}/${app.name}`)); return { - id: repoData.id.toString(), - repo: repoUrl, + id: app.repo, + repo: app.repo, owner: orgId, - description: app.description ?? repoData.description ?? '', + description: app.description, name: app.name, title: app.title, - defaultBranch: app.defaultBranch ?? repoData.default_branch, - isTemplate: repoData.is_template ?? false, + defaultBranch: app.defaultBranch ?? app.releases[0]?.tag, kind: app.kind, - lastUpdate: repoData.updated_at, - license: app.license ?? repoData.license?.name ?? undefined, - watchers: repoData.watchers_count, - stars: repoData.stargazers_count, - forks: repoData.forks_count, + license: app.license ?? 'Other License', apps: app.apps, releases: app.releases, tags: app.tags, - docsUrl: docsUrl, + docsUrl: app.docsUrl, restricted: app.restricted, - }; + avatar: app.avatar, + } as Application; } catch { throw new Error(`Failed to fetch data for ${orgId}/${app.name}`); } diff --git a/site/src/app/AppBlock.tsx b/site/src/app/AppBlock.tsx index 5891827..4ec46df 100644 --- a/site/src/app/AppBlock.tsx +++ b/site/src/app/AppBlock.tsx @@ -3,49 +3,51 @@ * SPDX-License-Identifier: BSD-3-Clause */ -import formatRelative from 'date-fns/formatRelative'; import classNames from 'classnames'; import Markdown from 'react-markdown'; import { - EyeIcon, LawIcon, LinkExternalIcon, - MailIcon, - RepoForkedIcon, - RepoIcon, - RepoTemplateIcon, - StarIcon, TerminalIcon, VerifiedIcon, BookIcon, LockIcon, + RepoIcon, + OrganizationIcon, } from '@primer/octicons-react'; import { useState } from 'react'; import { NormalisedApp } from '../schema'; import VSCodeButton from './VSCodeButton'; import TagList from './TagList'; -import ReleasesDropDownList from './ReleasesDropDownList' +import ReleasesDropDownList from './ReleasesDropDownList'; import VSCodeQueryParams from './VSCodeQueryParams'; import { AppDetails } from './Root'; import { telemetry } from './telemetry'; -import { ShowAppGuideEvent, OpenDocsEvent } from './telemetryEvents'; +import { ShowAppGuideEvent, OpenDocsEvent, ShowSupportInfoEvent } from './telemetryEvents'; interface Props { app: NormalisedApp; setShowingAppDetails: (showingAppDetails: AppDetails) => void; } -function Avatar({ app }: { app: NormalisedApp }) { - if (app.owner.type === 'Organization') { - return ; +function Avatar({ app, sizeInPx }: { app: NormalisedApp; sizeInPx?: number }) { + const src = app.avatar ?? app.owner.avatar; + sizeInPx ??= 96; // Rendered size is 48px, so for nice retina quality we need an icon of size 96px + + try { + if (src) { + const { origin, pathname } = new URL(src); + return ; + } + } catch { + // URL might be invalid } - return app.isTemplate ? : ; + return ; } function AppBlock({ app, setShowingAppDetails }: Props): JSX.Element { - const [queryParams, setQueryParams] = useState(new VSCodeQueryParams(app)); return ( @@ -62,14 +64,16 @@ function AppBlock({ app, setShowingAppDetails }: Props): JSX.Element { - {app.restricted && - - + {app.restricted && ( + + - } + )}
@@ -79,7 +83,7 @@ function AppBlock({ app, setShowingAppDetails }: Props): JSX.Element {

- {app.owner.name} + {app.owner.name}

{app.owner.kind !== 'External' && ( @@ -89,12 +93,6 @@ function AppBlock({ app, setShowingAppDetails }: Props): JSX.Element { })} /> )} - - {app.owner.urls.email && ( - - - - )}
@@ -108,9 +106,9 @@ function AppBlock({ app, setShowingAppDetails }: Props): JSX.Element {
- { + { const newQueryParams = new VSCodeQueryParams(app); newQueryParams.branch = branch!; setQueryParams(newQueryParams); @@ -122,44 +120,59 @@ function AppBlock({ app, setShowingAppDetails }: Props): JSX.Element { - {!!app.docsUrl - && telemetry.trackEvent(new OpenDocsEvent(app.name, app.owner.name))} + onClick={() => + telemetry.trackEvent(new OpenDocsEvent(app.name, app.owner.name)) + } > Documentation - } + + )} + +
+
-
- {app.stars} -
-
- {app.watchers} -
-
- {app.forks} -
{app.license && (
{app.license}
)} -
- Last updated {formatRelative(new Date(app.lastUpdate), new Date())} -
); diff --git a/site/src/app/Root.tsx b/site/src/app/Root.tsx index 116bbf9..6ff8bc8 100644 --- a/site/src/app/Root.tsx +++ b/site/src/app/Root.tsx @@ -14,6 +14,7 @@ import Header from './Header'; import { filterReducer, initialFilters } from './filters'; import Dialog from './Dialog'; import InstructionsDialog from './InstructionsDialog'; +import SupportDialog from './SupportDialog'; import AboutDialog from './AboutDialog'; import { telemetry } from './telemetry'; @@ -21,38 +22,41 @@ interface Props { apps: NormalisedApp[]; } -export type AppDetails = { id: string, sha: string }; +export type AppDetails = { id: string, sha: string, type: 'instruction' | 'support' }; function Root({ apps }: Props) { const [filters, dispatchFilters] = useReducer(filterReducer, initialFilters); const [showingAppDetails, setShowingAppDetails] = useState(null); const [showingAboutDialog, setShowingAboutDialog] = useState(false); + const [showingSupportInfo, setShowingSupportInfo] = useState(null); const dialogRef = useRef(null); function onDialogClose() { dialogRef.current?.close(); setShowingAppDetails(null); setShowingAboutDialog(false); + setShowingSupportInfo(null); } function showAboutDialog() { setShowingAboutDialog(true); } - useEffect(() => { - if (showingAppDetails !== null || showingAboutDialog) { - dialogRef.current?.showModal(); - } else { - onDialogClose(); + function setupTelemetry() { + const hostname = window.location.hostname; + let enableTelemetry = true; + + // Do not send telemetry events from an instance running on a localhost + if (hostname === 'localhost' || + hostname === '[::1]' || + hostname.match(/^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/)) { + enableTelemetry = false; } - }, [showingAppDetails, showingAboutDialog]); - useEffect(() => { - dialogRef.current?.addEventListener('close', onDialogClose); - return () => dialogRef.current?.removeEventListener('close', onDialogClose); - }); + telemetry.activate(enableTelemetry); + } - useEffect(() => { + function resolveUrlParams() { const searchParams = new URLSearchParams(window.location.search); const app = searchParams.get("app"); const ncs = searchParams.get("ncs"); @@ -68,6 +72,35 @@ function Root({ apps }: Props) { if (app || ncs) { telemetry.trackEvent(new SearchEvent(ncs ?? undefined, app ?? undefined)); } + } + + function setShowingDialog(app: AppDetails | null) { + switch (app?.type) { + case 'instruction': + setShowingAppDetails(app); + break; + case 'support': + setShowingSupportInfo(app); + break; + } + } + + useEffect(() => { + if (showingSupportInfo !== null || showingAppDetails !== null || showingAboutDialog) { + dialogRef.current?.showModal(); + } else { + onDialogClose(); + } + }, [showingAppDetails, showingAboutDialog, showingSupportInfo]); + + useEffect(() => { + dialogRef.current?.addEventListener('close', onDialogClose); + return () => dialogRef.current?.removeEventListener('close', onDialogClose); + }); + + useEffect(() => { + setupTelemetry(); + resolveUrlParams(); }, []); const showingApp = useMemo( @@ -75,11 +108,17 @@ function Root({ apps }: Props) { [showingAppDetails], ); + const showingSupportApp = useMemo( + () => apps.find((app) => app.id === showingSupportInfo?.id), + [showingSupportInfo], + ); + return (
{showingApp && } {showingAboutDialog && } + {showingSupportApp && }
- +
); diff --git a/site/src/app/SupportDialog.tsx b/site/src/app/SupportDialog.tsx new file mode 100644 index 0000000..cdf550d --- /dev/null +++ b/site/src/app/SupportDialog.tsx @@ -0,0 +1,48 @@ +/* Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +import { NormalisedApp } from '../schema'; +import { DialogTitle } from './Dialog'; + +interface Props { + app: NormalisedApp; + close: () => void; +} + +const Warning = ({warningText}: {warningText: string}) => { + return (
+

+ + ! + + {warningText} +

+
) +} + +function SupportDialog({ app, close }: Props): JSX.Element { + return ( +
+ + +
+

+ Use the following information to contact {app.owner.name}. +

+ + {app.owner.contact && +
    + {app.owner.contact?.devzoneUsername &&
  • Nordic Semiconductor DevZone username: {app.owner.contact?.devzoneUsername}
  • } + {app.owner.contact?.email &&
  • e-mail: {app.owner.contact?.email}
  • } +
+ } + + {!app.owner.contact && } +
+
+ ); +} + +export default SupportDialog; diff --git a/site/src/app/telemetry.ts b/site/src/app/telemetry.ts index d07d7dc..e07a274 100644 --- a/site/src/app/telemetry.ts +++ b/site/src/app/telemetry.ts @@ -13,20 +13,39 @@ export interface TelemetryEvent { }; class Telemetry { - private appInsights: ApplicationInsights; + private appInsights: ApplicationInsights | undefined; + private enabled = false; + private activated = false; - constructor(private connectionString: string) { - this.appInsights = new ApplicationInsights({ - config: { - connectionString: this.connectionString, - } - }); + constructor(private connectionString: string) {} - this.appInsights.loadAppInsights(); + activate(enabled: boolean) { + if (this.activated) { + return; + } + + if (this.enabled) { + this.appInsights = new ApplicationInsights({ + config: { + connectionString: this.connectionString, + } + }); + + this.appInsights.loadAppInsights(); + } + + this.activated = true; + this.enabled = enabled; + } + + private get canSend() { + return this.activated && this.enabled && this.appInsights; } trackEvent(e: T) { - this.appInsights.trackEvent({name: e.id, properties: e.props}); + if (this.canSend) { + this.appInsights?.trackEvent({name: e.id, properties: e.props}); + } } }; diff --git a/site/src/app/telemetryEvents.ts b/site/src/app/telemetryEvents.ts index 6837d3d..a6783c0 100644 --- a/site/src/app/telemetryEvents.ts +++ b/site/src/app/telemetryEvents.ts @@ -36,3 +36,10 @@ export class OpenDocsEvent extends NCSAddonsEvent { this.props = { app: app, org: org }; } }; + +export class ShowSupportInfoEvent extends NCSAddonsEvent { + constructor(app: string, revision: string, org: string) { + super('showSupportInfo'); + this.props = { app: app, revision: revision, org: org }; + } +}; diff --git a/site/src/sampleData.ts b/site/src/sampleData.ts index ee51d3e..999cd5d 100644 --- a/site/src/sampleData.ts +++ b/site/src/sampleData.ts @@ -18,11 +18,8 @@ function createFakeOrg(id: string): AppIndex['orgs'][string] { description: faker.lorem.paragraph(), kind: 'External', type: faker.helpers.arrayElement(validOrgTypes), - urls: { - support: faker.internet.url(), - email: faker.internet.email(), - twitter: faker.helpers.maybe(() => faker.internet.displayName()), - blog: faker.helpers.maybe(() => faker.internet.url()), + contact: { + devzoneUsername: faker.internet.email(), }, }; } @@ -33,16 +30,13 @@ function createFakeApp(): AppIndex['apps'][number] { return { id, name: faker.commerce.productName(), + docsUrl: faker.internet.url(), description: faker.lorem.paragraph(), defaultBranch: faker.git.branch(), - forks: faker.number.int({ max: 200 }), - isTemplate: faker.helpers.arrayElement([true, false]), kind: faker.helpers.arrayElement(validAppKinds), lastUpdate: faker.date.recent().toString(), license: faker.word.verb(), tags: faker.helpers.arrayElements(validTags), - watchers: faker.number.int({ max: 10 }), - stars: faker.number.int({ max: 10 }), manifest: faker.lorem.word(), owner: faker.helpers.arrayElement(companyIds), repo: `https://github.com/${faker.lorem.slug()}/${id}`, diff --git a/site/src/schema.ts b/site/src/schema.ts index d2ba75e..bcf8a12 100644 --- a/site/src/schema.ts +++ b/site/src/schema.ts @@ -67,6 +67,10 @@ export const appMetadataSchema = { description: 'The name of the application repo. Should be the repo-name in the GitHub URL: https://github.com/org/repo-name.', }, + repo: { + type: 'string', + description: 'The Url of an Add-on repository', + }, title: { type: 'string', description: @@ -81,6 +85,10 @@ export const appMetadataSchema = { default: 'west.yml', description: 'Alternative filename for the west manifest. Defaults to west.yml.', }, + avatar: { + type: 'string', + description: 'An url of an avatar displayed next to an Add-on.' + }, kind: appKindSchema, tags: { type: 'array', @@ -137,7 +145,7 @@ export const appMetadataSchema = { } }, additionalProperties: false, - required: ['name', 'kind', 'tags', 'releases'], + required: ['name', 'repo', 'kind', 'tags', 'releases', 'description', 'docsUrl'], } as const satisfies JSONSchema; export const orgIndexSchema = { @@ -151,6 +159,24 @@ export const orgIndexSchema = { type: 'string', description: 'A short sentence describing the organization.', }, + avatar: { + type: 'string', + description: 'An url of an avatar displayed next to an Add-on.' + }, + contact: { + type: 'object', + properties: { + devzoneUsername: { + type: 'string', + description: 'Nordic Semiconductor devzone`s account registered as responsible for providing the support for the Add-ons.', + }, + email: { + type: 'string', + description: 'The email that the Nordic Semiconductor DevZone account is registered with.' + } + }, + required: ['devzoneUsername'], + }, apps: { type: 'array', items: appMetadataSchema, @@ -172,21 +198,24 @@ export const orgSchema = { description: { type: 'string' }, type: { type: 'string', enum: validOrgTypes }, kind: { type: 'string', enum: validOrgKinds }, - location: { type: 'string' }, avatar: { type: 'string', format: 'uri' }, - urls: { + contact: { type: 'object', properties: { - support: { type: 'string', format: 'uri' }, - email: { type: 'string', format: 'uri' }, - blog: { type: 'string', format: 'uri' }, - twitter: { type: 'string', format: 'uri' }, + devzoneUsername: { + type: 'string', + description: 'Nordic Semiconductor devzone`s account registered as responsible for providing the support for the Add-ons.', + }, + email: { + type: 'string', + description: 'The email that the Nordic Semiconductor DevZone account is registered with.' + } }, - required: ['support'], + required: ['devzoneUsername'], additionalProperties: false, - }, + } }, - required: ['id', 'name', 'description', 'type', 'kind', 'urls'], + required: ['id', 'name', 'description', 'type', 'kind'], additionalProperties: false, } as const satisfies JSONSchema; @@ -199,7 +228,6 @@ export const appSchema = { description: { type: 'string' }, license: { type: 'string' }, repo: { type: 'string' }, - isTemplate: { type: 'boolean' }, owner: { type: 'string', description: 'The ID of the owner organization.' }, manifest: { type: 'string' }, kind: appKindSchema, @@ -219,9 +247,6 @@ export const appSchema = { }, minItems: 1, }, - watchers: { type: 'integer' }, - stars: { type: 'integer' }, - forks: { type: 'integer' }, defaultBranch: { type: 'string' }, lastUpdate: { type: 'string', format: 'date-time' }, apps: { type: 'string' }, @@ -236,23 +261,24 @@ export const appSchema = { }, }, required: ['detailsUrl'], + }, + avatar: { + type: 'string', + description: 'An image displayed next to an Add-on', } }, required: [ 'id', 'name', + 'repo', 'description', - 'isTemplate', 'owner', 'kind', 'tags', 'releases', - 'watchers', - 'stars', - 'forks', 'defaultBranch', 'lastUpdate', - 'repo' + 'docsUrl', ], additionalProperties: false, } as const satisfies JSONSchema;