Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions src/app/country-intel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,12 @@ export class CountryIntelManager implements AppModule {
const geo = await reverseGeocode(lat, lon);
if (token !== this.briefRequestToken) return;
if (!geo) {
this.ctx.countryBriefPage.hide();
this.ctx.map?.setRenderPaused(false);
if (this.ctx.countryBriefPage.showGeoError) {
this.ctx.countryBriefPage.showGeoError(() => this.openCountryBrief(lat, lon));
} else {
this.ctx.countryBriefPage.hide();
this.ctx.map?.setRenderPaused(false);
}
return;
}

Expand Down Expand Up @@ -343,7 +347,7 @@ export class CountryIntelManager implements AppModule {
const page = this.ctx.countryBriefPage;
if (!page?.isVisible()) return;
const code = page.getCode();
if (!code || code === '__loading__') return;
if (!code || code === '__loading__' || code === '__error__') return;
const name = TIER1_COUNTRIES[code] ?? CountryIntelManager.resolveCountryName(code);
const scores = calculateCII();
let score = scores.find((s) => s.code === code) ?? null;
Expand Down
50 changes: 50 additions & 0 deletions src/components/CountryBriefPage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,56 @@ export class CountryBriefPage implements CountryBriefPanel {
this.overlay.classList.add('active');
}

public showGeoError(onRetry: () => void): void {
this.currentCode = '__error__';
this.overlay.textContent = '';

const page = document.createElement('div');
page.className = 'country-brief-page';

const header = document.createElement('div');
header.className = 'cb-header';
const headerLeft = document.createElement('div');
headerLeft.className = 'cb-header-left';
const flag = document.createElement('span');
flag.className = 'cb-flag';
flag.textContent = '\u26A0\uFE0F';
const title = document.createElement('span');
title.className = 'cb-country-name';
title.textContent = t('countryBrief.geocodeFailed');
headerLeft.append(flag, title);
const headerRight = document.createElement('div');
headerRight.className = 'cb-header-right';
const closeX = document.createElement('button');
closeX.className = 'cb-close';
closeX.setAttribute('aria-label', t('components.newsPanel.close'));
closeX.textContent = '\u00D7';
headerRight.append(closeX);
header.append(headerLeft, headerRight);

const body = document.createElement('div');
body.className = 'cb-body';
const errorWrap = document.createElement('div');
errorWrap.className = 'cb-geo-error';
const actions = document.createElement('div');
actions.className = 'cb-geo-error-actions';
const retryBtn = document.createElement('button');
retryBtn.className = 'cb-geo-retry-btn';
retryBtn.textContent = t('countryBrief.retryBtn');
retryBtn.addEventListener('click', () => onRetry(), { once: true });
const closeBtn = document.createElement('button');
closeBtn.className = 'cb-geo-close-btn';
closeBtn.textContent = t('countryBrief.closeBtn');
closeBtn.addEventListener('click', () => this.hide(), { once: true });
actions.append(retryBtn, closeBtn);
errorWrap.append(actions);
body.append(errorWrap);

page.append(header, body);
this.overlay.append(page);
this.overlay.classList.add('active');
}

public get signal(): AbortSignal {
return this.abortController.signal;
}
Expand Down
1 change: 1 addition & 0 deletions src/components/CountryBriefPanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ export interface CountryBriefPanel {
updateMarkets(markets: PredictionMarket[]): void;
updateStock(data: StockIndexData): void;
updateInfrastructure(code: string): void;
showGeoError?(onRetry: () => void): void;
updateScore?(score: CountryScore | null, signals: CountryBriefSignals): void;
updateSignalDetails?(details: CountryDeepDiveSignalDetails): void;
updateMilitaryActivity?(summary: CountryDeepDiveMilitarySummary): void;
Expand Down
26 changes: 26 additions & 0 deletions src/components/CountryDeepDivePanel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,32 @@ export class CountryDeepDivePanel implements CountryBriefPanel {
this.open();
}

public showGeoError(onRetry: () => void): void {
this.currentCode = '__error__';
this.currentName = null;
this.content.replaceChildren();

const wrapper = this.el('div', 'cdp-geo-error');
wrapper.append(
this.el('div', 'cdp-geo-error-icon', '\u26A0\uFE0F'),
this.el('div', 'cdp-geo-error-msg', t('countryBrief.geocodeFailed')),
);

const actions = this.el('div', 'cdp-geo-error-actions');

const retryBtn = this.el('button', 'cdp-geo-error-retry', t('countryBrief.retryBtn')) as HTMLButtonElement;
retryBtn.type = 'button';
retryBtn.addEventListener('click', () => onRetry(), { once: true });

const closeBtn = this.el('button', 'cdp-geo-error-close', t('countryBrief.closeBtn')) as HTMLButtonElement;
closeBtn.type = 'button';
closeBtn.addEventListener('click', () => this.hide(), { once: true });

actions.append(retryBtn, closeBtn);
wrapper.append(actions);
this.content.append(wrapper);
}

public show(country: string, code: string, score: CountryScore | null, signals: CountryBriefSignals): void {
this.abortController.abort();
this.abortController = new AbortController();
Expand Down
3 changes: 3 additions & 0 deletions src/locales/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"countryBrief": {
"identifying": "جارٍ تحديد الدولة...",
"locating": "جارٍ تحديد المنطقة...",
"geocodeFailed": "تعذّر تحديد دولة في هذا الموقع",
"retryBtn": "إعادة المحاولة",
"closeBtn": "إغلاق",
"limitedCoverage": "تغطية محدودة",
"instabilityIndex": "مؤشر عدم الاستقرار",
"notTracked": "غير مُتتبَّع — {{country}} ليست في قائمة CII من المستوى الأول",
Expand Down
3 changes: 3 additions & 0 deletions src/locales/bg.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"countryBrief": {
"identifying": "Идентифициране на държава...",
"locating": "Локализиране на регион...",
"geocodeFailed": "Не може да се определи държава на това местоположение",
"retryBtn": "Опитай отново",
"closeBtn": "Затвори",
"limitedCoverage": "Ограничено покритие",
"instabilityIndex": "Индекс на нестабилност",
"notTracked": "Не се проследява — {{country}} не е в списъка на CII tier-1",
Expand Down
3 changes: 3 additions & 0 deletions src/locales/cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"countryBrief": {
"identifying": "Identifikuji zemi...",
"locating": "Lokalizuji region...",
"geocodeFailed": "Nepodařilo se identifikovat zemi na tomto místě",
"retryBtn": "Zkusit znovu",
"closeBtn": "Zavřít",
"limitedCoverage": "Omezené pokrytí",
"instabilityIndex": "Index nestability",
"notTracked": "Nesledováno — {{country}} není v seznamu CII tier-1",
Expand Down
3 changes: 3 additions & 0 deletions src/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"countryBrief": {
"identifying": "Land wird identifiziert...",
"locating": "Region wird gesucht...",
"geocodeFailed": "Land an diesem Standort konnte nicht identifiziert werden",
"retryBtn": "Erneut versuchen",
"closeBtn": "Schließen",
"limitedCoverage": "Begrenzte Abdeckung",
"instabilityIndex": "Instabilitätsindex",
"notTracked": "Nicht verfolgt – {{country}} ist nicht in der CII-Tier-1-Liste",
Expand Down
3 changes: 3 additions & 0 deletions src/locales/el.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"countryBrief": {
"identifying": "Αναγνώριση χώρας...",
"locating": "Εντοπισμός περιοχής...",
"geocodeFailed": "Δεν ήταν δυνατή η αναγνώριση χώρας σε αυτή τη θέση",
"retryBtn": "Επανάληψη",
"closeBtn": "Κλείσιμο",
"limitedCoverage": "Περιορισμένη κάλυψη",
"instabilityIndex": "Δείκτης Αστάθειας",
"notTracked": "Δεν παρακολουθείται — η {{country}} δεν είναι στη λίστα CII tier-1",
Expand Down
3 changes: 3 additions & 0 deletions src/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"countryBrief": {
"identifying": "Identifying country...",
"locating": "Locating region...",
"geocodeFailed": "Could not identify a country at this location",
"retryBtn": "Retry",
"closeBtn": "Close",
"limitedCoverage": "Limited coverage",
"instabilityIndex": "Instability Index",
"notTracked": "Not tracked — {{country}} is not in the CII tier-1 list",
Expand Down
3 changes: 3 additions & 0 deletions src/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"countryBrief": {
"identifying": "Identificando el país...",
"locating": "Localizando región...",
"geocodeFailed": "No se pudo identificar un país en esta ubicación",
"retryBtn": "Reintentar",
"closeBtn": "Cerrar",
"limitedCoverage": "Cobertura limitada",
"instabilityIndex": "Índice de inestabilidad",
"notTracked": "Sin seguimiento: {{country}} no está en la lista de nivel 1 de CII",
Expand Down
3 changes: 3 additions & 0 deletions src/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"countryBrief": {
"identifying": "Identification du pays...",
"locating": "Localisation de la région...",
"geocodeFailed": "Impossible d'identifier un pays à cet emplacement",
"retryBtn": "Réessayer",
"closeBtn": "Fermer",
"limitedCoverage": "Couverture limitée",
"instabilityIndex": "Indice d'instabilité",
"notTracked": "Non suivi — {{country}} n'est pas dans la liste CII tier-1",
Expand Down
3 changes: 3 additions & 0 deletions src/locales/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"countryBrief": {
"identifying": "Identificazione del paese...",
"locating": "Localizzazione della regione...",
"geocodeFailed": "Impossibile identificare un paese in questa posizione",
"retryBtn": "Riprova",
"closeBtn": "Chiudi",
"limitedCoverage": "Copertura limitata",
"instabilityIndex": "Indice di instabilità",
"notTracked": "Non monitorato — {{country}} non è nella lista CII tier-1",
Expand Down
3 changes: 3 additions & 0 deletions src/locales/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"countryBrief": {
"identifying": "国を特定中...",
"locating": "地域を特定中...",
"geocodeFailed": "この場所の国を特定できませんでした",
"retryBtn": "再試行",
"closeBtn": "閉じる",
"limitedCoverage": "限定的なカバレッジ",
"instabilityIndex": "不安定性指数",
"notTracked": "未追跡 — {{country}}はCII第1階層リストに含まれていない",
Expand Down
3 changes: 3 additions & 0 deletions src/locales/ko.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"countryBrief": {
"identifying": "국가 식별 중...",
"locating": "지역 탐색 중...",
"geocodeFailed": "이 위치의 국가를 식별할 수 없습니다",
"retryBtn": "다시 시도",
"closeBtn": "닫기",
"limitedCoverage": "제한된 커버리지",
"instabilityIndex": "불안정 지수",
"notTracked": "추적 대상 아님 — {{country}}은(는) CII 1등급 목록에 포함되지 않습니다",
Expand Down
3 changes: 3 additions & 0 deletions src/locales/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,9 @@
"countryIntel": {
"identifying": "Land identificeren...",
"locating": "Regio lokaliseren...",
"geocodeFailed": "Kan geen land identificeren op deze locatie",
"retryBtn": "Opnieuw proberen",
"closeBtn": "Sluiten",
"instabilityIndex": "Instabiliteitsindex",
"protests": "protesten",
"militaryAircraft": "miljoen vliegtuigen",
Expand Down
3 changes: 3 additions & 0 deletions src/locales/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"countryBrief": {
"identifying": "Identyfikacja kraju...",
"locating": "Lokalizowanie regionu...",
"geocodeFailed": "Nie udało się zidentyfikować kraju w tej lokalizacji",
"retryBtn": "Ponów",
"closeBtn": "Zamknij",
"limitedCoverage": "Ograniczony zasięg",
"instabilityIndex": "Indeks niestabilności",
"notTracked": "Nie monitorowane — {{country}} nie znajduje się na liście CII poziomu 1",
Expand Down
3 changes: 3 additions & 0 deletions src/locales/pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,9 @@
"countryIntel": {
"identifying": "Identificando país...",
"locating": "Localizando região...",
"geocodeFailed": "Não foi possível identificar um país nesta localização",
"retryBtn": "Tentar novamente",
"closeBtn": "Fechar",
"instabilityIndex": "Índice de Instabilidade",
"protests": "protestos",
"militaryAircraft": "mil. aeronave",
Expand Down
3 changes: 3 additions & 0 deletions src/locales/ro.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"countryBrief": {
"identifying": "Se identifică țara...",
"locating": "Se localizează regiunea...",
"geocodeFailed": "Nu s-a putut identifica o țară la această locație",
"retryBtn": "Reîncearcă",
"closeBtn": "Închide",
"limitedCoverage": "Acoperire limitată",
"instabilityIndex": "Indicele de instabilitate",
"notTracked": "Nu este urmărită — {{country}} nu se află în lista CII de nivel 1",
Expand Down
3 changes: 3 additions & 0 deletions src/locales/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"countryBrief": {
"identifying": "Определение страны...",
"locating": "Определение региона...",
"geocodeFailed": "Не удалось определить страну в этом местоположении",
"retryBtn": "Повторить",
"closeBtn": "Закрыть",
"limitedCoverage": "Ограниченное покрытие",
"instabilityIndex": "Индекс нестабильности",
"notTracked": "Не отслеживается — {{country}} отсутствует в списке CII первого уровня",
Expand Down
3 changes: 3 additions & 0 deletions src/locales/sv.json
Original file line number Diff line number Diff line change
Expand Up @@ -304,6 +304,9 @@
"countryIntel": {
"identifying": "Identifierar land...",
"locating": "Hittar region...",
"geocodeFailed": "Kunde inte identifiera ett land på denna plats",
"retryBtn": "Försök igen",
"closeBtn": "Stäng",
"instabilityIndex": "Instabilitetsindex",
"protests": "protester",
"militaryAircraft": "mil. flygplan",
Expand Down
3 changes: 3 additions & 0 deletions src/locales/th.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"countryBrief": {
"identifying": "กำลังระบุประเทศ...",
"locating": "กำลังค้นหาภูมิภาค...",
"geocodeFailed": "ไม่สามารถระบุประเทศที่ตำแหน่งนี้ได้",
"retryBtn": "ลองอีกครั้ง",
"closeBtn": "ปิด",
"limitedCoverage": "ครอบคลุมจำกัด",
"instabilityIndex": "ดัชนีความไม่มั่นคง",
"notTracked": "ไม่ได้ติดตาม — {{country}} ไม่อยู่ในรายการ CII ระดับ 1",
Expand Down
3 changes: 3 additions & 0 deletions src/locales/tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"countryBrief": {
"identifying": "Ulke belirleniyor...",
"locating": "Bolge tespit ediliyor...",
"geocodeFailed": "Bu konumda bir ülke belirlenemedi",
"retryBtn": "Tekrar dene",
"closeBtn": "Kapat",
"limitedCoverage": "Sinirli kapsam",
"instabilityIndex": "Istikrarsizlik Endeksi",
"notTracked": "Takip edilmiyor — {{country}} CII seviye-1 listesinde degil",
Expand Down
3 changes: 3 additions & 0 deletions src/locales/vi.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"countryBrief": {
"identifying": "Đang xác định quốc gia...",
"locating": "Đang định vị khu vực...",
"geocodeFailed": "Không thể xác định quốc gia tại vị trí này",
"retryBtn": "Thử lại",
"closeBtn": "Đóng",
"limitedCoverage": "Phạm vi theo dõi hạn chế",
"instabilityIndex": "Chỉ số Bất ổn",
"notTracked": "Không theo dõi — {{country}} không nằm trong danh sách CII cấp 1",
Expand Down
3 changes: 3 additions & 0 deletions src/locales/zh.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
"countryBrief": {
"identifying": "正在识别国家...",
"locating": "正在定位区域...",
"geocodeFailed": "无法识别此位置的国家",
"retryBtn": "重试",
"closeBtn": "关闭",
"limitedCoverage": "覆盖范围有限",
"instabilityIndex": "不稳定指数",
"notTracked": "未跟踪 — {{country}}不在CII一级监控名单中",
Expand Down
49 changes: 49 additions & 0 deletions src/styles/country-deep-dive.css
Original file line number Diff line number Diff line change
Expand Up @@ -694,6 +694,55 @@
}
}

.cdp-geo-error {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 12px;
padding: 32px 16px;
text-align: center;
}

.cdp-geo-error-icon {
font-size: 28px;
}

.cdp-geo-error-msg {
color: var(--text-muted);
font-size: 13px;
line-height: 1.4;
}

.cdp-geo-error-actions {
display: flex;
gap: 8px;
margin-top: 4px;
}

.cdp-geo-error-retry,
.cdp-geo-error-close {
padding: 6px 16px;
border: 1px solid var(--border);
border-radius: 6px;
background: transparent;
color: var(--text-primary);
font-size: 12px;
cursor: pointer;
transition: background 0.15s;
}

.cdp-geo-error-retry:hover,
.cdp-geo-error-close:hover {
background: color-mix(in srgb, var(--text-faint) 15%, transparent);
}

.cdp-geo-error-retry {
background: color-mix(in srgb, var(--accent) 15%, transparent);
border-color: var(--accent);
color: var(--accent);
}

.cdp-timeline-mount {
min-height: 80px;
}
Expand Down
Loading