fix(ui): remove redundant wrappers around Cloudly tables

This commit is contained in:
2026-05-26 19:38:52 +00:00
parent 756c35aa05
commit 06d54db747
6 changed files with 147 additions and 140 deletions
+36 -36
View File
@@ -42,8 +42,8 @@ export class CloudlyViewImages extends DeesElement {
.detail-title { margin: 0; font-size: 26px; font-weight: 700; color: var(--ci-shade-7, #e4e4e7); }
.detail-subtitle { margin-top: 6px; color: var(--ci-shade-4, #71717a); font-size: 14px; overflow-wrap: anywhere; }
.back-button { border: 1px solid var(--ci-shade-2, #27272a); border-radius: 7px; padding: 9px 13px; font-size: 13px; cursor: pointer; background: var(--ci-shade-1, #09090b); color: var(--ci-shade-7, #e4e4e7); }
.summary-card, .detail-card { background: var(--ci-shade-1, #09090b); border: 1px solid var(--ci-shade-2, #27272a); border-radius: 9px; padding: 16px; }
.spaced-card { margin-top: 14px; }
.detail-card { background: var(--ci-shade-1, #09090b); border: 1px solid var(--ci-shade-2, #27272a); border-radius: 9px; padding: 16px; }
.spaced-table, .spaced-card { margin-top: 14px; }
.details-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-top: 14px; }
.section-title { font-size: 14px; font-weight: 700; color: var(--ci-shade-7, #e4e4e7); margin-bottom: 10px; }
.kv-list { display: grid; gap: 8px; }
@@ -172,43 +172,43 @@ export class CloudlyViewImages extends DeesElement {
<dees-statsgrid .tiles=${this.getImageStatsTiles(image)} .minTileWidth=${220} .gap=${12}></dees-statsgrid>
<div class="detail-card">
<div class="section-title">Versions</div>
<dees-table
.heading1=${'Image Versions'}
.heading2=${versions.length ? 'Stored image versions and registry metadata' : 'No versions recorded'}
.data=${versions}
.displayFunction=${(versionArg: plugins.interfaces.data.IImage['data']['versions'][number]) => ({
Version: versionArg.versionString,
Source: this.renderSourceBadge(versionArg.source),
Size: this.formatBytes(versionArg.size),
Digest: versionArg.digest || '-',
Repository: versionArg.registryRepository || '-',
Tag: versionArg.registryTag || '-',
Storage: versionArg.storagePath || '-',
Created: this.formatDate(versionArg.createdAt),
})}
></dees-table>
${servicesUsingImage.length ? html`
<dees-table
.heading1=${'Image Versions'}
.heading2=${versions.length ? 'Stored image versions and registry metadata' : 'No versions recorded'}
.data=${versions}
.displayFunction=${(versionArg: plugins.interfaces.data.IImage['data']['versions'][number]) => ({
Version: versionArg.versionString,
Source: this.renderSourceBadge(versionArg.source),
Size: this.formatBytes(versionArg.size),
Digest: versionArg.digest || '-',
Repository: versionArg.registryRepository || '-',
Tag: versionArg.registryTag || '-',
Storage: versionArg.storagePath || '-',
Created: this.formatDate(versionArg.createdAt),
class="spaced-table"
.heading1=${'Service Usage'}
.heading2=${'Services currently configured with this image ID'}
.data=${servicesUsingImage}
.displayFunction=${(serviceArg: plugins.interfaces.data.IService) => ({
Name: serviceArg.data.name,
Version: serviceArg.data.imageVersion || '-',
Category: serviceArg.data.serviceCategory || 'workload',
Strategy: serviceArg.data.deploymentStrategy || 'custom',
Domains: serviceArg.data.domains?.map((domainArg) => domainArg.name).join(', ') || '-',
Deployments: serviceArg.data.deploymentIds?.length || 0,
})}
></dees-table>
</div>
<div class="detail-card spaced-card">
<div class="section-title">Services Using This Image</div>
${servicesUsingImage.length ? html`
<dees-table
.heading1=${'Service Usage'}
.heading2=${'Services currently configured with this image ID'}
.data=${servicesUsingImage}
.displayFunction=${(serviceArg: plugins.interfaces.data.IService) => ({
Name: serviceArg.data.name,
Version: serviceArg.data.imageVersion || '-',
Category: serviceArg.data.serviceCategory || 'workload',
Strategy: serviceArg.data.deploymentStrategy || 'custom',
Domains: serviceArg.data.domains?.map((domainArg) => domainArg.name).join(', ') || '-',
Deployments: serviceArg.data.deploymentIds?.length || 0,
})}
></dees-table>
` : html`<div class="empty-state">No services currently reference this image.</div>`}
</div>
` : html`
<div class="detail-card spaced-card">
<div class="section-title">Services Using This Image</div>
<div class="empty-state">No services currently reference this image.</div>
</div>
`}
<div class="details-grid">
<div class="detail-card">
+55 -56
View File
@@ -368,66 +368,65 @@ export class CloudlyViewServices extends DeesElement {
</div>
</div>
<div class="detail-card">
<div style="display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px;">
<div>
<div class="section-title">Deployments</div>
<div class="detail-subtitle">Container-level runtime actions happen here.</div>
</div>
<button class="back-button" @click=${() => this.loadDeploymentsForService(service)}>Refresh</button>
</div>
${this.deploymentsLoading ? html`<div class="detail-subtitle">Loading deployments...</div>` : html`
<dees-table
.heading1=${'Live Deployments'}
.heading2=${this.serviceDeployments.length ? 'Docker Swarm tasks reported by connected Coreflows' : 'No live deployments reported'}
.data=${this.serviceDeployments}
.displayFunction=${(deploymentArg: any) => ({
Status: this.renderStatusBadge(deploymentArg.status),
Node: deploymentArg.nodeName || deploymentArg.nodeId || '-',
Slot: deploymentArg.slot || '-',
Version: deploymentArg.version || service.data.imageVersion,
Container: deploymentArg.containerId ? deploymentArg.containerId.slice(0, 12) : '-',
CPU: deploymentArg.resourceUsage ? `${deploymentArg.resourceUsage.cpuUsagePercent.toFixed(1)}%` : '-',
Memory: deploymentArg.resourceUsage ? `${deploymentArg.resourceUsage.memoryUsedMB} MB` : '-',
Updated: deploymentArg.updatedAt ? new Date(deploymentArg.updatedAt).toLocaleString() : '-',
})}
.dataActions=${[
{
name: 'Details',
iconName: 'lucide:Eye',
type: ['contextmenu', 'inRow', 'doubleClick'],
actionFunc: async (actionDataArg: any) => {
await this.showDeploymentDetailsModal(actionDataArg.item);
},
${this.deploymentsLoading ? html`<div class="detail-subtitle">Loading deployments...</div>` : html`
<dees-table
.heading1=${'Live Deployments'}
.heading2=${this.serviceDeployments.length ? 'Docker Swarm tasks reported by connected Coreflows' : 'No live deployments reported'}
.data=${this.serviceDeployments}
.displayFunction=${(deploymentArg: any) => ({
Status: this.renderStatusBadge(deploymentArg.status),
Node: deploymentArg.nodeName || deploymentArg.nodeId || '-',
Slot: deploymentArg.slot || '-',
Version: deploymentArg.version || service.data.imageVersion,
Container: deploymentArg.containerId ? deploymentArg.containerId.slice(0, 12) : '-',
CPU: deploymentArg.resourceUsage ? `${deploymentArg.resourceUsage.cpuUsagePercent.toFixed(1)}%` : '-',
Memory: deploymentArg.resourceUsage ? `${deploymentArg.resourceUsage.memoryUsedMB} MB` : '-',
Updated: deploymentArg.updatedAt ? new Date(deploymentArg.updatedAt).toLocaleString() : '-',
})}
.dataActions=${[
{
name: 'Refresh',
iconName: 'refresh-cw',
type: ['header'],
actionFunc: async () => {
await this.loadDeploymentsForService(service);
},
{
name: 'Open IDE',
iconName: 'terminal',
type: ['contextmenu', 'inRow'],
actionFunc: async (actionDataArg: any) => {
await this.openDeploymentWorkspace(actionDataArg.item);
},
},
{
name: 'Details',
iconName: 'lucide:Eye',
type: ['contextmenu', 'inRow', 'doubleClick'],
actionFunc: async (actionDataArg: any) => {
await this.showDeploymentDetailsModal(actionDataArg.item);
},
{
name: 'Restart',
iconName: 'refresh-cw',
type: ['contextmenu', 'inRow'],
actionFunc: async (actionDataArg: any) => {
await this.restartDeployment(actionDataArg.item);
},
},
{
name: 'Open IDE',
iconName: 'terminal',
type: ['contextmenu', 'inRow'],
actionFunc: async (actionDataArg: any) => {
await this.openDeploymentWorkspace(actionDataArg.item);
},
{
name: 'Kill Container',
iconName: 'skull',
type: ['contextmenu', 'inRow'],
actionFunc: async (actionDataArg: any) => {
await this.confirmKillDeployment(actionDataArg.item);
},
},
{
name: 'Restart',
iconName: 'refresh-cw',
type: ['contextmenu', 'inRow'],
actionFunc: async (actionDataArg: any) => {
await this.restartDeployment(actionDataArg.item);
},
] as plugins.deesCatalog.ITableAction[]}
></dees-table>
`}
</div>
},
{
name: 'Kill Container',
iconName: 'skull',
type: ['contextmenu', 'inRow'],
actionFunc: async (actionDataArg: any) => {
await this.confirmKillDeployment(actionDataArg.item);
},
},
] as plugins.deesCatalog.ITableAction[]}
></dees-table>
`}
<div class="details-grid">
<div class="detail-card">
+24 -26
View File
@@ -266,32 +266,30 @@ export class CloudlyViewTasks extends DeesElement {
<cloudly-sectionheading>Execution History</cloudly-sectionheading>
<dees-panel .title=${'Recent Executions'} .subtitle=${'History of task runs and their outcomes'} .variant=${'outline'}>
<dees-table
.heading1=${'Task Executions'}
.heading2=${'History of task runs and their outcomes'}
.data=${this.data.taskExecutions || []}
.displayFunction=${(itemArg: plugins.interfaces.data.ITaskExecution) => {
return {
Task: itemArg.data.taskName,
Status: html`<span class="status-badge status-${itemArg.data.status}">${itemArg.data.status}</span>`,
'Started At': formatDate(itemArg.data.startedAt),
Duration: itemArg.data.duration ? formatDuration(itemArg.data.duration) : '-',
'Triggered By': itemArg.data.triggeredBy,
Logs: itemArg.data.logs?.length || 0,
} as any;
}}
.actionFunction=${async (itemArg: plugins.interfaces.data.ITaskExecution) => {
const actions: any[] = [
{ name: 'View Details', iconName: 'lucide:Eye', type: ['inRow'], actionFunc: async () => { this.selectedExecution = itemArg; } }
];
if (itemArg.data.status === 'running') {
actions.push({ name: 'Cancel', iconName: 'lucide:SquareX', type: ['inRow'], actionFunc: async () => { await appstate.dataState.dispatchAction(appstate.taskActions.cancelTask, { executionId: itemArg.id }); await this.loadExecutionsWithFilter(); } });
}
return actions;
}}
></dees-table>
</dees-panel>
<dees-table
.heading1=${'Task Executions'}
.heading2=${'History of task runs and their outcomes'}
.data=${this.data.taskExecutions || []}
.displayFunction=${(itemArg: plugins.interfaces.data.ITaskExecution) => {
return {
Task: itemArg.data.taskName,
Status: html`<span class="status-badge status-${itemArg.data.status}">${itemArg.data.status}</span>`,
'Started At': formatDate(itemArg.data.startedAt),
Duration: itemArg.data.duration ? formatDuration(itemArg.data.duration) : '-',
'Triggered By': itemArg.data.triggeredBy,
Logs: itemArg.data.logs?.length || 0,
} as any;
}}
.actionFunction=${async (itemArg: plugins.interfaces.data.ITaskExecution) => {
const actions: any[] = [
{ name: 'View Details', iconName: 'lucide:Eye', type: ['inRow'], actionFunc: async () => { this.selectedExecution = itemArg; } }
];
if (itemArg.data.status === 'running') {
actions.push({ name: 'Cancel', iconName: 'lucide:SquareX', type: ['inRow'], actionFunc: async () => { await appstate.dataState.dispatchAction(appstate.taskActions.cancelTask, { executionId: itemArg.id }); await this.loadExecutionsWithFilter(); } });
}
return actions;
}}
></dees-table>
${this.selectedExecution ? html`
<cloudly-sectionheading>Execution Details</cloudly-sectionheading>