mirror of
https://github.com/netbirdio/dashboard.git
synced 2026-09-23 00:30:43 -07:00
* Replace the pending-approval error page with a dedicated screen A user waiting for an administrator to approve their account was shown the generic /error page: an error icon, a raw response_message dump and "if you believe this is an error, please contact your administrator". Waiting for approval is an expected part of signing up, so it now gets a screen of its own, following the onboarding layout of logo, centered card and a stepper placing the user between "Account Created" and "Join Account". The owner to contact is read out of the refusal management sends, which names a masked address, and the copy asks for the owner without naming one when the address is absent. Steps.Step gains an optional status and size, so a step can render as complete, current or upcoming at a larger scale. Steps that pass neither keep the look used by the instruction-list steppers. * Detect the pending-approval screen by test id in e2e The three places that recognise a pending user matched on "User Approval Pending", the heading the old error page rendered. The screen that replaced it has no heading, so the approval branch of the login races could never resolve and every leg timed out instead: both fixtures treat approval as a legitimate outcome, not a failure. They now key off the data-testid on the screen's root, which does not move when the copy does. * Point the pending-approval footer at the docs and support pages A mailbox asks the reader to compose something; the approval docs explain what they are waiting for, and the support page routes them the way every other NetBird surface does. The support address constant went with it. * Render the pending-approval screen from the caller-identity response Every call a pending user makes is refused, but only /users/current names the owner who can approve them. Redirecting to /error on whichever 403 landed first therefore threw that name away about as often as it kept it, and which screen appeared came down to a race. Pending users are no longer routed to /error at all. The provider that already waits on /users/current renders the screen from that response, which also ends the wait it could never finish: the caller identity it blocks on is exactly what such a user is refused. * Decide the refused-user screen from the responses, not the first 403 Routing on whichever refused call landed first meant the screen depended on a race, and once only /users/current could name the owner it meant the name was usually thrown away. Management older than that reports a pending user as merely blocked there, so it also meant a pending user landed on the blocked screen, which is what the e2e run caught. UserProfileProvider now decides from the responses it already waits on: /users/current first, since it alone names the owner, then the user list, which reports pending on every management version. Blocked keeps the error page, decided after pending so the older wording cannot win. * Wait for both refusal responses before choosing a screen Concluding blocked the moment /users/current said so meant concluding it while the user list was still in flight — and on management that reports a pending user as merely blocked there, that list is the one response that would have said otherwise. The screen it was meant to prevent was reachable again, just through a narrower window. The decision moves into resolveRefusedUser, which holds off while a better-informed response is still coming: the named one before the list, the list before blocked. Navigating to the error page becomes an effect rather than a call made while rendering. * Move the refused-user decision next to the provider that makes it It sat in utils only because two callers needed it, but the second one — the guard keeping these errors off the error boundary — needs nothing more than to recognise them, which is a line of its own. So the decision moves beside the provider that acts on it, and reads an error into a RefusalReason rather than assembling a discriminated union. The cost is that both files now spell out management's wording. The one here is the tested reading; the one in @utils/api only has to be no narrower than it. * Assert the refusal kind the resolver actually returns The result property is kind, not reason. toEqual types its argument loosely, so the stale expectations type-checked and only failed when run.