doxa-maps-cdn/app/components/public/CountryDetail.vue
corsacca eae6143be1
Replace country pages with region dashboards (#23)
* replace country pages with region dashboards

- move /countries to /regions, with 301 redirects from the old URLs in
  every locale
- add a region dashboard: a map of the region's countries with pins
  coloured by prayer coverage, headline figures, and prayer, adoption and
  engagement progress against the DOXA goals, plus a per-country table
- roll up progress stats per country and region in the shared summary
  and serve them from /api/regions
- frame region maps with antimeridian-aware bounds
- add Regions to the hamburger menu and translate the new strings in all
  eight locales

* put each region on the regions index in a card with a header band

The region name links to its dashboard with the site's chevron icon, and
the people-group and country counts sit centred beside it on a brand-green
header; the country list is the padded white card body.
2026-09-07 11:49:24 +01:00

69 lines
2.8 KiB
Vue

<script setup lang="ts">
// Per-country page body: a map that highlights the country and plots its
// people groups (plus surrounding ones, muted), alongside the people-group list
// locked to this country. The summary (name, code, count, region, map
// center/zoom) comes from /api/regions via the /regions/[slug] page.
import { buildUupgListTranslations } from '~/utils/uupgListTranslations'
import { regionSlug, type CountrySummary } from '~~/config/countries-meta'
const props = defineProps<{ country: CountrySummary }>()
const { t, locale } = useI18n()
const localePath = useLocalePath()
const translations = computed(() => buildUupgListTranslations(t))
const regionLink = computed(() => localePath(`/regions/${regionSlug(props.country.region.value)}`))
const regionLabel = computed(() => props.country.region.label || t('Other'))
// Map note. Placeholders are substituted manually with %count%/%country%
// tokens rather than vue-i18n's {0}/{1} slots: vue-i18n empties brace slots
// when the key resolves with no args (the English source), and leaves them
// literal when the key is missing (untranslated locales fall back to the
// English source) — neither path interpolates correctly. Non-brace tokens are
// passed through untouched in every locale, so a single manual replace works
// for English now and for Weblate translations later (which keep the tokens).
const mapNote = computed(() =>
t('There are %count% people groups in %country%. Check the map for the people groups on the border of this country as well.')
.replace('%count%', String(props.country.count))
.replace('%country%', props.country.name)
)
</script>
<template>
<div class="container page-content uupgs-page stack stack--3xl">
<div class="stack stack--xs text-center">
<NuxtLink class="light-link" :to="localePath('/regions')">&larr; {{ t('All Regions') }}</NuxtLink>
<h1 class="highlight" data-highlight-last>{{ country.name }}</h1>
<p class="color-brand-lighter">
{{ country.count }} {{ t('People Groups') }} &middot;
<NuxtLink class="light-link" :to="regionLink">{{ regionLabel }}</NuxtLink>
</p>
</div>
<div class="stack stack--sm">
<p class="country-map-note | text-center color-primary font-weight-bold">
{{ mapNote }}
</p>
<ClientOnly>
<CountryMap
:country-codes="[country.code]"
:center="country.center"
:zoom="country.zoom"
:language-code="locale"
:research-url="localePath('/research') + '/'"
class="rounded-xlg"
/>
</ClientOnly>
</div>
<UupgsList
:language-code="locale"
:research-url="localePath('/research') + '/'"
:t="translations"
:lock-country-code="country.code"
hide-see-all-link
/>
</div>
</template>