UNPKG

@c8y/ngx-components

Version:

Angular modules for Cumulocity IoT applications

1 lines • 74.7 kB
{"version":3,"file":"c8y-ngx-components-search.mjs","sources":["../../search/search.service.ts","../../search/search-config.model.ts","../../search/search-custom-filters.component.ts","../../search/search-custom-filters.component.html","../../search/search-action.component.ts","../../search/search-action.component.html","../../search/columns/asset-type-search-grid-column.ts","../../search/search-grid.component.ts","../../search/search-grid.component.html","../../search/search-results.component.ts","../../search/search-results.component.html","../../search/search.module.ts","../../search/c8y-ngx-components-search.ts"],"sourcesContent":["import { inject, Injectable } from '@angular/core';\nimport { QueriesUtil } from '@c8y/client';\nimport {\n ActionControl,\n BulkActionControl,\n Column,\n ContextRouteService,\n Pagination,\n PreviewService,\n SearchFilters,\n WILDCARD_SEARCH_FEATURE_KEY\n} from '@c8y/ngx-components';\nimport { AssetNodeService } from '@c8y/ngx-components/assets-navigator';\nimport {\n AlarmsDeviceGridColumn,\n DeviceGridService,\n ImeiDeviceGridColumn,\n ModelDeviceGridColumn,\n NameDeviceGridColumn,\n RegistrationDateDeviceGridColumn,\n SerialNumberDeviceGridColumn,\n SystemIdDeviceGridColumn\n} from '@c8y/ngx-components/device-grid';\nimport { BehaviorSubject, catchError, firstValueFrom, of } from 'rxjs';\n@Injectable({\n providedIn: 'root'\n})\nexport class AssetSearchService {\n GRID_CONFIG_STORAGE_KEY = 'search-grid-config';\n DEFAULT_PAGE_SIZE = 50;\n getGlobalSearchData = this.getSearchData.bind(this);\n appliedFilters$: BehaviorSubject<{\n [SearchFilters.ALL_FILTERS]: boolean;\n [SearchFilters.ONLY_GROUPS_AND_ASSETS]: boolean;\n [SearchFilters.ONLY_DEVICES]: boolean;\n [SearchFilters.CURRENT_HIERARCHY]: boolean;\n }> = new BehaviorSubject({\n allFilters: true,\n onlyDevices: true,\n onlyGroupsAndAssets: true,\n currentHierarchy: false\n });\n private queriesUtil: QueriesUtil;\n private previewService: PreviewService = inject(PreviewService);\n private deviceGridService: DeviceGridService = inject(DeviceGridService);\n private assetNodeService: AssetNodeService = inject(AssetNodeService);\n private contextRouteService: ContextRouteService = inject(ContextRouteService);\n\n constructor() {\n this.queriesUtil = new QueriesUtil();\n }\n\n /**\n * Resets the status of applied filters, used during the search.\n * Applies only to filters: 'All', 'Show only devices', 'Show only groups and assets'.\n */\n resetAppliedFilters() {\n this.appliedFilters$.next({\n allFilters: true,\n onlyDevices: true,\n onlyGroupsAndAssets: true,\n currentHierarchy: false\n });\n }\n\n buildCombinedRootQueryFilter(columns: Column[], pagination: Pagination) {\n const rootQuery = {\n __filter: {\n __and: { __not: { __has: `c8y_IsBinary` } }\n }\n };\n\n const { onlyDevices, onlyGroupsAndAssets } = this.appliedFilters$.value;\n const searchQuery = this.buildSearchQuery({ onlyDevices, onlyGroupsAndAssets });\n\n const userQuery = this.deviceGridService.getQueryObj(columns, pagination);\n const queryPart = this.queriesUtil.addOrderbys(rootQuery, userQuery.__orderby, 'append');\n const fullQuery = this.queriesUtil.addAndFilter(queryPart, userQuery.__filter);\n const queryWithSearch = this.queriesUtil.addAndFilter(fullQuery, searchQuery);\n return queryWithSearch;\n }\n\n async getData(columns: Column[], pagination: Pagination, text?: string) {\n const query = this.buildCombinedRootQueryFilter(columns, pagination);\n if (await this.isWildcardSearchEnabled()) {\n const wildcardQuery = this.queriesUtil.addAndFilter(query, {\n name: `*${text.trim().replace(/\\s+/g, '*')}*`\n });\n return this.assetNodeService.getAllInventories({\n ...pagination,\n query: this.queriesUtil.buildQuery(wildcardQuery)\n });\n }\n return this.assetNodeService.getAllInventories({\n ...pagination,\n query: this.queriesUtil.buildQuery(query),\n text\n });\n }\n\n getDefaultColumns(): Column[] {\n const defaultColumns = [\n new NameDeviceGridColumn({ sortOrder: 'asc' }),\n new ModelDeviceGridColumn(),\n new SerialNumberDeviceGridColumn({ visible: false }),\n new RegistrationDateDeviceGridColumn({ visible: false }),\n new SystemIdDeviceGridColumn({ visible: false }),\n new ImeiDeviceGridColumn({ visible: false }),\n new AlarmsDeviceGridColumn()\n ];\n return defaultColumns;\n }\n\n getDefaultActionControls(): ActionControl[] {\n return [];\n }\n\n getDefaultBulkActionControls(): BulkActionControl[] {\n return [];\n }\n\n getDefaultPagination(): Pagination {\n return {\n pageSize: 25,\n currentPage: 1\n };\n }\n\n async isWildcardSearchEnabled(): Promise<boolean> {\n return firstValueFrom(\n this.previewService.getState$(WILDCARD_SEARCH_FEATURE_KEY).pipe(catchError(() => of(true)))\n );\n }\n\n private buildSearchQuery(model) {\n const filter: any = {};\n const ors = [];\n if (model.types?.length) {\n ors.push({ type: { __in: model.types } });\n }\n if (model.onlyDevices) {\n ors.push({ __has: 'c8y_IsDevice' });\n }\n if (model.onlyGroupsAndAssets) {\n ors.push({ __has: 'c8y_IsDynamicGroup' });\n ors.push({ __has: 'c8y_IsDeviceGroup' });\n }\n if (!model.onlyDevices && !model.onlyGroupsAndAssets) {\n ors.push([\n { __has: 'c8y_IsDeviceGroup' },\n { __has: 'c8y_IsDevice' },\n { __has: 'c8y_IsAsset' }\n ]);\n }\n\n if (ors.length) {\n filter.__or = ors;\n }\n\n if (model.currentHierarchy && this.contextRouteService.activatedContextData?.contextData?.id) {\n filter.__and = [\n { __isinhierarchyof: this.contextRouteService.activatedContextData.contextData.id }\n ];\n }\n return filter;\n }\n\n /**\n * Get search data based on the provided text and pagination.\n * @param text The search text.\n * @param pagination The pagination options.\n * @returns The search results.\n */\n private async getSearchData(\n text: string,\n pagination: Pagination = { currentPage: 1, pageSize: this.DEFAULT_PAGE_SIZE }\n ) {\n const { onlyDevices, onlyGroupsAndAssets, currentHierarchy } = this.appliedFilters$.value;\n const query = this.buildSearchQuery({ onlyDevices, onlyGroupsAndAssets, currentHierarchy });\n const queryString = this.queriesUtil.buildQuery(query);\n if (await this.isWildcardSearchEnabled()) {\n const wildcardQuery = this.queriesUtil.addAndFilter(query, {\n name: `*${text.trim().replace(/\\s+/g, '*')}*`\n });\n return this.assetNodeService.getAllInventories({\n ...pagination,\n query: this.queriesUtil.buildQuery(wildcardQuery)\n });\n }\n return this.assetNodeService.getAllInventories({ ...pagination, query: queryString, text });\n }\n}\n","import { InjectionToken } from '@angular/core';\nexport const SEARCH_CONFIG = new InjectionToken<SearchConfig>('SearchConfig');\n\n/**\n * Configuration object of the SearchModule.\n */\nexport interface SearchConfig {\n /**\n * Allows to enable advanced filters.\n * Default value: true.\n */\n showAdvancedFilters?: boolean;\n /**\n * Allows a custom placeholder.\n * Default value: ''\n */\n placeholder?: string;\n}\n","import { AsyncPipe } from '@angular/common';\nimport { Component, EventEmitter, inject, Input, OnInit, Output } from '@angular/core';\nimport { takeUntilDestroyed } from '@angular/core/rxjs-interop';\nimport { FormsModule } from '@angular/forms';\nimport { ActivationEnd, Router } from '@angular/router';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport {\n C8yTranslatePipe,\n ContextRouteService,\n SearchFilters,\n TabComponent,\n TabsOutletComponent,\n ViewContext\n} from '@c8y/ngx-components';\nimport { filter, map, startWith } from 'rxjs/operators';\nimport { AssetSearchService } from './search.service';\n\nexport type Checkbox = {\n label: string;\n name: string;\n value: boolean;\n indeterminate?: boolean;\n isDisabled?: boolean;\n};\n\n@Component({\n selector: 'c8y-search-custom-filters',\n templateUrl: 'search-custom-filters.component.html',\n imports: [C8yTranslatePipe, TabsOutletComponent, TabComponent, AsyncPipe, FormsModule]\n})\nexport class SearchCustomFiltersComponent implements OnInit {\n @Input() useTabs = false;\n @Output() customDataQuery = new EventEmitter();\n @Output() refresh = new EventEmitter();\n tabNames = {\n all: gettext('All'),\n devices: gettext('Devices'),\n groupsAndAssets: gettext('Groups and assets')\n };\n selectedTab: (typeof this.tabNames)[keyof typeof this.tabNames] = this.tabNames.all;\n\n checkboxesState: Array<Checkbox> = [\n {\n label: gettext('All'),\n name: SearchFilters.ALL_FILTERS,\n value: true,\n indeterminate: false,\n isDisabled: true\n },\n { label: gettext('Show only devices'), name: SearchFilters.ONLY_DEVICES, value: true },\n {\n label: gettext('Show only groups and assets'),\n name: SearchFilters.ONLY_GROUPS_AND_ASSETS,\n value: true\n }\n ];\n\n @Input()\n isOnlyHierarchyQuery = false;\n @Output()\n isOnlyHierarchyQueryChange = new EventEmitter<boolean>();\n\n private assetSearchService = inject(AssetSearchService);\n private contextRouteService = inject(ContextRouteService);\n private router = inject(Router);\n\n contextRouteData$ = this.router.events.pipe(\n filter(event => event instanceof ActivationEnd),\n map((routeData: ActivationEnd) => {\n const data = this.contextRouteService.getContextData(routeData.snapshot);\n if (data?.context === ViewContext.Group) {\n return data;\n }\n return null;\n }),\n takeUntilDestroyed()\n );\n\n context$ = this.contextRouteData$.pipe(\n startWith(this.contextRouteService.activatedContextData),\n takeUntilDestroyed()\n );\n\n ngOnInit() {\n this.customDataQuery.next(this.assetSearchService.getGlobalSearchData);\n }\n\n onCheckboxChange(event: Event, checkbox: Checkbox): void {\n const { checked } = event.target as HTMLInputElement;\n\n if (checked == undefined) {\n return;\n }\n\n switch (checkbox.name) {\n case SearchFilters.ALL_FILTERS:\n this.onSelectAll(checkbox, checked);\n break;\n case SearchFilters.ONLY_DEVICES:\n this.onAllDevices(checkbox, checked);\n break;\n case SearchFilters.ONLY_GROUPS_AND_ASSETS:\n this.onGroupsAndAssets(checkbox, checked);\n break;\n }\n\n // Handle allFilters checkbox when ONLY_GROUPS_AND_ASSETS and ONLY_DEVICES are selected\n if (\n this.getCheckbox(SearchFilters.ONLY_DEVICES).value &&\n this.getCheckbox(SearchFilters.ONLY_GROUPS_AND_ASSETS).value\n ) {\n Object.assign(this.getCheckbox(SearchFilters.ALL_FILTERS), {\n indeterminate: false,\n isDisabled: true,\n value: true\n });\n }\n\n this.onSearchFilterChange(\n this.getCheckbox(SearchFilters.ALL_FILTERS).value,\n this.getCheckbox(SearchFilters.ONLY_DEVICES).value,\n this.getCheckbox(SearchFilters.ONLY_GROUPS_AND_ASSETS).value\n );\n\n this.refresh.next(null);\n }\n\n onTabChange(\n tabName: (typeof this.tabNames)[keyof typeof this.tabNames] = this.tabNames.all\n ): void {\n this.selectedTab = tabName;\n this.onSearchFilterChange(\n tabName === this.tabNames.all,\n tabName === this.tabNames.devices,\n tabName === this.tabNames.groupsAndAssets,\n this.isOnlyHierarchyQuery\n );\n }\n\n onHierarchyQueryChange(checked: boolean): void {\n this.isOnlyHierarchyQuery = checked;\n this.isOnlyHierarchyQueryChange.next(checked);\n this.onSearchFilterChange(\n this.selectedTab === this.tabNames.all,\n this.selectedTab === this.tabNames.devices,\n this.selectedTab === this.tabNames.groupsAndAssets,\n checked\n );\n }\n\n onSearchFilterChange(\n all: boolean,\n devices: boolean,\n groupsAndAssets: boolean,\n currentHierarchy = false\n ) {\n this.assetSearchService.appliedFilters$.next({\n [SearchFilters.ALL_FILTERS]: all,\n [SearchFilters.ONLY_DEVICES]: devices,\n [SearchFilters.ONLY_GROUPS_AND_ASSETS]: groupsAndAssets,\n [SearchFilters.CURRENT_HIERARCHY]: currentHierarchy\n });\n this.refresh.next(null);\n }\n\n private saveCheckboxValue(checkbox, value) {\n checkbox.value = value;\n }\n\n private onSelectAll(currentCheckbox: Checkbox, checked: boolean) {\n // Block unchecked state\n if (checked) {\n this.saveCheckboxValue(currentCheckbox, checked);\n }\n this.getCheckbox(SearchFilters.ALL_FILTERS).isDisabled = true;\n this.getCheckbox(SearchFilters.ONLY_DEVICES).value = true;\n this.getCheckbox(SearchFilters.ONLY_GROUPS_AND_ASSETS).value = true;\n }\n\n private onAllDevices(currentCheckbox: Checkbox, checked: boolean) {\n this.saveCheckboxValue(currentCheckbox, checked);\n\n Object.assign(this.getCheckbox(SearchFilters.ALL_FILTERS), {\n indeterminate: true,\n isDisabled: false,\n value: null\n });\n\n this.getCheckbox(SearchFilters.ONLY_GROUPS_AND_ASSETS).value = true;\n }\n\n private onGroupsAndAssets(currentCheckbox: Checkbox, checked: boolean) {\n this.saveCheckboxValue(currentCheckbox, checked);\n\n Object.assign(this.getCheckbox(SearchFilters.ALL_FILTERS), {\n indeterminate: true,\n isDisabled: false,\n value: null\n });\n\n this.getCheckbox(SearchFilters.ONLY_DEVICES).value = true;\n }\n\n private getCheckbox(checkboxName: SearchFilters): Checkbox {\n return this.checkboxesState.find(checkbox => checkbox.name === checkboxName);\n }\n}\n","<div class=\"d-flex gap-16 p-l-4 p-l-24 p-r-24\">\n @if (!useTabs) {\n <div class=\"p-b-8 separator-bottom sticky-top p-t-4\">\n @for (checkbox of checkboxesState; track $index) {\n <label class=\"c8y-checkbox\">\n <input\n type=\"checkbox\"\n [checked]=\"checkbox.value\"\n [indeterminate]=\"checkbox.indeterminate\"\n (click)=\"onCheckboxChange($event, checkbox)\"\n [attr.disabled]=\"checkbox.isDisabled ? true : null\"\n />\n <span></span>\n <span>{{ checkbox.label | translate }}</span>\n </label>\n }\n </div>\n }\n</div>\n\n@if (useTabs) {\n <c8y-tabs-outlet\n class=\"elevation-none\"\n outletName=\"searchTabs\"\n orientation=\"horizontal\"\n ></c8y-tabs-outlet>\n}\n\n<c8y-tab\n [icon]=\"'asterisk-key'\"\n [title]=\"'All devices, assets and groups' | translate\"\n [isActive]=\"selectedTab === tabNames.all\"\n [tabsOutlet]=\"'searchTabs'\"\n [label]=\"tabNames.all | translate\"\n [priority]=\"1000\"\n (onSelect)=\"onTabChange(tabNames.all)\"\n></c8y-tab>\n<c8y-tab\n [icon]=\"'exchange'\"\n [title]=\"'Devices only' | translate\"\n [isActive]=\"selectedTab === tabNames.devices\"\n [tabsOutlet]=\"'searchTabs'\"\n [label]=\"tabNames.devices | translate\"\n [priority]=\"750\"\n (onSelect)=\"onTabChange(tabNames.devices)\"\n></c8y-tab>\n<c8y-tab\n [icon]=\"'c8y-modules'\"\n [title]=\"'Assets and groups only' | translate\"\n [isActive]=\"selectedTab === tabNames.groupsAndAssets\"\n [tabsOutlet]=\"'searchTabs'\"\n [label]=\"tabNames.groupsAndAssets | translate\"\n [priority]=\"500\"\n (onSelect)=\"onTabChange(tabNames.groupsAndAssets)\"\n></c8y-tab>\n\n@if (useTabs && (context$ | async)?.contextData?.name) {\n <div class=\"p-l-48 p-t-8 d-flex a-i-center\">\n <span translate>Search only in`specified group or asset`</span>\n <span class=\"text-bold p-l-4\">{{ (context$ | async)?.contextData?.name }}</span>\n <label\n class=\"m-l-8 c8y-switch c8y-switch--inline\"\n [title]=\"'Search only within the current group or asset hierarchy' | translate\"\n >\n <input\n [attr.aria-label]=\"'Search only within the current group or asset hierarchy' | translate\"\n type=\"checkbox\"\n [ngModel]=\"isOnlyHierarchyQuery\"\n (ngModelChange)=\"onHierarchyQueryChange($event)\"\n />\n <span></span>\n </label>\n </div>\n}\n","import { AsyncPipe, NgIf } from '@angular/common';\nimport { Component, EventEmitter, inject } from '@angular/core';\nimport { NavigationExtras, Router } from '@angular/router';\nimport { IManagedObject, IResultList } from '@c8y/client';\nimport {\n RouterService as C8yRouter,\n PreviewService,\n SearchInputComponent,\n WILDCARD_SEARCH_FEATURE_KEY\n} from '@c8y/ngx-components';\nimport { catchError, lastValueFrom, Observable, of, UnaryFunction } from 'rxjs';\nimport { SEARCH_CONFIG, SearchConfig } from './search-config.model';\nimport { SearchCustomFiltersComponent } from './search-custom-filters.component';\n\n@Component({\n selector: 'c8y-search-action',\n templateUrl: './search-action.component.html',\n imports: [SearchInputComponent, NgIf, SearchCustomFiltersComponent, AsyncPipe]\n})\nexport class SearchActionComponent {\n showAdvancedFilters: boolean;\n customPlaceholder: string;\n customQuery: UnaryFunction<string, Observable<IResultList<IManagedObject>>>;\n typeaheadReload = new EventEmitter();\n\n previewService = inject(PreviewService);\n\n isWildcardSearchEnabled$ = this.previewService\n .getState$(WILDCARD_SEARCH_FEATURE_KEY)\n .pipe(catchError(() => of(true)));\n\n c8yRouter = inject(C8yRouter);\n router = inject(Router);\n moduleConfig: SearchConfig = inject(SEARCH_CONFIG, { optional: true });\n isOnlyHierarchyQuery = false;\n\n constructor() {\n this.showAdvancedFilters = this.moduleConfig?.showAdvancedFilters ?? true;\n this.customPlaceholder = this.moduleConfig?.placeholder;\n }\n\n triggerDataLoad() {\n this.typeaheadReload.next(null);\n }\n\n onOpenChange(isOpen: boolean) {\n if (isOpen) {\n this.triggerDataLoad();\n }\n }\n\n async onSearch(on: string) {\n if (await lastValueFrom(this.isWildcardSearchEnabled$)) {\n return;\n }\n this.navigate(['/assetsearch'], {\n queryParams: { search: on },\n replaceUrl: true\n });\n }\n\n onFilter(on: string) {\n this.navigate(['/assetsearch'], {\n queryParams: { filter: on },\n replaceUrl: true\n });\n }\n\n onClick(mo: IManagedObject) {\n this.router.navigateByUrl(this.c8yRouter.getHref(mo, '/'));\n }\n\n private navigate(commands: any[], extras?: NavigationExtras) {\n this.router\n .navigateByUrl('/', { skipLocationChange: true })\n .then(() => this.router.navigate(commands, extras));\n }\n}\n","<c8y-search-input\n #input\n (filter)=\"onFilter($event)\"\n (search)=\"onSearch($event)\"\n (onClick)=\"onClick($event)\"\n (onOpenToggle)=\"onOpenChange($event)\"\n [enableCustomTemplatePlaceholder]=\"true\"\n [customPlaceholder]=\"customPlaceholder\"\n [customDataQuery]=\"customQuery\"\n [externalTerm]=\"typeaheadReload\"\n [mode]=\"(isWildcardSearchEnabled$ | async) ? 'wildcardsearch' : 'search'\"\n>\n <c8y-search-custom-filters\n *ngIf=\"showAdvancedFilters && input.term.length > 0\"\n (refresh)=\"triggerDataLoad()\"\n [useTabs]=\"isWildcardSearchEnabled$ | async\"\n [(isOnlyHierarchyQuery)]=\"isOnlyHierarchyQuery\"\n (customDataQuery)=\"customQuery = $event\"\n ></c8y-search-custom-filters>\n</c8y-search-input>\n","import { gettext } from '@c8y/ngx-components/gettext';\nimport {\n ColumnConfig,\n getBasicInputArrayFormFieldConfig,\n SearchFilters\n} from '@c8y/ngx-components';\nimport { FormlyFieldConfig } from '@ngx-formly/core';\nimport { AssetSearchService } from '../search.service';\nimport { AssetTypeGridColumn } from '@c8y/ngx-components/data-grid-columns/asset-type';\n\nexport class AssetTypeSearchGridColumn extends AssetTypeGridColumn {\n constructor(\n hideExtendedFilters: boolean,\n initialColumnConfig?: ColumnConfig,\n assetSearchService?: AssetSearchService,\n customPlaceholder?: string\n ) {\n super(initialColumnConfig);\n\n this.filterable = true;\n this.filteringConfig = this.getFilteringConfig(\n hideExtendedFilters,\n assetSearchService,\n customPlaceholder\n );\n }\n\n private getFilteringConfig(\n hideExtendedFilters: boolean,\n assetSearchService: AssetSearchService,\n customPlaceholder?: string\n ) {\n return {\n fields: [\n ...getBasicInputArrayFormFieldConfig({\n key: 'types',\n label: gettext('Show items with type'),\n addText: gettext('Add next`type`'),\n tooltip: gettext('Use * as a wildcard character'),\n placeholder: customPlaceholder ? customPlaceholder : gettext('building`e.g. house`'),\n optional: !hideExtendedFilters\n }),\n {\n key: SearchFilters.ALL_FILTERS,\n type: 'checkbox',\n hide: hideExtendedFilters,\n props: {\n indeterminate: false,\n disabled: false,\n label: gettext('All'),\n click: (field, clickEvent) => {\n const { checked } = clickEvent.target;\n\n // Handle checked state\n if (checked) {\n field.form.get(SearchFilters.ONLY_DEVICES).setValue(true);\n field.form.get(SearchFilters.ONLY_GROUPS_AND_ASSETS).setValue(true);\n\n // Emit new state\n assetSearchService.appliedFilters$.next({\n [SearchFilters.ALL_FILTERS]: checked,\n [SearchFilters.ONLY_DEVICES]: true,\n [SearchFilters.ONLY_GROUPS_AND_ASSETS]: true,\n [SearchFilters.CURRENT_HIERARCHY]: null\n });\n }\n }\n },\n expressionProperties: {\n 'props.indeterminate': (model: any, formState: any, field: FormlyFieldConfig) => {\n // Do nothing\n if (field.form.get(SearchFilters.ALL_FILTERS).value === true) {\n return;\n }\n // Set indeterminate state\n if (\n !field.form.get(SearchFilters.ONLY_DEVICES).value ||\n !field.form.get(SearchFilters.ONLY_GROUPS_AND_ASSETS).value\n ) {\n field.form.get(SearchFilters.ALL_FILTERS).setValue(null);\n return true;\n }\n return false;\n },\n 'props.disabled': (model: any, formState: any, field: FormlyFieldConfig) => {\n if (field.form.get(SearchFilters.ALL_FILTERS).value === true) {\n return true;\n }\n return false;\n }\n },\n hooks: {\n onInit: field => {\n // Get initial state\n const { allFilters } = assetSearchService?.appliedFilters$?.value;\n field.formControl.setValue(allFilters);\n }\n }\n },\n {\n key: SearchFilters.ONLY_DEVICES,\n type: 'checkbox',\n hide: hideExtendedFilters,\n props: {\n indeterminate: false,\n label: gettext('Show only devices'),\n click: (field, clickEvent) => {\n const oldFilterValue = assetSearchService.appliedFilters$.value;\n const { checked } = clickEvent.target;\n\n // Handle checked state\n if (checked) {\n field.form.get(SearchFilters.ALL_FILTERS).setValue(true);\n // Emit new state\n assetSearchService.appliedFilters$.next({\n ...oldFilterValue,\n [SearchFilters.ALL_FILTERS]: true,\n [SearchFilters.ONLY_DEVICES]: checked\n });\n return;\n }\n // Handle unchecked state\n field.form.get(SearchFilters.ALL_FILTERS).setValue(null); // Trigger indeterminate state\n field.form.get(SearchFilters.ONLY_GROUPS_AND_ASSETS).setValue(true);\n\n // Emit new state\n assetSearchService.appliedFilters$.next({\n [SearchFilters.ALL_FILTERS]: null,\n [SearchFilters.ONLY_GROUPS_AND_ASSETS]: true,\n [SearchFilters.ONLY_DEVICES]: checked,\n [SearchFilters.CURRENT_HIERARCHY]: null\n });\n }\n },\n hooks: {\n onInit: field => {\n // Get initial state\n const { onlyDevices } = assetSearchService?.appliedFilters$?.value;\n field.formControl.setValue(onlyDevices);\n }\n }\n },\n {\n key: SearchFilters.ONLY_GROUPS_AND_ASSETS,\n type: 'checkbox',\n hide: hideExtendedFilters,\n props: {\n indeterminate: false,\n label: gettext('Show only groups and assets'),\n click: (field, clickEvent) => {\n const oldFilterValue = assetSearchService.appliedFilters$.value;\n const { checked } = clickEvent.target;\n\n // Handle checked state\n if (checked) {\n field.form.get(SearchFilters.ALL_FILTERS).setValue(true);\n // Emit new state\n assetSearchService.appliedFilters$.next({\n ...oldFilterValue,\n [SearchFilters.ALL_FILTERS]: true,\n [SearchFilters.ONLY_GROUPS_AND_ASSETS]: checked\n });\n return;\n }\n // Handle unchecked state\n field.form.get(SearchFilters.ALL_FILTERS).setValue(null); // Trigger indeterminate state\n field.form.get(SearchFilters.ONLY_DEVICES).setValue(true);\n\n // Emit new state\n assetSearchService.appliedFilters$.next({\n [SearchFilters.ALL_FILTERS]: null,\n [SearchFilters.ONLY_GROUPS_AND_ASSETS]: checked,\n [SearchFilters.ONLY_DEVICES]: true,\n [SearchFilters.CURRENT_HIERARCHY]: false\n });\n }\n },\n hooks: {\n onInit: field => {\n // Get initial state\n const { onlyGroupsAndAssets } = assetSearchService?.appliedFilters$?.value;\n field.formControl.setValue(onlyGroupsAndAssets);\n }\n }\n }\n ],\n /**\n * Adding devices and groups to a filter is already handled in {@link AssetSearchService#buildSearchQuery}\n * */\n getFilter(model: any): any {\n const filter: any = {};\n const ors = [];\n if (model.types?.length) {\n ors.push({ type: { __in: model.types } });\n }\n if (ors.length) {\n filter.__or = ors;\n }\n return filter;\n }\n };\n }\n}\n","import { Component, EventEmitter, inject, Input, Output, ViewChild } from '@angular/core';\nimport { IManagedObject, SmartGroupsService } from '@c8y/client';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport {\n ActionControl,\n BuiltInActionType,\n BulkActionControl,\n C8yTranslatePipe,\n Column,\n ColumnDirective,\n DATA_GRID_CONFIGURATION_CONTEXT_PROVIDER,\n DATA_GRID_CONFIGURATION_STRATEGY,\n DataGridComponent,\n DataSourceModifier,\n EmptyStateComponent,\n FilteringActionType,\n FilteringModifier,\n GridConfigContextProvider,\n Pagination,\n PreviewService,\n Row,\n SearchFilters,\n ServerSideDataResult,\n UserPreferencesConfigurationStrategy,\n UserPreferencesGridConfigContext,\n WILDCARD_SEARCH_FEATURE_KEY\n} from '@c8y/ngx-components';\nimport {\n AlarmsDeviceGridColumn,\n ImeiDeviceGridColumn,\n ModelDeviceGridColumn,\n NameDeviceGridColumn,\n RegistrationDateDeviceGridColumn,\n SerialNumberDeviceGridColumn,\n SystemIdDeviceGridColumn\n} from '@c8y/ngx-components/device-grid';\nimport {\n DeleteAssetsModalComponent,\n DeleteModalCheckboxes,\n SubAssetsService\n} from '@c8y/ngx-components/sub-assets';\nimport { BsModalService } from 'ngx-bootstrap/modal';\nimport { catchError, of } from 'rxjs';\nimport { AssetTypeSearchGridColumn } from './columns/asset-type-search-grid-column';\nimport { SEARCH_CONFIG } from './search-config.model';\nimport { AssetSearchService } from './search.service';\n\nimport { AsyncPipe, NgFor } from '@angular/common';\n\n@Component({\n selector: 'c8y-search-grid',\n templateUrl: './search-grid.component.html',\n providers: [\n {\n provide: DATA_GRID_CONFIGURATION_STRATEGY,\n useClass: UserPreferencesConfigurationStrategy\n },\n {\n provide: DATA_GRID_CONFIGURATION_CONTEXT_PROVIDER,\n useExisting: SearchGridComponent\n }\n ],\n imports: [\n DataGridComponent,\n NgFor,\n ColumnDirective,\n EmptyStateComponent,\n C8yTranslatePipe,\n AsyncPipe\n ]\n})\nexport class SearchGridComponent implements GridConfigContextProvider {\n @Input('parent-group') parentGroup: IManagedObject;\n @Input() title = '';\n @Input() loadingItemsLabel: string = gettext('Loading results…');\n @Input('columns') set _columns(value: Column[]) {\n if (value) {\n this.columns = value;\n } else {\n this.columns = this.assetSearchService.getDefaultColumns();\n }\n }\n @Input('pagination') set _pagination(value: Pagination) {\n if (value) {\n this.pagination = value;\n }\n }\n @Input('actionControls') set _actionControls(value: ActionControl[]) {\n if (value) {\n this.actionControls = value;\n } else {\n this.actionControls = this.assetSearchService.getDefaultActionControls();\n }\n }\n @Input() selectable = false;\n @Input('bulkActionControls') set _bulkActionControls(value: BulkActionControl[]) {\n if (value) {\n this.bulkActionControls = value;\n } else {\n this.bulkActionControls = this.assetSearchService.getDefaultBulkActionControls();\n }\n }\n @Output() onColumnsChange: EventEmitter<Column[]> = new EventEmitter<Column[]>();\n\n @Input()\n searchText = '';\n\n @Input()\n filteringName: string;\n\n /** The name of the key where columns configuration will be stored. */\n @Input() columnsConfigKey: string;\n\n moduleConfig = inject(SEARCH_CONFIG, { optional: true });\n showAdvancedFilters = this.moduleConfig?.showAdvancedFilters ?? false;\n customPlaceholder = this.moduleConfig?.placeholder ?? undefined;\n\n columns: Column[];\n assetSearchService = inject(AssetSearchService);\n pagination: Pagination = this.assetSearchService.getDefaultPagination();\n actionControls: ActionControl[];\n bulkActionControls: BulkActionControl[] = this.assetSearchService.getDefaultBulkActionControls();\n serverSideDataCallback: any;\n refresh: EventEmitter<any> = new EventEmitter();\n\n @ViewChild(DataGridComponent, { static: true })\n dataGrid: DataGridComponent;\n\n previewService = inject(PreviewService);\n isWildcardSearchEnabled$ = this.previewService\n .getState$(WILDCARD_SEARCH_FEATURE_KEY)\n .pipe(catchError(() => of(true)));\n\n wildcardSearchTitle = gettext('Asset data');\n fullTextSearchTitle = gettext('Search results');\n\n private sizeCount = 0;\n private bsModalService = inject(BsModalService);\n private smartGroupsService = inject(SmartGroupsService);\n private subAssetsGridService = inject(SubAssetsService);\n\n getGridConfigContext(): UserPreferencesGridConfigContext {\n return { key: this.columnsConfigKey || this.assetSearchService.GRID_CONFIG_STORAGE_KEY };\n }\n\n ngOnInit(): void {\n if (!this.filteringName) {\n this.columns = [\n new AssetTypeSearchGridColumn(\n this.showAdvancedFilters,\n { sortOrder: 'desc' },\n this.assetSearchService,\n this.customPlaceholder\n ),\n ...this.assetSearchService.getDefaultColumns()\n ];\n } else {\n this.columns = [\n new AssetTypeSearchGridColumn(\n this.showAdvancedFilters,\n { sortOrder: 'desc' },\n this.assetSearchService\n ),\n new NameDeviceGridColumn({\n sortOrder: 'asc',\n filter: { externalFilterQuery: { names: [this.filteringName] } }\n }),\n new ModelDeviceGridColumn(),\n new SerialNumberDeviceGridColumn({ visible: false }),\n new RegistrationDateDeviceGridColumn({ visible: false }),\n new SystemIdDeviceGridColumn({ visible: false }),\n new ImeiDeviceGridColumn({ visible: false }),\n new AlarmsDeviceGridColumn()\n ];\n }\n this.serverSideDataCallback = this.onDataSourceModifier.bind(this);\n this.setActionControls();\n }\n\n ngAfterViewInit() {\n this.setInitialFilterForTypeColumn();\n }\n\n trackByName(_index, column: Column): string {\n return column.name;\n }\n\n async onDataSourceModifier(\n dataSourceModifier: DataSourceModifier\n ): Promise<ServerSideDataResult> {\n const response = await this.assetSearchService.getData(\n dataSourceModifier.columns,\n dataSourceModifier.pagination,\n dataSourceModifier.searchText\n );\n const { res, data, paging } = response;\n\n if (paging.currentPage === 1) {\n this.sizeCount = 0;\n }\n this.sizeCount += data.length;\n this.onColumnsChange.emit(dataSourceModifier.columns);\n\n return {\n res,\n data,\n paging,\n filteredSize: this.sizeCount,\n size: undefined\n };\n }\n\n setActionControls() {\n const actionControls: ActionControl[] = [];\n const deleteAction: ActionControl = {\n type: BuiltInActionType.Delete,\n callback: (asset: Row) => this.onDeleteAsset(asset as IManagedObject, this.parentGroup)\n };\n actionControls.push(deleteAction);\n if (!this.actionControls) {\n this.actionControls = actionControls;\n }\n }\n\n updateFiltering(\n columnNames: string[],\n action: {\n type: FilteringActionType;\n payload?: { filteringModifier: FilteringModifier };\n }\n ) {\n const { type } = action;\n if (type === FilteringActionType.ResetFilter) {\n this.dataGrid.clearFilters();\n } else {\n /**\n * TODO: find better solution. After new changes from DM team, we're running into race condition where\n * this.dataGrid.updateFiltering is executed before this.configurationStrategy.getConfig$() value is emitted.\n * Columns setter sets columns after this.dataGrid.updateFiltering executes its logic. Value of this.columns in\n * dataGrid.updateFiltering is just not yet set.\n */\n setTimeout(() => {\n this.dataGrid.updateFiltering(columnNames, action, false);\n }, 500);\n }\n }\n\n onColumnFilterReset(column: Column) {\n if (column.name === 'type') {\n this.assetSearchService.resetAppliedFilters();\n }\n }\n\n private onDeleteAsset(asset, parentRef) {\n const initialState = {\n showWithDeviceUserCheckbox: this.subAssetsGridService.shouldShowWithDeviceUserCheckbox(asset),\n asset,\n showWithCascadeCheckbox: !this.smartGroupsService.isSmartGroup(asset)\n };\n\n const modalRef = this.bsModalService.show(DeleteAssetsModalComponent, { initialState });\n\n modalRef.content.closeSubject.subscribe(async (result: DeleteModalCheckboxes) => {\n if (result) {\n await this.subAssetsGridService.deleteAsset(asset, parentRef, result);\n this.refresh.emit();\n }\n });\n }\n\n private setInitialFilterForTypeColumn() {\n const checkboxes = this.assetSearchService.appliedFilters$.value;\n // Set filter only when all checkboxes are not selected\n if (\n checkboxes[SearchFilters.ONLY_DEVICES] !== checkboxes[SearchFilters.ONLY_GROUPS_AND_ASSETS]\n ) {\n const externalFilterQuery = {\n [SearchFilters.ONLY_DEVICES]: checkboxes[SearchFilters.ONLY_DEVICES],\n [SearchFilters.ONLY_GROUPS_AND_ASSETS]: checkboxes[SearchFilters.ONLY_GROUPS_AND_ASSETS]\n };\n this.updateFiltering(['type'], {\n type: FilteringActionType.ApplyFilter,\n payload: {\n filteringModifier: {\n externalFilterQuery\n }\n }\n });\n }\n }\n}\n","<div class=\"card--grid--fullpage border-top border-bottom\">\n <c8y-data-grid\n [title]=\"\n ((isWildcardSearchEnabled$ | async) ? wildcardSearchTitle : fullTextSearchTitle) | translate\n \"\n [loadingItemsLabel]=\"loadingItemsLabel\"\n [columns]=\"columns\"\n [pagination]=\"pagination\"\n [actionControls]=\"actionControls\"\n [selectable]=\"selectable\"\n [bulkActionControls]=\"bulkActionControls\"\n [serverSideDataCallback]=\"serverSideDataCallback\"\n [infiniteScroll]=\"'auto'\"\n [showSearch]=\"true\"\n [searchText]=\"searchText\"\n [refresh]=\"refresh\"\n (onColumnFilterReset)=\"onColumnFilterReset($event)\"\n >\n <ng-container *ngFor=\"let column of columns; trackBy: trackByName\">\n <c8y-column [name]=\"column.name\"></c8y-column>\n </ng-container>\n\n <c8y-ui-empty-state\n [icon]=\"'search'\"\n [title]=\"'No results to display.' | translate\"\n [subtitle]=\"'Refine your search terms or check your spelling.' | translate\"\n [horizontal]=\"true\"\n ></c8y-ui-empty-state>\n </c8y-data-grid>\n</div>\n","import { Component, inject, OnDestroy, ViewChild } from '@angular/core';\nimport { ActivatedRoute } from '@angular/router';\nimport { of, Subject } from 'rxjs';\nimport { catchError, takeUntil } from 'rxjs/operators';\nimport { SearchGridComponent } from './search-grid.component';\nimport { gettext } from '@c8y/ngx-components/gettext';\nimport {\n FilteringActionType,\n AlertService,\n Status,\n Alert,\n PreviewService,\n TitleComponent,\n C8yTranslateDirective,\n WILDCARD_SEARCH_FEATURE_KEY\n} from '@c8y/ngx-components';\nimport { AsyncPipe, NgIf } from '@angular/common';\n\n@Component({\n selector: 'c8y-search-results',\n templateUrl: './search-results.component.html',\n imports: [TitleComponent, C8yTranslateDirective, NgIf, SearchGridComponent, AsyncPipe]\n})\nexport class SearchResultsComponent implements OnDestroy {\n filter = '';\n searchText = '';\n @ViewChild(SearchGridComponent, { static: true })\n searchGrid: SearchGridComponent;\n filteringName: string;\n\n previewService = inject(PreviewService);\n isWildcardSearchEnabled$ = this.previewService\n .getState$(WILDCARD_SEARCH_FEATURE_KEY)\n .pipe(catchError(() => of(true)));\n\n private readonly WARNING_TIMEOUT_TIME = 3000;\n private unsubscribe$ = new Subject<void>();\n\n constructor(\n private route: ActivatedRoute,\n private alert: AlertService\n ) {}\n\n ngOnInit() {\n this.route.queryParams.pipe(takeUntil(this.unsubscribe$)).subscribe(params => {\n if (params.filter) {\n this.filteringName = params.filter;\n }\n });\n }\n\n ngAfterViewInit(): void {\n this.route.queryParams\n .pipe(takeUntil(this.unsubscribe$))\n .subscribe(({ filter, search }) => this.onQueryParamsChange(filter, search));\n }\n\n resetSearch() {\n if (this.searchGrid.dataGrid.searchText) {\n this.alert.add({\n text: gettext('Search reset. Full text search does not support filtering.'),\n type: Status.WARNING,\n timeout: this.WARNING_TIMEOUT_TIME\n } as Alert);\n this.searchText = '';\n this.searchGrid.dataGrid.searchText = '';\n }\n }\n\n resetFilter() {\n this.filter = '';\n if (this.searchGrid.dataGrid.filteringApplied) {\n this.alert.add({\n text: gettext('Filter reset. Full text search does not support filtering.'),\n type: Status.WARNING,\n timeout: this.WARNING_TIMEOUT_TIME\n } as Alert);\n this.searchGrid.dataGrid.clearFilters(false);\n }\n }\n\n ngOnDestroy() {\n this.unsubscribe$.next();\n this.unsubscribe$.complete();\n }\n\n private onQueryParamsChange(filter: string, searchTerm: string) {\n if (!this.shouldFilter(filter) && searchTerm) {\n this.searchText = searchTerm || '';\n }\n }\n\n private shouldFilter(filter) {\n if (!filter) {\n return false;\n }\n this.resetSearch();\n this.filter = filter || '';\n this.searchGrid.updateFiltering(['name'], {\n type: FilteringActionType.ApplyFilter,\n payload: {\n filteringModifier: {\n externalFilterQuery: {\n names: [this.filter]\n }\n }\n }\n });\n return true;\n }\n}\n","<c8y-title *ngIf=\"isWildcardSearchEnabled$ | async\">\n <span\n class=\"p-r-4\"\n translate\n >\n Search\n </span>\n <small\n ngNonBindable\n translate\n *ngIf=\"searchText\"\n [translateParams]=\"{\n searchHint: searchText\n }\"\n >\n searching \"{{ searchHint }}\"\n </small>\n <small\n ngNonBindable\n translate\n *ngIf=\"filter\"\n [translateParams]=\"{\n filterHint: filter\n }\"\n >\n filtered by \"{{ filterHint }}\"\n </small>\n</c8y-title>\n\n<c8y-title\n *ngIf=\"isWildcardSearchEnabled$ | async\"\n translate\n>\n Asset table\n</c8y-title>\n\n<c8y-search-grid\n [searchText]=\"searchText\"\n [filteringName]=\"filteringName\"\n></c8y-search-grid>\n","import { ModuleWithProviders, NgModule } from '@angular/core';\nimport { CoreModule, CoreSearchModule, hookRoute, hookSearch } from '@c8y/ngx-components';\nimport { AssetTypeCellRendererComponent } from '@c8y/ngx-components/data-grid-columns/asset-type';\nimport { DeviceGridModule } from '@c8y/ngx-components/device-grid';\nimport { SearchActionComponent } from './search-action.component';\nimport { SEARCH_CONFIG, SearchConfig } from './search-config.model';\nimport { SearchCustomFiltersComponent } from './search-custom-filters.component';\nimport { SearchGridComponent } from './search-grid.component';\nimport { SearchResultsComponent } from './search-results.component';\nimport { AssetSearchService } from './search.service';\n\n@NgModule({\n imports: [\n CoreModule,\n DeviceGridModule,\n CoreSearchModule,\n AssetTypeCellRendererComponent,\n SearchResultsComponent,\n SearchGridComponent,\n SearchActionComponent,\n SearchCustomFiltersComponent\n ],\n exports: [SearchResultsComponent, SearchGridComponent],\n providers: [\n AssetSearchService,\n hookRoute({\n path: 'assetsearch',\n component: SearchResultsComponent\n }),\n hookSearch({\n template: SearchActionComponent,\n onSearch: undefined\n })\n ]\n})\nexport class SearchModule {\n static config(config: SearchConfig = {}): ModuleWithProviders<SearchModule> {\n return {\n ngModule: SearchModule,\n providers: [\n {\n provide: SEARCH_CONFIG,\n useValue: config\n }\n ]\n };\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["C8yRouter","catchError","i1"],"mappings":"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