feat(appstore,workspace): add App Store upgrade progress tracking and interactive workspace processes

This commit is contained in:
2026-05-25 06:24:29 +00:00
parent 3e68e875ac
commit d2c1bed82c
16 changed files with 1069 additions and 122 deletions
+63 -6
View File
@@ -146,6 +146,7 @@ export class ObViewServices extends DeesElement {
accessor appStoreState: appstate.IAppStoreState = {
apps: [],
upgradeableServices: [],
upgradeOperations: [],
};
constructor() {
@@ -227,6 +228,7 @@ export class ObViewServices extends DeesElement {
appstate.servicesStatePart.dispatchAction(appstate.fetchServicesAction, null),
appstate.servicesStatePart.dispatchAction(appstate.fetchPlatformServicesAction, null),
appstate.appStoreStatePart.dispatchAction(appstate.fetchUpgradeableAppStoreServicesAction, null),
appstate.appStoreStatePart.dispatchAction(appstate.fetchAppStoreUpgradeOperationsAction, null),
]);
// If a platform service was selected from the dashboard, navigate to its detail
@@ -471,9 +473,21 @@ export class ObViewServices extends DeesElement {
const upgradeInfo = service
? this.appStoreState.upgradeableServices.find((u) => u.serviceName === service.name)
: null;
const upgradeOperation = service
? this.appStoreState.upgradeOperations.find((operation) => {
return operation.serviceName === service.name && operation.status === 'running';
})
: null;
const latestUpgradeOperation = service
? this.appStoreState.upgradeOperations.find((operation) => operation.serviceName === service.name)
: null;
return html`
<ob-sectionheading>Service Details</ob-sectionheading>
${upgradeOperation ? this.renderUpgradeOperation(upgradeOperation) : ''}
${!upgradeOperation && latestUpgradeOperation?.status === 'failed'
? this.renderUpgradeOperation(latestUpgradeOperation)
: ''}
${upgradeInfo ? html`
<div style="
background: linear-gradient(135deg, rgba(59, 130, 246, 0.1), rgba(139, 92, 246, 0.1));
@@ -496,18 +510,14 @@ export class ObViewServices extends DeesElement {
<button
class="deploy-button"
style="padding: 8px 16px; font-size: 13px;"
?disabled=${Boolean(upgradeOperation)}
@click=${async () => {
await appstate.appStoreStatePart.dispatchAction(appstate.upgradeAppStoreServiceAction, {
serviceName: upgradeInfo.serviceName,
targetVersion: upgradeInfo.latestVersion,
});
// Refresh service data
appstate.servicesStatePart.dispatchAction(appstate.fetchServiceAction, {
name: upgradeInfo.serviceName,
});
appstate.servicesStatePart.dispatchAction(appstate.fetchServicesAction, null);
}}
>Upgrade</button>
>${upgradeOperation ? 'Upgrading...' : 'Upgrade'}</button>
</div>
` : ''}
<sz-service-detail-view
@@ -544,6 +554,53 @@ export class ObViewServices extends DeesElement {
`;
}
private renderUpgradeOperation(
operationArg: interfaces.requests.IAppStoreUpgradeOperation,
): TemplateResult {
const color = operationArg.status === 'failed' ? '#f87171' : '#60a5fa';
return html`
<div style="
background: var(--ci-shade-1, #09090b);
border: 1px solid ${color};
border-radius: 8px;
padding: 16px;
margin-bottom: 16px;
">
<div style="display: flex; justify-content: space-between; gap: 16px; align-items: flex-start;">
<div>
<div style="font-size: 14px; font-weight: 600; color: var(--ci-shade-7, #e4e4e7);">
Upgrade ${operationArg.fromVersion} &rarr; ${operationArg.targetVersion}: ${operationArg.step}
</div>
<div style="font-size: 12px; color: var(--ci-shade-4, #71717a); margin-top: 4px;">
${operationArg.status === 'running' ? 'Operation is running in the background.' : operationArg.error || 'Operation finished.'}
</div>
</div>
<span style="font-size: 12px; color: ${color}; text-transform: uppercase; letter-spacing: 0.04em;">
${operationArg.status}
</span>
</div>
<div style="
margin-top: 12px;
padding: 10px 12px;
background: var(--ci-shade-0, #030305);
border-radius: 6px;
color: var(--ci-shade-5, #a1a1aa);
font-family: monospace;
font-size: 12px;
line-height: 1.5;
max-height: 130px;
overflow: auto;
white-space: pre-wrap;
">${operationArg.progressLines.slice(-8).join('\n')}</div>
${operationArg.warnings.length > 0 ? html`
<div style="margin-top: 10px; color: #fbbf24; font-size: 12px;">
${operationArg.warnings.join(' | ')}
</div>
` : ''}
</div>
`;
}
private renderBackupsView(): TemplateResult {
return html`
<ob-sectionheading>Backups</ob-sectionheading>