@ -15,6 +15,162 @@
limitations under the License.
limitations under the License.
-->
-->
<!-- Shared step content template -->
< ng-template # stepContent let-ws >
< div class = "tb-wizard-step-content" >
@switch (ws.type) {
@case ('instruction') {
< div class = "tb-device-install-instruction" # instructionContainer >
< tb-markdown [ data ] = " ws . markdown "
[usePlainMarkdown]="true"
(ready)="onMarkdownReady(instructionContainer)">
< / tb-markdown >
< / div >
}
@case ('form') {
< div class = "tb-device-install-form" >
@if (ws.formGroup) {
< form [ formGroup ] = " ws . formGroup " >
@for (field of ws.formFields; track field.key) {
@switch (field.type) {
@case ('BOOLEAN') {
< mat-checkbox [ formControlName ] = " field . key " >
{{ field.label }}
< / mat-checkbox >
}
@case ('SELECT') {
< mat-form-field appearance = "outline" >
< mat-label > {{ field.label }}< / mat-label >
< mat-select [ formControlName ] = " field . key " >
@for (opt of field.options; track opt.value) {
< mat-option [ value ] = " opt . value " > {{ opt.label }}< / mat-option >
}
< / mat-select >
@if (ws.formGroup.controls[field.key]?.hasError('required')) {
< mat-error > {{ field.label + ' is required' }}< / mat-error >
}
< / mat-form-field >
}
@case ('PASSWORD') {
< mat-form-field appearance = "outline" >
< mat-label > {{ field.label }}< / mat-label >
< input matInput [ type ] = " passwordVisible [ field . key ] ? ' text ' : ' password ' "
[formControlName]="field.key">
< button mat-icon-button matSuffix type = "button"
(click)="passwordVisible[field.key] = !passwordVisible[field.key]">
< mat-icon > {{ passwordVisible[field.key] ? 'visibility_off' : 'visibility' }}< / mat-icon >
< / button >
@if (ws.formGroup.controls[field.key]?.hasError('required')) {
< mat-error > {{ field.label + ' is required' }}< / mat-error >
}
@if (ws.formGroup.controls[field.key]?.hasError('pattern')) {
< mat-error > {{ getPatternErrorMessage(field) }}< / mat-error >
}
< / mat-form-field >
}
@case ('INTEGER') {
< mat-form-field appearance = "outline" >
< mat-label > {{ field.label }}< / mat-label >
< input matInput type = "number" [ formControlName ] = " field . key " >
@if (ws.formGroup.controls[field.key]?.hasError('required')) {
< mat-error > {{ field.label + ' is required' }}< / mat-error >
}
@if (ws.formGroup.controls[field.key]?.hasError('pattern')) {
< mat-error > {{ getPatternErrorMessage(field) }}< / mat-error >
}
< / mat-form-field >
}
@default {
< mat-form-field appearance = "outline" >
< mat-label > {{ field.label }}< / mat-label >
< input matInput [ formControlName ] = " field . key " >
@if (ws.formGroup.controls[field.key]?.hasError('required')) {
< mat-error > {{ field.label + ' is required' }}< / mat-error >
}
@if (ws.formGroup.controls[field.key]?.hasError('pattern')) {
< mat-error > {{ getPatternErrorMessage(field) }}< / mat-error >
}
< / mat-form-field >
}
}
@if (field.helpText) {
< div class = "tb-form-field-help" > {{ field.helpText }}< / div >
}
@if (field.helpImage) {
< img class = "tb-form-field-help-image" [ src ] = " resolveImagePath ( field . helpImage ) " [ alt ] = " field . label " >
}
}
< / form >
}
< / div >
}
@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 >
}
@case ('running') {
< mat-spinner diameter = "20" > < / 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 >
}
@case ('conflict') {
< mat-icon class = "tb-progress-conflict" > warning< / mat-icon >
}
}
< / 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 >
}
< / 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 >
}
}
< / div >
< / ng-template >
<!-- Main dialog -->
@if (loading) {
@if (loading) {
< div class = "tb-device-install-loading" >
< div class = "tb-device-install-loading" >
< mat-spinner diameter = "40" > < / mat-spinner >
< mat-spinner diameter = "40" > < / mat-spinner >
@ -56,182 +212,54 @@
{{ 'action.next' | translate }}
{{ 'action.next' | translate }}
< / button >
< / button >
< / mat-dialog-actions >
< / mat-dialog-actions >
} @else if (reviewMode) {
<!-- Review mode: tabs -->
< div class = "tb-device-install-tabs-container" >
< mat-tab-group ( selectedIndexChange ) = " onTabChanged ( $ event ) " >
@for (ws of wizardSteps; track ws.label) {
< mat-tab [ label ] = " ws . label " >
< ng-template matTabContent >
< div class = "tb-tab-content" >
< ng-container * ngTemplateOutlet = "stepContent; context: { $implicit: ws }" > < / ng-container >
< / div >
< / ng-template >
< / mat-tab >
}
< / mat-tab-group >
< / div >
< mat-dialog-actions align = "end" >
< button mat-button ( click ) = " done ( ) " > {{ 'action.close' | translate }}< / button >
@if (primaryEntityAction; as action) {
< button mat-flat-button color = "primary" ( click ) = " openEntity ( action . url ) " >
{{ action.label }}
< / button >
}
< / mat-dialog-actions >
} @else {
} @else {
<!-- Stepper wizard -->
<!-- Install mode: stepper -->
< div class = "tb-device-install-stepper-container" >
< div class = "tb-device-install-stepper-container" >
< mat-horizontal-stepper # installStepper
< mat-horizontal-stepper # installStepper
[linear]="!reviewMode"
[linear]="true"
labelPosition="bottom"
labelPosition="bottom">
(selectionChange)="onStepChanged()">
< ng-template matStepperIcon = "edit" >
< ng-template matStepperIcon = "edit" >
< mat-icon > done< / mat-icon >
< mat-icon > done< / mat-icon >
< / ng-template >
< / ng-template >
@for (ws of wizardSteps; track ws.label; let i = $index; let last = $last) {
@for (ws of wizardSteps; track ws.label; let i = $index; let last = $last) {
< mat-step [ editable ] = " reviewMode | | ws . type = == ' form ' "
< mat-step [ editable ] = " ws . type = == ' form ' "
[completed]="ws.completed"
[completed]="ws.completed"
[stepControl]="ws.type === 'form' & & !reviewMode ? ws.formGroup : null">
[stepControl]="ws.type === 'form' ? ws.formGroup : null">
< ng-template matStepLabel > {{ ws.label }}< / ng-template >
< ng-template matStepLabel > {{ ws.label }}< / ng-template >
< ng-container * ngTemplateOutlet = "stepContent; context: { $implicit: ws }" > < / ng-container >
< div class = "tb-wizard-step-content" >
@switch (ws.type) {
@case ('instruction') {
< div class = "tb-device-install-instruction" # instructionContainer >
< tb-markdown [ data ] = " ws . markdown "
[usePlainMarkdown]="true"
(ready)="onMarkdownReady(instructionContainer)">
< / tb-markdown >
< / div >
}
@case ('form') {
< div class = "tb-device-install-form" >
@if (ws.formGroup) {
< form [ formGroup ] = " ws . formGroup " >
@for (field of ws.formFields; track field.key) {
@switch (field.type) {
@case ('BOOLEAN') {
< mat-checkbox [ formControlName ] = " field . key " >
{{ field.label }}
< / mat-checkbox >
}
@case ('SELECT') {
< mat-form-field appearance = "outline" >
< mat-label > {{ field.label }}< / mat-label >
< mat-select [ formControlName ] = " field . key " >
@for (opt of field.options; track opt.value) {
< mat-option [ value ] = " opt . value " > {{ opt.label }}< / mat-option >
}
< / mat-select >
@if (ws.formGroup.controls[field.key]?.hasError('required')) {
< mat-error > {{ field.label + ' is required' }}< / mat-error >
}
< / mat-form-field >
}
@case ('PASSWORD') {
< mat-form-field appearance = "outline" >
< mat-label > {{ field.label }}< / mat-label >
< input matInput [ type ] = " passwordVisible [ field . key ] ? ' text ' : ' password ' "
[formControlName]="field.key">
< button mat-icon-button matSuffix type = "button"
(click)="passwordVisible[field.key] = !passwordVisible[field.key]">
< mat-icon > {{ passwordVisible[field.key] ? 'visibility_off' : 'visibility' }}< / mat-icon >
< / button >
@if (ws.formGroup.controls[field.key]?.hasError('required')) {
< mat-error > {{ field.label + ' is required' }}< / mat-error >
}
@if (ws.formGroup.controls[field.key]?.hasError('pattern')) {
< mat-error > {{ getPatternErrorMessage(field) }}< / mat-error >
}
< / mat-form-field >
}
@case ('INTEGER') {
< mat-form-field appearance = "outline" >
< mat-label > {{ field.label }}< / mat-label >
< input matInput type = "number" [ formControlName ] = " field . key " >
@if (ws.formGroup.controls[field.key]?.hasError('required')) {
< mat-error > {{ field.label + ' is required' }}< / mat-error >
}
@if (ws.formGroup.controls[field.key]?.hasError('pattern')) {
< mat-error > {{ getPatternErrorMessage(field) }}< / mat-error >
}
< / mat-form-field >
}
@default {
< mat-form-field appearance = "outline" >
< mat-label > {{ field.label }}< / mat-label >
< input matInput [ formControlName ] = " field . key " >
@if (ws.formGroup.controls[field.key]?.hasError('required')) {
< mat-error > {{ field.label + ' is required' }}< / mat-error >
}
@if (ws.formGroup.controls[field.key]?.hasError('pattern')) {
< mat-error > {{ getPatternErrorMessage(field) }}< / mat-error >
}
< / mat-form-field >
}
}
@if (field.helpText) {
< div class = "tb-form-field-help" > {{ field.helpText }}< / div >
}
@if (field.helpImage) {
< img class = "tb-form-field-help-image" [ src ] = " resolveImagePath ( field . helpImage ) " [ alt ] = " field . label " >
}
}
< / form >
}
< / div >
}
@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 >
}
@case ('running') {
< mat-spinner diameter = "20" > < / 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 >
}
@case ('conflict') {
< mat-icon class = "tb-progress-conflict" > warning< / mat-icon >
}
}
< / 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 >
}
< / 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 >
}
}
< / div >
< / mat-step >
< / mat-step >
}
}
< / mat-horizontal-stepper >
< / mat-horizontal-stepper >
< / div >
< / div >
< mat-dialog-actions align = "end" >
< mat-dialog-actions align = "end" >
@if (reviewMode) {
@if (currentWizardStep; as step) {
< button mat-flat-button color = "primary" ( click ) = " done ( ) " > {{ 'action.close' | translate }}< / button >
} @else if (currentWizardStep; as step) {
@switch (step.type) {
@switch (step.type) {
@case ('instruction') {
@case ('instruction') {
@if (isLastWizardStep) {
@if (isLastWizardStep) {