Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 57 additions & 0 deletions .github/workflows/changelog.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: changelog

# Rebuilds CHANGELOG.md with git-cliff (config: cliff.toml) and opens a PR into
# dev with the result. dev and main only change through PRs, so the workflow
# can't push to them.
#
# Runs when a release or hotfix PR is merged into main (by then
# `Nova.ps1 publish` has tagged the release as nova/<tag>), or by hand from the
# Actions tab. Uses the CHANGELOG_TOKEN secret if it exists (a fine-grained
# token or GitHub App token with contents + pull-requests write), so the PR's
# CI checks run; without it, the PR is opened with the built-in token and a
# lead merges it without waiting for checks.

on:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: write
pull-requests: write

concurrency:
group: changelog
cancel-in-progress: false

jobs:
changelog:
name: changelog
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
with:
ref: dev
fetch-depth: 0

- name: Generate CHANGELOG.md
uses: orhun/git-cliff-action@v4
with:
config: cliff.toml
args: --verbose
env:
OUTPUT: CHANGELOG.md

- name: Open or update the changelog PR
uses: peter-evans/create-pull-request@v7
with:
token: ${{ secrets.CHANGELOG_TOKEN || github.token }}
base: dev
branch: chore/update-changelog
add-paths: CHANGELOG.md
commit-message: 'docs(changelog): update changelog'
title: 'docs(changelog): update changelog'
body: |
Regenerated by the `changelog` workflow with git-cliff after a release.
Only `CHANGELOG.md` changes. Merge with **Squash and merge**.
delete-branch: true
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Changelog

What changed in this repo in each Nova release, newest first.

> [!NOTE]
> This file is **generated** by [git-cliff](https://git-cliff.org) from the PR
> titles merged into `dev`, by the `changelog` GitHub Actions workflow. Don't
> edit it by hand: change `cliff.toml` instead. Each release's test, notes and
> the other repos' versions are on the
> [Nova-Collected releases page](https://github.com/UTATRocketry/Nova-Collected/releases).

93 changes: 72 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,86 @@
# NovaOps — Frontend
# NovaOps Frontend

Next.js + Chakra UI v3 ground control station.
The operator UI for the Nova ground station: live engine and flight-computer
data, the interactive P&ID, configuration and analysis tools, in any browser.
Built with Next.js, React and Chakra UI v3. It talks only to the
[NovaOps backend](https://github.com/UTATRocketry/novaOps-back), which checks
every command before anything moves.

## Set up
> [!IMPORTANT]
> This is a control surface. "No data" always renders as "—", never a made-up
> `0`, so an operator can tell stale from zero. Keep it that way
> ([style guide](https://github.com/UTATRocketry/Nova-Collected/blob/main/docs/development/style.md#typescript-and-react-frontend)).

## Repo structure

```
src/
├── app/ pages: engine, flight, devices, console, config, tools, settings
├── components/ primitives/ (shared), shell/ (app frame), and one folder per page
├── hooks/ data hooks (React Query, store)
└── lib/ no-React logic: api/ (REST), ws/ (WebSocket), store/, theme/, ...
public/ fonts, logos
tools/ backend_API.http: sample requests for the backend
```

Where new code goes: [organization guide](https://github.com/UTATRocketry/Nova-Collected/blob/main/docs/development/organization.md#novaops-front-frontend).

## Getting started

### Set up

Node.js 20+, and a running backend (see the backend's README).

```bash
npm install
cp .env.example .env.local # point at your backend
npm run build
cp .env.example .env.local
```

## Run It
`.env.local` points at the backend. When the UI and backend share a host, the
URLs can be left unset; they're derived from the page's address.

| Variable | Default in `.env.example` |
|---|---|
| `NEXT_PUBLIC_NOVA_API_BASE_URL` | `http://localhost:8000` |
| `NEXT_PUBLIC_NOVA_WS_BASE_URL` | `ws://localhost:8000` |
| `NEXT_PUBLIC_NOVA_FAKE_SIGNAL` | `0` (dev only: fake signal strength) |

### Run

```bash
npm run dev # development, http://localhost:3000
npm run build && npm run start # production
```
# for development
npm run dev # http://localhost:3000

# for production
npm run start
Check a change the way CI does:

```bash
npm run typecheck
npm run build
```

> [!NOTE]
> On the ground station the UI runs as a Windows service managed by
> Nova-Collected's `ops/Nova.ps1`.

### Troubleshoot

| Symptom | Fix |
|---|---|
| Every value shows "—" | The backend isn't reachable, or nothing is publishing. Check `.env.local` and that the backend and a simulator are running |
| `npm run build` fails fetching fonts | The build downloads Inter and JetBrains Mono from Google Fonts; it needs internet |
| `npm run lint` hangs | There's no ESLint config yet, so it waits for input. Don't run it in scripts |
| Commands do nothing | Your session is a `viewer`. Take a role in Settings |

`npm run typecheck` and `npm run build` both pass. (In a sandbox without
network access to Google Fonts the build can't fetch Inter/JetBrains Mono;
on a normal machine this works.)
More: [Nova SUPPORT.md](https://github.com/UTATRocketry/Nova-Collected/blob/main/SUPPORT.md).

## Notes / decisions baked in
## More information

- Color mode: dark is the default (bunker/night operation).
- Chrome colours (`chrome.*`) are fixed dark and do NOT follow color mode —
this is what keeps the nav rail and top bar dark in light mode.
- All "no data" states render an explicit dash, never a fabricated `0` —
important for a control surface (see the resilience section of the plan).
- Icons use the Material Symbols variable font; weight/fill is centralized in
`Icon.tsx`.
| To... | Read |
|---|---|
| Use the UI as an operator | [Operator guide](https://github.com/UTATRocketry/Nova-Collected/blob/main/docs/user-guides/operator-guide.md) |
| Look up the backend API | [Backend API](https://github.com/UTATRocketry/Nova-Collected/blob/main/docs/api/backend-api.md), or `/docs` on a running backend |
| Follow code style | [Style guide](https://github.com/UTATRocketry/Nova-Collected/blob/main/docs/development/style.md) |
| See what changed in each release | [CHANGELOG.md](CHANGELOG.md) |
| Contribute | [CONTRIBUTING.md](CONTRIBUTING.md) |
| See the whole system | [Nova-Collected](https://github.com/UTATRocketry/Nova-Collected) |
56 changes: 56 additions & 0 deletions cliff.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# git-cliff config: builds CHANGELOG.md from this repo's history.
# Run by .github/workflows/changelog.yml whenever a release reaches main.
# Docs: https://git-cliff.org/docs/configuration
#
# Versions are Nova releases. `Nova.ps1 publish` tags every commit a release
# pins as nova/<release>, e.g. nova/v2026.10.04, and each section lists the
# PRs merged into dev since the previous release. PR titles are squash-merged
# into dev, so they follow Conventional Commits (CONTRIBUTING.md section 6).

[changelog]
header = """
# Changelog

What changed in this repo in each Nova release, newest first.

> [!NOTE]
> This file is **generated** by [git-cliff](https://git-cliff.org) from the PR
> titles merged into `dev`, by the `changelog` GitHub Actions workflow. Don't
> edit it by hand: change `cliff.toml` instead. Each release's test, notes and
> the other repos' versions are on the
> [Nova-Collected releases page](https://github.com/UTATRocketry/Nova-Collected/releases).
"""
body = """
{% if version %}\
{% set release = version | trim_start_matches(pat="nova/") %}\
## [{{ release }}](https://github.com/UTATRocketry/Nova-Collected/releases/tag/{{ release }}) - {{ timestamp | date(format="%Y-%m-%d") }}
{% for group, commits in commits | group_by(attribute="group") %}
### {{ group | striptags | trim }}
{% for commit in commits %}
- {% if commit.scope %}**{{ commit.scope }}:** {% endif %}\
{% if commit.breaking %}**BREAKING** {% endif %}\
{{ commit.message | split(pat="\n") | first | trim }}\
{% endfor %}
{% endfor %}
{% endif %}\
"""
trim = false

[git]
conventional_commits = true
filter_unconventional = true
filter_commits = true
split_commits = false
protect_breaking_commits = true
tag_pattern = "^nova/v[0-9]{4}\\.[0-9]{2}\\.[0-9]{2}(\\.[0-9]+)?$"
sort_commits = "oldest"
commit_parsers = [
# The changelog workflow's own PRs.
{ message = "^docs\\(changelog\\)", skip = true },
{ message = "^feat", group = "<!-- 1 -->Features" },
{ message = "^fix", group = "<!-- 2 -->Fixes" },
{ message = "^refactor", group = "<!-- 3 -->Refactoring" },
{ message = "^docs", group = "<!-- 4 -->Documentation" },
{ message = "^test", group = "<!-- 5 -->Tests" },
{ message = "^chore", group = "<!-- 6 -->Chores" },
]
Loading