## Socket.IO broadcasts (core task):
- POST /users/:id/roles: uses .returning() to emit apps_changed only when row inserted
- DELETE /users/:id/roles/:roleName: uses .returning() to emit only when row deleted
- Added emitAppsChangedToRoleHolders(roleId) helper to realtime.ts
- New role-permission endpoints in roles.ts, emit only on effective changes:
- GET /roles/:id/permissions — list permissions for a role
- POST /roles/:id/permissions — assign; emits to role holders on actual insert
- DELETE /roles/:id/permissions/:permId — remove; emits only if row was deleted
## Bug fix: admin panel not showing disabled apps
- Added GET /api/admin/apps (requireAdmin) returning ALL apps from DB
- Updated admin.tsx: useQuery → /api/admin/apps with ADMIN_APPS_KEY constant
- All admin.tsx mutation handlers invalidate ADMIN_APPS_KEY
## UI fix: disabled app cards nearly invisible (opacity-60 on glass)
- Gray background + grayscale icon + muted text + stronger Disabled badge
Replit-Task-Id: f439ec75-bcd5-4030-8ee1-83a5d976f1a1
## DB: removed 11 duplicate rows from app_permissions table