@c8y/ngx-components
Version:
Angular modules for Cumulocity IoT applications
1,114 lines • 140 kB
JavaScript
import * as i0 from '@angular/core';
import { Injectable, EventEmitter, forwardRef, Component, Input, inject, DestroyRef, NgModule } from '@angular/core';
import * as i2 from '@c8y/ngx-components';
import { NavigatorNode, gettext, PRODUCT_EXPERIENCE_EVENT_SOURCE, ModalSelectionMode, BuiltInActionType, Status, ManagedObjectRealtimeService, ViewContext, CoreModule, CommonModule, hookNavigator, hookRoute } from '@c8y/ngx-components';
import * as i1 from '@c8y/client';
import { QueriesUtil, OperationStatus } from '@c8y/client';
import { get, isEmpty, mapValues, omitBy, keyBy, uniqBy, keys, pickBy, sortBy, toArray, isNil, assign, concat, uniqWith, isEqual, has, cloneDeep } from 'lodash-es';
import * as i1$1 from '@c8y/ngx-components/repository/shared';
import { PRODUCT_EXPERIENCE_REPOSITORY_SHARED, RepositoryType, RepositorySelectModalComponent, RepositoryItemNameGridColumn, DeviceTypeGridColumn, SharedRepositoryModule } from '@c8y/ngx-components/repository/shared';
import { Subject, from, BehaviorSubject, pipe, combineLatest } from 'rxjs';
import { switchMap, map, take, distinctUntilChanged, shareReplay, filter, tap } from 'rxjs/operators';
import * as i2$1 from '@angular/common';
import * as i5 from '@angular/router';
import { RouterModule } from '@angular/router';
import * as i2$2 from 'ngx-bootstrap/dropdown';
import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
import * as i10 from 'ngx-bootstrap/popover';
import { PopoverModule } from 'ngx-bootstrap/popover';
import * as i9 from 'ngx-bootstrap/tooltip';
import { TooltipModule } from 'ngx-bootstrap/tooltip';
import { ButtonsModule } from 'ngx-bootstrap/buttons';
import * as i8 from '@angular/forms';
import { ReactiveFormsModule } from '@angular/forms';
import * as i4 from 'ngx-bootstrap/modal';
import * as i4$1 from '@ngx-translate/core';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
import * as i6 from '@c8y/ngx-components/operations/operation-details';
import { OperationDetailsModule } from '@c8y/ngx-components/operations/operation-details';
class DeviceProfileNavigationFactory {
async get() {
if (!this.nodeItem) {
this.nodeItem = new NavigatorNode({
label: gettext('Device profiles'),
path: '/device-profiles',
icon: 'c8y-device-profile',
parent: gettext('Management')
});
}
return this.nodeItem;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceProfileNavigationFactory, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceProfileNavigationFactory }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceProfileNavigationFactory, decorators: [{
type: Injectable
}] });
class DeviceProfileService {
constructor(inventoryService, operationService, alertService) {
this.inventoryService = inventoryService;
this.operationService = operationService;
this.alertService = alertService;
this.dateFrom = new Date(0);
this.dateTo = new Date(Date.now() + 86400000); // 1 day in the future
this.NOT_INSTALLED_WARNING = gettext('Not installed on the device');
this.VERSION_MISSMATCH_WARNING = gettext('Version mismatch');
this.SAME_URL_WARNING = gettext('Installed configuration has the same URL but different name or type than the one in the profile');
this.queriesUtil = new QueriesUtil();
}
createDeviceProfile(deviceProfile) {
if (get(deviceProfile, 'c8y_Filter.type') === '') {
delete deviceProfile.c8y_Filter.type;
}
return this.inventoryService.create(deviceProfile);
}
/**
* Determines the available device profiles for a given device by considering device type
* and the supported software types declared by the devices. Because of limitations in the
* Inventory Query API the methods return profile that contain at least one of the supported
* software types and omits profiles having only non-supported software types. Resulting device
* profiles need to be further filtered on the client side to exclude the ones that contain
* non-supported software types next to the supported ones.
*
* @param device A device MO
* @param name Optional device profile name filter
* @returns Candidate device profiles that contain at least on software with supported type.
*/
getDeviceProfilesByDevice(device, name = null) {
const deviceTypeFilter = {
__or: [
{ 'c8y_Filter.type': device.type },
{ 'c8y_Filter.type': '' },
{ __not: { __has: 'c8y_Filter.type' } }
]
};
let softwareTypeFilter = {};
if (!isEmpty(device.c8y_SupportedSoftwareTypes)) {
softwareTypeFilter = {
__hasany: device.c8y_SupportedSoftwareTypes.map((type) => `softwareType!${type}`)
};
}
let query = this.queriesUtil.addAndFilter(deviceTypeFilter, softwareTypeFilter);
let profileNameFilter = {};
if (!isEmpty(name)) {
profileNameFilter = { name: `*${name}*` };
}
query = this.queriesUtil.addAndFilter(query, profileNameFilter);
return this.getDeviceProfiles(query);
}
/**
* @deprecated Use `getDeviceProfilesByDevice` instead as it also considers the supported software types.
*/
getDeviceProfilesByDeviceType(deviceType) {
const deviceTypeFilter = {
__or: [
{ 'c8y_Filter.type': deviceType },
{ 'c8y_Filter.type': '' },
{ __not: { __has: 'c8y_Filter.type' } }
]
};
return this.getDeviceProfiles(deviceTypeFilter);
}
getDeviceProfiles(andQuery) {
let query = {
type: 'c8y_Profile'
};
const filter = {
pageSize: 100,
withTotalPages: true
};
query = this.queriesUtil.addAndFilter(query, andQuery || {});
return this.inventoryService.listQuery(query, filter);
}
async getProfileOperation(deviceId) {
const filter = {
deviceId,
fragmentType: 'c8y_DeviceProfile',
dateFrom: this.dateFrom.toISOString(),
dateTo: this.dateTo.toISOString(),
revert: true,
pageSize: 1
};
const operation = (await this.operationService.list(filter)).data[0];
return operation && operation.status !== OperationStatus.SUCCESSFUL ? operation : undefined;
}
async createProfileOperation(device, deviceProfile) {
let operation;
const operationCfg = {
deviceId: device.id,
profileId: deviceProfile.id,
profileName: deviceProfile.name,
c8y_DeviceProfile: deviceProfile.c8y_DeviceProfile,
description: `Assign device profile ${deviceProfile.name} to device ${device.name}`
};
try {
const { data } = await this.operationService.create(operationCfg);
operation = data;
}
catch (ex) {
this.alertService.addServerFailure(ex);
}
return operation;
}
getFirmwareItems(device, selectedProfile) {
const deviceFirmware = device.c8y_Firmware;
const profileFirmware = get(selectedProfile, 'c8y_DeviceProfile.firmware');
const deviceItems = [];
const profileItems = [];
if (deviceFirmware) {
deviceItems.push(deviceFirmware);
}
if (profileFirmware) {
profileItems.push(profileFirmware);
}
return this.createProfileComparison(deviceItems, profileItems, 'name', 'version', null, this.getAlert('firmware'));
}
getSoftwareItems(device, selectedProfile) {
const deviceSoftware = device.c8y_SoftwareList;
const profileSoftware = get(selectedProfile, 'c8y_DeviceProfile.software');
return this.createProfileComparison(deviceSoftware, profileSoftware, 'name', 'version', 'softwareType', this.getAlert('software'));
}
getConfigurationItems(device, selectedProfile) {
const deviceConfiguration = [];
Object.keys(device).forEach(key => {
if (key.slice(0, 18) === 'c8y_Configuration_') {
deviceConfiguration.push(device[key]);
}
});
const profileConfiguration = get(selectedProfile, 'c8y_DeviceProfile.configuration');
return this.createProfileComparison(deviceConfiguration, profileConfiguration, 'url', null, 'type', this.getAlert('configuration'));
}
/**
* Aligns device profile managed object's `softwareType!*` fragments to the software items
* included in the device profile. Removes obsolete software type fragments and adds new.
*
* @param profilePartial The device profile managed object which `softwareType!*` fragments will be adjusted.
* @returns The adjusted device profile managed object.
*/
alignSoftwareTypeFragments(profilePartial, profile) {
if (!profilePartial?.c8y_DeviceProfile?.software || !profile) {
return profilePartial;
}
const removedSoftwareTypes = mapValues(omitBy(profile, (_, key) => typeof key === 'string' && !key.startsWith('softwareType!')), () => null);
const softwareTypesToAdd = mapValues(keyBy(uniqBy(profilePartial.c8y_DeviceProfile.software, 'softwareType'), (profile) => `softwareType!${profile.softwareType}`), () => ({}));
const result = { ...profilePartial, ...removedSoftwareTypes, ...softwareTypesToAdd };
return result;
}
getSoftwareTypes(profile) {
return keys(pickBy(profile, (_, key) => typeof key === 'string' && key.startsWith('softwareType!'))).map(softwareType => softwareType.substr('softwareType!'.length));
}
getAlert(itemType) {
const notInstalled = (comparisionResult) => {
return !comparisionResult.device ? this.NOT_INSTALLED_WARNING : '';
};
switch (itemType) {
case 'firmware':
case 'software':
return (comparisionResult) => {
return comparisionResult.device &&
comparisionResult.profile &&
comparisionResult.device.itemDetails !== comparisionResult.profile.itemDetails
? this.VERSION_MISSMATCH_WARNING
: notInstalled(comparisionResult);
};
case 'configuration':
return (comparisionResult) => {
return comparisionResult.device &&
comparisionResult.profile &&
(comparisionResult.device.itemName !== comparisionResult.profile.itemName ||
comparisionResult.device.itemDetails !== comparisionResult.profile.itemDetails)
? this.SAME_URL_WARNING
: notInstalled(comparisionResult);
};
default:
return notInstalled;
}
}
createProfileComparison(deviceItems = [], profileItems = [], mergeByProperty, propertyNameWithDetails, propertyNameWithType, getAlert) {
const comparisonObj = this.createProfileComparisonFromDeviceItems(deviceItems, mergeByProperty, propertyNameWithDetails, propertyNameWithType);
const extendedComparisonObj = this.extendProfileComparisonWithProfileItems(comparisonObj, profileItems, mergeByProperty, propertyNameWithDetails, propertyNameWithType, getAlert);
return sortBy(toArray(extendedComparisonObj), 'name');
}
createProfileComparisonFromDeviceItems(deviceItems, mergeByProperty, propertyNameWithDetails, propertyNameWithType) {
return deviceItems.reduce((comapritionItem, deviceItem) => Object.assign(comapritionItem, {
[deviceItem[mergeByProperty]]: {
device: omitBy({
itemName: deviceItem.name,
itemDetails: deviceItem[propertyNameWithDetails],
itemType: deviceItem[propertyNameWithType],
itemUrl: deviceItem.url
}, isNil),
profile: undefined
}
}), {});
}
extendProfileComparisonWithProfileItems(comparisonObj, profileItems, mergeByProperty, propertyNameWithDetails, propertyNameWithType, getAlert) {
profileItems.forEach(profileItem => {
const comparisionResult = {
profile: omitBy({
itemName: profileItem.name,
itemDetails: profileItem[propertyNameWithDetails],
itemType: profileItem[propertyNameWithType],
itemUrl: profileItem.url
}, isNil),
device: comparisonObj[profileItem[mergeByProperty]]
? comparisonObj[profileItem[mergeByProperty]].device
: undefined
};
comparisionResult.comparisonAlert = getAlert(comparisionResult);
comparisonObj[profileItem[mergeByProperty]] = comparisionResult;
});
return comparisonObj;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceProfileService, deps: [{ token: i1.InventoryService }, { token: i1.OperationService }, { token: i2.AlertService }], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceProfileService }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceProfileService, decorators: [{
type: Injectable
}], ctorParameters: () => [{ type: i1.InventoryService }, { type: i1.OperationService }, { type: i2.AlertService }] });
class SelectConfigurationModalComponent {
constructor(repositoryService) {
this.repositoryService = repositoryService;
this.PRODUCT_EXPERIENCE = PRODUCT_EXPERIENCE_REPOSITORY_SHARED;
this.title = gettext('Select configuration');
this.load = new Subject();
this.configurations = this.load.pipe(switchMap(() => this.getItems()), map(({ data }) => this.aggregate(data)));
this.resultEmitter = new EventEmitter();
this.deviceTypeQuery = {};
this.searchQuery = {};
this.labels = { ok: gettext('Save') };
this.queriesUtil = new QueriesUtil();
}
search(searchTerm) {
if (!searchTerm) {
this.searchQuery = {};
}
else {
this.searchQuery = this.queriesUtil.addOrFilter({ name: `*${searchTerm}*` }, { configurationType: `*${searchTerm}*` });
}
this.load.next();
}
result(selectedItems) {
this.resultEmitter.emit(selectedItems);
}
getItems() {
return this.repositoryService.listRepositoryEntries(RepositoryType.CONFIGURATION, {
query: this.queriesUtil.addOrFilter(this.deviceTypeQuery, this.searchQuery),
params: { pageSize: 100 }
});
}
aggregate(mos) {
const selectedItems = this.selected;
return mos.reduce((acc, curr) => {
curr.configurationType = curr.configurationType || curr.name;
const selected = selectedItems && selectedItems.filter(val => val.url === curr.url).length > 0;
const selectModalOption = {
body: [{ value: curr.name }],
obj: curr,
selected
};
let selectModalObject = acc.find(val => val.body[0].value === curr.configurationType);
if (selectModalObject) {
selectModalObject.options.push(selectModalOption);
}
else {
selectModalObject = {
groupId: curr.id,
body: [{ value: curr.configurationType }],
options: [selectModalOption]
};
acc.push(selectModalObject);
}
return acc;
}, []);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectConfigurationModalComponent, deps: [{ token: i1$1.RepositoryService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: SelectConfigurationModalComponent, isStandalone: false, selector: "c8y-select-configuration-modal", providers: [
{
provide: PRODUCT_EXPERIENCE_EVENT_SOURCE,
useExisting: forwardRef(() => SelectConfigurationModalComponent)
}
], ngImport: i0, template: "<c8y-select-modal\n [icon]=\"'gears'\"\n [title]=\"title\"\n [items]=\"configurations | async\"\n [mode]=\"'multi'\"\n (result)=\"result($event)\"\n (search)=\"search($event)\"\n [disableSelected]=\"true\"\n [labels]=\"labels\"\n c8yProductExperience\n inherit\n suppressDataOverriding\n [actionData]=\"{ component: PRODUCT_EXPERIENCE.SHARED.COMPONENTS.SELECT_CONFIGURATION_MODAL }\"\n></c8y-select-modal>\n", dependencies: [{ kind: "component", type: i2.SelectModalComponent, selector: "c8y-select-modal", inputs: ["icon", "title", "subTitle", "items", "mode", "disableSelected", "showFilter", "additionalFilterTemplate", "areMoreEntries", "labels", "noItemsMessage", "hideEmptyItems"], outputs: ["result", "search", "onChoiceUpdated"] }, { kind: "directive", type: i2.ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "pipe", type: i2$1.AsyncPipe, name: "async" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: SelectConfigurationModalComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-select-configuration-modal', providers: [
{
provide: PRODUCT_EXPERIENCE_EVENT_SOURCE,
useExisting: forwardRef(() => SelectConfigurationModalComponent)
}
], standalone: false, template: "<c8y-select-modal\n [icon]=\"'gears'\"\n [title]=\"title\"\n [items]=\"configurations | async\"\n [mode]=\"'multi'\"\n (result)=\"result($event)\"\n (search)=\"search($event)\"\n [disableSelected]=\"true\"\n [labels]=\"labels\"\n c8yProductExperience\n inherit\n suppressDataOverriding\n [actionData]=\"{ component: PRODUCT_EXPERIENCE.SHARED.COMPONENTS.SELECT_CONFIGURATION_MODAL }\"\n></c8y-select-modal>\n" }]
}], ctorParameters: () => [{ type: i1$1.RepositoryService }] });
var DeviceProfileOperation;
(function (DeviceProfileOperation) {
DeviceProfileOperation["APPLY_PROFILE"] = "c8y_DeviceProfile";
})(DeviceProfileOperation || (DeviceProfileOperation = {}));
const PRODUCT_EXPERIENCE_DEVICE_PROFILE = {
EVENTS: {
REPOSITORY: 'deviceProfileRepository',
DEVICE_TAB: 'deviceProfileTab'
},
COMPONENTS: {
DEVICE_PROFILE_LIST: 'device-profile-list',
ADD_DEVICE_PROFILE: 'add-device-profile',
DEVICE_PROFILE: 'device-profile',
DEVICE_TAB_PROFILE: 'device-tab-profile'
},
ACTIONS: {
CANCEL: 'cancel',
CREATE: 'create',
REMOVE: 'remove',
ADD: 'add',
SAVE: 'save',
ASSIGN_DEVICE_PROFILE: 'assignDeviceProfile'
},
RESULTS: {
ADD_SOFTWARE: 'addSoftware'
},
FRAGMENTS: {
FIRMWARE: 'firmware',
SOFTWARE: 'software',
CONFGIURATION: 'configuration'
}
};
class DeviceProfileComponent {
constructor(route, alertService, inventoryService, bsModal, repositoryService, deviceProfileService) {
this.route = route;
this.alertService = alertService;
this.inventoryService = inventoryService;
this.bsModal = bsModal;
this.repositoryService = repositoryService;
this.deviceProfileService = deviceProfileService;
this.DEVICE_TYPE_POPOVER = gettext('The device profile will be available for assignments on devices of the specified type. Otherwise, it will be available for all device types.');
this.DEVICE_TYPE_DISABLED_POPOVER = gettext('Device type cannot be changed on profiles with already defined firmware, software or configuration since they may not be applicable to devices of the new device type.');
this.PRODUCT_EXPERIENCE = PRODUCT_EXPERIENCE_DEVICE_PROFILE;
this.productExperienceEvent = {
eventName: PRODUCT_EXPERIENCE_DEVICE_PROFILE.EVENTS.REPOSITORY,
data: {
component: PRODUCT_EXPERIENCE_DEVICE_PROFILE.COMPONENTS.DEVICE_PROFILE
}
};
this.queriesUtil = new QueriesUtil();
}
async ngOnInit() {
const profileId = this.route.snapshot.paramMap.get('id');
this.deviceProfile = (await this.getDeviceProfile(profileId));
if (this.deviceProfile) {
this.profileName = this.deviceProfile.name;
if (!this.deviceProfile.c8y_DeviceProfile.software) {
this.deviceProfile.c8y_DeviceProfile.software = [];
}
if (!this.deviceProfile.c8y_DeviceProfile.configuration) {
this.deviceProfile.c8y_DeviceProfile.configuration = [];
}
}
}
addFirmware() {
const initialState = {
deviceTypeQuery: this.getDeviceTypeQuery(RepositoryType.FIRMWARE),
repositoryType: RepositoryType.FIRMWARE,
repositoryEntriesWithVersionsFn$: modalDialog => this.getRepositoryEntriesWithVersions$(modalDialog.content.searchTerm, RepositoryType.FIRMWARE),
icon: 'c8y-firmware',
title: gettext('Select firmware'),
mode: ModalSelectionMode.SINGLE,
productExperienceEvent: this.productExperienceEvent
};
const modal = this.bsModal.show(RepositorySelectModalComponent, {
ignoreBackdropClick: true,
keyboard: false,
ariaLabelledBy: 'modal-title',
initialState
});
if (initialState.repositoryEntriesWithVersionsFn$) {
modal.content.repositoryEntriesWithVersions$ =
initialState.repositoryEntriesWithVersionsFn$(modal);
}
modal.content.load.next();
modal.content.resultEmitter.pipe(take(1)).subscribe(firmwareList => {
const [firmware] = firmwareList;
if (!firmware) {
return;
}
const deviceProfilePartial = {
c8y_DeviceProfile: this.deviceProfile.c8y_DeviceProfile || {}
};
assign(deviceProfilePartial.c8y_DeviceProfile, {
firmware: {
name: firmware.name,
version: firmware.version,
url: firmware.url,
isPatch: firmware.isPatch,
patchDependency: firmware.patchDependency
}
});
this.updateDeviceProfile(deviceProfilePartial);
});
}
getRepositoryEntriesWithVersions$(searchTerm$, repoType) {
return searchTerm$.pipe(distinctUntilChanged(), switchMap(searchTerm => this.repositoryService.listRepositoryEntries(repoType, {
query: this.getDeviceTypeQuery(repoType),
partialName: searchTerm.name,
params: { pageSize: 100 },
skipLegacy: true
})), map(({ data }) => data), map(mos => this.getAndAssignRepositoryBinaries(mos)), shareReplay(1));
}
getAndAssignRepositoryBinaries(mos) {
mos.forEach(mo => {
mo.versions = this.repositoryService.listBaseVersions(mo);
});
return mos;
}
addConfiguration() {
const modal = this.bsModal.show(SelectConfigurationModalComponent, {
ignoreBackdropClick: true,
keyboard: false,
ariaLabelledBy: 'modal-title',
initialState: {
productExperienceEvent: this.productExperienceEvent
}
});
modal.content.deviceTypeQuery = this.getDeviceTypeQuery(RepositoryType.CONFIGURATION);
modal.content.selected = this.deviceProfile.c8y_DeviceProfile.configuration;
modal.content.load.next();
modal.content.resultEmitter.pipe(take(1)).subscribe(selectedConfigurations => {
const selectedMapped = selectedConfigurations.map(selectedItem => {
return assign({
url: selectedItem.url,
name: selectedItem.name
}, selectedItem.configurationType ? { type: selectedItem.configurationType } : {});
});
const merged = concat(selectedMapped, this.deviceProfile.c8y_DeviceProfile.configuration || []);
const configuration = uniqWith(merged, (arrVal, othVal) => {
return arrVal.type && othVal.type && arrVal.type === othVal.type;
});
const deviceProfilePartial = {
c8y_DeviceProfile: this.deviceProfile.c8y_DeviceProfile || {}
};
assign(deviceProfilePartial.c8y_DeviceProfile, { configuration });
this.updateDeviceProfile(deviceProfilePartial);
});
}
addSoftware() {
const initialState = {
deviceTypeQuery: this.getDeviceTypeQuery(RepositoryType.SOFTWARE),
repositoryType: RepositoryType.SOFTWARE,
repositoryEntriesWithVersionsFn$: modalDialog => this.getRepositoryEntriesWithVersions$(modalDialog.content.searchTerm, RepositoryType.SOFTWARE),
selected: this.deviceProfile.c8y_DeviceProfile.software,
icon: 'c8y-tools',
title: gettext('Select software'),
mode: ModalSelectionMode.MULTI,
productExperienceEvent: this.productExperienceEvent
};
const modal = this.bsModal.show(RepositorySelectModalComponent, {
ignoreBackdropClick: true,
keyboard: false,
ariaLabelledBy: 'modal-title',
initialState
});
if (initialState.repositoryEntriesWithVersionsFn$) {
modal.content.repositoryEntriesWithVersions$ =
initialState.repositoryEntriesWithVersionsFn$(modal);
}
modal.content.load.next();
modal.content.resultEmitter.pipe(take(1)).subscribe(selectedSoftware => {
const selectedMapped = selectedSoftware.map(selectedItem => {
return {
name: selectedItem.name,
version: selectedItem.version,
url: selectedItem.url,
softwareType: selectedItem.softwareType,
action: 'install'
};
});
const merged = concat(selectedMapped, this.deviceProfile.c8y_DeviceProfile.software || []);
const software = uniqWith(merged, (arrVal, othVal) => {
return arrVal.name && othVal.name && arrVal.name === othVal.name;
});
const deviceProfilePartial = {
c8y_DeviceProfile: this.deviceProfile.c8y_DeviceProfile || {}
};
assign(deviceProfilePartial.c8y_DeviceProfile, { software });
this.updateDeviceProfile(deviceProfilePartial);
});
}
get isDeviceProfileEmpty() {
const isSoftware = this.deviceProfile.c8y_DeviceProfile.software &&
this.deviceProfile.c8y_DeviceProfile.software.length > 0;
const isFirmware = Boolean(this.deviceProfile.c8y_DeviceProfile.firmware);
const isConfiguration = this.deviceProfile.c8y_DeviceProfile.configuration &&
this.deviceProfile.c8y_DeviceProfile.configuration.length > 0;
return isSoftware || isFirmware || isConfiguration;
}
removeItem(removedItem, category) {
const deviceProfilePartial = {
c8y_DeviceProfile: this.deviceProfile.c8y_DeviceProfile
};
const filtered = deviceProfilePartial.c8y_DeviceProfile[category].filter(item => !isEqual(removedItem, item));
deviceProfilePartial.c8y_DeviceProfile[category] = filtered;
this.updateDeviceProfile(deviceProfilePartial);
}
removeFirmware() {
delete this.deviceProfile.c8y_DeviceProfile.firmware;
this.updateDeviceProfile({ c8y_DeviceProfile: this.deviceProfile.c8y_DeviceProfile });
}
async updateDeviceProfile(partialDeviceProfile) {
if (partialDeviceProfile.c8y_Filter && partialDeviceProfile.c8y_Filter.type === '') {
delete partialDeviceProfile.c8y_Filter.type;
}
Object.assign(partialDeviceProfile, { id: this.deviceProfile.id });
try {
const profileWithSoftwareTypeFragments = this.deviceProfileService.alignSoftwareTypeFragments(partialDeviceProfile, this.deviceProfile);
const { data } = await this.inventoryService.update(profileWithSoftwareTypeFragments);
this.deviceProfile = data;
this.profileName = this.deviceProfile.name;
this.alertService.success(gettext('Device profile changed.'));
}
catch (ex) {
this.alertService.addServerFailure(ex);
}
}
async getDeviceProfile(profileId) {
try {
const { data } = await this.inventoryService.detail(profileId);
return data;
}
catch (ex) {
this.alertService.addServerFailure(ex);
}
}
getDeviceTypeQuery(repositoryType) {
if (has(this.deviceProfile, 'c8y_Filter.type') &&
!isEmpty(this.deviceProfile.c8y_Filter.type)) {
if (repositoryType === RepositoryType.CONFIGURATION) {
return this.queriesUtil.addOrFilter({ deviceType: this.deviceProfile.c8y_Filter.type }, { __not: { __has: `deviceType` } });
}
else {
return this.queriesUtil.addOrFilter({ 'c8y_Filter.type': this.deviceProfile.c8y_Filter.type }, {
__or: [{ 'c8y_Filter.type': '' }, { __not: { __has: `c8y_Filter.type` } }]
});
}
}
return {};
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceProfileComponent, deps: [{ token: i5.ActivatedRoute }, { token: i2.AlertService }, { token: i1.InventoryService }, { token: i4.BsModalService }, { token: i1$1.RepositoryService }, { token: DeviceProfileService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: DeviceProfileComponent, isStandalone: false, selector: "c8y-device-profile", providers: [
{
provide: PRODUCT_EXPERIENCE_EVENT_SOURCE,
useExisting: forwardRef(() => DeviceProfileComponent)
}
], ngImport: i0, template: "<c8y-title>{{ profileName }}</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n icon=\"c8y-management\"\n label=\"{{ 'Management' | translate }}\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-device-profile'\"\n [label]=\"'Device profiles' | translate\"\n [path]=\"'device-profiles'\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n icon=\"c8y-device-profile\"\n label=\"{{ profileName }}\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<div\n class=\"row\"\n *ngIf=\"deviceProfile\"\n>\n <div class=\"col-lg-12 col-lg-max\">\n <div\n class=\"card card--fullpage\"\n *ngIf=\"deviceProfile\"\n >\n <div class=\"card-block bg-level-1 flex-no-shrink p-t-24 p-b-24 overflow-visible\">\n <div class=\"content-flex-70\">\n <div class=\"text-center\">\n <i class=\"c8y-icon-duocolor icon-48 c8y-icon c8y-icon-device-profile\"></i>\n <p>\n <small class=\"label label-info\">{{ 'Device profile' | translate }}</small>\n </p>\n </div>\n <div class=\"flex-grow col-10\">\n <div class=\"row\">\n <div class=\"col-md-4\">\n <form #editNameForm=\"ngForm\">\n <c8y-form-group>\n <label\n class=\"control-label\"\n translate\n >\n Name\n </label>\n <div class=\"input-group input-group-editable\">\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g. My device profile' | translate }}\"\n name=\"name\"\n type=\"text\"\n required\n [(ngModel)]=\"deviceProfile.name\"\n data-cy=\"device-profile--add-device-profile-name\"\n size=\"{{ deviceProfile.name?.length || 1 }}\"\n />\n <span></span>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n type=\"button\"\n data-cy=\"device-profile--save\"\n (click)=\"\n updateDeviceProfile({ name: deviceProfile.name });\n editNameForm.form.markAsPristine()\n \"\n [disabled]=\"editNameForm.form.invalid\"\n c8yProductExperience\n inherit\n [actionData]=\"{ action: PRODUCT_EXPERIENCE.ACTIONS.SAVE }\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n </div>\n </c8y-form-group>\n </form>\n </div>\n <div class=\"col-md-4\">\n <form #editTypeForm=\"ngForm\">\n <c8y-form-group>\n <label class=\"control-label\">\n {{ 'Device type' | translate }}\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"\n (DEVICE_TYPE_POPOVER | translate) +\n (isDeviceProfileEmpty\n ? ' ' + (DEVICE_TYPE_DISABLED_POPOVER | translate)\n : '')\n \"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </label>\n <div class=\"input-group input-group-editable\">\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} c8y_Linux\"\n name=\"type\"\n type=\"text\"\n [(ngModel)]=\"deviceProfile.c8y_Filter.type\"\n data-cy=\"device-profile--device-type\"\n size=\"{{ deviceProfile.c8y_Filter.type?.length || 14 }}\"\n [disabled]=\"isDeviceProfileEmpty\"\n />\n <span></span>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n type=\"button\"\n (click)=\"\n updateDeviceProfile({\n c8y_Filter: { type: deviceProfile.c8y_Filter.type }\n });\n editTypeForm.form.markAsPristine()\n \"\n [disabled]=\"isDeviceProfileEmpty\"\n c8yProductExperience\n inherit\n [actionData]=\"{ action: PRODUCT_EXPERIENCE.ACTIONS.SAVE }\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n </div>\n </c8y-form-group>\n </form>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"inner-scroll\">\n <div class=\"card-header separator-top-bottom bg-content sticky-top\">\n <div class=\"card-icon\">\n <i\n class=\"c8y-icon-duocolor\"\n [c8yIcon]=\"'c8y-firmware'\"\n ></i>\n </div>\n <div\n class=\"card-title\"\n translate\n >\n Firmware\n </div>\n </div>\n <div\n class=\"card-block p-t-0\"\n *ngIf=\"deviceProfile.c8y_DeviceProfile.firmware\"\n >\n <c8y-list-group>\n <c8y-li>\n <c8y-li-icon>\n <i [c8yIcon]=\"'c8y-firmware'\"></i>\n </c8y-li-icon>\n <c8y-li-body class=\"content-flex-50 m-l-4\">\n <div class=\"col-4\">\n <span\n class=\"text-truncate\"\n title=\"{{ deviceProfile.c8y_DeviceProfile.firmware.name }}\"\n >\n <span\n class=\"text-label-small m-r-8\"\n translate\n >\n Name\n </span>\n {{ deviceProfile.c8y_DeviceProfile.firmware.name }}\n </span>\n </div>\n <div class=\"col-4\"></div>\n <div class=\"col-3 d-flex a-i-center\">\n <span\n class=\"text-truncate\"\n title=\"{{ deviceProfile.c8y_DeviceProfile.firmware.version }}\"\n >\n <span\n class=\"text-label-small m-r-4\"\n translate\n >\n Version\n </span>\n {{ deviceProfile.c8y_DeviceProfile.firmware.version }}\n </span>\n <button\n class=\"btn btn-danger btn-xs visible-xs m-l-auto m-r-8 m-t-8\"\n title=\"{{ 'Remove`firmware`' | translate }}\"\n type=\"button\"\n (click)=\"removeFirmware()\"\n c8yProductExperience\n inherit\n [actionData]=\"{\n action: PRODUCT_EXPERIENCE.ACTIONS.REMOVE,\n fragment: PRODUCT_EXPERIENCE.FRAGMENTS.FIRMWARE\n }\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n {{ 'Remove`firmware`' | translate }}\n </button>\n </div>\n <div class=\"m-l-auto p-r-8 hidden-xs\">\n <button\n class=\"btn btn-dot showOnHover text-danger\"\n [attr.aria-label]=\"'Remove`firmware`' | translate\"\n tooltip=\"{{ 'Remove`firmware`' | translate }}\"\n placement=\"right\"\n type=\"button\"\n [delay]=\"500\"\n (click)=\"removeFirmware()\"\n c8yProductExperience\n inherit\n [actionData]=\"{\n action: PRODUCT_EXPERIENCE.ACTIONS.REMOVE,\n fragment: PRODUCT_EXPERIENCE.FRAGMENTS.FIRMWARE\n }\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n </button>\n </div>\n </c8y-li-body>\n </c8y-li>\n </c8y-list-group>\n </div>\n <div\n class=\"card-block p-t-16\"\n *ngIf=\"!deviceProfile.c8y_DeviceProfile.firmware\"\n >\n <c8y-ui-empty-state\n class=\"p-t-16 d-block\"\n icon=\"c8y-firmware\"\n [title]=\"'No firmware defined.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n <div\n class=\"card-footer p-t-0\"\n *ngIf=\"!deviceProfile.c8y_DeviceProfile.firmware\"\n >\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Add firmware' | translate }}\"\n type=\"button\"\n data-cy=\"device-profile--Add-firmware-button\"\n (click)=\"addFirmware()\"\n c8yProductExperience\n inherit\n [actionData]=\"{\n action: PRODUCT_EXPERIENCE.ACTIONS.ADD,\n fragment: PRODUCT_EXPERIENCE.FRAGMENTS.FIRMWARE\n }\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Add firmware' | translate }}\n </button>\n </div>\n\n <div class=\"card-header separator-top-bottom sticky-top bg-component\">\n <div class=\"card-icon\">\n <i\n class=\"c8y-icon-duocolor\"\n [c8yIcon]=\"'c8y-tools'\"\n ></i>\n </div>\n <div\n class=\"card-title\"\n translate\n >\n Software\n </div>\n </div>\n <div\n class=\"card-block p-t-0\"\n *ngIf=\"deviceProfile.c8y_DeviceProfile.software?.length > 0\"\n >\n <c8y-list-group>\n <c8y-li *ngFor=\"let software of deviceProfile.c8y_DeviceProfile.software\">\n <c8y-li-icon>\n <i [c8yIcon]=\"'c8y-tools'\"></i>\n </c8y-li-icon>\n <c8y-li-body class=\"content-flex-50 m-l-4\">\n <div class=\"col-4\">\n <span\n class=\"text-truncate-wrap\"\n title=\"{{ software.name }}\"\n >\n <span\n class=\"text-label-small m-r-8\"\n translate\n >\n Name\n </span>\n {{ software.name }}\n </span>\n </div>\n <div class=\"col-4\">\n <span\n class=\"text-truncate-wrap\"\n title=\"{{ software.name }}\"\n >\n <span\n class=\"text-label-small m-r-8\"\n translate\n >\n Type\n </span>\n <span\n class=\"label label-info m-l-4\"\n *ngIf=\"!!software.softwareType\"\n >\n {{ software.softwareType }}\n </span>\n </span>\n </div>\n <div class=\"col-3 d-flex a-i-center\">\n <span\n class=\"text-truncate-wrap\"\n title=\"{{ software.version }}\"\n >\n <span\n class=\"text-label-small m-r-8\"\n translate\n >\n Version\n </span>\n {{ software.version }}\n </span>\n <button\n class=\"btn btn-danger btn-xs visible-xs m-l-auto m-r-8 m-t-8\"\n title=\"{{ 'Remove`software`' | translate }}\"\n type=\"button\"\n ((click)=\"removeItem(software, 'software')\"\n c8yProductExperience\n inherit\n [actionData]=\"{\n action: PRODUCT_EXPERIENCE.ACTIONS.REMOVE,\n fragment: PRODUCT_EXPERIENCE.FRAGMENTS.SOFTWARE\n }\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n {{ 'Remove`software`' | translate }}\n </button>\n </div>\n <div class=\"m-l-auto p-r-8 hidden-xs\">\n <button\n class=\"btn btn-dot showOnHover text-danger\"\n [attr.aria-label]=\"'Remove`software`' | translate\"\n tooltip=\"{{ 'Remove`software`' | translate }}\"\n placement=\"right\"\n type=\"button\"\n [delay]=\"500\"\n (click)=\"removeItem(software, 'software')\"\n c8yProductExperience\n inherit\n [actionData]=\"{\n action: PRODUCT_EXPERIENCE.ACTIONS.REMOVE,\n fragment: PRODUCT_EXPERIENCE.FRAGMENTS.SOFTWARE\n }\"\n >\n <i\n c8yIcon=\"minus-circle\"\n data-cy=\"device-profile--Remove-icon\"\n ></i>\n </button>\n </div>\n </c8y-li-body>\n </c8y-li>\n </c8y-list-group>\n </div>\n <div\n class=\"card-block p-t-16\"\n *ngIf=\"deviceProfile.c8y_DeviceProfile.software?.length === 0\"\n >\n <c8y-ui-empty-state\n icon=\"c8y-tools\"\n [title]=\"'No software defined.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n <div class=\"card-footer p-t-0\">\n <button\n class=\"btn btn-default m-b-0\"\n title=\"{{ 'Add software' | translate }}\"\n type=\"button\"\n data-cy=\"device-profile--Add-software-button\"\n (click)=\"addSoftware()\"\n c8yProductExperience\n inherit\n [actionData]=\"{\n action: PRODUCT_EXPERIENCE.ACTIONS.ADD,\n fragment: PRODUCT_EXPERIENCE.FRAGMENTS.SOFTWARE\n }\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Add software' | translate }}\n </button>\n </div>\n\n <div class=\"card-header separator-top-bottom bg-component sticky-top\">\n <div class=\"card-icon\">\n <i\n class=\"c8y-icon-duocolor\"\n [c8yIcon]=\"'gears'\"\n ></i>\n </div>\n <div\n class=\"card-title\"\n translate\n >\n Configuration\n </div>\n </div>\n <div\n class=\"card-block p-t-0\"\n *ngIf=\"deviceProfile.c8y_DeviceProfile.configuration?.length > 0\"\n >\n <c8y-list-group>\n <c8y-li *ngFor=\"let configuration of deviceProfile.c8y_DeviceProfile.configuration\">\n <c8y-li-icon>\n <i [c8yIcon]=\"'gears'\"></i>\n </c8y-li-icon>\n <c8y-li-body class=\"content-flex-50\">\n <div class=\"col-4\">\n <span\n class=\"text-truncate\"\n title=\"{{ configuration.name }}\"\n >\n <span\n class=\"text-label-small m-r-8\"\n translate\n >\n Name\n </span>\n {{ configuration.name }}\n </span>\n </div>\n <div class=\"col-4\">\n <span\n class=\"text-label-small m-r-8\"\n translate\n >\n Type\n </span>\n <span class=\"label label-info\">{{ configuration.type }}</span>\n <button\n class=\"btn btn-danger btn-xs visible-xs m-l-auto m-r-8 m-t-8\"\n title=\"{{ 'Remove`configuration`' | translate }}\"\n type=\"button\"\n (click)=\"removeItem(configuration, 'configuration')\"\n c8yProductExperience\n inherit\n [actionData]=\"{\n action: PRODUCT_EXPERIENCE.ACTIONS.REMOVE,\n fragment: PRODUCT_EXPERIENCE.FRAGMENTS.CONFGIURATION\n }\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n {{ 'Remove`configuration`' | translate }}\n </button>\n </div>\n <div class=\"col-3 d-flex a-i-center\"></div>\n <div class=\"m-l-auto p-r-8 hidden-xs\">\n <button\n class=\"btn btn-dot showOnHover text-danger\"\n [attr.aria-label]=\"'Remove`configuration`' | translate\"\n tooltip=\"{{ 'Remove`configuration`' | translate }}\"\n placement=\"top\"\n type=\"button\"\n (click)=\"removeItem(configuration, 'configuration')\"\n [delay]=\"500\"\n c8yProductExperience\n inherit\n [actionData]=\"{\n action: PRODUCT_EXPERIENCE.ACTIONS.REMOVE,\n fragment: PRODUCT_EXPERIENCE.FRAGMENTS.CONFGIURATION\n }\"\n >\n <i\n c8yIcon=\"minus-circle\"\n data-cy=\"device-profile--Remove-icon\"\n ></i>\n </button>\n </div>\n </c8y-li-body>\n </c8y-li>\n </c8y-list-group>\n </div>\n <div\n class=\"card-block p-t-16\"\n *ngIf=\"deviceProfile.c8y_DeviceProfile.configuration?.length === 0\"\n >\n <c8y-ui-empty-state\n icon=\"gears\"\n [title]=\"'No configuration defined.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n <div class=\"card-footer p-t-0\">\n <div class=\"p-t-8\">\n <button\n class=\"btn btn-default m-b-0\"\n title=\"{{ 'Add configuration' | translate }}\"\n type=\"button\"\n data-cy=\"device-profile--Add-configuration-button\"\n (click)=\"addConfiguration()\"\n c8yProductExperience\n inherit\n [actionData]=\"{\n action: PRODUCT_EXPERIENCE.ACTIONS.ADD,\n fragment: PRODUCT_EXPERIENCE.FRAGMENTS.CONFGIURATION\n }\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Add configuration' | translate }}\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "component", type: i2.BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: i2.BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "component", type: i2.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i2.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i2.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "directive", type: i8.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i8.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i8.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i8.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i8.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i8.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i2.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i2.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "component", type: i2.ListGroupComponent, selector: "c8y-list-group" }, { kind: "component", type: i2.ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "component", type: i2.ListItemIconComponent, selector: "c8y-list-item-icon, c8y-li-icon", inputs: ["icon", "status"] }, { kind: "component", type: i2.ListItemBodyComponent, selector: "c8y-list-item-body, c8y-li-body", inputs: ["body"] }, { kind: "directive", type: i2.ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "directive", type: i9.TooltipDirective, selector: "[tooltip], [tooltipHtml]", inputs: ["adaptivePosition", "tooltip", "placement", "triggers", "container", "containerClass", "boundariesElement", "isOpen", "isDisabled", "delay", "tooltipHtml", "tooltipPlacement", "tooltipIsOpen", "tooltipEnable", "tooltipAppendToBody", "tooltipAnimation", "tooltipClass", "tooltipContext", "tooltipPopupDelay", "tooltipFadeDuration", "tooltipTrigger"], outputs: ["tooltipChange", "onShown", "onHidden", "tooltipStateChanged"], exportAs: ["bs-tooltip"] }, { kind: "directive", type: i10.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceProfileComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-device-profile', providers: [
{
provide: PRODUCT_EXPERIENCE_EVENT_SOURCE,
useExisting: forwardRef(() => DeviceProfileComponent)
}
], standalone: false, template: "<c8y-title>{{ profileName }}</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n icon=\"c8y-management\"\n label=\"{{ 'Management' | translate }}\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n [icon]=\"'c8y-device-profile'\"\n [label]=\"'Device profiles' | translate\"\n [path]=\"'device-profiles'\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n icon=\"c8y-device-profile\"\n label=\"{{ profileName }}\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<div\n class=\"row\"\n *ngIf=\"deviceProfile\"\n>\n <div class=\"col-lg-12 col-lg-max\">\n <div\n class=\"card card--fullpage\"\n *ngIf=\"deviceProfile\"\n >\n <div class=\"card-block bg-level-1 flex-no-shrink p-t-24 p-b-24 overflow-visible\">\n <div class=\"content-flex-70\">\n <div class=\"text-center\">\n <i class=\"c8y-icon-duocolor icon-48 c8y-icon c8y-icon-device-profile\"></i>\n <p>\n <small class=\"label label-info\">{{ 'Device profile' | translate }}</small>\n </p>\n </div>\n <div class=\"flex-grow col-10\">\n <div class=\"row\">\n <div class=\"col-md-4\">\n <form #editNameForm=\"ngForm\">\n <c8y-form-group>\n <label\n class=\"control-label\"\n translate\n >\n Name\n </label>\n <div class=\"input-group input-group-editable\">\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g. My device profile' | translate }}\"\n name=\"name\"\n type=\"text\"\n required\n [(ngModel)]=\"deviceProfile.name\"\n data-cy=\"device-profile--add-device-profile-name\"\n size=\"{{ deviceProfile.name?.length || 1 }}\"\n />\n <span></span>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n type=\"button\"\n data-cy=\"device-profile--save\"\n (click)=\"\n updateDeviceProfile({ name: deviceProfile.name });\n editNameForm.form.markAsPristine()\n \"\n [disabled]=\"editNameForm.form.invalid\"\n c8yProductExperience\n inherit\n [actionData]=\"{ action: PRODUCT_EXPERIENCE.ACTIONS.SAVE }\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n </div>\n </c8y-form-group>\n </form>\n </div>\n <div class=\"col-md-4\">\n <form #editTypeForm=\"ngForm\">\n <c8y-form-group>\n <label class=\"control-label\">\n {{ 'Device type' | translate }}\n <button\n class=\"btn-help\"\n [attr.aria-label]=\"'Help' | translate\"\n [popover]=\"\n (DEVICE_TYPE_POPOVER | translate) +\n (isDeviceProfileEmpty\n ? ' ' + (DEVICE_TYPE_DISABLED_POPOVER | translate)\n : '')\n \"\n placement=\"right\"\n triggers=\"focus\"\n container=\"body\"\n type=\"button\"\n ></button>\n </label>\n <div class=\"input-group input-group-editable\">\n <input\n class=\"form-control\"\n placeholder=\"{{ 'e.g.' | translate }} c8y_Linux\"\n name=\"type\"\n type=\"text\"\n [(ngModel)]=\"deviceProfile.c8y_Filter.type\"\n data-cy=\"device-profile--device-type\"\n size=\"{{ deviceProfile.c8y_Filter.type?.length || 14 }}\"\n [disabled]=\"isDeviceProfileEmpty\"\n />\n <span></span>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Save' | translate }}\"\n type=\"button\"\n (click)=\"\n updateDeviceProfile({\n c8y_Filter: { type: deviceProfile.c8y_Filter.type }\n });\n editTypeForm.form.markAsPristine()\n \"\n [disabled]=\"isDeviceProfileEmpty\"\n c8yProductExperience\n inherit\n [actionData]=\"{ action: PRODUCT_EXPERIENCE.ACTIONS.SAVE }\"\n >\n {{ 'Save' | translate }}\n </button>\n </div>\n </div>\n </c8y-form-group>\n </form>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"inner-scroll\">\n <div class=\"card-header separator-top-bottom bg-content sticky-top\">\n <div class=\"card-icon\">\n <i\n class=\"c8y-icon-duocolor\"\n [c8yIcon]=\"'c8y-firmware'\"\n ></i>\n </div>\n <div\n class=\"card-title\"\n translate\n >\n Firmware\n </div>\n </div>\n <div\n class=\"card-block p-t-0\"\n *ngIf=\"deviceProfile.c8y_DeviceProfile.firmware\"\n >\n <c8y-list-group>\n <c8y-li>\n <c8y-li-icon>\n <i [c8yIcon]=\"'c8y-firmware'\"></i>\n </c8y-li-icon>\n <c8y-li-body class=\"content-flex-50 m-l-4\">\n <div class=\"col-4\">\n <span\n class=\"text-truncate\"\n title=\"{{ deviceProfile.c8y_DeviceProfile.firmware.name }}\"\n >\n <span\n class=\"text-label-small m-r-8\"\n translate\n >\n Name\n </span>\n {{ deviceProfile.c8y_DeviceProfile.firmware.name }}\n </span>\n </div>\n <div class=\"col-4\"></div>\n <div class=\"col-3 d-flex a-i-center\">\n <span\n class=\"text-truncate\"\n title=\"{{ deviceProfile.c8y_DeviceProfile.firmware.version }}\"\n >\n <span\n class=\"text-label-small m-r-4\"\n translate\n >\n Version\n </span>\n {{ deviceProfile.c8y_DeviceProfile.firmware.version }}\n </span>\n <button\n class=\"btn btn-danger btn-xs visible-xs m-l-auto m-r-8 m-t-8\"\n title=\"{{ 'Remove`firmware`' | translate }}\"\n type=\"button\"\n (click)=\"removeFirmware()\"\n c8yProductExperience\n inherit\n [actionData]=\"{\n action: PRODUCT_EXPERIENCE.ACTIONS.REMOVE,\n fragment: PRODUCT_EXPERIENCE.FRAGMENTS.FIRMWARE\n }\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n {{ 'Remove`firmware`' | translate }}\n </button>\n </div>\n <div class=\"m-l-auto p-r-8 hidden-xs\">\n <button\n class=\"btn btn-dot showOnHover text-danger\"\n [attr.aria-label]=\"'Remove`firmware`' | translate\"\n tooltip=\"{{ 'Remove`firmware`' | translate }}\"\n placement=\"right\"\n type=\"button\"\n [delay]=\"500\"\n (click)=\"removeFirmware()\"\n c8yProductExperience\n inherit\n [actionData]=\"{\n action: PRODUCT_EXPERIENCE.ACTIONS.REMOVE,\n fragment: PRODUCT_EXPERIENCE.FRAGMENTS.FIRMWARE\n }\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n </button>\n </div>\n </c8y-li-body>\n </c8y-li>\n </c8y-list-group>\n </div>\n <div\n class=\"card-block p-t-16\"\n *ngIf=\"!deviceProfile.c8y_DeviceProfile.firmware\"\n >\n <c8y-ui-empty-state\n class=\"p-t-16 d-block\"\n icon=\"c8y-firmware\"\n [title]=\"'No firmware defined.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n <div\n class=\"card-footer p-t-0\"\n *ngIf=\"!deviceProfile.c8y_DeviceProfile.firmware\"\n >\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Add firmware' | translate }}\"\n type=\"button\"\n data-cy=\"device-profile--Add-firmware-button\"\n (click)=\"addFirmware()\"\n c8yProductExperience\n inherit\n [actionData]=\"{\n action: PRODUCT_EXPERIENCE.ACTIONS.ADD,\n fragment: PRODUCT_EXPERIENCE.FRAGMENTS.FIRMWARE\n }\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Add firmware' | translate }}\n </button>\n </div>\n\n <div class=\"card-header separator-top-bottom sticky-top bg-component\">\n <div class=\"card-icon\">\n <i\n class=\"c8y-icon-duocolor\"\n [c8yIcon]=\"'c8y-tools'\"\n ></i>\n </div>\n <div\n class=\"card-title\"\n translate\n >\n Software\n </div>\n </div>\n <div\n class=\"card-block p-t-0\"\n *ngIf=\"deviceProfile.c8y_DeviceProfile.software?.length > 0\"\n >\n <c8y-list-group>\n <c8y-li *ngFor=\"let software of deviceProfile.c8y_DeviceProfile.software\">\n <c8y-li-icon>\n <i [c8yIcon]=\"'c8y-tools'\"></i>\n </c8y-li-icon>\n <c8y-li-body class=\"content-flex-50 m-l-4\">\n <div class=\"col-4\">\n <span\n class=\"text-truncate-wrap\"\n title=\"{{ software.name }}\"\n >\n <span\n class=\"text-label-small m-r-8\"\n translate\n >\n Name\n </span>\n {{ software.name }}\n </span>\n </div>\n <div class=\"col-4\">\n <span\n class=\"text-truncate-wrap\"\n title=\"{{ software.name }}\"\n >\n <span\n class=\"text-label-small m-r-8\"\n translate\n >\n Type\n </span>\n <span\n class=\"label label-info m-l-4\"\n *ngIf=\"!!software.softwareType\"\n >\n {{ software.softwareType }}\n </span>\n </span>\n </div>\n <div class=\"col-3 d-flex a-i-center\">\n <span\n class=\"text-truncate-wrap\"\n title=\"{{ software.version }}\"\n >\n <span\n class=\"text-label-small m-r-8\"\n translate\n >\n Version\n </span>\n {{ software.version }}\n </span>\n <button\n class=\"btn btn-danger btn-xs visible-xs m-l-auto m-r-8 m-t-8\"\n title=\"{{ 'Remove`software`' | translate }}\"\n type=\"button\"\n ((click)=\"removeItem(software, 'software')\"\n c8yProductExperience\n inherit\n [actionData]=\"{\n action: PRODUCT_EXPERIENCE.ACTIONS.REMOVE,\n fragment: PRODUCT_EXPERIENCE.FRAGMENTS.SOFTWARE\n }\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n {{ 'Remove`software`' | translate }}\n </button>\n </div>\n <div class=\"m-l-auto p-r-8 hidden-xs\">\n <button\n class=\"btn btn-dot showOnHover text-danger\"\n [attr.aria-label]=\"'Remove`software`' | translate\"\n tooltip=\"{{ 'Remove`software`' | translate }}\"\n placement=\"right\"\n type=\"button\"\n [delay]=\"500\"\n (click)=\"removeItem(software, 'software')\"\n c8yProductExperience\n inherit\n [actionData]=\"{\n action: PRODUCT_EXPERIENCE.ACTIONS.REMOVE,\n fragment: PRODUCT_EXPERIENCE.FRAGMENTS.SOFTWARE\n }\"\n >\n <i\n c8yIcon=\"minus-circle\"\n data-cy=\"device-profile--Remove-icon\"\n ></i>\n </button>\n </div>\n </c8y-li-body>\n </c8y-li>\n </c8y-list-group>\n </div>\n <div\n class=\"card-block p-t-16\"\n *ngIf=\"deviceProfile.c8y_DeviceProfile.software?.length === 0\"\n >\n <c8y-ui-empty-state\n icon=\"c8y-tools\"\n [title]=\"'No software defined.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n <div class=\"card-footer p-t-0\">\n <button\n class=\"btn btn-default m-b-0\"\n title=\"{{ 'Add software' | translate }}\"\n type=\"button\"\n data-cy=\"device-profile--Add-software-button\"\n (click)=\"addSoftware()\"\n c8yProductExperience\n inherit\n [actionData]=\"{\n action: PRODUCT_EXPERIENCE.ACTIONS.ADD,\n fragment: PRODUCT_EXPERIENCE.FRAGMENTS.SOFTWARE\n }\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Add software' | translate }}\n </button>\n </div>\n\n <div class=\"card-header separator-top-bottom bg-component sticky-top\">\n <div class=\"card-icon\">\n <i\n class=\"c8y-icon-duocolor\"\n [c8yIcon]=\"'gears'\"\n ></i>\n </div>\n <div\n class=\"card-title\"\n translate\n >\n Configuration\n </div>\n </div>\n <div\n class=\"card-block p-t-0\"\n *ngIf=\"deviceProfile.c8y_DeviceProfile.configuration?.length > 0\"\n >\n <c8y-list-group>\n <c8y-li *ngFor=\"let configuration of deviceProfile.c8y_DeviceProfile.configuration\">\n <c8y-li-icon>\n <i [c8yIcon]=\"'gears'\"></i>\n </c8y-li-icon>\n <c8y-li-body class=\"content-flex-50\">\n <div class=\"col-4\">\n <span\n class=\"text-truncate\"\n title=\"{{ configuration.name }}\"\n >\n <span\n class=\"text-label-small m-r-8\"\n translate\n >\n Name\n </span>\n {{ configuration.name }}\n </span>\n </div>\n <div class=\"col-4\">\n <span\n class=\"text-label-small m-r-8\"\n translate\n >\n Type\n </span>\n <span class=\"label label-info\">{{ configuration.type }}</span>\n <button\n class=\"btn btn-danger btn-xs visible-xs m-l-auto m-r-8 m-t-8\"\n title=\"{{ 'Remove`configuration`' | translate }}\"\n type=\"button\"\n (click)=\"removeItem(configuration, 'configuration')\"\n c8yProductExperience\n inherit\n [actionData]=\"{\n action: PRODUCT_EXPERIENCE.ACTIONS.REMOVE,\n fragment: PRODUCT_EXPERIENCE.FRAGMENTS.CONFGIURATION\n }\"\n >\n <i c8yIcon=\"minus-circle\"></i>\n {{ 'Remove`configuration`' | translate }}\n </button>\n </div>\n <div class=\"col-3 d-flex a-i-center\"></div>\n <div class=\"m-l-auto p-r-8 hidden-xs\">\n <button\n class=\"btn btn-dot showOnHover text-danger\"\n [attr.aria-label]=\"'Remove`configuration`' | translate\"\n tooltip=\"{{ 'Remove`configuration`' | translate }}\"\n placement=\"top\"\n type=\"button\"\n (click)=\"removeItem(configuration, 'configuration')\"\n [delay]=\"500\"\n c8yProductExperience\n inherit\n [actionData]=\"{\n action: PRODUCT_EXPERIENCE.ACTIONS.REMOVE,\n fragment: PRODUCT_EXPERIENCE.FRAGMENTS.CONFGIURATION\n }\"\n >\n <i\n c8yIcon=\"minus-circle\"\n data-cy=\"device-profile--Remove-icon\"\n ></i>\n </button>\n </div>\n </c8y-li-body>\n </c8y-li>\n </c8y-list-group>\n </div>\n <div\n class=\"card-block p-t-16\"\n *ngIf=\"deviceProfile.c8y_DeviceProfile.configuration?.length === 0\"\n >\n <c8y-ui-empty-state\n icon=\"gears\"\n [title]=\"'No configuration defined.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </div>\n <div class=\"card-footer p-t-0\">\n <div class=\"p-t-8\">\n <button\n class=\"btn btn-default m-b-0\"\n title=\"{{ 'Add configuration' | translate }}\"\n type=\"button\"\n data-cy=\"device-profile--Add-configuration-button\"\n (click)=\"addConfiguration()\"\n c8yProductExperience\n inherit\n [actionData]=\"{\n action: PRODUCT_EXPERIENCE.ACTIONS.ADD,\n fragment: PRODUCT_EXPERIENCE.FRAGMENTS.CONFGIURATION\n }\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Add configuration' | translate }}\n </button>\n </div>\n </div>\n </div>\n </div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: i5.ActivatedRoute }, { type: i2.AlertService }, { type: i1.InventoryService }, { type: i4.BsModalService }, { type: i1$1.RepositoryService }, { type: DeviceProfileService }] });
class AddDeviceProfileComponent {
constructor(modal, deviceProfileService) {
this.modal = modal;
this.deviceProfileService = deviceProfileService;
this.DEVICE_TYPE_POPOVER = gettext('The device profile will be available for assignments on devices of the specified type. Otherwise, it will be available for all device types.');
this.PRODUCT_EXPERIENCE = PRODUCT_EXPERIENCE_DEVICE_PROFILE;
this.deviceProfile = {
name: '',
type: 'c8y_Profile',
c8y_Filter: {},
c8y_DeviceProfile: {}
};
this.result = new Promise((resolve, reject) => {
this._save = resolve;
this._cancel = reject;
});
}
async create() {
const mo = (await this.deviceProfileService.createDeviceProfile(this.deviceProfile)).data;
this._save(mo.id);
}
close() {
this._cancel();
this.modal.hide();
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AddDeviceProfileComponent, deps: [{ token: i4.BsModalRef }, { token: DeviceProfileService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: AddDeviceProfileComponent, isStandalone: false, selector: "c8y-add-device-profile", providers: [
{
provide: PRODUCT_EXPERIENCE_EVENT_SOURCE,
useExisting: forwardRef(() => AddDeviceProfileComponent)
}
], ngImport: i0, template: "<div class=\"viewport-modal\">\n <div class=\"modal-header dialog-header\">\n <i [c8yIcon]=\"'c8y-device-profile'\"></i>\n <div\n class=\"modal-title\"\n id=\"addDeviceProfileModalTitle\"\n translate\n >\n Add device profile\n </div>\n </div>\n\n <form\n #createDeviceProfile=\"ngForm\"\n (ngSubmit)=\"createDeviceProfile.form.valid && create()\"\n >\n <div class=\"modal-inner-scroll\">\n <div\n class=\"modal-body\"\n id=\"addDeviceProfileModalDescription\"\n >\n <c8y-form-group>\n <label\n for=\"name\"\n translate\n >\n Name\n </label>\n <input\n class=\"form-control\"\n id=\"name\"\n placeholder=\"{{ 'e.g. My device profile' | translate }}\"\n name=\"name\"\n type=\"text\"\n autocomplete=\"off\"\n required\n [(ngModel)]=\"deviceProfile.name\"\n data-cy=\"add-device-profile--device-profile-name\"\n />\n </c8y-form-group>\n <c8y-form-group>\n <label>\n {{ 'Device type' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ DEVICE_TYPE_POPOVER | translate }}\"\n placement=\"right\"\n triggers=\"focus\"\n type=\"button\"\n ></button>\n </label>\n <input\n class=\"form-control\"\n id=\"deviceType\"\n placeholder=\"{{ 'e.g.' | translate }} c8y_Linux\"\n name=\"deviceType\"\n [(ngModel)]=\"deviceProfile.c8y_Filter.type\"\n data-cy=\"add-device-profile--device-type\"\n />\n </c8y-form-group>\n </div>\n </div>\n <div class=\"modal-footer\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"close()\"\n c8yProductExperience\n inherit\n suppressDataOverriding\n [actionData]=\"{ action: PRODUCT_EXPERIENCE.ACTIONS.CANCEL }\"\n >\n {{ 'Cancel' | translate }}\n </button>\n\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Continue' | translate }}\"\n type=\"submit\"\n [disabled]=\"createDeviceProfile.form.invalid\"\n c8yProductExperience\n inherit\n suppressDataOverriding\n [actionData]=\"{ action: PRODUCT_EXPERIENCE.ACTIONS.CREATE }\"\n >\n {{ 'Continue' | translate }}\n </button>\n </div>\n </form>\n</div>\n", dependencies: [{ kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i2.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i8.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i8.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i8.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i8.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i8.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i8.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i2.FormGroupComponent, selector: "c8y-form-group", inputs: ["hasError", "hasWarning", "hasSuccess", "novalidation", "status"] }, { kind: "directive", type: i2.RequiredInputPlaceholderDirective, selector: "input[required], input[formControlName]" }, { kind: "directive", type: i2.ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "directive", type: i10.PopoverDirective, selector: "[popover]", inputs: ["adaptivePosition", "boundariesElement", "popover", "popoverContext", "popoverTitle", "placement", "outsideClick", "triggers", "container", "containerClass", "isOpen", "delay"], outputs: ["onShown", "onHidden"], exportAs: ["bs-popover"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: AddDeviceProfileComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-add-device-profile', providers: [
{
provide: PRODUCT_EXPERIENCE_EVENT_SOURCE,
useExisting: forwardRef(() => AddDeviceProfileComponent)
}
], standalone: false, template: "<div class=\"viewport-modal\">\n <div class=\"modal-header dialog-header\">\n <i [c8yIcon]=\"'c8y-device-profile'\"></i>\n <div\n class=\"modal-title\"\n id=\"addDeviceProfileModalTitle\"\n translate\n >\n Add device profile\n </div>\n </div>\n\n <form\n #createDeviceProfile=\"ngForm\"\n (ngSubmit)=\"createDeviceProfile.form.valid && create()\"\n >\n <div class=\"modal-inner-scroll\">\n <div\n class=\"modal-body\"\n id=\"addDeviceProfileModalDescription\"\n >\n <c8y-form-group>\n <label\n for=\"name\"\n translate\n >\n Name\n </label>\n <input\n class=\"form-control\"\n id=\"name\"\n placeholder=\"{{ 'e.g. My device profile' | translate }}\"\n name=\"name\"\n type=\"text\"\n autocomplete=\"off\"\n required\n [(ngModel)]=\"deviceProfile.name\"\n data-cy=\"add-device-profile--device-profile-name\"\n />\n </c8y-form-group>\n <c8y-form-group>\n <label>\n {{ 'Device type' | translate }}\n <button\n class=\"btn-help btn-help--sm\"\n [attr.aria-label]=\"'Help' | translate\"\n popover=\"{{ DEVICE_TYPE_POPOVER | translate }}\"\n placement=\"right\"\n triggers=\"focus\"\n type=\"button\"\n ></button>\n </label>\n <input\n class=\"form-control\"\n id=\"deviceType\"\n placeholder=\"{{ 'e.g.' | translate }} c8y_Linux\"\n name=\"deviceType\"\n [(ngModel)]=\"deviceProfile.c8y_Filter.type\"\n data-cy=\"add-device-profile--device-type\"\n />\n </c8y-form-group>\n </div>\n </div>\n <div class=\"modal-footer\">\n <button\n class=\"btn btn-default\"\n title=\"{{ 'Cancel' | translate }}\"\n type=\"button\"\n (click)=\"close()\"\n c8yProductExperience\n inherit\n suppressDataOverriding\n [actionData]=\"{ action: PRODUCT_EXPERIENCE.ACTIONS.CANCEL }\"\n >\n {{ 'Cancel' | translate }}\n </button>\n\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Continue' | translate }}\"\n type=\"submit\"\n [disabled]=\"createDeviceProfile.form.invalid\"\n c8yProductExperience\n inherit\n suppressDataOverriding\n [actionData]=\"{ action: PRODUCT_EXPERIENCE.ACTIONS.CREATE }\"\n >\n {{ 'Continue' | translate }}\n </button>\n </div>\n </form>\n</div>\n" }]
}], ctorParameters: () => [{ type: i4.BsModalRef }, { type: DeviceProfileService }] });
class DeviceProfileListComponent {
constructor(repositoryService, gridService, modalService, bsModalService, translateService, alertService, router, activatedRoute, deviceProfileService) {
this.repositoryService = repositoryService;
this.gridService = gridService;
this.modalService = modalService;
this.bsModalService = bsModalService;
this.translateService = translateService;
this.alertService = alertService;
this.router = router;
this.activatedRoute = activatedRoute;
this.deviceProfileService = deviceProfileService;
this.sizeRequestDone = false;
this.refresh$ = new EventEmitter();
this.isDataPresent$ = from(this.repositoryService.listRepositoryEntries(RepositoryType.PROFILE, { skipLegacy: true })).pipe(map(({ data }) => data.length > 0));
this.columns = [
new RepositoryItemNameGridColumn({
filterLabel: gettext('Filter device profile by name'),
placeholder: gettext('ubuntu core')
}),
new DeviceTypeGridColumn({ filterLabel: gettext('Filter device profile by device type') })
];
this.actionControls = [];
this.pagination = {
pageSize: 50,
currentPage: 1
};
this.productExperienceEvent = {
eventName: PRODUCT_EXPERIENCE_DEVICE_PROFILE.EVENTS.REPOSITORY,
data: {
component: PRODUCT_EXPERIENCE_DEVICE_PROFILE.COMPONENTS.DEVICE_PROFILE_LIST
}
};
this.noResultsMessage = gettext('No results to display.');
this.noDataMessage = gettext('No device profiles to display.');
this.noResultsSubtitle = gettext('Refine your search terms or check your spelling.');
this.noDataSubtitle = gettext('Add a new device profile by clicking below.');
this.serverSideDataCallback = this.onDataSourceModifier.bind(this);
}
ngOnInit() {
this.actionControls.push({
type: BuiltInActionType.Edit,
callback: this.editDeviceProfile.bind(this)
});
this.actionControls.push({
type: 'duplicate',
icon: 'copy',
text: gettext('Duplicate'),
callback: this.duplicateDeviceProfile.bind(this)
});
this.actionControls.push({
type: BuiltInActionType.Delete,
callback: this.deleteDeviceProfile.bind(this)
});
}
async onDataSourceModifier(dataSourceModifier) {
const dataRequest = this.repositoryService.listRepositoryEntries(RepositoryType.PROFILE, {
query: this.gridService.getQueryObj(dataSourceModifier.columns),
skipDefaultOrder: true,
params: {
pageSize: dataSourceModifier.pagination.pageSize,
currentPage: dataSourceModifier.pagination.currentPage
}
});
const filtererdSizeRequest = this.repositoryService
.listRepositoryEntries(RepositoryType.PROFILE, {
skipDefaultOrder: true,
query: this.gridService.getQueryObj(dataSourceModifier.columns),
params: { pageSize: 1 }
})
.then(response => response?.paging?.totalPages);
this.sizeRequest = this.repositoryService
.listRepositoryEntries(RepositoryType.PROFILE, {
skipDefaultOrder: true,
params: { pageSize: 1 }
})
.then(response => {
this.sizeRequestDone = true;
return response?.paging?.totalPages;
});
const [dataResponse, size, filteredSize] = await Promise.all([
dataRequest,
this.sizeRequest,
filtererdSizeRequest
]);
const { res, data, paging } = dataResponse;
const serverSideDataResult = {
res,
data,
paging,
filteredSize,
size
};
return serverSideDataResult;
}
editDeviceProfile(deviceProfile) {
this.router.navigate([deviceProfile.id], { relativeTo: this.activatedRoute });
}
async createDeviceProfile() {
const modal = this.bsModalService.show(AddDeviceProfileComponent, {
class: 'modal-sm',
ariaDescribedby: 'addDeviceProfileModalDescription',
ariaLabelledBy: 'addDeviceProfileModalTitle',
ignoreBackdropClick: true,
keyboard: false,
initialState: {
productExperienceEvent: {
...this.productExperienceEvent,
data: {
...this.productExperienceEvent.data,
component: PRODUCT_EXPERIENCE_DEVICE_PROFILE.COMPONENTS.ADD_DEVICE_PROFILE
}
}
}
}).content;
try {
const profileId = await modal.result;
modal.close();
this.router.navigateByUrl(`/device-profiles/${profileId}`);
}
catch (ex) {
// do nothing
}
}
async duplicateDeviceProfile(deviceProfile) {
const copy = cloneDeep(deviceProfile);
copy.id = null;
copy.name = 'Duplicate of ' + deviceProfile.name;
const mo = (await this.deviceProfileService.createDeviceProfile(copy)).data;
this.router.navigateByUrl(`/device-profiles/${mo.id}`);
}
async deleteDeviceProfile(deviceProfile) {
const deviceProfileName = deviceProfile.name;
const title = gettext('Delete device profile');
const confirmationText = this.translateService.instant(gettext('You are about to delete a device profile "{{ deviceProfileName }}".'), { deviceProfileName });
const finalQuestion = this.translateService.instant(gettext('Do you want to proceed?'));
try {
await this.modalService.confirm(title, `${confirmationText} ${finalQuestion}`, Status.DANGER, {
ok: gettext('Delete')
}, {}, this.productExperienceEvent);
await this.delete(deviceProfile.id);
this.refresh$.next();
}
catch (ex) {
// do nothing
}
}
trackByName(_index, column) {
return column.name;
}
async delete(profileId) {
try {
await this.repositoryService.delete(profileId);
this.alertService.success(gettext('Device profile deleted.'));
}
catch (ex) {
this.alertService.addServerFailure(ex);
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceProfileListComponent, deps: [{ token: i1$1.RepositoryService }, { token: i2.DataGridService }, { token: i2.ModalService }, { token: i4.BsModalService }, { token: i4$1.TranslateService }, { token: i2.AlertService }, { token: i5.Router }, { token: i5.ActivatedRoute }, { token: DeviceProfileService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: DeviceProfileListComponent, isStandalone: false, selector: "c8y-device-profile-list", providers: [
{
provide: PRODUCT_EXPERIENCE_EVENT_SOURCE,
useExisting: forwardRef(() => DeviceProfileListComponent)
}
], ngImport: i0, template: "<c8y-title>{{ 'Device profiles' | translate }}</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n icon=\"c8y-management\"\n label=\"{{ 'Management' | translate }}\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n icon=\"c8y-device-profile\"\n label=\"{{ 'Device profiles' | translate }}\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item [placement]=\"'right'\">\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Add device profile' | translate }}\"\n data-cy=\"device-profile-list--Add-device-profile\"\n (click)=\"createDeviceProfile()\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Add device profile' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-help\n src=\"/docs/device-management-application/managing-device-data/#managing-device-profiles\"\n></c8y-help>\n\n<div class=\"content-fullpage border-top border-bottom\">\n <c8y-data-grid\n [title]=\"'Device profiles' | translate\"\n [refresh]=\"refresh$\"\n [pagination]=\"pagination\"\n [columns]=\"columns\"\n [actionControls]=\"actionControls\"\n [infiniteScroll]=\"'auto'\"\n [serverSideDataCallback]=\"serverSideDataCallback\"\n >\n <c8y-ui-empty-state\n [icon]=\"stats?.size > 0 ? 'search' : 'c8y-tools'\"\n [title]=\"stats?.size > 0 ? (noResultsMessage | translate) : (noDataMessage | translate)\"\n [subtitle]=\"stats?.size > 0 ? (noResultsSubtitle | translate) : (noDataSubtitle | translate)\"\n *emptyStateContext=\"let stats\"\n [horizontal]=\"stats?.size > 0\"\n >\n <p *ngIf=\"stats?.size === 0\">\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Add device profile' | translate }}\"\n type=\"button\"\n (click)=\"createDeviceProfile()\"\n >\n {{ 'Add device profile' | translate }}\n </button>\n </p>\n </c8y-ui-empty-state>\n <ng-container *ngFor=\"let column of columns; trackBy: trackByName\">\n <c8y-column [name]=\"column.name\"></c8y-column>\n </ng-container>\n </c8y-data-grid>\n</div>\n", dependencies: [{ kind: "component", type: i2.ActionBarItemComponent, selector: "c8y-action-bar-item", inputs: ["placement", "priority", "itemClass", "injector", "groupId", "inGroupPriority"] }, { kind: "component", type: i2.BreadcrumbComponent, selector: "c8y-breadcrumb" }, { kind: "component", type: i2.BreadcrumbItemComponent, selector: "c8y-breadcrumb-item", inputs: ["icon", "translate", "label", "path", "injector"] }, { kind: "component", type: i2.EmptyStateComponent, selector: "c8y-ui-empty-state", inputs: ["icon", "title", "subtitle", "horizontal"] }, { kind: "directive", type: i2.EmptyStateContextDirective, selector: "[emptyStateContext]" }, { kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.ColumnDirective, selector: "c8y-column", inputs: ["name"] }, { kind: "component", type: i2.DataGridComponent, selector: "c8y-data-grid", inputs: ["title", "loadMoreItemsLabel", "loadingItemsLabel", "showSearch", "refresh", "columns", "rows", "pagination", "childNodePagination", "infiniteScroll", "serverSideDataCallback", "selectable", "singleSelection", "selectionPrimaryKey", "displayOptions", "actionControls", "bulkActionControls", "headerActionControls", "searchText", "configureColumnsEnabled", "showCounterWarning", "activeClassName", "expandableRows", "treeGrid", "hideReload", "childNodesProperty", "parentNodeLabelProperty"], outputs: ["rowMouseOver", "rowMouseLeave", "rowClick", "onConfigChange", "onBeforeFilter", "onBeforeSearch", "onFilter", "itemsSelect", "onReload", "onAddCustomColumn", "onRemoveCustomColumn", "onColumnFilterReset", "onSort", "onPageSizeChange", "onColumnReordered", "onColumnVisibilityChange"] }, { kind: "component", type: i2.TitleComponent, selector: "c8y-title", inputs: ["pageTitleUpdate"] }, { kind: "component", type: i2.HelpComponent, selector: "c8y-help", inputs: ["src", "isCollapsed", "priority", "icon"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceProfileListComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-device-profile-list', providers: [
{
provide: PRODUCT_EXPERIENCE_EVENT_SOURCE,
useExisting: forwardRef(() => DeviceProfileListComponent)
}
], standalone: false, template: "<c8y-title>{{ 'Device profiles' | translate }}</c8y-title>\n\n<c8y-breadcrumb>\n <c8y-breadcrumb-item\n icon=\"c8y-management\"\n label=\"{{ 'Management' | translate }}\"\n ></c8y-breadcrumb-item>\n <c8y-breadcrumb-item\n icon=\"c8y-device-profile\"\n label=\"{{ 'Device profiles' | translate }}\"\n ></c8y-breadcrumb-item>\n</c8y-breadcrumb>\n\n<c8y-action-bar-item [placement]=\"'right'\">\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Add device profile' | translate }}\"\n data-cy=\"device-profile-list--Add-device-profile\"\n (click)=\"createDeviceProfile()\"\n >\n <i c8yIcon=\"plus-circle\"></i>\n {{ 'Add device profile' | translate }}\n </button>\n</c8y-action-bar-item>\n\n<c8y-help\n src=\"/docs/device-management-application/managing-device-data/#managing-device-profiles\"\n></c8y-help>\n\n<div class=\"content-fullpage border-top border-bottom\">\n <c8y-data-grid\n [title]=\"'Device profiles' | translate\"\n [refresh]=\"refresh$\"\n [pagination]=\"pagination\"\n [columns]=\"columns\"\n [actionControls]=\"actionControls\"\n [infiniteScroll]=\"'auto'\"\n [serverSideDataCallback]=\"serverSideDataCallback\"\n >\n <c8y-ui-empty-state\n [icon]=\"stats?.size > 0 ? 'search' : 'c8y-tools'\"\n [title]=\"stats?.size > 0 ? (noResultsMessage | translate) : (noDataMessage | translate)\"\n [subtitle]=\"stats?.size > 0 ? (noResultsSubtitle | translate) : (noDataSubtitle | translate)\"\n *emptyStateContext=\"let stats\"\n [horizontal]=\"stats?.size > 0\"\n >\n <p *ngIf=\"stats?.size === 0\">\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Add device profile' | translate }}\"\n type=\"button\"\n (click)=\"createDeviceProfile()\"\n >\n {{ 'Add device profile' | translate }}\n </button>\n </p>\n </c8y-ui-empty-state>\n <ng-container *ngFor=\"let column of columns; trackBy: trackByName\">\n <c8y-column [name]=\"column.name\"></c8y-column>\n </ng-container>\n </c8y-data-grid>\n</div>\n" }]
}], ctorParameters: () => [{ type: i1$1.RepositoryService }, { type: i2.DataGridService }, { type: i2.ModalService }, { type: i4.BsModalService }, { type: i4$1.TranslateService }, { type: i2.AlertService }, { type: i5.Router }, { type: i5.ActivatedRoute }, { type: DeviceProfileService }] });
class DeviceProfileGuard {
canActivate(route) {
const contextData = route.data.contextData || route.parent.data.contextData;
if (!contextData) {
return false;
}
return this.hasSupportedOperation(contextData, DeviceProfileOperation.APPLY_PROFILE);
}
hasSupportedOperation(mo, operation) {
const supported = mo.c8y_SupportedOperations || [];
if (!supported) {
return false;
}
return !!supported.find(supportedOperation => supportedOperation === operation);
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceProfileGuard, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceProfileGuard }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceProfileGuard, decorators: [{
type: Injectable
}] });
class DeviceProfileItemListComponent {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceProfileItemListComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: DeviceProfileItemListComponent, isStandalone: false, selector: "c8y-device-profile-item-list", inputs: { icon: "icon", comparisonResults: "comparisonResults", showProfileItems: "showProfileItems", showTextLabel: "showTextLabel" }, ngImport: i0, template: "<div\n class=\"d-contents\"\n *ngFor=\"let comparisonResult of comparisonResults\"\n>\n <div\n class=\"p-l-16 p-r-16\"\n *ngIf=\"showProfileItems\"\n >\n <div class=\"c8y-list__item fit-h\">\n <div\n class=\"c8y-list__item__block\"\n *ngIf=\"comparisonResult.profile\"\n >\n <div class=\"c8y-list__item__icon\">\n <i [c8yIcon]=\"icon\"></i>\n </div>\n <div class=\"c8y-list__item__body\">\n <ng-container\n *ngTemplateOutlet=\"comparisonResultInfo; context: comparisonResult.profile\"\n ></ng-container>\n </div>\n </div>\n </div>\n </div>\n <div class=\"p-l-16 p-r-16 bg-level-1\">\n <div\n class=\"c8y-list__item bg-level-1\"\n [ngClass]=\"{\n 'has-warning': !!comparisonResult.comparisonAlert\n }\"\n >\n <div class=\"c8y-list__item__block\">\n <div class=\"c8y-list__item__icon\">\n <i [c8yIcon]=\"icon\"></i>\n </div>\n <div class=\"c8y-list__item__body\">\n <ng-container\n *ngTemplateOutlet=\"\n comparisonResultInfo;\n context: comparisonResult.device ? comparisonResult.device : comparisonResult.profile\n \"\n ></ng-container>\n <c8y-messages\n class=\"m-0\"\n style=\"margin-bottom: calc(var(--margin-base, 8px) * -1)\"\n *ngIf=\"comparisonResult.comparisonAlert\"\n >\n <c8y-message>\n {{ comparisonResult.comparisonAlert | translate }}\n </c8y-message>\n </c8y-messages>\n </div>\n </div>\n </div>\n </div>\n <div\n class=\"p-l-16 p-r-16 bg-level-0 hidden-xs hidden-sm\"\n *ngIf=\"!showProfileItems\"\n ></div>\n</div>\n\n<ng-template\n #comparisonResultInfo\n let-name=\"itemName\"\n let-details=\"itemDetails\"\n let-type=\"itemType\"\n>\n <div class=\"content-flex-40\">\n <div class=\"col-5\">\n <span class=\"text-truncate\">\n <span class=\"text-label-small m-r-4\">Name</span>\n <span title=\"{{ name }}\">\n {{ name }}\n </span>\n </span>\n </div>\n <div class=\"col-3\">\n <span\n class=\"text-truncate\"\n *ngIf=\"!!type\"\n >\n <span class=\"text-label-small m-r-4\">Type</span>\n <span title=\"{{ type }}\">\n <span class=\"label label-info m-l-4\">\n {{ type }}\n </span>\n </span>\n </span>\n </div>\n <div class=\"col-4\">\n <span\n class=\"text-truncate\"\n *ngIf=\"showTextLabel && details; else showInfoLabel\"\n >\n <span\n class=\"text-label-small m-r-4\"\n translate\n >\n Version\n </span>\n <span title=\"{{ details }}\">{{ details }}</span>\n </span>\n <ng-template #showInfoLabel>\n <span class=\"label label-info\">{{ details }}</span>\n </ng-template>\n </div>\n </div>\n</ng-template>\n", dependencies: [{ kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i2.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2$1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.MessageDirective, selector: "c8y-message", inputs: ["name", "text"] }, { kind: "component", type: i2.MessagesComponent, selector: "c8y-messages", inputs: ["show", "defaults", "helpMessage"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceProfileItemListComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-device-profile-item-list', standalone: false, template: "<div\n class=\"d-contents\"\n *ngFor=\"let comparisonResult of comparisonResults\"\n>\n <div\n class=\"p-l-16 p-r-16\"\n *ngIf=\"showProfileItems\"\n >\n <div class=\"c8y-list__item fit-h\">\n <div\n class=\"c8y-list__item__block\"\n *ngIf=\"comparisonResult.profile\"\n >\n <div class=\"c8y-list__item__icon\">\n <i [c8yIcon]=\"icon\"></i>\n </div>\n <div class=\"c8y-list__item__body\">\n <ng-container\n *ngTemplateOutlet=\"comparisonResultInfo; context: comparisonResult.profile\"\n ></ng-container>\n </div>\n </div>\n </div>\n </div>\n <div class=\"p-l-16 p-r-16 bg-level-1\">\n <div\n class=\"c8y-list__item bg-level-1\"\n [ngClass]=\"{\n 'has-warning': !!comparisonResult.comparisonAlert\n }\"\n >\n <div class=\"c8y-list__item__block\">\n <div class=\"c8y-list__item__icon\">\n <i [c8yIcon]=\"icon\"></i>\n </div>\n <div class=\"c8y-list__item__body\">\n <ng-container\n *ngTemplateOutlet=\"\n comparisonResultInfo;\n context: comparisonResult.device ? comparisonResult.device : comparisonResult.profile\n \"\n ></ng-container>\n <c8y-messages\n class=\"m-0\"\n style=\"margin-bottom: calc(var(--margin-base, 8px) * -1)\"\n *ngIf=\"comparisonResult.comparisonAlert\"\n >\n <c8y-message>\n {{ comparisonResult.comparisonAlert | translate }}\n </c8y-message>\n </c8y-messages>\n </div>\n </div>\n </div>\n </div>\n <div\n class=\"p-l-16 p-r-16 bg-level-0 hidden-xs hidden-sm\"\n *ngIf=\"!showProfileItems\"\n ></div>\n</div>\n\n<ng-template\n #comparisonResultInfo\n let-name=\"itemName\"\n let-details=\"itemDetails\"\n let-type=\"itemType\"\n>\n <div class=\"content-flex-40\">\n <div class=\"col-5\">\n <span class=\"text-truncate\">\n <span class=\"text-label-small m-r-4\">Name</span>\n <span title=\"{{ name }}\">\n {{ name }}\n </span>\n </span>\n </div>\n <div class=\"col-3\">\n <span\n class=\"text-truncate\"\n *ngIf=\"!!type\"\n >\n <span class=\"text-label-small m-r-4\">Type</span>\n <span title=\"{{ type }}\">\n <span class=\"label label-info m-l-4\">\n {{ type }}\n </span>\n </span>\n </span>\n </div>\n <div class=\"col-4\">\n <span\n class=\"text-truncate\"\n *ngIf=\"showTextLabel && details; else showInfoLabel\"\n >\n <span\n class=\"text-label-small m-r-4\"\n translate\n >\n Version\n </span>\n <span title=\"{{ details }}\">{{ details }}</span>\n </span>\n <ng-template #showInfoLabel>\n <span class=\"label label-info\">{{ details }}</span>\n </ng-template>\n </div>\n </div>\n</ng-template>\n" }]
}], propDecorators: { icon: [{
type: Input
}], comparisonResults: [{
type: Input
}], showProfileItems: [{
type: Input
}], showTextLabel: [{
type: Input
}] } });
class DeviceTabProfileDetailComponent {
constructor() {
this.emptyStateText = '';
this.emptyStateDetails = '';
this.showTextLabel = true;
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceTabProfileDetailComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: DeviceTabProfileDetailComponent, isStandalone: false, selector: "c8y-device-tab-profile-detail", inputs: { sectionTitle: "sectionTitle", sectionIcon: "sectionIcon", emptyStateText: "emptyStateText", emptyStateDetails: "emptyStateDetails", isProfileSelected: "isProfileSelected", isEmpty: "isEmpty", items: "items", showTextLabel: "showTextLabel" }, ngImport: i0, template: "<div class=\"card--grid grid__col--6-6--md\">\n <div class=\"bg-level-0 card-block sticky-top\">\n <h5 class=\"legend form-block\">{{ sectionTitle | translate }}</h5>\n </div>\n <div class=\"bg-level-1 card-block sticky-top hidden-xs hidden-sm\">\n <h5 class=\"legend form-block\">{{ sectionTitle | translate }}</h5>\n </div>\n <div class=\"bg-level-0 p-l-16 p-r-16\">\n <hr class=\"m-0\" />\n </div>\n <div class=\"bg-level-1 p-l-16 p-r-16\">\n <hr class=\"m-0\" />\n </div>\n <div class=\"d-contents\" *ngIf=\"!isProfileSelected || isEmpty\">\n <div class=\"p-l-16 p-r-16\">\n <div class=\"c8y-empty-state text-left\">\n <h1 [c8yIcon]=\"sectionIcon\" class=\"c8y-icon-duocolor\"></h1>\n <p>\n <span>{{ emptyStateText | translate }}</span\n ><br />\n <small *ngIf=\"isProfileSelected; else noItems\">\n {{ emptyStateDetails | translate }}\n </small>\n <ng-template #noItems>\n <small translate>No device profile selected</small>\n </ng-template>\n </p>\n </div>\n </div>\n </div>\n <div class=\"bg-level-1\" *ngIf=\"items.length === 0\"></div>\n <c8y-device-profile-item-list\n *ngIf=\"items.length > 0\"\n [icon]=\"sectionIcon\"\n [comparisonResults]=\"items\"\n [showProfileItems]=\"isProfileSelected && !isEmpty\"\n [showTextLabel]=\"showTextLabel\"\n class=\"d-contents\"\n ></c8y-device-profile-item-list>\n <div class=\"bg-level-0 p-t-24\" *ngIf=\"isProfileSelected && !isEmpty\"></div>\n <div class=\"bg-level-1 p-t-24\" *ngIf=\"isProfileSelected && !isEmpty\"></div>\n</div>\n", dependencies: [{ kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i2.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2$1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: DeviceProfileItemListComponent, selector: "c8y-device-profile-item-list", inputs: ["icon", "comparisonResults", "showProfileItems", "showTextLabel"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceTabProfileDetailComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-device-tab-profile-detail', standalone: false, template: "<div class=\"card--grid grid__col--6-6--md\">\n <div class=\"bg-level-0 card-block sticky-top\">\n <h5 class=\"legend form-block\">{{ sectionTitle | translate }}</h5>\n </div>\n <div class=\"bg-level-1 card-block sticky-top hidden-xs hidden-sm\">\n <h5 class=\"legend form-block\">{{ sectionTitle | translate }}</h5>\n </div>\n <div class=\"bg-level-0 p-l-16 p-r-16\">\n <hr class=\"m-0\" />\n </div>\n <div class=\"bg-level-1 p-l-16 p-r-16\">\n <hr class=\"m-0\" />\n </div>\n <div class=\"d-contents\" *ngIf=\"!isProfileSelected || isEmpty\">\n <div class=\"p-l-16 p-r-16\">\n <div class=\"c8y-empty-state text-left\">\n <h1 [c8yIcon]=\"sectionIcon\" class=\"c8y-icon-duocolor\"></h1>\n <p>\n <span>{{ emptyStateText | translate }}</span\n ><br />\n <small *ngIf=\"isProfileSelected; else noItems\">\n {{ emptyStateDetails | translate }}\n </small>\n <ng-template #noItems>\n <small translate>No device profile selected</small>\n </ng-template>\n </p>\n </div>\n </div>\n </div>\n <div class=\"bg-level-1\" *ngIf=\"items.length === 0\"></div>\n <c8y-device-profile-item-list\n *ngIf=\"items.length > 0\"\n [icon]=\"sectionIcon\"\n [comparisonResults]=\"items\"\n [showProfileItems]=\"isProfileSelected && !isEmpty\"\n [showTextLabel]=\"showTextLabel\"\n class=\"d-contents\"\n ></c8y-device-profile-item-list>\n <div class=\"bg-level-0 p-t-24\" *ngIf=\"isProfileSelected && !isEmpty\"></div>\n <div class=\"bg-level-1 p-t-24\" *ngIf=\"isProfileSelected && !isEmpty\"></div>\n</div>\n" }]
}], propDecorators: { sectionTitle: [{
type: Input
}], sectionIcon: [{
type: Input
}], emptyStateText: [{
type: Input
}], emptyStateDetails: [{
type: Input
}], isProfileSelected: [{
type: Input
}], isEmpty: [{
type: Input
}], items: [{
type: Input
}], showTextLabel: [{
type: Input
}] } });
class DeviceTabProfileComponent {
constructor(deviceRealtime, deviceProfileService, route, operationRealtime, alertService) {
this.deviceRealtime = deviceRealtime;
this.deviceProfileService = deviceProfileService;
this.route = route;
this.operationRealtime = operationRealtime;
this.alertService = alertService;
this.PRODUCT_EXPERIENCE = PRODUCT_EXPERIENCE_DEVICE_PROFILE;
this.firmwareItems = [];
this.softwareItems = [];
this.configurationItems = [];
this.pattern$ = new BehaviorSubject('');
this.reload$ = new BehaviorSubject(true);
this.productExperienceEvent = {
eventName: PRODUCT_EXPERIENCE_DEVICE_PROFILE.EVENTS.DEVICE_TAB,
data: {
component: PRODUCT_EXPERIENCE_DEVICE_PROFILE.COMPONENTS.DEVICE_TAB_PROFILE
}
};
this.destroyRef = inject(DestroyRef);
this.device = this.route.snapshot.parent.data.contextData;
}
async ngOnInit() {
this.getDeviceProfilesAndUpdateProfileItems();
this.subscribeToManagedObjects();
const supportedSoftwareTypes = new Set(this.device.c8y_SupportedSoftwareTypes);
const isSubset = (profileTypes) => {
return profileTypes.every(type => supportedSoftwareTypes.has(type));
};
this.filterPipe = pipe(map(data => {
if (supportedSoftwareTypes.size) {
return data.filter(mo => isSubset(this.deviceProfileService.getSoftwareTypes(mo)));
}
else {
return data;
}
}));
}
async getDeviceProfilesAndUpdateProfileItems() {
this.deviceProfiles$ = combineLatest([this.reload$.pipe(filter(Boolean)), this.pattern$]).pipe(switchMap(([_, name]) => this.deviceProfileService.getDeviceProfilesByDevice(this.device, name)), tap(async (profiles) => {
if (this.device.c8y_Profile) {
const profileId = this.device.c8y_Profile.profileId;
this.selectedProfile = profiles.data.find(mo => mo.id === profileId);
}
this.updateProfileItems(this.device, this.selectedProfile);
this.operation = await this.deviceProfileService.getProfileOperation(this.device.id);
this.subscribeToOperations();
}), tap(() => this.reload$.next(false)), takeUntilDestroyed(this.destroyRef));
}
selectProfile(mo) {
this.selectedProfile = this.toProfileForDevice(mo);
this.updateProfileItems(this.device, this.selectedProfile);
}
async createOperation() {
this.operation = await this.deviceProfileService.createProfileOperation(this.device, this.selectedProfile);
}
updateProfileItems(device, profile) {
this.firmwareItems = this.deviceProfileService.getFirmwareItems(device, profile);
this.softwareItems = this.deviceProfileService.getSoftwareItems(device, profile);
this.configurationItems = this.deviceProfileService.getConfigurationItems(device, profile);
}
subscribeToManagedObjects() {
this.deviceRealtime
.onUpdate$(this.device.id)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((managedObject) => {
this.updateProfileItems(managedObject, this.selectedProfile);
});
this.deviceRealtime
.onDelete$(this.device.id)
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe(() => {
this.alertService.danger(gettext('This device has just been deleted. You will be redirected to "All devices" page now.'));
window.location.href = '#/device';
});
}
subscribeToOperations() {
this.operationRealtime
.onAll$(this.device.id)
.pipe(map(({ data }) => data), filter(operation => operation.c8y_DeviceProfile), takeUntilDestroyed(this.destroyRef))
.subscribe(operation => {
this.operation = operation;
});
}
toProfileForDevice(profile) {
if (Array.from(profile?.c8y_DeviceProfile?.software || []).length === 0 ||
has(this.device, 'c8y_SupportedSoftwareTypes')) {
return profile;
}
else {
return {
...profile,
c8y_DeviceProfile: {
...profile.c8y_DeviceProfile,
software: profile.c8y_DeviceProfile.software.map(sw => pickBy(sw, (_, key) => key !== 'softwareType'))
}
};
}
}
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceTabProfileComponent, deps: [{ token: i2.ManagedObjectRealtimeService }, { token: DeviceProfileService }, { token: i5.ActivatedRoute }, { token: i2.OperationRealtimeService }, { token: i2.AlertService }], target: i0.ɵɵFactoryTarget.Component }); }
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "19.2.14", type: DeviceTabProfileComponent, isStandalone: false, selector: "c8y-device-tab-profile", providers: [
ManagedObjectRealtimeService,
{
provide: PRODUCT_EXPERIENCE_EVENT_SOURCE,
useExisting: forwardRef(() => DeviceTabProfileComponent)
}
], ngImport: i0, template: "<c8y-action-bar-item [placement]=\"'right'\">\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Reload' | translate }}\"\n type=\"button\"\n (click)=\"reload$.next(true)\"\n >\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': reload$.value }\"\n ></i>\n {{ 'Reload' | translate }}\n </button>\n</c8y-action-bar-item>\n<c8y-action-bar-item [placement]=\"'right'\">\n <c8y-realtime-btn [service]=\"deviceRealtime\"></c8y-realtime-btn>\n</c8y-action-bar-item>\n\n<div class=\"card card--grid--fullpage card--grid--fullpage card--grid grid__row--2-10--md\">\n <div class=\"card--grid grid__col--6-6--md\">\n <!-- AVAILABLE PROFILES -->\n <div class=\"bg-level-0\">\n <div class=\"card-header separator\">\n <div\n class=\"card-title\"\n translate\n >\n Device profile\n </div>\n </div>\n <div class=\"p-16\">\n <form #deviceProfileForm=\"ngForm\">\n <div class=\"input-group\">\n <c8y-typeahead\n class=\"flex-grow\"\n placeholder=\"{{ 'Select device profile' | translate }}\"\n name=\"selectProfile\"\n data-cy=\"device-tab-profile--select-device-profile\"\n [(ngModel)]=\"selectedProfile\"\n (onSearch)=\"pattern$.next($event)\"\n [allowFreeEntries]=\"false\"\n >\n <c8y-li\n class=\"p-l-8 p-r-8 c8y-list__item--link\"\n *c8yFor=\"let profile of deviceProfiles$; pipe: filterPipe\"\n (click)=\"selectProfile(profile); pattern$.next('')\"\n >\n <c8y-highlight\n [text]=\"profile.name || '--'\"\n [pattern]=\"pattern$.value\"\n ></c8y-highlight>\n </c8y-li>\n </c8y-typeahead>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Assign device profile' | translate }}\"\n type=\"button\"\n (click)=\"createOperation()\"\n data-cy=\"device-tab-profile--Assign-device-profile-button\"\n [disabled]=\"!selectedProfile?.id\"\n c8yProductExperience\n inherit\n [actionData]=\"{ action: PRODUCT_EXPERIENCE.ACTIONS.ASSIGN_DEVICE_PROFILE }\"\n >\n <span>{{ 'Assign device profile' | translate }}</span>\n </button>\n </div>\n </div>\n </form>\n </div>\n </div>\n\n <!-- INSTALL PROFILE OPERATION -->\n <div class=\"bg-level-1\">\n <div class=\"card-header separator\">\n <div\n class=\"card-title\"\n translate\n >\n Currently installed\n </div>\n </div>\n <div class=\"card-block\">\n <c8y-operation-details\n [operation]=\"operation\"\n c8yProductExperience\n inherit\n suppressDataOverriding\n ></c8y-operation-details>\n </div>\n </div>\n </div>\n <div class=\"card--grid__inner-scroll d-col no-align-items\">\n <div class=\"d-contents\">\n <!-- FIRMWARE -->\n <c8y-device-tab-profile-detail\n class=\"d-contents\"\n [sectionIcon]=\"'c8y-firmware'\"\n [sectionTitle]=\"'Firmware' | translate\"\n [emptyStateText]=\"'No firmware to display.' | translate\"\n [emptyStateDetails]=\"'No firmware assigned.' | translate\"\n [isProfileSelected]=\"!!selectedProfile\"\n [items]=\"firmwareItems\"\n [isEmpty]=\"!selectedProfile?.c8y_DeviceProfile?.firmware?.name\"\n ></c8y-device-tab-profile-detail>\n </div>\n <div class=\"d-contents\">\n <!-- SOFTWARE -->\n <c8y-device-tab-profile-detail\n class=\"d-contents\"\n [sectionIcon]=\"'c8y-tools'\"\n [sectionTitle]=\"'Software' | translate\"\n [emptyStateText]=\"'No software to display.' | translate\"\n [emptyStateDetails]=\"'No software assigned.' | translate\"\n [isProfileSelected]=\"!!selectedProfile\"\n [items]=\"softwareItems\"\n [isEmpty]=\"\n !selectedProfile?.c8y_DeviceProfile?.software ||\n selectedProfile?.c8y_DeviceProfile?.software?.length === 0\n \"\n ></c8y-device-tab-profile-detail>\n </div>\n <div class=\"d-contents\">\n <!-- CONFIGURATION -->\n <c8y-device-tab-profile-detail\n class=\"d-contents\"\n [sectionIcon]=\"'gears'\"\n [sectionTitle]=\"'Configuration' | translate\"\n [emptyStateText]=\"'No configuration to display' | translate\"\n [emptyStateDetails]=\"'No configuration assigned' | translate\"\n [isProfileSelected]=\"!!selectedProfile\"\n [items]=\"configurationItems\"\n [isEmpty]=\"\n !selectedProfile?.c8y_DeviceProfile?.configuration ||\n selectedProfile?.c8y_DeviceProfile?.configuration?.length === 0\n \"\n ></c8y-device-tab-profile-detail>\n </div>\n <!-- fill in the remanining vertical space when empty -->\n <div class=\"card--grid grid__col--6-6--md flex-grow\">\n <div class=\"bg-level-0\"></div>\n <div class=\"bg-level-1\"></div>\n </div>\n </div>\n</div>\n", dependencies: [{ kind: "component", type: i2.ActionBarItemComponent, selector: "c8y-action-bar-item", inputs: ["placement", "priority", "itemClass", "injector", "groupId", "inGroupPriority"] }, { kind: "directive", type: i2.IconDirective, selector: "[c8yIcon]", inputs: ["c8yIcon"] }, { kind: "directive", type: i2.C8yTranslateDirective, selector: "[translate],[ngx-translate]" }, { kind: "directive", type: i2$1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i2.ForOfDirective, selector: "[c8yFor]", inputs: ["c8yForOf", "c8yForLoadMore", "c8yForPipe", "c8yForNotFound", "c8yForMaxIterations", "c8yForLoadingTemplate", "c8yForLoadNextLabel", "c8yForLoadingLabel", "c8yForRealtime", "c8yForRealtimeOptions", "c8yForComparator", "c8yForEnableVirtualScroll", "c8yForVirtualScrollElementSize", "c8yForVirtualScrollStrategy", "c8yForVirtualScrollContainerHeight"], outputs: ["c8yForCount", "c8yForChange", "c8yForLoadMoreComponent"] }, { kind: "component", type: i2.HighlightComponent, selector: "c8y-highlight", inputs: ["pattern", "text", "elementClass", "shouldTrimPattern"] }, { kind: "component", type: i2.TypeaheadComponent, selector: "c8y-typeahead", inputs: ["required", "maxlength", "disabled", "allowFreeEntries", "placeholder", "displayProperty", "icon", "name", "autoClose", "hideNew", "container", "selected", "highlightFirstItem"], outputs: ["onSearch", "onIconClick"] }, { kind: "directive", type: i8.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i8.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i8.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i8.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i8.NgForm, selector: "form:not([ngNoForm]):not([formGroup]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: i2.ListItemComponent, selector: "c8y-list-item, c8y-li", inputs: ["active", "highlighted", "emptyActions", "dense", "collapsed", "selectable"], outputs: ["collapsedChange"] }, { kind: "directive", type: i2.ProductExperienceDirective, selector: "[c8yProductExperience]", inputs: ["actionName", "actionData", "inherit", "suppressDataOverriding"] }, { kind: "component", type: i2.RealtimeButtonComponent, selector: "c8y-realtime-btn", inputs: ["service", "label", "title", "disabled"], outputs: ["onToggle"] }, { kind: "component", type: i6.OperationDetailsComponent, selector: "c8y-operation-details", inputs: ["operation"] }, { kind: "component", type: DeviceTabProfileDetailComponent, selector: "c8y-device-tab-profile-detail", inputs: ["sectionTitle", "sectionIcon", "emptyStateText", "emptyStateDetails", "isProfileSelected", "isEmpty", "items", "showTextLabel"] }, { kind: "pipe", type: i2.C8yTranslatePipe, name: "translate" }] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceTabProfileComponent, decorators: [{
type: Component,
args: [{ selector: 'c8y-device-tab-profile', providers: [
ManagedObjectRealtimeService,
{
provide: PRODUCT_EXPERIENCE_EVENT_SOURCE,
useExisting: forwardRef(() => DeviceTabProfileComponent)
}
], standalone: false, template: "<c8y-action-bar-item [placement]=\"'right'\">\n <button\n class=\"btn btn-link\"\n title=\"{{ 'Reload' | translate }}\"\n type=\"button\"\n (click)=\"reload$.next(true)\"\n >\n <i\n c8yIcon=\"refresh\"\n [ngClass]=\"{ 'icon-spin': reload$.value }\"\n ></i>\n {{ 'Reload' | translate }}\n </button>\n</c8y-action-bar-item>\n<c8y-action-bar-item [placement]=\"'right'\">\n <c8y-realtime-btn [service]=\"deviceRealtime\"></c8y-realtime-btn>\n</c8y-action-bar-item>\n\n<div class=\"card card--grid--fullpage card--grid--fullpage card--grid grid__row--2-10--md\">\n <div class=\"card--grid grid__col--6-6--md\">\n <!-- AVAILABLE PROFILES -->\n <div class=\"bg-level-0\">\n <div class=\"card-header separator\">\n <div\n class=\"card-title\"\n translate\n >\n Device profile\n </div>\n </div>\n <div class=\"p-16\">\n <form #deviceProfileForm=\"ngForm\">\n <div class=\"input-group\">\n <c8y-typeahead\n class=\"flex-grow\"\n placeholder=\"{{ 'Select device profile' | translate }}\"\n name=\"selectProfile\"\n data-cy=\"device-tab-profile--select-device-profile\"\n [(ngModel)]=\"selectedProfile\"\n (onSearch)=\"pattern$.next($event)\"\n [allowFreeEntries]=\"false\"\n >\n <c8y-li\n class=\"p-l-8 p-r-8 c8y-list__item--link\"\n *c8yFor=\"let profile of deviceProfiles$; pipe: filterPipe\"\n (click)=\"selectProfile(profile); pattern$.next('')\"\n >\n <c8y-highlight\n [text]=\"profile.name || '--'\"\n [pattern]=\"pattern$.value\"\n ></c8y-highlight>\n </c8y-li>\n </c8y-typeahead>\n <div class=\"input-group-btn\">\n <button\n class=\"btn btn-primary\"\n title=\"{{ 'Assign device profile' | translate }}\"\n type=\"button\"\n (click)=\"createOperation()\"\n data-cy=\"device-tab-profile--Assign-device-profile-button\"\n [disabled]=\"!selectedProfile?.id\"\n c8yProductExperience\n inherit\n [actionData]=\"{ action: PRODUCT_EXPERIENCE.ACTIONS.ASSIGN_DEVICE_PROFILE }\"\n >\n <span>{{ 'Assign device profile' | translate }}</span>\n </button>\n </div>\n </div>\n </form>\n </div>\n </div>\n\n <!-- INSTALL PROFILE OPERATION -->\n <div class=\"bg-level-1\">\n <div class=\"card-header separator\">\n <div\n class=\"card-title\"\n translate\n >\n Currently installed\n </div>\n </div>\n <div class=\"card-block\">\n <c8y-operation-details\n [operation]=\"operation\"\n c8yProductExperience\n inherit\n suppressDataOverriding\n ></c8y-operation-details>\n </div>\n </div>\n </div>\n <div class=\"card--grid__inner-scroll d-col no-align-items\">\n <div class=\"d-contents\">\n <!-- FIRMWARE -->\n <c8y-device-tab-profile-detail\n class=\"d-contents\"\n [sectionIcon]=\"'c8y-firmware'\"\n [sectionTitle]=\"'Firmware' | translate\"\n [emptyStateText]=\"'No firmware to display.' | translate\"\n [emptyStateDetails]=\"'No firmware assigned.' | translate\"\n [isProfileSelected]=\"!!selectedProfile\"\n [items]=\"firmwareItems\"\n [isEmpty]=\"!selectedProfile?.c8y_DeviceProfile?.firmware?.name\"\n ></c8y-device-tab-profile-detail>\n </div>\n <div class=\"d-contents\">\n <!-- SOFTWARE -->\n <c8y-device-tab-profile-detail\n class=\"d-contents\"\n [sectionIcon]=\"'c8y-tools'\"\n [sectionTitle]=\"'Software' | translate\"\n [emptyStateText]=\"'No software to display.' | translate\"\n [emptyStateDetails]=\"'No software assigned.' | translate\"\n [isProfileSelected]=\"!!selectedProfile\"\n [items]=\"softwareItems\"\n [isEmpty]=\"\n !selectedProfile?.c8y_DeviceProfile?.software ||\n selectedProfile?.c8y_DeviceProfile?.software?.length === 0\n \"\n ></c8y-device-tab-profile-detail>\n </div>\n <div class=\"d-contents\">\n <!-- CONFIGURATION -->\n <c8y-device-tab-profile-detail\n class=\"d-contents\"\n [sectionIcon]=\"'gears'\"\n [sectionTitle]=\"'Configuration' | translate\"\n [emptyStateText]=\"'No configuration to display' | translate\"\n [emptyStateDetails]=\"'No configuration assigned' | translate\"\n [isProfileSelected]=\"!!selectedProfile\"\n [items]=\"configurationItems\"\n [isEmpty]=\"\n !selectedProfile?.c8y_DeviceProfile?.configuration ||\n selectedProfile?.c8y_DeviceProfile?.configuration?.length === 0\n \"\n ></c8y-device-tab-profile-detail>\n </div>\n <!-- fill in the remanining vertical space when empty -->\n <div class=\"card--grid grid__col--6-6--md flex-grow\">\n <div class=\"bg-level-0\"></div>\n <div class=\"bg-level-1\"></div>\n </div>\n </div>\n</div>\n" }]
}], ctorParameters: () => [{ type: i2.ManagedObjectRealtimeService }, { type: DeviceProfileService }, { type: i5.ActivatedRoute }, { type: i2.OperationRealtimeService }, { type: i2.AlertService }] });
const deviceProfilesRoutes = [
{
path: 'device-profiles/:id',
component: DeviceProfileComponent
},
{
path: 'device-profiles',
component: DeviceProfileListComponent
}
];
const deviceTabProfileRoutes = [
{
context: ViewContext.Device,
path: 'device-profile',
component: DeviceTabProfileComponent,
label: gettext('Device profile'),
icon: 'c8y-device-profile',
canActivate: [DeviceProfileGuard]
}
];
class DeviceProfileModule {
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceProfileModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.14", ngImport: i0, type: DeviceProfileModule, declarations: [DeviceProfileComponent,
DeviceProfileListComponent,
AddDeviceProfileComponent,
SelectConfigurationModalComponent,
DeviceTabProfileComponent,
DeviceTabProfileDetailComponent,
DeviceProfileItemListComponent], imports: [CoreModule,
CommonModule,
SharedRepositoryModule, i5.RouterModule, i2$2.BsDropdownModule, TooltipModule,
ReactiveFormsModule,
ButtonsModule,
PopoverModule,
OperationDetailsModule] }); }
static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceProfileModule, providers: [
hookNavigator(DeviceProfileNavigationFactory),
hookRoute(deviceTabProfileRoutes),
DeviceProfileService,
DeviceProfileGuard
], imports: [CoreModule,
CommonModule,
SharedRepositoryModule,
RouterModule.forChild(deviceProfilesRoutes),
BsDropdownModule.forRoot(),
TooltipModule,
ReactiveFormsModule,
ButtonsModule,
PopoverModule,
OperationDetailsModule] }); }
}
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.14", ngImport: i0, type: DeviceProfileModule, decorators: [{
type: NgModule,
args: [{
declarations: [
DeviceProfileComponent,
DeviceProfileListComponent,
AddDeviceProfileComponent,
SelectConfigurationModalComponent,
DeviceTabProfileComponent,
DeviceTabProfileDetailComponent,
DeviceProfileItemListComponent
],
exports: [],
imports: [
CoreModule,
CommonModule,
SharedRepositoryModule,
RouterModule.forChild(deviceProfilesRoutes),
BsDropdownModule.forRoot(),
TooltipModule,
ReactiveFormsModule,
ButtonsModule,
PopoverModule,
OperationDetailsModule
],
providers: [
hookNavigator(DeviceProfileNavigationFactory),
hookRoute(deviceTabProfileRoutes),
DeviceProfileService,
DeviceProfileGuard
]
}]
}] });
/**
* Generated bundle index. Do not edit.
*/
export { AddDeviceProfileComponent, DeviceProfileComponent, DeviceProfileGuard, DeviceProfileItemListComponent, DeviceProfileListComponent, DeviceProfileModule, DeviceProfileNavigationFactory, DeviceProfileOperation, DeviceProfileService, DeviceTabProfileComponent, DeviceTabProfileDetailComponent, PRODUCT_EXPERIENCE_DEVICE_PROFILE, SelectConfigurationModalComponent };
//# sourceMappingURL=c8y-ngx-components-device-profile.mjs.map