7 changed files with 248 additions and 71 deletions
@ -0,0 +1,41 @@ |
|||||
|
<!-- |
||||
|
|
||||
|
Copyright © 2016-2023 The Thingsboard Authors |
||||
|
|
||||
|
Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
you may not use this file except in compliance with the License. |
||||
|
You may obtain a copy of the License at |
||||
|
|
||||
|
http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
|
||||
|
Unless required by applicable law or agreed to in writing, software |
||||
|
distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
See the License for the specific language governing permissions and |
||||
|
limitations under the License. |
||||
|
|
||||
|
--> |
||||
|
|
||||
|
<div class="tb-assignee" fxLayout="row" fxLayoutAlign="start center" |
||||
|
(click)="openAlarmAssigneePanel($event, alarm)"> |
||||
|
<span *ngIf="alarm.assigneeId" class="assigned-container"> |
||||
|
<span class="user-avatar" [style.backgroundColor]="getAvatarBgColor(alarm.assignee)"> |
||||
|
{{ getUserInitials(alarm.assignee) }} |
||||
|
</span> |
||||
|
<span [matTooltip]="getUserDisplayName(alarm.assignee)" |
||||
|
matTooltipPosition="above" |
||||
|
style="text-overflow: ellipsis">{{ getUserDisplayName(alarm.assignee) }}</span> |
||||
|
</span> |
||||
|
<span *ngIf="!alarm.assigneeId" class="unassigned-container" fxLayout="row" fxLayoutAlign="start center"> |
||||
|
<mat-icon class="material-icons unassigned-icon">account_circle</mat-icon> |
||||
|
<span translate>alarm.unassigned</span> |
||||
|
</span> |
||||
|
<button fxFlexAlign="flex-end" |
||||
|
mat-icon-button |
||||
|
matTooltip="{{ 'alarm.assign' | translate }}" |
||||
|
matTooltipPosition="above"> |
||||
|
<mat-icon> |
||||
|
keyboard_arrow_down |
||||
|
</mat-icon> |
||||
|
</button> |
||||
|
</div> |
||||
@ -0,0 +1,51 @@ |
|||||
|
/** |
||||
|
* Copyright © 2016-2023 The Thingsboard Authors |
||||
|
* |
||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||
|
* you may not use this file except in compliance with the License. |
||||
|
* You may obtain a copy of the License at |
||||
|
* |
||||
|
* http://www.apache.org/licenses/LICENSE-2.0 |
||||
|
* |
||||
|
* Unless required by applicable law or agreed to in writing, software |
||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||
|
* See the License for the specific language governing permissions and |
||||
|
* limitations under the License. |
||||
|
*/ |
||||
|
|
||||
|
:host { |
||||
|
.tb-assignee { |
||||
|
cursor: pointer; |
||||
|
|
||||
|
.assigned-container { |
||||
|
max-width: 233px; |
||||
|
overflow: hidden; |
||||
|
white-space: nowrap; |
||||
|
text-overflow: ellipsis; |
||||
|
|
||||
|
.user-avatar { |
||||
|
display: inline-flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
margin-right: 8px; |
||||
|
border-radius: 50%; |
||||
|
width: 28px; |
||||
|
height: 28px; |
||||
|
min-width: 28px; |
||||
|
min-height: 28px; |
||||
|
color: white; |
||||
|
font-size: 13px; |
||||
|
font-weight: 700; |
||||
|
} |
||||
|
} |
||||
|
.material-icons.unassigned-icon { |
||||
|
width: 28px; |
||||
|
height: 28px; |
||||
|
font-size: 28px; |
||||
|
margin-right: 8px; |
||||
|
color: rgba(0, 0, 0, 0.38); |
||||
|
overflow: visible; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,141 @@ |
|||||
|
///
|
||||
|
/// Copyright © 2016-2023 The Thingsboard Authors
|
||||
|
///
|
||||
|
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
|
/// you may not use this file except in compliance with the License.
|
||||
|
/// You may obtain a copy of the License at
|
||||
|
///
|
||||
|
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
///
|
||||
|
/// Unless required by applicable law or agreed to in writing, software
|
||||
|
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
|
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
|
/// See the License for the specific language governing permissions and
|
||||
|
/// limitations under the License.
|
||||
|
///
|
||||
|
|
||||
|
import { |
||||
|
Component, EventEmitter, Injector, Input, Output, StaticProvider, ViewContainerRef |
||||
|
} from '@angular/core'; |
||||
|
import { UtilsService } from '@core/services/utils.service'; |
||||
|
import { AlarmAssignee, AlarmInfo } from '@shared/models/alarm.models'; |
||||
|
import { |
||||
|
ALARM_ASSIGNEE_PANEL_DATA, AlarmAssigneePanelComponent, |
||||
|
AlarmAssigneePanelData |
||||
|
} from '@home/components/alarm/alarm-assignee-panel.component'; |
||||
|
import { ConnectedPosition, Overlay, OverlayConfig, OverlayRef } from '@angular/cdk/overlay'; |
||||
|
import { ComponentPortal } from '@angular/cdk/portal'; |
||||
|
|
||||
|
@Component({ |
||||
|
selector: 'tb-alarm-assignee', |
||||
|
templateUrl: './alarm-assignee.component.html', |
||||
|
styleUrls: ['./alarm-assignee.component.scss'] |
||||
|
}) |
||||
|
export class AlarmAssigneeComponent { |
||||
|
@Input() |
||||
|
alarm: AlarmInfo; |
||||
|
|
||||
|
@Output() |
||||
|
alarmReassigned = new EventEmitter<boolean>(); |
||||
|
|
||||
|
constructor(private utilsService: UtilsService, |
||||
|
private overlay: Overlay, |
||||
|
private viewContainerRef: ViewContainerRef) { |
||||
|
} |
||||
|
|
||||
|
getUserDisplayName(entity: AlarmAssignee) { |
||||
|
let displayName = ''; |
||||
|
if ((entity.firstName && entity.firstName.length > 0) || |
||||
|
(entity.lastName && entity.lastName.length > 0)) { |
||||
|
if (entity.firstName) { |
||||
|
displayName += entity.firstName; |
||||
|
} |
||||
|
if (entity.lastName) { |
||||
|
if (displayName.length > 0) { |
||||
|
displayName += ' '; |
||||
|
} |
||||
|
displayName += entity.lastName; |
||||
|
} |
||||
|
} else { |
||||
|
displayName = entity.email; |
||||
|
} |
||||
|
return displayName; |
||||
|
} |
||||
|
|
||||
|
getUserInitials(entity: AlarmAssignee): string { |
||||
|
let initials = ''; |
||||
|
if (entity.firstName && entity.firstName.length || |
||||
|
entity.lastName && entity.lastName.length) { |
||||
|
if (entity.firstName) { |
||||
|
initials += entity.firstName.charAt(0); |
||||
|
} |
||||
|
if (entity.lastName) { |
||||
|
initials += entity.lastName.charAt(0); |
||||
|
} |
||||
|
} else { |
||||
|
initials += entity.email.charAt(0); |
||||
|
} |
||||
|
return initials.toUpperCase(); |
||||
|
} |
||||
|
|
||||
|
getFullName(entity: AlarmAssignee): string { |
||||
|
let fullName = ''; |
||||
|
if ((entity.firstName && entity.firstName.length > 0) || |
||||
|
(entity.lastName && entity.lastName.length > 0)) { |
||||
|
if (entity.firstName) { |
||||
|
fullName += entity.firstName; |
||||
|
} |
||||
|
if (entity.lastName) { |
||||
|
if (fullName.length > 0) { |
||||
|
fullName += ' '; |
||||
|
} |
||||
|
fullName += entity.lastName; |
||||
|
} |
||||
|
} |
||||
|
return fullName; |
||||
|
} |
||||
|
|
||||
|
getAvatarBgColor(entity: AlarmAssignee) { |
||||
|
return this.utilsService.stringToHslColor(this.getUserDisplayName(entity), 40, 60); |
||||
|
} |
||||
|
|
||||
|
openAlarmAssigneePanel($event: Event, alarm: AlarmInfo) { |
||||
|
if ($event) { |
||||
|
$event.stopPropagation(); |
||||
|
} |
||||
|
const target = $event.target || $event.srcElement || $event.currentTarget; |
||||
|
const config = new OverlayConfig(); |
||||
|
config.backdropClass = 'cdk-overlay-transparent-backdrop'; |
||||
|
config.hasBackdrop = true; |
||||
|
const connectedPosition: ConnectedPosition = { |
||||
|
originX: 'end', |
||||
|
originY: 'bottom', |
||||
|
overlayX: 'end', |
||||
|
overlayY: 'top' |
||||
|
}; |
||||
|
config.positionStrategy = this.overlay.position().flexibleConnectedTo(target as HTMLElement) |
||||
|
.withPositions([connectedPosition]); |
||||
|
config.minWidth = '260px'; |
||||
|
const overlayRef = this.overlay.create(config); |
||||
|
overlayRef.backdropClick().subscribe(() => { |
||||
|
overlayRef.dispose(); |
||||
|
}); |
||||
|
const providers: StaticProvider[] = [ |
||||
|
{ |
||||
|
provide: ALARM_ASSIGNEE_PANEL_DATA, |
||||
|
useValue: { |
||||
|
alarmId: alarm.id.id, |
||||
|
assigneeId: alarm.assigneeId?.id |
||||
|
} as AlarmAssigneePanelData |
||||
|
}, |
||||
|
{ |
||||
|
provide: OverlayRef, |
||||
|
useValue: overlayRef |
||||
|
} |
||||
|
]; |
||||
|
const injector = Injector.create({parent: this.viewContainerRef.injector, providers}); |
||||
|
overlayRef.attach(new ComponentPortal(AlarmAssigneePanelComponent, |
||||
|
this.viewContainerRef, injector)).onDestroy(() => this.alarmReassigned.emit(true)); |
||||
|
} |
||||
|
|
||||
|
} |
||||
Loading…
Reference in new issue