PARKED: the country map's own poster surface, opt-in, waiting on on-demand print code

<doxa-country-map> gains the poster, prayer list, card deck and packet that the
regions wall has, for the one country it draws. The surface is opt-in through
profile-config (showPosters, read only as literal true or 'true'), off by
default, behind v-if so an embed that never asks for it emits no markup, and
loaded through retryableAsyncComponent rather than a static import.

NOT LANDABLE YET, and the measurement is why. Built element, default OFF:

  baseline          528,901 B
  with the surface  930,692 B
  delta            +401,791 B

The lazy load does not help. This repo builds each bundle as a single IIFE with
inlineDynamicImports, which flattens an async component exactly as it flattens a
plain import(): PosterWall, packet-render and createPosterGenerator all appear in
the built element with the flag off. my-upg-100-list's lazy detail panel shows
the same pattern, so this is the build shape rather than this wiring.

So a country page that never turns the surface on would still carry ~400 KB of
printing code. This element exists precisely so such a page need not load the
regions dashboard, so that trade is the wrong way round. It waits for the print
subsystem to be served on demand, the same change that took the regions wiring
from +208 KB to +17 KB once the sheet generator moved to a pinned asset.

countrySheet.js moves to library/packet/ HERE rather than in the wiring commit:
its second consumer is this parked surface, so until this lands the file has one
live consumer and stays plugin-local. The wiring lands plugin-only.

Holds either way: records and sheet come from the shared providers, never a copy;
the single poster item resolves from country-posters.json, with an
unsupported-reason row for a code that has none; poster-action is print
throughout; no host is switched on.
This commit is contained in:
driver 2026-09-29 07:48:18 +00:00
parent e4aac1cd65
commit 0bedb889ba
3 changed files with 88 additions and 2 deletions

View file

@ -1,5 +1,9 @@
/**
* country-sheet-print.js — a country's map sheet, GENERATED in the browser at the click.
* countrySheet.js — a country's map sheet, GENERATED in the browser at the click.
*
* SHARED (@map/packet/countrySheet.js). Two consumers: the regions dashboard's country-poster wall
* and the standalone <doxa-country-map> element. It was plugin-local while the regions list was its
* only caller; it moved here when the element became the second, rather than being copied.
*
* The print standard is generate, store only previews: no country sheet is shipped, and none should
* be. MEASURED before this existed: data/posters holds 28 .html sheets and all 28 are ZONES, while

View file

@ -32,6 +32,26 @@ import RegionCountryMap from '@map/components/maps/RegionCountryMap.vue'
import { toNumber, PEOPLE_GROUP_LIST_FIELDS } from '@map/utils/countries-meta.js'
import { peopleGroupPoints, PEOPLE_GROUP_POINT_FIELDS } from '@map/utils/peopleGroupPoints.js'
import { useRegionsI18n } from '@map/i18n/useRegionsI18n.js'
import { retryableAsyncComponent } from '@map/composables/retryableAsyncComponent.js'
import { createPeopleGroupRecords } from '@map/lists/peopleGroupRecords.js'
import { makeCountrySheet } from '@map/packet/countrySheet.js'
import { resolveAssetsBase } from '@map/utils/embedBase.js'
import posterData from '@map/data/country-posters.json'
// ★ THE PRINT SURFACE IS OPT-IN AND LAZY, AND BOTH HALVES MATTER.
//
// OPT-IN, because other sites embed this element. A page that does not set showPosters must render
// exactly what it renders today; the surface is additive, never a change to the existing contract.
//
// LAZY, because PosterWall STATICALLY imports usePosterPrints, which pulls the whole printing
// subsystem. A plain `import PosterWall` would therefore land that weight in every embed including
// the ones that never turn the flag on, which is the opposite of opt-in. An async component keeps
// it out until a page asks for it.
//
// retryableAsyncComponent, not raw defineAsyncComponent: Vue CACHES a rejected loader promise, so
// one dropped request would disable the surface for the life of the page. Same reason
// upg-100-list.vue uses it for its detail panel.
const PosterWall = retryableAsyncComponent(() => import('@map/components/poster/PosterWall.vue'))
const props = defineProps({
profileConfig: { type: String, default: '' },
@ -72,6 +92,43 @@ const researchUrl = computed(() => cfg.value.researchUrl || 'https://doxa.life/r
const prayBaseUrl = computed(() => cfg.value.prayBaseUrl || 'https://pray.doxa.life')
const countryUrl = computed(() => cfg.value.countryUrl || 'https://doxa.life/regions/')
const languageCode = computed(() => cfg.value.languageCode || cfg.value.locale || cfg.value.lang || 'en')
// OFF unless the host says otherwise, and only true for a literal true / 'true': an absent key, a
// typo'd value or a stray string must not switch a public page's UI on by accident.
const showPosters = computed(() => cfg.value.showPosters === true || cfg.value.showPosters === 'true')
// The wall shows THIS element's country, not a region's list, so its single item is the entry for
// the first country code. An unknown code still yields a row, carrying the reason it cannot print,
// exactly as the regions list does for a country with no poster.
const POSTER_BY_ISO3 = new Map((posterData.countries || []).map((c) => [String(c.iso3 || '').toUpperCase(), c]))
const posterItems = computed(() => {
const iso3 = countryCodes.value[0]
if (!iso3) return []
const rec = POSTER_BY_ISO3.get(iso3)
if (rec) return [rec]
return [{
id: `country-${iso3.toLowerCase()}`,
iso3,
label: iso3,
count: 0,
unsupportedReason: t('No poster has been generated for this country yet.'),
}]
})
const instanceId = computed(() => cfg.value.instanceId || 'country-map')
const posterAssetsBase = computed(() => resolveAssetsBase(cfg.value))
// The same shared provider the regions wall uses, not a copy of its fetch: one cached request per
// language, and the same rows feed the sheet, so the printed map and the printed list cannot
// disagree about which groups are in this country.
const countryRecords = createPeopleGroupRecords({
apiBase: () => prayBaseUrl.value,
lang: () => languageCode.value,
profileConfig: injectedCfg,
})
const countrySheet = makeCountrySheet({
records: countryRecords,
assetsBase: () => posterAssetsBase.value,
})
const { t } = useRegionsI18n()
// ★ THE ELEMENT FETCHES ITS OWN PINS. prayBaseUrl is in the contract, and it is the API base the
@ -145,4 +202,29 @@ watch(() => [languageCode.value, countryCodes.value.join(',')], loadPoints)
"Regionsdaten" all say regions. The people-groups sentence is already in the catalogue and
already translated in all eleven, so this costs no new string. -->
<p v-if="loadFailed" class="cm-status" role="status">{{ t('People groups could not be loaded. This is a connection problem, not a filter.') }}</p>
<!-- OPT-IN. v-if, not v-show: an embed that never sets showPosters must not carry this markup at
all, and the async component behind it is never even requested. Turning it on is a change to
a live page, so no host is switched on here. -->
<PosterWall
v-if="showPosters && posterItems.length"
:items="posterItems"
:instance-id="instanceId"
:assets-base="posterAssetsBase"
:heading="''"
:intro="''"
unit="UPG"
:item-noun="t('Country')"
:item-noun-plural="t('Countries')"
item-count-key="regions.countryCount"
:records="(item) => countryRecords({ type: 'country', values: [item.iso3] })"
:sheet="countrySheet"
:card-stocks="true"
:collapsible="false"
:embedded="true"
:section-heads="false"
:columns="1"
:unsupported="(c) => c.unsupportedReason || null"
download-prefix="doxa-country"
poster-action="print"
/>
</template>

View file

@ -68,7 +68,7 @@ import PosterWall from '@map/components/poster/PosterWall.vue'
import RegionSectionCard from '@map/components/regions/RegionSectionCard.vue'
import posterData from '@map/data/country-posters.json'
import { createPeopleGroupRecords } from '@map/lists/peopleGroupRecords.js'
import { makeCountrySheet } from './src/country-sheet-print.js'
import { makeCountrySheet } from '@map/packet/countrySheet.js'
import { resolveAssetsBase } from '@map/utils/embedBase.js'
import { onBeforeUnmount } from 'vue'
import { cyrillicHeading } from '@map/styles/portedGlobals.js'