diff --git a/pages/admin/tenants/data().php b/pages/admin/tenants/data().php
index 4965bf7..bf2f8cf 100644
--- a/pages/admin/tenants/data().php
+++ b/pages/admin/tenants/data().php
@@ -1,7 +1,6 @@
computeCounts(
$tenantRows,
$userTenantRepository->countUsersByTenantIds(...),
$userTenantRepository->countActiveUsersByTenantIds(...)
);
- $tenantIds = array_keys($userCounts);
- $tenantSsoMap = $tenantMicrosoftAuthRepository->listByTenantIds($tenantIds);
-
$defaultTenantId = $settingsDefaultsGateway->getDefaultTenantId();
$rows = [];
foreach ($tenantRows as $row) {
$tenantId = (int) ($row['id'] ?? 0);
- $tenantTheme = strtolower(trim((string) ($row['default_theme'] ?? '')));
- $themeIsTenantOverride = $tenantTheme !== '' && isset($themes[$tenantTheme]);
- $resolvedThemeKey = $themeIsTenantOverride ? $tenantTheme : $globalDefaultTheme;
- $resolvedThemeLabel = (string) ($themes[$resolvedThemeKey] ?? ucfirst($resolvedThemeKey));
-
- $themePolicyRaw = $row['allow_user_theme'] ?? null;
- $themePolicyMode = 'inherit';
- if ($themePolicyRaw !== null && $themePolicyRaw !== '') {
- $normalized = strtolower(trim((string) $themePolicyRaw));
- if (in_array($normalized, ['1', 'true', 'yes', 'on'], true)) {
- $themePolicyMode = 'allow';
- } elseif (in_array($normalized, ['0', 'false', 'no', 'off'], true)) {
- $themePolicyMode = 'disallow';
- }
- }
-
- $tenantSso = $tenantSsoMap[$tenantId] ?? null;
- $ssoEnabled = !empty($tenantSso['enabled']);
- $ssoMicrosoftOnly = $ssoEnabled && !empty($tenantSso['enforce_microsoft_login']);
$counts = $userCounts[$tenantId] ?? ['active_users' => 0, 'inactive_users' => 0];
$tenantStatus = TenantStatus::normalizeOr((string) ($row['status'] ?? ''), TenantStatus::Active);
$tenantUuid = (string) ($row['uuid'] ?? '');
@@ -75,20 +46,7 @@ $fetchRows = static function (
'status' => $tenantStatus->value,
'status_badge' => $tenantStatus->badgeVariant(),
'status_label' => t($tenantStatus->labelToken()),
- 'theme' => [
- 'key' => $resolvedThemeKey,
- 'label' => t($resolvedThemeLabel),
- 'is_override' => $themeIsTenantOverride ? 1 : 0,
- 'policy_mode' => $themePolicyMode,
- ],
- 'sso' => [
- 'enabled' => $ssoEnabled ? 1 : 0,
- 'microsoft_only' => $ssoMicrosoftOnly ? 1 : 0,
- ],
- 'active_users' => $counts['active_users'],
- 'inactive_users' => $counts['inactive_users'],
- 'created' => dt($row['created'] ?? ''),
- 'modified' => dt($row['modified'] ?? ''),
+ 'total_users' => $counts['active_users'] + $counts['inactive_users'],
'has_avatar' => $tenantUuid !== '' && $tenantAvatarService->hasAvatar($tenantUuid),
];
}
@@ -96,13 +54,6 @@ $fetchRows = static function (
return $rows;
};
-$themes = appThemes();
-$globalDefaultTheme = $settingsAppGateway->getAppTheme();
-if ($globalDefaultTheme === null || !isset($themes[$globalDefaultTheme])) {
- $envTheme = strtolower(trim((string) (getenv('APP_THEME') ?: 'light')));
- $globalDefaultTheme = isset($themes[$envTheme]) ? $envTheme : 'light';
-}
-
$listOptions = [
'limit' => $filters['limit'],
'offset' => $filters['offset'],
@@ -131,25 +82,10 @@ if (in_array($order, $computedOrderKeys, true)) {
'order' => 'description',
'dir' => 'asc',
]);
- $rows = $fetchRows($fullResult['rows'] ?? [], $themes, $globalDefaultTheme);
+ $rows = $fetchRows($fullResult['rows'] ?? []);
- usort($rows, static function (array $a, array $b) use ($order, $dir): int {
- $valueA = '';
- $valueB = '';
- if ($order === 'theme') {
- $valueA = strtolower((string) ($a['theme']['label'] ?? ''));
- $valueB = strtolower((string) ($b['theme']['label'] ?? ''));
- } elseif ($order === 'sso') {
- $valueA = ((int) $a['sso']['enabled'] * 10) + (int) $a['sso']['microsoft_only'];
- $valueB = ((int) $b['sso']['enabled'] * 10) + (int) $b['sso']['microsoft_only'];
- } elseif ($order === 'inactive_users') {
- $valueA = (int) $a['inactive_users'];
- $valueB = (int) $b['inactive_users'];
- } else {
- $valueA = (int) $a['active_users'];
- $valueB = (int) $b['active_users'];
- }
- $cmp = $valueA <=> $valueB;
+ usort($rows, static function (array $a, array $b) use ($dir): int {
+ $cmp = ((int) $a['total_users']) <=> ((int) $b['total_users']);
if ($cmp === 0) {
$cmp = strcasecmp((string) $a['description'], (string) $b['description']);
}
@@ -157,7 +93,7 @@ if (in_array($order, $computedOrderKeys, true)) {
});
$rows = array_slice($rows, $filters['offset'], $filters['limit']);
} else {
- $rows = $fetchRows($result['rows'] ?? [], $themes, $globalDefaultTheme);
+ $rows = $fetchRows($result['rows'] ?? []);
}
gridJsonDataResult($rows, $total);
diff --git a/pages/admin/tenants/filter-schema.php b/pages/admin/tenants/filter-schema.php
index 977ec32..2879938 100644
--- a/pages/admin/tenants/filter-schema.php
+++ b/pages/admin/tenants/filter-schema.php
@@ -18,7 +18,7 @@ return gridFilterSchema([
'status' => ['type' => 'enum', 'allowed' => TenantStatus::values(), 'default' => '', 'lowercase' => true],
'order' => [
'type' => 'order',
- 'allowed' => ['description', 'status', 'theme', 'sso', 'active_users', 'inactive_users', 'created', 'modified'],
+ 'allowed' => ['description', 'users'],
'default' => 'description',
],
'dir' => ['type' => 'dir', 'default' => 'asc'],
diff --git a/pages/admin/tenants/index(default).phtml b/pages/admin/tenants/index(default).phtml
index 31ce279..2a75936 100644
--- a/pages/admin/tenants/index(default).phtml
+++ b/pages/admin/tenants/index(default).phtml
@@ -44,17 +44,8 @@ $pageConfig = [
'gridLang' => $gridLang,
'labels' => [
'avatar' => t('Avatar'),
- 'description' => t('Description'),
- 'status' => t('Status'),
- 'theme' => t('Theme'),
- 'sso' => t('SSO'),
- 'activeUsers' => t('Active users'),
- 'inactiveUsers' => t('Inactive users'),
- 'created' => t('Created'),
- 'active' => t('Active'),
- 'default' => t('Default'),
- 'microsoftOnly' => t('Microsoft only'),
- 'inactive' => t('Inactive'),
+ 'tenant' => t('Tenant'),
+ 'users' => t('Users'),
],
];
?>
diff --git a/web/css/vendor-overrides/gridjs.css b/web/css/vendor-overrides/gridjs.css
index b42a1df..ccdbe32 100644
--- a/web/css/vendor-overrides/gridjs.css
+++ b/web/css/vendor-overrides/gridjs.css
@@ -203,6 +203,21 @@
text-overflow: ellipsis;
}
+ .grid-tenant-profile {
+ display: inline-flex;
+ align-items: center;
+ gap: 0.75rem;
+ min-width: 0;
+ }
+
+ .grid-tenant-profile-name-text {
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ font-weight: 500;
+ }
+
.gridjs-loading-bar:after {
background-image: linear-gradient(
90deg,
diff --git a/web/js/pages/admin-tenants-index.js b/web/js/pages/admin-tenants-index.js
index de010d6..2578051 100644
--- a/web/js/pages/admin-tenants-index.js
+++ b/web/js/pages/admin-tenants-index.js
@@ -1,5 +1,5 @@
import { createListPageModule } from '../core/app-list-page-module.js';
-import { badgeHtml, escapeHtml } from './app-list-utils.js';
+import { escapeHtml } from './app-list-utils.js';
createListPageModule({
configId: 'admin-tenants-index',
@@ -7,54 +7,19 @@ createListPageModule({
missingGridMessage: 'Tenants grid init failed: Grid.js missing',
initErrorMessage: 'Tenants grid init failed',
setup: ({ config, gridjs, appBase, initListPage }) => {
- const gridSearch = config.gridSearch ?? null;
const filterSchema = config.filterSchema ?? [];
const filterChipMeta = config.filterChipMeta ?? [];
const labels = config.labels ?? {};
- const tenantUuidIndex = 8;
- const tenantDescriptionIndex = 1;
- const formatBadge = (value) => badgeHtml(gridjs, value);
+ const tenantUuidIndex = 2;
const initialsForRow = (row) => {
- const descriptionCell = row?.cells?.[tenantDescriptionIndex]?.data;
+ const descriptionCell = row?.cells?.[0]?.data;
const label = typeof descriptionCell === 'object' && descriptionCell !== null
? String(descriptionCell.label ?? '')
: String(descriptionCell ?? '');
const initial = label.trim().charAt(0).toUpperCase();
return initial || '?';
};
- const formatStatus = (value) => {
- if (!value || typeof value !== 'object') {
- return gridjs.html('-');
- }
- const variant = String(value.variant || 'neutral');
- const label = String(value.label || '-');
- return gridjs.html(`${label}`);
- };
- const formatTheme = (cell) => {
- if (!cell || typeof cell !== 'object') {
- return gridjs.html('');
- }
- const themeLabel = escapeHtml(cell.label ?? '');
- const label = cell.is_override ? themeLabel : `${themeLabel} (${escapeHtml(labels.default || 'Default')})`;
- return gridjs.html(label);
- };
- const formatSso = (cell) => {
- if (!cell || typeof cell !== 'object') {
- return gridjs.html('');
- }
- if (!cell.enabled) {
- return gridjs.html(escapeHtml(labels.inactive || 'Inactive'));
- }
- if (cell.microsoft_only) {
- return gridjs.html(escapeHtml(labels.microsoftOnly || 'Microsoft only'));
- }
- return gridjs.html(escapeHtml(labels.active || 'Active'));
- };
- const formatCount = (value) => {
- const number = Number(value ?? 0);
- return Number.isFinite(number) ? number : 0;
- };
const { gridConfig } = initListPage({
grid: {
@@ -64,84 +29,59 @@ createListPageModule({
appBase,
columns: [
{
- name: labels.avatar || 'Avatar',
- sort: false,
+ name: labels.tenant || 'Tenant',
+ sort: true,
+ width: '75%',
formatter: (cell, row) => {
- if (!cell) {
+ const hasAvatar = cell?.has_avatar ? 1 : 0;
+ const uuid = encodeURIComponent(String(cell?.uuid ?? ''));
+ let avatarHtml = '';
+ if (hasAvatar && uuid) {
+ const src = new URL(`admin/tenants/avatar-file?uuid=${uuid}&size=64`, appBase).toString();
+ avatarHtml = ``;
+ } else {
const initials = escapeHtml(initialsForRow(row));
- return gridjs.html(`${initials}`);
+ avatarHtml = `${initials}`;
}
- const uuid = encodeURIComponent(String(cell));
- const src = new URL(`admin/tenants/avatar-file?uuid=${uuid}&size=64`, appBase).toString();
- const full = new URL(`admin/tenants/avatar-file?uuid=${uuid}&size=256`, appBase).toString();
- return gridjs.html(`
`);
+
+ const nameText = escapeHtml(String(cell?.label ?? ''));
+
+ return gridjs.html(
+ `