From 14aee356cf1b0132eb5a8be63d8182f01662353f Mon Sep 17 00:00:00 2001 From: tegwick Date: Sun, 27 Sep 2026 01:46:41 +0200 Subject: [PATCH] Rename Suspend to Suspend access and Make user to Change role. Assistant: grok Assistant-Session: 01a0d25d-d358-7e13-b84a-d007fbb7e34f --- src/user_engine/web.py | 50 ++++++++++++++++++++++++++++++++---------- tests/test_web.py | 7 ++++++ 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/user_engine/web.py b/src/user_engine/web.py index fa9008d..c1de182 100644 --- a/src/user_engine/web.py +++ b/src/user_engine/web.py @@ -2071,10 +2071,18 @@ Use the login name they provide; it may differ from your display name.

str: actions = "" if invitation.status.value == "pending": - actions = f"""
-
-
-
""" + invite_root = f"/admin/{escape(tenant)}/invitations/{escape(invitation.invitation_id)}" + hidden = f'' + actions = ( + f'
{hidden}' + f'' + f'
' + f'

Sends this invitation again.

' + f'
{hidden}' + f'' + f'
' + f'

Stops this invitation. The person can no longer accept it.

' + ) expires = invitation.expires_at.isoformat() if invitation.expires_at else "—" events = [e for e in self.service.store.outbox_history() if e.tenant == tenant and e.aggregate_id == invitation.invitation_id and e.event_type in {"family_invitation.created", "family_invitation.resent", "family_member.invited"}] @@ -2092,9 +2100,16 @@ Use the login name they provide; it may differ from your display name.

str: + def form(action: str, label: str, help_text: str, **fields: str) -> str: hidden = "".join(f'' for k,v in fields.items()) - return f'
{hidden}
' + help_id = f"help-{action}-{membership.user_id}" + return ( + f'
' + f'{hidden}' + f'' + f'
' + f'

{escape(help_text)}

' + ) login = (f'

Login name: {escape(self._directory_login(directory.subject))}

' '

Pass this name on with the sign-in address. It is not the email address, ' 'and signing in by email does not work.

' @@ -2102,13 +2117,23 @@ Use the login name they provide; it may differ from your display name.

Password and authenticator status are not available here.

') if directory else '

Login not created. Prepare a login before asking this person to sign in.

' actions = "" if not inactive: - actions += form("provision", "Create password setup link" if directory else "Create login") - actions += form("status", "Reactivate" if inactive else "Suspend", status="active" if inactive else "suspended") + if directory: + actions += form("provision", "Create password setup link", "Makes a new single-use link so this person can set a password for the existing login. It does not change the login name.") + else: + actions += form("provision", "Create login", "Creates the directory login. The login name comes from the email name. It is not the display name, and this page cannot rename it afterwards.") + if inactive: + actions += form("status", "Reactivate", "Lets this person use this tenant again. It does not change the login name or the password.", status="active") + else: + actions += form("status", "Suspend access", "Stops this person using this tenant. The login and any other tenant stay as they are.", status="suspended") if status != AccountStatus.DISABLED: - actions += form("remove", "Remove account") + actions += form("remove", "Remove account", "Disables this person's account for this tenant. The shared login is kept.") next_role = "user" if membership.kind == "tenant-admin" else "tenant-admin" - actions += form("role", "Make user" if next_role == "user" else "Make tenant administrator", role=next_role) - if platform_operator: actions += form("recover", "Restore tenant account") + if next_role == "user": + actions += form("role", "Change role", "This person administers the tenant. This changes them to an ordinary user of this tenant. It does not change the login name.", role=next_role) + else: + actions += form("role", "Make tenant administrator", "This person is an ordinary user. This lets them manage the users of this tenant.", role=next_role) + if platform_operator: + actions += form("recover", "Restore tenant account", "Turns this tenant account back on. It creates a directory login only when one is missing. It does not reset a password, remove an authenticator, or change the login name.") return (f'{escape(user.display_name or membership.user_id) if user else escape(membership.user_id)}' f'{escape(user.primary_email or "") if user else ""}{escape(membership.kind)}' f'{escape(status.value)} for this tenant{login}{actions}') @@ -2489,9 +2514,12 @@ h1{{font:clamp(2.2rem,7vw,5.5rem)/.98 Georgia,serif;max-width:13ch}}a{{color:var table{{width:100%;border-collapse:collapse;background:#fff}}th,td{{padding:.75rem;text-align:left;border-bottom:1px solid var(--line)}} section{{margin:2rem 0}}form{{display:grid;gap:.8rem;max-width:42rem}}label{{display:grid;gap:.25rem}} label:has(input[type=checkbox]){{display:flex;align-items:center;gap:.6rem}} +td form{{display:flex;align-items:center;flex-wrap:wrap;gap:.4rem;max-width:none}} input,select,button{{font:inherit;padding:.65rem}} input[type=checkbox]{{width:1.15rem;height:1.15rem;margin:0;padding:0;flex:none}} button{{background:var(--accent);color:white;border:0;border-radius:.3rem;cursor:pointer}} +button.help{{background:transparent;color:var(--accent);border:1px solid var(--line);padding:.15rem .45rem;min-width:1.8rem}} +[popover]{{border:1px solid var(--line);background:#fff;color:var(--ink);padding:1rem;max-width:24rem;border-radius:.35rem}} a:focus-visible,input:focus-visible,select:focus-visible,button:focus-visible{{outline:3px solid #e59f24;outline-offset:3px}}@media(max-width:640px){{body{{font-size:16px}}table{{display:block;overflow-x:auto}}}}
NetKingdom Identity{_ACCOUNT_NAVIGATION.get()}
{body}
""" diff --git a/tests/test_web.py b/tests/test_web.py index 485ba53..4f8c9df 100644 --- a/tests/test_web.py +++ b/tests/test_web.py @@ -979,6 +979,13 @@ class PortalApplicationTests(unittest.TestCase): self.assertEqual("200 OK", admin_page["status"]) self.assertIn(b"Lifecycle diagnostics", html) self.assertIn(b"Restore tenant account", html) + self.assertIn(b">Suspend access", html) + self.assertIn(b">Change role", html) + self.assertNotIn(b">Make user", html) + self.assertNotIn(b">Suspend", html) + self.assertIn(b'popovertarget="help-role-', html) + self.assertIn(b"This person administers the tenant.", html) + self.assertIn(b"It does not change the login name.", html) recovered, _ = invoke_confirmed( self.app, f"/admin/tenant:friendly:browser/users/{memberships[0].user_id}/recover",