Skip to content

Commit e4769fb

Browse files
Copilotpatrickrb
andauthored
Implement QRZ.com logbook sync with manual and automatic upload functionality (#117)
* Initial plan * Implement core QRZ logbook sync functionality with API endpoints and UI Co-authored-by: patrickrb <[email protected]> * Add QRZ sync UI components and bulk sync functionality Co-authored-by: patrickrb <[email protected]> * Complete QRZ log export implementation with comprehensive documentation Co-authored-by: patrickrb <[email protected]> * qrz sync * Fix build errors: update Next.js 15 route params typing and add missing QRZ sync methods Co-authored-by: patrickrb <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: patrickrb <[email protected]> Co-authored-by: Patrick Burns <[email protected]>
1 parent 82e4f85 commit e4769fb

File tree

26 files changed

+2555
-34
lines changed

26 files changed

+2555
-34
lines changed

QRZ_IMPLEMENTATION.md

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
# QRZ Log Export Feature - Implementation Complete
2+
3+
## Overview
4+
This implementation adds comprehensive QRZ logbook synchronization functionality to the Nextlog amateur radio logging application. Users can now sync their logged QSOs with their QRZ.com logbook both manually and automatically.
5+
6+
## Features Implemented
7+
8+
### ✅ Core Functionality
9+
- **QRZ Logbook API Integration**: Full implementation using QRZ's POST-based logbook API
10+
- **ADIF Format Conversion**: Proper conversion of contacts to ADIF format for QRZ upload
11+
- **Authentication**: Uses existing encrypted QRZ credential storage
12+
- **Auto-sync**: Background sync on contact creation/update when enabled
13+
- **Manual Sync**: Individual contact sync and bulk selection sync
14+
- **Status Tracking**: Comprehensive sync status per contact
15+
16+
### ✅ Database Schema
17+
- Added QRZ sync fields to contacts table:
18+
- `qrz_sync_status`: enum ('not_synced', 'synced', 'error', 'already_exists')
19+
- `qrz_sync_date`: timestamp of last sync attempt
20+
- `qrz_logbook_id`: QRZ logbook record ID if successfully synced
21+
- `qrz_sync_error`: error message for failed syncs
22+
- Added `qrz_auto_sync` setting to users table
23+
24+
### ✅ API Endpoints
25+
- `/api/user/qrz/validate` - Validate QRZ logbook credentials
26+
- `/api/contacts/qrz-sync` - Bulk sync multiple contacts
27+
- `/api/contacts/[id]/qrz-sync` - Sync individual contact
28+
- Updated `/api/user/profile` to include auto-sync setting
29+
30+
### ✅ UI Components
31+
- **QRZSyncIndicator**: Visual status indicator with tooltips and sync buttons
32+
- **Profile Page**: QRZ credential validation and auto-sync toggle
33+
- **Contacts Tables**: Added QRZ sync column to search and dashboard pages
34+
- **Bulk Operations**: Checkbox selection and bulk sync controls
35+
- **Error Handling**: Clear error messages and retry functionality
36+
37+
### ✅ Error Handling & Conflict Resolution
38+
- Handles existing QSOs gracefully (marks as "already_exists")
39+
- Comprehensive error tracking and display
40+
- Retry functionality for failed syncs
41+
- Network error handling
42+
43+
## Setup Instructions
44+
45+
### 1. Database Migration
46+
Apply the QRZ sync schema changes:
47+
```sql
48+
-- Run the migration script
49+
\i scripts/add-qrz-sync-fields.sql
50+
```
51+
52+
### 2. Configure QRZ Credentials
53+
1. Go to Profile page
54+
2. Enter QRZ.com username and password
55+
3. Click "Validate Credentials" to test logbook access
56+
4. Enable "Auto-sync new contacts to QRZ logbook" if desired
57+
58+
### 3. Usage
59+
**Manual Sync:**
60+
- Go to Search Contacts page
61+
- Select contacts using checkboxes
62+
- Click "Sync X to QRZ" button
63+
- Or click individual sync buttons on each contact
64+
65+
**Auto Sync:**
66+
- Enable in Profile settings
67+
- New contacts will automatically sync in background
68+
- Updated contacts will re-sync if core fields change
69+
70+
## Technical Implementation Details
71+
72+
### QRZ Logbook API
73+
The implementation uses QRZ's logbook API (different from XML lookup API):
74+
- Endpoint: `https://logbook.qrz.com/api`
75+
- Authentication: POST with username/password
76+
- Upload: ADIF format via form data
77+
- Response: TEXT format with status codes
78+
79+
### Auto-sync Logic
80+
- Triggers on contact create/update
81+
- Runs in background (non-blocking)
82+
- Only syncs if user has auto-sync enabled
83+
- Resets sync status when contact is modified
84+
85+
### Status Management
86+
- `not_synced`: New contact, not yet synced
87+
- `synced`: Successfully uploaded to QRZ
88+
- `error`: Sync failed (see error message)
89+
- `already_exists`: QSO already in QRZ logbook
90+
91+
### Security
92+
- Leverages existing encrypted credential storage
93+
- Credentials validated before use
94+
- Background operations handle auth failures gracefully
95+
96+
## Files Modified/Created
97+
98+
### New Files
99+
- `src/lib/qrz-auto-sync.ts` - Auto-sync functionality
100+
- `src/components/QRZSyncIndicator.tsx` - UI status component
101+
- `src/components/ui/tooltip.tsx` - Tooltip component
102+
- `src/components/ui/checkbox.tsx` - Checkbox component
103+
- `src/app/api/user/qrz/validate/route.ts` - Credential validation
104+
- `src/app/api/contacts/qrz-sync/route.ts` - Bulk sync API
105+
- `src/app/api/contacts/[id]/qrz-sync/route.ts` - Single sync API
106+
- `scripts/add-qrz-sync-fields.sql` - Database migration
107+
108+
### Modified Files
109+
- `src/lib/qrz.ts` - Extended with logbook API functions
110+
- `src/models/Contact.ts` - Added QRZ sync methods
111+
- `src/models/User.ts` - Added auto-sync setting
112+
- `src/app/api/contacts/route.ts` - Added auto-sync to creation
113+
- `src/app/api/contacts/[id]/route.ts` - Added auto-sync to updates
114+
- `src/app/api/user/profile/route.ts` - Added auto-sync setting
115+
- `src/app/profile/page.tsx` - Added QRZ validation and toggle
116+
- `src/app/search/page.tsx` - Added QRZ sync UI and bulk operations
117+
- `src/app/dashboard/page.tsx` - Added QRZ sync column
118+
119+
## Dependencies Added
120+
- `@radix-ui/react-tooltip` - For status tooltips
121+
- `@radix-ui/react-checkbox` - For bulk selection
122+
123+
## Testing Checklist
124+
125+
### ✅ Build & Lint
126+
- [x] Code passes ESLint validation
127+
- [x] TypeScript compilation successful
128+
- [x] Next.js build completes without errors
129+
130+
### 🔄 Functional Testing (Requires Database)
131+
- [ ] Apply database migration
132+
- [ ] Start application with database
133+
- [ ] Test QRZ credential validation
134+
- [ ] Test manual sync (individual contact)
135+
- [ ] Test manual sync (bulk selection)
136+
- [ ] Test auto-sync on contact creation
137+
- [ ] Test auto-sync on contact update
138+
- [ ] Test error handling with invalid credentials
139+
- [ ] Test conflict resolution with existing QSOs
140+
141+
### 📸 UI Testing
142+
- [ ] Profile page shows QRZ validation and auto-sync toggle
143+
- [ ] Search page shows checkboxes and QRZ sync column
144+
- [ ] Dashboard page shows QRZ sync column
145+
- [ ] Status indicators display correctly
146+
- [ ] Bulk sync controls appear when contacts selected
147+
- [ ] Error messages display properly
148+
149+
## Next Steps for Production
150+
151+
1. **Database Migration**: Apply the schema changes to production database
152+
2. **Rate Limiting**: Consider implementing rate limiting for QRZ API calls
153+
3. **Monitoring**: Add logging for sync operations and failures
154+
4. **User Documentation**: Create help documentation for QRZ sync feature
155+
5. **Testing**: Comprehensive testing with real QRZ accounts and data
156+
157+
## Conclusion
158+
159+
The QRZ log export feature is now fully implemented with minimal changes to the existing codebase. The implementation follows the existing patterns and conventions, provides comprehensive error handling, and offers both manual and automatic sync capabilities as requested in the requirements.

install-database.sql

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
-- Nextlog Database Installation Script
2-
-- Complete schema with all tables including LOTW integration and reference data
2+
-- Complete schema with all tables including LOTW integration, QRZ sync, and reference data
33
-- This script creates all tables, indexes, functions, triggers, and loads reference data
44

5+
-- No enum needed for QRZ sync - we use qrz_qsl_sent/qrz_qsl_rcvd fields like LoTW
6+
57
-- Create users table
68
CREATE TABLE users (
79
id SERIAL PRIMARY KEY,
@@ -12,6 +14,7 @@ CREATE TABLE users (
1214
grid_locator VARCHAR(10),
1315
qrz_username VARCHAR(255),
1416
qrz_password VARCHAR(255),
17+
qrz_auto_sync BOOLEAN DEFAULT FALSE,
1518
role VARCHAR(50) DEFAULT 'user' NOT NULL,
1619
status VARCHAR(50) DEFAULT 'active' NOT NULL,
1720
last_login TIMESTAMP,
@@ -63,6 +66,7 @@ CREATE TABLE stations (
6366
qrz_username VARCHAR(255),
6467
qrz_password VARCHAR(255),
6568
qrz_api_key VARCHAR(255),
69+
qrz_auto_sync BOOLEAN DEFAULT FALSE,
6670
lotw_username VARCHAR(255),
6771
club_callsign VARCHAR(50),
6872

@@ -112,6 +116,12 @@ CREATE TABLE contacts (
112116
lotw_qsl_rcvd VARCHAR(10),
113117
lotw_qsl_sent VARCHAR(10),
114118

119+
-- QRZ QSL tracking (matches LoTW pattern)
120+
qrz_qsl_sent VARCHAR(10),
121+
qrz_qsl_rcvd VARCHAR(10),
122+
qrz_qsl_sent_date DATE,
123+
qrz_qsl_rcvd_date DATE,
124+
115125
-- Additional QSO data
116126
qso_date_off DATE,
117127
time_off TIME,
@@ -230,6 +240,8 @@ CREATE INDEX idx_contacts_datetime ON contacts(datetime DESC);
230240
CREATE INDEX idx_contacts_frequency ON contacts(frequency);
231241
CREATE INDEX idx_contacts_mode ON contacts(mode);
232242
CREATE INDEX idx_contacts_band ON contacts(band);
243+
CREATE INDEX idx_contacts_qrz_qsl_sent ON contacts(qrz_qsl_sent);
244+
CREATE INDEX idx_contacts_qrz_qsl_rcvd ON contacts(qrz_qsl_rcvd);
233245

234246
CREATE INDEX idx_storage_config_type ON storage_config(config_type);
235247
CREATE INDEX idx_storage_config_enabled ON storage_config(is_enabled);

package-lock.json

Lines changed: 66 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"@azure/storage-blob": "^12.27.0",
2020
"@radix-ui/react-alert-dialog": "^1.1.14",
2121
"@radix-ui/react-avatar": "^1.1.10",
22+
"@radix-ui/react-checkbox": "^1.3.2",
2223
"@radix-ui/react-dialog": "^1.1.14",
2324
"@radix-ui/react-dropdown-menu": "^2.1.15",
2425
"@radix-ui/react-label": "^2.1.7",
@@ -28,6 +29,7 @@
2829
"@radix-ui/react-slot": "^1.2.3",
2930
"@radix-ui/react-switch": "^1.2.5",
3031
"@radix-ui/react-tabs": "^1.1.12",
32+
"@radix-ui/react-tooltip": "^1.2.7",
3133
"@types/leaflet": "^1.9.19",
3234
"bcryptjs": "^3.0.2",
3335
"class-variance-authority": "^0.7.1",

scripts/add-qrz-sync-fields.sql

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-- Add QRZ sync status tracking fields to contacts table
2+
-- This migration adds fields to track QRZ logbook sync status
3+
4+
-- Add enum type for QRZ sync status
5+
DO $$ BEGIN
6+
CREATE TYPE qrz_sync_status_enum AS ENUM ('not_synced', 'synced', 'error', 'already_exists');
7+
EXCEPTION
8+
WHEN duplicate_object THEN null;
9+
END $$;
10+
11+
-- Add QRZ sync fields to contacts table
12+
ALTER TABLE contacts
13+
ADD COLUMN IF NOT EXISTS qrz_sync_status qrz_sync_status_enum DEFAULT 'not_synced',
14+
ADD COLUMN IF NOT EXISTS qrz_sync_date TIMESTAMP NULL,
15+
ADD COLUMN IF NOT EXISTS qrz_logbook_id INTEGER NULL,
16+
ADD COLUMN IF NOT EXISTS qrz_sync_error TEXT NULL;
17+
18+
-- Add index for sync status queries
19+
CREATE INDEX IF NOT EXISTS idx_contacts_qrz_sync_status ON contacts(qrz_sync_status);
20+
CREATE INDEX IF NOT EXISTS idx_contacts_qrz_sync_date ON contacts(qrz_sync_date);
21+
22+
-- Add auto-sync setting to users table
23+
ALTER TABLE users
24+
ADD COLUMN IF NOT EXISTS qrz_auto_sync BOOLEAN DEFAULT FALSE;
25+
26+
-- Add auto-sync setting to stations table
27+
ALTER TABLE stations
28+
ADD COLUMN IF NOT EXISTS qrz_auto_sync BOOLEAN DEFAULT FALSE;

0 commit comments

Comments
 (0)