Default to using Bun instead of Node.js. - Use `bun ` instead of `node ` or `ts-node ` - Use `bun test` instead of `jest` or `vitest` - Use `bun build ` instead of `webpack` or `esbuild` - Use `bun install` instead of `npm install` or `yarn install` or `pnpm install` - Use `bun run ``` With the following `frontend.tsx`: ```tsx#frontend.tsx import React from "react"; import { createRoot } from "react-dom/client"; // import .css files directly and it works import './index.css'; const root = createRoot(document.body); export default function Frontend() { return

Hello, world!

; } root.render(); ``` Then, run index.ts ```sh bun --hot ./index.ts ``` For more information, read the Bun API docs in `node_modules/bun-types/docs/**.mdx`. ## Project: Quickawards by DJ7NT Quickawards is a amateur radio award tracking application that calculates progress toward various awards based on QSO (contact) data. ### Award System Architecture The award system is JSON-driven and located in `award-definitions/` directory. Each award has: - `id`: Unique identifier (e.g., "dld", "dxcc") - `name`: Display name - `description`: Short description - `caption`: Detailed explanation - `category`: Award category ("dxcc", "darc", etc.) - `rules`: Award calculation logic ### Award Rule Types 1. **`entity`**: Count unique entities (DXCC countries, states, grid squares) - `entityType`: What to count ("dxcc", "state", "grid", "callsign") - `target`: Number required for award - `filters`: Optional filters (band, mode, etc.) - `displayField`: Optional field to display 2. **`dok`**: Count unique DOK (DARC Ortsverband Kennung) combinations - `target`: Number required - `confirmationType`: "dcl" (DARC Community Logbook) - Counts unique (DOK, band, mode) combinations - Only DCL-confirmed QSOs count 3. **`points`**: Point-based awards - `stations`: Array of {callsign, points} - `target`: Points required - `countMode`: "perStation", "perBandMode", or "perQso" 4. **`filtered`**: Filtered version of another award - `baseRule`: The base entity rule - `filters`: Additional filters to apply 5. **`counter`**: Count QSOs or callsigns ### Key Files **Backend Award Service**: `src/backend/services/awards.service.js` - `getAllAwards()`: Returns all available award definitions - `calculateAwardProgress(userId, award, options)`: Main calculation function - `calculateDOKAwardProgress(userId, award, options)`: DOK-specific calculation - `calculatePointsAwardProgress(userId, award, options)`: Point-based calculation - `getAwardEntityBreakdown(userId, awardId)`: Detailed entity breakdown - `getAwardProgressDetails(userId, awardId)`: Progress with details **Database Schema**: `src/backend/db/schema/index.js` - QSO fields include: `darcDok`, `dclQslRstatus`, `dclQslRdate` - DOK fields support DLD award tracking - DCL confirmation fields separate from LoTW **Award Definitions**: `award-definitions/*.json` - Add new awards by creating JSON definition files - Add filename to `loadAwardDefinitions()` file list in awards.service.js **ADIF Parser**: `src/backend/utils/adif-parser.js` - `parseADIF(adifData)`: Parse ADIF format into QSO records - `parseDCLResponse(response)`: Parse DCL's JSON response format `{ "adif": "..." }` - `normalizeBand(band)`: Standardize band names (80m, 40m, etc.) - `normalizeMode(mode)`: Standardize mode names (CW, FT8, SSB, etc.) - Used by both LoTW and DCL services for consistency **DCL Service**: `src/backend/services/dcl.service.js` - `fetchQSOsFromDCL(dclApiKey, sinceDate)`: Fetch from DCL API - `parseDCLJSONResponse(jsonResponse)`: Parse example/test payloads - `syncQSOs(userId, dclApiKey, sinceDate, jobId)`: Sync QSOs to database - `getLastDCLQSLDate(userId)`: Get last QSL date for incremental sync - Ready for when DCL publishes their API ### DLD Award Implementation (COMPLETED) The DLD (Deutschland Diplom) award was recently implemented: **Definition**: `award-definitions/dld.json` ```json { "id": "dld", "name": "DLD", "description": "Deutschland Diplom - Confirm 100 unique DOKs on different bands/modes", "caption": "Contact and confirm stations with 100 unique DOKs (DARC Ortsverband Kennung) on different band/mode combinations.", "category": "darc", "rules": { "type": "dok", "target": 100, "confirmationType": "dcl", "displayField": "darcDok" } } ``` **Implementation Details**: - Function: `calculateDOKAwardProgress()` in `src/backend/services/awards.service.js` (lines 173-268) - Counts unique (DOK, band, mode) combinations - Only DCL-confirmed QSOs count (`dclQslRstatus === 'Y'`) - Each unique DOK on each unique band/mode counts separately - Returns worked, confirmed counts and entity breakdowns **Database Fields Used**: - `darcDok`: DOK identifier (e.g., "F03", "P30", "G20") - `band`: Band (e.g., "80m", "40m", "20m") - `mode`: Mode (e.g., "CW", "SSB", "FT8") - `dclQslRstatus`: DCL confirmation status ('Y' = confirmed) - `dclQslRdate`: DCL confirmation date **Documentation**: See `docs/DOCUMENTATION.md` for complete documentation including DLD award example. ### Adding New Awards To add a new award: 1. Create JSON definition in `award-definitions/` 2. Add filename to `loadAwardDefinitions()` in `src/backend/services/awards.service.js` 3. If new rule type needed, add calculation function 4. Add type handling in `calculateAwardProgress()` switch statement 5. Add type handling in `getAwardEntityBreakdown()` if needed 6. Update documentation in `docs/DOCUMENTATION.md` 7. Test with sample QSO data ### Confirmation Systems - **LoTW (Logbook of The World)**: ARRL's confirmation system - Service: `src/backend/services/lotw.service.js` - Fields: `lotwQslRstatus`, `lotwQslRdate` - Used for DXCC, WAS, VUCC, most awards - ADIF format with `` delimiters - Supports incremental sync by date - **DCL (DARC Community Logbook)**: DARC's confirmation system - Service: `src/backend/services/dcl.service.js` - Fields: `dclQslRstatus`, `dclQslRdate` - DOK fields: `darcDok` (partner's DOK), `myDarcDok` (user's DOK) - Required for DLD award - German amateur radio specific - API in development (parser ready) - Response format: JSON with ADIF string in `adif` field - Supports DOK (DARC Ortsverband Kennung) data ### ADIF Format Both LoTW and DCL return data in ADIF (Amateur Data Interchange Format): - Field format: `value` - Record delimiter: `` (end of record) - Header ends with: `` (end of header) - Example: `DK0MU80m20250621` **DCL-specific fields**: - `DCL_QSL_RCVD`: DCL confirmation status (Y/N/?) - `DCL_QSLRDATE`: DCL confirmation date (YYYYMMDD) - `DARC_DOK`: QSO partner's DOK - `MY_DARC_DOK`: User's own DOK - `STATION_CALLSIGN`: User's callsign ### Recent Commits - `8a1a580`: feat: implement DCL ADIF parser and service integration - Add shared ADIF parser utility (src/backend/utils/adif-parser.js) - Implement DCL service with API integration ready - Refactor LoTW service to use shared parser - Tested with example DCL payload (6 QSOs parsed successfully) - `c982dcd`: feat: implement DLD (Deutschland Diplom) award - `322ccaf`: docs: add DLD (Deutschland Diplom) award documentation