From e1d9c28c514a2c470de6fe335fdbbf72926df4d4 Mon Sep 17 00:00:00 2001 From: itsrubberduck Date: Thu, 21 May 2026 10:30:23 +0200 Subject: [PATCH] Simplify PM frequency panel --- app/pages/pm.vue | 328 +++++++++++++++++++++++++---------------------- 1 file changed, 176 insertions(+), 152 deletions(-) diff --git a/app/pages/pm.vue b/app/pages/pm.vue index f7a224e..7191190 100644 --- a/app/pages/pm.vue +++ b/app/pages/pm.vue @@ -454,62 +454,9 @@
-
-

Radio Setup

- - {{ frequencies.active || '---' }} - -
+

Radio Setup

- -
-

- Presets - tap for active, hold & drag to select -

-
- - - - - - -
-
- - {{ presetLabel(preset) }} · {{ preset.frequency }} - -
-
- -
+
+
- - - Swap frequencies -
@@ -571,94 +516,79 @@
- - - - Frequencies for {{ flightContext.dep || 'departure' }} - - -
- - Loading frequencies from the network… -
-
- No frequencies available. Please try again later. -
-
-
-
-
-
- {{ freq.type }} - {{ freq.label }} -
-
- {{ freq.callsign }} -
-
- {{ freq.source === 'vatsim' ? 'VATSIM' : 'OpenAIP' }} -
-
-
-
{{ freq.frequency }}
-
- - - Active - - - - Standby - -
-
-
-
- - {{ freq.source.toUpperCase() }} - - - ATIS {{ freq.atisCode }} - -
+
+ + Loading frequencies from the network... +
+
+ No frequencies available. Please try again later. +
+
+
+
+
+
+ {{ freq.type }} + {{ freq.label }} +
+
+ {{ freq.callsign }}
- - - +
+ {{ freq.frequency }} +
+
+ + +
+
@@ -1707,6 +1637,61 @@ const frequencySourceLabels = computed(() => { return labels }) +const normalizedFrequencyValue = (value: string | undefined) => + (value || '').trim().replace(/\s+/g, '').replace(',', '.') + +const frequencyDisplayKey = (entry: AirportFrequencyEntry) => + [ + (entry.type || '').trim().toUpperCase(), + normalizedFrequencyValue(entry.frequency), + ].join('|') + +const sourceLabel = (sources: Array<'vatsim' | 'openaip'>) => { + const hasVatsim = sources.includes('vatsim') + const hasOpenAip = sources.includes('openaip') + if (hasVatsim && hasOpenAip) return 'VATSIM + OpenAIP' + if (hasVatsim) return 'VATSIM' + if (hasOpenAip) return 'OpenAIP' + return 'Source' +} + +const displayAirportFrequencies = computed(() => { + const grouped = new Map() + + for (const entry of airportFrequencies.value) { + if (!entry.frequency || entry.frequency === FREQUENCY_PLACEHOLDER) continue + + const key = frequencyDisplayKey(entry) + const existing = grouped.get(key) + + if (!existing) { + grouped.set(key, { + ...entry, + displayKey: key, + sourceList: [entry.source], + sourceLabel: sourceLabel([entry.source]), + }) + continue + } + + if (!existing.sourceList.includes(entry.source)) { + existing.sourceList.push(entry.source) + } + existing.sourceLabel = sourceLabel(existing.sourceList) + existing.callsign ||= entry.callsign + existing.atisCode ||= entry.atisCode + existing.atisText ||= entry.atisText + } + + return [...grouped.values()].sort((a, b) => { + const aRoleIndex = FREQ_ROLE_ORDER.includes(a.type) ? FREQ_ROLE_ORDER.indexOf(a.type) : Number.MAX_SAFE_INTEGER + const bRoleIndex = FREQ_ROLE_ORDER.includes(b.type) ? FREQ_ROLE_ORDER.indexOf(b.type) : Number.MAX_SAFE_INTEGER + const roleDiff = aRoleIndex - bRoleIndex + if (roleDiff !== 0) return roleDiff + return a.frequency.localeCompare(b.frequency) + }) +}) + type PreparedSpeech = { template: string plain: string @@ -1750,6 +1735,12 @@ type AirportFrequencyEntry = { lastUpdated?: string } +type DisplayAirportFrequencyEntry = AirportFrequencyEntry & { + displayKey: string + sourceList: Array<'vatsim' | 'openaip'> + sourceLabel: string +} + type FrequencyVariableUpdate = Partial> const simulationPilotSteps = [ @@ -3336,6 +3327,39 @@ watch(() => activeFrequency.value, (newFreq) => { gap: 2px; } +.frequency-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); + gap: 10px; +} +.frequency-card { + display: flex; + min-height: 118px; + flex-direction: column; + justify-content: space-between; + gap: 12px; + border-radius: 14px; + border: 1px solid rgba(255, 255, 255, 0.1); + background: rgba(0, 0, 0, 0.32); + padding: 12px; +} +.frequency-card-main, +.frequency-card-footer { + display: flex; + align-items: flex-start; + justify-content: space-between; + gap: 12px; +} +.frequency-card-footer { + align-items: center; +} +.frequency-actions { + display: inline-flex; + align-items: center; + gap: 4px; + flex: 0 0 auto; +} + /* Tablet / desktop: use the extra space ----------------------------------- */ @media (min-width: 1024px) { .pm-sidenav {