feat: public projects view (Community Translation v1.0) - #3775
Merged
Anty0 merged 6 commits intoJul 1, 2026
Conversation
Contributor
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
Add a logged-out-capable /public-projects page listing all public projects with stats, search and paging, backed by a new anonymous GET /v2/public/projects/with-stats endpoint. - Backend: PublicProjectsController (anonymous, hidden from public docs); ProjectService.findAllPublicPaged + ProjectRepository.findAllPublic (reuses BASE_VIEW_QUERY with a NO_USER_ID sentinel; guards public / deleted / org-owner / base-language to keep the anonymous read side-effect-free); public flag exposed on ProjectWithStatsModel. - Frontend: /public-projects route, PublicProjectListView with PublicTopBar and a community-translation onboarding banner; shared ProjectsList extracted from ProjectListView; DashboardProjectListItem public variant (public badge + org line, member affordances suppressed, row click goes to login). - Tests: PublicProjectsControllerTest (anonymous/member/direct-permission joins, all guard filters, case-insensitive search) and a publicProjects.cy.ts E2E.
Bring the /public-projects Community Translation banner and search in line with the Figma final view (file H6BDCYRjNFgXymTtnkQriW). - Full-bleed banner with the Figma left-to-right pink gradient (background.default@12% -> primary.main@12% @69% -> background.default@12%), theme-token based so it also covers the dark variant. - New dev-mouse mascot exported from Figma node 573:6097 (public/images/communityMouse.svg), peeking from the banner bottom edge; hidden below the shared SPLIT_CONTENT_BREAK_POINT so it never overlaps text. - Search is left-anchored and shown only above the projects-page threshold (> 5 projects); visibility is latched so clearing a narrowed search cannot unmount the field mid-interaction. Deliberate Figma pixel/type values kept as literals (no matching MUI theme typography variant or spacing multiple; not worth one-off theme entries for this single banner), from nodes 573:5991 (banner) and 573:6097 (mouse): - eyebrow type: 24px / weight 600 / line-height 1.235 / letter-spacing 0.25px - heading type: 40px / weight 700 / line-height 1.167 / letter-spacing -1.5px (rendered as variant h1 for semantics; the visual is the Figma override) - banner text column max-width: 561px - mouse height: 190px; bullet icons: 21x22px - search field width: 220px (overrides SearchField's 200px default)
Address design feedback on the public-projects banner: - Add a consistent gap between the banner and the project list even when the search field is hidden (top padding moved onto the content column). - Let the mascot dangle below the banner's bottom edge (banner overflow no longer clipped; mouse bottom -20px) so its paws grip the edge, as in Figma. - Keep the "Help projects speak your language" heading on one line (the 561px cap now applies only to the subtext, not the whole text column).
Address design feedback on the public-projects banner (Figma 573:5991): - Add a consistent gap between the banner and the project list even when the search field is hidden (top padding moved onto the content column). - Let the mascot dangle below the banner's bottom edge (banner overflow no longer clipped; mouse bottom -15px) so its paws grip the edge, per Figma. - Keep the "Help projects speak your language" heading on one line: the 561px cap now applies only to the subtext; the heading flows to full width. - Reserve the mascot's horizontal zone (padding-right 260px, removed below the 900px breakpoint where the mouse is hidden) so long-locale headings wrap instead of rendering under the mouse. Deliberate Figma pixel values (no matching MUI theme token): mouse height 190px, mouse dangle offset -15px, mascot-zone reserve 260px, subtext max-width 561px.
The base now includes public badge + org on project rows (#3771) and community permissions for public projects (#3770), which overlap this PR: - Drop the duplicated `public` field on ProjectWithStatsModel and its assembler mapping (kept #3771's); the row now uses #3771's public badge + org name (data-cy project-list-public-badge), so the public-projects E2E asserts that hook. - DashboardProjectListItem keeps only the variant='public' behaviour on top of #3771's row: member affordances suppressed, whole-row click -> login. - Update PublicProjectsControllerTest for the community floor: a logged-in user with no role on a public project now gets VIEW (origin COMMUNITY) instead of NONE; anonymous callers still get NONE. - Regenerate apiSchema + dataCyType against the merged backend.
Anty0
force-pushed
the
jirikuchynka/public-projects-view
branch
from
July 1, 2026 19:43
d73514b to
7e57d4a
Compare
- Anonymous read is now structurally side-effect-free: getAllPublicWithStatistics is @transactional(readOnly = true), so the shared stats assembler cannot write even if the repository guard filter ever drifts (kept as defense in depth). - Remove the wildcard @crossorigin from the public endpoint; the webapp is same-origin and the public namespace now matches the rest of the API. - Auth-aware public row click: a logged-in visitor (who has at least the community VIEW permission on public projects, #3770) opens the project dashboard; only anonymous visitors are sent to login. - Rename the endpoint method to getAllPublicWithStatistics so it gets its own operationId and stops renumbering existing operations in the generated schema. - E2E: assert the public-variant affordance suppression (no row menu / QA badge / translations shortcut) and add a logged-in test (row click -> /projects/<id>). - Drop six comments that restated names/assertions/KDoc rationale. PublicTopBar deliberately copies the existing TopBar chrome (no matching MUI theme token): light-mode boxShadow rgba(0,0,0,0.02) 0px 4px 6px, toolbar padding 12.5px, Righteous logo at 20px/500. TopBar is coupled to the authenticated dashboard shell (scroll-hide, announcements, quick-start, notifications) so it cannot be reused as-is; a shared-shell extraction is a future follow-up if a third bar appears. Note: the anonymous stats payload still includes qaIssueCount/qaChecksStaleCount (standard ProjectWithStatsModel) for already-public projects; a leaner public stats model is a possible follow-up if that exposure is unwanted.
Anty0
added a commit
that referenced
this pull request
Jul 2, 2026
Adds a logged-out-capable /public-projects page listing all public projects (stats, search, paging) via a new anonymous GET /v2/public/projects/with-stats endpoint, reusing the project-list UI with a community-translation onboarding banner. - Backend: PublicProjectsController (anonymous, @OpenApiHideFromPublicDocs, @transactional(readOnly=true)); ProjectService.findAllPublicPaged (NO_USER_ID sentinel); ProjectRepository.findAllPublic (reuses BASE_VIEW_QUERY; guards public/deleted/org-owner/base-language to keep the anonymous read side-effect-free + community-permission floor via #3770). - Frontend: /public-projects route, PublicProjectListView + PublicTopBar + community-translation banner (Figma-matched gradient, dev-mouse mascot, left-anchored gated search); shared ProjectsList extracted from ProjectListView; DashboardProjectListItem variant='public' (member affordances suppressed, row click -> project when logged-in, else login). - Tests: PublicProjectsControllerTest (anonymous/member/community/direct-permission joins, guard filters, case-insensitive search) + publicProjects.cy.ts E2E.
Anty0
added a commit
that referenced
this pull request
Jul 2, 2026
Adds a logged-out-capable /public-projects page listing all public projects (stats, search, paging) via a new anonymous GET /v2/public/projects/with-stats endpoint, reusing the project-list UI with a community-translation onboarding banner. - Backend: PublicProjectsController (anonymous, @OpenApiHideFromPublicDocs, @transactional(readOnly=true)); ProjectService.findAllPublicPaged (NO_USER_ID sentinel); ProjectRepository.findAllPublic (reuses BASE_VIEW_QUERY; guards public/deleted/org-owner/base-language to keep the anonymous read side-effect-free + community-permission floor via #3770). - Frontend: /public-projects route, PublicProjectListView + PublicTopBar + community-translation banner (Figma-matched gradient, dev-mouse mascot, left-anchored gated search); shared ProjectsList extracted from ProjectListView; DashboardProjectListItem variant='public' (member affordances suppressed, row click -> project when logged-in, else login). - Tests: PublicProjectsControllerTest (anonymous/member/community/direct-permission joins, guard filters, case-insensitive search) + publicProjects.cy.ts E2E.
Anty0
added a commit
that referenced
this pull request
Jul 2, 2026
Adds a logged-out-capable /public-projects page listing all public projects (stats, search, paging) via a new anonymous GET /v2/public/projects/with-stats endpoint, reusing the project-list UI with a community-translation onboarding banner. - Backend: PublicProjectsController (anonymous, @OpenApiHideFromPublicDocs, @transactional(readOnly=true)); ProjectService.findAllPublicPaged (NO_USER_ID sentinel); ProjectRepository.findAllPublic (reuses BASE_VIEW_QUERY; guards public/deleted/org-owner/base-language to keep the anonymous read side-effect-free + community-permission floor via #3770). - Frontend: /public-projects route, PublicProjectListView + PublicTopBar + community-translation banner (Figma-matched gradient, dev-mouse mascot, left-anchored gated search); shared ProjectsList extracted from ProjectListView; DashboardProjectListItem variant='public' (member affordances suppressed, row click -> project when logged-in, else login). - Tests: PublicProjectsControllerTest (anonymous/member/community/direct-permission joins, guard filters, case-insensitive search) + publicProjects.cy.ts E2E.
Anty0
added a commit
that referenced
this pull request
Jul 3, 2026
Adds a logged-out-capable /public-projects page listing all public projects (stats, search, paging) via a new anonymous GET /v2/public/projects/with-stats endpoint, reusing the project-list UI with a community-translation onboarding banner. - Backend: PublicProjectsController (anonymous, @OpenApiHideFromPublicDocs, @transactional(readOnly=true)); ProjectService.findAllPublicPaged (NO_USER_ID sentinel); ProjectRepository.findAllPublic (reuses BASE_VIEW_QUERY; guards public/deleted/org-owner/base-language to keep the anonymous read side-effect-free + community-permission floor via #3770). - Frontend: /public-projects route, PublicProjectListView + PublicTopBar + community-translation banner (Figma-matched gradient, dev-mouse mascot, left-anchored gated search); shared ProjectsList extracted from ProjectListView; DashboardProjectListItem variant='public' (member affordances suppressed, row click -> project when logged-in, else login). - Tests: PublicProjectsControllerTest (anonymous/member/community/direct-permission joins, guard filters, case-insensitive search) + publicProjects.cy.ts E2E.
Anty0
added a commit
that referenced
this pull request
Jul 3, 2026
Adds a logged-out-capable /public-projects page listing all public projects (stats, search, paging) via a new anonymous GET /v2/public/projects/with-stats endpoint, reusing the project-list UI with a community-translation onboarding banner. - Backend: PublicProjectsController (anonymous, @OpenApiHideFromPublicDocs, @transactional(readOnly=true)); ProjectService.findAllPublicPaged (NO_USER_ID sentinel); ProjectRepository.findAllPublic (reuses BASE_VIEW_QUERY; guards public/deleted/org-owner/base-language to keep the anonymous read side-effect-free + community-permission floor via #3770). - Frontend: /public-projects route, PublicProjectListView + PublicTopBar + community-translation banner (Figma-matched gradient, dev-mouse mascot, left-anchored gated search); shared ProjectsList extracted from ProjectListView; DashboardProjectListItem variant='public' (member affordances suppressed, row click -> project when logged-in, else login). - Tests: PublicProjectsControllerTest (anonymous/member/community/direct-permission joins, guard filters, case-insensitive search) + publicProjects.cy.ts E2E.
Anty0
added a commit
that referenced
this pull request
Jul 3, 2026
Adds a logged-out-capable /public-projects page listing all public projects (stats, search, paging) via a new anonymous GET /v2/public/projects/with-stats endpoint, reusing the project-list UI with a community-translation onboarding banner. - Backend: PublicProjectsController (anonymous, @OpenApiHideFromPublicDocs, @transactional(readOnly=true)); ProjectService.findAllPublicPaged (NO_USER_ID sentinel); ProjectRepository.findAllPublic (reuses BASE_VIEW_QUERY; guards public/deleted/org-owner/base-language to keep the anonymous read side-effect-free + community-permission floor via #3770). - Frontend: /public-projects route, PublicProjectListView + PublicTopBar + community-translation banner (Figma-matched gradient, dev-mouse mascot, left-anchored gated search); shared ProjectsList extracted from ProjectListView; DashboardProjectListItem variant='public' (member affordances suppressed, row click -> project when logged-in, else login). - Tests: PublicProjectsControllerTest (anonymous/member/community/direct-permission joins, guard filters, case-insensitive search) + publicProjects.cy.ts E2E.
Anty0
added a commit
that referenced
this pull request
Jul 14, 2026
Adds a logged-out-capable /public-projects page listing all public projects (stats, search, paging) via a new anonymous GET /v2/public/projects/with-stats endpoint, reusing the project-list UI with a community-translation onboarding banner. - Backend: PublicProjectsController (anonymous, @OpenApiHideFromPublicDocs, @transactional(readOnly=true)); ProjectService.findAllPublicPaged (NO_USER_ID sentinel); ProjectRepository.findAllPublic (reuses BASE_VIEW_QUERY; guards public/deleted/org-owner/base-language to keep the anonymous read side-effect-free + community-permission floor via #3770). - Frontend: /public-projects route, PublicProjectListView + PublicTopBar + community-translation banner (Figma-matched gradient, dev-mouse mascot, left-anchored gated search); shared ProjectsList extracted from ProjectListView; DashboardProjectListItem variant='public' (member affordances suppressed, row click -> project when logged-in, else login). - Tests: PublicProjectsControllerTest (anonymous/member/community/direct-permission joins, guard filters, case-insensitive search) + publicProjects.cy.ts E2E.
Anty0
added a commit
that referenced
this pull request
Jul 15, 2026
Adds a logged-out-capable /public-projects page listing all public projects (stats, search, paging) via a new anonymous GET /v2/public/projects/with-stats endpoint, reusing the project-list UI with a community-translation onboarding banner. - Backend: PublicProjectsController (anonymous, @OpenApiHideFromPublicDocs, @transactional(readOnly=true)); ProjectService.findAllPublicPaged (NO_USER_ID sentinel); ProjectRepository.findAllPublic (reuses BASE_VIEW_QUERY; guards public/deleted/org-owner/base-language to keep the anonymous read side-effect-free + community-permission floor via #3770). - Frontend: /public-projects route, PublicProjectListView + PublicTopBar + community-translation banner (Figma-matched gradient, dev-mouse mascot, left-anchored gated search); shared ProjectsList extracted from ProjectListView; DashboardProjectListItem variant='public' (member affordances suppressed, row click -> project when logged-in, else login). - Tests: PublicProjectsControllerTest (anonymous/member/community/direct-permission joins, guard filters, case-insensitive search) + publicProjects.cy.ts E2E.
Anty0
added a commit
that referenced
this pull request
Jul 16, 2026
- ✅ **Public project toggle** — `public` flag on `Project` (`is_public`
column + Liquibase migration), exposed on `ProjectModel`/`ProjectDto`;
owner-only `PUT /v2/projects/{projectId}/publishing` (gated by
`PROJECT_EDIT` + `checkUserIsOwnerOrServerAdmin`, activity-logged,
cache-evicting); confirmation-guarded "Public project" switch in
advanced settings.
- ✅ **Community permission** (#3770) — authenticated users on a public
project get a computed community floor (read subset of VIEW +
`translations.suggest` + `translation-comments.add`, all languages;
origin `COMMUNITY`) without a stored `Permission` row, so they
contribute without taking a paid seat. Direct/org permissions and
admin/supporter elevation layer on top, so no authenticated user is
below a non-member; anonymous users get nothing. Member emails + member
count are redacted behind `MEMBERS_VIEW` (REST + websocket);
out-of-surface endpoints are gated (MT credits →
`ORGANIZATION_QUOTAS_VIEW`, import-settings → `TRANSLATIONS_EDIT`
(mirrors the import menu gate `translations.edit` + `keys.view`),
branch-merge → `BRANCH_MANAGEMENT`, blocking-tasks →
task-view-or-assigned).
- ✅ **List-row public indicator** (#3771) — public projects show a
second line under the project name in the shared list row
(`DashboardProjectListItem`): an outlined `public` badge + organization
name. Adds `public` to `ProjectWithStatsModel` + assembler. Sole
indicator (no project-detail icon).
- ✅ **Suggestion resolution split** (#3776) — splits the single
suggestion Accept into "Accept only" (`declineOther=false`, leaves the
competing suggestions active) and "Accept and decline others"
(`declineOther=true`, prior behavior). Inline row actions are ✓
Accept-only and ✕ Decline; an `⋮` overflow menu holds
Accept-and-decline-others and Delete (own suggestions only).
Frontend-only — the backend already supported `declineOther`. Optimistic
cache keeps the surviving sibling visible until refetch
(vitest-covered). Two new i18n keys created in "Tolgee itself".
- ✅ **Suggestion moderation** (#3785) — new
`translation-suggestions.manage` scope lets project members delete
suggestions authored by other users (previously author-only). Own
per-language dimension (`suggestManageLanguages`, new join table +
Liquibase migration); `Suggest` and `Manage` are grouped under a new
"Suggestions" permission category, each with its own language selector.
Granted to the EDIT/MANAGE presets; the suggestion list endpoint accepts
`suggest` OR `manage` so moderators see what they can act on. Deletion
is language-restricted like the sibling suggestion actions;
decline/accept stay on `translations.state-edit`. New i18n keys in
"Tolgee itself".
- ✅ **Suggestion display** (#3774) — translations read view embeds up to
3 active suggestions per cell (was 1), newest-first via a windowed top-N
query (cap `MAX_DISPLAYED_SUGGESTIONS=3`, `created_at desc, id desc`);
the existing `+N` overflow badge is the "more" affordance (no separate
button). Stays capped at 3 across the editor open/close, mutate, and
suggest-create paths; `+N` restyled as an inline circle on the last
suggestion row.
- ✅ **Public projects view** (#3775) — logged-out-capable
`/public-projects` page listing all public projects (stats, paged +
search) via anonymous `GET /v2/public/projects/with-stats`
(`@Transactional(readOnly=true)`, filters guard the anonymous read so it
is side-effect-free; the community-permission floor from #3770 applies
for logged-in visitors). Reuses the projects-list UI (shared
`ProjectsList` extracted from `ProjectListView`); community-translation
banner (Figma-matched gradient + dev-mouse mascot + Suggest/Comment
bullets, no Vote), left-anchored search shown only above a >5 project
threshold, `LOG IN`/`SIGN UP` top bar, no org switcher.
`DashboardProjectListItem` gains `variant='public'`: member affordances
(⋮ menu / QA / translations) suppressed, whole-row click opens the
project when logged-in (community VIEW) else routes to login.
- ✅ **Projects-page redesign** (#3777) — projects list header
redesigned: centered "Projects" title with the org switcher relocated
beside it (`standaloneTitle` hides the separator, adds top spacing,
stacks search on its own row); every non-list page keeps the top-left
switcher. Synthetic "Community translation" switcher entry navigates to
`/community-projects` (does not switch org) — **implemented but
temporarily disabled pending contributor tracking** (its e2e tests are
skipped with a re-enable TODO). `/community-projects` page reuses the
projects-list shell, ignores the selected org, lists all public
projects, email-verification gated, with the community-translation
banner on top (**diverges from the pitch's "no banner"** — designer
request). `DashboardProjectListItem` gains a public grid variant that
drops the controls column entirely — no reserved space (default title
width 150→180px, smaller breakpoints unchanged). New i18n keys in
"Tolgee itself".
- ✅ **Community access to public-project orgs** (#3792) — opening a
public project auto-switches the preferred org; for non-members this
previously 403'd because org access only recognized stored
membership/permission rows. Extends the org view floor
(`canUserViewStrictOrPublic` = direct permission / stored org role
**OR** the org owns a publicly visible project) so community and
direct-permission users can view the org and set it as preferred —
**without a dedicated role** (an earlier GUEST-role iteration was
removed; below-member users hold no role). Anything beyond viewing still
requires `MEMBER+`. Reduced preferred-org model below MEMBER
(`activeCloudSubscription` hidden; new
`PrivateOrganizationModel.limitedView` drives the community project-list
view). Guest-visible surface scoped to accessible projects — org GETs
(role `null`), project listings, languages/base-languages, glossary
reads incl. CSV export (one canonical
`ProjectRepository.BELOW_MEMBER_ACCESSIBLE_PROJECT` predicate),
llm-providers (name/type/token-credit pricing only), `/leave`.
Preference healing writes on stale/missing preference; below-member
viewers excluded from member listings, seat stats, and assignee search.
**Billing repo should audit its org-scoped `@UseDefaultPermissions`
endpoints for the widened floor.**
## ⚠️ Breaking changes
- `GET /v2/projects/{id}/machine-translation-credit-balance` now
requires `ORGANIZATION_QUOTAS_VIEW`; granular permissions / API keys
without it get 403 and must be re-granted.
- Branch-merge session endpoints now require `BRANCH_MANAGEMENT`.
## Deliberate inline style literals (no matching MUI theme token)
These Figma-exact values are kept inline on purpose — single-use
surfaces with no matching
`theme` token; not folded into `theme.typography`/`theme.spacing`:
- **CommunityTranslationBanner** (one-off marketing surface): eyebrow
24px/600/lh1.235/ls0.25px;
heading 40px/700/lh1.167/-1.5px (rendered as `h1` for semantics, visual
is the Figma override);
subtext max-width 561px; mascot-zone reserve padding-right 260px; mouse
height 190px / bottom -15px;
bullet icons 21×22px; gradient stop 69%.
- **SuggestionsFirst** show-all button: 13px / letter-spacing 0.46px
(matches `TrialChip.tsx`) / 6px vertical padding.
- **DashboardProjectListItem** `StyledPublicChip` label: 13px.
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
## Summary
* **New Features**
* Added public/community projects listing with search, pagination, and
stats.
* Added project publishing toggle to mark projects public/private.
* Extended permissions with `translation-suggestions.manage`
(per-language suggestion management) and `organization-quotas.view`.
* **Bug Fixes**
* Improved privacy redaction for usernames/emails across REST responses
and realtime activity.
* Refined public/community permission behavior and member visibility
(e.g., hidden member counts when not allowed).
* **Tests**
* Expanded API, UI, and permissions test coverage for public/community
access, publishing, masking, and suggestion-permission rules.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part of the Community Translation v1.0 pitch (#3763). Stacked on #3765 (public project toggle).
What
A logged-out-capable
/public-projectspage listing all public projects (stats, search, paging), backed by a new anonymous endpoint.Backend
GET /v2/public/projects/with-stats—PublicProjectsController, no auth (/v2/public/**is permitAll),@OpenApiHideFromPublicDocs(UI-backing).ProjectService.findAllPublicPagedresolves the caller viaauthenticatedUserOrNull?.id ?: NO_USER_ID(sentinel) so anonymous callers match no permission/role rows →NONE.ProjectRepository.findAllPublicreusesBASE_VIEW_QUERY;WHEREguardspublic/deletedAt/organizationOwner/baseLanguage/bl.deletedAtto keep the anonymous read side-effect-free (no base-language write-on-read, no assembler NPE).publicflag added toProjectWithStatsModel.Frontend
/public-projectsroute (renders for logged-out visitors).PublicProjectListView+PublicTopBar(logo, language menu, log in / sign up) + a community-translation onboarding banner.ProjectsListextracted fromProjectListView(no behavior change to the authenticated list).DashboardProjectListItemgains apublicvariant: public badge + org line (also lights up the normal list's indicator for public projects), member-only affordances suppressed, whole-row click → login.Tests
PublicProjectsControllerTest— anonymous / org-member / direct-permission joins, every guard filter (write-on-read proven via DB re-read), case-insensitive search.publicProjects.cy.tsE2E — banner + login/sign-up, public rows with badge+org, private hidden, search narrows, row → login.Verified locally: backend tests,
tsc/eslint, and the Cypress E2E (run via the dev path) all green.