diff --git a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java index 0530f59c97..c6ff15c3ac 100644 --- a/application/src/main/java/org/thingsboard/server/controller/DashboardController.java +++ b/application/src/main/java/org/thingsboard/server/controller/DashboardController.java @@ -261,7 +261,7 @@ public class DashboardController extends BaseController { checkParameter(DASHBOARD_ID, strDashboardId); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER); - Set customerIds = customerIdFromStr(strCustomerIds, dashboard); + Set customerIds = customerIdFromStr(strCustomerIds); return tbDashboardService.updateDashboardCustomers(dashboard, customerIds, getCurrentUser()); } @@ -281,7 +281,7 @@ public class DashboardController extends BaseController { checkParameter(DASHBOARD_ID, strDashboardId); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); Dashboard dashboard = checkDashboardId(dashboardId, Operation.ASSIGN_TO_CUSTOMER); - Set customerIds = customerIdFromStr(strCustomerIds, dashboard); + Set customerIds = customerIdFromStr(strCustomerIds); return tbDashboardService.addDashboardCustomers(dashboard, customerIds, getCurrentUser()); } @@ -301,7 +301,7 @@ public class DashboardController extends BaseController { checkParameter(DASHBOARD_ID, strDashboardId); DashboardId dashboardId = new DashboardId(toUUID(strDashboardId)); Dashboard dashboard = checkDashboardId(dashboardId, Operation.UNASSIGN_FROM_CUSTOMER); - Set customerIds = customerIdFromStr(strCustomerIds, dashboard); + Set customerIds = customerIdFromStr(strCustomerIds); return tbDashboardService.removeDashboardCustomers(dashboard, customerIds, getCurrentUser()); } @@ -704,14 +704,11 @@ public class DashboardController extends BaseController { } } - private Set customerIdFromStr(String[] strCustomerIds, Dashboard dashboard) { + private Set customerIdFromStr(String[] strCustomerIds) { Set customerIds = new HashSet<>(); if (strCustomerIds != null) { for (String strCustomerId : strCustomerIds) { - CustomerId customerId = new CustomerId(UUID.fromString(strCustomerId)); - if (dashboard.isAssignedToCustomer(customerId)) { - customerIds.add(customerId); - } + customerIds.add(new CustomerId(UUID.fromString(strCustomerId))); } } return customerIds; diff --git a/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java b/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java index 741a89749f..5d9b4290f4 100644 --- a/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java +++ b/application/src/main/java/org/thingsboard/server/service/entitiy/dashboard/DefaultTbDashboardService.java @@ -183,11 +183,17 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement TenantId tenantId = dashboard.getTenantId(); DashboardId dashboardId = dashboard.getId(); try { - if (customerIds.isEmpty()) { + Set addedCustomerIds = new HashSet<>(); + for (CustomerId customerId : customerIds) { + if (!dashboard.isAssignedToCustomer(customerId)) { + addedCustomerIds.add(customerId); + } + } + if (addedCustomerIds.isEmpty()) { return dashboard; } else { Dashboard savedDashboard = null; - for (CustomerId customerId : customerIds) { + for (CustomerId customerId : addedCustomerIds) { savedDashboard = checkNotNull(dashboardService.assignDashboardToCustomer(tenantId, dashboardId, customerId)); ShortCustomerInfo customerInfo = savedDashboard.getAssignedCustomerInfo(customerId); notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, customerId, savedDashboard, @@ -207,11 +213,17 @@ public class DefaultTbDashboardService extends AbstractTbEntityService implement TenantId tenantId = dashboard.getTenantId(); DashboardId dashboardId = dashboard.getId(); try { - if (customerIds.isEmpty()) { + Set removedCustomerIds = new HashSet<>(); + for (CustomerId customerId : customerIds) { + if (dashboard.isAssignedToCustomer(customerId)) { + removedCustomerIds.add(customerId); + } + } + if (removedCustomerIds.isEmpty()) { return dashboard; } else { Dashboard savedDashboard = null; - for (CustomerId customerId : customerIds) { + for (CustomerId customerId : removedCustomerIds) { ShortCustomerInfo customerInfo = dashboard.getAssignedCustomerInfo(customerId); savedDashboard = checkNotNull(dashboardService.unassignDashboardFromCustomer(tenantId, dashboardId, customerId)); notificationEntityService.notifyAssignOrUnassignEntityToCustomer(tenantId, dashboardId, customerId, savedDashboard, diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java index 34dfcab964..8d6af88c59 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/query/DefaultEntityQueryRepository.java @@ -543,6 +543,8 @@ public class DefaultEntityQueryRepository implements EntityQueryRepository { ctx.addUuidParameter("permissions_customer_id", ctx.getCustomerId().getId()); if (ctx.getEntityType() == EntityType.CUSTOMER) { return "e.tenant_id=:permissions_tenant_id and e.id=:permissions_customer_id"; + } else if (ctx.getEntityType() == EntityType.API_USAGE_STATE) { + return "e.tenant_id=:permissions_tenant_id and e.entity_id=:permissions_customer_id"; } else { return "e.tenant_id=:permissions_tenant_id and e.customer_id=:permissions_customer_id"; } diff --git a/ui-ngx/src/app/shared/components/phone-input.component.html b/ui-ngx/src/app/shared/components/phone-input.component.html index da727bed90..87ce1f6c1b 100644 --- a/ui-ngx/src/app/shared/components/phone-input.component.html +++ b/ui-ngx/src/app/shared/components/phone-input.component.html @@ -35,7 +35,6 @@ type="tel" matInput placeholder="{{ placeholder | translate }}" - [pattern]="phoneNumberPattern" (focus)="focus()" autocomplete="off" [required]="required"> diff --git a/ui-ngx/src/app/shared/components/phone-input.component.ts b/ui-ngx/src/app/shared/components/phone-input.component.ts index 5fae7c0d0f..45f8151d5f 100644 --- a/ui-ngx/src/app/shared/components/phone-input.component.ts +++ b/ui-ngx/src/app/shared/components/phone-input.component.ts @@ -30,7 +30,6 @@ import { import { TranslateService } from '@ngx-translate/core'; import { Country, CountryData } from '@shared/models/country.models'; import examples from 'libphonenumber-js/examples.mobile.json'; -import { phoneNumberPattern } from '@shared/models/settings.models'; import { Subscription } from 'rxjs'; import { FloatLabelType, MatFormFieldAppearance } from '@angular/material/form-field/form-field'; @@ -82,7 +81,6 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida phonePlaceholder = '+12015550123'; flagIcon: string; phoneFormGroup: FormGroup; - phoneNumberPattern = phoneNumberPattern; private isLoading = true; get isLoad(): boolean { @@ -103,7 +101,8 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida private baseCode = 127397; private countryCallingCode = '+'; private modelValue: string; - private valueChange$: Subscription = null; + private changeSubscriptions: Subscription[] = []; + private propagateChange = (v: any) => { }; constructor(private translate: TranslateService, @@ -116,7 +115,7 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida } ngOnInit(): void { - const validators: ValidatorFn[] = [Validators.pattern(phoneNumberPattern), this.validatePhoneNumber()]; + const validators: ValidatorFn[] = [(c: FormControl) => Validators.pattern(this.getPhoneNumberPattern())(c), this.validatePhoneNumber()]; if (this.required) { validators.push(Validators.required); } @@ -125,12 +124,12 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida phoneNumber: [null, validators] }); - this.valueChange$ = this.phoneFormGroup.get('phoneNumber').valueChanges.subscribe(value => { + this.changeSubscriptions.push(this.phoneFormGroup.get('phoneNumber').valueChanges.subscribe(value => { this.updateModel(); this.defineCountryFromNumber(value); - }); + })); - this.phoneFormGroup.get('country').valueChanges.subscribe(value => { + this.changeSubscriptions.push(this.phoneFormGroup.get('country').valueChanges.subscribe(value => { if (value) { const code = this.countryCallingCode; this.getFlagAndPhoneNumberData(value); @@ -142,21 +141,23 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida } } } - }); + })); } ngOnDestroy() { - if (this.valueChange$) { - this.valueChange$.unsubscribe(); + for (const subscription of this.changeSubscriptions) { + subscription.unsubscribe(); } } focus() { const phoneNumber = this.phoneFormGroup.get('phoneNumber'); - this.phoneFormGroup.markAsPristine(); - this.phoneFormGroup.markAsUntouched(); if (!phoneNumber.value) { - phoneNumber.patchValue(this.countryCallingCode); + phoneNumber.patchValue(this.countryCallingCode, {emitEvent: false}); + } + if (phoneNumber.untouched && this.countryCallingCode !== phoneNumber.value) { + phoneNumber.markAsTouched(); + phoneNumber.updateValueAndValidity(); } } @@ -182,7 +183,7 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida validatePhoneNumber(): ValidatorFn { return (c: FormControl) => { const phoneNumber = c.value; - if (phoneNumber && this.parsePhoneNumberFromString) { + if (phoneNumber && this.countryCallingCode !== phoneNumber && this.parsePhoneNumberFromString) { const parsedPhoneNumber = this.parsePhoneNumberFromString(phoneNumber); if (!parsedPhoneNumber?.isValid() || !parsedPhoneNumber?.isPossible()) { return { @@ -206,8 +207,13 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida } } + private getPhoneNumberPattern(): RegExp { + return new RegExp(`^${this.countryCallingCode.replace('+', '\\+')}$|^\\+[1-9]\\d{1,14}$`); + } + validate(): ValidationErrors | null { - return this.phoneFormGroup.get('phoneNumber').valid ? null : { + const phoneNumber = this.phoneFormGroup.get('phoneNumber'); + return phoneNumber.valid || phoneNumber.untouched || this.countryCallingCode === phoneNumber.value ? null : { phoneFormGroup: false }; } @@ -232,18 +238,18 @@ export class PhoneInputComponent implements OnInit, ControlValueAccessor, Valida this.modelValue = phoneNumber; let country = this.defaultCountry; if (this.parsePhoneNumberFromString) { - country = phoneNumber ? this.parsePhoneNumberFromString(phoneNumber)?.country : this.defaultCountry; + country = phoneNumber ? this.parsePhoneNumberFromString(phoneNumber)?.country || this.defaultCountry : this.defaultCountry; this.getFlagAndPhoneNumberData(country); } - this.phoneFormGroup.patchValue({phoneNumber, country}, {emitEvent: !phoneNumber}); + this.phoneFormGroup.reset({phoneNumber, country}, {emitEvent: false}); } private updateModel() { const phoneNumber = this.phoneFormGroup.get('phoneNumber'); - if (phoneNumber.valid && phoneNumber.value) { + if (phoneNumber.valid) { this.modelValue = phoneNumber.value; this.propagateChange(this.modelValue); - } else if (phoneNumber.invalid && phoneNumber.value) { + } else { this.propagateChange(null); } }