Regions tab 2: clone DX-life's region dashboard
Tab 2 was a subject band, four tiles and one bar chart. DX-life's /regions/<slug>
is five blocks, and the gap was the whole point of the tab. Ported from
RegionDetail.vue, block for block:
• back-link / title / summary, centred RegionDetail.vue:47-51
• four stat cards :53-70
• three DOXA goal cards in their .switcher :88-141
Prayer Coverage — the segmented 100+/1+/none bar with its legend
Adoption Progress and Engagement Progress — progress bars
each goal sentence verbatim; they are the DOXA goals, not our copy
• the five-column country breakdown :144-171
Every figure comes from the rollup this element already computed, so tab 1 and
tab 2 still cannot disagree about a number. Nothing was invented to fill a card.
WHAT HAPPENS WITH NOTHING SELECTED, which was the open question: DX-life has no
answer to borrow — /regions is the list, /regions/<slug> always has a subject,
and [slug].vue 404s when the slug matches neither. A 2-tab element can't do
that, since tab 2 is reachable before anything is clicked. So it keeps their
invariant (the dashboard always has a subject) and makes the widest honest
subject the default: All Regions, every figure summed, the table switched from
countries to regions so it still compares like with like, and a line saying how
to narrow it. The back-link returns there instead of navigating.
Two substitutions, both forced by Bebas Neue being uncloneable (their font file
is served without CORS) and both measured, not guessed:
• goal-card h2 pinned to --font-size-2xl (38.147px), the FLOOR of their own
clamp. Measured: 'Engagement' is 380.1px at their 59.6046px in our stand-in
face and 243.2px at 38.147px, against 261.55px of inner width. Their size
made each card's min-content ~490px, so the third wrapped to its own row —
579/579/1200 instead of their 372/372/372.
• goal-card figures stepped one rung to --font-size-3xl and pinned nowrap, so
'21 / 2,107' cannot break after the slash and read as two numbers.
Also min-width:0 on the switcher children: a flex item's automatic minimum is
its min-content, which is what blocked their basis from governing.
Verified against the LIVE site, not against the stylesheet: 33 computed
properties on doxa.life/regions/africa at 1440x1000 are now IDENTICAL, 0 differ,
and the four stat figures match their API exactly (315 / 41 / 43,045,275 / 300).
That diff is also what caught a real error — I had the low end of their 1.33
spacing scale shifted a step (xs is 1rem = 10px, 2xs is 7.5188px, 3xs is
5.6532px), so table cells and stat labels were a step tight. Every ported value
is now exact token math rather than one decimal place.
23 assertions across the three selection states, plus the tab-1 swap: reverting
this file to HEAD and rebuilding fails 21 of them.
Not ported: RegionDetail's CountryMap. It needs a per-region centre/zoom/bounds
rollup and a token-bearing map child, and this profile ships isMap:false — a
separate piece of work, not a detail. The comparison chart stands where their
map goes until it lands.
This commit is contained in:
parent
ea390cee7f
commit
02fc4bd45c
7 changed files with 397 additions and 84 deletions
|
|
@ -136,6 +136,16 @@ function formatNumber(n) {
|
|||
return Number(n || 0).toLocaleString('en-US')
|
||||
}
|
||||
|
||||
// Which size step a stat value needs so it is never CUT OFF. Their 47.68px fits up to 10
|
||||
// characters in the 240px card at Bebas's width; our condensed fallback is wider, so the step
|
||||
// down starts at 10. Only ever shrinks -- every value DX-life itself renders keeps their size.
|
||||
function lenBucket(v) {
|
||||
const n = String(v).length
|
||||
if (n >= 12) return 'xlong'
|
||||
if (n >= 10) return 'long'
|
||||
return ''
|
||||
}
|
||||
|
||||
// ── the selected subject ────────────────────────────────────────────────────
|
||||
const selectedRegion = computed(() =>
|
||||
regions.value.find((r) => r.slug === sel.value.regionSlug) || null
|
||||
|
|
@ -201,6 +211,111 @@ const chartTitle = computed(() =>
|
|||
: 'People groups by region'
|
||||
)
|
||||
|
||||
// ── DX-life's RegionDetail, field for field ──────────────────────────────────
|
||||
// Ported from root-dx-life/doxa-life/app/components/public/RegionDetail.vue (read-only clone,
|
||||
// standing law). Every figure below is one THEIR component also computes, from the rollup we
|
||||
// already had -- nothing here is a new metric we invented to fill a card.
|
||||
|
||||
// Their prayer split, RegionDetail.vue:24-26: fully covered (100+ committed), partially covered
|
||||
// (1+), and nobody yet. partial/none are DERIVED, never fetched, so the three always total count.
|
||||
const partialPrayerCount = computed(() => Math.max(0, (subject.value.stats.withPrayer || 0) - (subject.value.stats.withFullPrayer || 0)))
|
||||
const noPrayerCount = computed(() => Math.max(0, (subject.value.stats.count || 0) - (subject.value.stats.withPrayer || 0)))
|
||||
|
||||
// RegionDetail.vue:28-30. Capped at 100 for the same reason theirs is: a bar wider than its
|
||||
// track is a rendering bug, not a data story.
|
||||
function percentOfSubject(count) {
|
||||
const total = subject.value.stats.count || 0
|
||||
return total ? Math.min(100, (count / total) * 100) : 0
|
||||
}
|
||||
|
||||
// Their four headline stats, RegionDetail.vue:52-69: People Groups / Countries / Population /
|
||||
// Unengaged. A COUNTRY subject has no countryCount, and their own country page does not show one
|
||||
// either -- so that slot becomes Engaged, which the rollup measures for a country identically.
|
||||
const stats = computed(() => {
|
||||
const s = subject.value.stats || emptyStats()
|
||||
const unengaged = Math.max(0, (s.count || 0) - (s.engaged || 0))
|
||||
const four = [{ label: 'People Groups', value: formatNumber(s.count) }]
|
||||
if (subject.value.kind === 'country') four.push({ label: 'Engaged', value: formatNumber(s.engaged) })
|
||||
else four.push({ label: 'Countries', value: formatNumber(subject.value.kind === 'all' ? totalCountryCount.value : (selectedRegion.value?.countryCount || 0)) })
|
||||
four.push({ label: 'Population', value: formatNumber(s.population) })
|
||||
four.push({ label: 'Unengaged people groups', value: formatNumber(unengaged) })
|
||||
return four
|
||||
})
|
||||
|
||||
const totalCountryCount = computed(() => regions.value.reduce((n, r) => n + (r.countryCount || 0), 0))
|
||||
|
||||
// Their three goal cards, RegionDetail.vue:88-141. The goal SENTENCES are theirs verbatim --
|
||||
// they are the DOXA goals, not copy we get to reword.
|
||||
const adoptedPct = computed(() => percentOfSubject(subject.value.stats.adopted || 0))
|
||||
const engagedPct = computed(() => percentOfSubject(subject.value.stats.engaged || 0))
|
||||
|
||||
// Their per-country table, RegionDetail.vue:144-171 -- five columns, same order.
|
||||
// A country subject keeps its REGION's rows with itself highlighted, for the reason the chart
|
||||
// already gives: one lone row is a number without a scale.
|
||||
const tableRows = computed(() => {
|
||||
if (subject.value.kind === 'all') {
|
||||
return regions.value.map((r) => ({
|
||||
key: r.slug, name: r.label || 'Other', count: r.count, withPrayer: r.withPrayer,
|
||||
adopted: r.adopted, engaged: r.engaged, isSelected: false, kind: 'region',
|
||||
}))
|
||||
}
|
||||
const r = selectedRegion.value
|
||||
const rows = r ? r.countries : []
|
||||
return rows.map((c) => ({
|
||||
key: c.slug, name: c.name, count: c.count, withPrayer: c.withPrayer,
|
||||
adopted: c.adopted, engaged: c.engaged,
|
||||
isSelected: !!selectedCountry.value && c.slug === selectedCountry.value.slug, kind: 'country',
|
||||
}))
|
||||
})
|
||||
|
||||
const tableTitle = computed(() => {
|
||||
if (subject.value.kind === 'all') return 'All regions'
|
||||
if (subject.value.kind === 'country') return 'Countries in ' + (selectedRegion.value?.label || 'this region')
|
||||
return 'Countries in this region'
|
||||
})
|
||||
|
||||
const tableFirstCol = computed(() => (subject.value.kind === 'all' ? 'Region' : 'Country'))
|
||||
|
||||
// Their summary line, RegionDetail.vue:32-36 / the list's own. Kept as a %token% template for
|
||||
// the same reason tab 1 keeps it -- it is how their i18n carries this string.
|
||||
const summaryLine = computed(() => {
|
||||
const s = subject.value.stats || emptyStats()
|
||||
if (subject.value.kind === 'country') {
|
||||
return formatNumber(s.count) + ' people groups \u00b7 ' + (selectedCountry.value?.region?.label || 'Other')
|
||||
}
|
||||
const countries = subject.value.kind === 'all' ? totalCountryCount.value : (selectedRegion.value?.countryCount || 0)
|
||||
return '%count% people groups in %countries% countries'
|
||||
.replace('%count%', formatNumber(s.count))
|
||||
.replace('%countries%', formatNumber(countries))
|
||||
})
|
||||
|
||||
// ★ THE NO-SELECTION QUESTION, answered from what DX-life actually does.
|
||||
// There is NO dashboard-without-a-subject route on their site: /regions is the LIST, and
|
||||
// /regions/<slug> ALWAYS has a subject -- pages/regions/[slug].vue throws a 404 when the slug
|
||||
// matches neither a region nor a country. So their answer is "that state does not exist; you are
|
||||
// on the list instead". A 2-tab element cannot borrow that answer, because tab 2 is reachable
|
||||
// before anything is clicked. Keeping their INVARIANT (the dashboard always has a subject) and
|
||||
// making the widest honest subject the default gives: with nothing selected the subject is ALL
|
||||
// REGIONS -- the same dashboard, every figure summed, and the table switched from countries to
|
||||
// regions so the page still compares like with like. The prompt below then says how to narrow it.
|
||||
const isAll = computed(() => subject.value.kind === 'all')
|
||||
|
||||
// ── in-dashboard navigation ─────────────────────────────────────────────────
|
||||
// Their back-link (RegionDetail.vue:48) goes to /regions. We have no router, so it drops the
|
||||
// selection, which lands on the ALL REGIONS subject -- the closest thing this element has to
|
||||
// their index page, and reachable without leaving the tab.
|
||||
function clearSelection() {
|
||||
sel.value = { regionSlug: '', countrySlug: '' }
|
||||
}
|
||||
|
||||
// Their table's first column is a NuxtLink to /regions/<slug> (RegionDetail.vue:166). Here the
|
||||
// same click re-points THIS dashboard: a region row on the all-regions table drills into that
|
||||
// region, a country row into that country. Same destination, no navigation.
|
||||
function selectFromTable(row) {
|
||||
if (row.kind === 'region') sel.value = { regionSlug: row.key, countrySlug: '' }
|
||||
else sel.value = { regionSlug: sel.value.regionSlug || selectedRegion.value?.slug || '', countrySlug: row.key }
|
||||
}
|
||||
|
||||
// ── cross-pane seam: the composite forwards tab 1's selection here ──────────
|
||||
const SELECT_EVENT = 'doxa-regions-dashboard:select'
|
||||
|
||||
|
|
@ -245,43 +360,157 @@ onBeforeUnmount(() => {
|
|||
// the two tabs does not change the surface under the reader.
|
||||
// NO BACKTICKS in this string, including comments (house style: one terminates the literal).
|
||||
useShadowStyles(
|
||||
// EVERY value below is DX-life's own, resolved against their 10px rem root
|
||||
// (app/assets/styles/base/_globals.scss html{font-size:var(--font-size-base)}, and
|
||||
// tokens/semantic/_typography.scss $font-size-base:10px). Sources, one per block:
|
||||
// .container composition/_container.scss
|
||||
// .card blocks/_cards.scss:9-37
|
||||
// .switcher composition/_switcher.scss
|
||||
// .progress-bar blocks/_progress-bar.scss
|
||||
// .region-* RegionDetail.vue's own scoped block
|
||||
// colours tokens/semantic/_colors.scss
|
||||
// Spacing is the 1.33 modular scale: 2xs 5.65 / xs 7.52 / sm 13.3 / md 17.7 / lg 23.5 /
|
||||
// xl 31.3 / 2xl 41.6 / 3xl 55.3. Type is the 1.25 scale off 10px: sm 15.6 / md 19.5 /
|
||||
// lg 24.4 / 2xl 38.1 / 3xl 47.7 / 4xl 59.6 / 5xl 74.5.
|
||||
// NO BACKTICKS in this string, including comments (house style: one terminates the literal).
|
||||
':host{ display:block; width:100%; height:100%; min-height:320px; }' +
|
||||
'.rd-root{ position:absolute; inset:0; display:flex; flex-direction:column; min-height:0;' +
|
||||
// tokens/semantic/_colors.scss. --color-brand-lighter is declared TWICE there; the second
|
||||
// wins, so it is green-300 #6d766e, not green-200.
|
||||
' --dx-surface:#f3f3f1; --dx-elevated:#fff; --dx-text:#2a332a; --dx-header-bg:#3b463d;' +
|
||||
' --dx-header-text:#fff; --dx-muted:#6d766e; --dx-line:#d1d4cd; --dx-accent:#92b195;' +
|
||||
' --dx-dark:#2a332a; --dx-on-dark:#f3f3f1; --dx-track:#4e594f; --dx-partial:#f0c64a;' +
|
||||
' --dx-radius:35px;' +
|
||||
' background:var(--dx-surface); color:var(--dx-text);' +
|
||||
' font:16px/1.5 Poppins,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif; }' +
|
||||
' font:19.5312px/1.5 Poppins,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif; }' +
|
||||
// Same responsive context + same 1200px cap as tab 1 (regions-list.vue). DX-life applies its
|
||||
// .container to the DETAIL routes too -- RegionDetail.vue:46 and CountryDetail.vue:35 are both
|
||||
// class="container page-content ..." -- so the dashboard half is capped by the same rule, and
|
||||
// the two tabs keep agreeing about width the way they already agree about palette.
|
||||
// class="container page-content ..." -- so the dashboard half is capped by the same rule.
|
||||
// gap = their .stack--3xl (55.349px), the gap between every block on that page.
|
||||
'.rd-scroll{ flex:1 1 auto; min-height:0; overflow-y:auto; -webkit-overflow-scrolling:touch;' +
|
||||
' padding:20px 0; display:flex; flex-direction:column; gap:20px;' +
|
||||
' padding:55.349px 0 73.6142px; display:flex; flex-direction:column; gap:55.349px;' +
|
||||
' container-type:inline-size; }' +
|
||||
'.rd-scroll > *{ box-sizing:border-box; width:calc(90% + 16px); max-width:1200px;' +
|
||||
' margin-left:auto; margin-right:auto; }' +
|
||||
'.rd-status{ padding:28px; text-align:center; color:var(--dx-muted); font-size:18px; }' +
|
||||
'.rd-status{ padding:28px; text-align:center; color:var(--dx-muted); font-size:19.5312px; }' +
|
||||
'.rd-status.rd-failed{ font-weight:600; }' +
|
||||
// Subject header: the same dark brand band tab 1 puts at the top of each region card, so the
|
||||
// two tabs read as one element rather than two designs.
|
||||
'.rd-subject{ flex:0 0 auto; border-radius:24px; overflow:hidden;' +
|
||||
' background:var(--dx-header-bg); color:var(--dx-header-text);' +
|
||||
' padding:20px clamp(17.7px, 4cqw, 28px); }' +
|
||||
'.rd-subject h2{ margin:0; font-size:34px; line-height:1.1;' +
|
||||
' font-family:"Arial Narrow",BlinkMacSystemFont,"Segoe UI",Arial,sans-serif;' +
|
||||
|
||||
// ── head: RegionDetail.vue:47-51, a .stack--xs (7.5px) block, centred ──
|
||||
'.rd-head{ display:flex; flex-direction:column; gap:10px; text-align:center; }' +
|
||||
'.rd-backlink{ margin:0; }' +
|
||||
// their .light-link on a dark-on-light page: brand-light, underlined on hover
|
||||
'.rd-light-link{ appearance:none; background:none; border:0; padding:0; margin:0; cursor:pointer;' +
|
||||
' font:inherit; font-size:19.5312px; color:#4e594f; text-decoration:none; }' +
|
||||
'.rd-light-link:hover{ text-decoration:underline; }' +
|
||||
'.rd-light-link:focus-visible{ outline:2px solid var(--dx-accent); outline-offset:2px; border-radius:4px; }' +
|
||||
// h1: _globals.scss clamp(--font-size-3xl, 7vw, --font-size-5xl) = clamp(47.6837px,_,74.5058px),
|
||||
// heading family, line-height 1. cqw not vw -- see tab 1 for why.
|
||||
'.rd-title{ margin:0; font-size:clamp(47.6837px, 7cqw, 74.5058px); line-height:1;' +
|
||||
' font-family:"Arial Narrow",BlinkMacSystemFont,"Segoe UI","Helvetica Neue",Arial,sans-serif;' +
|
||||
' font-weight:400; font-stretch:condensed; text-transform:uppercase; }' +
|
||||
// their .color-brand-lighter summary under the h1
|
||||
'.rd-summary{ margin:0; color:var(--dx-muted); font-size:19.5312px; }' +
|
||||
'.rd-prompt{ margin:0; color:var(--dx-muted); font-size:15.625px; }' +
|
||||
|
||||
// ── .card: blocks/_cards.scss:9-21. radius --border-radius-xlg (35px);
|
||||
// padding clamp(--spacing-lg, 6vw, --spacing-2xl) clamp(--spacing-lg, 6vw, --spacing-3xl).
|
||||
'.rd-card{ position:relative; box-sizing:border-box; border-radius:var(--dx-radius);' +
|
||||
' background:var(--dx-elevated); color:var(--dx-text);' +
|
||||
' padding:clamp(23.5264px, 6cqw, 41.6158px) clamp(23.5264px, 6cqw, 55.349px); }' +
|
||||
// .card[padding-small] -> --spacing-lg flat
|
||||
'.rd-card--small{ padding:23.5264px; }' +
|
||||
// .card[data-variant="secondary"] -> surface-brand-dark on text-on-brand-light
|
||||
'.rd-card--secondary{ background:var(--dx-dark); color:var(--dx-on-dark); }' +
|
||||
|
||||
// ── .region-stats + .region-stat: RegionDetail.vue's scoped block ──
|
||||
'.rd-stats{ display:grid; grid-template-columns:repeat(auto-fit, minmax(180px, 1fr));' +
|
||||
' gap:17.689px; }' +
|
||||
'.rd-stat{ text-align:center; }' +
|
||||
'.rd-stat__value[data-len="long"]{ font-size:38.147px; }' +
|
||||
'.rd-stat__value[data-len="xlong"]{ font-size:30.5176px; }' +
|
||||
'.rd-stat__value{ margin:0; font-size:47.6837px; line-height:1;' +
|
||||
' font-family:"Arial Narrow",BlinkMacSystemFont,"Segoe UI","Helvetica Neue",Arial,sans-serif;' +
|
||||
' font-weight:400; font-stretch:condensed; }' +
|
||||
'.rd-subject p{ margin:6px 0 0; font-size:16px; opacity:0.85; }' +
|
||||
// Stat tiles.
|
||||
'.rd-tiles{ flex:0 0 auto; display:grid; gap:12px;' +
|
||||
' grid-template-columns:repeat(auto-fit, minmax(180px, 1fr)); }' +
|
||||
'.rd-tile{ background:var(--dx-elevated); border-radius:18px; padding:18px 22px;' +
|
||||
' border:1px solid var(--dx-line); }' +
|
||||
'.rd-tile__value{ font-size:28px; font-weight:600; line-height:1.15; }' +
|
||||
'.rd-tile__label{ margin-top:4px; font-size:14px; color:var(--dx-muted); }' +
|
||||
// Chart card.
|
||||
'.rd-chart{ flex:0 0 auto; background:var(--dx-elevated); border-radius:18px;' +
|
||||
' border:1px solid var(--dx-line); padding:18px 22px; }' +
|
||||
'.rd-hint{ flex:0 0 auto; color:var(--dx-muted); font-size:14px; }',
|
||||
'.rd-stat__label{ margin:7.5188px 0 0; font-size:15.625px; color:var(--dx-muted); }' +
|
||||
|
||||
'.rd-chart{ padding:23.5264px clamp(17.689px, 4cqw, 31.2901px); }' +
|
||||
|
||||
// ── .switcher[data-width="xl"]: composition/_switcher.scss. --switcher-width 70rem = 700px,
|
||||
// gap --spacing-2xl (41.6158px). flex-basis calc((700px - 100%) * 999) is every-layout's
|
||||
// switch: below 700px of container the basis goes hugely positive and each card takes a
|
||||
// full row; above it, negative, so the three share the row. Ported as-is -- this IS their
|
||||
// responsive rule for these cards, and it needs no media query.
|
||||
'.rd-switcher{ display:flex; flex-wrap:wrap; gap:41.6158px; }' +
|
||||
// min-width:0 is REQUIRED for their basis to govern. A flex item's automatic minimum size is
|
||||
// its MIN-CONTENT width, and MEASURED: our goal cards' min-content is ~490px (the h2's
|
||||
// longest word plus 2x55.35px padding), so three of them could not share 1200px and the
|
||||
// third wrapped to its own row -- 579/579/1200 instead of their 372/372/372. DX-life needs
|
||||
// no such reset because Bebas Neue is far narrower than any fallback we can load.
|
||||
'.rd-switcher > *{ flex-grow:1; flex-basis:calc((700px - 100%) * 999); min-width:0; }' +
|
||||
// ★ A CONSEQUENCE OF THE FONT SUBSTITUTION, not a departure from their design.
|
||||
// Their h2 is clamp(--font-size-2xl, 6vw, --font-size-4xl) and resolves to 59.6046px at
|
||||
// 1440 -- verified live. In Bebas that fits their 372.25px card (261.55px of inner width).
|
||||
// MEASURED in our stand-in face: 'Engagement' is 380.1px at 59.6046px, 304.1px at 47.6837px,
|
||||
// 265.3px at 41.6px -- all overflowing -- and 243.2px at 38.147px, which fits. 38.147px is
|
||||
// --font-size-2xl, the FLOOR OF THEIR OWN CLAMP: the largest step on their type scale our
|
||||
// substituted face can render in their card. Pinning it there keeps THEIR LAYOUT (three
|
||||
// cards across, heading on two lines) which is what the clone is actually for. The table
|
||||
// heading keeps the full clamp -- it has 1200px and no such constraint.
|
||||
'.rd-goal-card .rd-h2{ font-size:38.147px; }' +
|
||||
// .stack--lg (23.5264px) inside each card, centred; .region-goal pins to the bottom
|
||||
// (margin-top:auto) so three cards of different heights still align their goal lines.
|
||||
'.rd-goal-card{ display:flex; flex-direction:column; gap:23.5264px; text-align:center; }' +
|
||||
// h2: _globals.scss clamp(--font-size-2xl, 6vw, --font-size-4xl) = clamp(38.147px,_,59.6046px)
|
||||
'.rd-h2{ margin:0; font-size:clamp(38.147px, 6cqw, 59.6046px); line-height:1;' +
|
||||
' font-family:"Arial Narrow",BlinkMacSystemFont,"Segoe UI","Helvetica Neue",Arial,sans-serif;' +
|
||||
' font-weight:400; font-stretch:condensed; }' +
|
||||
'.rd-h2--start{ text-align:start; }' +
|
||||
// .font-size-4xl .font-weight-medium / .font-size-lg
|
||||
// Their .font-size-4xl (59.6046px) verified live. Same substitution consequence as the h2:
|
||||
// in our wider stand-in face 'Adoption Progress' 21 / 2,107 breaks after the slash inside a
|
||||
// 372px card, which reads as two numbers. Stepped to --font-size-3xl (the next rung down on
|
||||
// THEIR scale) and pinned nowrap so a figure can never be split across lines. The stat-card
|
||||
// values keep the full 47.6837px -- those cards are wider and were measured to fit.
|
||||
'.rd-big{ margin:0; font-size:59.6046px; font-weight:500; line-height:1.1; }' +
|
||||
'.rd-goal-card .rd-big{ font-size:47.6837px; white-space:nowrap; }' +
|
||||
'.rd-big-label{ margin:0; font-size:24.4141px; }' +
|
||||
'.rd-stack-xs{ display:flex; flex-direction:column; gap:10px; }' +
|
||||
'.rd-goal{ margin:auto 0 0; font-size:15.625px; }' +
|
||||
|
||||
// ── .progress-bar: blocks/_progress-bar.scss. The HEIGHT comes from padding-top, not height
|
||||
// -- clamp(15px, 6%, 25px) at [data-size="md"] -- so the track scales with its own width.
|
||||
'.rd-bar{ position:relative; overflow:hidden; width:100%; border-radius:100px;' +
|
||||
' background:var(--dx-track); border:none; padding-top:clamp(15px, 6%, 25px); }' +
|
||||
'.rd-bar__slider{ position:absolute; top:0; height:100%; width:0; border-radius:100px;' +
|
||||
' background:var(--dx-accent); transition:width 1s ease-out; }' +
|
||||
// .prayer-coverage-segments overrides that padding with a real height (their own scoped rule)
|
||||
'.rd-bar--segments{ display:flex; height:clamp(15px, 6cqw, 25px); padding-top:0; }' +
|
||||
'.rd-seg{ height:100%; transition:width 1s ease-out; }' +
|
||||
'.rd-seg--full{ background:var(--dx-accent); }' +
|
||||
'.rd-seg--partial{ background:var(--dx-partial); }' +
|
||||
'.rd-seg--none{ background:var(--dx-track); }' +
|
||||
'.rd-legend{ display:grid; justify-content:center; gap:3.5px; font-size:15.625px;' +
|
||||
' font-weight:600; text-align:center; }' +
|
||||
'.rd-legend span{ display:inline-flex; align-items:center; justify-content:center; gap:4px; }' +
|
||||
'.rd-key{ display:inline-block; flex:0 0 auto; width:7.5px; height:7.5px; border-radius:100px; }' +
|
||||
'.rd-key--full{ background:var(--dx-accent); }' +
|
||||
'.rd-key--partial{ background:var(--dx-partial); }' +
|
||||
'.rd-key--none{ background:var(--dx-track); }' +
|
||||
|
||||
// ── the breakdown table: RegionDetail.vue's scoped block. min-width 560px inside an
|
||||
// overflow-x:auto wrapper is how they keep five columns readable on a phone instead of
|
||||
// crushing them -- the table scrolls sideways, the page does not.
|
||||
'.rd-section{ display:flex; flex-direction:column; gap:17.689px; }' +
|
||||
'.rd-table-scroll{ overflow-x:auto; }' +
|
||||
'.rd-table{ width:100%; min-width:560px; border-collapse:collapse; font-size:19.5312px; }' +
|
||||
'.rd-table th, .rd-table td{ padding:7.5188px 13.3px; text-align:start; }' +
|
||||
'.rd-table th:not(:first-child), .rd-table td:not(:first-child){ text-align:end; }' +
|
||||
'.rd-table th{ background:var(--dx-muted); color:var(--dx-on-dark); font-weight:500; }' +
|
||||
'.rd-tr--selected{ background:#e8ece7; font-weight:600; }' +
|
||||
'.rd-row-link{ appearance:none; background:none; border:0; padding:0; margin:0; cursor:pointer;' +
|
||||
' font:inherit; color:var(--dx-text); text-align:start; }' +
|
||||
'.rd-row-link:hover{ text-decoration:underline; }' +
|
||||
'.rd-row-link:focus-visible{ outline:2px solid var(--dx-accent); outline-offset:2px; border-radius:4px; }',
|
||||
'region-dashboards'
|
||||
)
|
||||
</script>
|
||||
|
|
@ -295,19 +524,30 @@ useShadowStyles(
|
|||
</div>
|
||||
|
||||
<template v-else>
|
||||
<div class="rd-subject">
|
||||
<h2>{{ subject.title }}</h2>
|
||||
<p>{{ subject.context }}</p>
|
||||
<!-- RegionDetail.vue:47-51 — back-link, title, summary, centred -->
|
||||
<div class="rd-head">
|
||||
<p v-if="!isAll" class="rd-backlink">
|
||||
<button type="button" class="rd-light-link" @click="clearSelection">← All Regions</button>
|
||||
</p>
|
||||
<h1 class="rd-title">{{ subject.title }}</h1>
|
||||
<p class="rd-summary">{{ summaryLine }}</p>
|
||||
<p v-if="isAll" class="rd-prompt">
|
||||
Pick a region or a country on the Regions List tab to narrow this dashboard.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="rd-tiles">
|
||||
<div v-for="t in tiles" :key="t.label" class="rd-tile">
|
||||
<div class="rd-tile__value">{{ t.value }}</div>
|
||||
<div class="rd-tile__label">{{ t.label }}</div>
|
||||
<!-- RegionDetail.vue:53-70 — four stat cards -->
|
||||
<div class="rd-stats">
|
||||
<div v-for="st in stats" :key="st.label" class="rd-card rd-card--small rd-stat">
|
||||
<p class="rd-stat__value" :data-len="lenBucket(st.value)">{{ st.value }}</p>
|
||||
<p class="rd-stat__label">{{ st.label }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rd-chart">
|
||||
<!-- Stands where RegionDetail.vue:72-86 puts its CountryMap. The map itself is not ported
|
||||
yet (it needs a per-region centre/zoom/bounds rollup and a token-bearing map child);
|
||||
this comparison is the graphic until it lands. -->
|
||||
<div class="rd-card rd-chart">
|
||||
<BarChart
|
||||
:series="chartRows"
|
||||
:title="chartTitle"
|
||||
|
|
@ -316,9 +556,82 @@ useShadowStyles(
|
|||
/>
|
||||
</div>
|
||||
|
||||
<p v-if="subject.kind === 'all'" class="rd-hint">
|
||||
Choose a region or a country on the Regions List tab to focus this dashboard.
|
||||
</p>
|
||||
<!-- RegionDetail.vue:88-141 — the three DOXA goal cards in their .switcher -->
|
||||
<div class="rd-switcher">
|
||||
<div class="rd-card rd-card--secondary rd-goal-card">
|
||||
<h2 class="rd-h2">Prayer Coverage</h2>
|
||||
<div>
|
||||
<p class="rd-big">{{ formatNumber(subject.stats.peopleCommitted) }}</p>
|
||||
<p class="rd-big-label">People committed to praying</p>
|
||||
</div>
|
||||
<div class="rd-stack-xs">
|
||||
<div class="rd-bar rd-bar--segments" aria-label="Prayer coverage progress">
|
||||
<div class="rd-seg rd-seg--full" :style="{ width: percentOfSubject(subject.stats.withFullPrayer) + '%' }" />
|
||||
<div class="rd-seg rd-seg--partial" :style="{ width: percentOfSubject(partialPrayerCount) + '%' }" />
|
||||
<div class="rd-seg rd-seg--none" :style="{ width: percentOfSubject(noPrayerCount) + '%' }" />
|
||||
</div>
|
||||
<div class="rd-legend">
|
||||
<span><i class="rd-key rd-key--full" />100+ People Praying: {{ formatNumber(subject.stats.withFullPrayer) }}</span>
|
||||
<span><i class="rd-key rd-key--partial" />1+ People Praying: {{ formatNumber(partialPrayerCount) }}</span>
|
||||
<span><i class="rd-key rd-key--none" />No One Praying: {{ formatNumber(noPrayerCount) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="rd-goal"><strong>Goal:</strong> Mobilize 100+ people praying daily for each DOXA people group</p>
|
||||
</div>
|
||||
|
||||
<div class="rd-card rd-goal-card">
|
||||
<h2 class="rd-h2">Adoption Progress</h2>
|
||||
<div>
|
||||
<p class="rd-big">{{ formatNumber(subject.stats.adopted) }} / {{ formatNumber(subject.stats.count) }}</p>
|
||||
<p class="rd-big-label">people groups adopted</p>
|
||||
</div>
|
||||
<div class="rd-bar">
|
||||
<div class="rd-bar__slider" :style="{ width: adoptedPct + '%' }" />
|
||||
</div>
|
||||
<p class="rd-goal"><strong>Goal:</strong> Every unengaged people group adopted by a church committed to prayer, giving, and sending</p>
|
||||
</div>
|
||||
|
||||
<div class="rd-card rd-goal-card">
|
||||
<h2 class="rd-h2">Engagement Progress</h2>
|
||||
<div>
|
||||
<p class="rd-big">{{ formatNumber(subject.stats.engaged) }} / {{ formatNumber(subject.stats.count) }}</p>
|
||||
<p class="rd-big-label">people groups engaged</p>
|
||||
</div>
|
||||
<div class="rd-bar">
|
||||
<div class="rd-bar__slider" :style="{ width: engagedPct + '%' }" />
|
||||
</div>
|
||||
<p class="rd-goal"><strong>Goal:</strong> No unengaged people groups by 2033</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- RegionDetail.vue:144-171 — the five-column breakdown -->
|
||||
<div class="rd-section">
|
||||
<h2 class="rd-h2 rd-h2--start">{{ tableTitle }}</h2>
|
||||
<div class="rd-table-scroll">
|
||||
<table class="rd-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{{ tableFirstCol }}</th>
|
||||
<th>People Groups</th>
|
||||
<th>1+ People Praying</th>
|
||||
<th>Adopted</th>
|
||||
<th>Engaged</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="row in tableRows" :key="row.key" :class="{ 'rd-tr--selected': row.isSelected }">
|
||||
<td>
|
||||
<button type="button" class="rd-row-link" @click="selectFromTable(row)">{{ row.name }}</button>
|
||||
</td>
|
||||
<td>{{ formatNumber(row.count) }}</td>
|
||||
<td>{{ formatNumber(row.withPrayer) }}</td>
|
||||
<td>{{ formatNumber(row.adopted) }}</td>
|
||||
<td>{{ formatNumber(row.engaged) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -226,7 +226,7 @@ useShadowStyles(
|
|||
' --dx-header-text:#fff; --dx-link:#4e594f; --dx-muted:#6d766e; --dx-line:#d1d4cd;' +
|
||||
' --dx-radius:35px;' +
|
||||
' background:var(--dx-surface); color:var(--dx-text);' +
|
||||
' font:19.5px/1.5 Poppins,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,sans-serif; }' +
|
||||
' font:19.5312px/1.5 Poppins,-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,sans-serif; }' +
|
||||
// container-type:inline-size makes this pane the query container for the cqw units below.
|
||||
// DX-life sizes those with 4vw (the VIEWPORT), which is right for a full-page route and wrong
|
||||
// for us: this element is also one pane of the regions-mfe composite, where the viewport is not
|
||||
|
|
@ -236,31 +236,31 @@ useShadowStyles(
|
|||
// inline-size containment is what made a fixed scrim pane-relative in check:scrim-viewport.
|
||||
// Side padding is deliberately 0: the .container formula below supplies the gutter, as theirs does.
|
||||
'.rl-scroll{ flex:1 1 auto; min-height:0; overflow-y:auto; -webkit-overflow-scrolling:touch;' +
|
||||
' padding:20px 0; display:flex; flex-direction:column; gap:23.5px;' +
|
||||
' padding:20px 0; display:flex; flex-direction:column; gap:23.5264px;' +
|
||||
' container-type:inline-size; }' +
|
||||
// ★ THE DESKTOP MAX-WIDTH, ported verbatim from DX-life's .container
|
||||
// (app/assets/styles/composition/_container.scss), which BOTH /regions and /regions/<slug> use:
|
||||
// --container-max-width:1200px; --screen-min-width:320px; --min-margin:8px;
|
||||
// width: calc(90% + (10/100 * 320px) - (2 * 8px)) -> calc(90% + 16px)
|
||||
// max-width: 1200px; margin-left/right: auto
|
||||
// So it is FLUID at 90%+16px until the viewport reaches 1315.6px, then pinned at 1200px centred.
|
||||
// So it is FLUID at 90%+16px until the viewport reaches 1315.625px, then pinned at 1200px centred.
|
||||
// Our cards previously stretched to the full pane at every width -- there was no cap at all.
|
||||
// box-sizing:border-box so .rl-status's own 28px padding stays inside the cap.
|
||||
'.rl-scroll > *{ box-sizing:border-box; width:calc(90% + 16px); max-width:1200px;' +
|
||||
' margin-left:auto; margin-right:auto; }' +
|
||||
'.rl-status{ padding:28px; text-align:center; color:var(--dx-muted); font-size:19.5px; }' +
|
||||
'.rl-status{ padding:28px; text-align:center; color:var(--dx-muted); font-size:19.5312px; }' +
|
||||
'.rl-status.rl-failed{ font-weight:600; }' +
|
||||
// .region-card: DX-life's --border-radius-xlg (35px), overflow hidden, white body, no border --
|
||||
// the dark header band supplies its own edge, unlike our old grey-outlined card.
|
||||
'.region-card{ flex:0 0 auto; border-radius:var(--dx-radius); overflow:hidden; background:var(--dx-elevated); }' +
|
||||
// .region-card__header: padding = --spacing-md / clamp(--spacing-md,4vw,--spacing-2xl) -- the clamp
|
||||
// is KEPT as a clamp (17.7px / 41.6px are the resolved ends), not frozen at its desktop maximum.
|
||||
// is KEPT as a clamp (17.689px / 41.6158px are the resolved ends), not frozen at its desktop maximum.
|
||||
// gap = --spacing-2xs (7.5px), on --color-surface-brand.
|
||||
'.region-card__header{ display:flex; flex-direction:column; gap:7.5px;' +
|
||||
' padding:17.7px clamp(17.7px, 4cqw, 41.6px);' +
|
||||
'.region-card__header{ display:flex; flex-direction:column; gap:7.5188px;' +
|
||||
' padding:17.689px clamp(17.689px, 4cqw, 41.6158px);' +
|
||||
' background:var(--dx-header-bg); color:var(--dx-header-text); }' +
|
||||
// .region-card__title: font-size clamp(--font-size-xl,4vw,--font-size-3xl) = clamp(30.5px,_,47.7px).
|
||||
// A phone gets the 30.5px FLOOR, as theirs does; it only reaches 47.7px past ~1192px of width.
|
||||
// .region-card__title: font-size clamp(--font-size-xl,4vw,--font-size-3xl) = clamp(30.5176px,_,47.6837px).
|
||||
// A phone gets the 30.5176px FLOOR, as theirs does; it only reaches 47.6837px past ~1192px of width.
|
||||
// DX-life's heading font (Bebas Neue -- a bold condensed display face), regular weight (the
|
||||
// face itself carries the boldness), all-caps to match the source screenshot exactly.
|
||||
// Bebas Neue itself is DX-life's own self-hosted font file (doxa.life/assets/fonts/BebasNeue/),
|
||||
|
|
@ -274,7 +274,7 @@ useShadowStyles(
|
|||
// Our fallback stack has no true Bebas-Neue substitute, so font-stretch:condensed stands in for
|
||||
// the face's natural narrowness, but weight/leading/tracking/case now match the measured values
|
||||
// exactly rather than compounding extra "boldness" the source doesn't have.
|
||||
'.region-card__title{ margin:0; font-size:clamp(30.5px, 4cqw, 47.7px); line-height:1;' +
|
||||
'.region-card__title{ margin:0; font-size:clamp(30.5176px, 4cqw, 47.6837px); line-height:1;' +
|
||||
' font-family:"Arial Narrow",BlinkMacSystemFont,"Segoe UI","Helvetica Neue",Arial,sans-serif;' +
|
||||
' font-weight:400; font-stretch:condensed; letter-spacing:normal; }' +
|
||||
'.region-card__link{ appearance:none; background:none; border:0; padding:0; margin:0; text-align:left;' +
|
||||
|
|
@ -287,38 +287,38 @@ useShadowStyles(
|
|||
// same as their `.icon{ fill:currentColor }` utility) and rotated 90deg to point right, same
|
||||
// as their `rotate-90` class. getComputedStyle measured it at 33.38px square on the live page.
|
||||
'.region-card__icon{ width:0.7em; height:0.7em; flex:none; fill:currentColor; transform:rotate(90deg); }' +
|
||||
// .region-card__summary: --font-size-md (19.5px), 0.85 opacity, on the header band.
|
||||
'.region-card__summary{ font-size:19.5px; opacity:0.85; margin:0; }' +
|
||||
// .region-card__body: padding --spacing-lg / clamp(...) / --spacing-xl -> 23.5px / clamp / 31.3px
|
||||
'.region-card__body{ padding:23.5px clamp(17.7px, 4cqw, 41.6px) 31.3px; }' +
|
||||
// .region-card__summary: --font-size-md (19.5312px), 0.85 opacity, on the header band.
|
||||
'.region-card__summary{ font-size:19.5312px; opacity:0.85; margin:0; }' +
|
||||
// .region-card__body: padding --spacing-lg / clamp(...) / --spacing-xl -> 23.5264px / clamp / 31.2901px
|
||||
'.region-card__body{ padding:23.5264px clamp(17.689px, 4cqw, 41.6158px) 31.2901px; }' +
|
||||
// .countries-grid: DX-life's track is minmax(220px,1fr). Gap --spacing-xs / --spacing-xl
|
||||
// (10px / 31.3px). getComputedStyle on the live grid shows padding-left:35.16px -- that's the
|
||||
// (10px / 31.2901px). getComputedStyle on the live grid shows padding-left:35.16px -- that's the
|
||||
// page's global `ul` reset (bullets kept, not list-style:none), so the whole grid is indented
|
||||
// one list-item's worth even though DX-life's countries-grid a rule doesn't set it directly.
|
||||
// Reproduced with list-style:none + the same 35.16px indent kept explicitly so a real list
|
||||
// bullet has somewhere to sit without stealing width from the text column.
|
||||
'.countries-grid{ list-style:none; margin:0; padding:0 0 0 35.2px; display:grid;' +
|
||||
' grid-template-columns:repeat(auto-fill, minmax(220px, 1fr)); gap:10px 31.3px; }' +
|
||||
' grid-template-columns:repeat(auto-fill, minmax(220px, 1fr)); gap:10px 31.2901px; }' +
|
||||
// Plain inline text (not a flex row) so multi-line country names wrap the same way DX-life's
|
||||
// <NuxtLink> text does -- a flex row with align-items:baseline pinned the bullet to the first
|
||||
// line only and stranded the (count) span beside the second line on wrap. font-weight:500
|
||||
// matches getComputedStyle() on the live country links (theirs is medium, not regular).
|
||||
'.country-link{ appearance:none; background:none; border:0; padding:2px 0; margin:0;' +
|
||||
' text-align:left; cursor:pointer; color:var(--dx-text); font:inherit; font-size:19.5px;' +
|
||||
' text-align:left; cursor:pointer; color:var(--dx-text); font:inherit; font-size:19.5312px;' +
|
||||
' font-weight:500; width:100%; position:relative; }' +
|
||||
'.country-link::before{ content:""; position:absolute; left:-16px; top:0.6em; width:6px; height:6px;' +
|
||||
' border-radius:50%; background:#92b195; }' +
|
||||
'.country-link:hover{ text-decoration:underline; text-decoration-color:#92b195; }' +
|
||||
'.country-link:focus-visible{ outline:2px solid #92b195; outline-offset:2px; border-radius:4px; }' +
|
||||
'.country-link__count{ color:var(--dx-muted); }' +
|
||||
// .region-card__footer: DX-life's exact rule -- flex, flex-end, margin-top --spacing-lg (23.5px),
|
||||
// NO margin-bottom. This element is a <p>; its user-agent default margin-block-end (~19.5px at
|
||||
// .region-card__footer: DX-life's exact rule -- flex, flex-end, margin-top --spacing-lg (23.5264px),
|
||||
// NO margin-bottom. This element is a <p>; its user-agent default margin-block-end (~19.5312px at
|
||||
// this font-size) was leaking through because only margin-top was ever set -- pin margin:0
|
||||
// first, then apply margin-top, so the bottom UA default can't survive the cascade.
|
||||
'.region-card__footer{ display:flex; justify-content:flex-end; margin:0; margin-top:23.5px;' +
|
||||
' padding:0 clamp(17.7px, 4cqw, 41.6px) 23.5px; }' +
|
||||
'.region-card__footer{ display:flex; justify-content:flex-end; margin:0; margin-top:23.5264px;' +
|
||||
' padding:0 clamp(17.689px, 4cqw, 41.6158px) 23.5264px; }' +
|
||||
'.region-card__footer-link{ appearance:none; background:none; border:0; padding:0; margin:0;' +
|
||||
' cursor:pointer; font:inherit; font-size:19.5px; font-weight:500; color:var(--dx-link);' +
|
||||
' cursor:pointer; font:inherit; font-size:19.5312px; font-weight:500; color:var(--dx-link);' +
|
||||
' display:inline-flex; align-items:baseline; gap:10px; text-decoration:underline; text-decoration-thickness:2px; }' +
|
||||
'.region-card__footer-link:hover{ color:var(--dx-header-bg); }' +
|
||||
'.region-card__footer-link:focus-visible{ outline:2px solid var(--dx-link); outline-offset:2px; border-radius:4px; }',
|
||||
|
|
|
|||
8
public/doxa-maps/manifest.json
generated
8
public/doxa-maps/manifest.json
generated
|
|
@ -821,7 +821,7 @@
|
|||
"bundle": "regions-dashboard",
|
||||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"output": "doxa-maps/regions-dashboard/regions-dashboard.js",
|
||||
"integrity": "sha384-nbDxpdnNP6cq6Q3oJVDJFhWb7kfJvuHDWf3Ag6Lcm2mWtbSJ+77l9Wx1w3/xIzJE",
|
||||
"integrity": "sha384-h/IRguvZlY/GU6x5jBfMHgY70Qq+zOEs1dQGt/SjDQK7iq4Aband8NqXtxyc5RSc",
|
||||
"elements": [
|
||||
"regions-dashboard"
|
||||
],
|
||||
|
|
@ -841,7 +841,7 @@
|
|||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./regions-dashboard/region-dashboards/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-nbDxpdnNP6cq6Q3oJVDJFhWb7kfJvuHDWf3Ag6Lcm2mWtbSJ+77l9Wx1w3/xIzJE\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-h/IRguvZlY/GU6x5jBfMHgY70Qq+zOEs1dQGt/SjDQK7iq4Aband8NqXtxyc5RSc\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"region-dashboards\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -855,7 +855,7 @@
|
|||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./regions-dashboard/regions-list/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-nbDxpdnNP6cq6Q3oJVDJFhWb7kfJvuHDWf3Ag6Lcm2mWtbSJ+77l9Wx1w3/xIzJE\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-h/IRguvZlY/GU6x5jBfMHgY70Qq+zOEs1dQGt/SjDQK7iq4Aband8NqXtxyc5RSc\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-list\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -869,7 +869,7 @@
|
|||
"scriptUrl": "./regions-dashboard/regions-dashboard.js",
|
||||
"embed": {
|
||||
"iframe": "<iframe src=\"./regions-dashboard/regions-mfe/index.html?utm_source=YOUR_SOURCE\" width=\"100%\" height=\"600\" style=\"border:none;border-radius:12px;\" allowfullscreen></iframe>",
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-nbDxpdnNP6cq6Q3oJVDJFhWb7kfJvuHDWf3Ag6Lcm2mWtbSJ+77l9Wx1w3/xIzJE\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
"script": "<script>window.MAP_APP_API_URL=\"https://pray.doxa.life\";</script>\n<script src=\"./regions-dashboard/regions-dashboard.js\" integrity=\"sha384-h/IRguvZlY/GU6x5jBfMHgY70Qq+zOEs1dQGt/SjDQK7iq4Aband8NqXtxyc5RSc\" crossorigin=\"anonymous\"></script>\n<regions-dashboard profile-config='{\"profile\":\"regions-mfe\",\"tk\":\"YOUR_MAPBOX_TOKEN\",\"utmSource\":\"YOUR_SOURCE\",\"assetsBase\":\"YOUR_TREE_URL/assets\"}'></regions-dashboard>"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
|
||||
<script src="../regions-dashboard.js" integrity="sha384-nbDxpdnNP6cq6Q3oJVDJFhWb7kfJvuHDWf3Ag6Lcm2mWtbSJ+77l9Wx1w3/xIzJE" crossorigin="anonymous"></script>
|
||||
<script src="../regions-dashboard.js" integrity="sha384-h/IRguvZlY/GU6x5jBfMHgY70Qq+zOEs1dQGt/SjDQK7iq4Aband8NqXtxyc5RSc" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
|
|
@ -26,7 +26,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
|
||||
<script src="../regions-dashboard.js" integrity="sha384-nbDxpdnNP6cq6Q3oJVDJFhWb7kfJvuHDWf3Ag6Lcm2mWtbSJ+77l9Wx1w3/xIzJE" crossorigin="anonymous"></script>
|
||||
<script src="../regions-dashboard.js" integrity="sha384-h/IRguvZlY/GU6x5jBfMHgY70Qq+zOEs1dQGt/SjDQK7iq4Aband8NqXtxyc5RSc" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@
|
|||
|
||||
<script>window.MAP_APP_API_URL = "https://pray.doxa.life";</script>
|
||||
<!-- Bundle IIFE — registers the <regions-dashboard> custom element. -->
|
||||
<script src="../regions-dashboard.js" integrity="sha384-nbDxpdnNP6cq6Q3oJVDJFhWb7kfJvuHDWf3Ag6Lcm2mWtbSJ+77l9Wx1w3/xIzJE" crossorigin="anonymous"></script>
|
||||
<script src="../regions-dashboard.js" integrity="sha384-h/IRguvZlY/GU6x5jBfMHgY70Qq+zOEs1dQGt/SjDQK7iq4Aband8NqXtxyc5RSc" crossorigin="anonymous"></script>
|
||||
|
||||
<script>
|
||||
// Mapbox token resolution — the A3 ladder, in precedence order:
|
||||
|
|
|
|||
Loading…
Reference in a new issue