feat: Complete translation in French
This commit is contained in:
@@ -4,6 +4,7 @@ import { SearchAwardReferences, GetAwardDefs, GetAwardReferenceMeta } from '../.
|
||||
import {
|
||||
Select, SelectTrigger, SelectValue, SelectContent, SelectItem,
|
||||
} from '@/components/ui/select';
|
||||
import { useI18n } from '@/lib/i18n';
|
||||
|
||||
type AwardRef = { code: string; name: string; dxcc: number; group: string; subgrp: string };
|
||||
type AwardDef = { code: string; name: string; field?: string; dxcc_filter?: number[] | null; dynamic?: boolean };
|
||||
@@ -36,6 +37,7 @@ interface Props {
|
||||
}
|
||||
|
||||
export function AwardRefSelector({ dxcc, value, onChange, fieldValues, heightClass = 'h-[210px]' }: Props) {
|
||||
const { t } = useI18n();
|
||||
const [defs, setDefs] = useState<AwardDef[]>([]);
|
||||
const [metas, setMetas] = useState<Record<string, Meta>>({});
|
||||
const [awardCode, setAwardCode] = useState('POTA');
|
||||
@@ -208,9 +210,9 @@ export function AwardRefSelector({ dxcc, value, onChange, fieldValues, heightCla
|
||||
|
||||
{/* Group / Sub from selected ref */}
|
||||
<div className="grid grid-cols-[38px_1fr] items-center gap-x-1.5 gap-y-0.5 text-xs">
|
||||
<span className="text-muted-foreground text-[11px]">Group</span>
|
||||
<span className="text-muted-foreground text-[11px]">{t('awrs.group')}</span>
|
||||
<span className="font-mono truncate text-[11px]">{selectedRef?.group || '—'}</span>
|
||||
<span className="text-muted-foreground text-[11px]">Sub</span>
|
||||
<span className="text-muted-foreground text-[11px]">{t('awrs.sub')}</span>
|
||||
<span className="font-mono truncate text-[11px]">{selectedRef?.subgrp || '—'}</span>
|
||||
</div>
|
||||
|
||||
@@ -225,7 +227,7 @@ export function AwardRefSelector({ dxcc, value, onChange, fieldValues, heightCla
|
||||
</div>
|
||||
) : (
|
||||
<div className="h-6 flex items-center px-2 text-[11px] text-muted-foreground italic border border-dashed border-border rounded">
|
||||
← pick a reference
|
||||
{t('awrs.pickReference')}
|
||||
</div>
|
||||
)}
|
||||
|
||||
@@ -236,10 +238,10 @@ export function AwardRefSelector({ dxcc, value, onChange, fieldValues, heightCla
|
||||
onClick={() => selectedRef && addRef(selectedRef)}
|
||||
className="flex items-center gap-1 h-6 px-2 text-xs rounded border border-border hover:bg-accent disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
<Plus className="size-3" />Add
|
||||
<Plus className="size-3" />{t('awrs.add')}
|
||||
</button>
|
||||
<span className="text-[11px] text-muted-foreground">
|
||||
{dxcc ? `DXCC #${dxcc}` : 'Enter a callsign first'}
|
||||
{dxcc ? `DXCC #${dxcc}` : t('awrs.enterCallsignFirst')}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -248,7 +250,7 @@ export function AwardRefSelector({ dxcc, value, onChange, fieldValues, heightCla
|
||||
{/* Added refs list */}
|
||||
<div className="flex-1 overflow-auto space-y-0.5 min-h-0">
|
||||
{entries.length === 0 ? (
|
||||
<p className="text-[11px] text-muted-foreground italic py-0.5">No references added yet</p>
|
||||
<p className="text-[11px] text-muted-foreground italic py-0.5">{t('awrs.noRefsAdded')}</p>
|
||||
) : (
|
||||
entries.map((entry) => (
|
||||
<div
|
||||
@@ -273,7 +275,7 @@ export function AwardRefSelector({ dxcc, value, onChange, fieldValues, heightCla
|
||||
|
||||
{/* Right panel: reference search */}
|
||||
<div className="w-[172px] shrink-0 flex flex-col gap-1.5 border-l pl-2 min-w-0">
|
||||
<span className="text-xs font-semibold">References</span>
|
||||
<span className="text-xs font-semibold">{t('awrs.references')}</span>
|
||||
{/* Auto-match from the QSO field (e.g. State NB → RAC@NB). */}
|
||||
{autoMatch && (
|
||||
<button
|
||||
@@ -285,17 +287,17 @@ export function AwardRefSelector({ dxcc, value, onChange, fieldValues, heightCla
|
||||
? 'border-emerald-300 bg-emerald-50 text-emerald-800 cursor-default'
|
||||
: 'border-emerald-300 bg-emerald-50/60 text-emerald-800 hover:bg-emerald-100'
|
||||
}`}
|
||||
title={`The ${selField.toUpperCase()} field is ${autoMatch.code} — this award counts it automatically`}
|
||||
title={t('awrs.autoMatchTitle', { field: selField.toUpperCase(), code: autoMatch.code })}
|
||||
>
|
||||
{autoAlreadyAdded ? '✓ ' : '+ '}
|
||||
<span className="font-mono font-semibold">{autoMatch.code}</span>
|
||||
<span className="text-emerald-700"> from {selField}</span>
|
||||
{!autoAlreadyAdded && <span className="block text-[10px] text-emerald-700/80">auto — click to add</span>}
|
||||
<span className="text-emerald-700"> {t('awrs.fromField', { field: selField })}</span>
|
||||
{!autoAlreadyAdded && <span className="block text-[10px] text-emerald-700/80">{t('awrs.autoClickToAdd')}</span>}
|
||||
</button>
|
||||
)}
|
||||
<input
|
||||
className="h-6 w-full rounded border border-input bg-background px-2 text-xs focus:outline-none focus:ring-1 focus:ring-ring"
|
||||
placeholder="Search…"
|
||||
placeholder={t('awrs.search')}
|
||||
value={q}
|
||||
onChange={(e) => setQ(e.target.value)}
|
||||
/>
|
||||
@@ -306,22 +308,22 @@ export function AwardRefSelector({ dxcc, value, onChange, fieldValues, heightCla
|
||||
type="button"
|
||||
onClick={() => { addRef({ code: q.trim().toUpperCase(), name: '' } as AwardRef); setQ(''); }}
|
||||
className="text-left rounded border border-dashed border-primary/50 px-1.5 py-1 text-[11px] text-primary hover:bg-primary/10"
|
||||
title="Add this reference even though it isn't in the list yet (new / unlisted)"
|
||||
title={t('awrs.addUnlistedTitle')}
|
||||
>
|
||||
+ Add <span className="font-mono font-semibold">{q.trim().toUpperCase()}</span>
|
||||
<span className="text-[10px] text-muted-foreground"> (unlisted)</span>
|
||||
{t('awrs.addPrefix')} <span className="font-mono font-semibold">{q.trim().toUpperCase()}</span>
|
||||
<span className="text-[10px] text-muted-foreground"> {t('awrs.unlisted')}</span>
|
||||
</button>
|
||||
)}
|
||||
<div className="flex-1 overflow-auto border rounded-md text-xs min-h-0">
|
||||
{busy && (
|
||||
<div className="flex items-center gap-1.5 px-2 py-1.5 text-muted-foreground">
|
||||
<Loader2 className="size-3 animate-spin" />Searching…
|
||||
<Loader2 className="size-3 animate-spin" />{t('awrs.searching')}
|
||||
</div>
|
||||
)}
|
||||
{/* Too many auto-results → require typed search */}
|
||||
{!busy && q.length < 2 && tooManyAuto && (
|
||||
<div className="px-2 py-1.5 text-[11px] text-muted-foreground leading-snug">
|
||||
Type 2+ chars to search
|
||||
{t('awrs.typeToSearch')}
|
||||
</div>
|
||||
)}
|
||||
{/* Empty short-query state: prompt for a callsign (dynamic lists) or
|
||||
@@ -329,16 +331,16 @@ export function AwardRefSelector({ dxcc, value, onChange, fieldValues, heightCla
|
||||
{!busy && q.length < 2 && !tooManyAuto && autoResults.length === 0 && (
|
||||
<div className="px-2 py-1.5 text-[11px] text-muted-foreground leading-snug">
|
||||
{isDynamic && !dxcc
|
||||
? 'Enter a callsign, or type to search.'
|
||||
: 'No references for this entity.'}
|
||||
? t('awrs.enterCallsignOrSearch')
|
||||
: t('awrs.noRefsForEntity')}
|
||||
</div>
|
||||
)}
|
||||
{/* Typed search, no results */}
|
||||
{!busy && q.length >= 2 && results.length === 0 && (
|
||||
<div className="px-2 py-2 text-[11px] text-muted-foreground leading-snug">
|
||||
No results.
|
||||
{t('awrs.noResults')}
|
||||
<br />
|
||||
<span className="text-[10px]">Download reference lists in the Awards panel → Import data.</span>
|
||||
<span className="text-[10px]">{t('awrs.downloadLists')}</span>
|
||||
</div>
|
||||
)}
|
||||
{results.map((r) => (
|
||||
|
||||
Reference in New Issue
Block a user