Skip to content

OpenConceptLab/ocl_issues#2899 | Link the Terms of Use and Privacy Policy from the footer - #64

Merged
paynejd merged 4 commits into
mainfrom
OpenConceptLab/ocl_issues#2899-footer-legal-links
Oct 14, 2026
Merged

paynejd merged 4 commits into
mainfrom
OpenConceptLab/ocl_issues#2899-footer-legal-links

Conversation

@paynejd

@paynejd paynejd commented Oct 13, 2026 •

Copy link
Copy Markdown
Member

What changed and why

Using TBv3 means accepting OCL's Terms of Use, but TBv3 didn't link it or the Privacy Policy anywhere. The footer now does, on every page, signed in or not:

  • Terms of Use links to https://openconceptlab.org/terms-of-use and Privacy Policy to https://openconceptlab.org/privacy-policy. Each opens in a new tab (target="_blank", rel="noopener noreferrer"). The URLs are TERMS_OF_USE_URL and PRIVACY_POLICY_URL in src/common/constants.js.
  • The labels are common.terms_of_use and common.privacy_policy, in all four catalogs (table below).
  • From 900 px up (MUI's md breakpoint) the footer stays one 30 px row: Terms of Use | Privacy Policy | © 2026 Open Concept Lab | TB-… | API-….
  • Below 900 px that row doesn't fit in every language. At 375 px even the old footer wrapped, and its second line fell below the bottom of the screen. Now the links take a row of their own, and the copyright and versions wrap under them, centered, with spacing in place of the dividers.
  • A taller footer would cover the bottom of the page. So the footer publishes how much taller than 30 px it is, as --footer-extra-height, and --app-height in src/index.scss subtracts it. It uses a ResizeObserver, as AnnouncementBanner does for --announcement-height. At 900 px and up the value is 0, so nothing changes there.

New strings

Key en es pt-BR zh
common.terms_of_use Terms of Use Términos de uso Termos de uso 使用条款
common.privacy_policy Privacy Policy Política de privacidad Política de privacidade 隐私政策
  • These are the usual names for the two documents. pt-BR uses sentence case, as the localization guide asks.
  • No string was left in English, but each language's translation lead may want to confirm these.
  • The linked pages themselves are in English.
  • The Mapper's footer ticket, Mapper: link the Terms of Use and Privacy Policy from the footer ocl_issues#2900, can reuse these keys and translations, so the shared keys stay identical in both apps.

Screenshots

Footer at 1280 px, right-hand 800 px of the row:

TBv3 footer at 1280 px in en, es, pt-BR and zh

Footer at 375 px:

TBv3 footer at 375 px in en, es, pt-BR and zh

In context, English, on a source page, at 1280 px and 375 px:

Source page at 1280 px with the footer Source page at 375 px with the footer

  • All were taken from this branch, signed out, in headless Chrome against the staging API.
  • They use a production-length build string, so the TB version reads TB-3.0.17-alpha-<sha> rather than -dev.
  • The zh strips were taken with a two-line local guard in UserMenu.jsx that is not in this PR, because zh crashes the app on main too (see "Noticed, not fixed").

How to review it locally

This PR stays a draft until someone has done this.

gh pr checkout 'OpenConceptLab/ocl_issues#2899-footer-legal-links' --repo OpenConceptLab/oclweb3
npm ci
WEB_PORT=4002 NODE_ENV=development API_URL=https://api.staging.openconceptlab.org npm start

Pages to open at http://localhost:4002, signed out and then signed in:

  • / (dashboard)
  • /search/?q=malaria
  • /orgs/CIEL/
  • /orgs/CIEL/sources/CIEL/
  • a missing page such as /no-such-page/

Check in each language (switch with user menu → Language: English, Español, Português (Brasil)):

  • The footer starts with the two links, labelled as in the table above.
  • Each link opens the right openconceptlab.org page in a new tab, and the TBv3 tab stays where it was.
  • © 2026 Open Concept Lab, the TB- version and the API- version all still show.
  • At 375 px (devtools device mode), the links sit on a row of their own above the copyright and versions. Nothing is cut off at the bottom, and the page ends above the footer, not under it.
  • From 900 px up, the footer is a single row again.

zh has no entry in the language menu, and setting ocl.language to zh blanks the app on main as well. Check zh in the screenshots above, or apply the guard from "Noticed, not fixed" locally first.

How I tested it

CI gate. Every run: step of .github/workflows/pr.yml, in order, on Node 22.23.2: the start-prod.sh secret-variable check, npm ci and ESLint. All pass.

  • I ran it on a standalone copy of the branch, because this session's worktree sits inside another checkout of the repo, and ESLint would also have loaded that checkout's .eslintrc.json.
  • The first ESLint run caught a British spelling in a comment, which I fixed before committing.
  • On this PR, pr.yml's ESLint job passed on the final commit, 7e170c2: https://github.com/OpenConceptLab/oclweb3/actions/runs/38447139908

Browser check. Playwright drove headless Chrome in a fresh context, signed out, against npm start on the staging API, with ocl.language set before each load. It covered five pages (/, /search/?q=malaria, /orgs/CIEL/, /orgs/CIEL/sources/CIEL/, /no-such-page-2899/), four languages and two widths (1280 px and 375 px). On each it asserted that:

  • there is exactly one link to each URL, with target="_blank" and rel containing noopener and noreferrer;
  • the label matches the catalog and isn't clipped;
  • the footer text, the TB version and the API version are inside the viewport;
  • no footer text overlaps other footer text, and the page doesn't scroll sideways.

Results:

  • This branch: all pass, zh with the local guard.
  • main, the same script, en, es and pt-BR: 90 failures. There are no links on any page, and at 375 px the footer text and the TB version sit partly below the screen.
  • Other widths: 320, 600, 899, 900 and 1024 px, all four languages, / and /orgs/CIEL/sources/CIEL/. All pass: one row (30 px) from 900 px up, two rows (51 px) from 600 to 899 px, three rows (70 px) at 320 and 375 px.
  • Resizing one page: --footer-extra-height reads 0 px at 1280, 40 px at 375, 21 px at 899, then 0 px again at 900 and 1280.
  • Clicking each link in each language at both widths opens one new tab at the right URL with window.opener null, and the original tab doesn't navigate.

After the simplification cuts (17b3956) I reran the gate and the browser check: en, es and pt-BR on all five pages at both widths with the click test, zh with the local guard, and the resize check. All pass.

Signed in: not tested, because this run doesn't sign in. The footer is rendered in App.jsx outside the router's Switch, with no auth condition, and reads no user state.

Review

  • Codex pass 1 (codex-cli 0.162.1, commit 1de9d66): clean, so no further passes.
  • Simplification review: two cuts, both applied in 17b3956.
    • S1 dropped a null-ref guard that can't fire.
    • S2 wrote the two links out instead of mapping an array.
  • Codex check of the cuts (commit 17b3956): clean.
  • No finding is open.

Acceptance criteria

  • The footer (src/components/app/Footer.jsx) links Terms of Use to https://openconceptlab.org/terms-of-use and Privacy Policy to https://openconceptlab.org/privacy-policy, on every page, signed in or not.
    • The browser check covers five pages, four languages and two widths, signed out.
    • Signed in, the footer has no auth branch. The local review above covers it in a browser.
  • Both open in a new tab (target="_blank", rel="noopener noreferrer"): the attribute assertions and the click test.
  • The footer's existing text and app versions still show, at desktop and phone widths: the in-view and no-overlap assertions at 1280 and 375 px, plus 320 to 1024 px. At 375 px they were partly cut off before this change.
  • Both labels are in every locale (en, es, pt-BR, zh): the table above, with each one checked in the browser.
  • The PR stays a draft until it has been reviewed locally in a browser. This description has the steps and the 1280 px and 375 px screenshots. Left unticked until that review is done.

Docs

  • src/index.scss: the --app-height comment now names --footer-extra-height alongside --announcement-height.

  • README and docs/: neither covers the footer.

  • OCL Design System: its component list marks Footer as a stub with no guidance. This builds the footer from what exists:

    • MUI Link in the primary color, with the hover fix AnnouncementBanner uses;
    • the footer's existing 12 px text and dividers;
    • the banner's published-height pattern.

    Proposal: promote the footer, with its legal links and narrow-screen layout, to a documented component in the design system.

Not done, and why

  • No automated test. The repo has no test suite (pr.yml runs lint only), so the browser check above was run by hand, before and after the change.
  • Signed-in check. This run doesn't sign in.

Noticed, not fixed

  • zh blanks the app (on main too).
    • src/i18n/config.js accepts a stored zh, but LANGUAGES in src/common/constants.js has no zh entry.
    • So with ocl.language set to zh, UserMenu.jsx reads selectedLanguage.name and selectedLanguage.locale of undefined, and the whole app unmounts.
    • Either a guard (selectedLanguage?.name, selectedLanguage?.locale) or a zh entry in LANGUAGES would fix it. Which one depends on whether the menu should offer zh, so it's left out of this PR.
  • API version link color. It still uses Bootstrap's default link blue, while the new links use the primary token.
  • Long custom footer text. A long info.site.footerText in a server config can still overflow the single row on wide screens, as it could before.

About the screenshot commit

The screenshots are committed in 1de9d66 so this description can show them. The PR's last commit removes them, so they never reach the merged tree. A squash merge would also keep them out of main's history.

closes OpenConceptLab/ocl_issues#2899

🤖 Generated with Claude Code

https://claude.ai/code/session_01SDUDybmzSUtWMPsVozhSz3

Written by an Octoad session worker (worker-v0.11, recipe lean-loop, flow public>public, run openconceptlab-ocl-issues-2899-20261013-134559).

paynejd and others added 2 commits October 13, 2026 14:13
…licy from the footer

The footer links the Terms of Use and the Privacy Policy on
openconceptlab.org on every page, signed in or not, and each opens in a
new tab. Their labels are common.terms_of_use and common.privacy_policy,
in the en, es, pt-BR and zh catalogs.

Below MUI's md breakpoint the single row no longer fits in every language,
so the links take a row of their own and the copyright and versions wrap
under them. The footer publishes how much taller than its usual 30 px that
makes it (--footer-extra-height), and --app-height subtracts it, so pages
end above the footer rather than under it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SDUDybmzSUtWMPsVozhSz3
…request

The pull request's description shows these, linked to this commit. The
pull request's last commit removes them, so they never reach the merged
tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SDUDybmzSUtWMPsVozhSz3
@paynejd paynejd added origin/ai-generated Issue or PR created by an AI agent origin/session-worker Opened by the Octoad session worker; a run record backs it labels Oct 13, 2026

@paynejd paynejd left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex review, pass 1 (codex-cli 0.162.1, commit 1de9d66)

Clean.

findings.json
{
  "reviewer": "codex-cli 0.162.1",
  "perspective": "defects",
  "mode": "full",
  "commit": "1de9d6632ee193d07252fc4e0cb3add2f339470f",
  "verdict": "clean",
  "prior": [],
  "findings": []
}

@paynejd

paynejd commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

Response to Codex pass 1

No findings to answer. No P0 or P1 is open, so the Codex defect review stops after this pass, and the simplification review is next.

@paynejd paynejd left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Simplification review (commit 1de9d66)

2 items: 1 delete, 1 shrink.

  • S1 · delete · src/components/app/Footer.jsx:35 · The if (!el) return; guard in the layout effect can't fire. Footer has no early return and App.jsx mounts it unconditionally, so <footer ref={ref}> is always attached when the layout effect runs. The guard was copied from AnnouncementBanner.jsx, which needs it because that component returns null once dismissed. → Nothing replaces it. Delete the two lines and keep const el = ref.current;. (-2)
  • S2 · shrink · src/components/app/Footer.jsx:16 · LEGAL_LINKS, its .map, the React.Fragment keys and the i > 0 && divider make a data table and a loop for two fixed links. Writing the two Links out is shorter and needs no index logic. The output is the same DOM. → Replace the array with a shared sx constant; the Bootstrap comment moves onto it, and the 'On every page' comment stays: const LEGAL_LINK_SX = { whiteSpace: 'nowrap', '&:hover, &:focus': { color: 'primary.main' } };. Then the footer-legal div holds <Link href={TERMS_OF_USE_URL} target='_blank' rel='noopener noreferrer' underline='hover' sx={LEGAL_LINK_SX}>{t('common.terms_of_use')}</Link>, the existing <Divider orientation='vertical' className='footer-divider-vertical' />, and the same Link for PRIVACY_POLICY_URL with t('common.privacy_policy'). That is 8 lines where there were 16, and target and rel stay visible on each link for AC 2. (-8)
findings.json
{
  "reviewer": "claude subagent",
  "perspective": "simplify",
  "mode": "full",
  "commit": "1de9d6632ee193d07252fc4e0cb3add2f339470f",
  "verdict": "findings",
  "prior": [],
  "findings": [
    {
      "id": "S1",
      "severity": null,
      "tag": "delete",
      "file": "src/components/app/Footer.jsx",
      "line": 35,
      "claim": "The `if (!el) return;` guard in the layout effect can't fire. Footer has no early return and App.jsx mounts it unconditionally, so `<footer ref={ref}>` is always attached when the layout effect runs. The guard was copied from AnnouncementBanner.jsx, which needs it because that component returns null once dismissed.",
      "scenario": null,
      "action": "Nothing replaces it. Delete the two lines and keep `const el = ref.current;`.",
      "lines_delta": -2,
      "confidence": "high"
    },
    {
      "id": "S2",
      "severity": null,
      "tag": "shrink",
      "file": "src/components/app/Footer.jsx",
      "line": 16,
      "claim": "LEGAL_LINKS, its .map, the React.Fragment keys and the `i > 0 &&` divider make a data table and a loop for two fixed links. Writing the two Links out is shorter and needs no index logic. The output is the same DOM.",
      "scenario": null,
      "action": "Replace the array with a shared sx constant; the Bootstrap comment moves onto it, and the 'On every page' comment stays: `const LEGAL_LINK_SX = { whiteSpace: 'nowrap', '&:hover, &:focus': { color: 'primary.main' } };`. Then the footer-legal div holds `<Link href={TERMS_OF_USE_URL} target='_blank' rel='noopener noreferrer' underline='hover' sx={LEGAL_LINK_SX}>{t('common.terms_of_use')}</Link>`, the existing `<Divider orientation='vertical' className='footer-divider-vertical' />`, and the same Link for PRIVACY_POLICY_URL with t('common.privacy_policy'). That is 8 lines where there were 16, and target and rel stay visible on each link for AC 2.",
      "lines_delta": -8,
      "confidence": "medium"
    }
  ]
}

… drop a guard that can't fire

From the simplification review: the two legal links are written out with a
shared sx instead of mapped from an array, and the layout effect loses its
null-ref guard, since the footer element always renders.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SDUDybmzSUtWMPsVozhSz3
@paynejd

paynejd commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

Response to the simplification review

After the cuts, the pr.yml gate passes again: the start-prod.sh check, npm ci and ESLint. The headless browser check passes for en, es and pt-BR on all five pages at 1280 px and 375 px, including the new-tab clicks. --footer-extra-height still reads 0 px from 900 px up and 40 px at 375 px. A Codex check of the cuts is next.

@paynejd paynejd left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Codex check of the cuts (commit 17b3956)

Clean.

findings.json
{
  "reviewer": "codex-cli 0.162.1",
  "perspective": "defects",
  "mode": "cuts",
  "commit": "17b3956840c315f950461b2b34c095b39d313d78",
  "verdict": "clean",
  "prior": [],
  "findings": []
}

…eenshots from the tree

The pull request's description still shows them from 1de9d66. Removing
them here keeps them out of the merged tree.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SDUDybmzSUtWMPsVozhSz3
@paynejd

paynejd commented Oct 13, 2026

Copy link
Copy Markdown
Member Author

Needs you

The ticket keeps this PR a draft until someone reviews it locally in a browser, and that's the one step left.

Everything else is done:

  • Codex pass 1 and the Codex check of the cuts are clean.
  • Both simplification cuts are applied.
  • CI passes on 7e170c2.
  • No finding is open.

What's needed: follow "How to review it locally" in the description: signed out and signed in, on the five pages, at desktop width and at 375 px. If the footer looks right, mark the PR ready for review. If it doesn't, say what to change.

@paynejd
paynejd marked this pull request as ready for review October 14, 2026 01:34
@paynejd
paynejd merged commit 67dd5fa into main Oct 14, 2026
2 checks passed
@paynejd
paynejd deleted the OpenConceptLab/ocl_issues#2899-footer-legal-links branch October 14, 2026 01:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

origin/ai-generated Issue or PR created by an AI agent origin/session-worker Opened by the Octoad session worker; a run record backs it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TBv3: link the Terms of Use and Privacy Policy from the footer

1 participant