Add admin enhancements: impersonation, plan management, org status enforcement
Enhancement 1 - Block suspended/archived org access: - Add org status check in switchOrganization() (auth.service.ts) - Filter suspended/archived orgs from login response (generateTokenResponse) - Add org status guard with 60s cache in TenantMiddleware - Frontend: filter orgs in SelectOrgPage, add 403 handler in api.ts Enhancement 2 - Change tenant plan level: - Add updatePlanLevel() to organizations.service.ts - Add PUT /admin/organizations/:id/plan endpoint - Frontend: clickable plan dropdown in Organizations table + confirmation modal - Plan level Select in tenant detail drawer Enhancement 3 - User impersonation: - Add impersonateUser() to auth.service.ts with impersonatedBy JWT claim - Add POST /admin/impersonate/:userId endpoint - Frontend: Impersonate button in Users tab (disabled for admins) - Impersonation state management in authStore (start/stop/persist) - Orange impersonation banner in AppLayout header with stop button Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -49,6 +49,11 @@ export function SelectOrgPage() {
|
||||
},
|
||||
});
|
||||
|
||||
// Filter out suspended/archived organizations (defense in depth)
|
||||
const activeOrganizations = (organizations || []).filter(
|
||||
(org: any) => !org.status || !['suspended', 'archived'].includes(org.status),
|
||||
);
|
||||
|
||||
const handleSelect = async (org: any) => {
|
||||
try {
|
||||
const { data } = await api.post('/auth/switch-org', {
|
||||
@@ -90,8 +95,15 @@ export function SelectOrgPage() {
|
||||
Choose an HOA to manage or create a new one
|
||||
</Text>
|
||||
|
||||
<Stack mt={30}>
|
||||
{organizations.map((org) => (
|
||||
{/* Filter out suspended/archived orgs (defense in depth — backend also filters) */}
|
||||
{organizations.length > activeOrganizations.length && (
|
||||
<Alert icon={<IconAlertCircle size={16} />} color="yellow" variant="light" mt="md">
|
||||
Some organizations are currently suspended or archived and are not shown.
|
||||
</Alert>
|
||||
)}
|
||||
|
||||
<Stack mt={organizations.length > activeOrganizations.length ? 'sm' : 30}>
|
||||
{activeOrganizations.map((org) => (
|
||||
<Card
|
||||
key={org.id}
|
||||
shadow="sm"
|
||||
|
||||
Reference in New Issue
Block a user