fix(awards): "no field" leaves no rule running behind the panel

Choosing "no field" cleared the primary field and hid the matching controls —
but the fallback OR rules stayed in the definition and went on scanning their
own fields. An RDA award declared to match nothing still found a district in the
QTH, and the panel no longer showed the rule doing it. Only a manual override on
the same QSO kept the wrong reference out of the log.

So the choice now clears the fallbacks too, which is what its label promises,
and a rule that survives — from an imported definition, a shared template — is
rendered even in "no field" mode. Nothing that still runs may be invisible.

No catalogue award pairs an empty field with OR rules, so nothing shipped
changes behaviour.
This commit is contained in:
2026-08-21 14:35:54 +02:00
parent f45952d7fb
commit c31ab05c80
+14 -3
View File
@@ -574,8 +574,12 @@ export function AwardEditor({ open, onClose, onSaved }: Props) {
{/* NO_FIELD is a sentinel because the stored value is the {/* NO_FIELD is a sentinel because the stored value is the
empty string and a Select cannot carry one — an empty empty string and a Select cannot carry one — an empty
item value means "show the placeholder" to Radix. */} item value means "show the placeholder" to Radix. */}
{/* Picking "no field" clears the fallbacks too: it promises that
nothing is matched automatically, and an OR rule left behind goes
on scanning its own field — worse than a visible one, because the
panel stops showing it. */}
<Select value={cur.field || NO_FIELD} <Select value={cur.field || NO_FIELD}
onValueChange={(v) => patch({ field: v === NO_FIELD ? '' : v })}> onValueChange={(v) => patch(v === NO_FIELD ? { field: '', or_rules: [] } : { field: v })}>
<SelectTrigger className="h-8 text-xs w-56"><SelectValue /></SelectTrigger> <SelectTrigger className="h-8 text-xs w-56"><SelectValue /></SelectTrigger>
<SelectContent className="max-h-72"> <SelectContent className="max-h-72">
<SelectItem value={NO_FIELD}>{t('awed.fieldNone')}</SelectItem> <SelectItem value={NO_FIELD}>{t('awed.fieldNone')}</SelectItem>
@@ -609,10 +613,17 @@ export function AwardEditor({ open, onClose, onSaved }: Props) {
<Field2 label={t('awed.prefix')}><Input className="h-8 font-mono text-xs" value={cur.prefix ?? ''} onChange={(e) => patch({ prefix: e.target.value })} placeholder={t('awed.prefixPlaceholder')} title={t('awed.prefixTitle')} /></Field2> <Field2 label={t('awed.prefix')}><Input className="h-8 font-mono text-xs" value={cur.prefix ?? ''} onChange={(e) => patch({ prefix: e.target.value })} placeholder={t('awed.prefixPlaceholder')} title={t('awed.prefixTitle')} /></Field2>
</div> </div>
</>)}
{/* Fallback searches: tried in order, only while nothing {/* Fallback searches: tried in order, only while nothing
has matched yet — the first that hits wins (short-circuit), has matched yet — the first that hits wins (short-circuit),
so a value already resolved by the primary rule isn't so a value already resolved by the primary rule isn't
re-derived differently by a later one. */} re-derived differently by a later one.
Shown even with no field when rules survive from an
imported definition: a rule that still runs must still
be visible, or the award finds references the panel
says it cannot. */}
{(!noField || (cur.or_rules?.length ?? 0) > 0) && (
<div className="border-t pt-2.5 space-y-2"> <div className="border-t pt-2.5 space-y-2">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<p className="text-[11px] text-muted-foreground">{t('awed.additionalSearches')} <span className="font-semibold">(OR)</span> {t('awed.orAlsoMatch')}</p> <p className="text-[11px] text-muted-foreground">{t('awed.additionalSearches')} <span className="font-semibold">(OR)</span> {t('awed.orAlsoMatch')}</p>
@@ -650,7 +661,7 @@ export function AwardEditor({ open, onClose, onSaved }: Props) {
); );
})} })}
</div> </div>
</>)} )}
</div> </div>
</TabsContent> </TabsContent>