fix(web-applications): show Enabled column and active WebUI info card
This commit is contained in:
@@ -28,3 +28,9 @@ scripts/
|
|||||||
!README.md
|
!README.md
|
||||||
!CHANGELOG.md
|
!CHANGELOG.md
|
||||||
/SPEC-*
|
/SPEC-*
|
||||||
|
# Tool-generated artifacts during agent sessions
|
||||||
|
.playwright-mcp/
|
||||||
|
outputs/
|
||||||
|
.vite_*.log
|
||||||
|
.tmp_*
|
||||||
|
*.py
|
||||||
|
|||||||
@@ -333,8 +333,22 @@ interface ConfirmAction {
|
|||||||
|
|
||||||
function isActiveWebApplication(item: Record<string, unknown>): boolean {
|
function isActiveWebApplication(item: Record<string, unknown>): boolean {
|
||||||
const prefixes = item.urlPrefix;
|
const prefixes = item.urlPrefix;
|
||||||
if (!Array.isArray(prefixes)) return false;
|
const values: string[] = [];
|
||||||
return prefixes.includes('/admin') || prefixes.includes('/account');
|
if (Array.isArray(prefixes)) {
|
||||||
|
values.push(...prefixes.map(String));
|
||||||
|
} else if (typeof prefixes === 'string') {
|
||||||
|
values.push(...prefixes.split(',').map((s) => s.trim()));
|
||||||
|
} else if (prefixes && typeof prefixes === 'object') {
|
||||||
|
// JMAP exposes urlPrefix as a set object such as { "/admin": true, "/account": true }
|
||||||
|
for (const [key, value] of Object.entries(prefixes)) {
|
||||||
|
if (value === true) {
|
||||||
|
values.push(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (prefixes != null) {
|
||||||
|
values.push(String(prefixes));
|
||||||
|
}
|
||||||
|
return values.some((p) => p === '/admin' || p === '/account');
|
||||||
}
|
}
|
||||||
interface DynamicListProps {
|
interface DynamicListProps {
|
||||||
viewName: string;
|
viewName: string;
|
||||||
@@ -362,22 +376,27 @@ export function DynamicList({ viewName }: DynamicListProps) {
|
|||||||
}, [schema, viewName]);
|
}, [schema, viewName]);
|
||||||
|
|
||||||
const objectName = resolved?.obj.objectName;
|
const objectName = resolved?.obj.objectName;
|
||||||
const isWebApplications = objectName === 'x:Application';
|
const isWebApplications = viewName === 'x:Application' || objectName === 'x:Application';
|
||||||
|
|
||||||
const displayColumns = useMemo(() => {
|
const displayColumns = useMemo(() => {
|
||||||
const columns = resolved?.list?.columns ?? [];
|
const columns = resolved?.list?.columns ?? [];
|
||||||
if (!isWebApplications) return columns;
|
if (!isWebApplications) return columns;
|
||||||
|
|
||||||
// For Web Applications, present Description first and Enabled second
|
// For Web Applications, present Description first and Enabled second
|
||||||
// to match the layout of other tables such as Domains.
|
// to match the layout of other tables such as Domains. If the schema
|
||||||
|
// does not expose an Enabled column, add a synthetic one.
|
||||||
const ordered = ['description', 'enabled'];
|
const ordered = ['description', 'enabled'];
|
||||||
const rest = columns.filter((c) => !ordered.includes(c.name));
|
const rest = columns.filter((c) => !ordered.includes(c.name));
|
||||||
const descriptionCol = columns.find((c) => c.name === 'description');
|
const descriptionCol = columns.find((c) => c.name === 'description');
|
||||||
const enabledCol = columns.find((c) => c.name === 'enabled');
|
const enabledCol = columns.find((c) => c.name === 'enabled');
|
||||||
|
|
||||||
const result = [];
|
const result: Array<{ name: string; label: string }> = [];
|
||||||
if (descriptionCol) result.push(descriptionCol);
|
if (descriptionCol) result.push(descriptionCol);
|
||||||
if (enabledCol) result.push(enabledCol);
|
if (enabledCol) {
|
||||||
|
result.push(enabledCol);
|
||||||
|
} else {
|
||||||
|
result.push({ name: 'enabled', label: t('webApplications.enabled', 'Enabled') });
|
||||||
|
}
|
||||||
result.push(...rest);
|
result.push(...rest);
|
||||||
return result;
|
return result;
|
||||||
}, [resolved?.list?.columns, isWebApplications]);
|
}, [resolved?.list?.columns, isWebApplications]);
|
||||||
@@ -414,9 +433,9 @@ export function DynamicList({ viewName }: DynamicListProps) {
|
|||||||
|
|
||||||
void (async () => {
|
void (async () => {
|
||||||
try {
|
try {
|
||||||
const accountId = getAccountId('x:Application');
|
const accountId = getAccountId(objectName ?? 'x:Application');
|
||||||
const result = await jmapQueryAllAndGet(
|
const result = await jmapQueryAllAndGet(
|
||||||
'x:Application',
|
objectName ?? 'x:Application',
|
||||||
accountId,
|
accountId,
|
||||||
{},
|
{},
|
||||||
['id', 'description', 'enabled', 'urlPrefix'],
|
['id', 'description', 'enabled', 'urlPrefix'],
|
||||||
@@ -475,6 +494,9 @@ export function DynamicList({ viewName }: DynamicListProps) {
|
|||||||
try {
|
try {
|
||||||
const accountId = getAccountId(obj.objectName);
|
const accountId = getAccountId(obj.objectName);
|
||||||
const properties = ['id', ...list.columns.map((c) => c.name)];
|
const properties = ['id', ...list.columns.map((c) => c.name)];
|
||||||
|
if (isWebApplications && !properties.includes('enabled')) {
|
||||||
|
properties.push('enabled');
|
||||||
|
}
|
||||||
const filter = buildFilter();
|
const filter = buildFilter();
|
||||||
const sortArr = buildSort();
|
const sortArr = buildSort();
|
||||||
|
|
||||||
@@ -1358,7 +1380,14 @@ export function DynamicList({ viewName }: DynamicListProps) {
|
|||||||
)}
|
)}
|
||||||
{displayColumns.map((col) => (
|
{displayColumns.map((col) => (
|
||||||
<td key={col.name} className="px-3 py-2">
|
<td key={col.name} className="px-3 py-2">
|
||||||
{renderCellValue(
|
{isWebApplications && col.name === 'enabled' && !fields[col.name] ? (
|
||||||
|
item.enabled === true ? (
|
||||||
|
<Check className="h-4 w-4 text-green-600" />
|
||||||
|
) : (
|
||||||
|
<X className="h-4 w-4 text-red-500" />
|
||||||
|
)
|
||||||
|
) : (
|
||||||
|
renderCellValue(
|
||||||
item[col.name],
|
item[col.name],
|
||||||
fields[col.name],
|
fields[col.name],
|
||||||
col.name,
|
col.name,
|
||||||
@@ -1366,7 +1395,7 @@ export function DynamicList({ viewName }: DynamicListProps) {
|
|||||||
resolved.obj.objectName,
|
resolved.obj.objectName,
|
||||||
getDisplayName,
|
getDisplayName,
|
||||||
)
|
)
|
||||||
}
|
)}
|
||||||
</td>
|
</td>
|
||||||
))}
|
))}
|
||||||
{hasItemActions && <td className="px-3 py-2 text-right">{renderItemActions(item)}</td>}
|
{hasItemActions && <td className="px-3 py-2 text-right">{renderItemActions(item)}</td>}
|
||||||
|
|||||||
+2
-1
@@ -400,6 +400,7 @@
|
|||||||
},
|
},
|
||||||
"webApplications": {
|
"webApplications": {
|
||||||
"activeWebUI": "Active WebUI",
|
"activeWebUI": "Active WebUI",
|
||||||
"version": "Version"
|
"version": "Version",
|
||||||
|
"enabled": "Enabled"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user