mirror of https://github.com/apache/nifi.git
NIFI-13047: Adding property history to the property tooltip (#8652)
* NIFI-13047: - Adding property history to the property tooltip in the Edit dialogs for Processors, Controller Services, Reporting Tasks, Parameter Providers, and Flow Analysis Rules. * NIFI-13054: - Addressing review feedback. This closes #8652
This commit is contained in:
parent
44b1353440
commit
05558ca2de
|
@ -30,6 +30,7 @@ import { EditControllerService } from '../../../../ui/common/controller-service/
|
|||
import {
|
||||
ComponentType,
|
||||
ControllerServiceReferencingComponent,
|
||||
EditControllerServiceDialogRequest,
|
||||
UpdateControllerServiceRequest
|
||||
} from '../../../../state/shared';
|
||||
import { Router } from '@angular/router';
|
||||
|
@ -196,6 +197,30 @@ export class ControllerServicesEffects {
|
|||
this.actions$.pipe(
|
||||
ofType(ControllerServicesActions.openConfigureControllerServiceDialog),
|
||||
map((action) => action.request),
|
||||
concatLatestFrom(() => this.store.select(selectCurrentProcessGroupId)),
|
||||
switchMap(([request, processGroupId]) =>
|
||||
from(this.propertyTableHelperService.getComponentHistory(request.id)).pipe(
|
||||
map((history) => {
|
||||
return {
|
||||
...request,
|
||||
history: history.componentHistory
|
||||
} as EditControllerServiceDialogRequest;
|
||||
}),
|
||||
tap({
|
||||
error: (errorResponse: HttpErrorResponse) => {
|
||||
this.store.dispatch(
|
||||
ControllerServicesActions.selectControllerService({
|
||||
request: {
|
||||
processGroupId,
|
||||
id: request.id
|
||||
}
|
||||
})
|
||||
);
|
||||
this.store.dispatch(ErrorActions.snackBarError({ error: errorResponse.error }));
|
||||
}
|
||||
})
|
||||
)
|
||||
),
|
||||
concatLatestFrom(() => [
|
||||
this.store.select(selectParameterContext),
|
||||
this.store.select(selectCurrentProcessGroupId)
|
||||
|
@ -205,9 +230,7 @@ export class ControllerServicesEffects {
|
|||
|
||||
const editDialogReference = this.dialog.open(EditControllerService, {
|
||||
...LARGE_DIALOG,
|
||||
data: {
|
||||
controllerService: request.controllerService
|
||||
},
|
||||
data: request,
|
||||
id: serviceId
|
||||
});
|
||||
|
||||
|
|
|
@ -1102,11 +1102,15 @@ export class FlowEffects {
|
|||
ofType(FlowActions.openEditProcessorDialog),
|
||||
map((action) => action.request),
|
||||
switchMap((request) =>
|
||||
from(this.flowService.getProcessor(request.entity.id)).pipe(
|
||||
map((entity) => {
|
||||
combineLatest([
|
||||
this.flowService.getProcessor(request.entity.id),
|
||||
this.propertyTableHelperService.getComponentHistory(request.entity.id)
|
||||
]).pipe(
|
||||
map(([entity, history]) => {
|
||||
return {
|
||||
...request,
|
||||
entity
|
||||
entity,
|
||||
history: history.componentHistory
|
||||
};
|
||||
}),
|
||||
tap({
|
||||
|
|
|
@ -19,6 +19,7 @@ import { BreadcrumbEntity, Position } from '../shared';
|
|||
import {
|
||||
BulletinEntity,
|
||||
Bundle,
|
||||
ComponentHistory,
|
||||
ComponentType,
|
||||
DocumentedType,
|
||||
ParameterContextReferenceEntity,
|
||||
|
@ -346,6 +347,7 @@ export interface EditComponentDialogRequest {
|
|||
type: ComponentType;
|
||||
uri: string;
|
||||
entity: any;
|
||||
history?: ComponentHistory;
|
||||
}
|
||||
|
||||
export interface EditRemotePortDialogRequest extends EditComponentDialogRequest {
|
||||
|
|
|
@ -170,6 +170,7 @@
|
|||
[parameterContext]="parameterContext"
|
||||
[convertToParameter]="convertToParameter"
|
||||
[goToService]="goToService"
|
||||
[propertyHistory]="request.history"
|
||||
[supportsSensitiveDynamicProperties]="
|
||||
request.entity.component.supportsSensitiveDynamicProperties
|
||||
"></property-table>
|
||||
|
|
|
@ -31,7 +31,7 @@ import { Router } from '@angular/router';
|
|||
import { selectSaving } from '../management-controller-services/management-controller-services.selectors';
|
||||
import { UpdateControllerServiceRequest } from '../../../../state/shared';
|
||||
import { EditFlowAnalysisRule } from '../../ui/flow-analysis-rules/edit-flow-analysis-rule/edit-flow-analysis-rule.component';
|
||||
import { CreateFlowAnalysisRuleSuccess } from './index';
|
||||
import { CreateFlowAnalysisRuleSuccess, EditFlowAnalysisRuleDialogRequest } from './index';
|
||||
import { PropertyTableHelperService } from '../../../../service/property-table-helper.service';
|
||||
import * as ErrorActions from '../../../../state/error/error.actions';
|
||||
import { ErrorHelper } from '../../../../service/error-helper.service';
|
||||
|
@ -218,14 +218,38 @@ export class FlowAnalysisRulesEffects {
|
|||
this.actions$.pipe(
|
||||
ofType(FlowAnalysisRuleActions.openConfigureFlowAnalysisRuleDialog),
|
||||
map((action) => action.request),
|
||||
switchMap((request) =>
|
||||
from(this.propertyTableHelperService.getComponentHistory(request.id)).pipe(
|
||||
map((history) => {
|
||||
return {
|
||||
...request,
|
||||
history: history.componentHistory
|
||||
} as EditFlowAnalysisRuleDialogRequest;
|
||||
}),
|
||||
tap({
|
||||
error: (errorResponse: HttpErrorResponse) => {
|
||||
this.store.dispatch(
|
||||
FlowAnalysisRuleActions.selectFlowAnalysisRule({
|
||||
request: {
|
||||
id: request.id
|
||||
}
|
||||
})
|
||||
);
|
||||
this.store.dispatch(
|
||||
FlowAnalysisRuleActions.flowAnalysisRuleSnackbarApiError({
|
||||
error: errorResponse.error
|
||||
})
|
||||
);
|
||||
}
|
||||
})
|
||||
)
|
||||
),
|
||||
tap((request) => {
|
||||
const ruleId: string = request.id;
|
||||
|
||||
const editDialogReference = this.dialog.open(EditFlowAnalysisRule, {
|
||||
...LARGE_DIALOG,
|
||||
data: {
|
||||
flowAnalysisRule: request.flowAnalysisRule
|
||||
},
|
||||
data: request,
|
||||
id: ruleId
|
||||
});
|
||||
|
||||
|
|
|
@ -15,7 +15,14 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BulletinEntity, Bundle, DocumentedType, Permissions, Revision } from '../../../../state/shared';
|
||||
import {
|
||||
BulletinEntity,
|
||||
Bundle,
|
||||
ComponentHistory,
|
||||
DocumentedType,
|
||||
Permissions,
|
||||
Revision
|
||||
} from '../../../../state/shared';
|
||||
|
||||
export const flowAnalysisRulesFeatureKey = 'flowAnalysisRules';
|
||||
|
||||
|
@ -88,6 +95,7 @@ export interface ConfigureFlowAnalysisRuleRequest {
|
|||
export interface EditFlowAnalysisRuleDialogRequest {
|
||||
id: string;
|
||||
flowAnalysisRule: FlowAnalysisRuleEntity;
|
||||
history?: ComponentHistory;
|
||||
}
|
||||
|
||||
export interface DeleteFlowAnalysisRuleRequest {
|
||||
|
|
|
@ -32,6 +32,7 @@ import { EditControllerService } from '../../../../ui/common/controller-service/
|
|||
import {
|
||||
ComponentType,
|
||||
ControllerServiceReferencingComponent,
|
||||
EditControllerServiceDialogRequest,
|
||||
UpdateControllerServiceRequest
|
||||
} from '../../../../state/shared';
|
||||
import { Router } from '@angular/router';
|
||||
|
@ -188,14 +189,38 @@ export class ManagementControllerServicesEffects {
|
|||
this.actions$.pipe(
|
||||
ofType(ManagementControllerServicesActions.openConfigureControllerServiceDialog),
|
||||
map((action) => action.request),
|
||||
switchMap((request) =>
|
||||
from(this.propertyTableHelperService.getComponentHistory(request.id)).pipe(
|
||||
map((history) => {
|
||||
return {
|
||||
...request,
|
||||
history: history.componentHistory
|
||||
} as EditControllerServiceDialogRequest;
|
||||
}),
|
||||
tap({
|
||||
error: (errorResponse: HttpErrorResponse) => {
|
||||
this.store.dispatch(
|
||||
ManagementControllerServicesActions.selectControllerService({
|
||||
request: {
|
||||
id: request.id
|
||||
}
|
||||
})
|
||||
);
|
||||
this.store.dispatch(
|
||||
ManagementControllerServicesActions.managementControllerServicesSnackbarApiError({
|
||||
error: errorResponse.error
|
||||
})
|
||||
);
|
||||
}
|
||||
})
|
||||
)
|
||||
),
|
||||
tap((request) => {
|
||||
const serviceId: string = request.id;
|
||||
|
||||
const editDialogReference = this.dialog.open(EditControllerService, {
|
||||
...LARGE_DIALOG,
|
||||
data: {
|
||||
controllerService: request.controllerService
|
||||
},
|
||||
data: request,
|
||||
id: serviceId
|
||||
});
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
import {
|
||||
AffectedComponentEntity,
|
||||
Bundle,
|
||||
ComponentHistory,
|
||||
DocumentedType,
|
||||
ParameterContextReferenceEntity,
|
||||
ParameterEntity,
|
||||
|
@ -158,6 +159,7 @@ export interface DeleteParameterProviderSuccess {
|
|||
export interface EditParameterProviderRequest {
|
||||
id: string;
|
||||
parameterProvider: ParameterProviderEntity;
|
||||
history?: ComponentHistory;
|
||||
}
|
||||
|
||||
export interface ConfigureParameterProviderRequest {
|
||||
|
|
|
@ -49,7 +49,7 @@ import { CreateParameterProvider } from '../../ui/parameter-providers/create-par
|
|||
import { YesNoDialog } from '../../../../ui/common/yes-no-dialog/yes-no-dialog.component';
|
||||
import { EditParameterProvider } from '../../ui/parameter-providers/edit-parameter-provider/edit-parameter-provider.component';
|
||||
import { PropertyTableHelperService } from '../../../../service/property-table-helper.service';
|
||||
import { ParameterProviderEntity, UpdateParameterProviderRequest } from './index';
|
||||
import { EditParameterProviderRequest, ParameterProviderEntity, UpdateParameterProviderRequest } from './index';
|
||||
import { ManagementControllerServiceService } from '../../service/management-controller-service.service';
|
||||
import { FetchParameterProviderParameters } from '../../ui/parameter-providers/fetch-parameter-provider-parameters/fetch-parameter-provider-parameters.component';
|
||||
import * as ErrorActions from '../../../../state/error/error.actions';
|
||||
|
@ -266,13 +266,33 @@ export class ParameterProvidersEffects {
|
|||
this.actions$.pipe(
|
||||
ofType(ParameterProviderActions.openConfigureParameterProviderDialog),
|
||||
map((action) => action.request),
|
||||
switchMap((request) =>
|
||||
from(this.propertyTableHelperService.getComponentHistory(request.id)).pipe(
|
||||
map((history) => {
|
||||
return {
|
||||
...request,
|
||||
history: history.componentHistory
|
||||
} as EditParameterProviderRequest;
|
||||
}),
|
||||
tap({
|
||||
error: (errorResponse: HttpErrorResponse) => {
|
||||
this.store.dispatch(
|
||||
ParameterProviderActions.selectParameterProvider({
|
||||
request: {
|
||||
id: request.id
|
||||
}
|
||||
})
|
||||
);
|
||||
this.store.dispatch(ErrorActions.snackBarError({ error: errorResponse.error }));
|
||||
}
|
||||
})
|
||||
)
|
||||
),
|
||||
tap((request) => {
|
||||
const id = request.id;
|
||||
const editDialogReference = this.dialog.open(EditParameterProvider, {
|
||||
...LARGE_DIALOG,
|
||||
data: {
|
||||
parameterProvider: request.parameterProvider
|
||||
},
|
||||
data: request,
|
||||
id
|
||||
});
|
||||
|
||||
|
|
|
@ -15,7 +15,14 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { BulletinEntity, Bundle, DocumentedType, Permissions, Revision } from '../../../../state/shared';
|
||||
import {
|
||||
BulletinEntity,
|
||||
Bundle,
|
||||
ComponentHistory,
|
||||
DocumentedType,
|
||||
Permissions,
|
||||
Revision
|
||||
} from '../../../../state/shared';
|
||||
|
||||
export const reportingTasksFeatureKey = 'reportingTasks';
|
||||
|
||||
|
@ -66,6 +73,7 @@ export interface UpdateReportingTaskRequest {
|
|||
export interface EditReportingTaskDialogRequest {
|
||||
id: string;
|
||||
reportingTask: ReportingTaskEntity;
|
||||
history?: ComponentHistory;
|
||||
}
|
||||
|
||||
export interface StartReportingTaskRequest {
|
||||
|
|
|
@ -30,7 +30,7 @@ import { Router } from '@angular/router';
|
|||
import { selectSaving } from '../management-controller-services/management-controller-services.selectors';
|
||||
import { UpdateControllerServiceRequest } from '../../../../state/shared';
|
||||
import { EditReportingTask } from '../../ui/reporting-tasks/edit-reporting-task/edit-reporting-task.component';
|
||||
import { CreateReportingTaskSuccess } from './index';
|
||||
import { CreateReportingTaskSuccess, EditReportingTaskDialogRequest } from './index';
|
||||
import { ManagementControllerServiceService } from '../../service/management-controller-service.service';
|
||||
import { PropertyTableHelperService } from '../../../../service/property-table-helper.service';
|
||||
import * as ErrorActions from '../../../../state/error/error.actions';
|
||||
|
@ -232,14 +232,36 @@ export class ReportingTasksEffects {
|
|||
this.actions$.pipe(
|
||||
ofType(ReportingTaskActions.openConfigureReportingTaskDialog),
|
||||
map((action) => action.request),
|
||||
switchMap((request) =>
|
||||
from(this.propertyTableHelperService.getComponentHistory(request.id)).pipe(
|
||||
map((history) => {
|
||||
return {
|
||||
...request,
|
||||
history: history.componentHistory
|
||||
} as EditReportingTaskDialogRequest;
|
||||
}),
|
||||
tap({
|
||||
error: (errorResponse: HttpErrorResponse) => {
|
||||
this.store.dispatch(
|
||||
ReportingTaskActions.selectReportingTask({
|
||||
request: {
|
||||
id: request.id
|
||||
}
|
||||
})
|
||||
);
|
||||
this.store.dispatch(
|
||||
ReportingTaskActions.reportingTasksSnackbarApiError({ error: errorResponse.error })
|
||||
);
|
||||
}
|
||||
})
|
||||
)
|
||||
),
|
||||
tap((request) => {
|
||||
const taskId: string = request.id;
|
||||
|
||||
const editDialogReference = this.dialog.open(EditReportingTask, {
|
||||
...LARGE_DIALOG,
|
||||
data: {
|
||||
reportingTask: request.reportingTask
|
||||
},
|
||||
data: request,
|
||||
id: taskId
|
||||
});
|
||||
|
||||
|
|
|
@ -73,6 +73,7 @@
|
|||
formControlName="properties"
|
||||
[createNewProperty]="createNewProperty"
|
||||
[createNewService]="createNewService"
|
||||
[propertyHistory]="request.history"
|
||||
[goToService]="goToService">
|
||||
</property-table>
|
||||
</div>
|
||||
|
|
|
@ -71,6 +71,7 @@
|
|||
formControlName="properties"
|
||||
[createNewProperty]="createNewProperty"
|
||||
[createNewService]="createNewService"
|
||||
[propertyHistory]="request.history"
|
||||
[goToService]="goToService"></property-table>
|
||||
</div>
|
||||
</mat-tab>
|
||||
|
|
|
@ -89,6 +89,7 @@
|
|||
[createNewProperty]="createNewProperty"
|
||||
[createNewService]="createNewService"
|
||||
[goToService]="goToService"
|
||||
[propertyHistory]="request.history"
|
||||
[supportsSensitiveDynamicProperties]="
|
||||
request.reportingTask.component.supportsSensitiveDynamicProperties
|
||||
">
|
||||
|
|
|
@ -19,6 +19,7 @@ import { Injectable } from '@angular/core';
|
|||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { catchError, EMPTY, map, Observable, switchMap, take, takeUntil, tap } from 'rxjs';
|
||||
import {
|
||||
ComponentHistoryEntity,
|
||||
ControllerServiceCreator,
|
||||
ControllerServiceEntity,
|
||||
CreateControllerServiceRequest,
|
||||
|
@ -37,20 +38,29 @@ import { Client } from './client.service';
|
|||
import { NiFiState } from '../state';
|
||||
import { Store } from '@ngrx/store';
|
||||
import { snackBarError } from '../state/error/error.actions';
|
||||
import { HttpErrorResponse } from '@angular/common/http';
|
||||
import { HttpClient, HttpErrorResponse } from '@angular/common/http';
|
||||
import { LARGE_DIALOG, SMALL_DIALOG } from '../index';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class PropertyTableHelperService {
|
||||
private static readonly API: string = '../nifi-api';
|
||||
|
||||
constructor(
|
||||
private httpClient: HttpClient,
|
||||
private dialog: MatDialog,
|
||||
private store: Store<NiFiState>,
|
||||
private extensionTypesService: ExtensionTypesService,
|
||||
private client: Client
|
||||
) {}
|
||||
|
||||
getComponentHistory(componentId: string): Observable<ComponentHistoryEntity> {
|
||||
return this.httpClient.get<ComponentHistoryEntity>(
|
||||
`${PropertyTableHelperService.API}/flow/history/components/${componentId}`
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a function that can be used to pass into a PropertyTable to support creating a new property
|
||||
* @param id id of the component to create the property for
|
||||
|
|
|
@ -128,6 +128,7 @@ export interface CreateControllerServiceDialogRequest {
|
|||
export interface EditControllerServiceDialogRequest {
|
||||
id: string;
|
||||
controllerService: ControllerServiceEntity;
|
||||
history?: ComponentHistory;
|
||||
}
|
||||
|
||||
export interface UpdateControllerServiceRequest {
|
||||
|
@ -204,6 +205,25 @@ export interface ProvenanceEventDialogRequest {
|
|||
event: ProvenanceEvent;
|
||||
}
|
||||
|
||||
export interface PreviousValue {
|
||||
previousValue: string;
|
||||
timestamp: string;
|
||||
userIdentity: string;
|
||||
}
|
||||
|
||||
export interface PropertyHistory {
|
||||
previousValues: PreviousValue[];
|
||||
}
|
||||
|
||||
export interface ComponentHistory {
|
||||
componentId: string;
|
||||
propertyHistory: { [key: string]: PropertyHistory };
|
||||
}
|
||||
|
||||
export interface ComponentHistoryEntity {
|
||||
componentHistory: ComponentHistory;
|
||||
}
|
||||
|
||||
export interface TextTipInput {
|
||||
text: string;
|
||||
}
|
||||
|
@ -232,6 +252,7 @@ export interface BulletinsTipInput {
|
|||
|
||||
export interface PropertyTipInput {
|
||||
descriptor: PropertyDescriptor;
|
||||
propertyHistory?: PropertyHistory;
|
||||
}
|
||||
|
||||
export interface ParameterTipInput {
|
||||
|
|
|
@ -102,6 +102,7 @@
|
|||
[goToParameter]="goToParameter"
|
||||
[convertToParameter]="convertToParameter"
|
||||
[goToService]="goToService"
|
||||
[propertyHistory]="request.history"
|
||||
[supportsSensitiveDynamicProperties]="
|
||||
request.controllerService.component.supportsSensitiveDynamicProperties
|
||||
"></property-table>
|
||||
|
|
|
@ -37,14 +37,12 @@
|
|||
{{ item.descriptor.displayName }}
|
||||
</div>
|
||||
<div>
|
||||
@if (hasInfo(item.descriptor)) {
|
||||
<div
|
||||
class="fa fa-question-circle primary-color"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="PropertyTip"
|
||||
[tooltipInputData]="getPropertyTipData(item)"
|
||||
[delayClose]="false"></div>
|
||||
}
|
||||
<div
|
||||
class="fa fa-question-circle primary-color"
|
||||
nifiTooltip
|
||||
[tooltipComponentType]="PropertyTip"
|
||||
[tooltipInputData]="getPropertyTipData(item)"
|
||||
[delayClose]="false"></div>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
|
|
|
@ -34,6 +34,7 @@ import { NiFiCommon } from '../../../service/nifi-common.service';
|
|||
import { AsyncPipe, NgTemplateOutlet } from '@angular/common';
|
||||
import {
|
||||
AllowableValueEntity,
|
||||
ComponentHistory,
|
||||
InlineServiceCreationRequest,
|
||||
InlineServiceCreationResponse,
|
||||
Parameter,
|
||||
|
@ -105,6 +106,7 @@ export class PropertyTable implements AfterViewInit, ControlValueAccessor {
|
|||
@Input() convertToParameter!: (name: string, sensitive: boolean, value: string | null) => Observable<string>;
|
||||
@Input() goToService!: (serviceId: string) => void;
|
||||
@Input() supportsSensitiveDynamicProperties = false;
|
||||
@Input() propertyHistory: ComponentHistory | undefined;
|
||||
|
||||
private static readonly PARAM_REF_REGEX: RegExp = /#{[a-zA-Z0-9-_. ]+}/;
|
||||
|
||||
|
@ -348,14 +350,6 @@ export class PropertyTable implements AfterViewInit, ControlValueAccessor {
|
|||
return 'property-' + item.id;
|
||||
}
|
||||
|
||||
hasInfo(descriptor: PropertyDescriptor): boolean {
|
||||
return (
|
||||
!this.nifiCommon.isBlank(descriptor.description) ||
|
||||
!this.nifiCommon.isBlank(descriptor.defaultValue) ||
|
||||
descriptor.supportsEl
|
||||
);
|
||||
}
|
||||
|
||||
isSensitiveProperty(descriptor: PropertyDescriptor): boolean {
|
||||
return descriptor.sensitive;
|
||||
}
|
||||
|
@ -396,7 +390,8 @@ export class PropertyTable implements AfterViewInit, ControlValueAccessor {
|
|||
|
||||
getPropertyTipData(item: PropertyItem): PropertyTipInput {
|
||||
return {
|
||||
descriptor: item.descriptor
|
||||
descriptor: item.descriptor,
|
||||
propertyHistory: this.propertyHistory?.propertyHistory[item.property]
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -16,8 +16,8 @@
|
|||
-->
|
||||
|
||||
<div class="tooltip" [style.left.px]="left" [style.top.px]="top">
|
||||
@if (data?.descriptor; as descriptor) {
|
||||
<div class="flex flex-col gap-y-3">
|
||||
<div class="flex flex-col gap-y-3">
|
||||
@if (data?.descriptor; as descriptor) {
|
||||
@if (hasDescription(descriptor)) {
|
||||
<div>{{ descriptor.description }}</div>
|
||||
}
|
||||
|
@ -38,7 +38,16 @@
|
|||
[bundle]="descriptor.identifiesControllerServiceBundle"></controller-service-api>
|
||||
</div>
|
||||
}
|
||||
<!-- TODO - Property History -->
|
||||
</div>
|
||||
}
|
||||
}
|
||||
@if (data?.propertyHistory; as propertyHistory) {
|
||||
<div>
|
||||
<b>History</b>
|
||||
<ul class="px-2">
|
||||
@for (previousValue of propertyHistory.previousValues; track previousValue) {
|
||||
<li>{{ previousValue.previousValue }} - {{ previousValue.timestamp }} ({{ previousValue.userIdentity }})</li>
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue