Commit 52e6e76ac6c46cbead43f17b6c53b7ebc967dba6
1 parent
b0126a9d
Oauth2 - set provider name to created user additionalInfo. UI: Improve device profile alarm rules.
Showing
28 changed files
with
165 additions
and
175 deletions
@@ -31,6 +31,8 @@ import org.thingsboard.server.common.data.id.CustomerId; | @@ -31,6 +31,8 @@ import org.thingsboard.server.common.data.id.CustomerId; | ||
31 | import org.thingsboard.server.common.data.id.DashboardId; | 31 | import org.thingsboard.server.common.data.id.DashboardId; |
32 | import org.thingsboard.server.common.data.id.IdBased; | 32 | import org.thingsboard.server.common.data.id.IdBased; |
33 | import org.thingsboard.server.common.data.id.TenantId; | 33 | import org.thingsboard.server.common.data.id.TenantId; |
34 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; | ||
35 | +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; | ||
34 | import org.thingsboard.server.common.data.page.PageData; | 36 | import org.thingsboard.server.common.data.page.PageData; |
35 | import org.thingsboard.server.common.data.page.PageLink; | 37 | import org.thingsboard.server.common.data.page.PageLink; |
36 | import org.thingsboard.server.common.data.security.Authority; | 38 | import org.thingsboard.server.common.data.security.Authority; |
@@ -76,12 +78,15 @@ public abstract class AbstractOAuth2ClientMapper { | @@ -76,12 +78,15 @@ public abstract class AbstractOAuth2ClientMapper { | ||
76 | 78 | ||
77 | private final Lock userCreationLock = new ReentrantLock(); | 79 | private final Lock userCreationLock = new ReentrantLock(); |
78 | 80 | ||
79 | - protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, boolean allowUserCreation, boolean activateUser) { | 81 | + protected SecurityUser getOrCreateSecurityUserFromOAuth2User(OAuth2User oauth2User, OAuth2ClientRegistrationInfo clientRegistration) { |
82 | + | ||
83 | + OAuth2MapperConfig config = clientRegistration.getMapperConfig(); | ||
84 | + | ||
80 | UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, oauth2User.getEmail()); | 85 | UserPrincipal principal = new UserPrincipal(UserPrincipal.Type.USER_NAME, oauth2User.getEmail()); |
81 | 86 | ||
82 | User user = userService.findUserByEmail(TenantId.SYS_TENANT_ID, oauth2User.getEmail()); | 87 | User user = userService.findUserByEmail(TenantId.SYS_TENANT_ID, oauth2User.getEmail()); |
83 | 88 | ||
84 | - if (user == null && !allowUserCreation) { | 89 | + if (user == null && !config.isAllowUserCreation()) { |
85 | throw new UsernameNotFoundException("User not found: " + oauth2User.getEmail()); | 90 | throw new UsernameNotFoundException("User not found: " + oauth2User.getEmail()); |
86 | } | 91 | } |
87 | 92 | ||
@@ -106,21 +111,28 @@ public abstract class AbstractOAuth2ClientMapper { | @@ -106,21 +111,28 @@ public abstract class AbstractOAuth2ClientMapper { | ||
106 | user.setFirstName(oauth2User.getFirstName()); | 111 | user.setFirstName(oauth2User.getFirstName()); |
107 | user.setLastName(oauth2User.getLastName()); | 112 | user.setLastName(oauth2User.getLastName()); |
108 | 113 | ||
114 | + ObjectNode additionalInfo = objectMapper.createObjectNode(); | ||
115 | + | ||
109 | if (!StringUtils.isEmpty(oauth2User.getDefaultDashboardName())) { | 116 | if (!StringUtils.isEmpty(oauth2User.getDefaultDashboardName())) { |
110 | Optional<DashboardId> dashboardIdOpt = | 117 | Optional<DashboardId> dashboardIdOpt = |
111 | user.getAuthority() == Authority.TENANT_ADMIN ? | 118 | user.getAuthority() == Authority.TENANT_ADMIN ? |
112 | getDashboardId(tenantId, oauth2User.getDefaultDashboardName()) | 119 | getDashboardId(tenantId, oauth2User.getDefaultDashboardName()) |
113 | : getDashboardId(tenantId, customerId, oauth2User.getDefaultDashboardName()); | 120 | : getDashboardId(tenantId, customerId, oauth2User.getDefaultDashboardName()); |
114 | if (dashboardIdOpt.isPresent()) { | 121 | if (dashboardIdOpt.isPresent()) { |
115 | - ObjectNode additionalInfo = objectMapper.createObjectNode(); | ||
116 | additionalInfo.put("defaultDashboardFullscreen", oauth2User.isAlwaysFullScreen()); | 122 | additionalInfo.put("defaultDashboardFullscreen", oauth2User.isAlwaysFullScreen()); |
117 | additionalInfo.put("defaultDashboardId", dashboardIdOpt.get().getId().toString()); | 123 | additionalInfo.put("defaultDashboardId", dashboardIdOpt.get().getId().toString()); |
118 | - user.setAdditionalInfo(additionalInfo); | ||
119 | } | 124 | } |
120 | } | 125 | } |
121 | 126 | ||
127 | + if (clientRegistration.getAdditionalInfo() != null && | ||
128 | + clientRegistration.getAdditionalInfo().has("providerName")) { | ||
129 | + additionalInfo.put("authProviderName", clientRegistration.getAdditionalInfo().get("providerName").asText()); | ||
130 | + } | ||
131 | + | ||
132 | + user.setAdditionalInfo(additionalInfo); | ||
133 | + | ||
122 | user = userService.saveUser(user); | 134 | user = userService.saveUser(user); |
123 | - if (activateUser) { | 135 | + if (config.isActivateUser()) { |
124 | UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId()); | 136 | UserCredentials userCredentials = userService.findUserCredentialsByUserId(user.getTenantId(), user.getId()); |
125 | userService.activateUserCredentials(user.getTenantId(), userCredentials.getActivateToken(), passwordEncoder.encode("")); | 137 | userService.activateUserCredentials(user.getTenantId(), userCredentials.getActivateToken(), passwordEncoder.encode("")); |
126 | } | 138 | } |
@@ -18,6 +18,7 @@ package org.thingsboard.server.service.security.auth.oauth2; | @@ -18,6 +18,7 @@ package org.thingsboard.server.service.security.auth.oauth2; | ||
18 | import lombok.extern.slf4j.Slf4j; | 18 | import lombok.extern.slf4j.Slf4j; |
19 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; | 19 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; |
20 | import org.springframework.stereotype.Service; | 20 | import org.springframework.stereotype.Service; |
21 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; | ||
21 | import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; | 22 | import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; |
22 | import org.thingsboard.server.dao.oauth2.OAuth2User; | 23 | import org.thingsboard.server.dao.oauth2.OAuth2User; |
23 | import org.thingsboard.server.service.security.model.SecurityUser; | 24 | import org.thingsboard.server.service.security.model.SecurityUser; |
@@ -29,11 +30,12 @@ import java.util.Map; | @@ -29,11 +30,12 @@ import java.util.Map; | ||
29 | public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper { | 30 | public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper { |
30 | 31 | ||
31 | @Override | 32 | @Override |
32 | - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2MapperConfig config) { | 33 | + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2ClientRegistrationInfo clientRegistration) { |
34 | + OAuth2MapperConfig config = clientRegistration.getMapperConfig(); | ||
33 | Map<String, Object> attributes = token.getPrincipal().getAttributes(); | 35 | Map<String, Object> attributes = token.getPrincipal().getAttributes(); |
34 | String email = BasicMapperUtils.getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); | 36 | String email = BasicMapperUtils.getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); |
35 | OAuth2User oauth2User = BasicMapperUtils.getOAuth2User(email, attributes, config); | 37 | OAuth2User oauth2User = BasicMapperUtils.getOAuth2User(email, attributes, config); |
36 | 38 | ||
37 | - return getOrCreateSecurityUserFromOAuth2User(oauth2User, config.isAllowUserCreation(), config.isActivateUser()); | 39 | + return getOrCreateSecurityUserFromOAuth2User(oauth2User, clientRegistration); |
38 | } | 40 | } |
39 | } | 41 | } |
@@ -23,6 +23,7 @@ import org.springframework.security.oauth2.client.authentication.OAuth2Authentic | @@ -23,6 +23,7 @@ import org.springframework.security.oauth2.client.authentication.OAuth2Authentic | ||
23 | import org.springframework.stereotype.Service; | 23 | import org.springframework.stereotype.Service; |
24 | import org.springframework.util.StringUtils; | 24 | import org.springframework.util.StringUtils; |
25 | import org.springframework.web.client.RestTemplate; | 25 | import org.springframework.web.client.RestTemplate; |
26 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; | ||
26 | import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; | 27 | import org.thingsboard.server.common.data.oauth2.OAuth2CustomMapperConfig; |
27 | import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; | 28 | import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; |
28 | import org.thingsboard.server.dao.oauth2.OAuth2User; | 29 | import org.thingsboard.server.dao.oauth2.OAuth2User; |
@@ -38,9 +39,10 @@ public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme | @@ -38,9 +39,10 @@ public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme | ||
38 | private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); | 39 | private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); |
39 | 40 | ||
40 | @Override | 41 | @Override |
41 | - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2MapperConfig config) { | 42 | + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2ClientRegistrationInfo clientRegistration) { |
43 | + OAuth2MapperConfig config = clientRegistration.getMapperConfig(); | ||
42 | OAuth2User oauth2User = getOAuth2User(token, providerAccessToken, config.getCustom()); | 44 | OAuth2User oauth2User = getOAuth2User(token, providerAccessToken, config.getCustom()); |
43 | - return getOrCreateSecurityUserFromOAuth2User(oauth2User, config.isAllowUserCreation(), config.isActivateUser()); | 45 | + return getOrCreateSecurityUserFromOAuth2User(oauth2User, clientRegistration); |
44 | } | 46 | } |
45 | 47 | ||
46 | private synchronized OAuth2User getOAuth2User(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2CustomMapperConfig custom) { | 48 | private synchronized OAuth2User getOAuth2User(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2CustomMapperConfig custom) { |
@@ -23,6 +23,7 @@ import org.springframework.boot.web.client.RestTemplateBuilder; | @@ -23,6 +23,7 @@ import org.springframework.boot.web.client.RestTemplateBuilder; | ||
23 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; | 23 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; |
24 | import org.springframework.stereotype.Service; | 24 | import org.springframework.stereotype.Service; |
25 | import org.springframework.web.client.RestTemplate; | 25 | import org.springframework.web.client.RestTemplate; |
26 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; | ||
26 | import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; | 27 | import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; |
27 | import org.thingsboard.server.dao.oauth2.OAuth2Configuration; | 28 | import org.thingsboard.server.dao.oauth2.OAuth2Configuration; |
28 | import org.thingsboard.server.dao.oauth2.OAuth2User; | 29 | import org.thingsboard.server.dao.oauth2.OAuth2User; |
@@ -45,12 +46,13 @@ public class GithubOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme | @@ -45,12 +46,13 @@ public class GithubOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme | ||
45 | private OAuth2Configuration oAuth2Configuration; | 46 | private OAuth2Configuration oAuth2Configuration; |
46 | 47 | ||
47 | @Override | 48 | @Override |
48 | - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2MapperConfig config) { | 49 | + public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2ClientRegistrationInfo clientRegistration) { |
50 | + OAuth2MapperConfig config = clientRegistration.getMapperConfig(); | ||
49 | Map<String, String> githubMapperConfig = oAuth2Configuration.getGithubMapper(); | 51 | Map<String, String> githubMapperConfig = oAuth2Configuration.getGithubMapper(); |
50 | String email = getEmail(githubMapperConfig.get(EMAIL_URL_KEY), providerAccessToken); | 52 | String email = getEmail(githubMapperConfig.get(EMAIL_URL_KEY), providerAccessToken); |
51 | Map<String, Object> attributes = token.getPrincipal().getAttributes(); | 53 | Map<String, Object> attributes = token.getPrincipal().getAttributes(); |
52 | OAuth2User oAuth2User = BasicMapperUtils.getOAuth2User(email, attributes, config); | 54 | OAuth2User oAuth2User = BasicMapperUtils.getOAuth2User(email, attributes, config); |
53 | - return getOrCreateSecurityUserFromOAuth2User(oAuth2User, config.isAllowUserCreation(), config.isActivateUser()); | 55 | + return getOrCreateSecurityUserFromOAuth2User(oAuth2User, clientRegistration); |
54 | } | 56 | } |
55 | 57 | ||
56 | private synchronized String getEmail(String emailUrl, String oauth2Token) { | 58 | private synchronized String getEmail(String emailUrl, String oauth2Token) { |
@@ -16,9 +16,9 @@ | @@ -16,9 +16,9 @@ | ||
16 | package org.thingsboard.server.service.security.auth.oauth2; | 16 | package org.thingsboard.server.service.security.auth.oauth2; |
17 | 17 | ||
18 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; | 18 | import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken; |
19 | -import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig; | 19 | +import org.thingsboard.server.common.data.oauth2.OAuth2ClientRegistrationInfo; |
20 | import org.thingsboard.server.service.security.model.SecurityUser; | 20 | import org.thingsboard.server.service.security.model.SecurityUser; |
21 | 21 | ||
22 | public interface OAuth2ClientMapper { | 22 | public interface OAuth2ClientMapper { |
23 | - SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2MapperConfig config); | 23 | + SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2ClientRegistrationInfo clientRegistration); |
24 | } | 24 | } |
@@ -74,7 +74,7 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS | @@ -74,7 +74,7 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS | ||
74 | token.getPrincipal().getName()); | 74 | token.getPrincipal().getName()); |
75 | OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(clientRegistration.getMapperConfig().getType()); | 75 | OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(clientRegistration.getMapperConfig().getType()); |
76 | SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, oAuth2AuthorizedClient.getAccessToken().getTokenValue(), | 76 | SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, oAuth2AuthorizedClient.getAccessToken().getTokenValue(), |
77 | - clientRegistration.getMapperConfig()); | 77 | + clientRegistration); |
78 | 78 | ||
79 | JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); | 79 | JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); |
80 | JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); | 80 | JwtToken refreshToken = refreshTokenRepository.requestRefreshToken(securityUser); |
@@ -85,4 +85,4 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS | @@ -85,4 +85,4 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS | ||
85 | URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.toString())); | 85 | URLEncoder.encode(e.getMessage(), StandardCharsets.UTF_8.toString())); |
86 | } | 86 | } |
87 | } | 87 | } |
88 | -} | ||
88 | +} |
@@ -33,11 +33,6 @@ | @@ -33,11 +33,6 @@ | ||
33 | <tb-anchor #entityDetailsForm></tb-anchor> | 33 | <tb-anchor #entityDetailsForm></tb-anchor> |
34 | </div> | 34 | </div> |
35 | <div mat-dialog-actions fxLayoutAlign="end center"> | 35 | <div mat-dialog-actions fxLayoutAlign="end center"> |
36 | - <button mat-raised-button color="primary" | ||
37 | - type="submit" | ||
38 | - [disabled]="(isLoading$ | async) || detailsForm?.invalid || !detailsForm?.dirty"> | ||
39 | - {{ 'action.add' | translate }} | ||
40 | - </button> | ||
41 | <button mat-button color="primary" | 36 | <button mat-button color="primary" |
42 | type="button" | 37 | type="button" |
43 | cdkFocusInitial | 38 | cdkFocusInitial |
@@ -45,5 +40,10 @@ | @@ -45,5 +40,10 @@ | ||
45 | (click)="cancel()"> | 40 | (click)="cancel()"> |
46 | {{ 'action.cancel' | translate }} | 41 | {{ 'action.cancel' | translate }} |
47 | </button> | 42 | </button> |
43 | + <button mat-raised-button color="primary" | ||
44 | + type="submit" | ||
45 | + [disabled]="(isLoading$ | async) || detailsForm?.invalid || !detailsForm?.dirty"> | ||
46 | + {{ 'action.add' | translate }} | ||
47 | + </button> | ||
48 | </div> | 48 | </div> |
49 | </form> | 49 | </form> |
@@ -15,5 +15,5 @@ | @@ -15,5 +15,5 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<div class="tb-filter-text" [ngClass]="{disabled: disabled, required: requiredClass}" | 18 | +<div class="tb-filter-text" [ngClass]="{disabled: disabled, required: requiredClass, nowrap: nowrap}" |
19 | [innerHTML]="filterText"></div> | 19 | [innerHTML]="filterText"></div> |
@@ -54,6 +54,9 @@ export class FilterTextComponent implements ControlValueAccessor, OnInit { | @@ -54,6 +54,9 @@ export class FilterTextComponent implements ControlValueAccessor, OnInit { | ||
54 | @Input() | 54 | @Input() |
55 | addFilterPrompt = this.translate.instant('filter.add-filter-prompt'); | 55 | addFilterPrompt = this.translate.instant('filter.add-filter-prompt'); |
56 | 56 | ||
57 | + @Input() | ||
58 | + nowrap = false; | ||
59 | + | ||
57 | requiredClass = false; | 60 | requiredClass = false; |
58 | 61 | ||
59 | private filterText: string; | 62 | private filterText: string; |
@@ -15,7 +15,7 @@ | @@ -15,7 +15,7 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<div style="min-width: 1000px;"> | 18 | +<div> |
19 | <mat-toolbar color="primary"> | 19 | <mat-toolbar color="primary"> |
20 | <h2 translate>device-profile.add</h2> | 20 | <h2 translate>device-profile.add</h2> |
21 | <span fxFlex></span> | 21 | <span fxFlex></span> |
@@ -106,28 +106,25 @@ | @@ -106,28 +106,25 @@ | ||
106 | </mat-step> | 106 | </mat-step> |
107 | </mat-horizontal-stepper> | 107 | </mat-horizontal-stepper> |
108 | </div> | 108 | </div> |
109 | - <div mat-dialog-actions fxLayout="column" fxLayoutAlign="start wrap" fxLayoutGap="8px" style="height: 100px;"> | ||
110 | - <div fxFlex fxLayout="row" fxLayoutAlign="end"> | ||
111 | - <button mat-raised-button | ||
112 | - *ngIf="showNext" | ||
113 | - [disabled]="(isLoading$ | async)" | ||
114 | - (click)="nextStep()">{{ 'action.next-with-label' | translate:{label: (getFormLabel(this.selectedIndex+1) | translate)} }}</button> | ||
115 | - </div> | ||
116 | - <div fxFlex fxLayout="row"> | ||
117 | - <button mat-button | ||
118 | - color="primary" | ||
119 | - [disabled]="(isLoading$ | async)" | ||
120 | - (click)="cancel()">{{ 'action.cancel' | translate }}</button> | ||
121 | - <span fxFlex></span> | ||
122 | - <div fxLayout="row wrap" fxLayoutGap="8px"> | ||
123 | - <button mat-raised-button *ngIf="selectedIndex > 0" | ||
124 | - [disabled]="(isLoading$ | async)" | ||
125 | - (click)="previousStep()">{{ 'action.back' | translate }}</button> | ||
126 | - <button mat-raised-button | ||
127 | - [disabled]="(isLoading$ | async)" | ||
128 | - color="primary" | ||
129 | - (click)="add()">{{ 'action.add' | translate }}</button> | ||
130 | - </div> | ||
131 | - </div> | 109 | + <div mat-dialog-actions fxLayout="row"> |
110 | + <button mat-stroked-button *ngIf="selectedIndex > 0" | ||
111 | + [disabled]="(isLoading$ | async)" | ||
112 | + (click)="previousStep()">{{ 'action.back' | translate }}</button> | ||
113 | + <span fxFlex></span> | ||
114 | + <button mat-stroked-button | ||
115 | + color="primary" | ||
116 | + *ngIf="showNext" | ||
117 | + [disabled]="(isLoading$ | async)" | ||
118 | + (click)="nextStep()">{{ 'action.next-with-label' | translate:{label: (getFormLabel(this.selectedIndex+1) | translate)} }}</button> | ||
119 | + </div> | ||
120 | + <mat-divider></mat-divider> | ||
121 | + <div mat-dialog-actions fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="end"> | ||
122 | + <button mat-button | ||
123 | + [disabled]="(isLoading$ | async)" | ||
124 | + (click)="cancel()">{{ 'action.cancel' | translate }}</button> | ||
125 | + <button mat-raised-button | ||
126 | + [disabled]="(isLoading$ | async)" | ||
127 | + color="primary" | ||
128 | + (click)="add()">{{ 'action.add' | translate }}</button> | ||
132 | </div> | 129 | </div> |
133 | </div> | 130 | </div> |
@@ -28,7 +28,7 @@ | @@ -28,7 +28,7 @@ | ||
28 | display: flex; | 28 | display: flex; |
29 | flex-direction: column; | 29 | flex-direction: column; |
30 | height: 100%; | 30 | height: 100%; |
31 | - padding: 24px 24px 8px !important; | 31 | + padding: 0 !important; |
32 | 32 | ||
33 | .mat-stepper-horizontal { | 33 | .mat-stepper-horizontal { |
34 | display: flex; | 34 | display: flex; |
@@ -45,7 +45,7 @@ | @@ -45,7 +45,7 @@ | ||
45 | } | 45 | } |
46 | } | 46 | } |
47 | .mat-horizontal-content-container { | 47 | .mat-horizontal-content-container { |
48 | - height: 350px; | 48 | + height: 530px; |
49 | max-height: 100%; | 49 | max-height: 100%; |
50 | width: 100%;; | 50 | width: 100%;; |
51 | overflow-y: auto; | 51 | overflow-y: auto; |
@@ -15,25 +15,22 @@ | @@ -15,25 +15,22 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<div fxLayout="column" fxFlex [formGroup]="alarmRuleConditionFormGroup"> | ||
19 | - <div fxLayout="row" fxLayoutAlign="start center" style="min-height: 40px;"> | ||
20 | - <label class="tb-title" translate>device-profile.condition</label> | ||
21 | - <span fxFlex></span> | ||
22 | - <a mat-button color="primary" | ||
23 | - type="button" | ||
24 | - (click)="openFilterDialog($event)" | ||
25 | - matTooltip="{{ (disabled ? 'action.view' : 'action.edit') | translate }}" | ||
26 | - matTooltipPosition="above"> | ||
27 | - {{ (disabled ? 'action.view' : (conditionSet() ? 'action.edit' : 'action.add')) | translate }} | ||
28 | - </a> | ||
29 | - </div> | 18 | +<div fxLayout="row" fxLayoutAlign="start center" [formGroup]="alarmRuleConditionFormGroup" style="min-width: 0;"> |
30 | <div class="tb-alarm-rule-condition" fxFlex fxLayout="column" fxLayoutAlign="center" (click)="openFilterDialog($event)"> | 19 | <div class="tb-alarm-rule-condition" fxFlex fxLayout="column" fxLayoutAlign="center" (click)="openFilterDialog($event)"> |
31 | <tb-filter-text formControlName="condition" | 20 | <tb-filter-text formControlName="condition" |
21 | + [nowrap]="true" | ||
32 | required | 22 | required |
33 | addFilterPrompt="{{'device-profile.enter-alarm-rule-condition-prompt' | translate}}"> | 23 | addFilterPrompt="{{'device-profile.enter-alarm-rule-condition-prompt' | translate}}"> |
34 | </tb-filter-text> | 24 | </tb-filter-text> |
35 | - <span class="tb-alarm-rule-condition-spec" [ngClass]="{disabled: this.disabled}" [innerHTML]="specText"> | 25 | + <span *ngIf="specText" class="tb-alarm-rule-condition-spec" [ngClass]="{disabled: this.disabled}" [innerHTML]="specText"> |
36 | </span> | 26 | </span> |
37 | </div> | 27 | </div> |
38 | - | 28 | + <button mat-icon-button |
29 | + [color]="conditionSet() ? 'primary' : 'warn'" | ||
30 | + type="button" | ||
31 | + (click)="openFilterDialog($event)" | ||
32 | + matTooltip="{{ (disabled ? 'action.view' : 'action.edit') | translate }}" | ||
33 | + matTooltipPosition="above"> | ||
34 | + <mat-icon>{{ disabled ? 'visibility' : (conditionSet() ? 'edit' : 'add') }}</mat-icon> | ||
35 | + </button> | ||
39 | </div> | 36 | </div> |
@@ -22,13 +22,10 @@ | @@ -22,13 +22,10 @@ | ||
22 | } | 22 | } |
23 | .tb-alarm-rule-condition { | 23 | .tb-alarm-rule-condition { |
24 | cursor: pointer; | 24 | cursor: pointer; |
25 | + min-width: 0; | ||
25 | .tb-alarm-rule-condition-spec { | 26 | .tb-alarm-rule-condition-spec { |
26 | - margin-top: 1em; | ||
27 | - line-height: 1.8em; | 27 | + opacity: 0.7; |
28 | padding: 4px; | 28 | padding: 4px; |
29 | - &.disabled { | ||
30 | - opacity: 0.7; | ||
31 | - } | ||
32 | } | 29 | } |
33 | } | 30 | } |
34 | } | 31 | } |
@@ -16,36 +16,23 @@ | @@ -16,36 +16,23 @@ | ||
16 | 16 | ||
17 | --> | 17 | --> |
18 | <div fxLayout="column" [formGroup]="alarmRuleFormGroup"> | 18 | <div fxLayout="column" [formGroup]="alarmRuleFormGroup"> |
19 | - <tb-alarm-rule-condition fxFlex class="row" | ||
20 | - formControlName="condition"> | 19 | + <tb-alarm-rule-condition formControlName="condition"> |
21 | </tb-alarm-rule-condition> | 20 | </tb-alarm-rule-condition> |
22 | - <mat-divider class="row"></mat-divider> | ||
23 | - <tb-alarm-schedule-info fxFlex class="row" | ||
24 | - formControlName="schedule"> | 21 | + <tb-alarm-schedule-info formControlName="schedule"> |
25 | </tb-alarm-schedule-info> | 22 | </tb-alarm-schedule-info> |
26 | - <mat-divider class="row"></mat-divider> | ||
27 | - <div fxLayout="column" fxFlex class="tb-alarm-rule-details row"> | ||
28 | - <div fxLayout="row" fxLayoutAlign="start center" style="min-height: 40px;"> | ||
29 | - <label class="tb-title" translate>device-profile.alarm-rule-details</label> | ||
30 | - <span fxFlex></span> | ||
31 | - <a mat-button color="primary" | ||
32 | - *ngIf="!disabled" | ||
33 | - type="button" | ||
34 | - (click)="openEditDetailsDialog($event)" | ||
35 | - matTooltip="{{ 'action.edit' | translate }}" | ||
36 | - matTooltipPosition="above"> | ||
37 | - {{ 'action.edit' | translate }} | ||
38 | - </a> | ||
39 | - </div> | ||
40 | - <div fxLayout="row" fxLayoutAlign="start start"> | ||
41 | - <div class="tb-alarm-rule-details-content" [ngClass]="{disabled: this.disabled, collapsed: !this.expandAlarmDetails}" | ||
42 | - (click)="!disabled ? openEditDetailsDialog($event) : {}" | ||
43 | - fxFlex [innerHTML]="alarmRuleFormGroup.get('alarmDetails').value"></div> | ||
44 | - <a mat-button color="primary" | ||
45 | - type="button" | ||
46 | - (click)="expandAlarmDetails = !expandAlarmDetails"> | ||
47 | - {{ (expandAlarmDetails ? 'action.hide' : 'action.read-more') | translate }} | ||
48 | - </a> | ||
49 | - </div> | 23 | + <div *ngIf="!disabled || alarmRuleFormGroup.get('alarmDetails').value" fxLayout="row" fxLayoutAlign="start center"> |
24 | + <span class="tb-alarm-rule-details title" (click)="openEditDetailsDialog($event)"> | ||
25 | + {{ alarmRuleFormGroup.get('alarmDetails').value ? ('device-profile.alarm-rule-details' | translate) + ': ' : ('device-profile.add-alarm-rule-details' | translate) }} | ||
26 | + </span> | ||
27 | + <span *ngIf="alarmRuleFormGroup.get('alarmDetails').value" class="tb-alarm-rule-details" | ||
28 | + (click)="openEditDetailsDialog($event)" | ||
29 | + [innerHTML]="alarmRuleFormGroup.get('alarmDetails').value"></span> | ||
30 | + <button mat-icon-button color="primary" | ||
31 | + type="button" | ||
32 | + (click)="openEditDetailsDialog($event)" | ||
33 | + matTooltip="{{ (disabled ? 'action.view' : 'action.edit') | translate }}" | ||
34 | + matTooltipPosition="above"> | ||
35 | + <mat-icon>{{ disabled ? 'visibility' : (alarmRuleFormGroup.get('alarmDetails').value ? 'edit' : 'add') }}</mat-icon> | ||
36 | + </button> | ||
50 | </div> | 37 | </div> |
51 | </div> | 38 | </div> |
@@ -14,31 +14,19 @@ | @@ -14,31 +14,19 @@ | ||
14 | * limitations under the License. | 14 | * limitations under the License. |
15 | */ | 15 | */ |
16 | :host { | 16 | :host { |
17 | + min-width: 0; | ||
17 | .row { | 18 | .row { |
18 | margin-top: 1em; | 19 | margin-top: 1em; |
19 | } | 20 | } |
20 | .tb-alarm-rule-details { | 21 | .tb-alarm-rule-details { |
21 | - a.mat-button { | ||
22 | - &:hover, &:focus { | ||
23 | - border-bottom: none; | ||
24 | - } | ||
25 | - } | ||
26 | - .tb-alarm-rule-details-content { | ||
27 | - min-height: 33px; | ||
28 | - overflow: hidden; | ||
29 | - white-space: pre; | ||
30 | - line-height: 1.8em; | ||
31 | - padding: 4px; | ||
32 | - cursor: pointer; | ||
33 | - &.collapsed { | ||
34 | - max-height: 33px; | ||
35 | - white-space: nowrap; | ||
36 | - text-overflow: ellipsis; | ||
37 | - } | ||
38 | - &.disabled { | ||
39 | - opacity: 0.7; | ||
40 | - cursor: auto; | ||
41 | - } | 22 | + padding: 4px; |
23 | + cursor: pointer; | ||
24 | + overflow: hidden; | ||
25 | + white-space: nowrap; | ||
26 | + text-overflow: ellipsis; | ||
27 | + &.title { | ||
28 | + opacity: 0.7; | ||
29 | + overflow: visible; | ||
42 | } | 30 | } |
43 | } | 31 | } |
44 | } | 32 | } |
@@ -118,7 +118,8 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat | @@ -118,7 +118,8 @@ export class AlarmRuleComponent implements ControlValueAccessor, OnInit, Validat | ||
118 | disableClose: true, | 118 | disableClose: true, |
119 | panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], | 119 | panelClass: ['tb-dialog', 'tb-fullscreen-dialog'], |
120 | data: { | 120 | data: { |
121 | - alarmDetails: this.alarmRuleFormGroup.get('alarmDetails').value | 121 | + alarmDetails: this.alarmRuleFormGroup.get('alarmDetails').value, |
122 | + readonly: this.disabled | ||
122 | } | 123 | } |
123 | }).afterClosed().subscribe((alarmDetails) => { | 124 | }).afterClosed().subscribe((alarmDetails) => { |
124 | if (isDefinedAndNotNull(alarmDetails)) { | 125 | if (isDefinedAndNotNull(alarmDetails)) { |
@@ -15,19 +15,16 @@ | @@ -15,19 +15,16 @@ | ||
15 | limitations under the License. | 15 | limitations under the License. |
16 | 16 | ||
17 | --> | 17 | --> |
18 | -<div fxLayout="column" fxFlex> | ||
19 | - <div fxLayout="row" fxLayoutAlign="start center" style="min-height: 40px;"> | ||
20 | - <label class="tb-title" translate>device-profile.schedule</label> | ||
21 | - <span fxFlex></span> | ||
22 | - <a mat-button color="primary" | ||
23 | - type="button" | ||
24 | - (click)="openScheduleDialog($event)" | ||
25 | - matTooltip="{{ (disabled ? 'action.view' : 'action.edit') | translate }}" | ||
26 | - matTooltipPosition="above"> | ||
27 | - {{ (disabled ? 'action.view' : 'action.edit' ) | translate }} | ||
28 | - </a> | ||
29 | - </div> | ||
30 | - <sapn class="tb-alarm-rule-schedule" [ngClass]="{disabled: this.disabled}" (click)="openScheduleDialog($event)" | 18 | +<div fxLayout="row" fxLayoutAlign="start center" style="min-width: 0;"> |
19 | + <span class="tb-alarm-rule-schedule title" (click)="openScheduleDialog($event)">{{('device-profile.schedule' | translate) + ': '}}</span> | ||
20 | + <span class="tb-alarm-rule-schedule" (click)="openScheduleDialog($event)" | ||
31 | [innerHTML]="scheduleText"> | 21 | [innerHTML]="scheduleText"> |
32 | - </sapn> | 22 | + </span> |
23 | + <button mat-icon-button color="primary" | ||
24 | + type="button" | ||
25 | + (click)="openScheduleDialog($event)" | ||
26 | + matTooltip="{{ (disabled ? 'action.view' : 'action.edit') | translate }}" | ||
27 | + matTooltipPosition="above"> | ||
28 | + <mat-icon>{{ disabled ? 'visibility' : 'edit' }}</mat-icon> | ||
29 | + </button> | ||
33 | </div> | 30 | </div> |
@@ -21,14 +21,14 @@ | @@ -21,14 +21,14 @@ | ||
21 | } | 21 | } |
22 | } | 22 | } |
23 | .tb-alarm-rule-schedule { | 23 | .tb-alarm-rule-schedule { |
24 | - line-height: 1.8em; | ||
25 | padding: 4px; | 24 | padding: 4px; |
26 | cursor: pointer; | 25 | cursor: pointer; |
27 | - &.disabled { | 26 | + overflow: hidden; |
27 | + white-space: nowrap; | ||
28 | + text-overflow: ellipsis; | ||
29 | + &.title { | ||
28 | opacity: 0.7; | 30 | opacity: 0.7; |
29 | - } | ||
30 | - .nowrap { | ||
31 | - white-space: nowrap; | 31 | + overflow: visible; |
32 | } | 32 | } |
33 | } | 33 | } |
34 | } | 34 | } |
@@ -101,7 +101,7 @@ export class AlarmScheduleInfoComponent implements ControlValueAccessor, OnInit | @@ -101,7 +101,7 @@ export class AlarmScheduleInfoComponent implements ControlValueAccessor, OnInit | ||
101 | for (const item of schedule.items) { | 101 | for (const item of schedule.items) { |
102 | if (item.enabled) { | 102 | if (item.enabled) { |
103 | if (this.scheduleText.length) { | 103 | if (this.scheduleText.length) { |
104 | - this.scheduleText += '<br/>'; | 104 | + this.scheduleText += ', '; |
105 | } | 105 | } |
106 | this.scheduleText += this.translate.instant(dayOfWeekTranslations[item.dayOfWeek - 1]); | 106 | this.scheduleText += this.translate.instant(dayOfWeekTranslations[item.dayOfWeek - 1]); |
107 | this.scheduleText += ' <b>' + getAlarmScheduleRangeText(utcTimestampToTimeOfDay(item.startsOn), | 107 | this.scheduleText += ' <b>' + getAlarmScheduleRangeText(utcTimestampToTimeOfDay(item.startsOn), |
@@ -19,7 +19,7 @@ | @@ -19,7 +19,7 @@ | ||
19 | <div *ngFor="let createAlarmRuleControl of createAlarmRulesFormArray().controls; let $index = index; | 19 | <div *ngFor="let createAlarmRuleControl of createAlarmRulesFormArray().controls; let $index = index; |
20 | last as isLast;" fxLayout="row" fxLayoutAlign="start center" | 20 | last as isLast;" fxLayout="row" fxLayoutAlign="start center" |
21 | fxLayoutGap="8px" style="padding-bottom: 8px;" [formGroup]="createAlarmRuleControl"> | 21 | fxLayoutGap="8px" style="padding-bottom: 8px;" [formGroup]="createAlarmRuleControl"> |
22 | - <div class="create-alarm-rule" fxFlex fxLayout="column" fxLayoutGap="8px" fxLayoutAlign="start"> | 22 | + <div class="create-alarm-rule" fxFlex fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="start"> |
23 | <mat-form-field class="severity mat-block" floatLabel="always" hideRequiredMarker> | 23 | <mat-form-field class="severity mat-block" floatLabel="always" hideRequiredMarker> |
24 | <mat-label translate>alarm.severity</mat-label> | 24 | <mat-label translate>alarm.severity</mat-label> |
25 | <mat-select formControlName="severity" | 25 | <mat-select formControlName="severity" |
@@ -34,7 +34,7 @@ | @@ -34,7 +34,7 @@ | ||
34 | {{ 'device-profile.alarm-severity-required' | translate }} | 34 | {{ 'device-profile.alarm-severity-required' | translate }} |
35 | </mat-error> | 35 | </mat-error> |
36 | </mat-form-field> | 36 | </mat-form-field> |
37 | - <mat-divider></mat-divider> | 37 | + <mat-divider vertical></mat-divider> |
38 | <tb-alarm-rule formControlName="alarmRule" required fxFlex> | 38 | <tb-alarm-rule formControlName="alarmRule" required fxFlex> |
39 | </tb-alarm-rule> | 39 | </tb-alarm-rule> |
40 | </div> | 40 | </div> |
@@ -98,7 +98,7 @@ | @@ -98,7 +98,7 @@ | ||
98 | <mat-icon>remove_circle_outline</mat-icon> | 98 | <mat-icon>remove_circle_outline</mat-icon> |
99 | </button> | 99 | </button> |
100 | </div> | 100 | </div> |
101 | - <div *ngIf="!alarmFormGroup.get('clearRule').value"> | 101 | + <div *ngIf="disabled && !alarmFormGroup.get('clearRule').value"> |
102 | <span translate fxLayoutAlign="center center" style="margin: 16px 0" | 102 | <span translate fxLayoutAlign="center center" style="margin: 16px 0" |
103 | class="tb-prompt">device-profile.no-clear-alarm-rule</span> | 103 | class="tb-prompt">device-profile.no-clear-alarm-rule</span> |
104 | </div> | 104 | </div> |
@@ -38,16 +38,16 @@ | @@ -38,16 +38,16 @@ | ||
38 | </fieldset> | 38 | </fieldset> |
39 | </div> | 39 | </div> |
40 | <div mat-dialog-actions fxLayoutAlign="end center"> | 40 | <div mat-dialog-actions fxLayoutAlign="end center"> |
41 | - <button mat-raised-button color="primary" | ||
42 | - type="submit" | ||
43 | - [disabled]="(isLoading$ | async) || editDetailsFormGroup.invalid || !editDetailsFormGroup.dirty"> | ||
44 | - {{ 'action.save' | translate }} | ||
45 | - </button> | ||
46 | <button mat-button color="primary" | 41 | <button mat-button color="primary" |
47 | type="button" | 42 | type="button" |
48 | [disabled]="(isLoading$ | async)" | 43 | [disabled]="(isLoading$ | async)" |
49 | (click)="cancel()" cdkFocusInitial> | 44 | (click)="cancel()" cdkFocusInitial> |
50 | {{ 'action.cancel' | translate }} | 45 | {{ 'action.cancel' | translate }} |
51 | </button> | 46 | </button> |
47 | + <button *ngIf="!data.readonly" mat-raised-button color="primary" | ||
48 | + type="submit" | ||
49 | + [disabled]="(isLoading$ | async) || editDetailsFormGroup.invalid || !editDetailsFormGroup.dirty"> | ||
50 | + {{ 'action.save' | translate }} | ||
51 | + </button> | ||
52 | </div> | 52 | </div> |
53 | </form> | 53 | </form> |
@@ -27,6 +27,7 @@ import { TranslateService } from '@ngx-translate/core'; | @@ -27,6 +27,7 @@ import { TranslateService } from '@ngx-translate/core'; | ||
27 | 27 | ||
28 | export interface EditAlarmDetailsDialogData { | 28 | export interface EditAlarmDetailsDialogData { |
29 | alarmDetails: string; | 29 | alarmDetails: string; |
30 | + readonly: boolean; | ||
30 | } | 31 | } |
31 | 32 | ||
32 | @Component({ | 33 | @Component({ |
@@ -57,6 +58,9 @@ export class EditAlarmDetailsDialogComponent extends DialogComponent<EditAlarmDe | @@ -57,6 +58,9 @@ export class EditAlarmDetailsDialogComponent extends DialogComponent<EditAlarmDe | ||
57 | this.editDetailsFormGroup = this.fb.group({ | 58 | this.editDetailsFormGroup = this.fb.group({ |
58 | alarmDetails: [this.alarmDetails] | 59 | alarmDetails: [this.alarmDetails] |
59 | }); | 60 | }); |
61 | + if (this.data.readonly) { | ||
62 | + this.editDetailsFormGroup.disable(); | ||
63 | + } | ||
60 | } | 64 | } |
61 | 65 | ||
62 | ngOnInit(): void { | 66 | ngOnInit(): void { |
@@ -151,28 +151,25 @@ | @@ -151,28 +151,25 @@ | ||
151 | </mat-step> | 151 | </mat-step> |
152 | </mat-horizontal-stepper> | 152 | </mat-horizontal-stepper> |
153 | </div> | 153 | </div> |
154 | - <div mat-dialog-actions fxLayout="column" fxLayoutAlign="start wrap" fxLayoutGap="8px" style="height: 100px;"> | ||
155 | - <div fxFlex fxLayout="row" fxLayoutAlign="end"> | ||
156 | - <button mat-raised-button | ||
157 | - *ngIf="showNext" | ||
158 | - [disabled]="(isLoading$ | async)" | ||
159 | - (click)="nextStep()">{{ 'action.next-with-label' | translate:{label: (getFormLabel(this.selectedIndex+1) | translate)} }}</button> | ||
160 | - </div> | ||
161 | - <div fxFlex fxLayout="row"> | ||
162 | - <button mat-button | ||
163 | - color="primary" | ||
164 | - [disabled]="(isLoading$ | async)" | ||
165 | - (click)="cancel()">{{ 'action.cancel' | translate }}</button> | ||
166 | - <span fxFlex></span> | ||
167 | - <div fxLayout="row wrap" fxLayoutGap="8px"> | ||
168 | - <button mat-raised-button *ngIf="selectedIndex > 0" | ||
169 | - [disabled]="(isLoading$ | async)" | ||
170 | - (click)="previousStep()">{{ 'action.back' | translate }}</button> | ||
171 | - <button mat-raised-button | ||
172 | - [disabled]="(isLoading$ | async)" | ||
173 | - color="primary" | ||
174 | - (click)="add()">{{ 'action.add' | translate }}</button> | ||
175 | - </div> | ||
176 | - </div> | 154 | + <div mat-dialog-actions fxLayout="row"> |
155 | + <button mat-stroked-button *ngIf="selectedIndex > 0" | ||
156 | + [disabled]="(isLoading$ | async)" | ||
157 | + (click)="previousStep()">{{ 'action.back' | translate }}</button> | ||
158 | + <span fxFlex></span> | ||
159 | + <button mat-stroked-button | ||
160 | + color="primary" | ||
161 | + *ngIf="showNext" | ||
162 | + [disabled]="(isLoading$ | async)" | ||
163 | + (click)="nextStep()">{{ 'action.next-with-label' | translate:{label: (getFormLabel(this.selectedIndex+1) | translate)} }}</button> | ||
164 | + </div> | ||
165 | + <mat-divider></mat-divider> | ||
166 | + <div mat-dialog-actions fxLayout="row" fxLayoutGap="8px" fxLayoutAlign="end"> | ||
167 | + <button mat-button | ||
168 | + [disabled]="(isLoading$ | async)" | ||
169 | + (click)="cancel()">{{ 'action.cancel' | translate }}</button> | ||
170 | + <button mat-raised-button | ||
171 | + [disabled]="(isLoading$ | async)" | ||
172 | + color="primary" | ||
173 | + (click)="add()">{{ 'action.add' | translate }}</button> | ||
177 | </div> | 174 | </div> |
178 | </div> | 175 | </div> |
@@ -29,6 +29,7 @@ | @@ -29,6 +29,7 @@ | ||
29 | display: flex; | 29 | display: flex; |
30 | flex-direction: column; | 30 | flex-direction: column; |
31 | height: 100%; | 31 | height: 100%; |
32 | + padding: 0 !important; | ||
32 | 33 | ||
33 | .mat-stepper-horizontal { | 34 | .mat-stepper-horizontal { |
34 | display: flex; | 35 | display: flex; |
@@ -45,7 +46,7 @@ | @@ -45,7 +46,7 @@ | ||
45 | } | 46 | } |
46 | } | 47 | } |
47 | .mat-horizontal-content-container { | 48 | .mat-horizontal-content-container { |
48 | - height: 450px; | 49 | + height: 530px; |
49 | max-height: 100%; | 50 | max-height: 100%; |
50 | width: 100%;; | 51 | width: 100%;; |
51 | overflow-y: auto; | 52 | overflow-y: auto; |
@@ -923,6 +923,7 @@ | @@ -923,6 +923,7 @@ | ||
923 | "condition-duration-time-unit-required": "Time unit is required.", | 923 | "condition-duration-time-unit-required": "Time unit is required.", |
924 | "advanced-settings": "Advanced settings", | 924 | "advanced-settings": "Advanced settings", |
925 | "alarm-rule-details": "Details", | 925 | "alarm-rule-details": "Details", |
926 | + "add-alarm-rule-details": "Add details", | ||
926 | "propagate-alarm": "Propagate alarm", | 927 | "propagate-alarm": "Propagate alarm", |
927 | "alarm-rule-relation-types-list": "Relation types to propagate", | 928 | "alarm-rule-relation-types-list": "Relation types to propagate", |
928 | "alarm-rule-relation-types-list-hint": "If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.", | 929 | "alarm-rule-relation-types-list-hint": "If Propagate relation types are not selected, alarms will be propagated without filtering by relation type.", |
@@ -944,14 +945,14 @@ | @@ -944,14 +945,14 @@ | ||
944 | "condition-type": "Condition type", | 945 | "condition-type": "Condition type", |
945 | "condition-type-simple": "Simple", | 946 | "condition-type-simple": "Simple", |
946 | "condition-type-duration": "Duration", | 947 | "condition-type-duration": "Duration", |
947 | - "condition-during": "During <b>{{during}}</b>", | 948 | + "condition-during": "During {{during}}", |
948 | "condition-type-repeating": "Repeating", | 949 | "condition-type-repeating": "Repeating", |
949 | "condition-type-required": "Condition type is required.", | 950 | "condition-type-required": "Condition type is required.", |
950 | "condition-repeating-value": "Count of events", | 951 | "condition-repeating-value": "Count of events", |
951 | "condition-repeating-value-range": "Count of events should be in a range from 1 to 2147483647.", | 952 | "condition-repeating-value-range": "Count of events should be in a range from 1 to 2147483647.", |
952 | "condition-repeating-value-pattern": "Count of events should be integers.", | 953 | "condition-repeating-value-pattern": "Count of events should be integers.", |
953 | "condition-repeating-value-required": "Count of events is required.", | 954 | "condition-repeating-value-required": "Count of events is required.", |
954 | - "condition-repeat-times": "Repeats <b>{ count, plural, 1 {1 time} other {# times} }</b>", | 955 | + "condition-repeat-times": "Repeats { count, plural, 1 {1 time} other {# times} }", |
955 | "schedule-type": "Scheduler type", | 956 | "schedule-type": "Scheduler type", |
956 | "schedule-type-required": "Scheduler type is required.", | 957 | "schedule-type-required": "Scheduler type is required.", |
957 | "schedule": "Schedule", | 958 | "schedule": "Schedule", |
@@ -869,10 +869,7 @@ mat-label { | @@ -869,10 +869,7 @@ mat-label { | ||
869 | } | 869 | } |
870 | .mat-dialog-actions { | 870 | .mat-dialog-actions { |
871 | margin-bottom: 0; | 871 | margin-bottom: 0; |
872 | - padding: 8px 8px 8px 16px; | ||
873 | - button:last-of-type{ | ||
874 | - margin-right: 20px; | ||
875 | - } | 872 | + padding: 8px; |
876 | } | 873 | } |
877 | } | 874 | } |
878 | } | 875 | } |