fix(ui/invite): make pending-approval admin guidance non-clickable (#6786)
## Thinking Path
> - Paperclip orchestrates AI agents for zero-human companies, and
humans onboard into companies via invite links.
> - Some invite types (`company_join` with
`requires_company_admin_approval`) require an admin to approve the join
request after the invitee submits it.
> - While the requester waits, the invitee sees the
`AwaitingJoinApprovalPanel` in `InviteLanding`, which describes where
the admin needs to go to approve the request.
> - That panel rendered the destination — "Company Settings → Access" —
as two clickable `<a href="/company/settings/access">` links, even
though the surrounding copy is plainly addressed to the admin ("Ask
**them** to visit ..."), not the requester.
> - First-time invitees naturally click the only underlined link on the
screen, are sent to `/company/settings/access`, hit the "No company
access" panel (they have no membership yet), and conclude the invite
flow is broken.
> - This PR removes the navigation by rendering both "Company Settings →
Access" references as plain styled text (`<p>` / `<span>`), so the
guidance stays visible but cannot be followed by the requester.
> - The benefit is that the post-submit invite experience matches the
copy's intent — guidance for the admin, not navigation for the
requester.
Fixes #6784.
## What Changed
- `ui/src/pages/InviteLanding.tsx` — In `AwaitingJoinApprovalPanel`,
replace the two `<a href={approvalUrl}>Company Settings → Access</a>`
elements with `<p>` and `<span>` containing the same text. Remove the
now-unused `approvalUrl` constant.
- `ui/src/pages/InviteLanding.test.tsx` — Update the existing "pending
approval page" test: it previously asserted two anchor tags pointing at
`/company/settings/access`; it now asserts **zero** anchors while the
text "Company Settings → Access" still appears twice (in the "Approval
page" box and inline in the "Ask them to visit ..." sentence). Renamed
the test description from "...linked access instructions" to
"...non-clickable access instructions" to reflect the contract.
## Verification
```
pnpm vitest run ui/src/pages/InviteLanding.test.tsx
```
Result: 8 / 8 pass, including the updated "shows the pending approval
page with the company icon and non-clickable access instructions" case.
Manual reproduction (master @ `242a2c2f`,
`deploymentMode=authenticated`, `bind=lan`, embedded Postgres):
1. As instance admin, generate a `company_join` invite that requires
admin approval.
2. In a fresh browser profile, open the invite link.
3. Fill **Create your account** and submit.
4. The "Request to join \<company\>" panel appears.
5. Hover the "Company Settings → Access" mentions — no underline, no
link cursor; clicking does nothing. The text is still readable and the
surrounding copy ("Ask them to visit ...") still conveys the instruction
to the requester.
Before / after screenshots: see issue #6784 — the "before" state lands
users on `/company/settings/access` which renders "No company access".
After this PR the guidance is informational only.
## Risks
Low. UI-only change confined to one function in `InviteLanding.tsx` plus
its matching test. No API contracts, routes, or data shapes are
modified. The removed `approvalUrl` constant was only referenced by the
two anchor elements.
## Model Used
- Anthropic Claude Opus 4.7 (`claude-opus-4-7`, 1M context, extended
thinking enabled).
- Tools: file editing, Bash, Playwright reproduction against a
self-hosted Paperclip instance running master @ `242a2c2f`, and the
Paperclip monorepo's own Vitest suite for verifying the test update.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] If this change affects the UI, I have included before/after
screenshots (will attach in PR thread)
- [x] I have updated relevant documentation to reflect my changes (no
docs files needed updating)
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
This commit is contained in:
parent
68ba7ccae6
commit
c1f6698ec5
|
|
@ -506,7 +506,7 @@ describe("InviteLandingPage", () => {
|
|||
});
|
||||
});
|
||||
|
||||
it("shows the pending approval page with the company icon and linked access instructions", async () => {
|
||||
it("shows the pending approval page with the company icon and non-clickable access instructions", async () => {
|
||||
acceptInviteMock.mockResolvedValue({
|
||||
id: "join-1",
|
||||
companyId: "company-1",
|
||||
|
|
@ -553,14 +553,18 @@ describe("InviteLandingPage", () => {
|
|||
expect(container.querySelector('img[alt="Acme Robotics logo"]')).not.toBeNull();
|
||||
expect(container.textContent).not.toContain("http://localhost/company/settings/members");
|
||||
|
||||
const approvalLinks = Array.from(container.querySelectorAll("a")).filter(
|
||||
// The "Company Settings → Members" guidance addresses the company admin,
|
||||
// not the requester. It must render as plain text so the requester cannot
|
||||
// navigate themselves to /company/settings/members — a route they have no
|
||||
// permission to view, which renders a misleading "No company access"
|
||||
// panel and makes the invite flow look broken. See #6784.
|
||||
const approvalAnchors = Array.from(container.querySelectorAll("a")).filter(
|
||||
(link) => link.textContent === "Company Settings → Members",
|
||||
);
|
||||
expect(approvalLinks).toHaveLength(2);
|
||||
const expectedApprovalUrl = `${window.location.origin}/company/settings/members`;
|
||||
for (const link of approvalLinks) {
|
||||
expect(link.getAttribute("href")).toBe(expectedApprovalUrl);
|
||||
}
|
||||
expect(approvalAnchors).toHaveLength(0);
|
||||
const approvalMentions =
|
||||
container.textContent?.match(/Company Settings → Members/g) ?? [];
|
||||
expect(approvalMentions).toHaveLength(2);
|
||||
|
||||
await act(async () => {
|
||||
root.unmount();
|
||||
|
|
|
|||
|
|
@ -160,7 +160,6 @@ function AwaitingJoinApprovalPanel({
|
|||
claimApiKeyPath = null,
|
||||
onboardingTextUrl = null,
|
||||
}: AwaitingJoinApprovalPanelProps) {
|
||||
const approvalUrl = `${window.location.origin}/company/settings/members`;
|
||||
const approverLabel = invitedByUserName ?? "A company admin";
|
||||
|
||||
return (
|
||||
|
|
@ -181,15 +180,10 @@ function AwaitingJoinApprovalPanel({
|
|||
</p>
|
||||
<div className="border border-zinc-800 p-3">
|
||||
<p className="text-xs text-zinc-500 mb-1">Approval page</p>
|
||||
<a
|
||||
href={approvalUrl}
|
||||
className="text-sm text-zinc-200 underline underline-offset-2 hover:text-zinc-100"
|
||||
>
|
||||
Company Settings → Members
|
||||
</a>
|
||||
<p className="text-sm text-zinc-200">Company Settings → Members</p>
|
||||
</div>
|
||||
<p className="text-sm text-zinc-400">
|
||||
Ask them to visit <a href={approvalUrl} className="text-zinc-200 underline underline-offset-2 hover:text-zinc-100">Company Settings → Members</a> to approve your request.
|
||||
Ask them to visit <span className="text-zinc-200">Company Settings → Members</span> to approve your request.
|
||||
</p>
|
||||
<p className="text-xs text-zinc-500">
|
||||
Refresh this page after you've been approved — you'll be redirected automatically.
|
||||
|
|
|
|||
Loading…
Reference in New Issue