India's states as a library poster type: <dxm-poster-section type="indiaState"> shows the 36 states and union territories, each with its state poster, prayer packet and cards, through the same engine as Country Posters' India section (stateCards + makeIndiaStateSheet, now in @map/packet/indiaStateSheet.js). A type may load its table before its cards are read. The whole-India card stays out of the type.
This commit is contained in:
parent
9222c7f36b
commit
9fc2a5047b
7 changed files with 91 additions and 11 deletions
|
|
@ -21,6 +21,12 @@ const unknownType = ref(false)
|
|||
async function resolveType(name) {
|
||||
const a = await posterType(name)
|
||||
if (name !== typeName.value) return // the type changed while this one loaded
|
||||
// A type whose cards come from a FETCHED table (indiaState) loads it first; items() then reads it. A failed load
|
||||
// leaves items() empty, so the section says it has none rather than guessing.
|
||||
if (a && typeof a.load === 'function') {
|
||||
try { await a.load({ assetsBase: assetsBase.value }) } catch (_) { /* items() stays empty: the none line */ }
|
||||
if (name !== typeName.value) return
|
||||
}
|
||||
adapter.value = a; unknownType.value = !a
|
||||
}
|
||||
watch(typeName, resolveType, { immediate: true })
|
||||
|
|
|
|||
|
|
@ -64,3 +64,7 @@ if (!registry().loaders.has('country')) {
|
|||
registry().loaders.set('country', () => import('./types/countryPosterType.js'))
|
||||
announce('country')
|
||||
}
|
||||
if (!registry().loaders.has('indiaState')) {
|
||||
registry().loaders.set('indiaState', () => import('./types/indiaStatePosterType.js'))
|
||||
announce('indiaState')
|
||||
}
|
||||
|
|
|
|||
42
library/components/poster/types/indiaStatePosterType.js
Normal file
42
library/components/poster/types/indiaStatePosterType.js
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
/**
|
||||
* The 'indiaState' poster type: one poster per Indian state or union territory, from the shared state table
|
||||
* (library/data/indiaStates.js, served + pinned) and the ONE state-sheet path (@map/packet/indiaStateSheet.js), the
|
||||
* same cards and sheets as Country Posters' India section, so a host embeds them with
|
||||
* <dxm-poster-section type="indiaState" scope="all">
|
||||
* Each card prints its state poster, its prayer list/packet and its card deck. The WHOLE-INDIA card (country-ind)
|
||||
* is not in this type: it stays withheld wherever it appears (countrySheet.js WITHHELD).
|
||||
* The table is fetched, so the type has a load(): <dxm-poster-section> awaits it before reading items().
|
||||
* scope = 'all' (the only one). Order: most people groups first, as on the wall, matching each sheet's "map N of M".
|
||||
*/
|
||||
import { loadIndiaStates } from '@map/data/indiaStates.js'
|
||||
import { makeIndiaStateSheet, stateCards } from '@map/packet/indiaStateSheet.js'
|
||||
import { createPeopleGroupRecords } from '@map/lists/peopleGroupRecords.js'
|
||||
import { tStatic, getRuntimeLocale, detectLocale } from '@map/i18n/index.js'
|
||||
import { countryName } from '@map/utils/countryName.js'
|
||||
import data from '@map/data/country-posters.json'
|
||||
|
||||
const REGION = (data.countries.find((c) => c.iso3 === 'IND') || {}).region || 'Asia' // India's hue, as on the wall
|
||||
let table = null
|
||||
|
||||
export default {
|
||||
async load({ assetsBase }) { if (!table) table = (await loadIndiaStates(assetsBase)).states || [] },
|
||||
scopes: () => ['all'],
|
||||
items(scope, locale) {
|
||||
const cards = stateCards(table, REGION)
|
||||
if (/^en\b/i.test(locale || 'en')) return cards
|
||||
// named in the reader's language from the catalogue; the printed sheet stays English, like a country's
|
||||
return cards.map((st) => ({ ...st, label: tStatic(`countryPosters.stateNames.${st.iso}`, locale) || st.label }))
|
||||
},
|
||||
// the section is India's: its name in the reader's language (a host adds its own heading if it wants one)
|
||||
title() { const l = getRuntimeLocale() || detectLocale() || 'en'; return countryName('IND', l) || 'India' },
|
||||
actions({ assetsBase, apiBase, lang, profileConfig, t }) {
|
||||
const recordsFor = createPeopleGroupRecords({ apiBase, lang, profileConfig })
|
||||
const sheet = makeIndiaStateSheet({ records: recordsFor, assetsBase, states: () => table, region: REGION })
|
||||
return {
|
||||
records: (c) => (c.slugs && c.slugs.length ? recordsFor({ type: 'pg_slugs', values: c.slugs }) : Promise.resolve([])),
|
||||
sheet: (card, format) => sheet(card, format),
|
||||
unsupported: (c) => (c.noGroups ? t('countryPosters.indiaStateNoGroups') : null),
|
||||
unit: 'UPG', itemCountKey: 'countryPosters.indiaItemCount', eventName: 'doxa-country-posters:show-on-map', downloadPrefix: 'doxa-india-state', cardStocks: true,
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
@ -10,7 +10,9 @@
|
|||
* const sheet = makeIndiaStateSheet({ records, assetsBase: () => base, states })
|
||||
* <PosterWall :sheet="sheet" /> // (item, format) -> html, '' when it cannot be drawn
|
||||
*
|
||||
* ONE PATH: the wall's state cards draw through this and nothing else (check:country-sheet-one-path).
|
||||
* ONE PATH: the state cards draw through this and nothing else: Country Posters' wall and the library 'indiaState'
|
||||
* poster type (<dxm-poster-section type="indiaState">) both build their cards with stateCards() and their sheets
|
||||
* with makeIndiaStateSheet() (check:country-sheet-one-path).
|
||||
*/
|
||||
|
||||
// The states that get a poster: those with people groups listed, in the wall's own order (most groups first,
|
||||
|
|
@ -20,6 +22,17 @@ export function statesWithPosters(states) {
|
|||
.slice().sort((a, b) => b.count - a.count || String(a.name).localeCompare(String(b.name), 'en'))
|
||||
}
|
||||
|
||||
// The cards, in the wall's order: the states with people groups (a poster each), then those with none listed
|
||||
// (Nagaland, Mizoram), named with a card that says so rather than silently left out.
|
||||
export function stateCards(states, region = 'Asia') {
|
||||
const all = states || []
|
||||
const withGroups = statesWithPosters(all).map((st) => ({
|
||||
id: st.id, iso: st.iso, label: st.name, count: st.count, sheet: '3x3', region, indiaState: true, slugs: st.slugs,
|
||||
}))
|
||||
const none = all.filter((st) => !st.count).map((st) => ({ id: st.id, iso: st.iso, label: st.name, count: 0, indiaState: true, noGroups: true, slugs: [] }))
|
||||
return [...withGroups, ...none]
|
||||
}
|
||||
|
||||
export function stateSeries(states, state) {
|
||||
const tier = statesWithPosters(states)
|
||||
const index = tier.findIndex((s) => s.id === state?.id) + 1
|
||||
|
|
@ -483,7 +483,7 @@
|
|||
"check:composite-token-url": "a composite embedded BARE (no outer ProfileLoader) must honour a tokenUrl-only config: it reads tokenUrl, forwards it, and asks the token service only when no token is given. The expectation is derived from ProfileLoader, not restated. Headless; in gates:pre.",
|
||||
"check:composites-mount": "every capability-4/5 composite's OWN setup() and mounted hooks run without throwing, in en and ar, headless (no browser): vite loads each composite as its bundle entry defines it (configureApp, and ProfileLoader's provides where the entry mounts ProfileLoader), and @vue/runtime-core mounts it on a no-op renderer. Exists because 08f89f55 shipped two composites calling getRuntimeLocale/detectLocale/RTL_LOCALES with no import: the build was green, setup threw a ReferenceError in every locale, and Big Picture and Simple Research rendered nothing. Proves setup runs; cannot prove the children render.",
|
||||
"check:country-names": "every ISO 3166-1 alpha-3 our data uses must resolve to a localized country name. Guards the JOIN, not Intl: library/data/iso3-to-alpha2.json drifting from library/data/countries.json makes a country render as a bare code, which still looks like a value. Checks en/ar/zh (ar is our only RTL locale; zh makes an untranslated Latin code unmistakable) and asserts BOTH directions of drift. Carries two negative arms — an unassigned code and a malformed one — and FAILS if either resolves, because a coverage count cannot otherwise tell a hit from a lookup that returns its input.",
|
||||
"check:country-sheet-one-path": "a country sheet, and an Indian state or union territory sheet, each draw through ONE path (@map/packet/countrySheet.js; country-posters/src/indiaStateSheet.js), so one place cannot print two posters. Parses (comments cannot count) plugin sources: a direct generateCountrySheet FAILS anywhere, a direct generateSheet FAILS inside country-posters outside the state helper, every makeCountrySheet call must pass `countries`, and the known callers must be found. Named data answers: seriesFor over country-posters.json (India 1 of 1, each 3x3 country N of 173, an absent code no series, exactly one no-sheet country); over the served india-states.json + gb-IND-ADM1.geojson: 36 divisions, the 34 with groups numbered 1..34 each with an outline, the rest named, the shared ADM1 never mutated.",
|
||||
"check:country-sheet-one-path": "a country sheet, and an Indian state or union territory sheet, each draw through ONE path (@map/packet/countrySheet.js; library/packet/indiaStateSheet.js), so one place cannot print two posters. Parses (comments cannot count) plugin sources: a direct generateCountrySheet FAILS anywhere, a direct generateSheet FAILS inside country-posters outside the state helper, every makeCountrySheet call must pass `countries`, and the known callers must be found. Named data answers: seriesFor over country-posters.json (India 1 of 1, each 3x3 country N of 173, an absent code no series, exactly one no-sheet country); over the served india-states.json + gb-IND-ADM1.geojson: 36 divisions, the 34 with groups numbered 1..34 each with an outline, the rest named, the shared ADM1 never mutated.",
|
||||
"check:country-sheet-one-path:self-test": "plants each defect check:country-sheet-one-path guards (a direct generateCountrySheet call, makeCountrySheet without countries, the old guard that skipped India, a position given to an absent code) and requires each to FAIL; a comment naming generateCountrySheet must NOT.",
|
||||
"check:coverage-consistent": "the gospel-resources surfaces must agree: no resource on an ISO RESERVED code (und/mul/mis/zxx), every resourced code NAMED, table reach recounted off the pins, no invented URL, tiers reconcile. Static, sub-second. Self-test: `bun run check:coverage-consistent:poison`, all 12 clauses (A-L) must fail. Clause L is the one that is not about consistency: it fails when the link-verification record goes stale, because 'link checked' is a claim about the present tense. Written after the same drift defect was found by hand three times.",
|
||||
"check:csv-shape": "the export must OPEN as a table: header on line 0, every row matching. Run the `check:csv-shape:self-test` script for the NEGATIVE test (forces the provenance comment back to line 0 IN MEMORY and requires this gate to REJECT it) — the header used to claim 'NEGATIVE-TESTED' with no way to re-run it.",
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ import { isPhonePrint } from '@map/composables/printInPlace.js'
|
|||
import { keyedError } from '@map/i18n/keyedError.js'
|
||||
// India's states and union territories: the shared table (fetched when the wall shows them) and their sheet.
|
||||
import { loadIndiaStates } from '@map/data/indiaStates.js'
|
||||
import { makeIndiaStateSheet, statesWithPosters } from './src/indiaStateSheet.js'
|
||||
import { makeIndiaStateSheet, stateCards } from '@map/packet/indiaStateSheet.js'
|
||||
// The regions catalogue holds the not-yet sentence a host's country gets when no poster exists (all eleven locales);
|
||||
// its keys are English sentences with dots, so it is read by literal key, the way regions-list.vue reads it.
|
||||
import { useRegionsI18n } from '@map/i18n/useRegionsI18n.js'
|
||||
|
|
@ -128,11 +128,7 @@ const INDIA_REGION = (byIso3.get('IND') || {}).region || 'Asia'
|
|||
const stateItems = computed(() => {
|
||||
const all = indiaStates.value
|
||||
if (filtered.value || !all) return []
|
||||
const withGroups = statesWithPosters(all).map((st) => ({
|
||||
id: st.id, iso: st.iso, label: st.name, count: st.count, sheet: '3x3', region: INDIA_REGION, indiaState: true, slugs: st.slugs,
|
||||
}))
|
||||
const none = all.filter((st) => !st.count).map((st) => ({ id: st.id, iso: st.iso, label: st.name, count: 0, indiaState: true, noGroups: true, slugs: [] }))
|
||||
return [...withGroups, ...none]
|
||||
return stateCards(all, INDIA_REGION)
|
||||
})
|
||||
|
||||
const items = computed(() => {
|
||||
|
|
@ -187,7 +183,7 @@ const sheetForRecord = makeCountrySheet({
|
|||
countries: data.countries || [],
|
||||
})
|
||||
const countryOnlySheet = (card, format) => sheetForRecord(byId.get(card.id) || card, format)
|
||||
// A state's sheet through its own ONE path (src/indiaStateSheet.js), in India's hue and the indiaState series.
|
||||
// A state's sheet through its own ONE path (@map/packet/indiaStateSheet.js), in India's hue and the indiaState series.
|
||||
const stateSheet = makeIndiaStateSheet({
|
||||
records: recordsFor, assetsBase: () => assetsBase.value, states: () => indiaStates.value, region: INDIA_REGION,
|
||||
})
|
||||
|
|
|
|||
|
|
@ -31,6 +31,21 @@ const REGIONS_LIST = 'plugin-designs/regions-dashboard/regions-list.vue'
|
|||
const COUNTRY_TYPE = 'library/components/poster/types/countryPosterType.js'
|
||||
// ARM H: the library 'country' type (the element's caller) withholds like the wall: rows reject with the sentence,
|
||||
// a withheld poster's card shows the sentence.
|
||||
// ARM I: the library 'indiaState' type (<dxm-poster-section type="indiaState">, Bless India's states) is the SAME
|
||||
// engine as the wall's India section: both build their cards with stateCards() and their sheets with
|
||||
// makeIndiaStateSheet() from @map/packet/indiaStateSheet.js; the type never reaches the whole-India card or its sheet.
|
||||
const STATE_TYPE = 'library/components/poster/types/indiaStatePosterType.js'
|
||||
export function armI(H, states, typeSrc, wallSrc) {
|
||||
const fails = []
|
||||
if (!/import \{[^}]*\bmakeIndiaStateSheet\b[^}]*\bstateCards\b[^}]*\} from '@map\/packet\/indiaStateSheet\.js'/.test(typeSrc)) fails.push(`${STATE_TYPE}: does not build through @map/packet/indiaStateSheet.js (stateCards + makeIndiaStateSheet): a second engine`)
|
||||
if (/makeCountrySheet|country-ind|'IND'\s*\)\s*\?/.test(typeSrc.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/.*$/gm, '').replace(/c\.iso3 === 'IND'/, ''))) fails.push(`${STATE_TYPE}: reaches the whole-India card or its sheet (it must stay withheld)`)
|
||||
if (!/stateCards\(/.test(wallSrc)) fails.push('country-poster-wall.vue builds its own state cards: it must use stateCards() (one engine with the indiaState type)')
|
||||
const cards = H.stateCards ? H.stateCards(states, 'Asia') : []
|
||||
if (cards.length !== states.length) fails.push(`stateCards() gives ${cards.length} cards for ${states.length} states/UTs`)
|
||||
if (cards.some((c) => c.id === 'country-ind' || !c.indiaState)) fails.push('stateCards() returns a non-state card')
|
||||
return fails
|
||||
}
|
||||
|
||||
export function armH(typeSrc) {
|
||||
const fails = []
|
||||
if (!/makeCountrySheet\(/.test(typeSrc)) fails.push(`${COUNTRY_TYPE}: no longer draws through makeCountrySheet`)
|
||||
|
|
@ -40,7 +55,7 @@ export function armH(typeSrc) {
|
|||
}
|
||||
// India's states and union territories draw through their own ONE path; inside this plugin only that helper may
|
||||
// call the generator directly.
|
||||
const STATE_HELPER = 'plugin-designs/country-posters/src/indiaStateSheet.js'
|
||||
const STATE_HELPER = 'library/packet/indiaStateSheet.js'
|
||||
const STATE_CALLER = 'plugin-designs/country-posters/country-poster-wall.vue'
|
||||
|
||||
function scriptsOf(file, src) {
|
||||
|
|
@ -271,6 +286,7 @@ async function main() {
|
|||
const REG = readFileSync(join(ROOT, 'plugin-designs/regions-dashboard/regions-list.vue'), 'utf8')
|
||||
const MFE = readFileSync(join(ROOT, 'plugin-designs/country-posters/country-posters-mfe.vue'), 'utf8')
|
||||
const TYPE = readFileSync(join(ROOT, COUNTRY_TYPE), 'utf8')
|
||||
const TYPE_STATE = readFileSync(join(ROOT, STATE_TYPE), 'utf8')
|
||||
const regIsCaller = /makeCountrySheet\(/.test(REG)
|
||||
const lists = (wallSrc, regSrc = REG) => (regIsCaller ? { wall: wallSrc, regions: regSrc } : { wall: wallSrc })
|
||||
if (selfTest) {
|
||||
|
|
@ -301,6 +317,9 @@ async function main() {
|
|||
['the India tab shown without the preview flag', async () => armG(CS, lists(files[wall]), MFE.replace('...(import.meta.env && import.meta.env.DEV && globalThis.DXM_PREVIEW_UNRELEASED === true ? [INDIA_PANE] : []),', 'INDIA_PANE,'))],
|
||||
['the wall prints the list of a country whose rows are withheld', async () => armG(CS, lists(files[wall].replace('rowsWithheld(c) ? withheld() : ', '')))],
|
||||
['the library type prints withheld rows', () => armH(TYPE.replace('rowsWithheld(c) ? ', 'false ? '))],
|
||||
['the indiaState type draws a second way', () => armI(H, states, TYPE_STATE.replace("from '@map/packet/indiaStateSheet.js'", "from './myOwnStateSheet.js'"), files[wall])],
|
||||
['the indiaState type reaches the whole-India sheet', () => armI(H, states, TYPE_STATE + "\nimport { makeCountrySheet } from '@map/packet/countrySheet.js'", files[wall])],
|
||||
['the wall builds its own state cards again', () => armI(H, states, TYPE_STATE, files[wall].replace('stateCards(all, INDIA_REGION)', 'all.map((st) => st)'))],
|
||||
['the library type shows a withheld poster', () => armH(TYPE.replace('posterWithheld(c) ||', 'false ||'))],
|
||||
['a helper that mutates the shared ADM1', () => armC({ ...H, stateGeometry: (z, a, n) => { (a.features || []).forEach((f) => { if (f.properties.shapeName === n) f.properties.listId = z.id }); return H.stateGeometry(z, a, n) } }, states, JSON.parse(JSON.stringify(adm1Ind)))],
|
||||
]
|
||||
|
|
@ -321,7 +340,7 @@ async function main() {
|
|||
|
||||
const callers = []
|
||||
const E = armE(countries, states, files['plugin-designs/country-posters/country-poster-wall.vue'], readFileSync(join(ROOT, 'library/components/poster/PosterWall.vue'), 'utf8'), H)
|
||||
const G = [...await armG(CS, lists(files['plugin-designs/country-posters/country-poster-wall.vue']), MFE), ...armH(TYPE)]
|
||||
const G = [...await armG(CS, lists(files['plugin-designs/country-posters/country-poster-wall.vue']), MFE), ...armH(TYPE), ...armI(H, states, TYPE_STATE, files['plugin-designs/country-posters/country-poster-wall.vue'])]
|
||||
const fails = [...armA(files, callers), ...armB(seriesFor, countries, sheetFormatFor), ...armC(H, states, adm1Ind), ...G, ...E.fails, ...armD(states, areas, meta), ...armF(lists(files['plugin-designs/country-posters/country-poster-wall.vue']))]
|
||||
const missing = [...KNOWN_CALLERS, `${STATE_CALLER}#state`].filter((k) => !callers.includes(k))
|
||||
if (missing.length) fails.push(`known callers not found calling makeCountrySheet: ${missing.join(', ')}`)
|
||||
|
|
|
|||
Loading…
Reference in a new issue