doxa-maps/library/packet
Doxa Maps 51524b7897 A poster series for India's states and union territories; series-isolation sees shared wording and poster text correctly
series.json: a new `indiaState` series, modelled on `country`, so a state poster names itself honestly
("India State Maps", "What is a State Prayer Map?", one sheet for each state and union territory of India,
"which state God has put on their heart"). The country series' text would have called Gujarāt one of the
~170 Country Maps. Approved as the default wording; shown as editable.

check:series-isolation, two blind spots found by adding a third series:
- Shared wording was dropped from only the EARLIER series (sets were mutated while still being compared),
  so the later one kept it as a fingerprint and the earlier failed for carrying it. The shared set is now
  found on the original sets. Self-test: three series in any order; the old loop fails it.
- The contamination scan never read a series' `poster` block, where a country poster once printed the
  UPG-100 side box. It does now. The phrases that NAME country and indiaState are hand-listed, so a
  verbatim copy of one's framing into the other is caught even though symmetric dropping treats it as shared.
  Self-test 9/9, including both directions of a country/state copy.

countrySheet.js: the seriesFor comment no longer says "map 1 of 0" was printed; it was latent (the map-only
packet page prints no series line, and the India poster is gated), as a rendered check showed.
2026-09-30 23:33:34 +00:00
..
card-render.js Card names hyphenate only where a word cannot fit 2026-09-29 21:04:26 +00:00
countrySheet.js A poster series for India's states and union territories; series-isolation sees shared wording and poster text correctly 2026-09-30 23:33:34 +00:00
evangelicalShare.js Each packet's % evangelical page states its own list's share 2026-09-29 02:16:31 +00:00
mapLabels.js Name the map's countries on page 1 in the packet's language 2026-09-29 02:16:33 +00:00
packet-render.js Packet: the lightweight worksheet shows every group's photo and short description too (a photo cell and a two-line description per row, 16 + 18 rows a page); the variant gate asserts both headless, the render gate counts the rows' photos and follows the new pagination 2026-09-30 12:04:20 +00:00
packetSets.js The full packet's sets page gets its data, from every print path 2026-09-28 23:13:47 +00:00
packetStrings.js Serve the packet text at print time instead of bundling all 11 languages 2026-09-29 08:33:44 +00:00
poster-fetch.js Take a poster only if it is one: a host's fallback page is a miss 2026-09-29 05:26:24 +00:00
poster-raster.js library/ keeps the reason and drops the speaker: no comment quotes or cites the Driver 2026-09-29 05:26:25 +00:00
poster-render.integrity.js Packet: point 4's reference names what prints, Mark 5:18–20, in all 11 locales (the Gerasene reading keeps 5:12–20), re-pinned; the passage gets a 700-char ceiling beside its 250 floor, in the render gate and, headless, in the structure gate for every locale 2026-09-30 11:55:51 +00:00
poster-render.js A tiny series prints its exact count, not a rounded ~0 2026-09-30 03:05:49 +00:00
posterSheets.js Posters drawn at the click use the SPARSE layout, like their stored thumbnails 2026-09-30 02:53:15 +00:00
qr.js library/ keeps the reason and drops the speaker: no comment quotes or cites the Driver 2026-09-29 05:26:25 +00:00
README.md Map sheets are generated in the browser at the click, by the same generator the Node script runs 2026-09-29 05:26:27 +00:00
sha384.js Serve the packet text at print time instead of bundling all 11 languages 2026-09-29 08:33:44 +00:00

The print standard — prayer resources for any set of people groups

Any map in this library can print four prayer resources for whatever its reader picked — a UPG-100 list, a country, a region, a selection on the map:

Resource What it is
Poster the set's map sheet, printed on letter paper as a vector page
Card deck one prayer card per people group, in five card stocks
Prayer list the prayer packet: full detail (map, triangle, life calendar, % evangelical, profiles, bounded + centered sets, prayer wheel, prayer points) or lightweight (map, triangle, a worksheet of every group, wheel, points)

Everything is generated at the click, in the reader's language. Nothing a reader prints is stored; only the previews on a poster wall are stored files, because a wall shows many at once.

Where it is production, where it is experimental

Production (always on): the UPG 100 list (its legend exports, selection HUD and poster wall), the Regions dashboard's country posters, and the Country Posters wall.

Experimental (one switch, library/print/experimentalExports.js): any other map whose legend already has an Export menu may offer the same exports — the card deck, the packet — only while experimentalExportsOn(profileConfig) is true. Turn it off per page (experimentalExports: false in profile-config), per visit (?exports=off), per site (window.DOXA_EXPERIMENTAL_EXPORTS = false) or everywhere (EXPERIMENTAL_EXPORTS_DEFAULT); off restores the menu the map had before. The legend shows the Card Deck item only when told to (:export-cards, or provide('legendExportCards', …) for the mobile sheet), so a map that does not handle it never shows a dead button.

Adopting it in a map — three pieces

import { usePosterPrints } from '@map/composables/usePosterPrints.js'
import { createPeopleGroupRecords } from '@map/lists/peopleGroupRecords.js'
import { getApiBaseUrl } from '@map/api/apiBaseUrl.js'

// 1. RECORDS — the people of a set, by selector (one fetch per language, shared by every item)
const recordsFor = createPeopleGroupRecords({ apiBase: () => getApiBaseUrl(), lang: () => locale, profileConfig })

// 2. THE PRINTS — an item is { id, label } plus whatever your records/sheet functions need
const prints = usePosterPrints({
  records: (item) => recordsFor({ type: 'country', values: [item.iso3] }),
  lang: () => locale,
  assetsBase: () => assetsBase,
  sheet: (item, format) => mySheet(item, format),   // 3. optional: the generated map sheet
})

prints.printPacket(item, { variant: 'full' })   // or 'lightweight'
prints.printCardDeck(item)
prints.printPoster(item)

Or hand the same options to <PosterWall> as props (:records, :sheet, item-count-key, …) and the wall renders every card's Poster / Prayer list / Cards buttons itself. A wall that passes no records shows only the poster, rather than buttons that fail.

Selectors (peopleGroupRecords.js)

Selector Picks
{ type: 'country', values: ['TCD'] } the groups of those countries (ISO3)
{ type: 'pg_slugs', values: [...] } exactly those groups — a selection, a custom list
{ type: 'field', field: 'wagf_region', values: ['africa'] } any field's value
anything else your evaluate(selector, records) (the UPG-100 selector language)

An unknown selector selects nothing, never everything.

The map sheet (posterSheets.js)

generateSheet({ zone, pins, zoneFC, format, seriesId, ISO3_NAME, adm0, adm1Ind, adm1Chn, series }) draws a sheet with the same generator the Node script runs (poster-render.js). Formats: 3x4, 3x3 (wall posters), letter (the poster print), packet (the map-only sheet for a packet's page 1). loadBoundaries(assetsBase) fetches the full-resolution boundaries it draws from. generateCountrySheet({ country, records, assetsBase, ISO3_NAME, format, series }) does all of it for one country. A UPG-100 zone: plugin-designs/my-upg-100-list/data/zone-sheet-print.js.

Page 1 of a packet takes the first of: an explicit mapSvg, sheet(item, 'packet'), mapImage(item) (a raster of a live map), a stored sheet under assetsBase. With none, it says the map is missing.

What proves it

Gate Asserts
check:people-group-records a selector picks exactly its set, two-sided
check:poster-browser-equiv a sheet generated in the browser is byte-identical to the Node generator's
check:packet-a1-structure, check:packet-variants both packets' page order and contents
check:map-labels, check:plural-forms, check:i18n-* every word in the reader's language