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