From e49b44f99179bbd8fb336f81979b8c17de5ad22f Mon Sep 17 00:00:00 2001
From: Dave Liu <7david12liu@gmail.com>
Date: Sun, 18 Oct 2026 17:44:52 -0700
Subject: [PATCH 1/7] Embed Zeffy membership form on Join Us for staging review
(#750)
---
.github/lint-baseline.json | 17 ++---
OFFICER_START_HERE.md | 2 +
SYSTEM_DESIGN.md | 2 +
docs/officers/README.md | 2 +
docs/officers/ZEFFY_MEMBERSHIP.md | 56 +++++++++++++++++
src/App.test.jsx | 6 +-
src/components/ZeffyMembershipForm.jsx | 63 +++++++++++++++++++
src/components/ZeffyMembershipForm.test.jsx | 70 +++++++++++++++++++++
src/components/zeffyMembershipForm.css | 30 +++++++++
src/pages/joinUs/JoinUs.jsx | 29 +--------
src/pages/joinUs/JoinUs.test.jsx | 18 ++++++
11 files changed, 258 insertions(+), 37 deletions(-)
create mode 100644 docs/officers/ZEFFY_MEMBERSHIP.md
create mode 100644 src/components/ZeffyMembershipForm.jsx
create mode 100644 src/components/ZeffyMembershipForm.test.jsx
create mode 100644 src/components/zeffyMembershipForm.css
create mode 100644 src/pages/joinUs/JoinUs.test.jsx
diff --git a/.github/lint-baseline.json b/.github/lint-baseline.json
index 61c4e119..b55f7095 100644
--- a/.github/lint-baseline.json
+++ b/.github/lint-baseline.json
@@ -10,10 +10,10 @@
"targets": [
"src"
],
- "scannedFileCount": 137,
+ "scannedFileCount": 140,
"scannedFilesByExtension": {
".js": 12,
- ".jsx": 35,
+ ".jsx": 38,
".ts": 52,
".tsx": 38
},
@@ -31,7 +31,7 @@
".ts": 0,
".tsx": 6
},
- "fingerprint": "sha256:b4fdd4e8efba6c011d293d96f8453a4e2dccccddf7907e2ff9b67cf2b74ba81f",
+ "fingerprint": "sha256:b35a20454e66177c3055a06768686a0f310d15cca2f45d633a4b74f497cafd21",
"scannedFiles": [
"src/App.jsx",
"src/App.test.jsx",
@@ -56,6 +56,8 @@
"src/components/Subheader.jsx",
"src/components/UpcomingEventsPreview.tsx",
"src/components/VideoCarousel.jsx",
+ "src/components/ZeffyMembershipForm.jsx",
+ "src/components/ZeffyMembershipForm.test.jsx",
"src/components/index.ts",
"src/data.jsx",
"src/headerClearance.test.js",
@@ -97,6 +99,7 @@
"src/pages/events/raceCheckoutRequest.ts",
"src/pages/home/Home.jsx",
"src/pages/joinUs/JoinUs.jsx",
+ "src/pages/joinUs/JoinUs.test.jsx",
"src/pages/joinUs/JoinUsConditionalRoute.jsx",
"src/pages/joinUs/Route.jsx",
"src/pages/joinUs/Waiver.jsx",
@@ -1318,9 +1321,9 @@
},
{
"file": "src/pages/joinUs/JoinUs.jsx",
- "line": 130,
+ "line": 105,
"column": 1,
- "endLine": 130,
+ "endLine": 105,
"endColumn": 104,
"severity": 2,
"ruleId": "max-len",
@@ -1331,9 +1334,9 @@
},
{
"file": "src/pages/joinUs/JoinUs.jsx",
- "line": 134,
+ "line": 109,
"column": 17,
- "endLine": 134,
+ "endLine": 109,
"endColumn": 36,
"severity": 2,
"ruleId": "react/no-array-index-key",
diff --git a/OFFICER_START_HERE.md b/OFFICER_START_HERE.md
index 8072d529..d308cafc 100644
--- a/OFFICER_START_HERE.md
+++ b/OFFICER_START_HERE.md
@@ -1,5 +1,7 @@
# MPRC Website: Officer Start Here
+**Zeffy membership preview — October 18, 2026:** the Join Us source embeds the club's supplied Zeffy form with an iframe fallback and a direct link. The [view-only review guide](./docs/officers/ZEFFY_MEMBERSHIP.md) records staging evidence separately. This is the real provider form; do not submit details or pay during the demo. No Firebase membership activation or production release is included.
+
**Saved test Checkout — October 18, 2026, native clarification verified:** Hosting `30d5f27` now explains when Resume cannot reopen the same open, inactive purchase. A fresh sandbox decline/expiry retained the original order and term; no replacement payment or membership was created. Both owned test accounts were retired, seven financial-record hashes stayed unchanged and two expiry receipts were retained. Dues Functions remain v1/source `71f59e2`. [Dated evidence](./docs/TEST_DUES_RECOVERY_MESSAGE.md) and [officer steps](./docs/officers/TEST_DUES.md). Real payments, production, family invitations and final club policies remain separate. Earlier checkpoints below are historical.
**Member-screen staging checkpoint — #739, October 16, 2026:** Function v7 and Hosting source `799565e` are verified, including the downloaded archive, all 62 website files, genuine Auth/App Check and ten member/retry/expiry/cancellation checks with fresh fictional fixtures and verified cleanup. Full-site screenshots exposed a fixed-header style collision on the member navigation. The navigation correction is now published at `d08a6a3`; all 62 final assets, four deep routes, genuine sign-in, desktop/390px navigation/focus and fresh cleanup passed in [closed #739](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/739). Function v7 remains unchanged. Online dues and real membership/payments remain unavailable. [Officer review](./docs/officers/MEMBER_SCREEN.md).
diff --git a/SYSTEM_DESIGN.md b/SYSTEM_DESIGN.md
index 09339218..1f34b1e9 100644
--- a/SYSTEM_DESIGN.md
+++ b/SYSTEM_DESIGN.md
@@ -1,5 +1,7 @@
# MPRC Platform System Design
+**Zeffy membership embed — October 18, 2026, source prepared:** Join Us replaces its Google membership application link with the owner-supplied fixed Zeffy form, route-scoped loader, iframe-on-loader-error fallback and persistent direct link. The staging page labels this as a view-only display of a real provider form. No form value, payment result or provider message is read into Firebase; no account, role, dues, webhook, schema or membership synchronization is added. Prices and renewal terms remain provider content and require reconciliation before production. See the [officer procedure and data-flow map](./docs/officers/ZEFFY_MEMBERSHIP.md) for exact staging evidence. This source note does not claim a merge, deployment, submission or production release.
+
**Saved test Checkout — October 18, 2026, native clarification verified:** Hosting `30d5f27` now explains when Resume cannot reopen the same open, inactive purchase. A fresh sandbox decline/expiry retained the original order and term; no replacement payment or membership was created. Both owned test accounts were retired, seven financial-record hashes stayed unchanged and two expiry receipts were retained. Dues Functions remain v1/source `71f59e2`. [Dated evidence](./docs/TEST_DUES_RECOVERY_MESSAGE.md) and [officer steps](./docs/officers/TEST_DUES.md). Real payments, production, family invitations and final club policies remain separate. Earlier checkpoints below are historical.
**Member-screen staging checkpoint — #739, October 16, 2026:** Function v7 and Hosting source `799565e` are verified, including the downloaded archive, all 62 website files, genuine Auth/App Check and ten member/retry/expiry/cancellation checks with fresh fictional fixtures and verified cleanup. Full-site screenshots exposed a fixed-header style collision on the member navigation. The navigation correction is now published at `d08a6a3`; all 62 final assets, four deep routes, genuine sign-in, desktop/390px navigation/focus and fresh cleanup passed in [closed #739](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/739). Function v7 remains unchanged. Online dues and real membership/payments remain unavailable. [Officer review](./docs/officers/MEMBER_SCREEN.md).
diff --git a/docs/officers/README.md b/docs/officers/README.md
index 596e678c..6d3fcd37 100644
--- a/docs/officers/README.md
+++ b/docs/officers/README.md
@@ -1,5 +1,7 @@
# Officer Website Handbook
+[Review the Zeffy membership preview](./ZEFFY_MEMBERSHIP.md) on Join Us. The supplied form is real: view only during staging review. Provider submissions, payments, Firebase activation and production release remain separate.
+
**October 18:** [Test annual dues](./TEST_DUES.md) now has a verified stop message when a saved Checkout cannot reopen. Keep the purchase and ask Dave for reconciliation. Read the [native evidence and exact test-account cleanup](../TEST_DUES_RECOVERY_MESSAGE.md); real payments and production remain separate.
[Test annual dues](./TEST_DUES.md) has a connected staging screen and verified core hosted flow and [bounded native recovery checks](../TEST_DUES_NATIVE_RECOVERY.md) as of October 17. Fresh specialist-provided fictional accounts are required; the completed run's accounts were removed. Read the [dated evidence and remaining qualification](../TEST_DUES_HOSTED_EVIDENCE.md). Real payments and production remain separate.
diff --git a/docs/officers/ZEFFY_MEMBERSHIP.md b/docs/officers/ZEFFY_MEMBERSHIP.md
new file mode 100644
index 00000000..86041130
--- /dev/null
+++ b/docs/officers/ZEFFY_MEMBERSHIP.md
@@ -0,0 +1,56 @@
+# Review the Zeffy membership form
+
+**Status:** [issue #750](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/750), source prepared October 18, 2026. Staging deployment and screen evidence will be recorded below. Production is **NOT AVAILABLE YET** for this change.
+
+**Purpose:** review the embedded membership form on the Join Us page without creating a membership or payment.
+
+**Approver:** Dave for this staging demonstration. The membership lead, treasurer and platform owner must approve a later production release.
+
+**Prerequisites:** the specialist supplies the exact staging preview link and source revision. Stay signed out. The displayed Zeffy form is the club's real form, not a payment sandbox. Do not enter information, accept a waiver, submit or pay.
+
+```mermaid
+flowchart LR
+ A["Staging Join Us page"] --> B["Zeffy embed loader"]
+ B --> C["Club membership form on Zeffy"]
+ A --> D["Direct link if the embed is unavailable"]
+ D --> C
+ B -- "Loader fails" --> E["Embedded iframe fallback"]
+ E --> C
+ C -. "No account activation or sync added" .-> F["Existing Firebase member system"]
+```
+
+In words: the Join Us page displays the club's form from Zeffy and retains a separate direct link; a failed loader uses an iframe, and this change adds no Firebase membership synchronization.
+
+## Review without a terminal
+
+1. Open the specialist's staging link while signed out.
+2. Find **Become an MPRC Member** on Join Us.
+3. Confirm the notice identifies a staging preview of the real Zeffy form.
+4. Confirm the club's membership options or opening message appear inside the page.
+5. Open **Open the membership form in a new tab**.
+6. Confirm it points to the club's membership on `www.zeffy.com`.
+7. Close the new tab without entering information.
+8. Return to Home and then Join Us.
+9. Confirm only one visible membership form appears.
+10. Repeat at phone and computer widths.
+11. Leave the separate **Sign Waiver Form** unchanged and unused.
+
+**Expected result:** the form is readable within Join Us. A direct link remains usable if the embed fails. There is no duplicate Google membership signup link. Prices and renewal terms are supplied by Zeffy; this change does not settle the conflicting provider description and options noted October 18.
+
+**Stop conditions:** wrong club/form, missing real-form notice, clipped controls, duplicate forms, missing direct link, unexpected account activation, or any request to test a payment or submit real details.
+
+**Success proof:** exact source revision, staging URL and deployment ID, dated phone/computer screenshots, one-form navigation check, loader/fallback tests and served artifact hashes. Viewing a form proves display only. It does not prove payment, membership activation, provider configuration or production readiness.
+
+**Undo:** ask the platform maintainer to expire the task's preview channel. For a later published change, use a reviewed revert and the normal protected release path. Do not edit Zeffy records, delete members or refund a payment to undo a website preview.
+
+**Escalation:** platform owner for display or deployment; membership lead and treasurer for provider options/prices; privacy owner for collected data or policy. A specialist is still required for deployment.
+
+## Deployment evidence
+
+- Source: local implementation; not merged.
+- Tests: 28 frontend suites / 1,531 tests pass under Node 20. The new files have no lint findings. The exact lint baseline retains the same 113 legacy errors and six legacy warnings; only two old Join Us findings move with the removed Google-link lines.
+- Staging website: pending.
+- `runmprc.com`: unchanged; no production release authorized.
+- Firebase Functions, Rules, Auth, IAM and data: unchanged.
+- Zeffy account/form configuration: unchanged; public form display only.
+- Payments and membership submissions: not performed.
diff --git a/src/App.test.jsx b/src/App.test.jsx
index efcff535..e4810fea 100644
--- a/src/App.test.jsx
+++ b/src/App.test.jsx
@@ -921,9 +921,9 @@ test('keeps the real Join route structured data free of an unapproved membership
expect(JSON.stringify(joinSchema)).not.toMatch(
/"@type":"Offer"|"price":|"priceCurrency":|Annual membership fee for individuals \(2026\)/,
);
- expect(screen.getByText(
- 'Affordable membership fees: $25/individual or $30/household per calendar year',
- )).toBeInTheDocument();
+ expect(screen.getByRole('link', { name: /open the membership form in a new tab/i }))
+ .toHaveAttribute('href', 'https://www.zeffy.com/en-US/ticketing/mid-peninsula-running-clubs-membership');
+ expect(screen.queryByText(/Affordable membership fees:/)).not.toBeInTheDocument();
});
test('keeps the shared Join schema helper price-neutral while retaining organization and run data', () => {
diff --git a/src/components/ZeffyMembershipForm.jsx b/src/components/ZeffyMembershipForm.jsx
new file mode 100644
index 00000000..5bd2eb1b
--- /dev/null
+++ b/src/components/ZeffyMembershipForm.jsx
@@ -0,0 +1,63 @@
+import React, { useEffect, useRef, useState } from 'react';
+import './zeffyMembershipForm.css';
+
+const FORM_PATH = '/embed/ticketing/mid-peninsula-running-clubs-membership';
+const FORM_URL = `https://www.zeffy.com${FORM_PATH}`;
+const MEMBERSHIP_URL = 'https://www.zeffy.com/en-US/ticketing/mid-peninsula-running-clubs-membership';
+
+function ZeffyMembershipForm() {
+ const embedRef = useRef(null);
+ const scriptRef = useRef(null);
+ const [useFallback, setUseFallback] = useState(false);
+
+ useEffect(() => {
+ const embed = embedRef.current;
+ const script = document.createElement('script');
+ script.src = 'https://www.zeffy.com/embed/v2/zeffy-embed.js';
+ script.async = true;
+ script.onerror = () => setUseFallback(true);
+ scriptRef.current.appendChild(script);
+
+ return () => {
+ script.onerror = null;
+ script.remove();
+ // Zeffy owns the children it adds here. A route remount starts fresh.
+ embed.replaceChildren();
+ };
+ }, []);
+
+ return (
+
+ {process.env.REACT_APP_FIREBASE_ENVIRONMENT === 'staging' && (
+
+ Staging preview:
+ {' '}
+ This is the club's real Zeffy form. View only; do not submit details or pay.
+
+ )}
+
+ Join or renew through Zeffy. Membership options, prices and opening dates
+ are shown in the form.
+
-
- {JOIN_US_BUTTON_2}
-
);
diff --git a/src/pages/joinUs/JoinUs.test.jsx b/src/pages/joinUs/JoinUs.test.jsx
new file mode 100644
index 00000000..6ea869cb
--- /dev/null
+++ b/src/pages/joinUs/JoinUs.test.jsx
@@ -0,0 +1,18 @@
+/* eslint-env jest */
+import React from 'react';
+import { render, screen } from '@testing-library/react';
+import JoinUs from './JoinUs';
+
+jest.mock('../../components/SEO', () => () => null);
+jest.mock('./Route', () => () => null);
+
+test('replaces Google membership signup with Zeffy while preserving the separate activity waiver', () => {
+ render();
+ expect(screen.getByRole('region', { name: 'MPRC membership signup' })).toBeVisible();
+ expect(screen.getByRole('link', { name: /open the membership form/i }))
+ .toHaveAttribute('href', 'https://www.zeffy.com/en-US/ticketing/mid-peninsula-running-clubs-membership');
+ expect(screen.queryByRole('link', { name: 'Membership Application Form' })).not.toBeInTheDocument();
+ expect(screen.getByRole('link', { name: 'Sign Waiver Form' })).toBeVisible();
+ expect(screen.queryByText(/Affordable membership fees/)).not.toBeInTheDocument();
+ expect(screen.getByRole('heading', { name: 'Become an MPRC Member' })).toBeVisible();
+});
From 2b76f26e2ddd4861267ca9fa95c8191c4225b620 Mon Sep 17 00:00:00 2001
From: Dave Liu <7david12liu@gmail.com>
Date: Sun, 18 Oct 2026 17:48:44 -0700
Subject: [PATCH 2/7] Distinguish approved Zeffy form literals from Firebase
project identifiers
---
SYSTEM_DESIGN.md | 2 ++
docs/officers/ZEFFY_MEMBERSHIP.md | 1 +
scripts/firebase-hosting-contract.js | 7 ++++-
tests/firebase-hosting-foundation.test.js | 33 +++++++++++++++++++++++
4 files changed, 42 insertions(+), 1 deletion(-)
diff --git a/SYSTEM_DESIGN.md b/SYSTEM_DESIGN.md
index 1f34b1e9..e70f5953 100644
--- a/SYSTEM_DESIGN.md
+++ b/SYSTEM_DESIGN.md
@@ -2,6 +2,8 @@
**Zeffy membership embed — October 18, 2026, source prepared:** Join Us replaces its Google membership application link with the owner-supplied fixed Zeffy form, route-scoped loader, iframe-on-loader-error fallback and persistent direct link. The staging page labels this as a view-only display of a real provider form. No form value, payment result or provider message is read into Firebase; no account, role, dues, webhook, schema or membership synchronization is added. Prices and renewal terms remain provider content and require reconciliation before production. See the [officer procedure and data-flow map](./docs/officers/ZEFFY_MEMBERSHIP.md) for exact staging evidence. This source note does not claim a merge, deployment, submission or production release.
+The staging executable scan allows only the three complete quoted literals for this approved Zeffy form (relative embed path, absolute embed URL and public ticketing URL). Its slug overlaps the production Firebase project name. Staging configuration validation remains unchanged; every actual production identifier elsewhere in the same file or another executable still rejects, as do changed hosts, form suffixes and production endpoints. The Hosting regression test reproduces the original false rejection and checks the narrowed exception.
+
**Saved test Checkout — October 18, 2026, native clarification verified:** Hosting `30d5f27` now explains when Resume cannot reopen the same open, inactive purchase. A fresh sandbox decline/expiry retained the original order and term; no replacement payment or membership was created. Both owned test accounts were retired, seven financial-record hashes stayed unchanged and two expiry receipts were retained. Dues Functions remain v1/source `71f59e2`. [Dated evidence](./docs/TEST_DUES_RECOVERY_MESSAGE.md) and [officer steps](./docs/officers/TEST_DUES.md). Real payments, production, family invitations and final club policies remain separate. Earlier checkpoints below are historical.
**Member-screen staging checkpoint — #739, October 16, 2026:** Function v7 and Hosting source `799565e` are verified, including the downloaded archive, all 62 website files, genuine Auth/App Check and ten member/retry/expiry/cancellation checks with fresh fictional fixtures and verified cleanup. Full-site screenshots exposed a fixed-header style collision on the member navigation. The navigation correction is now published at `d08a6a3`; all 62 final assets, four deep routes, genuine sign-in, desktop/390px navigation/focus and fresh cleanup passed in [closed #739](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/739). Function v7 remains unchanged. Online dues and real membership/payments remain unavailable. [Officer review](./docs/officers/MEMBER_SCREEN.md).
diff --git a/docs/officers/ZEFFY_MEMBERSHIP.md b/docs/officers/ZEFFY_MEMBERSHIP.md
index 86041130..00916357 100644
--- a/docs/officers/ZEFFY_MEMBERSHIP.md
+++ b/docs/officers/ZEFFY_MEMBERSHIP.md
@@ -49,6 +49,7 @@ In words: the Join Us page displays the club's form from Zeffy and retains a sep
- Source: local implementation; not merged.
- Tests: 28 frontend suites / 1,531 tests pass under Node 20. The new files have no lint findings. The exact lint baseline retains the same 113 legacy errors and six legacy warnings; only two old Join Us findings move with the removed Google-link lines.
+- Hosting checks: all 16 pass. A test first reproduced the rejected Zeffy slug, then passed with the exact form-literal exception. All real production identifiers, endpoints, changed hosts and changed form suffixes still reject.
- Staging website: pending.
- `runmprc.com`: unchanged; no production release authorized.
- Firebase Functions, Rules, Auth, IAM and data: unchanged.
diff --git a/scripts/firebase-hosting-contract.js b/scripts/firebase-hosting-contract.js
index c69af947..0623b047 100644
--- a/scripts/firebase-hosting-contract.js
+++ b/scripts/firebase-hosting-contract.js
@@ -19,6 +19,10 @@ const PRODUCTION_CONFIGURATION = Object.freeze({
const PRODUCTION_IDENTIFIERS = Object.freeze(
Object.values(PRODUCTION_CONFIGURATION),
);
+// The approved public Zeffy slug happens to contain the Firebase project name.
+// Exclude only these complete, quoted form literals from the artifact text scan.
+// Configuration validation and every other production-identity occurrence remain strict.
+const APPROVED_ZEFFY_FORM_LITERAL = /(["'`])(?:https:\/\/www\.zeffy\.com\/(?:en-US\/ticketing|embed\/ticketing)|\/embed\/ticketing)\/mid-peninsula-running-clubs-membership\1/gu;
const STAGING_FIELDS = Object.freeze([
'REACT_APP_FIREBASE_API_KEY',
'REACT_APP_FIREBASE_AUTH_DOMAIN',
@@ -183,8 +187,9 @@ function verifyExecutableArtifact(directory, environment = process.env) {
requiredIdentifiers.forEach((identity) => {
if (contents.includes(identity)) foundIdentifiers.add(identity);
});
+ const identityContents = contents.replace(APPROVED_ZEFFY_FORM_LITERAL, '"approved-zeffy-form"');
if (selected.environment === 'staging'
- && PRODUCTION_IDENTIFIERS.some((identity) => contents.includes(identity))) {
+ && PRODUCTION_IDENTIFIERS.some((identity) => identityContents.includes(identity))) {
reject();
}
});
diff --git a/tests/firebase-hosting-foundation.test.js b/tests/firebase-hosting-foundation.test.js
index 171c0313..c048ef72 100644
--- a/tests/firebase-hosting-foundation.test.js
+++ b/tests/firebase-hosting-foundation.test.js
@@ -319,6 +319,39 @@ test('WEB-001A1 executable staging artifacts prove staging identity and reject p
);
});
+test('Zeffy public form literals do not masquerade as Firebase production identities', (t) => {
+ const contract = require('../scripts/firebase-hosting-contract');
+ const fixture = fs.mkdtempSync(path.join(process.env.TMPDIR || '/tmp', 'mprc-zeffy-hosting-'));
+ t.after(() => fs.rmSync(fixture, { force: true, recursive: true }));
+ const filename = path.join(fixture, 'main.js');
+ const config = `const config=${JSON.stringify(STAGING_BUILD_ENVIRONMENT)};\n`;
+ const paths = [
+ '/embed/ticketing/mid-peninsula-running-clubs-membership',
+ 'https://www.zeffy.com/embed/ticketing/mid-peninsula-running-clubs-membership',
+ 'https://www.zeffy.com/en-US/ticketing/mid-peninsula-running-clubs-membership',
+ ];
+ for (const quote of ['"', "'", '`']) {
+ for (const approved of paths) {
+ fs.writeFileSync(filename, `${config}const form=${quote}${approved}${quote};\n`);
+ assert.equal(contract.verifyExecutableArtifact(fixture, STAGING_BUILD_ENVIRONMENT).environment, 'staging');
+ }
+ }
+ const approved = paths.map(value => JSON.stringify(value)).join(',');
+ for (const invalid of [
+ ...Object.values(PRODUCTION_BUILD_ENVIRONMENT).filter(value => value !== 'production'),
+ 'https://us-central1-mid-peninsula-running-club.cloudfunctions.net/example',
+ 'https://unapproved.example/embed/ticketing/mid-peninsula-running-clubs-membership',
+ `${paths[0]}-different-form`,
+ `${paths[1]}.firebaseapp.com`,
+ ]) {
+ fs.writeFileSync(filename, `${config}const forms=[${approved}];const forbidden=${JSON.stringify(invalid)};\n`);
+ assert.throws(() => contract.verifyExecutableArtifact(fixture, STAGING_BUILD_ENVIRONMENT), /firebase_hosting_contract_rejected/u);
+ }
+ fs.writeFileSync(filename, `${config}const forms=[${approved}];\n`);
+ fs.writeFileSync(path.join(fixture, 'other.js'), 'const project="mid-peninsula-running-club";\n');
+ assert.throws(() => contract.verifyExecutableArtifact(fixture, STAGING_BUILD_ENVIRONMENT), /firebase_hosting_contract_rejected/u);
+});
+
test('CI-001D4 executable staging artifact contains the selected App Check key', (t) => {
// eslint-disable-next-line global-require, import/no-unresolved
const contract = require('../scripts/firebase-hosting-contract');
From c750d7bac5905eb25f20b0e5726006e719b06c0b Mon Sep 17 00:00:00 2001
From: Dave Liu <7david12liu@gmail.com>
Date: Sun, 18 Oct 2026 17:50:38 -0700
Subject: [PATCH 3/7] Keep embedded membership content within the existing card
spacing
---
src/components/ZeffyMembershipForm.jsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/components/ZeffyMembershipForm.jsx b/src/components/ZeffyMembershipForm.jsx
index 5bd2eb1b..4e4743d0 100644
--- a/src/components/ZeffyMembershipForm.jsx
+++ b/src/components/ZeffyMembershipForm.jsx
@@ -27,7 +27,7 @@ function ZeffyMembershipForm() {
}, []);
return (
-
+
Staging preview:
@@ -56,7 +56,7 @@ function ZeffyMembershipForm() {
-
+
);
}
From 72f3c3b4ff2f514aae7c623c8e544194741156a8 Mon Sep 17 00:00:00 2001
From: Dave Liu <7david12liu@gmail.com>
Date: Sun, 18 Oct 2026 17:52:51 -0700
Subject: [PATCH 4/7] Record verified Zeffy staging preview and officer review
evidence
---
OFFICER_START_HERE.md | 2 +-
SYSTEM_DESIGN.md | 2 +-
docs/officers/ZEFFY_MEMBERSHIP.md | 10 +++++++---
3 files changed, 9 insertions(+), 5 deletions(-)
diff --git a/OFFICER_START_HERE.md b/OFFICER_START_HERE.md
index d308cafc..cc5567ea 100644
--- a/OFFICER_START_HERE.md
+++ b/OFFICER_START_HERE.md
@@ -1,6 +1,6 @@
# MPRC Website: Officer Start Here
-**Zeffy membership preview — October 18, 2026:** the Join Us source embeds the club's supplied Zeffy form with an iframe fallback and a direct link. The [view-only review guide](./docs/officers/ZEFFY_MEMBERSHIP.md) records staging evidence separately. This is the real provider form; do not submit details or pay during the demo. No Firebase membership activation or production release is included.
+**Zeffy membership preview — October 18, 2026:** Join Us embeds the club's supplied Zeffy form with an iframe fallback and a direct link. The [view-only review guide](./docs/officers/ZEFFY_MEMBERSHIP.md) records verified phone/desktop display and all 61 staging file hashes at source `c750d7b`. The separate preview expires October 25; shared staging is unchanged. This is the real provider form; do not submit details or pay during the demo. No Firebase membership activation or production release is included.
**Saved test Checkout — October 18, 2026, native clarification verified:** Hosting `30d5f27` now explains when Resume cannot reopen the same open, inactive purchase. A fresh sandbox decline/expiry retained the original order and term; no replacement payment or membership was created. Both owned test accounts were retired, seven financial-record hashes stayed unchanged and two expiry receipts were retained. Dues Functions remain v1/source `71f59e2`. [Dated evidence](./docs/TEST_DUES_RECOVERY_MESSAGE.md) and [officer steps](./docs/officers/TEST_DUES.md). Real payments, production, family invitations and final club policies remain separate. Earlier checkpoints below are historical.
diff --git a/SYSTEM_DESIGN.md b/SYSTEM_DESIGN.md
index e70f5953..839126bb 100644
--- a/SYSTEM_DESIGN.md
+++ b/SYSTEM_DESIGN.md
@@ -1,6 +1,6 @@
# MPRC Platform System Design
-**Zeffy membership embed — October 18, 2026, source prepared:** Join Us replaces its Google membership application link with the owner-supplied fixed Zeffy form, route-scoped loader, iframe-on-loader-error fallback and persistent direct link. The staging page labels this as a view-only display of a real provider form. No form value, payment result or provider message is read into Firebase; no account, role, dues, webhook, schema or membership synchronization is added. Prices and renewal terms remain provider content and require reconciliation before production. See the [officer procedure and data-flow map](./docs/officers/ZEFFY_MEMBERSHIP.md) for exact staging evidence. This source note does not claim a merge, deployment, submission or production release.
+**Zeffy membership embed — October 18, 2026, staging display verified:** Join Us replaces its Google membership application link with the owner-supplied fixed Zeffy form, route-scoped loader, iframe-on-loader-error fallback and persistent direct link. The staging page labels this as a view-only display of a real provider form. No form value, payment result or provider message is read into Firebase; no account, role, dues, webhook, schema or membership synchronization is added. Prices and renewal terms remain provider content and require reconciliation before production. The separate `zeffy-750` preview serves source `c750d7b`, with all 61 file hashes and signed-out phone/desktop display verified; shared staging is unchanged. See the [officer procedure and data-flow map](./docs/officers/ZEFFY_MEMBERSHIP.md) for exact staging evidence. This does not claim a merge, submission or production release.
The staging executable scan allows only the three complete quoted literals for this approved Zeffy form (relative embed path, absolute embed URL and public ticketing URL). Its slug overlaps the production Firebase project name. Staging configuration validation remains unchanged; every actual production identifier elsewhere in the same file or another executable still rejects, as do changed hosts, form suffixes and production endpoints. The Hosting regression test reproduces the original false rejection and checks the narrowed exception.
diff --git a/docs/officers/ZEFFY_MEMBERSHIP.md b/docs/officers/ZEFFY_MEMBERSHIP.md
index 00916357..f4de6042 100644
--- a/docs/officers/ZEFFY_MEMBERSHIP.md
+++ b/docs/officers/ZEFFY_MEMBERSHIP.md
@@ -1,6 +1,6 @@
# Review the Zeffy membership form
-**Status:** [issue #750](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/750), source prepared October 18, 2026. Staging deployment and screen evidence will be recorded below. Production is **NOT AVAILABLE YET** for this change.
+**Status:** [issue #750](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/750), staging display verified October 18, 2026. [Open the view-only preview](https://run-mprc-staging--zeffy-750-nqx3741y.web.app/joinus#membership-signup). Production is **NOT AVAILABLE YET** for this change.
**Purpose:** review the embedded membership form on the Join Us page without creating a membership or payment.
@@ -47,11 +47,15 @@ In words: the Join Us page displays the club's form from Zeffy and retains a sep
## Deployment evidence
-- Source: local implementation; not merged.
+- Source: deployed implementation `c750d7bac5905eb25f20b0e5726006e719b06c0b`; not merged. Later documentation-only evidence commits do not change its executable source.
- Tests: 28 frontend suites / 1,531 tests pass under Node 20. The new files have no lint findings. The exact lint baseline retains the same 113 legacy errors and six legacy warnings; only two old Join Us findings move with the removed Google-link lines.
- Hosting checks: all 16 pass. A test first reproduced the rejected Zeffy slug, then passed with the exact form-literal exception. All real production identifiers, endpoints, changed hosts and changed form suffixes still reject.
-- Staging website: pending.
+- Staging website: project `run-mprc-staging`, preview channel `zeffy-750`, version `67290871d20f4d57`. All 61 served file hashes match the local artifact. Preview expires October 25, 2026 at 5:50 PM Pacific time.
+- Actual signed-out screens: 390×844 phone and 1280×720 desktop; form choices, real-form notice, direct-link target and zero page horizontal overflow verified. Home → Join Us reinitializes one visible form and one loader. A spacing correction avoids the site's outer-section margin. Dated screenshots are retained with this chat; no member or payment information was entered. Fallback behavior is covered by the local tests; no provider failure or checkout was induced.
+- Existing shared staging: version `6619f96ac1a39406` unchanged. Preview deployment explicitly disabled Auth-domain synchronization and did not enable the community or test-dues feature flags. No CI workflow was manually dispatched.
- `runmprc.com`: unchanged; no production release authorized.
- Firebase Functions, Rules, Auth, IAM and data: unchanged.
- Zeffy account/form configuration: unchanged; public form display only.
- Payments and membership submissions: not performed.
+
+The first compiled artifact was refused because the approved Zeffy slug overlapped the production-project text scan. The narrow exception above was tested before retry. A later successful CLI publication omitted its version field; native channel readback and all-file verification qualified that release without another deployment. The final spacing revision was then published and all 61 files verified again. No deployment guard was skipped.
From fcbefe0612f3d72e32e2b8588543d5451eee6c32 Mon Sep 17 00:00:00 2001
From: Dave Liu <7david12liu@gmail.com>
Date: Sun, 18 Oct 2026 22:28:56 -0700
Subject: [PATCH 5/7] Simplify Zeffy signup and separate guest waiver and
officer access
---
.github/lint-baseline.json | 37 +++--------------
OFFICER_START_HERE.md | 2 +
SYSTEM_DESIGN.md | 2 +
docs/officers/ACCESS_CONTINUITY.md | 2 +
docs/officers/README.md | 2 +
docs/officers/ZEFFY_MEMBERSHIP.md | 21 +++++++---
src/App.jsx | 3 +-
src/App.test.jsx | 17 ++++++++
src/components/Footer.jsx | 1 +
src/components/Navbar.jsx | 12 ------
src/components/PublicNavigation.test.jsx | 27 ++++++++++++
src/components/ZeffyMembershipForm.jsx | 4 +-
src/headerClearance.test.js | 2 +-
src/pages/joinUs/JoinUs.jsx | 32 +++++++--------
src/pages/joinUs/JoinUs.test.jsx | 12 +++++-
src/pages/joinUs/joinUs.css | 6 ++-
src/pages/login/LoginForm.jsx | 52 +++++++++++++++++-------
src/pages/login/LoginForm.test.jsx | 32 +++++++++++++++
18 files changed, 180 insertions(+), 86 deletions(-)
create mode 100644 src/components/PublicNavigation.test.jsx
diff --git a/.github/lint-baseline.json b/.github/lint-baseline.json
index b55f7095..af2a669e 100644
--- a/.github/lint-baseline.json
+++ b/.github/lint-baseline.json
@@ -10,18 +10,18 @@
"targets": [
"src"
],
- "scannedFileCount": 140,
+ "scannedFileCount": 141,
"scannedFilesByExtension": {
".js": 12,
- ".jsx": 38,
+ ".jsx": 39,
".ts": 52,
".tsx": 38
},
- "errorCount": 113,
+ "errorCount": 111,
"warningCount": 6,
"errorsByExtension": {
".js": 2,
- ".jsx": 26,
+ ".jsx": 24,
".ts": 2,
".tsx": 83
},
@@ -31,7 +31,7 @@
".ts": 0,
".tsx": 6
},
- "fingerprint": "sha256:b35a20454e66177c3055a06768686a0f310d15cca2f45d633a4b74f497cafd21",
+ "fingerprint": "sha256:c39f2619673c2ce72d2473f683bbca5119858c510e6f158fadd2d46fcc6e8b4e",
"scannedFiles": [
"src/App.jsx",
"src/App.test.jsx",
@@ -49,6 +49,7 @@
"src/components/ObfuscatedEmail.jsx",
"src/components/Officer.jsx",
"src/components/Paragraph.jsx",
+ "src/components/PublicNavigation.test.jsx",
"src/components/SEO.d.ts",
"src/components/SEO.jsx",
"src/components/ScrollToTop.jsx",
@@ -1319,32 +1320,6 @@
"nodeType": "ConditionalExpression",
"fatal": false
},
- {
- "file": "src/pages/joinUs/JoinUs.jsx",
- "line": 105,
- "column": 1,
- "endLine": 105,
- "endColumn": 104,
- "severity": 2,
- "ruleId": "max-len",
- "message": "This line has a length of 103. Maximum allowed is 100.",
- "messageId": "max",
- "nodeType": "Program",
- "fatal": false
- },
- {
- "file": "src/pages/joinUs/JoinUs.jsx",
- "line": 109,
- "column": 17,
- "endLine": 109,
- "endColumn": 36,
- "severity": 2,
- "ruleId": "react/no-array-index-key",
- "message": "Do not use Array index in keys",
- "messageId": "noArrayIndex",
- "nodeType": "TemplateLiteral",
- "fatal": false
- },
{
"file": "src/pages/joinUs/Route.jsx",
"line": 22,
diff --git a/OFFICER_START_HERE.md b/OFFICER_START_HERE.md
index cc5567ea..9e5362cc 100644
--- a/OFFICER_START_HERE.md
+++ b/OFFICER_START_HERE.md
@@ -1,5 +1,7 @@
# MPRC Website: Officer Start Here
+**October 18 simplified signup — staging update pending:** members use Zeffy, guests retain a separate waiver option, and officers use a footer Officer access link. The public account link and repeated member waiver text are removed in source. No account, permission or provider configuration changes. Follow the [Zeffy review guide](./docs/officers/ZEFFY_MEMBERSHIP.md) for availability and exact staging evidence.
+
**Zeffy membership preview — October 18, 2026:** Join Us embeds the club's supplied Zeffy form with an iframe fallback and a direct link. The [view-only review guide](./docs/officers/ZEFFY_MEMBERSHIP.md) records verified phone/desktop display and all 61 staging file hashes at source `c750d7b`. The separate preview expires October 25; shared staging is unchanged. This is the real provider form; do not submit details or pay during the demo. No Firebase membership activation or production release is included.
**Saved test Checkout — October 18, 2026, native clarification verified:** Hosting `30d5f27` now explains when Resume cannot reopen the same open, inactive purchase. A fresh sandbox decline/expiry retained the original order and term; no replacement payment or membership was created. Both owned test accounts were retired, seven financial-record hashes stayed unchanged and two expiry receipts were retained. Dues Functions remain v1/source `71f59e2`. [Dated evidence](./docs/TEST_DUES_RECOVERY_MESSAGE.md) and [officer steps](./docs/officers/TEST_DUES.md). Real payments, production, family invitations and final club policies remain separate. Earlier checkpoints below are historical.
diff --git a/SYSTEM_DESIGN.md b/SYSTEM_DESIGN.md
index 839126bb..60d89740 100644
--- a/SYSTEM_DESIGN.md
+++ b/SYSTEM_DESIGN.md
@@ -1,5 +1,7 @@
# MPRC Platform System Design
+**October 18 Zeffy simplification — source prepared, staging update pending:** Dave chose Zeffy for member enrollment. Join Us leads with that form, removes the repeated member waiver text, and retains the existing activity-waiver URL under a distinct Guest waiver section. Public navigation has no Sign in/My Account entry. Footer Officer access opens `/officer-access`, reusing the existing sign-in/recovery service without registration; successful sign-in still encounters the existing admin guard. Legacy `/login` and protected-route behavior remain compatible. No role, Auth provider, account, waiver policy/text, payment, schema or membership-sync change is included. The [officer flow map and review procedure](./docs/officers/ZEFFY_MEMBERSHIP.md) distinguish this source update from the earlier deployed preview and record the next exact staging receipt.
+
**Zeffy membership embed — October 18, 2026, staging display verified:** Join Us replaces its Google membership application link with the owner-supplied fixed Zeffy form, route-scoped loader, iframe-on-loader-error fallback and persistent direct link. The staging page labels this as a view-only display of a real provider form. No form value, payment result or provider message is read into Firebase; no account, role, dues, webhook, schema or membership synchronization is added. Prices and renewal terms remain provider content and require reconciliation before production. The separate `zeffy-750` preview serves source `c750d7b`, with all 61 file hashes and signed-out phone/desktop display verified; shared staging is unchanged. See the [officer procedure and data-flow map](./docs/officers/ZEFFY_MEMBERSHIP.md) for exact staging evidence. This does not claim a merge, submission or production release.
The staging executable scan allows only the three complete quoted literals for this approved Zeffy form (relative embed path, absolute embed URL and public ticketing URL). Its slug overlaps the production Firebase project name. Staging configuration validation remains unchanged; every actual production identifier elsewhere in the same file or another executable still rejects, as do changed hosts, form suffixes and production endpoints. The Hosting regression test reproduces the original false rejection and checks the narrowed exception.
diff --git a/docs/officers/ACCESS_CONTINUITY.md b/docs/officers/ACCESS_CONTINUITY.md
index 1c4159f0..ed8c6496 100644
--- a/docs/officers/ACCESS_CONTINUITY.md
+++ b/docs/officers/ACCESS_CONTINUITY.md
@@ -1,5 +1,7 @@
# Access Continuity
+**October 18 Zeffy preview update — source prepared:** footer **Officer access** opens a dedicated existing-account sign-in page. Public membership uses Zeffy without an MPRC account. This is a navigation change, not an officer role, provider or recovery configuration change. Check the [Zeffy staging receipt and no-terminal review](./ZEFFY_MEMBERSHIP.md) before use. Existing private-route and legacy login behavior remain compatible; ordinary officer access still requires its established authorization.
+
**#719 linking permission — STAGING VERIFIED 2026-10-12:** Dave/platform specialist supplies a fresh expiring association permission and exact disposable-target marker for the [supervised screen review](./STAGING_COMMUNITY.md#link-a-fictional-receipt--719). Existing recording/approval/event access grants no linking authority. Hosted revocation cleared target/receipt controls and denied old successful retries; old capabilities, accounts and records were removed. Do not change a role, email-match an account or share credentials/codes. Stop on an uncertain ID/revision or access surviving revocation. Enrollment, fixture supply, cleanup and rollback remain specialist-only; no backup appointment or production permission changes.
**Purpose:** ensure at least two named officers can reach every important service if the usual owner is unavailable.
diff --git a/docs/officers/README.md b/docs/officers/README.md
index 6d3fcd37..ce97c471 100644
--- a/docs/officers/README.md
+++ b/docs/officers/README.md
@@ -1,5 +1,7 @@
# Officer Website Handbook
+**Zeffy simplification, source prepared October 18:** members use Zeffy, guests keep the existing waiver form, and separate Officer access is in the footer. The top menu has no account link. Check the [dated staging review](./ZEFFY_MEMBERSHIP.md) before using the preview; source preparation is not a production release.
+
[Review the Zeffy membership preview](./ZEFFY_MEMBERSHIP.md) on Join Us. The supplied form is real: view only during staging review. Provider submissions, payments, Firebase activation and production release remain separate.
**October 18:** [Test annual dues](./TEST_DUES.md) now has a verified stop message when a saved Checkout cannot reopen. Keep the purchase and ask Dave for reconciliation. Read the [native evidence and exact test-account cleanup](../TEST_DUES_RECOVERY_MESSAGE.md); real payments and production remain separate.
diff --git a/docs/officers/ZEFFY_MEMBERSHIP.md b/docs/officers/ZEFFY_MEMBERSHIP.md
index f4de6042..3acf5104 100644
--- a/docs/officers/ZEFFY_MEMBERSHIP.md
+++ b/docs/officers/ZEFFY_MEMBERSHIP.md
@@ -1,5 +1,7 @@
# Review the Zeffy membership form
+**October 18 simplification — source prepared; staging update pending:** Dave selected Zeffy for members, removal of the duplicate member waiver and public account navigation, a guest waiver option, and separate officer access. The existing receipt below describes the earlier preview until a new dated release is recorded.
+
**Status:** [issue #750](https://github.com/Run-MPRC/Run-MPRC.github.io/issues/750), staging display verified October 18, 2026. [Open the view-only preview](https://run-mprc-staging--zeffy-750-nqx3741y.web.app/joinus#membership-signup). Production is **NOT AVAILABLE YET** for this change.
**Purpose:** review the embedded membership form on the Join Us page without creating a membership or payment.
@@ -10,16 +12,19 @@
```mermaid
flowchart LR
- A["Staging Join Us page"] --> B["Zeffy embed loader"]
+ A["Staging Join Us page"] --> B["Members: Zeffy embed loader"]
B --> C["Club membership form on Zeffy"]
A --> D["Direct link if the embed is unavailable"]
D --> C
B -- "Loader fails" --> E["Embedded iframe fallback"]
E --> C
+ A --> G["Guests: existing activity waiver form"]
+ A --> H["Footer: Officer access"]
+ H --> I["Existing officer sign-in and admin authorization"]
C -. "No account activation or sync added" .-> F["Existing Firebase member system"]
```
-In words: the Join Us page displays the club's form from Zeffy and retains a separate direct link; a failed loader uses an iframe, and this change adds no Firebase membership synchronization.
+In words: members use Zeffy, guests retain the existing waiver form, and officers have a separate footer entry; the public menu has no account link, and this change adds no Firebase membership synchronization or access grant.
## Review without a terminal
@@ -33,11 +38,17 @@ In words: the Join Us page displays the club's form from Zeffy and retains a sep
8. Return to Home and then Join Us.
9. Confirm only one visible membership form appears.
10. Repeat at phone and computer widths.
-11. Leave the separate **Sign Waiver Form** unchanged and unused.
+11. Select **Use the guest waiver** and confirm it reaches the **Guest waiver** section.
+12. Confirm **Complete guest waiver** points to the existing activity waiver form; do not submit it.
+13. Confirm the old **Club Activity Waiver** text block is absent.
+14. Confirm **Sign in** and **My Account** are absent from the top menu, including the phone menu.
+15. Select **Officer access** in the footer.
+16. Confirm the page says **Officer sign in**, offers password recovery and has no registration button.
+17. Select **membership signup on Zeffy** to return to Join Us.
-**Expected result:** the form is readable within Join Us. A direct link remains usable if the embed fails. There is no duplicate Google membership signup link. Prices and renewal terms are supplied by Zeffy; this change does not settle the conflicting provider description and options noted October 18.
+**Expected result after the staged update:** Zeffy is the member path, with no second MPRC account or repeated website waiver. Guests can reach the existing activity waiver. The footer officer entry uses existing authentication and admin authorization; it creates no account or privilege. The legacy `/login` route remains compatible with existing private-route redirects. Prices, renewal terms and the member waiver inside Zeffy remain provider configuration; this display change does not verify or alter them.
-**Stop conditions:** wrong club/form, missing real-form notice, clipped controls, duplicate forms, missing direct link, unexpected account activation, or any request to test a payment or submit real details.
+**Stop conditions:** wrong club/form, missing real-form notice, clipped controls, duplicate forms, missing guest/direct links, public account navigation, officer-page registration, unexpected account activation or permission, or any request to test a payment or submit real details.
**Success proof:** exact source revision, staging URL and deployment ID, dated phone/computer screenshots, one-form navigation check, loader/fallback tests and served artifact hashes. Viewing a form proves display only. It does not prove payment, membership activation, provider configuration or production readiness.
diff --git a/src/App.jsx b/src/App.jsx
index 1f8e338a..c5df44f5 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -100,7 +100,8 @@ function App() {
} />
} />
} />
- } />
+ } />
+ } />
} />
} />
} />
diff --git a/src/App.test.jsx b/src/App.test.jsx
index e4810fea..5ea32fec 100644
--- a/src/App.test.jsx
+++ b/src/App.test.jsx
@@ -895,6 +895,23 @@ describe('WEB-SUGGESTIONS-001A static Suggestions page', () => {
});
});
+test('keeps officer access separate and resets legacy registration when entering the officer page', async () => {
+ window.history.pushState({}, '', '/login');
+ render();
+ fireEvent.click(screen.getByRole('button', { name: 'New here? Register' }));
+ expect(screen.getByRole('heading', { name: 'Create your account' })).toBeVisible();
+ fireEvent.click(screen.getByRole('link', { name: 'Officer access' }));
+ expect(await screen.findByRole('heading', { name: 'Officer sign in' })).toBeVisible();
+ expect(window.location.pathname).toBe('/officer-access');
+ expect(screen.queryByRole('button', { name: 'New here? Register' })).not.toBeInTheDocument();
+ expect(screen.queryByRole('button', { name: 'Create account' })).not.toBeInTheDocument();
+ expect(screen.getByRole('button', { name: 'Forgot password?' })).toBeVisible();
+ expect(within(screen.getByRole('navigation')).queryByRole('link', { name: 'Sign in' }))
+ .not.toBeInTheDocument();
+ fireEvent.click(screen.getByRole('link', { name: 'membership signup on Zeffy' }));
+ expect(await screen.findByRole('region', { name: 'MPRC membership signup' })).toBeVisible();
+});
+
test('keeps the real Join route structured data free of an unapproved membership Offer', async () => {
window.history.pushState({}, '', '/joinus');
render();
diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx
index 3939db09..699a638c 100644
--- a/src/components/Footer.jsx
+++ b/src/components/Footer.jsx
@@ -42,6 +42,7 @@ function Footer() {
Suggestions
Terms
Privacy
+ Officer access