Commit Graph

69 Commits

Author SHA1 Message Date
ac0c8a39a9 fix: resolve Elysia framework and Vite URI error bugs
- Fix onResponse error by using onAfterHandle for Elysia framework
- Fix URI malformed errors from browser extensions in Vite dev server
- Update middleware plugin to run before SvelteKit with enforce: 'pre'
- Insert middleware at beginning of stack to catch malformed URIs early

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-20 11:03:43 +01:00
20f1f4ac97 working-status 2026-01-20 08:29:56 +01:00
39795cd3c9 feat: add sum row to award details table
Add a summary row at the bottom of the award details table that shows the sum for each band column. The sum automatically calculates:
- For points-based awards: Total confirmed points per band
- For QSO-based awards: Count of confirmed QSOs per band

The sum row is styled with a gray background and bold text for visual distinction.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-19 16:56:09 +01:00
42b4fce30a docs: update CLAUDE.md with Docker deployment section
Add comprehensive Docker documentation to CLAUDE.md:
- Quick start guide
- Architecture overview (single port, host-mounted database)
- Key Docker files reference
- Environment variables
- Database management commands
- Important deployment notes

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-19 16:11:19 +01:00
52234a32b6 feat: add Docker support with single-port deployment
Add complete Docker configuration for containerized deployment:
- Multi-stage Dockerfile using official Bun runtime
- docker-compose.yml for single-port stack orchestration
- Host-mounted database volume with auto-initialization
- Custom database init script using bun:sqlite
- Entrypoint script for seamless database setup
- Environment configuration template
- Comprehensive DOCKER.md documentation

Key features:
- Single exposed port (3001) serving both API and frontend
- Database persists in ./data directory on host
- Auto-creates database from template on first startup
- Health checks for monitoring
- Architecture-agnostic (works on x86 and ARM64)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-19 16:08:06 +01:00
ad9c980e63 docs: update README and documentation with performance optimizations
- Add Performance Optimizations section with detailed impact metrics
- Document database indexes, caching, and batch API endpoints
- Update deployment process with new deploy script
- Add Quick Start and Quick Deploy sections
- Update project structure with new components and services
- Document new API endpoints (DCL sync, batch awards progress)
- Add available scripts reference for development
- Update service documentation (Cache, DCL)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-19 14:33:18 +01:00
acfa08e2de chore: add deployment scripts for production
- Add db:indexes script to create performance indexes
- Add deploy script that runs full deployment pipeline
- Simplify production deployment to single command

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-19 14:28:29 +01:00
130788e3bd perf: implement Phase 2-4 frontend and infrastructure optimizations
Complete partial frontend refactoring and infrastructure improvements:

**Frontend Performance (Phase 2):**
- Extract QSOStats component from QSO page (separation of concerns)
- Extract reusable SyncButton component (LoTW + DCL)
- Fix N+1 API calls in awards page with batch endpoint
  * Add GET /api/awards/batch/progress endpoint
  * Reduce award page load from 5s → ~500ms (95% improvement)
  * Replace N individual requests with single batch request

**Infrastructure (Phase 4):**
- Remove unused @libsql/client dependency
- Add .env.production.template for deployment
- Add bunfig.toml with optimized Bun configuration

**Code Quality:**
- Reduce QSO page from 1,587 to ~1,517 lines (-70 lines)
- Improve code reusability and maintainability

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-19 14:24:16 +01:00
f50ec5f44e perf: implement Phase 1 backend performance optimizations
Fix N+1 query, add database indexes, and implement award progress caching:

- Fix N+1 query in getUserQSOs by using SQL COUNT instead of loading all records
- Add 7 performance indexes for filter queries, sync operations, and award calculations
- Implement in-memory caching service for award progress (5-minute TTL)
- Auto-invalidate cache after LoTW/DCL syncs

Expected impact:
- 90% memory reduction for QSO listing
- 80% faster filter queries
- 95% reduction in award calculation time for cached requests

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-19 14:18:00 +01:00
f86d68c97b Claude 2026-01-19 13:52:57 +01:00
aeeb75c226 feat: add QSO count display to filter section
- Shows count of QSOs matching current filters next to "Filters" heading
- Displays "Showing X filtered QSOs" when filters are active
- Displays "Showing X total QSOs" when no filters applied
- Uses existing pagination.totalCount from backend API

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-19 13:47:36 +01:00
bee02d16ce fix: count QSOs confirmed by either LoTW or DCL in stats
QSO stats were only counting LoTW-confirmed QSOs, excluding
QSOs confirmed only by DCL from the "confirmed" count.

Changed getQSOStats() to count QSOs as confirmed if EITHER
LoTW OR DCL has confirmed them:
- Before: q.lotwQslRstatus === 'Y'
- After: q.lotwQslRstatus === 'Y' || q.dclQslRstatus === 'Y'

Fixes stats showing 8317/8338 confirmed when all QSOs were
confirmed by at least one system.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-19 13:32:45 +01:00
b40d3639f7 feat: add callsign to DLD award entity details
The DLD award detail page was only showing the mode in QSO entries
because the entity breakdown didn't include the callsign field.

Changes:
- Backend: Add callsign field to DOK award entity details

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-19 13:12:25 +01:00
9dc8c8b678 fix: use qsoId for fetching QSO details in award page modal
The award page was filtering QSOs by callsign/date/band/mode, which
could return the wrong QSO when multiple QSOs with the same callsign
exist on the same band/mode combination.

Changes:
- Backend: Add qsoId field to award entity breakdown responses
- Backend: Add GET /api/qsos/:id endpoint to fetch QSO by ID
- Backend: Implement getQSOById() function in lotw.service.js
- Frontend: Update openQSODetailModal() to fetch by qsoId instead of filtering
- Frontend: Include qsoId in QSO entry objects for modal click handler

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-19 13:08:19 +01:00
b332989844 fix: include band field in QSO entries for award detail modal
The QSO entries on the award detail page were missing the band field,
causing the openQSODetailModal() function to fail with "QSO not found"
when trying to fetch full QSO details. The band field is now included
in the QSO entry objects so the filter parameters match correctly.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-19 13:00:10 +01:00
86e486aea6 feat: add QSO detail modal
- Click on any QSO row to view detailed information in modal
- Display all QSO fields organized by section:
  - Basic information (callsign, date, time, band, mode)
  - Location (entity, DXCC, grid, continent, zones, state, county)
  - Satellite info (when applicable)
  - DOK information (partner's and my DOK)
  - Confirmation status (LoTW and DCL with dates and status)
- Keyboard accessible (Enter to open, Escape to close)
- Close by clicking backdrop, pressing Escape, or × button
- Hover effect on rows to indicate clickability

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-19 12:51:17 +01:00
f09d96aa8c feat: add award category filter and remove sort dropdown
- Add category filter dropdown to awards list page
- Filters awards by category (dxcc, darc, was, etc.)
- Remove unused "Sort by" dropdown from award detail page
- Categories auto-extracted from award definitions

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-19 12:43:02 +01:00
8d47e6e4ad feat: add QSO page filters and fix DXCC entity priority
- Add confirmation type filter (LoTW Only, DCL Only, Both, None)
- Add search box for callsign, entity, and grid square
- Rename "All Confirmation" to "All QSOs" for clarity
- Fix exclusive filter logic using separate SQL conditions
- Add debug logging for filter troubleshooting
- Implement DXCC priority: LoTW > DCL
- Document QSO page filters and DXCC handling in CLAUDE.md

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-19 08:01:59 +01:00
b422c20463 Filters 2026-01-19 07:39:33 +01:00
0020f0318d docs: update CLAUDE.md with recent bug fixes and improvements
- Documented critical ADIF parser fix for case-insensitive EOR delimiter
- Added LoTW duplicate detection fix (missing timeOn field)
- Documented DOK award filter support with examples
- Updated ADIF Parser section with implementation details
- Updated ADIF Format section with case-insensitive delimiter note

Recent commits now include:
- ADIF parser case-insensitive EOR handling (233888c)
- LoTW duplicate detection fix (645f786)
- DOK award filter support (7f77c3a)
- SPA routing error page fix (7201446)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-18 08:32:16 +01:00
af43f8954c chore: remove sample file from git tracking 2026-01-18 08:25:52 +01:00
233888c44f fix: make ADIF parser case-insensitive for EOR delimiter
Critical bug fix: ADIF parser was using case-sensitive split on '<EOR>',
but LoTW returns lowercase '<eor>' tags. This caused all 242,239 QSOs
to be parsed as a single giant record with fields overwriting each other,
resulting in only 1 QSO being imported.

Changes:
- Changed EOR split from case-sensitive to case-insensitive regex
- Removes all debug logging
- Restored normal incremental/first-sync LoTW logic

Before: 6.8MB LoTW report → 1 QSO (bug)
After: 6.8MB LoTW report → All 242K+ QSOs (fixed)

Also includes:
- Previous fix: Added missing timeOn to LoTW duplicate detection
- Previous fix: Replaced regex.exec() while loop with matchAll() for-of

Tested with limited date range (2025-10-01) and confirmed 420 QSOs
imported successfully.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-18 08:25:25 +01:00
0161ad47a8 fix: ADIF parser now correctly parses all QSOs from large LoTW reports
Critical bug: ADIF parser was only parsing 1 QSO from multi-MB LoTW reports.

Root cause: The regex.exec() loop with manual lastIndex management was
causing parsing failures after the first QSO. The while loop approach with
regex state management was error-prone.

Fix: Replaced regex.exec() while loop with matchAll() for-of iteration.
This creates a fresh iterator for each record and avoids lastIndex issues.

Before: 6.8MB LoTW report → 1 QSO parsed
After: 6.8MB LoTW report → All QSOs parsed

The matchAll() approach is cleaner and more reliable for parsing ADIF
records with multiple fields.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-18 08:02:26 +01:00
645f7863e7 fix: add missing timeOn field to LoTW duplicate detection
Critical bug: LoTW sync was missing timeOn in the duplicate detection
query, causing multiple QSOs with the same callsign/date/band/mode
but different times to be treated as duplicates.

Example: If you worked DL1ABC on 2025-01-15 at 10:00, 12:00, and 14:00
all on 80m CW, only the first QSO would be imported.

Now matches DCL sync logic which correctly includes timeOn:
- userId, callsign, qsoDate, timeOn, band, mode

This ensures all unique QSOs are properly imported from LoTW.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-18 07:49:39 +01:00
9e73704220 docs: update CLAUDE.md with DLD award variants documentation
- Added filter support to DOK award type description
- Added new section "Creating DLD Award Variants" with examples
- Documented available filter operators and fields
- Examples include DLD 80m, DLD CW, and DLD 80m CW

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-18 07:30:49 +01:00
7f77c3adc9 feat: add filter support for DOK awards
The DOK award type (used for DLD award) now supports filtering by band,
mode, and other QSO fields. This allows creating award variants like:
- DLD on specific bands (80m, 40m, etc.)
- DLD on specific modes (CW, SSB, etc.)
- DLD with combined filters (e.g., 80m + CW)

Changes:
- Modified calculateDOKAwardProgress() to apply filters before processing
- Added example awards: dld-80m, dld-40m, dld-cw, dld-80m-cw
- Filter system uses existing applyFilters() function

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-18 07:30:15 +01:00
720144627e fix: return proper HTML for SPA routes instead of Bun error page
When accessing client-side routes (like /qsos) via curl or non-JS clients,
the server attempted to open them as static files, causing Bun to throw
an unhandled ENOENT error that showed an ugly error page.

Now checks if a path has a file extension before attempting to serve it.
Paths without extensions are immediately served index.html for SPA routing.
Also improves the 503 error page with user-friendly HTML when frontend build
is missing.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-18 07:17:28 +01:00
223461f536 fix: enable debug logging and improve DCL sync observability
- Fix logger bug where debug level (0) was treated as falsy
  - Change `||` to `??` in config.js to properly handle log level 0
  - Debug logs now work correctly when LOG_LEVEL=debug

- Add server startup logging
  - Log port, environment, and log level on server start
  - Helps verify configuration is loaded correctly

- Add DCL API request debug logging
  - Log full API request parameters when LOG_LEVEL=debug
  - API key is redacted (shows first/last 4 chars only)
  - Helps troubleshoot DCL sync issues

- Update CLAUDE.md documentation
  - Add Logging section with log levels and configuration
  - Document debug logging feature for DCL service
  - Add this fix to Recent Commits section

Note: .env file added locally with LOG_LEVEL=debug (not committed)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-18 07:02:52 +01:00
27d2ef14ef fix: preserve DOK data when DCL doesn't send values
When DCL sync updates a QSO without DOK fields, the previous code would
write empty strings to the database, overwriting any existing DOK data
that was previously imported or manually entered.

Now only updates DOK/grid fields when DCL actually provides non-empty
values, preserving existing data from other sources.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-18 06:35:04 +01:00
e09ab94e63 feat: skip QSOs with unchanged confirmation data
When syncing from LoTW/DCL, only update QSOs if confirmation data
has changed. This avoids unnecessary database updates and makes it
clear which QSOs actually changed.

- LoTW: Checks if lotwQslRstatus or lotwQslRdate changed
- DCL: Checks if dclQslRstatus, dclQslRdate, DOK, or grid changed
- Frontend: Shows skipped count in sync summary

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-17 19:49:56 +01:00
3592dbb4fb feat: add import log showing synced QSOs
Add import log display that shows QSOs imported via LoTW/DCL sync.
Backend now tracks added/updated QSOs (callsign, date, band, mode)
and returns them in sync result. Frontend displays tables showing
new and updated QSOs after sync completes.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-17 19:39:15 +01:00
f7d62ed247 docs: update CLAUDE.md with DCL parser information
- Add ADIF Parser utility section (src/backend/utils/adif-parser.js)
- Add DCL Service section with API integration details
- Update Confirmation Systems section with DCL information
- Add ADIF Format section explaining field format and DCL-specific fields
- Update Recent Commits with DCL parser implementation

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-17 11:50:33 +01:00
8a1a5804ff feat: implement DCL ADIF parser and service integration
- Add shared ADIF parser utility (src/backend/utils/adif-parser.js)
  - parseADIF(): Parse ADIF format into QSO records
  - parseDCLResponse(): Parse DCL's JSON response format
  - normalizeBand() and normalizeMode(): Standardize band/mode names

- Implement DCL service (src/backend/services/dcl.service.js)
  - fetchQSOsFromDCL(): Fetch from DCL API (ready for API availability)
  - parseDCLJSONResponse(): Parse example payload format
  - syncQSOs(): Update existing QSOs with DCL confirmations
  - Support DCL-specific fields: DCL_QSL_RCVD, DCL_QSLRDATE, DARC_DOK, MY_DARC_DOK

- Refactor LoTW service to use shared ADIF parser
  - Remove duplicate parseADIF, normalizeBand, normalizeMode functions
  - Import from shared utility for consistency

- Tested with example DCL payload
  - Successfully parses all 6 QSOs
  - Correctly extracts DCL confirmation data
  - Handles ADIF format with <EOR> delimiters

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-17 11:49:36 +01:00
322ccafcae docs: add DLD (Deutschland Diplom) award documentation
Add comprehensive documentation for the DLD award including:
- Award definition and JSON structure
- Progress calculation logic
- DOK entity type and DARC category
- Example DOKs and confirmation details
- DCL resources and references

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-17 11:24:01 +01:00
c982dcd0fe feat: implement DLD (Deutschland Diplom) award
Add DOK-based award tracking with DCL confirmation. Counts unique
(DOK, band, mode) combinations toward the 100 DOK target.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-17 11:22:01 +01:00
287d1fe972 docs: update documentation for branding and DCL preparation
- Update application name to "Quickawards by DJ7NT"
- Add DCL (DARC Community Logbook) preparation details
- Document new DOK fields (my_darc_dok, darc_dok) for German awards
- Update database schema documentation with DCL confirmation fields
- Add DCL API endpoint to API documentation
- Document multi-service confirmation display feature
- Add awards system documentation
- Update project structure with new services and routes

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-17 10:31:06 +01:00
47738c68a9 feat: prepare database and UI for DCL integration
Add infrastructure for future DARC Community Logbook (DCL) integration:
- Database schema: Add dcl_api_key, my_darc_dok, darc_dok, dcl_qsl_rdate, dcl_qsl_rstatus fields
- Create DCL service stub with placeholder functions for when DCL provides API
- Backend API: Add /api/auth/dcl-credentials endpoint for API key management
- Frontend settings: Add DCL API key input with informational notice about API availability
- QSO table: Add My DOK and DOK columns, update confirmation column for multiple services

Note: DCL download API is not yet available. These changes prepare the application
for future implementation when DCL adds programmatic access.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-17 10:24:43 +01:00
5db7f6b67f feat: rebrand to Quickawards by DJ7NT and fix logout redirect
- Update all page titles and branding from "Ham Radio Awards" to "Quickawards by DJ7NT"
- Simplify footer copyright text
- Redirect users to login page after logout instead of home page

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-17 10:00:34 +01:00
8a4ad9924d feat: add confirmation date and service type to QSO table
Replace Status column with Confirmed column that displays both the QSL service type (LoTW) and confirmation date, making it ready for future QSL services like eQSL and ClubLog.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-17 09:54:52 +01:00
0db6b68f48 refactor: simplify codebase and replace external dependencies with Bun built-ins
Backend changes:
- Merge duplicate award logic (calculatePointsAwardProgress + getPointsAwardEntityBreakdown)
- Simplify LoTW service (merge syncQSOs functions, simplify polling)
- Remove job queue abstraction (hardcode LoTW sync, remove processor registry)
- Consolidate config files (database.js, logger.js, jwt.js → single config.js)
- Replace bcrypt with Bun.password.hash/verify
- Replace Pino logger with console-based logger
- Fix: export syncQSOs and getLastLoTWQSLDate for job queue imports
- Fix: correct database path resolution using new URL()

Frontend changes:
- Simplify auth store (remove localStorage wrappers, reduce from 222→109 lines)
- Consolidate API layer (remove verbose JSDoc, 180→80 lines)
- Add shared UI components (Loading, ErrorDisplay, BackButton)

Dependencies:
- Remove bcrypt (replaced with Bun.password)
- Remove pino and pino-pretty (replaced with console logger)

Total: ~445 lines removed (net), 3 dependencies removed
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-16 18:27:10 +01:00
41ccb5c185 debloat 2026-01-16 17:28:30 +01:00
435828f281 100 for CW 2026-01-16 16:01:34 +01:00
f562158607 Remove Entity-Filter 2026-01-16 15:44:18 +01:00
20c0b8c9b8 Custom port 2026-01-16 14:55:17 +01:00
08e9ebbbb4 feat: Make backend port configurable via PORT environment variable (defaults to 3001)
This allows changing the port without editing code:
2026-01-16 14:28:21 +01:00
4590baa521 docs: Add frontend dependency installation step to deployment guide
When pulling updates from repository, remember to install frontend dependencies:

cd src/frontend && bun install && bun run build
2026-01-16 14:11:12 +01:00
907dc48f1b feat: Single-port deployment with improved error handling and SvelteKit static build
- Frontend now uses @sveltejs/adapter-static for production builds
- Backend serves both API and static files from single port (originally port 3000)
- Removed all throw statements from services to avoid Elysia prototype errors
- Fixed favicon serving and SvelteKit assets path handling
- Added ecosystem.config.js for PM2 process management
- Comprehensive deployment documentation (PM2 + HAProxy)
- Updated README with single-port architecture
- Created start.sh script for easy production start
2026-01-16 13:58:03 +01:00
413a6e9831 margins for table 2026-01-16 10:58:29 +01:00
9b671795d7 Fix WAS award and improve award progress display
- Fix WAS award filter to use entityId (291) instead of entity string
- Fix backend to use normalized rules.target for all award types
- Fix frontend to distinguish point-based from entity-based awards
- Fix "Needed" calculation for entity awards: target - worked
- Remove points display from non-point awards (WAS, DXCC, etc.)
- Add proper target field to all award API responses

Fixes issue where:
- WAS showed 0 states (filter didn't match "UNITED STATES OF AMERICA")
- DXCC CW showed "Needed: 0" (target not extracted from nested rules)
- Entity awards showed "Points: NaN" (incorrectly detected as point-based)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-16 10:41:27 +01:00
436e9c2278 Add award rule captions and configurable point counting modes
- Add caption field to all award definitions with detailed rule explanations
- Rename Special Stations Award to Wavelog Award
- Add configurable countMode for point-based awards:
  - perBandMode: count unique (callsign, band, mode) combinations
  - perStation: count each station once
  - perQso: count every QSO
- Update backend to respect countMode in progress calculation
- Add target field to award API responses
- Fix "Needed" calculation for point-based awards
- Display caption on award detail pages with styled info box

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-16 10:14:41 +01:00