Browse Source

feat(iot-hub): redesign device install dialog progress, conflict, and error states

Redesign progress view to match design: success (green check + Done),
running (spinner + In progress + gray bg), pending (numbered dashed circle),
conflict (orange bg + warning icon + inline action buttons), error (orange bg
+ Retry button). Add Back button, Next with label, Close when all progress
done. Use tb-fullscreen-dialog-lt-md panel class. Add markdown view overrides
matching detail dialog readme styles.
pull/15508/head
Igor Kulikov 3 months ago
parent
commit
6d2f652c89
  1. 175
      ui-ngx/src/app/modules/home/components/iot-hub/device-install-dialog/device-install-dialog.component.html
  2. 365
      ui-ngx/src/app/modules/home/components/iot-hub/device-install-dialog/device-install-dialog.component.scss
  3. 27
      ui-ngx/src/app/modules/home/components/iot-hub/device-install-dialog/device-install-dialog.component.ts
  4. 2
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-actions.service.ts
  5. 2
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.ts
  6. 2
      ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-detail-dialog.component.ts
  7. 2
      ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-installed-items.component.ts
  8. 5
      ui-ngx/src/assets/locale/locale.constant-en_US.json
  9. 19
      ui-ngx/src/styles.scss

175
ui-ngx/src/app/modules/home/components/iot-hub/device-install-dialog/device-install-dialog.component.html

@ -24,7 +24,6 @@
@case ('instruction') {
<div class="tb-device-install-instruction" #instructionContainer>
<tb-markdown [data]="ws.markdown"
[usePlainMarkdown]="true"
(ready)="onMarkdownReady(instructionContainer)">
</tb-markdown>
</div>
@ -110,59 +109,86 @@
@case ('progress') {
<div class="tb-device-install-progress">
@for (ep of ws.entitySteps; track ep.step.name) {
<div class="tb-progress-row" [class.tb-progress-conflict-row]="ep.status === 'conflict'">
<div class="tb-progress-icon">
@switch (ep.status) {
@case ('pending') {
<mat-icon class="tb-progress-pending">radio_button_unchecked</mat-icon>
@for (ep of ws.entitySteps; track ep.step.name; let i = $index) {
@if (i > 0) {
<div class="tb-progress-divider"></div>
}
@if (ep.status === 'conflict' || ep.status === 'error') {
<div class="tb-progress-row tb-progress-row-alert">
<div class="tb-progress-alert-content">
<div class="tb-progress-icon">
<mat-icon class="tb-progress-warning">warning</mat-icon>
</div>
<div class="tb-progress-alert-text">
<span class="tb-progress-label tb-progress-label-active">
{{ ('iot-hub.device-install-step-type-' + ep.step.type | translate) + ' — ' + (ep.resolvedName || ep.step.name) }}
</span>
@if (ep.status === 'conflict') {
<span class="tb-progress-alert-msg tb-progress-alert-msg-warning">
{{ 'iot-hub.device-install-conflict-exists' | translate:{ type: ('iot-hub.device-install-step-type-' + ep.step.type | translate) } }}
</span>
}
@if (ep.status === 'error' && ep.errorMessage) {
<span class="tb-progress-alert-msg tb-progress-alert-msg-error">{{ ep.errorMessage }}</span>
}
</div>
</div>
<div class="tb-progress-alert-actions">
@if (ep.status === 'conflict') {
@if (ep.conflictType === 'use-or-overwrite') {
<button class="tb-progress-action-btn tb-progress-action-primary" (click)="resolveConflict(ws, ep, 'use-existing')">
{{ 'iot-hub.device-install-use-existing' | translate }}
</button>
<button class="tb-progress-action-btn tb-progress-action-warning" (click)="resolveConflict(ws, ep, 'overwrite')">
{{ 'iot-hub.device-install-overwrite' | translate }}
</button>
} @else {
<button class="tb-progress-action-btn tb-progress-action-primary" (click)="resolveConflict(ws, ep, 'create-copy')">
{{ 'iot-hub.device-install-create-copy' | translate }}
</button>
<button class="tb-progress-action-btn tb-progress-action-warning" (click)="resolveConflict(ws, ep, 'overwrite')">
{{ 'iot-hub.device-install-overwrite' | translate }}
</button>
}
}
@case ('running') {
<mat-spinner diameter="20"></mat-spinner>
@if (ep.status === 'error') {
<button class="tb-progress-action-btn tb-progress-action-warning" (click)="retryEntitySteps(ws)">
{{ 'action.retry' | translate }}
</button>
}
@case ('success') {
<mat-icon class="tb-progress-success">check_circle</mat-icon>
</div>
</div>
} @else {
<div class="tb-progress-row"
[class.tb-progress-row-active]="ep.status === 'running'">
<div class="tb-progress-icon">
@switch (ep.status) {
@case ('pending') {
<div class="tb-progress-pending-circle">{{ i + 1 }}</div>
}
@case ('running') {
<mat-spinner diameter="24"></mat-spinner>
}
@case ('success') {
<mat-icon class="tb-progress-success">check_circle</mat-icon>
}
}
@case ('error') {
<mat-icon class="tb-progress-error">error</mat-icon>
</div>
<span class="tb-progress-label"
[class.tb-progress-label-active]="ep.status === 'running'"
[class.tb-progress-label-pending]="ep.status === 'pending'">
{{ ('iot-hub.device-install-step-type-' + ep.step.type | translate) + ' — ' + (ep.resolvedName || ep.step.name) }}
</span>
<span class="tb-progress-status">
@if (ep.status === 'success') {
<span class="tb-progress-status-done">{{ 'iot-hub.done' | translate }}</span>
}
@case ('conflict') {
<mat-icon class="tb-progress-conflict">warning</mat-icon>
@if (ep.status === 'running') {
<span class="tb-progress-status-progress">{{ 'iot-hub.in-progress' | translate }}</span>
}
}
</div>
<div class="tb-progress-info">
<span class="tb-progress-label">{{ ('iot-hub.device-install-step-type-' + ep.step.type | translate) + ' — ' + (ep.resolvedName || ep.step.name) }}</span>
@if (ep.status === 'error' && ep.errorMessage) {
<span class="tb-progress-error-msg">{{ ep.errorMessage }}</span>
}
@if (ep.status === 'conflict') {
<span class="tb-progress-conflict-msg">{{ 'iot-hub.device-install-conflict-exists' | translate:{ type: ('iot-hub.device-install-step-type-' + ep.step.type | translate) } }}</span>
}
</span>
</div>
@if (ep.status === 'success' && ep.resolution) {
<span class="tb-progress-resolution">{{ 'iot-hub.device-install-resolution-' + ep.resolution | translate }}</span>
}
@if (ep.status === 'conflict') {
<div class="tb-progress-conflict-actions">
@if (ep.conflictType === 'use-or-overwrite') {
<button mat-stroked-button color="primary" (click)="resolveConflict(ws, ep, 'use-existing')">
{{ 'iot-hub.device-install-use-existing' | translate }}
</button>
<button mat-stroked-button color="warn" (click)="resolveConflict(ws, ep, 'overwrite')">
{{ 'iot-hub.device-install-overwrite' | translate }}
</button>
} @else {
<button mat-stroked-button color="warn" (click)="resolveConflict(ws, ep, 'overwrite')">
{{ 'iot-hub.device-install-overwrite' | translate }}
</button>
<button mat-stroked-button color="primary" (click)="resolveConflict(ws, ep, 'create-copy')">
{{ 'iot-hub.device-install-create-copy' | translate }}
</button>
}
</div>
}
</div>
}
}
</div>
}
@ -178,9 +204,6 @@
} @else {
<div class="tb-device-install-header">
<h2 class="tb-device-install-title">{{ 'iot-hub.device-install-title' | translate:{ name: packageInfo?.name || data.item.name } }}</h2>
@if (selectedInstallMethod && wizardStarted) {
<span class="tb-device-install-badge">{{ installMethodLabels.get(selectedInstallMethod) }}</span>
}
<button mat-icon-button (click)="cancel()" tabindex="-1">
<mat-icon>close</mat-icon>
</button>
@ -189,7 +212,7 @@
@if (!wizardStarted) {
<!-- Connectivity selector -->
<div class="tb-device-install-body">
<div mat-dialog-content>
<div class="tb-device-install-connectivity">
<p>{{ 'iot-hub.device-install-select-connectivity' | translate }}</p>
<div class="tb-connectivity-options">
@ -206,7 +229,7 @@
</div>
<mat-dialog-actions align="end">
<button mat-button (click)="cancel()">{{ 'action.cancel' | translate }}</button>
<button mat-flat-button color="primary"
<button mat-stroked-button color="primary"
[disabled]="!selectedInstallMethod"
(click)="confirmConnectivity()">
{{ 'action.next' | translate }}
@ -215,7 +238,7 @@
} @else if (reviewMode) {
<!-- Review mode: tabs -->
<div class="tb-device-install-tabs-container">
<div mat-dialog-content>
<mat-tab-group (selectedIndexChange)="onTabChanged($event)">
@for (ws of wizardSteps; track ws.label) {
<mat-tab [label]="ws.label">
@ -239,26 +262,38 @@
} @else {
<!-- Install mode: stepper -->
<div class="tb-device-install-stepper-container">
<mat-horizontal-stepper #installStepper
[linear]="true"
<div mat-dialog-content>
<mat-stepper #installStepper
linear
labelPosition="bottom">
<ng-template matStepperIcon="edit">
<mat-icon>done</mat-icon>
</ng-template>
@for (ws of wizardSteps; track ws.label; let i = $index; let last = $last) {
<mat-step [editable]="ws.type === 'form'"
<mat-step [editable]="true"
[completed]="ws.completed"
[stepControl]="ws.type === 'form' ? ws.formGroup : null">
<ng-template matStepLabel>{{ ws.label }}</ng-template>
<ng-container *ngTemplateOutlet="stepContent; context: { $implicit: ws }"></ng-container>
</mat-step>
}
</mat-horizontal-stepper>
</mat-stepper>
</div>
<mat-dialog-actions align="end">
<ng-template #closeOrCancel>
@if (allProgressDone) {
<button mat-button (click)="done()">{{ 'action.close' | translate }}</button>
} @else {
<button mat-button (click)="cancel()">{{ 'action.cancel' | translate }}</button>
}
</ng-template>
<mat-dialog-actions>
@if (!isFirstWizardStep) {
<button mat-stroked-button (click)="previousStep()">{{ 'action.back' | translate }}</button>
}
<span class="flex-1"></span>
@if (currentWizardStep; as step) {
@switch (step.type) {
@case ('instruction') {
@ -270,22 +305,21 @@
</button>
}
} @else {
<button mat-button (click)="cancel()">{{ 'action.cancel' | translate }}</button>
<button mat-flat-button color="primary" (click)="nextStep()">
{{ 'action.next' | translate }}
<ng-container *ngTemplateOutlet="closeOrCancel"></ng-container>
<button mat-stroked-button color="primary" (click)="nextStep()">
{{ 'action.next' | translate }}@if (nextWizardStepLabel) {: {{ nextWizardStepLabel }}}
</button>
}
}
@case ('form') {
<button mat-button (click)="cancel()">{{ 'action.cancel' | translate }}</button>
<button mat-flat-button color="primary" (click)="nextStep()">
{{ 'action.next' | translate }}
<ng-container *ngTemplateOutlet="closeOrCancel"></ng-container>
<button mat-stroked-button color="primary" (click)="nextStep()">
{{ 'action.next' | translate }}@if (nextWizardStepLabel) {: {{ nextWizardStepLabel }}}
</button>
}
@case ('progress') {
@if (step.progressError) {
<button mat-button (click)="cancel()">{{ 'action.cancel' | translate }}</button>
<button mat-button (click)="goBackToForm()">{{ 'action.back' | translate }}</button>
<ng-container *ngTemplateOutlet="closeOrCancel"></ng-container>
<button mat-flat-button color="primary" (click)="retryEntitySteps(step)">
{{ 'action.retry' | translate }}
</button>
@ -296,6 +330,11 @@
{{ action.label }}
</button>
}
} @else if (!step.progressError) {
<ng-container *ngTemplateOutlet="closeOrCancel"></ng-container>
<button mat-stroked-button color="primary" [disabled]="!step.progressDone" (click)="nextStep()">
{{ 'action.next' | translate }}@if (nextWizardStepLabel) {: {{ nextWizardStepLabel }}}
</button>
}
}
}

365
ui-ngx/src/app/modules/home/components/iot-hub/device-install-dialog/device-install-dialog.component.scss

@ -14,18 +14,60 @@
* limitations under the License.
*/
@import "../../../../../../scss/constants";
:host-context(.tb-default .tb-dialog .mat-mdc-dialog-component-host),
:host-context(.tb-default .tb-fullscreen-dialog-lt-md .mat-mdc-dialog-component-host) {
.mat-mdc-dialog-content {
padding: 0;
max-height: 100%;
overflow: hidden;
}
@media #{$mat-gt-sm} {
width: 800px;
height: 1000px;
max-width: 100%;
}
}
:host {
display: flex;
flex-direction: column;
width: 720px;
max-width: 90vw;
max-height: 85vh;
max-height: 90vh;
@media #{$mat-lt-md} {
max-height: 100%;
height: 100%;
}
::ng-deep {
.mat-stepper-horizontal {
height: 100%;
}
.mat-horizontal-stepper-wrapper {
height: 100%;
}
.mat-horizontal-stepper-header-container {
padding: 24px 16px 16px;
}
.mat-horizontal-content-container {
overflow: auto;
flex: 1;
padding: 0;
}
.mat-horizontal-stepper-content.mat-horizontal-stepper-content-current {
height: 100%;
}
.mat-horizontal-stepper-header.mat-step-header {
padding: 0 12px;
}
}
}
.tb-device-install-header {
display: flex;
align-items: center;
padding: 16px 24px;
padding: 8px 8px 8px 24px;
gap: 12px;
.tb-device-install-title {
@ -40,18 +82,6 @@
text-overflow: ellipsis;
white-space: nowrap;
}
.tb-device-install-badge {
display: inline-flex;
align-items: center;
padding: 2px 8px;
border-radius: 12px;
background: #e3f2fd;
color: #1565c0;
font-size: 12px;
font-weight: 500;
white-space: nowrap;
}
}
.tb-device-install-loading {
@ -61,14 +91,6 @@
padding: 48px 0;
}
// Connectivity selector (before stepper)
.tb-device-install-body {
flex: 1;
overflow-y: auto;
padding: 0 24px 24px;
min-height: 120px;
}
.tb-device-install-connectivity {
display: flex;
flex-direction: column;
@ -97,56 +119,97 @@
}
}
// Tabs container (review mode)
.tb-device-install-tabs-container {
flex: 1;
overflow-y: auto;
min-height: 200px;
.tb-tab-content {
padding: 16px 24px;
}
.tb-tab-content {
padding: 16px 24px;
}
// Stepper container
.tb-device-install-stepper-container {
flex: 1;
overflow-y: auto;
min-height: 200px;
// Instruction view
.tb-device-install-instruction {
font-size: 14px;
line-height: 24px;
letter-spacing: 0.2px;
color: rgba(0, 0, 0, 0.76);
::ng-deep tb-markdown .tb-markdown-view {
padding: 16px 24px 24px;
h1, h2, h3, h4, h5, h6 {
font-size: 20px;
font-weight: 600;
line-height: 24px;
letter-spacing: 0.1px;
color: rgba(0, 0, 0, 0.76);
margin: 0;
padding: 0;
}
::ng-deep {
.mat-horizontal-stepper-header-container {
padding: 0 16px;
> h1, > h2, > h3, > h4, > h5, > h6 {
padding: 0;
margin-top: 20px;
}
.mat-horizontal-content-container {
padding: 0 24px 16px;
> :first-child {
margin-top: 0;
}
.mat-step-header {
padding: 0 12px;
h1 {
font-size: 24px;
line-height: 32px;
padding-right: 0;
}
}
}
// Step content wrapper
.tb-wizard-step-content {
min-height: 120px;
}
p {
font-size: 14px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0.2px;
color: rgba(0, 0, 0, 0.76);
margin: 0;
}
// Instruction view
.tb-device-install-instruction {
::ng-deep tb-markdown {
display: block;
}
> p, > div {
padding-left: 0;
padding-right: 0;
}
::ng-deep {
h1, h2, h3, h4 {
&:first-child {
margin-top: 0;
p + p {
margin-top: 8px;
}
h1 + p, h2 + p, h3 + p,
h1 + ul, h2 + ul, h3 + ul,
h1 + ol, h2 + ol, h3 + ol {
margin-top: 8px;
}
ul, ol {
padding-left: 21px;
padding-right: 0;
margin: 0;
font-size: 14px;
line-height: 24px;
letter-spacing: 0.2px;
color: rgba(0, 0, 0, 0.76);
+ h1, + h2, + h3, + h4, + h5, + h6 {
padding-top: 0;
margin-top: 20px;
}
}
li {
padding-bottom: 0;
margin-bottom: 0;
line-height: 24px;
}
code:not([class*=language-]) {
font-size: 14px;
}
}
::ng-deep {
.tb-download-btn {
display: inline-flex;
align-items: center;
@ -232,6 +295,7 @@
display: flex;
flex-direction: column;
gap: 8px;
padding: 24px;
mat-form-field {
width: 100%;
@ -258,19 +322,32 @@
.tb-device-install-progress {
display: flex;
flex-direction: column;
gap: 12px;
padding: 24px;
}
.tb-progress-divider {
width: 1px;
height: 16px;
background-color: rgba(0, 0, 0, 0.12);
margin-left: 24px;
}
.tb-progress-row {
display: flex;
align-items: flex-start;
gap: 12px;
align-items: center;
gap: 16px;
padding: 8px 12px;
border-radius: 8px;
border-radius: 4px;
&.tb-progress-row-active {
background: rgba(0, 0, 0, 0.03);
}
&.tb-progress-conflict-row {
background: #fff8e1;
border: 1px solid #ffcc02;
&.tb-progress-row-alert {
background: rgba(246, 103, 22, 0.06);
align-items: flex-start;
flex-wrap: wrap;
gap: 12px;
}
}
@ -283,70 +360,152 @@
justify-content: center;
mat-icon {
font-size: 20px;
width: 20px;
height: 20px;
}
.tb-progress-pending {
color: rgba(0, 0, 0, 0.38);
font-size: 24px;
width: 24px;
height: 24px;
}
.tb-progress-success {
color: #2e7d32;
}
.tb-progress-error {
color: #c62828;
color: #198038;
}
.tb-progress-conflict {
color: #f57f17;
.tb-progress-warning {
color: #f66716;
}
}
.tb-progress-info {
.tb-progress-pending-circle {
width: 24px;
height: 24px;
display: flex;
flex-direction: column;
gap: 4px;
min-height: 24px;
align-items: center;
justify-content: center;
font-size: 12px;
font-weight: 400;
line-height: 16px;
letter-spacing: 0.4px;
color: rgba(0, 0, 0, 0.54);
position: relative;
&::after {
content: '';
position: absolute;
width: 20px;
height: 20px;
border: 1px dashed rgba(0, 0, 0, 0.12);
border-radius: 999px;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
}
.tb-progress-label {
flex: 1;
font-size: 16px;
font-weight: 400;
line-height: 24px;
letter-spacing: 0.15px;
color: rgba(0, 0, 0, 0.76);
min-width: 0;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
&.tb-progress-label-active {
font-weight: 500;
letter-spacing: 0.25px;
}
&.tb-progress-label-pending {
color: rgba(0, 0, 0, 0.54);
}
}
.tb-progress-status {
flex-shrink: 0;
font-size: 14px;
font-weight: 500;
line-height: 20px;
color: rgba(0, 0, 0, 0.87);
letter-spacing: 0.25px;
padding: 6px 12px;
}
.tb-progress-error-msg {
font-size: 12px;
color: #c62828;
font-family: monospace;
white-space: pre-wrap;
word-break: break-word;
.tb-progress-status-done {
color: #198038;
}
.tb-progress-conflict-msg {
font-size: 12px;
color: #f57f17;
.tb-progress-status-progress {
color: rgba(0, 0, 0, 0.54);
}
.tb-progress-resolution {
margin-left: auto;
flex-shrink: 0;
font-size: 13px;
color: #2e7d32;
font-weight: 500;
.tb-progress-alert-content {
display: flex;
gap: 16px;
align-items: flex-start;
flex: 1;
min-width: 0;
}
.tb-progress-conflict-actions {
.tb-progress-alert-text {
display: flex;
flex-direction: column;
flex: 1;
min-width: 0;
}
.tb-progress-alert-msg {
font-size: 12px;
font-weight: 400;
line-height: 16px;
letter-spacing: 0.4px;
&.tb-progress-alert-msg-warning {
color: #f66716;
}
&.tb-progress-alert-msg-error {
color: #dd2c00;
}
}
.tb-progress-alert-actions {
display: flex;
gap: 8px;
margin-left: auto;
flex-shrink: 0;
}
.tb-progress-action-btn {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 6px 12px;
border-radius: 4px;
border: 1px solid rgba(0, 0, 0, 0.12);
background: white;
font-size: 14px;
font-weight: 500;
line-height: 20px;
letter-spacing: 0.25px;
white-space: nowrap;
cursor: pointer;
&.tb-progress-action-primary {
color: #00695c;
}
&.tb-progress-action-warning {
color: #f66716;
}
&:hover {
background: rgba(0, 0, 0, 0.04);
}
}
mat-dialog-actions {
padding: 8px 24px 16px;
border-top-style: solid;
border-top-color: var(--mat-divider-color, var(--mat-app-outline));
border-top-width: var(--mat-divider-width);
}

27
ui-ngx/src/app/modules/home/components/iot-hub/device-install-dialog/device-install-dialog.component.ts

@ -210,6 +210,14 @@ export class TbDeviceInstallDialogComponent extends DialogComponent<TbDeviceInst
// --- Wizard ---
get isFirstWizardStep(): boolean {
return !this.stepper || this.stepper.selectedIndex === 0;
}
get allProgressDone(): boolean {
return this.wizardSteps.filter(s => s.type === 'progress').every(s => s.progressDone);
}
get isLastWizardStep(): boolean {
return this.stepper && this.stepper.selectedIndex === this.wizardSteps.length - 1;
}
@ -221,6 +229,25 @@ export class TbDeviceInstallDialogComponent extends DialogComponent<TbDeviceInst
return this.wizardSteps[this.stepper.selectedIndex] ?? null;
}
get nextWizardStepLabel(): string {
if (!this.stepper || this.stepper.selectedIndex >= this.wizardSteps.length - 1) {
return '';
}
return this.wizardSteps[this.stepper.selectedIndex + 1]?.label || '';
}
previousStep(): void {
if (!this.stepper || this.stepper.selectedIndex <= 0) {
return;
}
const step = this.currentWizardStep;
if (step?.type === 'progress' && step.progressError) {
this.goBackToForm();
} else {
this.stepper.previous();
}
}
nextStep(): void {
const step = this.currentWizardStep;
if (!step) {

2
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-actions.service.ts

@ -108,7 +108,7 @@ export class IotHubActionsService {
mergeMap((blob: Blob) => blob.arrayBuffer()),
mergeMap((zipData: ArrayBuffer) =>
this.dialog.open(TbDeviceInstallDialogComponent, {
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
panelClass: ['tb-dialog', 'tb-fullscreen-dialog-lt-md'],
disableClose: true,
autoFocus: false,
data: { item, zipData } as DeviceInstallDialogData

2
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-browse.component.ts

@ -609,7 +609,7 @@ export class TbIotHubBrowseComponent implements OnInit, OnDestroy {
next: async (blob: Blob) => {
const zipData = await blob.arrayBuffer();
this.dialog.open(TbDeviceInstallDialogComponent, {
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
panelClass: ['tb-dialog', 'tb-fullscreen-dialog-lt-md'],
disableClose: false,
autoFocus: false,
data: {

2
ui-ngx/src/app/modules/home/components/iot-hub/iot-hub-item-detail-dialog.component.ts

@ -266,7 +266,7 @@ export class TbIotHubItemDetailDialogComponent extends DialogComponent<TbIotHubI
next: async (blob: Blob) => {
const zipData = await blob.arrayBuffer();
const dialogRef = this.dialog.open(TbDeviceInstallDialogComponent, {
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
panelClass: ['tb-dialog', 'tb-fullscreen-dialog-lt-md'],
disableClose: true,
autoFocus: false,
data: {

2
ui-ngx/src/app/modules/home/pages/iot-hub/iot-hub-installed-items.component.ts

@ -270,7 +270,7 @@ export class TbIotHubInstalledItemsComponent implements OnInit, AfterViewInit, O
const zipData = await blob.arrayBuffer();
this.iotHubApiService.getVersionInfo(item.itemVersionId, {ignoreLoading: true, ignoreErrors: true}).subscribe(versionView => {
this.dialog.open(TbDeviceInstallDialogComponent, {
panelClass: ['tb-dialog', 'tb-fullscreen-dialog'],
panelClass: ['tb-dialog', 'tb-fullscreen-dialog-lt-md'],
disableClose: false,
autoFocus: false,
data: {

5
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -3870,7 +3870,10 @@
"device-install-gateway-docker-compose": "Download docker-compose.yml for your gateway",
"device-install-title": "Install \"{{name}}\"",
"device-install-complete-title": "Install \"{{name}}\" — Complete",
"device-installing-title": "Installing \"{{name}}\""
"device-installing-title": "Installing \"{{name}}\"",
"done": "Done",
"in-progress": "In progress",
"error": "Error"
},
"item-data": {
"widget-type-timeseries": "Timeseries",

19
ui-ngx/src/styles.scss

@ -1078,6 +1078,25 @@ pre.tb-highlight {
}
}
.tb-fullscreen-dialog-lt-md {
@media #{$mat-lt-md} {
min-height: 100%;
min-width: 100%;
max-width: none !important;
position: absolute !important;
inset: 0;
.mat-mdc-dialog-container {
> *:first-child, form {
min-width: 100% !important;
height: 100%;
}
.mat-mdc-dialog-content {
max-height: 100%;
}
}
}
}
.tb-fullscreen-dialog {
@media #{$mat-lt-sm} {
min-height: 100%;

Loading…
Cancel
Save