Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
55a5d19
Update vertex production image tag
github-actions[bot] Jul 6, 2026
2aac7fa
Update analytics production image tag
github-actions[bot] Jul 6, 2026
f9fdbc4
feat(api): add TypeScript interfaces for API responses and forum events
OchiengPaul442 Jul 6, 2026
a59ecdc
refactor(ui): simplify layout and styling in ContactPage, layout, and…
OchiengPaul442 Jul 6, 2026
30baf60
feat(blog): enhance BlogDetailPage and BlogsPage with improved layout…
OchiengPaul442 Jul 6, 2026
3118b2a
refactor(blogs): improve BlogCard and BlogCardSkeleton components for…
OchiengPaul442 Jul 7, 2026
7c76260
refactor(ui): enhance styling and layout across FrameworkTabs, StatCa…
OchiengPaul442 Jul 7, 2026
ecd381e
refactor(ui): update styling for CustomButton, IconsPackagePage, and …
OchiengPaul442 Jul 7, 2026
330a6b1
refactor(ui): update layout and styling in BlogDetailPage and AirQoDe…
OchiengPaul442 Jul 7, 2026
402364a
refactor(ui): update tab links in TabNavigation component for consist…
OchiengPaul442 Jul 7, 2026
3452751
Add lazy loading for air quality card lists
Mozart299 Jul 7, 2026
67f2e07
Fix map markers rendering too small
Mozart299 Jul 7, 2026
e82c417
Add unit tests for API routes, base API service, and Redux slices
OchiengPaul442 Jul 7, 2026
737e119
feat(mobile): add Clean Air Forum selfie filter, live camera guide, a…
2phonebabykeem Jul 7, 2026
cc96356
fix(mobile): clear disposed camera controller and send submission secret
2phonebabykeem Jul 7, 2026
ccedf45
refactor(ui): update font styles in FloatingMiniBillboard and AirQual…
OchiengPaul442 Jul 7, 2026
d01ff94
fix(readme): remove unnecessary flag from codecov badge URL
OchiengPaul442 Jul 7, 2026
5aa3c0b
chore(vertex-desktop): bump version to 0.1.11
Codebmk Jul 7, 2026
0b1c7aa
refactor(ui): adjust spacing in BlogDetailPage content styles for imp…
OchiengPaul442 Jul 7, 2026
4c1f0b1
refactor(ui): simplify structure of content rendering in BlogDetailPage
OchiengPaul442 Jul 7, 2026
b1ceba1
fix(mobile): address review findings on selfie filter (mounted guards…
2phonebabykeem Jul 7, 2026
a20a662
refactor(tests): streamline localStorage mock calls in cookieConsent …
OchiengPaul442 Jul 7, 2026
bc5a4f7
refactor(ui): restructure Footer component for improved layout and ma…
OchiengPaul442 Jul 7, 2026
06d41b3
refactor(ui): update breadcrumb separators from '>' to '/' for consis…
OchiengPaul442 Jul 7, 2026
5cfa6b6
Merge pull request #3757 from airqo-platform/vertex-desktop-bump-0.1.11
Baalmart Jul 7, 2026
147bbd3
Merge pull request #3758 from airqo-platform/web-unit-test
Baalmart Jul 7, 2026
b7caabf
Merge pull request #3759 from airqo-platform/feature/lazy-loading-air…
Baalmart Jul 7, 2026
aecded4
refactor(ui): update references from AirQo Analytics to Nexus Platfor…
OchiengPaul442 Jul 7, 2026
25476a7
Merge branch 'staging' into web-unit-test
OchiengPaul442 Jul 7, 2026
3f7ce1e
Add documentation for Nexus Dashboard and Interactive Map; implement …
OchiengPaul442 Jul 7, 2026
afc0552
Merge pull request #3755 from airqo-platform/feature/clean-air-forum-…
Baalmart Jul 7, 2026
9f9f484
refactor: update references to AirQo Nexus for consistency across the…
OchiengPaul442 Jul 7, 2026
118e957
refactor: update product names to include "AirQo" for consistency acr…
OchiengPaul442 Jul 7, 2026
2737d5e
Merge branch 'staging' into web-unit-test
OchiengPaul442 Jul 7, 2026
1e82647
fix(docs): update references to AirQo Nexus across documentation
OchiengPaul442 Jul 7, 2026
0e6edfa
Merge branch 'staging' into branding-change
OchiengPaul442 Jul 7, 2026
8955b94
refactor: update AirQo Nexus references and paths across the application
OchiengPaul442 Jul 7, 2026
ffbf64e
fix(docs): update AirQo Nexus links to the new domain
OchiengPaul442 Jul 7, 2026
6236537
refactor: update URLs to point to Nexus for consistency across the ap…
OchiengPaul442 Jul 7, 2026
e4a9e69
refactor: enhance CI workflow with improved failure handling and PR c…
OchiengPaul442 Jul 7, 2026
c19f9fb
Merge pull request #3761 from airqo-platform/web-unit-test
Baalmart Jul 7, 2026
3b482e1
fix(docs): correct URL formatting in account creation, login, and pas…
OchiengPaul442 Jul 7, 2026
7272a9d
Merge pull request #3763 from airqo-platform/branding-change
Baalmart Jul 7, 2026
b9bcbcc
Update docs staging image tag to stage-7272a9d8-1783453080
github-actions[bot] Jul 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
216 changes: 216 additions & 0 deletions .github/workflows/website-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
name: website-ci

on:
pull_request:
branches:
- staging
- main
paths:
- "src/website/**"
- ".github/workflows/website-ci.yml"
push:
branches:
- staging
paths:
- "src/website/**"
- ".github/workflows/website-ci.yml"

permissions:
contents: write
pull-requests: write

jobs:
check-website:
name: check-website
runs-on: ubuntu-latest
defaults:
run:
working-directory: src/website
steps:
- name: Checkout
id: checkout
uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
with:
persist-credentials: true

- name: Setup Node
id: setup-node
uses: actions/setup-node@39370e3970a6d050c480ffad4ff0ed4d3fdee5af
with:
node-version: "20"
cache: "npm"
cache-dependency-path: src/website/package-lock.json

- name: Install dependencies
id: install
run: npm ci

- name: Lint
id: lint
run: npm run lint

- name: Type Check
id: typecheck
run: npx tsc --noEmit

- name: Unit Tests & Coverage
id: tests
run: npm run test:coverage

- name: Extract test stats
id: test-stats
if: github.event_name == 'push'
working-directory: src/website
shell: bash
run: |
TESTS=$(npm run test:coverage 2>&1 | grep -oP 'Tests:\s+\K[\d,]+' || echo "0")
TESTS=$(echo "$TESTS" | tr -d ',')
echo "tests=$TESTS" >> "$GITHUB_OUTPUT"

COVERAGE=$(cat coverage/coverage-summary.json 2>/dev/null | grep -oP '"total":\{"lines":\{"total":\d+,"covered":\d+,"skipped":0,"pct":\K[\d.]+' || echo "0")
echo "coverage=$COVERAGE" >> "$GITHUB_OUTPUT"

- name: Update README badges
if: github.event_name == 'push'
working-directory: src/website
shell: bash
run: |
TESTS=${{ steps.test-stats.outputs.tests }}
COVERAGE=${{ steps.test-stats.outputs.coverage }}

if [ -z "$TESTS" ] || [ "$TESTS" = "0" ]; then
TESTS=$(grep -oP 'Tests:\s+\K[\d,]+' <(npm run test:coverage 2>&1) | tail -1 | tr -d ',')
fi
if [ -z "$COVERAGE" ] || [ "$COVERAGE" = "0" ]; then
COVERAGE=$(python3 -c "import json; d=json.load(open('coverage/coverage-summary.json')); print(d['total']['lines']['pct'])" 2>/dev/null || echo "0")
fi

COVERAGE_INT=$(python3 -c "print(int(float('${COVERAGE:-0}')))" 2>/dev/null || echo "0")

if [ "$COVERAGE_INT" -ge 80 ]; then
COLOR="brightgreen"
elif [ "$COVERAGE_INT" -ge 60 ]; then
COLOR="yellow"
else
COLOR="red"
fi

if [ -f README.md ]; then
sed -i "s|![passing](https://img.shields.io/badge/passing-[0-9]*%20.*brightgreen)|![passing](https://img.shields.io/badge/passing-${TESTS}%20%E2%80%94%20100%25-brightgreen?style=flat-square)|g" README.md

if grep -q "![coverage]" README.md; then
sed -i "s|![coverage](https://img.shields.io/badge/coverage-[0-9]*%25.*)|![coverage](https://img.shields.io/badge/coverage-${COVERAGE_INT}%25-${COLOR}?style=flat-square)|g" README.md
else
sed -i "/![failing]/a ![coverage](https://img.shields.io/badge/coverage-${COVERAGE_INT}%25-${COLOR}?style=flat-square)" README.md
fi

echo "Updated README badges: tests=$TESTS, coverage=${COVERAGE_INT}%"
fi

- name: Commit README updates
if: github.event_name == 'push'
working-directory: src/website
run: |
git config --local user.email "github-actions[bot]@users.noreply.github.com"
git config --local user.name "github-actions[bot]"
git add README.md
git diff --staged --quiet || git commit -m "chore: update README test/coverage badges [skip ci]"
git push
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Upload coverage to Codecov
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
continue-on-error: true
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: src/website/coverage/
flags: website
fail_ci_if_error: false

- name: Comment on PR - Failure
if: failure() && github.event_name == 'pull_request'
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea
with:
script: |
const fs = require('fs');
const steps = [
{ name: 'Checkout', id: 'checkout', emoji: '📦' },
{ name: 'Setup Node', id: 'setup-node', emoji: '⚙️' },
{ name: 'Install dependencies', id: 'install', emoji: '📥' },
{ name: 'Lint', id: 'lint', emoji: '🔍' },
{ name: 'Type Check', id: 'typecheck', emoji: '🔎' },
{ name: 'Unit Tests & Coverage', id: 'tests', emoji: '🧪' }
];

const failedSteps = [];
for (const step of steps) {
const outcome = context.payload.workflow_run?.jobs?.[0]?.steps?.find(
s => s.name === step.name
)?.outcome;
if (outcome === 'failed') {
failedSteps.push(step);
}
}

// Fallback: if we couldn't determine exact step, assume tests or lint
if (failedSteps.length === 0) {
failedSteps.push({ name: 'Unknown step', id: 'unknown', emoji: '❌' });
}

const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
const failedList = failedSteps.map(s => `${s.emoji} **${s.name}**`).join('\n');

const body = [
'## ❌ Website CI Failed',
'',
`**Failed step(s):**`,
failedList,
'',
'### Next Steps',
'',
];

if (failedSteps.some(s => s.id === 'lint')) {
body.push('- Run `npm run lint:fix` to auto-fix lint issues');
}
if (failedSteps.some(s => s.id === 'typecheck')) {
body.push('- Check TypeScript errors above and fix type issues');
}
if (failedSteps.some(s => s.id === 'tests')) {
body.push('- Run `npm run test:coverage` locally to see test failures');
}
if (failedSteps.some(s => s.id === 'install')) {
body.push('- Check for dependency issues, try `rm -rf node_modules && npm install`');
}

body.push('', `🔗 [View workflow run](${runUrl})`);
body.push('', '---');
body.push('*Please fix the issues and push again.*');

// Find and update existing comment or create new one
const marker = '## ❌ Website CI Failed';
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
per_page: 100
});

const existing = comments.find(c => c.body.includes(marker));

if (existing) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: existing.id,
body: body.join('\n')
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: body.join('\n')
});
}
35 changes: 35 additions & 0 deletions .github/workflows/website-codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
name: website-codecov

on:
workflow_call:
inputs:
coverage-directory:
description: "Path to coverage directory"
required: false
type: string
default: "src/website/coverage/"
flag:
description: "Codecov flag name"
required: false
type: string
default: "website"
secrets:
CODECOV_TOKEN:
required: true

permissions:
contents: read

jobs:
upload-coverage:
name: Upload Website Coverage
runs-on: ubuntu-latest
steps:
- name: Upload coverage to Codecov
uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0
continue-on-error: true
with:
token: ${{ secrets.CODECOV_TOKEN }}
directory: ${{ inputs.coverage-directory }}
flags: ${{ inputs.flag }}
fail_ci_if_error: false
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
replicaCount: 2
image:
repository: airqoacr.azurecr.io/airqo-stage-docs
tag: stage-ca5b86e0-1783294032
tag: stage-7272a9d8-1783453080
pullPolicy: IfNotPresent
imagePullSecrets: []
nameOverride: ''
Expand Down
2 changes: 1 addition & 1 deletion k8s/platform/k8s-aks/airqo-platform/values-prod.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
replicaCount: 1
image:
repository: airqoacr.azurecr.io/airqo-next-platform
tag: prod-32c69c48-1783334514
tag: prod-c0e8e0d3-1783367904
pullPolicy: IfNotPresent
imagePullSecrets: []
nameOverride: ''
Expand Down
2 changes: 1 addition & 1 deletion k8s/vertex/k8s-aks/airqo-vertex/values-prod.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
replicaCount: 2
image:
repository: airqoacr.azurecr.io/airqo-vertex
tag: prod-32c69c48-1783334514
tag: prod-c0e8e0d3-1783367904
pullPolicy: IfNotPresent
imagePullSecrets: []
nameOverride: ''
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,17 @@ sidebar_label: Export Air Quality Data

# Export Air Quality Data

The Data Export page lets you download air quality datasets from AirQo Analytics. Select locations across Africa, configure date ranges and pollutants, and export in multiple formats for offline analysis.
The Data Export page lets you download air quality datasets from AirQo Nexus. Select locations across Africa, configure date ranges and pollutants, and export in multiple formats for offline analysis.

---

## Accessing the Export Page

1. Log in to [AirQo Analytics](https://analytics.airqo.net).
1. Log in to [AirQo Nexus](https://nexus.airqo.net/).
2. Open the sidebar and select **Visualization & Data Export** under the Data & Analysis section.
3. Direct links:
- **Individual workflow:** [analytics.airqo.net/user/data-export](https://analytics.airqo.net/user/data-export)
- **Organization workflow:** `https://analytics.airqo.net/org/<your-org>/data-export`
- **Individual workflow:** [nexus.airqo.net//user/data-export](https://nexus.airqo.net//user/data-export)
- **Organization workflow:** `https://nexus.airqo.net//org/<your-org>/data-export`

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,27 @@ sidebar_label: Creating an Account

# Creating an Account

Create a free AirQo Analytics account to explore air quality data, save favorite locations, and collaborate with your organization.
Create a free AirQo Nexus account to explore air quality data, save favorite locations, and collaborate with your organization.

---

## Before You Start

AirQo Analytics supports two workflows:
AirQo Nexus supports two workflows:

| Workflow | Best For | Web Address |
|----------|----------|-------------|
| **Individual** | Personal research, analysis, and exploration | `https://analytics.airqo.net/user/*` |
| **Organization** | Teams, government agencies, and NGOs managing sensor networks | `https://analytics.airqo.net/org/<your-org>/*` |
| **Individual** | Personal research, analysis, and exploration | `https://nexus.airqo.net/user/*` |
| **Organization** | Teams, government agencies, and NGOs managing sensor networks | `https://nexus.airqo.net/org/<your-org>/*` |

New accounts begin in the **Individual** workflow. You can switch to an organization from the header once you have been invited.

---

## Sign Up

1. Go to [AirQo Analytics](https://analytics.airqo.net).
2. Select **Sign Up** on the welcome page, or open the [registration page](https://analytics.airqo.net/user/creation/individual/register).
1. Go to [AirQo Nexus](https://nexus.airqo.net/).
2. Select **Sign Up** on the welcome page, or open the [registration page](https://nexus.airqo.net/user/creation/individual/register).
3. Enter the following details:
- **First name**
- **Last name**
Expand All @@ -34,7 +34,7 @@ New accounts begin in the **Individual** workflow. You can switch to an organiza
4. Read and accept the [Terms of Service](https://airqo.net/legal/terms-of-service) and [Privacy Policy](https://airqo.net/legal/privacy-policy).
5. Select **Continue**.
6. Check your email for a verification message and select the confirmation link.
7. Once verified, return to AirQo Analytics and [log in](https://analytics.airqo.net/user/login).
7. Once verified, return to AirQo Nexus and [log in](https://nexus.airqo.net/user/login).

:::tip Can’t find the email?
Check your spam or junk folder. If it is not there after a few minutes, try signing up again.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ sidebar_label: Deleting Your Account

# Deleting Your Account

You can permanently delete your AirQo Analytics account from your profile settings.
You can permanently delete your AirQo Nexus account from your profile settings.

---

## Before You Delete

Deleting your account:

- Removes your account and personal data from AirQo Analytics
- Removes your account and personal data from AirQo Nexus
- Revokes your access to all organizations
- Cannot be undone

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,26 @@ sidebar_label: Logging In

# Logging In

Access your AirQo Analytics account with your email and password or through a linked social account.
Access your AirQo Nexus account with your email and password or through a linked social account.

---

## Log In with Email and Password

1. Go to the [AirQo Analytics login page](https://analytics.airqo.net/user/login).
1. Go to the [AirQo Nexus login page](https://nexus.airqo.net/user/login).
2. Enter your **email address** and select **Continue**.
3. Enter your **password** and select **Login**.
4. You are taken to your [Home page](https://analytics.airqo.net/user/home).
4. You are taken to your [Home page](https://nexus.airqo.net/user/home).

---

## Log In with a Social Account

You can use an existing Google, GitHub, LinkedIn, or X (Twitter) account to sign in or sign up.

1. On the [login](https://analytics.airqo.net/user/login) or [registration](https://analytics.airqo.net/user/creation/individual/register) page, select the icon for the provider you want to use.
1. On the [login](https://nexus.airqo.net/user/login) or [registration](https://nexus.airqo.net/user/creation/individual/register) page, select the icon for the provider you want to use.
2. Complete the provider's sign-in steps.
3. You are returned to AirQo Analytics.
3. You are returned to AirQo Nexus.
- If you are new, an account is created automatically.
- If you already have an AirQo account linked to that provider, you are signed in.

Expand Down
Loading
Loading