Repository navigation
Conversation
P2-T02 (pages 01 and 19). The account menu from the avatar: identity, Profile & preferences, the account-menu.plan-billing slot for cloud, Help & docs and Sign out. Sign out POSTs /api/v1/logout with the CSRF token, resets analytics and opens the returned URL; if the POST fails it falls back to the GET logout. No "Switch to old version" item (D14). The organisation switcher from the pill: search, a row per organisation with role, member count and region (absent fields left out), the current one marked, a confirmation before switching, the one-organisation and member states, and a load error with retry. After a switch the app does a full load of the same section in the new organisation, else its dashboard. Create organisation is left for a decision (plan gap 6). lib/http.ts is the shared client: session cookie, X-CSRFToken on unsafe methods. Invisible until the shell (P2-T01) renders these components. Co-Authored-By: Claude Opus 5.5 <[email protected]>
… has no name Found on the dev preview: a user without a name got an empty title line above the e-mail. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…i 5.2 both declare containSubset Co-Authored-By: Claude Opus 5.5 <[email protected]>
… per-browser preference Co-Authored-By: Claude Opus 5.5 <[email protected]>
…tore Co-Authored-By: Claude Opus 5.5 <[email protected]>
…in three modes Co-Authored-By: Claude Opus 5.5 <[email protected]>
… match the v1 Tailwind root Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
…t it Co-Authored-By: Claude Opus 5.5 <[email protected]>
…ercase letters only Co-Authored-By: Claude Opus 5.5 <[email protected]>
…heme mapping and the focus utility Co-Authored-By: Claude Opus 5.5 <[email protected]>
…so biome ci passes src/v1/styles/tokens.css is written by scripts/build-v1-tokens.mjs and checked byte-for-byte by styles/tokens.test.ts; the formatter would rewrap it and break the check. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
…e and guard the CSRF cookie signOut now throws when the reply has no redirect URL so the GET logout fallback runs. signOutAndLeave in services/session.ts owns the whole sequence (POST, analytics reset, navigate, fallback) and AccountMenu calls it. A malformed CSRF cookie sends no header instead of throwing. The account trigger shows one letter, and the Help & docs link announces that it opens in a new tab. Co-Authored-By: Claude Opus 5.5 <[email protected]>
A switch in flight can no longer be cancelled, a 412 from the list sends the user to sign-in, an empty search says so, the dialog's from side comes from the current organisation, and overlapping list loads keep only the latest. The loading text is a live region and the current row carries aria-current. The Switch button gets the arrows icon, and avatars show two letters for a one-word name while the pill shows one. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
…sts and on missing variants Co-Authored-By: Claude Opus 5.5 <[email protected]>
…and portal sync Co-Authored-By: Claude Opus 5.5 <[email protected]>
…tEffect Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
…line styles, CSS imports and !important fail lint Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
… arbitrary values Co-Authored-By: Claude Opus 5.5 <[email protected]>
[Medium impact] The PR appears safe to merge, with one remaining non-blocking issue: reopening the switcher does not refresh memberships.
|
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Dismissed because a newer commit was pushed; Greptile will re-review the current head.
The dev-spec board sets shadow-md for dropdown menus and shadow-lg for popovers. The account menu is a dropdown menu. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Dismissed because a newer commit was pushed; Greptile will re-review the current head.
…est-branch history as a parent
ritwik-g
left a comment
There was a problem hiding this comment.
Thanks, Athul. This is close. Two things need fixing before merge: the README merge loss (#1) and the 412 handling (#2). The rest are small.
The account menu now matches the #2466 select panel exactly: shadow-md, rounded-lg, border, bg-popover and p-1.5. The organisation switcher panel and the confirm dialog do not follow the same scale yet (#7).
On the spec:
- Leaving out "Create organisation" matches the later Figma note on page 19: "The product cannot create an organisation, so the dropdown does not offer it." The spec page still lists it, so it's worth asking Hari to update it.
- The E2E acceptance item (switch and sign out) has to land with the PR that mounts these components (P2-T01).
Ran locally: typecheck:v1 and lint:v1 are clean, and the 65 tests in this PR pass.
…2, return focus, handle a removed organisation - Restore the v1 README's Plugins section, lost in the #2394 merges. - A 412 with a code goes to /error?code=&domain= as v0 does; an empty body still goes to /landing. - Closing the confirm dialog puts focus back on the organisation pill. - A 403 on switch says access was removed, keeps Switch disabled and reloads the list; other failures are logged. - Loading and load errors use Skeleton and InlineAlert; the dialog error uses InlineAlert. - Switcher panel shadow-md (dropdown), confirm dialog shadow-xl (dialog). - Log when sign-out falls back to the GET logout. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Dismissed because a newer commit was pushed; Greptile will re-review the current head.
|
Review body: asked Hari separately about dropping "Create organisation" from the P2-T02 page (draft below). E2E for switch and sign out lands with P2-T01. |
…reload The switch dialog captures the current organisation when Switch is clicked, so the list reload after a 403 cannot blank its role and member count. A test rejects with an axios 500 to pin the 403-only branch. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Dismissed because a newer commit was pushed; Greptile will re-review the current head.
vishnuszipstack
left a comment
There was a problem hiding this comment.
Review of PR 2395 — UN-4290 Add the v1 account menu and organisation switcher
Mode: INITIAL, at the PR's current head. Reviewed head 85797a2d116bdc24f74c7e7544fac2f94ec6016e, merge base 5cc28d787200f53196f14c238ef894310dbffb39, base freshened (base_fetch: ok), 10 files, 1535 lines (matches GitHub). Rubric: unstract plugin v2.1.80.
How this was done: a full fan-out (code reviewer, silent-failure hunter, type design, comments, lifecycle and a whole-diff cold read) ran at an earlier head a29fc096a. Before posting, every finding was re-checked against this head. Findings already fixed, already raised by another reviewer, or wrong on a second read were dropped (14 dropped: 9 already raised by ritwik-g and fixed in cacc2e2, 1 fixed, 4 withdrawn). The author's own commits since a29fc096a were reviewed for new defects, and the tests were re-run here.
Security deep-dive: degraded — the vendored security prompt ran once at this head, its filter step inline instead of as independent passes; no findings at confidence >= 8.
Verdict — APPROVE WITH NITS
Summary — Critical: 0 · High: 0 · Medium: 1 · Low: 7 · Lenses run: 19/19 (1 not covered, 6 N/A; see checklist).
The earlier rounds' fixes hold at this head, and cacc2e2 and f37d0d9 add no new defects. One robustness gap is left: the organisation list reply is trusted without a shape check, and a wrong body takes the top bar down instead of showing the alert. The rest are Lows: v1 conventions (icon roles, keeping URLs and status codes in services/), two edge cases in the panel copy, and a test that cannot fail. Tests at this head, from frontend/: the shell, organisation and service tests pass (4 files, 70/70); typecheck:v1 and lint:v1 pass; biome ci is clean on the 10 changed files. The full test:v1 suite and E2E were not run, because nothing mounts these components in a route yet.
Findings
Posted inline on the diff; index:
-
- [Medium] [Lens 3, 8] — A 200 reply without an
organizationsarray crashes the top bar —frontend/src/v1/services/organizations.ts:12
- [Medium] [Lens 3, 8] — A 200 reply without an
-
- [Low] [Lens 8] — The shared client has no timeout, so a hung switch locks the dialog —
frontend/src/v1/lib/http.ts:27
- [Low] [Lens 8] — The shared client has no timeout, so a hung switch locks the dialog —
-
- [Low] [Lens 4] — The CSRF header is attached whatever the request's origin —
frontend/src/v1/lib/http.ts:29
- [Low] [Lens 4] — The CSRF header is attached whatever the request's origin —
-
- [Low] [Lens 13] — The "outside the current organisation's paths" test never reaches the org check —
frontend/src/v1/lib/organizations.test.ts:104
- [Low] [Lens 13] — The "outside the current organisation's paths" test never reaches the org check —
-
- [Low] [Lens 2] — The shell imports Lucide glyphs directly instead of by role from
lib/icons.ts—frontend/src/v1/shell/OrganizationSwitcher.tsx:21
- [Low] [Lens 2] — The shell imports Lucide glyphs directly instead of by role from
-
- [Low] [Lens 3] — The panel contradicts itself when the current organisation is not in the list —
frontend/src/v1/shell/OrganizationSwitcher.tsx:345
- [Low] [Lens 3] — The panel contradicts itself when the current organisation is not in the list —
-
- [Low] [Lens 7, 16] — Region codes are shown raw, but the backend contract says the client maps them —
frontend/src/v1/lib/organizations.ts:36
- [Low] [Lens 7, 16] — Region codes are shown raw, but the backend contract says the client maps them —
-
- [Low] [Lens 2] — URL and HTTP-status handling sit in
shell/, notservices/—frontend/src/v1/shell/OrganizationSwitcher.tsx:84
- [Low] [Lens 2] — URL and HTTP-status handling sit in
Lens checklist
- Spec & intent — Clean
- Architectural fit & precedent — See findings #5, #8
- Correctness & edge cases — See findings #1, #6
- Security — See findings #3
- Data integrity & migrations — N/A — no persisted data or schema
- Concurrency — Clean
- API & contract compatibility — See findings #7
- Reliability & resilience — See findings #1, #2
- Performance & cost — Clean
- Observability — Clean
- Operational safety — N/A — the components are not mounted in any route yet; no flag or rollout config
- LLM/agent-specific — N/A — no model, prompt or agent path
- Testing — See findings #4
- Dependencies & build — N/A — no dependency or lockfile change
- Code quality — Clean
- Doc & comment accuracy — See findings #7
- Cross-document rule consistency — Not covered — merge-result sweep unavailable: git 2.34 here, the script needs 2.38
- On-prem deployment experience — N/A — no chart, values or on-prem build path touched
- Scaling & workload lifecycle — N/A — no worker or scaling config
| export async function listOrganizations(): Promise<Organization[]> { | ||
| const { data } = await http.get<{ organizations: Organization[] }>( | ||
| "/api/v1/organization", | ||
| ); | ||
| return data.organizations; | ||
| } |
There was a problem hiding this comment.
1. [Medium] [Lens 3, 8] — A 200 reply without an organizations array crashes the top bar
- Location:
frontend/src/v1/services/organizations.ts:L12-L17 listOrganizationsreturnsdata.organizationswithout checking it. A 200 with some other body resolves toundefined. That can come from a proxy or SPA fallback servingindex.html, or from a backend shape change. The switcher then stores{status: "ready", organizations: undefined}, andorganizations.filter(...)atOrganizationSwitcher.tsx:L270throws during render. The top bar goes down when it should show the "Could not load your organisations." alert with Retry.signOutin this same PR already validates its reply shape, so the convention is there. Fix:if (!Array.isArray(data?.organizations)) throw new Error("Organization list reply carried no organizations");. Add a service test that a{}body rejects.- Verification: VERIFIED — read at this head:
listOrganizationsreturnsdata.organizationsunchecked andOrganizationSwitcher.tsx:L270calls.filteron it
There was a problem hiding this comment.
Fixed in e05f055a4: listOrganizations now throws "Organization list reply carried no organizations" when the body has no array, so the switcher shows its load error with Retry. Service tests cover {}, {organizations: null} and an HTML body, and a switcher test checks the alert and Retry.
| * than through axios's xsrf options, which only the browser adapter applies. | ||
| * ponytail: no 401 or error-toast handling yet; add it when a page needs it. | ||
| */ | ||
| export const http = axios.create({ withCredentials: true }); |
There was a problem hiding this comment.
2. [Low] [Lens 8] — The shared client has no timeout, so a hung switch locks the dialog
- Location:
frontend/src/v1/lib/http.ts:L27 axios.create({ withCredentials: true })leavestimeoutat 0, which means no client-side limit. WhilePOST /organization/<id>/setis pending,ConfirmSwitchhasbusyset. Cancel is disabled, and Escape and overlay clicks are ignored (OrganizationSwitcher.tsx:L174-L176,L227). If the request never settles, only a reload gets the user out. Sign-out is similar: the GET fallback insignOutAndLeaveruns only on rejection, so a hung POST makes "Sign out" look dead. Any upstream proxy timeout would turn this into a 504, so the impact depends on the deployment. A defaulttimeoutof 15 to 30 s onhttpturns a hang into a rejection, and both paths already handle that.- Verification: UNVERIFIED — read only; does any proxy in front of these calls impose its own timeout?
There was a problem hiding this comment.
Left as is in this PR. UN-4313 replaces lib/http.ts with the shared client in services/http.ts right after this merges, and its owner asked that nothing new build on this file. The timeout is passed to that ticket's owner to carry into the shared client.
| http.interceptors.request.use((config) => { | ||
| const token = cookie("csrftoken"); | ||
| if ( | ||
| token !== undefined && | ||
| !SAFE_METHODS.has((config.method ?? "get").toLowerCase()) | ||
| ) { | ||
| config.headers.set("X-CSRFToken", token); | ||
| } | ||
| return config; | ||
| }); |
There was a problem hiding this comment.
3. [Low] [Lens 4] — The CSRF header is attached whatever the request's origin
- Location:
frontend/src/v1/lib/http.ts:L29-L38 - The interceptor adds
X-CSRFTokento every unsafe request and checks only the method. The comment says axios's xsrf options were bypassed on purpose, but those options also apply a same-origin check, and this replacement drops it. Every current caller uses a relative URL, so nothing leaks today. This is documented as "the one HTTP client v1 services use", though. The first service that POSTs to an absolute third-party URL through it will send the Django CSRF token to that host. Skip the header unlessnew URL(config.url ?? "", config.baseURL ?? window.location.origin).origin === window.location.origin, and add a test that a cross-origin POST carries no header. - Verification: VERIFIED — read at this head; no current caller is cross-origin
There was a problem hiding this comment.
Left as is in this PR for the same reason: the shared client from UN-4313 replaces lib/http.ts and already owns CSRF handling. The same-origin check, with its cross-origin test, is passed to that ticket's owner.
| test("falls back to the dashboard outside the current organisation's paths", () => { | ||
| expect(landingPath("/landing", "zipstack", "tanushree")).toBe( | ||
| "/tanushree/dashboard", | ||
| ); | ||
| }); |
There was a problem hiding this comment.
4. [Low] [Lens 13] — The "outside the current organisation's paths" test never reaches the org check
- Location:
frontend/src/v1/lib/organizations.test.ts:L104-L108 - The only input is
/landing, which has one segment, sosectionis undefined and the result is the dashboard either way. Deleteorg === fromOrg &&fromlandingPath(lib/organizations.ts:L78) and the suite still passes. That was mutation-verified at the old head, and the test is unchanged at the new one. A switch started from/<other>/<sub>would then land on/<toOrg>/<sub>. AddlandingPath("/otherorg/workflows", "zipstack", "tanushree")->"/tanushree/dashboard". The Help link has a similar gap: the AccountMenu test checks onlyhref. Iftarget="_blank"(AccountMenu.tsx:L34) were lost, the sr-only "(opens in a new tab)" text would be wrong and no test would fail. - Verification: VERIFIED — mutation run at the earlier head (removing
org === fromOrg &&stays green); the test is unchanged here
There was a problem hiding this comment.
Fixed in e05f055a4: added landingPath("/otherorg/workflows", "zipstack", "tanushree") -> /tanushree/dashboard, which fails with org === fromOrg && removed. The AccountMenu test now asserts the Help link's target="_blank" and rel="noreferrer" next to the sr-only text, and fails if they are dropped.
| import { | ||
| ArrowRightLeft, | ||
| ChevronRight, | ||
| ChevronsUpDown, | ||
| Search, | ||
| } from "lucide-react"; |
There was a problem hiding this comment.
5. [Low] [Lens 2] — The shell imports Lucide glyphs directly instead of by role from lib/icons.ts
- Location:
frontend/src/v1/shell/OrganizationSwitcher.tsx:L21-L26 - The v1 README says "Shared glyphs come by role from
lib/icons.ts".SHELL_ICONSalready definesorgSwitcher(ChevronsUpDown),search(Search),accountMenu(ChevronDown) andsignOut(LogOut), andACTION_ICONShasnext(ChevronRight) andopenExternal(ExternalLink). Both shell files import those glyphs fromlucide-reactdirectly (alsoAccountMenu.tsx:L5-L11).check-v1-iconsdoes not catch this. These are the first shell components, so a glyph swap in the role map will skip the top bar, and later authors get a precedent for going around the map. Use the roles. Keep direct imports only forArrowRightLeft,CircleUserRoundandCircleQuestionMark, which have no role. - Verification: VERIFIED — read at this head against
lib/icons.ts
There was a problem hiding this comment.
Fixed in e05f055a4: both shell files take orgSwitcher, search, accountMenu, signOut, next and openExternal from SHELL_ICONS / ACTION_ICONS. Only ArrowRightLeft, CircleUserRound and CircleQuestionMark, which have no role, are imported from lucide-react.
| {shown.length === 0 ? ( | ||
| <p className="ui:px-3 ui:py-4 ui:text-body-small ui:text-muted-foreground"> | ||
| No organisations match | ||
| </p> | ||
| ) : ( | ||
| <p className="ui:px-1.5 ui:pt-1 ui:pb-2 ui:text-overline ui:uppercase ui:text-muted-foreground"> | ||
| You belong to {count} organisation{count === 1 ? "" : "s"} | ||
| </p> | ||
| )} | ||
| <ul className="ui:flex ui:min-h-0 ui:flex-col ui:overflow-y-auto"> | ||
| {shown.map((org) => { | ||
| const isCurrent = org.id === current.id; | ||
| return ( | ||
| <li | ||
| key={org.id} | ||
| {...(isCurrent ? { "aria-current": "true" } : {})} | ||
| className={`ui:flex ui:h-14 ui:items-center ui:gap-2.5 ui:rounded-md ui:px-2.5 ${isCurrent ? "ui:bg-muted" : ""}`} | ||
| > | ||
| <OrgAvatar name={org.display_name} size="md" /> | ||
| <OrgLine org={org} /> | ||
| {isCurrent ? ( | ||
| <span className="ui:rounded-sm ui:bg-primary-muted ui:px-2 ui:py-0.75 ui:text-caption ui:font-medium ui:text-primary"> | ||
| Current | ||
| </span> | ||
| ) : ( | ||
| <button | ||
| type="button" | ||
| aria-label={`Switch to ${org.display_name}`} | ||
| className={`${BUTTON} ui:px-2.5`} | ||
| onClick={() => | ||
| setPending({ | ||
| from: { | ||
| ...organizations.find( | ||
| (o) => o.id === current.id, | ||
| ), | ||
| id: current.id, | ||
| name: current.name, | ||
| display_name: current.displayName, | ||
| }, | ||
| to: org, | ||
| }) | ||
| } | ||
| > | ||
| <ArrowRightLeft aria-hidden className="ui:size-4" /> | ||
| Switch | ||
| </button> | ||
| )} | ||
| </li> | ||
| ); | ||
| })} | ||
| </ul> | ||
| {count === 1 && ( | ||
| <p className="ui:px-3 ui:pt-1.5 ui:text-caption ui:text-muted-foreground"> | ||
| Nothing to switch to. | ||
| </p> | ||
| )} |
There was a problem hiding this comment.
6. [Low] [Lens 3] — The panel contradicts itself when the current organisation is not in the list
- Location:
frontend/src/v1/shell/OrganizationSwitcher.tsx:L345-L400 - The dialog is built to handle a current organisation that is missing from the list:
fromcomes fromcurrentprops, and a test covers that case. Take that case with one other organisation listed. The panel says "You belong to 1 organisation", shows a Switch row, and also shows "Nothing to switch to." In fact the user belongs to two. "Nothing to switch to." is keyed oncount === 1, not on there being no row other thancurrent.id. Also, with an empty list and an empty query, the panel says "No organisations match". Fix: base "Nothing to switch to." onorganizations.every((o) => o.id === current.id), and show "No organisations match" only whenquery.trim() !== "". - Verification: VERIFIED — traced by hand through the panel's render branches at this head
There was a problem hiding this comment.
Fixed in e05f055a4: "Nothing to switch to." now shows when every listed organisation is the current one, "No organisations match" only for a non-empty query, and the count includes a current organisation missing from the list. New tests cover the current organisation missing from a list with one other, and an empty list with an empty query.
| member_count === undefined | ||
| ? undefined | ||
| : `${member_count} member${member_count === 1 ? "" : "s"}`; | ||
| return [roleLabel(role), members, region].filter(Boolean).join(" · "); |
There was a problem hiding this comment.
7. [Low] [Lens 7, 16] — Region codes are shown raw, but the backend contract says the client maps them
- Location:
frontend/src/v1/lib/organizations.ts:L36 backend/account_v2/ReadMe.mddocumentsregionas a code (US,EU;STAGING,DEVorINTEGRATIONoutside production) and says "The client maps it to a label."describeOrganizationadds the code to the line as is. On cloud's non-production deployments, rows then read "Admin · 3 members · INTEGRATION". Either map codes to labels (an unknown code can fall back to the raw value), or add a comment that showing the code is the intended v1 behaviour and update the ReadMe line.- Verification: VERIFIED — read against
backend/account_v2/ReadMe.mdat this head
There was a problem hiding this comment.
v1 keeps showing the code. In e05f055a4 a comment on describeOrganization says so, and the region row in backend/account_v2/ReadMe.md now says the client shows the code as is.
| // 412: the backend logged the session out while building the list. | ||
| if (axios.isAxiosError(error) && error.response?.status === 412) { | ||
| window.location.assign(sessionEndedPath(error.response.data)); | ||
| return; | ||
| } |
There was a problem hiding this comment.
8. [Low] [Lens 2] — URL and HTTP-status handling sit in shell/, not services/
- Location:
frontend/src/v1/shell/OrganizationSwitcher.tsx:L84-L88 - The README folder table says
services/is "the only place a URL appears". The switcher importsaxiosto readerror.response?.statusfor the 412 and the 403. It holdsSIGN_IN_PATH = "/landing"(L34) and builds/error?code=…itself, andAccountMenu.tsx:L15holdsHELP_URL. A 412 meaning "the session was logged out", and a 403 on/setmeaning "access removed", are facts of the backend contract, soservices/organizations.tsshould own them. Have it return or throw a typed outcome, so the next caller oflistOrganizationsorswitchOrganizationdoes not copy this status logic. These are the first shell modules, so they set the pattern. - Verification: VERIFIED — read at this head against the v1 README folder table
There was a problem hiding this comment.
Fixed in e05f055a4: services/organizations.ts turns the 412 on the list and the 403 on set into an OrganizationError with kind session-ended or access-removed, and the switcher no longer imports axios. SIGN_IN_PATH, the /error?code= path and HELP_URL stay in shell/, since they are app routes and a docs link, not API URLs.
| if (open && state.status !== "ready") { | ||
| load(); |
There was a problem hiding this comment.
Once the list loads, reopening the switcher never refreshes it because state.status stays "ready". If someone adds the user to another organisation while this page stays open, it will not appear until a full page reload. Refresh the list when the switcher reopens, or give the cached list an expiry.
Prompt To Fix With AI
This is a comment left during a code review.
Path: frontend/src/v1/shell/OrganizationSwitcher.tsx
Line: 279-280
Comment:
**New organisations stay hidden**
Once the list loads, reopening the switcher never refreshes it because `state.status` stays `"ready"`. If someone adds the user to another organisation while this page stays open, it will not appear until a full page reload. Refresh the list when the switcher reopens, or give the cached list an expiry.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.… copy, icon roles - listOrganizations rejects a 200 whose body has no organizations array, so the switcher shows its load error with Retry instead of crashing. - services/organizations.ts reads the 412 on the list (session ended) and the 403 on set (access removed) and throws an OrganizationError with a kind; the switcher no longer imports axios. - "Nothing to switch to." shows when every listed organisation is the current one; "No organisations match" shows only for a non-empty query. - Shell glyphs with a role come from SHELL_ICONS / ACTION_ICONS. - Tests: landingPath from another organisation's path, the Help link's target and rel, the new service and panel cases. - Region codes are shown as sent; the comment and account_v2 ReadMe say so. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Dismissed because a newer commit was pushed; Greptile will re-review the current head.
|



What
account-menu.plan-billingslot that cloud fills.Why
How
POST /logout, resets analytics and opens the URL the backend returns (the identity provider's logout). If the POST fails it falls back to the GET logout, which still ends the session.lib/http.tsis a small axios client with the CSRF header. It is a stand-in for P0-T05 (UN-4313, the shared HTTP client) and is replaced when that lands.#v1-portalcontainer) when those land.Can this PR break any existing features. If yes, please list possible items. If no, please explain why. (PS: Admins do not merge the PR without this section filled)
frontend/src/v1/; nothing in v0 imports them, and no route renders them until the v1 shell (P2-T01) mounts them.Database Migrations
Env Config
Relevant Docs
83e4fff.Related Issues or PRs
Dependencies Versions
Notes on Testing
bun run typecheck:v1,lint:v1,test:v1: clean, 82 vitest tests.main, i.e. after UN-4270 [FEAT] v1 scaffold, strict TypeScript and import boundary #2380 and UN-4286 [FEAT] Add the v1 plugin manifest, slots and build check #2394 merge. The commands above were run locally./landingwith the session gone (after unstract-cloud#1899). No console errors.Screenshots
To add from the dev run.
Checklist
I have read and understood the Contribution Guidelines.
🤖 Generated with Claude Code