Offline-first PWA logbook for amateur radio operators.
Live app: https://nuetzliches.github.io/qso-log/
- Offline-first - works entirely without internet after the first load. All data is stored locally in the browser (IndexedDB).
- QSO logging - log contacts with auto-incrementing sequence numbers, UTC timestamps, mode-aware RST defaults, and automatic band detection from frequency. Form drafts are auto-saved so nothing is lost on accidental page reloads.
- Map view - visualize QSO locations on an interactive Leaflet map with marker clustering and dark-mode support.
- Statistics - interactive charts for band/mode distribution, activity timeline with streaks, DXCC/country tracking, and distance analysis. Supports operator filtering.
- Maidenhead locator - enter grid squares for your station and contacts. Distance and bearing are calculated automatically. A GPS button can auto-fill your locator from device coordinates.
- QSL tracking - track QSL sent/received status per QSO (yes / no / requested).
- Import & Export - ADIF 3.1.4, CSV, and JSON. Import includes validation, duplicate detection, and preview before committing.
- PDF reports - generate printable QSO reports (A4 landscape) with summary statistics.
- Callsign lookup - optional online lookup via QRZ.com or HamQTH to auto-fill name, QTH, country, and grid square.
- Multi-operator - manage multiple operator profiles and switch between them.
- Internationalization - German and English UI with automatic browser-language detection on first visit.
- Dark mode - light, dark, or system-preference theme.
- PWA - installable on desktop and mobile with home screen shortcuts and a custom install prompt.
- Mobile-optimized - responsive navigation
Prerequisites: Node.js 18+
# Install dependencies
npm install
# Start dev server
npm run dev
# Build for production
npm run build
# Preview production build
npm run previewQSOlog has seven main views:
| View | Description |
|---|---|
| New QSO | Log a new contact. Select mode, enter frequency (band auto-detects), callsign, RST, locator, and remarks. Common FT8/SSB/FM frequencies are available as presets. |
| History | Browse, filter, and search your logged QSOs. Export to ADIF/CSV/JSON, import from files (drag & drop supported), and generate PDF reports. |
| Statistics | Interactive charts and stats: band/mode distribution, activity timeline with streaks, DXCC countries, and distance analysis. Filter by operator. |
| Map | Interactive map showing QSO locations based on Maidenhead locators, with marker clustering. |
| Operators | Manage operator profiles with callsign, name, and QTH. |
| Settings | Configure language, theme, your station callsign (used in PDF headers), and optional QRZ.com / HamQTH API credentials for callsign lookups. |
| About | Version info, licenses, and AI disclosure. |
IARU Region 1: 160m, 80m, 60m, 40m, 30m, 20m, 17m, 15m, 12m, 10m, 6m, 2m, 70cm, 23cm
- Vue 3 + TypeScript + Vite
- Pinia (state management)
- Dexie.js (IndexedDB)
- Tailwind CSS + Headless UI
- Chart.js + vue-chartjs (interactive charts)
- Leaflet + leaflet.markercluster (map & clustering)
- jsPDF + jspdf-autotable (PDF generation)
- adif-parser-ts (ADIF parsing)
- vite-plugin-pwa (service worker & manifest)
# Unit tests
npm test
# Unit tests (watch mode)
npm run test:watch
# E2E tests (requires Playwright browsers)
npx playwright install
npm run test:e2eThis project uses release-it for semi-automatic releases. A single command bumps the version, generates the changelog, creates a git tag, and publishes a GitHub Release.
Commits must follow Conventional Commits so the changelog is generated correctly:
| Prefix | Example | Changelog section | Version bump |
|---|---|---|---|
feat: |
feat(qso): add SOTA field |
Features | minor |
fix: |
fix: correct RST default |
Bug Fixes | patch |
docs: |
docs: update README |
Documentation | patch |
perf: |
perf: speed up import |
Performance | patch |
refactor: |
refactor: extract utils |
Refactoring | patch |
test: |
test: add export tests |
Tests | patch |
BREAKING CHANGE |
footer in any commit | — | major |
Commits prefixed with chore:, style:, ci:, or build: are hidden from the changelog.
Authenticate with GitHub so release-it can create GitHub Releases:
gh auth loginAlternatively, set a GITHUB_TOKEN environment variable with contents: write permission.
Since direct commits to main are not allowed, every release is created from a release branch:
- Make sure
mainis up to date:git checkout main git pull
# Interactive -- prompts for patch / minor / major
npm run release
# Directly bump a specific level
npm run release -- patch
npm run release -- minor
# Preview without making any changes
npm run release -- --dry-run- Unit tests and production build run as a safety gate
- Version in
package.jsonis bumped CHANGELOG.mdis updated from commits since the last tag- A git commit (
chore: release vX.Y.Z) and annotated tag (vX.Y.Z) are created - Commit and tag are pushed to
origin - A GitHub Release with auto-generated notes is published
- The existing GitHub Pages workflow deploys the new build
Parts of this codebase were developed with the assistance of AI coding tools (e.g. Claude, GitHub Copilot). All code has been reviewed and is maintained by human contributors.